2252 lines
62 KiB
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)
|