esphome_elektromer_han/components/xt211/client.c

2252 lines
62 KiB
C

//
// --------------------------------------------------------------------------
// Gurux Ltd
//
//
//
// Filename: $HeadURL$
//
// Version: $Revision$,
// $Date$
// $Author$
//
// Copyright (c) Gurux Ltd
//
//---------------------------------------------------------------------------
//
// DESCRIPTION
//
// This file is a part of Gurux Device Framework.
//
// Gurux Device Framework is Open Source software; you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2 of the License.
// Gurux Device Framework is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// This code is licensed under the GNU General Public License v2.
// Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
//---------------------------------------------------------------------------
#include "gxignore.h"
#if !defined(DLMS_IGNORE_CLIENT)
#include "gxmem.h"
#if defined(_WIN32) || defined(_WIN64) || defined(__linux__)
#include <assert.h>
#endif
#if _MSC_VER > 1400
#include <crtdbg.h>
#endif
#include "apdu.h"
#include "client.h"
#include "gxkey.h"
#include "gxset.h"
#include "gxget.h"
#include "dlms.h"
#include "cosem.h"
#if defined(DLMS_IGNORE_MALLOC) || defined(DLMS_COSEM_EXACT_DATA_TYPES)
#include "gxsetignoremalloc.h"
#else
#include "gxsetmalloc.h"
#endif //DLMS_IGNORE_MALLOC
#ifndef DLMS_IGNORE_HDLC
int cl_snrmRequest(dlmsSettings* settings, message* messages)
{
int ret;
gxByteBuffer* reply;
gxByteBuffer* pData;
mes_clear(messages);
//Save default values.
settings->initializeMaxInfoTX = settings->maxInfoTX;
settings->initializeMaxInfoRX = settings->maxInfoRX;
settings->initializeWindowSizeTX = settings->windowSizeTX;
settings->initializeWindowSizeRX = settings->windowSizeRX;
settings->connected = DLMS_CONNECTION_STATE_NONE;
settings->isAuthenticationRequired = 0;
#ifndef DLMS_IGNORE_PLC
// SNRM request is not used for all communication channels.
if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC_HDLC)
{
reply = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
BYTE_BUFFER_INIT(reply);
ret = dlms_getMacHdlcFrame(settings, DLMS_COMMAND_SNRM, 0, NULL, reply);
if (ret == 0)
{
mes_push(messages, reply);
}
return ret;
}
#endif //DLMS_IGNORE_PLC
if (settings->interfaceType != DLMS_INTERFACE_TYPE_HDLC && settings->interfaceType != DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E)
{
return 0;
}
if (settings->interfaceType == DLMS_INTERFACE_TYPE_WRAPPER)
{
return DLMS_ERROR_CODE_OK;
}
resetFrameSequence(settings);
#ifdef DLMS_IGNORE_MALLOC
if (!(messages->capacity > messages->size))
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
reply = messages->data[messages->size];
++messages->size;
bb_clear(reply);
pData = settings->serializedPdu;
if (pData == NULL)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
bb_clear(pData);
#else
reply = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
BYTE_BUFFER_INIT(reply);
gxByteBuffer bb;
BYTE_BUFFER_INIT(&bb);
bb_capacity(&bb, 30);
pData = &bb;
#endif //DLMS_IGNORE_MALLOC
// FromatID
if ((ret = bb_setUInt8(pData, 0x81)) == 0 &&
// GroupID
(ret = bb_setUInt8(pData, 0x80)) == 0 &&
// Length is updated later.
(ret = bb_setUInt8(pData, 0)) == 0)
{
// If custom HDLC parameters are used.
if (ret == 0 &&
(DEFAULT_MAX_INFO_TX != settings->maxInfoTX ||
DEFAULT_MAX_INFO_RX != settings->maxInfoRX ||
DEFAULT_MAX_WINDOW_SIZE_TX != settings->windowSizeTX ||
DEFAULT_MAX_WINDOW_SIZE_RX != settings->windowSizeRX))
{
if ((ret = bb_setUInt8(pData, HDLC_INFO_MAX_INFO_TX)) != 0 ||
(ret = dlms_appendHdlcParameter(pData, settings->maxInfoTX)) != 0 ||
(ret = bb_setUInt8(pData, HDLC_INFO_MAX_INFO_RX)) != 0 ||
(ret = dlms_appendHdlcParameter(pData, settings->maxInfoRX)) != 0 ||
(ret = bb_setUInt8(pData, HDLC_INFO_WINDOW_SIZE_TX)) != 0 ||
(ret = bb_setUInt8(pData, 4)) != 0 ||
(ret = bb_setUInt32(pData, settings->windowSizeTX)) != 0 ||
(ret = bb_setUInt8(pData, HDLC_INFO_WINDOW_SIZE_RX)) != 0 ||
(ret = bb_setUInt8(pData, 4)) != 0 ||
(ret = bb_setUInt32(pData, settings->windowSizeRX)) != 0)
{
//Error is returned in the end of this method.
}
}
// If default HDLC parameters are not used.
if (ret == 0)
{
if (pData->size != 3)
{
// Length.
ret = bb_setUInt8ByIndex(pData, 2, (unsigned char)(pData->size - 3));
}
else
{
bb_clear(pData);
}
}
if (ret == 0 && (ret = dlms_getHdlcFrame(settings, DLMS_COMMAND_SNRM, pData, reply)) != 0)
{
bb_clear(pData);
bb_clear(reply);
#ifndef DLMS_IGNORE_MALLOC
gxfree(reply);
#endif //DLMS_IGNORE_MALLOC
return ret;
}
}
bb_clear(pData);
#ifndef DLMS_IGNORE_MALLOC
mes_push(messages, reply);
#endif //DLMS_IGNORE_MALLOC
return ret;
}
int cl_parseUAResponse(dlmsSettings* settings, gxByteBuffer* data)
{
int ret = dlms_parseSnrmUaResponse(settings, data);
if (ret == 0 && bb_size(data) != 0)
{
settings->connected = DLMS_CONNECTION_STATE_HDLC;
}
return ret;
}
int cl_getFrameSize(dlmsSettings* settings, gxByteBuffer* data, uint32_t* size)
{
static const unsigned char HDLC_FRAME_START_END = 0x7E;
int ret = 0;
uint16_t value;
unsigned char ch;
*size = 1;
switch (settings->interfaceType)
{
case DLMS_INTERFACE_TYPE_HDLC:
case DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E:
{
uint32_t pos, index = data->position;
// If whole frame is not received yet.
if (bb_available(data) > 8)
{
// Find start of HDLC frame.
for (pos = data->position; pos < data->size; ++pos)
{
ret = bb_getUInt8(data, &ch);
if (ret != 0 || ch == HDLC_FRAME_START_END)
{
break;
}
}
if ((ret = bb_getUInt8(data, &ch)) == 0)
{
// Check frame length.
if ((ch & 0x7) != 0)
{
*size = ((ch & 0x7) << 8);
}
if ((ret = bb_getUInt8(data, &ch)) == 0)
{
*size += 1 + ch;
}
}
}
data->position = index;
}
break;
case DLMS_INTERFACE_TYPE_WRAPPER:
if (bb_available(data) < 8 ||
(ret = bb_getUInt16ByIndex(data, data->position, &value)) != 0 ||
value != 1)
{
*size = 8;
}
else
{
if ((ret = bb_getUInt16ByIndex(data, data->position + 6, &value)) == 0)
{
*size = 8 + value;
}
}
break;
case DLMS_INTERFACE_TYPE_PLC:
if (bb_available(data) < 2 ||
(ret = bb_getUInt8ByIndex(data, data->position, &ch)) != 0 ||
ch != 2)
{
*size = 2;
}
else
{
if ((ret = bb_getUInt8ByIndex(data, data->position + 1, &ch)) == 0)
{
*size = 2 + ch;
}
}
break;
default:
*size = 1;
break;
}
if (*size < 1)
{
*size = 1;
}
return ret;
}
int cl_getRemainingFrameSize(dlmsSettings* settings, gxByteBuffer* data, uint32_t* size)
{
int ret = cl_getFrameSize(settings, data, size);
if (ret == 0)
{
*size -= bb_available(data);
}
return ret;
}
#endif //DLMS_IGNORE_HDLC
int cl_aarqRequest(
dlmsSettings* settings,
message* messages)
{
if (settings->proposedConformance == 0)
{
//Invalid conformance.
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
if (dlms_usePreEstablishedConnection(settings))
{
//Invalid conformance.
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
//Save default values.
settings->initializePduSize = settings->maxPduSize;
int ret;
gxByteBuffer* pdu;
#ifdef DLMS_IGNORE_MALLOC
if (!(messages->size < messages->capacity))
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
pdu = settings->serializedPdu;
if (pdu == NULL)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
bb_clear(pdu);
#else
gxByteBuffer buff;
#ifdef GX_DLMS_MICROCONTROLLER
static unsigned char GX_AARQ_PDU[130];
if ((ret = bb_attach(&buff, GX_AARQ_PDU, 0, sizeof(GX_AARQ_PDU))) != 0)
{
return ret;
}
pdu = &buff;
#else
BYTE_BUFFER_INIT(&buff);
if ((ret = bb_capacity(&buff, 100)) != 0)
{
return ret;
}
pdu = &buff;
#endif
#endif //DLMS_IGNORE_MALLOC
settings->connected &= ~DLMS_CONNECTION_STATE_DLMS;
resetBlockIndex(settings);
mes_clear(messages);
ret = dlms_checkInit(settings);
if (ret != DLMS_ERROR_CODE_OK)
{
return ret;
}
bb_clear(&settings->stoCChallenge);
if (settings->autoIncreaseInvokeID)
{
settings->invokeID = 0;
}
else
{
settings->invokeID = 1;
}
// If authentication or ciphering is used.
if (settings->authentication > DLMS_AUTHENTICATION_LOW && settings->customChallenges == 0)
{
if ((ret = dlms_generateChallenge(&settings->ctoSChallenge)) != 0)
{
return ret;
}
}
if ((ret = apdu_generateAarq(settings, pdu)) == 0)
{
if (settings->useLogicalNameReferencing)
{
gxLNParameters p;
params_initLN(&p, settings, 0, DLMS_COMMAND_AARQ, 0, pdu, NULL, 0xFF, DLMS_COMMAND_NONE, 0, 0);
ret = dlms_getLnMessages(&p, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
gxSNParameters p;
params_initSN(&p, settings, DLMS_COMMAND_AARQ, 0, 0, NULL, pdu, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, messages);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
}
settings->connected &= ~DLMS_CONNECTION_STATE_DLMS;
bb_clear(pdu);
return ret;
}
int cl_parseAAREResponse(dlmsSettings* settings, gxByteBuffer* reply)
{
int ret;
unsigned char sd;
DLMS_ASSOCIATION_RESULT result;
unsigned char command = 0;
if ((ret = apdu_parsePDU(settings, reply, &result, &sd, &command)) != 0)
{
return ret;
}
if (result != DLMS_ASSOCIATION_RESULT_ACCEPTED)
{
if (result == DLMS_ASSOCIATION_RESULT_TRANSIENT_REJECTED)
{
return DLMS_ERROR_CODE_REJECTED_TRANSIENT;
}
return DLMS_ERROR_CODE_REJECTED_PERMAMENT;
}
settings->isAuthenticationRequired = sd == DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_REQUIRED;
if (!settings->isAuthenticationRequired)
{
settings->connected |= DLMS_CONNECTION_STATE_DLMS;
}
if (settings->dlmsVersionNumber != 6)
{
//Invalid DLMS version number.
return DLMS_ERROR_CODE_INVALID_VERSION_NUMBER;
}
return 0;
}
int cl_getApplicationAssociationRequest(
dlmsSettings* settings,
message* messages)
{
int ret;
gxByteBuffer challenge;
gxByteBuffer* pw;
dlmsVARIANT data;
#ifndef DLMS_IGNORE_HIGH_GMAC
#ifdef DLMS_IGNORE_MALLOC
gxByteBuffer pw2;
#endif //DLMS_IGNORE_MALLOC
#endif //DLMS_IGNORE_HIGH_GMAC
#ifndef GX_DLMS_MICROCONTROLLER
unsigned char APPLICATION_ASSOCIATION_REQUEST[32];
#else
static unsigned char APPLICATION_ASSOCIATION_REQUEST[32];
#endif //DLMS_IGNORE_HIGH_GMAC
bb_attach(&challenge, APPLICATION_ASSOCIATION_REQUEST, 0, sizeof(APPLICATION_ASSOCIATION_REQUEST));
if (settings->authentication != DLMS_AUTHENTICATION_HIGH_ECDSA &&
#ifndef DLMS_IGNORE_HIGH_GMAC
settings->authentication != DLMS_AUTHENTICATION_HIGH_GMAC &&
#endif // DLMS_IGNORE_HIGH_GMAC
settings->password.size == 0)
{
//Password is invalid.
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
resetBlockIndex(settings);
#ifndef DLMS_IGNORE_HIGH_GMAC
if (settings->authentication == DLMS_AUTHENTICATION_HIGH_GMAC)
{
#ifndef DLMS_IGNORE_MALLOC
pw = &settings->cipher.systemTitle;
#else
bb_attach(&pw2, settings->cipher.systemTitle, 8, 8);
pw = &pw2;
#endif //DLMS_IGNORE_MALLOC
}
else
#endif //DLMS_IGNORE_HIGH_GMAC
{
pw = &settings->password;
}
ret = dlms_secure(settings,
#ifndef DLMS_IGNORE_HIGH_GMAC
settings->cipher.invocationCounter,
#else
0,
#endif //DLMS_IGNORE_HIGH_GMAC
& settings->stoCChallenge,
pw,
&challenge);
if (ret == 0)
{
++settings->cipher.invocationCounter;
var_init(&data);
data.vt = DLMS_DATA_TYPE_OCTET_STRING;
data.byteArr = &challenge;
{
if (settings->useLogicalNameReferencing)
{
static const unsigned char LN[6] = { 0, 0, 40, 0, 0, 255 };
ret = cl_methodLN(settings, LN, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME,
1, &data, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_methodSN(settings, 0xFA00, DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME, 8, &data,
messages);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
}
#ifndef DLMS_IGNORE_MALLOC
var_clear(&data);
bb_clear(&challenge);
#endif //DLMS_IGNORE_MALLOC
}
return ret;
}
int cl_parseApplicationAssociationResponse(
dlmsSettings* settings,
gxByteBuffer* reply)
{
unsigned char empty, equals = 0;
gxByteBuffer* secret;
gxByteBuffer challenge;
#ifndef DLMS_IGNORE_HIGH_GMAC
gxByteBuffer bb2;
#endif //DLMS_IGNORE_HIGH_GMAC
int ret;
uint32_t ic = 0;
gxByteBuffer value;
static unsigned char tmp[MAX_CHALLENGE_SIZE];
static unsigned char CHALLENGE_BUFF[MAX_CHALLENGE_SIZE];
bb_attach(&value, tmp, 0, sizeof(tmp));
bb_attach(&challenge, CHALLENGE_BUFF, 0, sizeof(CHALLENGE_BUFF));
if ((ret = cosem_getOctetString(reply, &value)) != 0)
{
settings->connected &= ~DLMS_CONNECTION_STATE_DLMS;
//ParseApplicationAssociationResponse failed. Server to Client do not match.
return DLMS_ERROR_CODE_AUTHENTICATION_FAILURE;
}
empty = value.size == 0;
if (!empty)
{
#ifndef DLMS_IGNORE_HIGH_GMAC
if (settings->authentication == DLMS_AUTHENTICATION_HIGH_GMAC)
{
unsigned char ch;
bb_attach(&bb2, settings->sourceSystemTitle, sizeof(settings->sourceSystemTitle), sizeof(settings->sourceSystemTitle));
secret = &bb2;
if ((ret = bb_set(&challenge, value.data, value.size)) != 0 ||
(ret = bb_getUInt8(&challenge, &ch)) != 0 ||
(ret = bb_getUInt32(&challenge, &ic)) != 0)
{
return ret;
}
bb_clear(&challenge);
}
else
#endif //DLMS_IGNORE_HIGH_GMAC
{
secret = &settings->password;
}
if ((ret = dlms_secure(
settings,
ic,
&settings->ctoSChallenge,
secret,
&challenge)) != 0)
{
return ret;
}
equals = bb_compare(
&challenge,
value.data,
value.size);
}
else
{
// Server did not accept CtoS.
}
if (!equals)
{
settings->connected &= ~DLMS_CONNECTION_STATE_DLMS;
//ParseApplicationAssociationResponse failed. Server to Client do not match.
return DLMS_ERROR_CODE_AUTHENTICATION_FAILURE;
}
settings->connected |= DLMS_CONNECTION_STATE_DLMS;
return 0;
}
int cl_getObjectsRequest(dlmsSettings* settings, message* messages)
{
int ret;
if (settings->useLogicalNameReferencing)
{
static const unsigned char ln[] = { 0, 0, 40, 0, 0, 0xFF };
ret = cl_readLN(settings, ln, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, 2, NULL, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_readSN(settings, 0xFA00, 2, NULL, messages);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
return ret;
}
#ifndef DLMS_IGNORE_MALLOC
int cl_parseObjects(dlmsSettings* settings, gxByteBuffer* data)
{
int ret;
oa_clear(&settings->objects, 0);
oa_clear(&settings->releasedObjects, 1);
if (settings->useLogicalNameReferencing)
{
#ifndef DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
ret = cosem_parseLNObjects(settings, data, &settings->objects);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif // DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cosem_parseSNObjects(settings, data, &settings->objects);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif // DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
return ret;
}
#endif //DLMS_IGNORE_MALLOC
int cl_parseObjectCount(
gxByteBuffer* data,
uint16_t* count)
{
unsigned char ch;
int ret;
//Get array tag.
ret = bb_getUInt8(data, &ch);
if (ret != DLMS_ERROR_CODE_OK)
{
return ret;
}
//Check that data is in the array
if (ch != DLMS_DATA_TYPE_ARRAY)
{
return DLMS_ERROR_CODE_INVALID_RESPONSE;
}
//get object count
if (hlp_getObjectCount2(data, count) != 0)
{
return DLMS_ERROR_CODE_OUTOFMEMORY;
}
return ret;
}
int cl_parseNextObject(
dlmsSettings* settings,
gxByteBuffer* data,
gxObject* object)
{
int ret = 0;
uint16_t size, capacity = sizeof(object->logicalName);
if (settings->useLogicalNameReferencing)
{
uint16_t pos;
signed char id;
unsigned char mode;
dlmsVARIANT selector;
if ((ret = cosem_checkStructure(data, 4)) == 0 &&
(ret = cosem_getUInt16(data, &object->objectType)) == 0 &&
(ret = cosem_getUInt8(data, &object->version)) == 0 &&
(ret = cosem_getOctetString2(data, object->logicalName, capacity, &size)) == 0 &&
(ret = cosem_checkStructure(data, 2)) == 0)
{
size = 0xFFFF;
if ((ret = cosem_checkArray(data, &size)) == 0)
{
var_init(&selector);
//Get access modes.
for (pos = 0; pos != size; ++pos)
{
if ((ret = cosem_checkStructure(data, 3)) != 0 ||
(ret = cosem_getInt8(data, &id)) != 0 ||
(ret = cosem_getEnum(data, &mode)) != 0 ||
(ret = cosem_getVariant(data, &selector)) != 0)
{
break;
}
//Save access mode if user wants to know it.
if (object->access != NULL)
{
if ((ret = bb_setUInt8ByIndex(&object->access->attributeAccessModes, pos, mode)) != 0)
{
break;
}
}
}
//Get action access modes.
if (ret == 0)
{
size = 0xFFFF;
if ((ret = cosem_checkArray(data, &size)) == 0)
{
for (pos = 0; pos != size; ++pos)
{
if ((ret = cosem_checkStructure(data, 2)) != 0 ||
(ret = cosem_getInt8(data, &id)) != 0)
{
break;
}
if ((ret = bb_getUInt8(data, &mode)) != 0)
{
return ret;
}
//Version 0 uses boolean and other versions uses enum.
if (mode != DLMS_DATA_TYPE_ENUM && mode != DLMS_DATA_TYPE_BOOLEAN)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
if ((ret = bb_getUInt8(data, &mode)) != 0)
{
return ret;
}
//Save action access mode if user wants to know it.
if (object->access != NULL)
{
if ((ret = bb_setUInt8ByIndex(&object->access->methodAccessModes, pos, mode)) != 0)
{
break;
}
}
}
}
}
}
}
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
if ((ret = cosem_checkStructure(data, 4)) == 0 &&
(ret = cosem_getInt16(data, (int16_t*)&object->shortName)) == 0 &&
(ret = cosem_getUInt16(data, &object->objectType)) == 0 &&
(ret = cosem_getUInt8(data, &object->version)) == 0 &&
(ret = cosem_getOctetString2(data, object->logicalName, capacity, &size)) == 0)
{
}
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif // DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
return ret;
}
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
int cl_readSN(
dlmsSettings* settings,
uint16_t address,
unsigned char attributeOrdinal,
gxByteBuffer* data,
message* messages)
{
int ret;
DLMS_VARIABLE_ACCESS_SPECIFICATION requestType;
gxSNParameters p;
gxByteBuffer attributeDescriptor;
if ((attributeOrdinal < 1))
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
BYTE_BUFFER_INIT(&attributeDescriptor);
resetBlockIndex(settings);
address += (attributeOrdinal - 1) * 8;
bb_setUInt16(&attributeDescriptor, address);
// Add Selector.
if (data != NULL && data->size != 0)
{
requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_PARAMETERISED_ACCESS;
}
else
{
requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_VARIABLE_NAME;
}
params_initSN(&p, settings, DLMS_COMMAND_READ_REQUEST, 1,
requestType, &attributeDescriptor, data, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, messages);
bb_clear(&attributeDescriptor);
return ret;
}
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
int cl_readLN(
dlmsSettings* settings,
const unsigned char* name,
DLMS_OBJECT_TYPE objectType,
unsigned char attributeOrdinal,
gxByteBuffer* data,
message* messages)
{
int ret;
gxLNParameters p;
gxByteBuffer* pdu;
if ((attributeOrdinal < 1))
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
#ifdef DLMS_IGNORE_MALLOC
if (settings->serializedPdu == NULL)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
pdu = settings->serializedPdu;
bb_clear(pdu);
#else
gxByteBuffer attributeDescriptor;
BYTE_BUFFER_INIT(&attributeDescriptor);
pdu = &attributeDescriptor;
#endif //DLMS_IGNORE_MALLOC
resetBlockIndex(settings);
// CI
if ((ret = bb_setUInt16(pdu, objectType)) == 0 &&
// Add LN
(ret = bb_set(pdu, name, 6)) == 0 &&
// Attribute ID.
(ret = bb_setUInt8(pdu, attributeOrdinal)) == 0)
{
if (data == NULL || data->size == 0)
{
// Access selection is not used.
ret = bb_setUInt8(pdu, 0);
}
else
{
// Access selection is used.
if ((ret = bb_setUInt8(pdu, 1)) == 0)
{
// Add data.
ret = bb_set2(pdu, data, 0, data->size);
}
}
}
if (ret == 0)
{
params_initLN(&p, settings, 0,
DLMS_COMMAND_GET_REQUEST, DLMS_GET_COMMAND_TYPE_NORMAL,
pdu, data, 0xFF, DLMS_COMMAND_NONE, 0, 0);
ret = dlms_getLnMessages(&p, messages);
}
bb_clear(pdu);
return ret;
}
int cl_readList(
dlmsSettings* settings,
gxArray* list,
message* reply)
{
gxListItem* it;
gxObject* obj;
uint16_t pos = 0, count;
int ret;
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
uint16_t sn;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
gxByteBuffer bb;
if (list->size == 0)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
BYTE_BUFFER_INIT(&bb);
resetBlockIndex(settings);
if (settings->useLogicalNameReferencing)
{
gxLNParameters p;
params_initLN(&p, settings, 0, DLMS_COMMAND_GET_REQUEST, DLMS_GET_COMMAND_TYPE_WITH_LIST,
&bb, NULL, 0xff, DLMS_COMMAND_NONE, 0, 0);
//Request service primitive shall always fit in a single APDU.
count = (settings->maxPduSize - 12) / 10;
if (list->size < count)
{
count = list->size;
}
//All meters can handle 10 items.
if (count > 10)
{
count = 10;
}
// Add length.
hlp_setObjectCount(count, &bb);
for (pos = 0; pos != list->size; )
{
#ifdef DLMS_IGNORE_MALLOC
if ((ret = arr_getByIndex(list, pos, (void**)&it, sizeof(gxObject*))) != 0)
{
break;
}
#else
if ((ret = arr_getByIndex(list, pos, (void**)&it)) != 0)
{
break;
}
#endif //DLMS_IGNORE_MALLOC
obj = (gxObject*)it->key;
// CI.
bb_setUInt16(&bb, obj->objectType);
bb_set(&bb, obj->logicalName, 6);
// Attribute ID.
bb_setUInt8(&bb, it->value);
// Attribute selector is not used.
bb_setUInt8(&bb, 0);
++pos;
if (pos % count == 0 && list->size != pos)
{
if ((ret = dlms_getLnMessages(&p, reply)) != 0)
{
return ret;
}
bb_clear(&bb);
if (list->size - pos < count)
{
hlp_setObjectCount(list->size - pos, &bb);
}
else
{
hlp_setObjectCount(count, &bb);
}
}
}
ret = dlms_getLnMessages(&p, reply);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
gxSNParameters p;
for (pos = 0; pos != list->size; ++pos)
{
#ifdef DLMS_IGNORE_MALLOC
if ((ret = arr_getByIndex(list, pos, (void**)&it, sizeof(gxListItem))) != 0)
{
return ret;
}
#else
if ((ret = arr_getByIndex(list, pos, (void**)&it)) != 0)
{
return ret;
}
#endif //DLMS_IGNORE_MALLOC
// Add variable type.
bb_setUInt8(&bb, 2);
sn = ((gxObject*)it->key)->shortName;
sn += ((uint16_t)it->value - 1) * 8;
bb_setUInt16(&bb, sn);
}
params_initSN(&p, settings, DLMS_COMMAND_READ_REQUEST, list->size, 0xFF, &bb, NULL, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, reply);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
bb_clear(&bb);
return ret;
}
int cl_read(
dlmsSettings* settings,
gxObject* object,
unsigned char attributeOrdinal,
message* messages)
{
int ret;
if (object == NULL)
{
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
}
else if (settings->useLogicalNameReferencing)
{
ret = cl_readLN(settings, object->logicalName, object->objectType, attributeOrdinal, NULL, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_readSN(settings, object->shortName, attributeOrdinal, NULL, messages);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
return ret;
}
int cl_getKeepAlive(
dlmsSettings* settings,
message* messages)
{
int ret;
if (settings->useLogicalNameReferencing)
{
static const unsigned char ln[6] = { 0, 0, 40, 0, 0, 255 };
ret = cl_readLN(settings, ln, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, 1, NULL, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_readSN(settings, 0xFA00, 1, NULL, messages);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
return ret;
}
#ifndef DLMS_IGNORE_PROFILE_GENERIC
int cl_readRowsByEntry(dlmsSettings* settings, gxProfileGeneric* object, uint32_t index, uint32_t count, message* messages)
{
return cl_readRowsByEntry2(settings, object, index, count, 1, 0, messages);
}
int cl_readRowsByEntry2(dlmsSettings* settings, gxProfileGeneric* object, uint32_t index, uint32_t count, uint16_t colStart, uint16_t colEnd, message* messages)
{
int ret;
gxByteBuffer data;
if (object == NULL)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
#ifdef DLMS_IGNORE_MALLOC
unsigned char buff[20];
bb_attach(&data, buff, 0, sizeof(buff));
#else
BYTE_BUFFER_INIT(&data);
#endif //DLMS_IGNORE_MALLOC
//Add AccessSelector
if ((ret = bb_setUInt8(&data, 2)) == 0 &&
//Add structure tag.
(ret = cosem_setStructure(&data, 4)) == 0 &&
//Add start index
(ret = cosem_setUInt32(&data, index)) == 0 &&
//Add Count
(ret = cosem_setUInt32(&data, count == 0 ? 0 : index + count - 1)) == 0 &&
//Add columns.
(ret = cosem_setUInt16(&data, colStart)) == 0 &&
(ret = cosem_setUInt16(&data, colEnd)) == 0)
{
if (settings->useLogicalNameReferencing)
{
ret = cl_readLN(settings, object->base.logicalName, object->base.objectType, 2, &data, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_readSN(settings, object->base.shortName, 2, &data, messages);
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
}
bb_clear(&data);
return ret;
}
int cl_readRowsByRange2(
dlmsSettings* settings,
gxProfileGeneric* object,
gxtime* start,
gxtime* end,
message* messages)
{
unsigned char unixTime = 0;
static unsigned char LN[] = { 0, 0, 1, 0, 0, 255 };
DLMS_OBJECT_TYPE type = DLMS_OBJECT_TYPE_CLOCK;
unsigned char* ln = LN;
int ret;
gxByteBuffer data;
if (object == NULL || start == NULL || end == NULL || messages == NULL)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
if (object->captureObjects.size != 0)
{
#if defined(DLMS_IGNORE_MALLOC) || defined(DLMS_COSEM_EXACT_DATA_TYPES)
gxTarget* kv;
ret = arr_getByIndex2(&object->captureObjects, 0, (void**)&kv, sizeof(gxTarget));
if (ret != 0)
{
return ret;
}
#ifndef DLMS_IGNORE_OBJECT_POINTERS
type = kv->target->objectType;
ln = kv->target->logicalName;
#else
type = kv->objectType;
ln = kv->logicalName;
#endif //DLMS_IGNORE_OBJECT_POINTERS
#else
gxKey* kv;
ret = arr_getByIndex(&object->captureObjects, 0, (void**)&kv);
if (ret != 0)
{
return ret;
}
type = ((gxObject*)kv->key)->objectType;
ln = ((gxObject*)kv->key)->logicalName;
#endif //DLMS_IGNORE_MALLOC
unixTime = type == DLMS_OBJECT_TYPE_DATA;
}
#ifndef DLMS_IGNORE_MALLOC
if ((ret = obj_clearProfileGenericBuffer(&object->buffer)) != 0)
{
return ret;
}
#endif //DLMS_IGNORE_MALLOC
#ifdef DLMS_IGNORE_MALLOC
unsigned char buff[100];
bb_attach(&data, buff, 0, sizeof(buff));
#else
BYTE_BUFFER_INIT(&data);
#endif //DLMS_IGNORE_MALLOC
//Add AccessSelector
if ((ret = bb_setUInt8(&data, 1)) == 0 &&
//Add structure tag.
(ret = cosem_setStructure(&data, 4)) == 0 &&
//Add structure tag.
(ret = cosem_setStructure(&data, 4)) == 0 &&
// Add class_id
(ret = cosem_setUInt16(&data, type)) == 0 &&
// Add parameter Logical name
(ret = cosem_setOctetString2(&data, ln, 6)) == 0 &&
//Add attribute index.
(ret = cosem_setInt8(&data, 2)) == 0 &&
//Add data index.
(ret = cosem_setUInt16(&data, 0)) == 0)
{
//Add start time
if (unixTime)
{
ret = cosem_setUInt32(&data, time_toUnixTime2(start));
}
else
{
if ((ret = cosem_setDateTimeAsOctetString(&data, start)) != 0)
{
bb_clear(&data);
return ret;
}
}
//Add end time
if (unixTime)
{
ret = cosem_setUInt32(&data, time_toUnixTime2(end));
}
else
{
if ((ret = cosem_setDateTimeAsOctetString(&data, end)) != 0)
{
bb_clear(&data);
return ret;
}
}
//Add array of read columns. Read All...
bb_setUInt8(&data, 0x01);
//Add item count
bb_setUInt8(&data, 0x00);
if (settings->useLogicalNameReferencing)
{
ret = cl_readLN(settings, object->base.logicalName, object->base.objectType, 2, &data, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_readSN(settings, object->base.shortName, 2, &data, messages);
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
}
bb_clear(&data);
return ret;
}
#ifdef DLMS_INDONESIA_STANDARD
int cl_readRowsByRange3(
dlmsSettings* settings,
gxProfileGeneric* object,
gxtime* start,
gxtime* end,
unsigned char startRegister,
unsigned char numberOfRegisters,
message* messages)
{
int ret;
gxByteBuffer data;
if (start == NULL || end == NULL || messages == NULL)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
#ifdef DLMS_IGNORE_MALLOC
unsigned char buff[100];
bb_attach(&data, buff, 0, sizeof(buff));
#else
BYTE_BUFFER_INIT(&data);
#endif //DLMS_IGNORE_MALLOC
//Add AccessSelector
if ((ret = bb_setUInt8(&data, 1)) == 0 &&
//Add structure tag.
(ret = cosem_setStructure(&data, 4)) == 0)
{
//Add start time
if ((ret = cosem_setDateTimeAsOctetString(&data, start)) != 0)
{
bb_clear(&data);
return ret;
}
//Add end time
if ((ret = cosem_setDateTimeAsOctetString(&data, end)) != 0)
{
bb_clear(&data);
return ret;
}
//Add start register
bb_setUInt8(&data, startRegister);
//Add number of registers.
bb_setUInt8(&data, numberOfRegisters);
if (settings->useLogicalNameReferencing)
{
ret = cl_readLN(settings, object->base.logicalName, object->base.objectType, 2, &data, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_readSN(settings, object->base.shortName, 2, &data, messages);
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
}
bb_clear(&data);
return ret;
}
#endif //DLMS_INDONESIA_STANDARD
#ifdef DLMS_USE_EPOCH_TIME
int cl_readRowsByRange(
dlmsSettings* settings,
gxProfileGeneric* object,
uint32_t start,
uint32_t end,
message* messages)
#else
int cl_readRowsByRange(
dlmsSettings* settings,
gxProfileGeneric* object,
struct tm* start,
struct tm* end,
message* messages)
#endif //DLMS_USE_EPOCH_TIME
{
gxtime s, e;
#ifdef DLMS_USE_EPOCH_TIME
time_initUnix(&s, start);
time_initUnix(&e, end);
#else
time_initUnix(&s, time_toUnixTime(start));
time_initUnix(&e, time_toUnixTime(end));
#endif //DLMS_USE_EPOCH_TIME
return cl_readRowsByRange2(settings, object, &s, &e, messages);
}
#endif // DLMS_IGNORE_PROFILE_GENERIC
int cl_getData(dlmsSettings* settings, gxByteBuffer* reply, gxReplyData* data)
{
return dlms_getData2(settings, reply, data, 0);
}
int cl_getData2(
dlmsSettings* settings,
gxByteBuffer* reply,
gxReplyData* data,
gxReplyData* notify,
unsigned char* isNotify)
{
return dlms_getData3(settings, reply, data, notify, 0, isNotify);
}
int cl_changeType(
gxByteBuffer* value,
DLMS_DATA_TYPE type,
dlmsVARIANT* newValue)
{
return dlms_changeType(value, type, newValue);
}
int cl_updateValue(
dlmsSettings* settings,
gxObject* target,
unsigned char attributeIndex,
dlmsVARIANT* value)
{
gxValueEventArg e;
if (target == NULL)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
e.target = target;
e.index = attributeIndex;
e.value = *value;
return cosem_setValue(settings, &e);
}
int cl_updateValues(
dlmsSettings* settings,
gxArray* list,
gxByteBuffer* data)
{
uint16_t count;
int ret = 0;
uint16_t pos;
gxListItem* it;
gxDataInfo info;
unsigned char ch;
gxValueEventArg e;
ve_init(&e);
if ((ret = hlp_getObjectCount2(data, &count)) != 0)
{
return ret;
}
if (count != list->size)
{
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
for (pos = 0; pos != list->size; ++pos)
{
di_init(&info);
#ifdef DLMS_IGNORE_MALLOC
if ((ret = arr_getByIndex(list, pos, (void**)&it, sizeof(gxListItem))) != 0)
{
break;
}
#else
if ((ret = arr_getByIndex(list, pos, (void**)&it)) != 0)
{
break;
}
#endif //DLMS_IGNORE_MALLOC
ret = bb_getUInt8(data, &ch);
if (ret != 0)
{
break;
}
if (ch == 0)
{
#if defined(DLMS_IGNORE_MALLOC) || defined(DLMS_COSEM_EXACT_DATA_TYPES)
e.value.vt = DLMS_DATA_TYPE_BYREF | DLMS_DATA_TYPE_OCTET_STRING;
e.value.byteArr = data;
#else
if ((ret = dlms_getData(data, &info, &e.value)) != 0)
{
break;
}
#endif //defined(DLMS_IGNORE_MALLOC) || defined(DLMS_COSEM_EXACT_DATA_TYPES)
e.target = (gxObject*)it->key;
e.index = it->value;
ret = cosem_setValue(settings, &e);
ve_clear(&e);
if (ret != 0)
{
break;
}
}
else
{
ret = ch;
break;
}
}
ve_clear(&e);
return ret;
}
int cl_receiverReady(dlmsSettings* settings, DLMS_DATA_REQUEST_TYPES type, gxByteBuffer* reply)
{
return dlms_receiverReady(settings, type, reply);
}
/**
* Generates a release request.
*
* @return Release request, as byte array.
*/
int cl_releaseRequest(dlmsSettings* settings, message* packets)
{
return cl_releaseRequest2(settings, packets, 0);
}
/**
* Generates a release request.
*
* @return Release request, as byte array.
*/
int cl_releaseRequest2(dlmsSettings* settings, message* packets, unsigned char useProtectedRelease)
{
int ret;
gxByteBuffer bb;
mes_clear(packets);
// If connection is not established, there is no need to send
// DisconnectRequest.
if ((settings->connected & DLMS_CONNECTION_STATE_DLMS) == 0)
{
return 0;
}
settings->connected &= ~DLMS_CONNECTION_STATE_DLMS;
BYTE_BUFFER_INIT(&bb);
if (!useProtectedRelease)
{
if ((ret = bb_setUInt8(&bb, 0x3)) != 0 ||
(ret = bb_setUInt8(&bb, 0x80)) != 0 ||
(ret = bb_setUInt8(&bb, 0x01)) != 0 ||
(ret = bb_setUInt8(&bb, 0x0)) != 0)
{
return ret;
}
}
else
{
// Length.
if ((ret = bb_setUInt8(&bb, 0x0)) != 0 ||
(ret = bb_setUInt8(&bb, 0x80)) != 0 ||
(ret = bb_setUInt8(&bb, 0x01)) != 0 ||
(ret = bb_setUInt8(&bb, 0x0)) != 0)
{
return ret;
}
apdu_generateUserInformation(settings, &bb);
bb.data[0] = (unsigned char)(bb.size - 1);
}
if (settings->useLogicalNameReferencing)
{
gxLNParameters p;
params_initLN(&p, settings, 0,
DLMS_COMMAND_RELEASE_REQUEST, DLMS_SET_COMMAND_TYPE_NORMAL,
&bb, NULL, 0xff, DLMS_COMMAND_NONE, 0, 0);
ret = dlms_getLnMessages(&p, packets);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
gxSNParameters p;
params_initSN(&p, settings,
DLMS_COMMAND_RELEASE_REQUEST, 1,
DLMS_VARIABLE_ACCESS_SPECIFICATION_VARIABLE_NAME,
NULL, &bb, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, packets);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
bb_clear(&bb);
//Restore default values.
settings->maxPduSize = settings->initializePduSize;
return ret;
}
int cl_disconnectRequest(dlmsSettings* settings, message* packets)
{
int ret = 0;
#ifndef DLMS_IGNORE_WRAPPER
gxByteBuffer bb;
#endif //DLMS_IGNORE_WRAPPER
gxByteBuffer* reply = NULL;
mes_clear(packets);
settings->maxPduSize = 0xFFFF;
// If connection is not established, there is no need to send DisconnectRequest.
if ((settings->connected & DLMS_CONNECTION_STATE_HDLC) == 0)
{
return ret;
}
settings->connected &= ~DLMS_CONNECTION_STATE_HDLC;
#ifdef DLMS_IGNORE_MALLOC
reply = packets->data[0];
++packets->size;
bb_clear(reply);
#else
reply = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
BYTE_BUFFER_INIT(reply);
#endif //DLMS_IGNORE_MALLOC
switch (settings->interfaceType)
{
#ifndef DLMS_IGNORE_HDLC
case DLMS_INTERFACE_TYPE_HDLC:
case DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E:
{
ret = dlms_getHdlcFrame(settings, DLMS_COMMAND_DISC, NULL, reply);
#ifndef DLMS_IGNORE_MALLOC
if (ret == 0)
{
mes_push(packets, reply);
}
else
{
gxfree(reply);
}
#endif //DLMS_IGNORE_MALLOC
}
break;
#endif //DLMS_IGNORE_HDLC
#ifndef DLMS_IGNORE_PLC
case DLMS_INTERFACE_TYPE_PLC:
#ifndef DLMS_IGNORE_MALLOC
gxfree(reply);
#endif //DLMS_IGNORE_MALLOC
break;
case DLMS_INTERFACE_TYPE_PLC_HDLC:
{
ret = dlms_getMacHdlcFrame(settings, DLMS_COMMAND_DISC, 0, NULL, reply);
#ifndef DLMS_IGNORE_MALLOC
if (ret == 0)
{
mes_push(packets, reply);
}
else
{
gxfree(reply);
}
#endif //DLMS_IGNORE_MALLOC
}
break;
#endif //DLMS_IGNORE_PLC
#ifndef DLMS_IGNORE_WRAPPER
case DLMS_INTERFACE_TYPE_WRAPPER:
{
BYTE_BUFFER_INIT(&bb);
bb_setUInt8(&bb, DLMS_COMMAND_RELEASE_REQUEST);
bb_setUInt8(&bb, 0x0);
ret = dlms_getWrapperFrame(settings, DLMS_COMMAND_NONE, &bb, reply);
#ifndef DLMS_IGNORE_MALLOC
if (ret == 0)
{
mes_push(packets, reply);
}
else
{
gxfree(reply);
}
#endif //DLMS_IGNORE_MALLOC
bb_clear(&bb);
}
break;
#endif //DLMS_IGNORE_WRAPPER
default:
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
break;
}
#ifndef DLMS_IGNORE_HDLC
if (dlms_useHdlc(settings->interfaceType))
{
//Restore default HDLC values.
settings->maxInfoTX = settings->initializeMaxInfoTX;
settings->maxInfoRX = settings->initializeMaxInfoRX;
settings->windowSizeTX = settings->initializeWindowSizeTX;
settings->windowSizeRX = settings->initializeWindowSizeRX;
}
#endif //DLMS_IGNORE_HDLC
//Restore default values.
settings->maxPduSize = settings->initializePduSize;
resetFrameSequence(settings);
return ret;
}
int cl_write(
dlmsSettings* settings,
gxObject* object,
unsigned char index,
message* messages)
{
unsigned int ret;
gxValueEventArg e;
ve_init(&e);
e.target = object;
e.index = index;
if ((ret = cosem_getValue(settings, &e)) != 0)
{
return ret;
}
if (settings->useLogicalNameReferencing)
{
ret = cl_writeLN(
settings,
object->logicalName,
object->objectType,
index,
&e.value,
e.byteArray,
messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_writeSN(
settings,
object->shortName,
index,
&e.value,
messages);
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
ve_clear(&e);
return ret;
}
int cl_writeList(
dlmsSettings* settings,
gxArray* list,
message* reply)
{
gxListItem* it;
uint16_t pos;
gxValueEventArg e;
gxByteBuffer data;
ve_init(&e);
gxByteBuffer* pdu;
int ret;
#ifdef DLMS_IGNORE_MALLOC
if (settings->serializedPdu == NULL)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
pdu = settings->serializedPdu;
//Use same buffer for header and data. Header size is 10 bytes.
BYTE_BUFFER_INIT(&data);
bb_clear(pdu);
#else
gxByteBuffer bb;
BYTE_BUFFER_INIT(&bb);
pdu = &bb;
BYTE_BUFFER_INIT(&data);
#endif //DLMS_IGNORE_MALLOC
if (list->size == 0)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
resetBlockIndex(settings);
if (settings->useLogicalNameReferencing)
{
gxLNParameters p;
params_initLN(&p, settings, 0, DLMS_COMMAND_SET_REQUEST, DLMS_SET_COMMAND_TYPE_WITH_LIST,
pdu, &data, 0xff, DLMS_COMMAND_NONE, 0, 0);
// Add length.
hlp_setObjectCount(list->size, pdu);
for (pos = 0; pos != list->size; ++pos)
{
#ifdef DLMS_IGNORE_MALLOC
if ((ret = arr_getByIndex(list, pos, (void**)&it, sizeof(gxListItem*))) != 0)
{
break;
}
#else
if ((ret = arr_getByIndex(list, pos, (void**)&it)) != 0)
{
break;
}
#endif //DLMS_IGNORE_MALLOC
// CI.
bb_setUInt16(pdu, it->key->objectType);
bb_set(pdu, it->key->logicalName, 6);
// Attribute ID.
bb_setUInt8(pdu, it->value);
// Attribute selector is not used.
bb_setUInt8(pdu, 0);
}
// Add length.
if (ret == 0)
{
hlp_setObjectCount(list->size, pdu);
for (pos = 0; pos != list->size; ++pos)
{
#ifdef DLMS_IGNORE_MALLOC
if ((ret = arr_getByIndex(list, pos, (void**)&it, sizeof(gxListItem*))) != 0)
{
break;
}
#else
if ((ret = arr_getByIndex(list, pos, (void**)&it)) != 0)
{
break;
}
#endif //DLMS_IGNORE_MALLOC
e.target = it->key;
e.index = it->value;
if ((ret = cosem_getValue(settings, &e)) != 0)
{
break;
}
#ifdef DLMS_IGNORE_MALLOC
if (e.byteArray != 0)
{
bb_set2(pdu, e.value.byteArr, 0, bb_size(e.value.byteArr));
}
else
{
if ((ret = dlms_setData(pdu, e.value.vt, &e.value)) != 0)
{
return ret;
}
}
#else
if (e.byteArray != 0)
{
bb_set2(&data, e.value.byteArr, 0, e.value.byteArr->size);
}
else
{
if ((ret = dlms_setData(&data, e.value.vt, &e.value)) != 0)
{
return ret;
}
}
#endif //DLMS_IGNORE_MALLOC
}
if (ret == 0)
{
ret = dlms_getLnMessages(&p, reply);
}
}
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
uint16_t sn;
gxSNParameters p;
for (pos = 0; pos != list->size; ++pos)
{
#ifdef DLMS_IGNORE_MALLOC
if ((ret = arr_getByIndex(list, pos, (void**)&it, sizeof(gxListItem))) != 0)
{
break;
}
#else
if ((ret = arr_getByIndex(list, pos, (void**)&it)) != 0)
{
break;
}
#endif //DLMS_IGNORE_MALLOC
// Add variable type.
bb_setUInt8(pdu, 2);
sn = ((gxObject*)it->key)->shortName;
sn += ((uint16_t)it->value - 1) * 8;
bb_setUInt16(pdu, sn);
}
if (ret == 0)
{
// Add length.
hlp_setObjectCount(list->size, pdu);
for (pos = 0; pos != list->size; ++pos)
{
#ifdef DLMS_IGNORE_MALLOC
if ((ret = arr_getByIndex(list, pos, (void**)&it, sizeof(gxListItem*))) != 0)
{
break;
}
#else
if ((ret = arr_getByIndex(list, pos, (void**)&it)) != 0)
{
break;
}
#endif //DLMS_IGNORE_MALLOC
e.target = it->key;
e.index = it->value;
if ((ret = cosem_getValue(settings, &e)) != 0)
{
break;
}
#ifdef DLMS_IGNORE_MALLOC
if (e.byteArray != 0)
{
bb_set2(pdu, value->byteArr, 0, value->byteArr->size);
}
else
{
if ((ret = dlms_setData(pdu, value->vt, value)) != 0)
{
break;
}
}
#else
if (e.byteArray != 0)
{
bb_set2(&data, e.value.byteArr, 0, e.value.byteArr->size);
}
else
{
if ((ret = dlms_setData(&data, e.value.vt, &e.value)) != 0)
{
break;
}
}
}
#endif //DLMS_IGNORE_MALLOC
if (ret == 0)
{
params_initSN(&p, settings, DLMS_COMMAND_WRITE_REQUEST, list->size, 0xFF, pdu, &data, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, reply);
}
}
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
ve_clear(&e);
bb_clear(pdu);
bb_clear(&data);
return ret;
}
int cl_writeLN(
dlmsSettings* settings,
const unsigned char* name,
DLMS_OBJECT_TYPE objectType,
unsigned char index,
dlmsVARIANT* value,
unsigned char byteArray,
message* messages)
{
int ret;
gxLNParameters p;
gxByteBuffer* pdu;
gxByteBuffer data;
if (index < 1)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
#ifdef DLMS_IGNORE_MALLOC
if (settings->serializedPdu == NULL)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
pdu = settings->serializedPdu;
//Use same buffer for header and data. Header size is 10 bytes.
BYTE_BUFFER_INIT(&data);
bb_clear(pdu);
#else
gxByteBuffer bb;
BYTE_BUFFER_INIT(&bb);
pdu = &bb;
BYTE_BUFFER_INIT(&data);
#endif //DLMS_IGNORE_MALLOC
resetBlockIndex(settings);
// Add CI.
bb_setUInt16(pdu, objectType);
// Add LN
bb_set(pdu, name, 6);
// Attribute ID.
bb_setUInt8(pdu, index);
// Access selection is not used.
bb_setUInt8(pdu, 0);
#ifdef DLMS_IGNORE_MALLOC
if (byteArray != 0)
{
bb_set2(pdu, value->byteArr, 0, value->byteArr->size);
}
else
{
if ((ret = dlms_setData(pdu, value->vt, value)) != 0)
{
return ret;
}
}
#else
if (byteArray != 0)
{
bb_set2(&data, value->byteArr, 0, value->byteArr->size);
}
else
{
if ((ret = dlms_setData(&data, value->vt, value)) != 0)
{
return ret;
}
}
#endif //DLMS_IGNORE_MALLOC
params_initLN(&p, settings, 0,
DLMS_COMMAND_SET_REQUEST, DLMS_SET_COMMAND_TYPE_NORMAL,
pdu, &data, 0xff, DLMS_COMMAND_NONE, 0, 0);
ret = dlms_getLnMessages(&p, messages);
bb_clear(&data);
bb_clear(pdu);
return ret;
}
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
int cl_writeSN(
dlmsSettings* settings,
uint16_t address,
int index,
dlmsVARIANT* value,
message* messages)
{
int ret;
gxSNParameters p;
gxByteBuffer bb, data;
if (index < 1)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
resetBlockIndex(settings);
BYTE_BUFFER_INIT(&bb);
BYTE_BUFFER_INIT(&data);
if ((ret = dlms_setData(&data, value->vt, value)) != 0)
{
return ret;
}
// Add name.
address += (uint16_t)((index - 1) * 8);
bb_setUInt16(&bb, address);
// Add data count.
bb_setUInt8(&bb, 1);
params_initSN(&p, settings,
DLMS_COMMAND_WRITE_REQUEST, 1,
DLMS_VARIABLE_ACCESS_SPECIFICATION_VARIABLE_NAME,
&bb, &data, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, messages);
bb_clear(&data);
bb_clear(&bb);
return ret;
}
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
int cl_method(
dlmsSettings* settings,
gxObject* object,
unsigned char index,
dlmsVARIANT* data,
message* messages)
{
int ret;
if (settings->useLogicalNameReferencing)
{
ret = cl_methodLN(settings, object->logicalName, object->objectType, index, data, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_methodSN(settings, object->shortName, object->objectType, index, data, messages);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
return ret;
}
int cl_method2(
dlmsSettings* settings,
gxObject* object,
unsigned char index,
unsigned char* value,
uint32_t length,
message* messages)
{
int ret;
if (settings->useLogicalNameReferencing)
{
ret = cl_methodLN2(settings, object->logicalName, object->objectType, index, value, length, messages);
}
else
{
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
ret = cl_methodSN2(settings, object->shortName, object->objectType, index, value, length, messages);
#else
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
}
return ret;
}
int cl_methodLN(
dlmsSettings* settings,
const unsigned char* name,
DLMS_OBJECT_TYPE objectType,
unsigned char index,
dlmsVARIANT* value,
message* messages)
{
int ret = 0;
gxLNParameters p;
gxByteBuffer* pdu;
gxByteBuffer data;
if (index < 1)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
#ifdef DLMS_IGNORE_MALLOC
if (settings->serializedPdu == NULL)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
pdu = settings->serializedPdu;
//Use same buffer for header and data. Header size is 10 bytes.
BYTE_BUFFER_INIT(&data);
bb_clear(pdu);
#else
gxByteBuffer bb;
unsigned char GX_METHOD_PDU[10];
bb_attach(&bb, GX_METHOD_PDU, 0, sizeof(GX_METHOD_PDU));
pdu = &bb;
BYTE_BUFFER_INIT(&data);
#endif //DLMS_IGNORE_MALLOC
resetBlockIndex(settings);
// CI
if ((ret = bb_setUInt16(pdu, objectType)) == 0 &&
// Add LN
(ret = bb_set(pdu, name, 6)) == 0 &&
// Attribute ID.
(ret = bb_setUInt8(pdu, index)) == 0 &&
// Is Method Invocation Parameters used.
(ret = bb_setUInt8(pdu, value == NULL || value->vt == DLMS_DATA_TYPE_NONE ? 0 : 1)) == 0)
{
#ifdef DLMS_IGNORE_MALLOC
if (value != NULL && value->vt != DLMS_DATA_TYPE_NONE)
{
if (value->vt == DLMS_DATA_TYPE_OCTET_STRING)
{
ret = bb_set(pdu, value->byteArr->data, value->byteArr->size);
}
else
{
ret = dlms_setData(pdu, value->vt, value);
}
}
#else
if (value != NULL && value->vt != DLMS_DATA_TYPE_NONE)
{
if ((value->vt == DLMS_DATA_TYPE_ARRAY || value->vt == DLMS_DATA_TYPE_STRUCTURE) &&
value->vt == DLMS_DATA_TYPE_OCTET_STRING)
{
ret = bb_set(&data, value->byteArr->data, value->byteArr->size);
}
else
{
if (value->vt == DLMS_DATA_TYPE_OCTET_STRING)
{
//Space is allocated for type and size
bb_capacity(&data, 5 + bb_size(value->byteArr));
}
ret = dlms_setData(&data, value->vt, value);
}
}
#endif //DLMS_IGNORE_MALLOC
}
if (ret == 0)
{
params_initLN(&p, settings, 0,
DLMS_COMMAND_METHOD_REQUEST, DLMS_ACTION_COMMAND_TYPE_NORMAL,
pdu, &data, 0xff, DLMS_COMMAND_NONE, 0, 0);
ret = dlms_getLnMessages(&p, messages);
}
bb_clear(&data);
bb_clear(pdu);
return ret;
}
int cl_methodLN2(
dlmsSettings* settings,
unsigned char* name,
DLMS_OBJECT_TYPE objectType,
unsigned char index,
unsigned char* value,
uint32_t length,
message* messages)
{
int ret = 0;
gxLNParameters p;
gxByteBuffer* pdu;
gxByteBuffer data;
if (index < 1)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
#ifdef DLMS_IGNORE_MALLOC
if (settings->serializedPdu == NULL)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
pdu = settings->serializedPdu;
//Use same buffer for header and data. Header size is 10 bytes.
BYTE_BUFFER_INIT(&data);
bb_clear(pdu);
#else
gxByteBuffer bb;
BYTE_BUFFER_INIT(&bb);
pdu = &bb;
BYTE_BUFFER_INIT(&data);
#endif //DLMS_IGNORE_MALLOC
resetBlockIndex(settings);
// CI
if ((ret = bb_setUInt16(pdu, objectType)) == 0 &&
// Add LN
(ret = bb_set(pdu, name, 6)) == 0 &&
// Attribute ID.
(ret = bb_setUInt8(pdu, index)) == 0 &&
// Is Method Invocation Parameters used.
(ret = bb_setUInt8(pdu, 1)) == 0)
{
ret = bb_set(pdu, value, length);
}
if (ret == 0)
{
params_initLN(&p, settings, 0,
DLMS_COMMAND_METHOD_REQUEST, DLMS_ACTION_COMMAND_TYPE_NORMAL,
pdu, &data, 0xff, DLMS_COMMAND_NONE, 0, 0);
ret = dlms_getLnMessages(&p, messages);
}
bb_clear(&data);
bb_clear(pdu);
return ret;
}
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
int cl_methodSN(
dlmsSettings* settings,
uint16_t address,
DLMS_OBJECT_TYPE objectType,
int index,
dlmsVARIANT* value,
message* messages)
{
int ret;
unsigned char v, count;
gxSNParameters p;
unsigned char requestType;
gxByteBuffer bb, data;
if (index < 1)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
resetBlockIndex(settings);
BYTE_BUFFER_INIT(&data);
if (value != NULL && value->vt != DLMS_DATA_TYPE_NONE)
{
if (value->vt == DLMS_DATA_TYPE_OCTET_STRING)
{
bb_set(&data, value->byteArr->data, value->byteArr->size);
}
else
{
dlms_setData(&data, value->vt, value);
}
}
BYTE_BUFFER_INIT(&bb);
if (value == NULL || value->vt == DLMS_DATA_TYPE_NONE)
{
requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_VARIABLE_NAME;
}
else
{
requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_PARAMETERISED_ACCESS;
}
if ((ret = dlms_getActionInfo(objectType, &v, &count)) != 0)
{
return ret;
}
if (index > count)
{
//Invalid parameter
bb_clear(&data);
bb_clear(&bb);
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
index = (v + (index - 1) * 0x8);
address += (uint16_t)index;
// Add SN count.
bb_setUInt8(&bb, 1);
// Add name length.
bb_setUInt8(&bb, 4);
// Add name.
bb_setUInt16(&bb, address);
// Method Invocation Parameters is not used.
if (value == NULL || value->vt == DLMS_DATA_TYPE_NONE)
{
bb_setUInt8(&bb, 0);
}
else
{
bb_setUInt8(&bb, 1);
}
params_initSN(&p, settings, DLMS_COMMAND_READ_REQUEST, 1,
requestType, &bb, &data, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, messages);
bb_clear(&data);
bb_clear(&bb);
return ret;
}
int cl_methodSN2(
dlmsSettings* settings,
uint16_t address,
DLMS_OBJECT_TYPE objectType,
int index,
unsigned char* value,
uint32_t length,
message* messages)
{
int ret;
unsigned char v, count;
gxSNParameters p;
gxByteBuffer bb, data;
if (index < 1)
{
//Invalid parameter
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
resetBlockIndex(settings);
BYTE_BUFFER_INIT(&data);
ret = bb_set(&data, value, length);
BYTE_BUFFER_INIT(&bb);
if ((ret = dlms_getActionInfo(objectType, &v, &count)) != 0)
{
return ret;
}
if (index > count)
{
//Invalid parameter
bb_clear(&data);
bb_clear(&bb);
return DLMS_ERROR_CODE_INVALID_PARAMETER;
}
index = (v + (index - 1) * 0x8);
address += (uint16_t)index;
// Add SN count.
bb_setUInt8(&bb, 1);
// Add name length.
bb_setUInt8(&bb, 4);
// Add name.
bb_setUInt16(&bb, address);
bb_setUInt8(&bb, 1);
params_initSN(&p, settings, DLMS_COMMAND_READ_REQUEST, 1,
DLMS_VARIABLE_ACCESS_SPECIFICATION_PARAMETERISED_ACCESS, &bb, &data, DLMS_COMMAND_NONE);
ret = dlms_getSnMessages(&p, messages);
bb_clear(&data);
bb_clear(&bb);
return ret;
}
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
uint16_t cl_getServerAddress(uint16_t logicalAddress, uint16_t physicalAddress, unsigned char addressSize)
{
uint16_t value;
if (addressSize < 4 && physicalAddress < 0x80 && logicalAddress < 0x80)
{
value = (uint16_t)(logicalAddress << 7 | physicalAddress);
}
else if (physicalAddress < 0x4000 && logicalAddress < 0x4000)
{
value = (uint16_t)(logicalAddress << 14 | physicalAddress);
}
else
{
value = 0;
}
return value;
}
#endif //!defined(DLMS_IGNORE_CLIENT)