6834 lines
186 KiB
C
6834 lines
186 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"
|
|
#include "serverevents.h"
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(__linux__)
|
|
#include <assert.h>
|
|
#endif
|
|
#include "gxmem.h"
|
|
#if _MSC_VER > 1400
|
|
#include <crtdbg.h>
|
|
#endif
|
|
#include <string.h> /* memset */
|
|
#include "enums.h"
|
|
#include "dlms.h"
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
#include "ciphering.h"
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
#include "crc.h"
|
|
#include "cosem.h"
|
|
#include "gxobjects.h"
|
|
#include "date.h"
|
|
#ifndef DLMS_IGNORE_HIGH_MD5
|
|
#include "gxmd5.h"
|
|
#endif //DLMS_IGNORE_HIGH_MD5
|
|
#ifndef DLMS_IGNORE_HIGH_SHA256
|
|
#include "gxsha256.h"
|
|
#endif //DLMS_IGNORE_HIGH_SHA256
|
|
#ifndef DLMS_IGNORE_HIGH_SHA1
|
|
#include "gxsha1.h"
|
|
#endif //DLMS_IGNORE_HIGH_SHA1
|
|
#ifndef DLMS_IGNORE_HIGH_AES
|
|
#include "gxaes.h"
|
|
#endif //DLMS_IGNORE_HIGH_AES
|
|
|
|
unsigned char pduAttributes[PDU_MAX_HEADER_SIZE];
|
|
static const unsigned char LLC_SEND_BYTES[3] = { 0xE6, 0xE6, 0x00 };
|
|
static const unsigned char LLC_REPLY_BYTES[3] = { 0xE6, 0xE7, 0x00 };
|
|
static const unsigned char HDLC_FRAME_START_END = 0x7E;
|
|
|
|
unsigned char dlms_useHdlc(DLMS_INTERFACE_TYPE type)
|
|
{
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
return type == DLMS_INTERFACE_TYPE_HDLC ||
|
|
type == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E ||
|
|
type == DLMS_INTERFACE_TYPE_PLC_HDLC;
|
|
#else
|
|
return 0;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
}
|
|
|
|
//Makes sure that the basic settings are set.
|
|
int dlms_checkInit(dlmsSettings* settings)
|
|
{
|
|
if (settings->clientAddress == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
if (settings->serverAddress == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS;
|
|
}
|
|
if (settings->maxPduSize < 64)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
unsigned char dlms_useDedicatedKey(dlmsSettings* settings)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
return settings->cipher.dedicatedKey != NULL;
|
|
#else
|
|
return memcmp(settings->cipher.dedicatedKey, EMPTY_KEY, 16) != 0;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
|
|
unsigned char dlms_usePreEstablishedConnection(dlmsSettings* settings)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
return settings->preEstablishedSystemTitle != NULL;
|
|
#else
|
|
return memcmp(settings->preEstablishedSystemTitle, EMPTY_SYSTEM_TITLE, 8) != 0;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
/**
|
|
* Get used glo message.
|
|
*
|
|
* @param command
|
|
* Executed DLMS command.
|
|
* @return Integer value of glo message.
|
|
*/
|
|
unsigned char dlms_getGloMessage(dlmsSettings* settings, DLMS_COMMAND command, DLMS_COMMAND encryptedCommand)
|
|
{
|
|
unsigned char cmd;
|
|
unsigned glo = settings->negotiatedConformance & DLMS_CONFORMANCE_GENERAL_PROTECTION ||
|
|
dlms_usePreEstablishedConnection(settings);
|
|
unsigned ded = dlms_useDedicatedKey(settings) && (settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0;
|
|
if (encryptedCommand == DLMS_COMMAND_GENERAL_GLO_CIPHERING ||
|
|
encryptedCommand == DLMS_COMMAND_GENERAL_DED_CIPHERING)
|
|
{
|
|
cmd = encryptedCommand;
|
|
}
|
|
else if (glo && encryptedCommand == DLMS_COMMAND_NONE)
|
|
{
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_GENERAL_DED_CIPHERING;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GENERAL_GLO_CIPHERING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (command)
|
|
{
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_READ_REQUEST:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_READ_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_READ_REQUEST;
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_GET_REQUEST:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_GET_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_GET_REQUEST;
|
|
}
|
|
break;
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_WRITE_REQUEST:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_WRITE_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_WRITE_REQUEST;
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_SET_REQUEST:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_SET_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_SET_REQUEST;
|
|
}
|
|
break;
|
|
case DLMS_COMMAND_METHOD_REQUEST:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_METHOD_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_METHOD_REQUEST;
|
|
}
|
|
break;
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_READ_RESPONSE:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_READ_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_READ_RESPONSE;
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_GET_RESPONSE:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_GET_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_GET_RESPONSE;
|
|
}
|
|
break;
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_WRITE_RESPONSE:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_WRITE_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_WRITE_RESPONSE;
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_SET_RESPONSE:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_SET_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_SET_RESPONSE;
|
|
}
|
|
break;
|
|
case DLMS_COMMAND_METHOD_RESPONSE:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_DED_METHOD_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_METHOD_RESPONSE;
|
|
}
|
|
break;
|
|
case DLMS_COMMAND_DATA_NOTIFICATION:
|
|
if (ded)
|
|
{
|
|
cmd = DLMS_COMMAND_GENERAL_DED_CIPHERING;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GENERAL_GLO_CIPHERING;
|
|
}
|
|
break;
|
|
case DLMS_COMMAND_RELEASE_REQUEST:
|
|
cmd = DLMS_COMMAND_RELEASE_REQUEST;
|
|
break;
|
|
case DLMS_COMMAND_RELEASE_RESPONSE:
|
|
cmd = DLMS_COMMAND_RELEASE_RESPONSE;
|
|
break;
|
|
case DLMS_COMMAND_GENERAL_DED_CIPHERING:
|
|
cmd = DLMS_COMMAND_GENERAL_DED_CIPHERING;
|
|
break;
|
|
case DLMS_COMMAND_GENERAL_GLO_CIPHERING:
|
|
cmd = DLMS_COMMAND_GENERAL_GLO_CIPHERING;
|
|
break;
|
|
default:
|
|
cmd = DLMS_COMMAND_NONE;
|
|
}
|
|
}
|
|
return cmd;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
|
|
unsigned char dlms_getInvokeIDPriority(dlmsSettings* settings, unsigned char increase)
|
|
{
|
|
unsigned char value = 0;
|
|
if (settings->priority == DLMS_PRIORITY_HIGH)
|
|
{
|
|
value |= 0x80;
|
|
}
|
|
if (settings->serviceClass == DLMS_SERVICE_CLASS_CONFIRMED)
|
|
{
|
|
value |= 0x40;
|
|
}
|
|
if (increase)
|
|
{
|
|
settings->invokeID = (unsigned char)((1 + settings->invokeID) & 0xF);
|
|
}
|
|
value |= settings->invokeID;
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Generates Invoke ID and priority.
|
|
*
|
|
* @param settings
|
|
* DLMS settings->
|
|
* @return Invoke ID and priority.
|
|
*/
|
|
int32_t dlms_getLongInvokeIDPriority(dlmsSettings* settings)
|
|
{
|
|
int32_t value = 0;
|
|
if (settings->priority == DLMS_PRIORITY_HIGH)
|
|
{
|
|
value = 0x80000000;
|
|
}
|
|
if (settings->serviceClass == DLMS_SERVICE_CLASS_CONFIRMED)
|
|
{
|
|
value |= 0x40000000;
|
|
}
|
|
value |= (settings->longInvokeID & 0xFFFFFF);
|
|
++settings->longInvokeID;
|
|
return value;
|
|
}
|
|
|
|
int dlms_getMaxPduSize(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* bb)
|
|
{
|
|
int offset = 0;
|
|
int size = data->size - data->position;
|
|
if (bb != NULL)
|
|
{
|
|
offset = bb->size;
|
|
}
|
|
if (size + offset > settings->maxPduSize)
|
|
{
|
|
size = settings->maxPduSize - offset;
|
|
size -= hlp_getObjectCountSizeInBytes(size);
|
|
}
|
|
else if (size + hlp_getObjectCountSizeInBytes(size) > settings->maxPduSize)
|
|
{
|
|
size = (size - hlp_getObjectCountSizeInBytes(size));
|
|
}
|
|
return size;
|
|
}
|
|
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
int dlms_setData2(
|
|
unsigned char* buff,
|
|
uint32_t length,
|
|
DLMS_DATA_TYPE type,
|
|
dlmsVARIANT* value)
|
|
#else
|
|
int dlms_setData2(
|
|
unsigned char* buff,
|
|
uint16_t length,
|
|
DLMS_DATA_TYPE type,
|
|
dlmsVARIANT* value)
|
|
#endif
|
|
{
|
|
gxByteBuffer bb;
|
|
bb_attach(&bb, buff, length, length);
|
|
return dlms_setData(&bb, type, value);
|
|
}
|
|
|
|
int dlms_setData(gxByteBuffer* buff, DLMS_DATA_TYPE type, dlmsVARIANT* value)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
int ret;
|
|
ret = var_changeType(value, type);
|
|
if (ret != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return var_getBytes2(value, type, buff);
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
int getCount(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
if (hlp_getObjectCount2(buff, &info->count) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
value->Arr = (variantArray*)gxmalloc(sizeof(variantArray));
|
|
if (value->Arr == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
va_init(value->Arr);
|
|
va_capacity(value->Arr, info->count);
|
|
value->vt = DLMS_DATA_TYPE_ARRAY;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get array from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* index
|
|
* starting index.
|
|
* Returns CGXDLMSVariant array.
|
|
*/
|
|
int getArray(gxByteBuffer* buff, gxDataInfo* info, uint16_t index, dlmsVARIANT* value)
|
|
{
|
|
dlmsVARIANT* tmp;
|
|
gxDataInfo info2;
|
|
uint32_t size;
|
|
uint16_t pos, startIndex;
|
|
int ret;
|
|
if (info->count == 0)
|
|
{
|
|
if ((ret = getCount(buff, info, value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
size = buff->size - buff->position;
|
|
if (info->count != 0 && size < 1)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
startIndex = index;
|
|
// Position where last row was found. Cache uses this info.
|
|
for (pos = info->index; pos != info->count; ++pos)
|
|
{
|
|
tmp = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT));
|
|
if (tmp == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(tmp);
|
|
di_init(&info2);
|
|
if ((ret = dlms_getData(buff, &info2, tmp)) != 0)
|
|
{
|
|
var_clear(tmp);
|
|
gxfree(tmp);
|
|
return ret;
|
|
}
|
|
if (!info2.complete)
|
|
{
|
|
var_clear(tmp);
|
|
gxfree(tmp);
|
|
buff->position = startIndex;
|
|
info->complete = 0;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (info2.count == info2.index)
|
|
{
|
|
startIndex = (uint16_t)buff->position;
|
|
va_push(value->Arr, tmp);
|
|
}
|
|
}
|
|
}
|
|
info->index = pos;
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
/**
|
|
* Get UInt32 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed UInt32 value.
|
|
*/
|
|
int getUInt32(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 4)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_UINT32;
|
|
}
|
|
if ((ret = bb_getUInt32(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->ulVal : value->pulVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
/**
|
|
* Get Int32 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed Int32 value.
|
|
*/
|
|
int getInt32(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 4)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_INT32;
|
|
}
|
|
if ((ret = bb_getInt32(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->lVal : value->plVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
/**
|
|
* Get bit std::string value from DLMS data.
|
|
*
|
|
* buff : Received DLMS data.
|
|
* info : Data info.
|
|
* Returns parsed bit std::string value.
|
|
*/
|
|
static int getBitString(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
uint16_t cnt = 0;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
int ret;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (hlp_getObjectCount2(buff, &cnt) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
uint16_t byteCnt = ba_getByteCount(cnt);
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < byteCnt)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (value->vt != (DLMS_DATA_TYPE_BIT_STRING | DLMS_DATA_TYPE_BYREF))
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if (value->capacity < cnt)
|
|
{
|
|
return DLMS_ERROR_CODE_INCONSISTENT_CLASS_OR_OBJECT;
|
|
}
|
|
value->size = cnt;
|
|
memcpy(value->pVal, buff->data + buff->position, byteCnt);
|
|
buff->position += byteCnt;
|
|
#else
|
|
value->bitArr = (bitArray*)gxmalloc(sizeof(bitArray));
|
|
ba_init(value->bitArr);
|
|
if ((ret = hlp_add(value->bitArr, buff, cnt)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return 0;
|
|
}
|
|
|
|
static int getBool(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
unsigned char ch;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 1)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_BOOLEAN;
|
|
value->boolVal = ch != 0;
|
|
}
|
|
else
|
|
{
|
|
*value->pboolVal = ch != 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get string value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed std::string value.
|
|
*/
|
|
int getString(gxByteBuffer* buff, gxDataInfo* info, unsigned char knownType, dlmsVARIANT* value)
|
|
{
|
|
int ret = 0;
|
|
uint16_t len = 0;
|
|
if (knownType)
|
|
{
|
|
len = (uint16_t)buff->size;
|
|
}
|
|
else
|
|
{
|
|
if (hlp_getObjectCount2(buff, &len) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < (uint16_t)len)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (value->vt != (DLMS_DATA_TYPE_STRING | DLMS_DATA_TYPE_BYREF))
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if (value->capacity < len)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
value->size = len;
|
|
memcpy(value->pVal, buff->data + buff->position, len);
|
|
buff->position += len;
|
|
#else
|
|
value->vt = DLMS_DATA_TYPE_STRING;
|
|
if (len > 0)
|
|
{
|
|
value->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
|
|
BYTE_BUFFER_INIT(value->strVal);
|
|
ret = bb_set2(value->strVal, buff, buff->position, len);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
/**
|
|
* Get UTF std::string value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed UTF std::string value.
|
|
*/
|
|
int getUtfString(
|
|
gxByteBuffer* buff,
|
|
gxDataInfo* info,
|
|
unsigned char knownType,
|
|
dlmsVARIANT* value)
|
|
{
|
|
int ret = 0;
|
|
uint16_t len = 0;
|
|
var_clear(value);
|
|
if (knownType)
|
|
{
|
|
len = (uint16_t)buff->size;
|
|
}
|
|
else
|
|
{
|
|
if (hlp_getObjectCount2(buff, &len) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < len)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
if (len > 0)
|
|
{
|
|
value->strUtfVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
|
|
BYTE_BUFFER_INIT(value->strUtfVal);
|
|
ret = bb_set2(value->strUtfVal, buff, buff->position, len);
|
|
value->vt = DLMS_DATA_TYPE_STRING_UTF8;
|
|
}
|
|
else
|
|
{
|
|
value->strUtfVal = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
/**
|
|
* Get octet string value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed octet string value.
|
|
*/
|
|
int getOctetString(gxByteBuffer* buff, gxDataInfo* info, unsigned char knownType, dlmsVARIANT* value)
|
|
{
|
|
uint16_t len;
|
|
int ret = 0;
|
|
if (knownType)
|
|
{
|
|
len = (uint16_t)buff->size;
|
|
}
|
|
else
|
|
{
|
|
if (hlp_getObjectCount2(buff, &len) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < len)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (value->vt != (DLMS_DATA_TYPE_OCTET_STRING | DLMS_DATA_TYPE_BYREF))
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if (value->capacity < len)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
value->size = len;
|
|
memcpy(value->pVal, buff->data + buff->position, len);
|
|
buff->position += len;
|
|
#else
|
|
if (len == 0)
|
|
{
|
|
var_clear(value);
|
|
}
|
|
else
|
|
{
|
|
ret = var_addBytes(value, buff->data + buff->position, len);
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
buff->position += (uint32_t)len;
|
|
#else
|
|
buff->position += (uint16_t)len;
|
|
#endif
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Get BCD value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed BCD value.
|
|
*/
|
|
int getBcd(gxByteBuffer* buff, gxDataInfo* info, unsigned char knownType, dlmsVARIANT* value)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
unsigned char idHigh, idLow;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
static const char hexArray[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
|
|
int ret = 0, a;
|
|
uint16_t len;
|
|
unsigned char ch;
|
|
if (knownType)
|
|
{
|
|
len = (uint16_t)buff->size;
|
|
}
|
|
else
|
|
{
|
|
if (hlp_getObjectCount2(buff, &len) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
// If there is not enough data available.
|
|
if ((buff->size - buff->position) < (uint16_t)(2 * len))
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
value->size = 2 * len;
|
|
if (value->vt != (DLMS_DATA_TYPE_OCTET_STRING | DLMS_DATA_TYPE_BYREF))
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
else if (value->capacity < len)
|
|
{
|
|
ret = DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
unsigned char* p = (unsigned char*)value->pVal;
|
|
for (a = 0; a != len; ++a)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
*p = hexArray[ch >> 4];
|
|
p++;
|
|
*p = hexArray[ch & 0x0F];
|
|
p++;
|
|
}
|
|
}
|
|
#else
|
|
value->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
|
|
BYTE_BUFFER_INIT(value->strVal);
|
|
value->vt = DLMS_DATA_TYPE_STRING;
|
|
bb_capacity(value->strVal, (uint16_t)(len * 2));
|
|
for (a = 0; a != len; ++a)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
idHigh = ch >> 4;
|
|
idLow = ch & 0x0F;
|
|
bb_setInt8(value->strVal, hexArray[idHigh]);
|
|
bb_setInt8(value->strVal, hexArray[idLow]);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get UInt8 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed UInt8 value.
|
|
*/
|
|
int getUInt8(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 1)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->bVal : value->pbVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_UINT8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get Int16 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed Int16 value.
|
|
*/
|
|
int getInt16(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 2)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getInt16(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->iVal : value->piVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_INT16;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get Int8 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed Int8 value.
|
|
*/
|
|
int getInt8(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 1)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getInt8(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->cVal : value->pcVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_INT8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get UInt16 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed UInt16 value.
|
|
*/
|
|
int getUInt16(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 2)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getUInt16(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->uiVal : value->puiVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_UINT16;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get Int64 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed Int64 value.
|
|
*/
|
|
int getInt64(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 8)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getInt64(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->llVal : value->pllVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_INT64;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get UInt64 value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed UInt64 value.
|
|
*/
|
|
int getUInt64(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 8)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getUInt64(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->ullVal : value->pullVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_UINT64;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get enumeration value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns parsed enumeration value.
|
|
*/
|
|
int getEnum(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 1)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->bVal : value->pbVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_ENUM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_FLOAT64
|
|
/**
|
|
* Get double value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns Parsed double value.
|
|
*/
|
|
int getDouble(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 8)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getDouble(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->dblVal : value->pdblVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_FLOAT64;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif //#endif //DLMS_IGNORE_FLOAT32
|
|
|
|
#ifndef DLMS_IGNORE_FLOAT32
|
|
/**
|
|
* Get float value from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns Parsed float value.
|
|
*/
|
|
int getFloat(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 4)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getFloat(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->fltVal : value->pfltVal)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_FLOAT32;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif //DLMS_IGNORE_FLOAT32
|
|
|
|
/**
|
|
* Get time from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns Parsed time.
|
|
*/
|
|
int getTime(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
unsigned char ch, hour, minute, second;
|
|
uint16_t ms = 0xFFFF;
|
|
if (buff->size - buff->position < 4)
|
|
{
|
|
// If there is not enough data available.
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
// Get time.
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
hour = ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
minute = ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
second = ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0xFF)
|
|
{
|
|
ms = ch * 10;
|
|
}
|
|
#if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->dateTime = (gxtime*)gxmalloc(sizeof(gxtime));
|
|
}
|
|
time_init(value->dateTime, (uint16_t)-1, 0xFF, 0xFF, hour, minute, second, ms, 0x8000);
|
|
value->vt = DLMS_DATA_TYPE_TIME;
|
|
#else
|
|
time_init((gxtime*)value->pVal, -1, -1, -1, hour, minute, second, ms, 0x8000);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get date from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns Parsed date.
|
|
*/
|
|
int getDate(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
int ret;
|
|
unsigned char month, day;
|
|
uint16_t year;
|
|
unsigned char ch;
|
|
if (buff->size - buff->position < 5)
|
|
{
|
|
// If there is not enough data available.
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
// Get year.
|
|
if ((ret = bb_getUInt16(buff, &year)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get month
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
month = ch;
|
|
// Get day.
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
day = ch;
|
|
// Skip week day
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
#if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->dateTime = (gxtime*)gxmalloc(sizeof(gxtime));
|
|
}
|
|
time_init(value->dateTime, year, month, day, 0xFF, 0xFF, 0xFF, 0xFF, 0x8000);
|
|
if (ch > 7)
|
|
{
|
|
value->dateTime->skip |= DATETIME_SKIPS_DAYOFWEEK;
|
|
}
|
|
value->vt = DLMS_DATA_TYPE_DATE;
|
|
#else
|
|
time_init((gxtime*)value->pVal, year, month, day, -1, -1, -1, -1, 0x8000);
|
|
if (ch > 7)
|
|
{
|
|
((gxtime*)value->pVal)->skip |= DATETIME_SKIPS_DAYOFWEEK;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get date and time from DLMS data.
|
|
*
|
|
* buff
|
|
* Received DLMS data.
|
|
* info
|
|
* Data info.
|
|
* Returns Parsed date and time.
|
|
*/
|
|
int getDateTime(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
uint16_t year;
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
int ret, status;
|
|
#else
|
|
int ret, ms, status;
|
|
#endif // DLMS_USE_EPOCH_TIME
|
|
unsigned char ch;
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 12)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
// Get year.
|
|
if ((ret = bb_getUInt16(buff, &year)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
gxtime* t;
|
|
#if defined(DLMS_IGNORE_MALLOC)
|
|
t = value->pVal;
|
|
#else
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
t = value->pVal;
|
|
}
|
|
else
|
|
{
|
|
t = value->dateTime = (gxtime*)gxmalloc(sizeof(gxtime));
|
|
value->vt = DLMS_DATA_TYPE_DATETIME;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
short deviation;
|
|
unsigned char mon = 0, day = 0, hour = 0, min = 0, sec = 0, wday = 0, skip = 0;
|
|
// Get month
|
|
if ((ret = bb_getUInt8(buff, &mon)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get day
|
|
if ((ret = bb_getUInt8(buff, &day)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Skip week day
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get time.
|
|
if ((ret = bb_getUInt8(buff, &hour)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &min)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &sec)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Get ms.
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = bb_getInt16(buff, &deviation)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
status = ch;
|
|
t->status = (DLMS_CLOCK_STATUS)status;
|
|
skip = DATETIME_SKIPS_MS;
|
|
if (year < 1 || year == 0xFFFF)
|
|
{
|
|
skip |= DATETIME_SKIPS_YEAR;
|
|
year = 1970;
|
|
}
|
|
if (wday > 7)
|
|
{
|
|
wday = 0;
|
|
skip |= DATETIME_SKIPS_DAYOFWEEK;
|
|
}
|
|
if ((mon < 1 || mon > 12) && mon != 0xFE && mon != 0xFD)
|
|
{
|
|
skip |= DATETIME_SKIPS_MONTH;
|
|
mon = 1;
|
|
}
|
|
if ((day < 1 || day > 31) && day != 0xFE && day != 0xFD)
|
|
{
|
|
skip |= DATETIME_SKIPS_DAY;
|
|
day = 1;
|
|
}
|
|
if (hour > 24)
|
|
{
|
|
skip |= DATETIME_SKIPS_HOUR;
|
|
hour = 0;
|
|
}
|
|
if (min > 60)
|
|
{
|
|
skip |= DATETIME_SKIPS_MINUTE;
|
|
min = 0;
|
|
}
|
|
if (sec > 60)
|
|
{
|
|
skip |= DATETIME_SKIPS_SECOND;
|
|
sec = 0;
|
|
}
|
|
time_init(t, year, mon, day, hour, min, sec, 0, deviation);
|
|
t->skip = skip;
|
|
t->status = status;
|
|
if (status == 0xFF)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_STATUS);
|
|
t->status = 0;
|
|
}
|
|
#else
|
|
t->value.tm_yday = 0;
|
|
t->value.tm_year = year;
|
|
// Get month
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
t->value.tm_mon = ch;
|
|
// Get day
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
t->value.tm_mday = ch;
|
|
// Skip week day
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
t->value.tm_wday = ch;
|
|
// Get time.
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
t->value.tm_hour = ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
t->value.tm_min = ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
t->value.tm_sec = ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
t->extraInfo = DLMS_DATE_TIME_EXTRA_INFO_NONE;
|
|
t->skip = DATETIME_SKIPS_NONE;
|
|
ms = ch;
|
|
if (ms != 0xFF)
|
|
{
|
|
ms *= 10;
|
|
}
|
|
else
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_MS);
|
|
}
|
|
if ((ret = bb_getInt16(buff, &t->deviation)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch == 0xFF)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_STATUS);
|
|
ch = 0;
|
|
}
|
|
status = ch;
|
|
t->status = (DLMS_CLOCK_STATUS)status;
|
|
if (year < 1 || year == 0xFFFF)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_YEAR);
|
|
t->value.tm_year = 1;
|
|
}
|
|
else
|
|
{
|
|
t->value.tm_year -= 1900;
|
|
}
|
|
if (t->value.tm_wday < 0 || t->value.tm_wday > 7)
|
|
{
|
|
t->value.tm_wday = 0;
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_DAYOFWEEK);
|
|
}
|
|
if (t->value.tm_mon == 0xFE)
|
|
{
|
|
t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_DST_BEGIN;
|
|
t->value.tm_mon = 0;
|
|
}
|
|
else if (t->value.tm_mon == 0xFD)
|
|
{
|
|
t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_DST_END;
|
|
t->value.tm_mon = 0;
|
|
}
|
|
else if (t->value.tm_mon < 1 || t->value.tm_mon > 12)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_MONTH);
|
|
t->value.tm_mon = 0;
|
|
}
|
|
else
|
|
{
|
|
t->value.tm_mon -= 1;
|
|
}
|
|
if (t->value.tm_mday == 0xFD)
|
|
{
|
|
// 2nd last day of month.
|
|
t->value.tm_mday = 1;
|
|
t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_LAST_DAY2;
|
|
}
|
|
else if (t->value.tm_mday == 0xFE)
|
|
{
|
|
//Last day of month
|
|
t->value.tm_mday = 1;
|
|
t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_LAST_DAY;
|
|
}
|
|
else if (t->value.tm_mday == -1 || t->value.tm_mday == 0 || t->value.tm_mday > 31)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_DAY);
|
|
t->value.tm_mday = 1;
|
|
}
|
|
if (t->value.tm_hour < 0 || t->value.tm_hour > 24)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_HOUR);
|
|
t->value.tm_hour = 0;
|
|
}
|
|
if (t->value.tm_min < 0 || t->value.tm_min > 60)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_MINUTE);
|
|
t->value.tm_min = 0;
|
|
}
|
|
if (t->value.tm_sec < 0 || t->value.tm_sec > 60)
|
|
{
|
|
t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_SECOND);
|
|
t->value.tm_sec = 0;
|
|
}
|
|
t->value.tm_isdst = (status & DLMS_CLOCK_STATUS_DAYLIGHT_SAVE_ACTIVE);
|
|
mktime(&t->value);
|
|
#endif //DLMS_USE_EPOCH_TIME
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
|
|
int getCompactArrayItem(
|
|
gxByteBuffer* buff,
|
|
DLMS_DATA_TYPE dt,
|
|
variantArray* list,
|
|
uint32_t len)
|
|
{
|
|
int ret;
|
|
gxDataInfo tmp;
|
|
di_init(&tmp);
|
|
tmp.type = dt;
|
|
uint32_t start = buff->position;
|
|
dlmsVARIANT* value = gxmalloc(sizeof(dlmsVARIANT));
|
|
if (value == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(value);
|
|
if (dt == DLMS_DATA_TYPE_STRING)
|
|
{
|
|
while (buff->position - start < len)
|
|
{
|
|
var_clear(value);
|
|
di_init(&tmp);
|
|
tmp.type = dt;
|
|
if ((ret = getString(buff, &tmp, 0, value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
va_push(list, value);
|
|
if (!tmp.complete)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (dt == DLMS_DATA_TYPE_OCTET_STRING)
|
|
{
|
|
while (buff->position - start < len)
|
|
{
|
|
var_clear(value);
|
|
di_init(&tmp);
|
|
tmp.type = dt;
|
|
if ((ret = getOctetString(buff, &tmp, 0, value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
va_push(list, value);
|
|
if (!tmp.complete)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (buff->position - start < len)
|
|
{
|
|
var_clear(value);
|
|
di_init(&tmp);
|
|
tmp.type = dt;
|
|
if ((ret = dlms_getData(buff, &tmp, value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
va_push(list, value);
|
|
if (!tmp.complete)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int getCompactArrayItem2(
|
|
gxByteBuffer* buff,
|
|
variantArray* dt,
|
|
variantArray* list,
|
|
int len)
|
|
{
|
|
int ret, pos;
|
|
dlmsVARIANT* tmp = gxmalloc(sizeof(dlmsVARIANT));
|
|
if (tmp == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
dlmsVARIANT* it;
|
|
var_init(tmp);
|
|
tmp->vt = DLMS_DATA_TYPE_ARRAY;
|
|
tmp->Arr = (variantArray*)gxmalloc(sizeof(variantArray));
|
|
if (tmp->Arr == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
va_init(tmp->Arr);
|
|
for (pos = 0; pos != dt->size; ++pos)
|
|
{
|
|
if ((ret = va_getByIndex(dt, pos, &it)) != 0)
|
|
{
|
|
var_clear(tmp);
|
|
gxfree(tmp);
|
|
return ret;
|
|
}
|
|
if (it->vt == DLMS_DATA_TYPE_ARRAY || it->vt == DLMS_DATA_TYPE_STRUCTURE)
|
|
{
|
|
if ((ret = getCompactArrayItem2(buff, it->Arr, tmp->Arr, 1)) != 0)
|
|
{
|
|
var_clear(tmp);
|
|
gxfree(tmp);
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = getCompactArrayItem(buff, (DLMS_DATA_TYPE)it->bVal, tmp->Arr, 1)) != 0)
|
|
{
|
|
var_clear(tmp);
|
|
gxfree(tmp);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
va_push(list, tmp);
|
|
return 0;
|
|
}
|
|
|
|
int getDataTypes(
|
|
gxByteBuffer* buff,
|
|
variantArray* cols,
|
|
int len)
|
|
{
|
|
int ret;
|
|
unsigned char ch;
|
|
uint16_t cnt;
|
|
DLMS_DATA_TYPE dt;
|
|
if (cols->size == 0)
|
|
{
|
|
va_capacity(cols, (uint16_t)len);
|
|
}
|
|
for (int pos = 0; pos != len; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
dt = (DLMS_DATA_TYPE)ch;
|
|
if (dt == DLMS_DATA_TYPE_ARRAY)
|
|
{
|
|
if ((ret = bb_getUInt16(buff, &cnt)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
dlmsVARIANT tmp;
|
|
dlmsVARIANT* it;
|
|
var_init(&tmp);
|
|
tmp.Arr = (variantArray*)gxmalloc(sizeof(variantArray));
|
|
tmp.vt = DLMS_DATA_TYPE_ARRAY;
|
|
if (tmp.Arr == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
va_init(tmp.Arr);
|
|
getDataTypes(buff, tmp.Arr, 1);
|
|
if ((ret = va_getByIndex(tmp.Arr, 0, &it)) != 0)
|
|
{
|
|
va_clear(cols);
|
|
return ret;
|
|
}
|
|
dlmsVARIANT* tmp2 = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT));
|
|
if (tmp2 == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(tmp2);
|
|
tmp2->vt = DLMS_DATA_TYPE_ARRAY;
|
|
tmp2->Arr = (variantArray*)gxmalloc(sizeof(variantArray));
|
|
if (tmp2->Arr == NULL)
|
|
{
|
|
var_clear(tmp2);
|
|
gxfree(tmp2);
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
va_init(tmp2->Arr);
|
|
for (int i = 0; i != cnt; ++i)
|
|
{
|
|
dlmsVARIANT* tmp3 = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT));
|
|
if (tmp3 == NULL)
|
|
{
|
|
var_clear(tmp2);
|
|
gxfree(tmp2);
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(tmp3);
|
|
if ((ret = var_copy(tmp3, it)) != 0)
|
|
{
|
|
var_clear(tmp3);
|
|
gxfree(tmp3);
|
|
var_clear(tmp2);
|
|
gxfree(tmp2);
|
|
return ret;
|
|
}
|
|
va_push(tmp2->Arr, tmp3);
|
|
}
|
|
var_clear(&tmp);
|
|
va_push(cols, tmp2);
|
|
}
|
|
else if (dt == DLMS_DATA_TYPE_STRUCTURE)
|
|
{
|
|
dlmsVARIANT* tmp = gxmalloc(sizeof(dlmsVARIANT));
|
|
if (tmp == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(tmp);
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
va_clear(cols);
|
|
var_clear(tmp);
|
|
gxfree(tmp);
|
|
return ret;
|
|
}
|
|
tmp->vt = DLMS_DATA_TYPE_STRUCTURE;
|
|
tmp->Arr = (variantArray*)gxmalloc(sizeof(variantArray));
|
|
if (tmp->Arr == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
va_init(tmp->Arr);
|
|
getDataTypes(buff, tmp->Arr, ch);
|
|
va_push(cols, tmp);
|
|
}
|
|
else
|
|
{
|
|
dlmsVARIANT* tmp = gxmalloc(sizeof(dlmsVARIANT));
|
|
if (tmp == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(tmp);
|
|
if (cols->size == 0)
|
|
{
|
|
va_capacity(cols, 1);
|
|
}
|
|
var_setUInt8(tmp, dt);
|
|
va_push(cols, tmp);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Get compact array value from DLMS data.
|
|
int getCompactArray(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
gxDataInfo* info,
|
|
dlmsVARIANT* value,
|
|
unsigned char onlyDataTypes)
|
|
{
|
|
int ret, pos;
|
|
uint16_t len;
|
|
unsigned char ch;
|
|
var_clear(value);
|
|
// If there is not enough data available.
|
|
if (buff->size - buff->position < 2)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
DLMS_DATA_TYPE dt = (DLMS_DATA_TYPE)ch;
|
|
if (dt == DLMS_DATA_TYPE_ARRAY)
|
|
{
|
|
//Invalid compact array data.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = hlp_getObjectCount2(buff, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
value->Arr = (variantArray*)gxmalloc(sizeof(variantArray));
|
|
value->vt = DLMS_DATA_TYPE_ARRAY;
|
|
if (value->Arr == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
va_init(value->Arr);
|
|
if (dt == DLMS_DATA_TYPE_STRUCTURE)
|
|
{
|
|
// Get data types.
|
|
variantArray cols;
|
|
va_init(&cols);
|
|
if ((ret = getDataTypes(buff, &cols, len)) != 0)
|
|
{
|
|
va_clear(&cols);
|
|
return ret;
|
|
}
|
|
if (onlyDataTypes)
|
|
{
|
|
va_attach2(value->Arr, &cols);
|
|
return 0;
|
|
}
|
|
if (buff->position == buff->size)
|
|
{
|
|
len = 0;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = hlp_getObjectCount2(buff, &len)) != 0)
|
|
{
|
|
va_clear(&cols);
|
|
return ret;
|
|
}
|
|
}
|
|
int start = buff->position;
|
|
while (buff->position - start < len)
|
|
{
|
|
dlmsVARIANT* it, * it2, * it3, * it4;
|
|
variantArray* row = (variantArray*)gxmalloc(sizeof(variantArray));
|
|
if (row == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
va_init(row);
|
|
for (pos = 0; pos != cols.size; ++pos)
|
|
{
|
|
if ((ret = va_getByIndex(&cols, pos, &it)) != 0)
|
|
{
|
|
va_clear(&cols);
|
|
va_clear(row);
|
|
gxfree(row);
|
|
return ret;
|
|
}
|
|
if (it->vt == DLMS_DATA_TYPE_STRUCTURE)
|
|
{
|
|
if ((ret = getCompactArrayItem2(buff, it->Arr, row, 1)) != 0)
|
|
{
|
|
va_clear(&cols);
|
|
va_clear(row);
|
|
gxfree(row);
|
|
return ret;
|
|
}
|
|
}
|
|
else if (it->vt == DLMS_DATA_TYPE_ARRAY)
|
|
{
|
|
int pos1;
|
|
variantArray tmp2;
|
|
va_init(&tmp2);
|
|
#ifdef DLMS_ITALIAN_STANDARD
|
|
//Some Italy meters require that there is a array count in data.
|
|
if (info->appendAA)
|
|
{
|
|
if ((ret = hlp_getObjectCount2(buff, &len)) != 0)
|
|
{
|
|
va_clear(&cols);
|
|
return ret;
|
|
}
|
|
if (it->Arr->size != len)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
#endif //DLMS_ITALIAN_STANDARD
|
|
|
|
if ((ret = getCompactArrayItem2(buff, it->Arr, &tmp2, 1)) != 0 ||
|
|
(ret = va_getByIndex(&tmp2, 0, &it2)) != 0)
|
|
{
|
|
va_clear(&tmp2);
|
|
va_clear(&cols);
|
|
va_clear(row);
|
|
gxfree(row);
|
|
return ret;
|
|
}
|
|
for (pos1 = 0; pos1 != it2->Arr->size; ++pos1)
|
|
{
|
|
if ((ret = va_getByIndex(it2->Arr, pos1, &it3)) != 0)
|
|
{
|
|
va_clear(&tmp2);
|
|
va_clear(&cols);
|
|
va_clear(row);
|
|
gxfree(row);
|
|
return ret;
|
|
}
|
|
it4 = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT));
|
|
if (it4 == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(it4);
|
|
if ((ret = var_copy(it4, it3)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
va_push(row, it4);
|
|
}
|
|
va_clear(&tmp2);
|
|
}
|
|
else
|
|
{
|
|
if ((ret = getCompactArrayItem(buff, (DLMS_DATA_TYPE)it->bVal, row, 1)) != 0)
|
|
{
|
|
va_clear(&cols);
|
|
va_clear(row);
|
|
gxfree(row);
|
|
return ret;
|
|
}
|
|
}
|
|
if (buff->position == buff->size)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//If all columns are read.
|
|
if (row->size >= cols.size)
|
|
{
|
|
dlmsVARIANT* tmp = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT));
|
|
if (tmp == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
var_init(tmp);
|
|
tmp->Arr = row;
|
|
tmp->vt = DLMS_DATA_TYPE_ARRAY;
|
|
va_push(value->Arr, tmp);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
va_clear(&cols);
|
|
return 0;
|
|
}
|
|
return getCompactArrayItem(buff, dt, value->Arr, len);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
int dlms_getData(gxByteBuffer* data, gxDataInfo* info, dlmsVARIANT* value)
|
|
{
|
|
unsigned char ch, knownType;
|
|
int ret = 0;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
uint32_t startIndex = data->position;
|
|
var_clear(value);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (data->position == data->size)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
info->complete = 1;
|
|
knownType = info->type != DLMS_DATA_TYPE_NONE;
|
|
if (!knownType)
|
|
{
|
|
ret = bb_getUInt8(data, &ch);
|
|
if (ret != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
info->type = (DLMS_DATA_TYPE)ch;
|
|
}
|
|
if (info->type == DLMS_DATA_TYPE_NONE)
|
|
{
|
|
//Do nothing.
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
if (data->position == data->size)
|
|
{
|
|
info->complete = 0;
|
|
return 0;
|
|
}
|
|
switch (info->type & ~DLMS_DATA_TYPE_BYREF)
|
|
{
|
|
case DLMS_DATA_TYPE_ARRAY:
|
|
case DLMS_DATA_TYPE_STRUCTURE:
|
|
{
|
|
#if !defined(DLMS_IGNORE_MALLOC)
|
|
ret = getArray(data, info, (uint16_t)startIndex, value);
|
|
value->vt = info->type;
|
|
#else
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
uint16_t count, pos;
|
|
if ((ret = hlp_getObjectCount2(data, &count)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//If user try to write too many items.
|
|
if (va_getCapacity(value->Arr) < count)
|
|
{
|
|
return DLMS_ERROR_CODE_INCONSISTENT_CLASS_OR_OBJECT;
|
|
}
|
|
value->Arr->size = count;
|
|
for (pos = 0; pos != count; ++pos)
|
|
{
|
|
gxDataInfo info2;
|
|
di_init(&info2);
|
|
dlmsVARIANT* value2;
|
|
if ((ret = va_getByIndex(value->Arr, pos, &value2)) != 0 ||
|
|
(ret = dlms_getData(data, &info2, value2)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
}
|
|
return 0;
|
|
#endif //#!defined(DLMS_IGNORE_MALLOC)
|
|
break;
|
|
}
|
|
case DLMS_DATA_TYPE_BOOLEAN:
|
|
{
|
|
ret = getBool(data, info, value);
|
|
break;
|
|
}
|
|
case DLMS_DATA_TYPE_BIT_STRING:
|
|
#if !defined(DLMS_IGNORE_MALLOC)
|
|
ret = getBitString(data, info, value);
|
|
#else
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
ret = getBitString(data, info, value);
|
|
}
|
|
else
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
return 0;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_MALLOC)
|
|
break;
|
|
case DLMS_DATA_TYPE_INT32:
|
|
ret = getInt32(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_UINT32:
|
|
ret = getUInt32(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_STRING:
|
|
#if !defined(DLMS_IGNORE_MALLOC)
|
|
ret = getString(data, info, knownType, value);
|
|
#else
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
ret = getString(data, info, 0, value);
|
|
}
|
|
else
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
ret = 0;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_MALLOC)
|
|
break;
|
|
case DLMS_DATA_TYPE_STRING_UTF8:
|
|
#if !defined(DLMS_IGNORE_MALLOC)
|
|
ret = getUtfString(data, info, knownType, value);
|
|
#else
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
return 0;
|
|
#endif //!defined(DLMS_IGNORE_MALLOC)
|
|
break;
|
|
case DLMS_DATA_TYPE_OCTET_STRING:
|
|
#if !defined(DLMS_IGNORE_MALLOC)
|
|
ret = getOctetString(data, info, knownType, value);
|
|
#else
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
ret = getOctetString(data, info, 0, value);
|
|
}
|
|
else
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
ret = 0;
|
|
}
|
|
#endif // DLMS_IGNORE_MALLOC
|
|
break;
|
|
case DLMS_DATA_TYPE_BINARY_CODED_DESIMAL:
|
|
ret = getBcd(data, info, knownType, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_INT8:
|
|
ret = getInt8(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_INT16:
|
|
ret = getInt16(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_UINT8:
|
|
ret = getUInt8(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_UINT16:
|
|
ret = getUInt16(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_COMPACT_ARRAY:
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
ret = getCompactArray(NULL, data, info, value, 0);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
break;
|
|
case DLMS_DATA_TYPE_INT64:
|
|
ret = getInt64(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_UINT64:
|
|
ret = getUInt64(data, info, value);
|
|
break;
|
|
case DLMS_DATA_TYPE_ENUM:
|
|
ret = getEnum(data, info, value);
|
|
break;
|
|
#ifndef DLMS_IGNORE_FLOAT32
|
|
case DLMS_DATA_TYPE_FLOAT32:
|
|
ret = getFloat(data, info, value);
|
|
break;
|
|
#endif //DLMS_IGNORE_FLOAT32
|
|
#ifndef DLMS_IGNORE_FLOAT64
|
|
case DLMS_DATA_TYPE_FLOAT64:
|
|
ret = getDouble(data, info, value);
|
|
break;
|
|
#endif //DLMS_IGNORE_FLOAT64
|
|
case DLMS_DATA_TYPE_DATETIME:
|
|
#if defined(DLMS_IGNORE_MALLOC)
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
ret = getDateTime(data, info, value);
|
|
}
|
|
else
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
ret = 0;
|
|
}
|
|
#else
|
|
ret = getDateTime(data, info, value);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
break;
|
|
case DLMS_DATA_TYPE_DATE:
|
|
#if defined(DLMS_IGNORE_MALLOC)
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
ret = getDate(data, info, value);
|
|
}
|
|
else
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
ret = 0;
|
|
}
|
|
#else
|
|
ret = getDate(data, info, value);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
break;
|
|
case DLMS_DATA_TYPE_TIME:
|
|
#if defined(DLMS_IGNORE_MALLOC)
|
|
if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0)
|
|
{
|
|
ret = getTime(data, info, value);
|
|
}
|
|
else
|
|
{
|
|
value->vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
--data->position;
|
|
value->byteArr = data;
|
|
ret = 0;
|
|
}
|
|
#else
|
|
ret = getTime(data, info, value);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
break;
|
|
default:
|
|
#ifdef _DEBUG
|
|
//Assert in debug version.
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(__linux__)
|
|
assert(0);
|
|
#endif
|
|
#endif
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (ret == 0 && (value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = info->type;
|
|
}
|
|
#else
|
|
if (ret == 0 && (value->vt & DLMS_DATA_TYPE_BYREF) == 0)
|
|
{
|
|
value->vt = info->type;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
|
|
//Return DLMS_ERROR_CODE_FALSE if LLC bytes are not included.
|
|
int dlms_checkLLCBytes(dlmsSettings* settings, gxByteBuffer* data)
|
|
{
|
|
if (settings->server)
|
|
{
|
|
//Check LLC bytes.
|
|
if (memcmp(data->data + data->position, LLC_SEND_BYTES, 3) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Check LLC bytes.
|
|
if (memcmp(data->data + data->position, LLC_REPLY_BYTES, 3) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
data->position += 3;
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
int dlms_getHDLCAddress(
|
|
gxByteBuffer* buff,
|
|
uint32_t* address,
|
|
unsigned char checkClientAddress)
|
|
{
|
|
unsigned char ch;
|
|
uint16_t s, pos;
|
|
uint32_t l;
|
|
int ret, size = 0;
|
|
for (pos = (uint16_t)buff->position; pos != (uint16_t)buff->size; ++pos)
|
|
{
|
|
++size;
|
|
if ((ret = bb_getUInt8ByIndex(buff, pos, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ch & 0x1) == 1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//DLMS CCT test requires that client size is one byte.
|
|
if (checkClientAddress && size != 1)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
|
|
if (size == 1)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
*address = ((ch & 0xFE) >> 1);
|
|
}
|
|
else if (size == 2)
|
|
{
|
|
if ((ret = bb_getUInt16(buff, &s)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
*address = ((s & 0xFE) >> 1) | ((s & 0xFE00) >> 2);
|
|
}
|
|
else if (size == 4)
|
|
{
|
|
if ((ret = bb_getUInt32(buff, &l)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
*address = ((l & 0xFE) >> 1) | ((l & 0xFE00) >> 2)
|
|
| ((l & 0xFE0000) >> 3) | ((l & 0xFE000000) >> 4);
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
void dlms_getServerAddress(uint32_t address, uint32_t* logical, uint32_t* physical)
|
|
{
|
|
if (address < 0x4000)
|
|
{
|
|
*logical = address >> 7;
|
|
*physical = address & 0x7F;
|
|
}
|
|
else
|
|
{
|
|
*logical = address >> 14;
|
|
*physical = address & 0x3FFF;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that client and server address match.
|
|
*
|
|
* @param server
|
|
* Is server.
|
|
* @param settings
|
|
* DLMS settings.
|
|
* @param reply
|
|
* Received data.
|
|
* @param index
|
|
* Position.
|
|
* @return True, if client and server address match.
|
|
*/
|
|
int dlms_checkHdlcAddress(
|
|
unsigned char server,
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* reply,
|
|
uint16_t index)
|
|
{
|
|
unsigned char ch;
|
|
int ret;
|
|
uint32_t source, target;
|
|
// Get destination and source addresses.
|
|
if ((ret = dlms_getHDLCAddress(reply, &target, 0)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = dlms_getHDLCAddress(reply, &source, server)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (server)
|
|
{
|
|
// Check that server addresses match.
|
|
if (settings->serverAddress != 0 && settings->serverAddress != target)
|
|
{
|
|
// Get frame command.
|
|
if (bb_getUInt8ByIndex(reply, reply->position, &ch) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS;
|
|
}
|
|
return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS;
|
|
}
|
|
else
|
|
{
|
|
settings->serverAddress = target;
|
|
}
|
|
|
|
// Check that client addresses match.
|
|
if (settings->clientAddress != 0 && settings->clientAddress != source)
|
|
{
|
|
// Get frame command.
|
|
if (bb_getUInt8ByIndex(reply, reply->position, &ch) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
//If SNRM and client has not call disconnect and changes client ID.
|
|
if (ch == DLMS_COMMAND_SNRM)
|
|
{
|
|
settings->clientAddress = (uint16_t)source;
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
settings->clientAddress = (uint16_t)source;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check that client addresses match.
|
|
if (settings->clientAddress != target)
|
|
{
|
|
// If echo.
|
|
if (settings->clientAddress == source && settings->serverAddress == target)
|
|
{
|
|
reply->position = index + 1;
|
|
}
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
// Check that server addresses match.
|
|
if (settings->serverAddress != source &&
|
|
// If All-station (Broadcast).
|
|
(settings->serverAddress & 0x7F) != 0x7F && (settings->serverAddress & 0x3FFF) != 0x3FFF)
|
|
{
|
|
//Check logical and physical address separately.
|
|
//This is done because some meters might send four bytes
|
|
//when only two bytes are needed.
|
|
uint32_t readLogical, readPhysical, logical, physical;
|
|
dlms_getServerAddress(source, &readLogical, &readPhysical);
|
|
dlms_getServerAddress(settings->serverAddress, &logical, &physical);
|
|
if (readLogical != logical || readPhysical != physical)
|
|
{
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
}
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
int dlms_getAddress(int32_t value, uint32_t* address, int* size)
|
|
{
|
|
if (value < 0x80)
|
|
{
|
|
*address = (unsigned char)(value << 1 | 1);
|
|
*size = 1;
|
|
return 0;
|
|
}
|
|
else if (value < 0x4000)
|
|
{
|
|
*address = (uint16_t)((value & 0x3F80) << 2 | (value & 0x7F) << 1 | 1);
|
|
*size = 2;
|
|
}
|
|
else if (value < 0x10000000)
|
|
{
|
|
*address = (uint32_t)((value & 0xFE00000) << 4 | (value & 0x1FC000) << 3
|
|
| (value & 0x3F80) << 2 | (value & 0x7F) << 1 | 1);
|
|
*size = 4;
|
|
}
|
|
else
|
|
{
|
|
//Invalid address
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
/**
|
|
* Get HDLC address as bytes.
|
|
*/
|
|
int dlms_getAddressBytes(
|
|
uint32_t value,
|
|
gxByteBuffer* bytes)
|
|
{
|
|
int ret, size;
|
|
uint32_t address;
|
|
if ((ret = dlms_getAddress(value, &address, &size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (size == 1)
|
|
{
|
|
bb_setUInt8(bytes, (unsigned char)address);
|
|
}
|
|
else if (size == 2)
|
|
{
|
|
bb_setUInt16(bytes, (uint16_t)address);
|
|
}
|
|
else if (size == 4)
|
|
{
|
|
bb_setUInt32(bytes, address);
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
int dlms_getHdlcFrame(
|
|
dlmsSettings* settings,
|
|
int frame,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
unsigned char tmp[4], tmp2[4];
|
|
uint16_t crc;
|
|
int ret;
|
|
uint16_t frameSize;
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t len = 0;
|
|
#else
|
|
uint16_t len = 0;
|
|
#endif
|
|
gxByteBuffer primaryAddress, secondaryAddress;
|
|
bb_clear(reply);
|
|
bb_attach(&primaryAddress, tmp, 0, 4);
|
|
bb_attach(&secondaryAddress, tmp2, 0, 4);
|
|
if (settings->server)
|
|
{
|
|
if ((ret = bb_capacity(&primaryAddress, 1)) == 0 &&
|
|
(ret = bb_capacity(&secondaryAddress, 4)) == 0)
|
|
{
|
|
if ((frame == 0x13 || frame == 0x3) && ((dlmsServerSettings*)settings)->pushClientAddress != 0)
|
|
{
|
|
ret = dlms_getAddressBytes(((dlmsServerSettings*)settings)->pushClientAddress, &primaryAddress);
|
|
}
|
|
else
|
|
{
|
|
ret = dlms_getAddressBytes(settings->clientAddress, &primaryAddress);
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
ret = dlms_getAddressBytes(settings->serverAddress, &secondaryAddress);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = bb_capacity(&primaryAddress, 4);
|
|
if (ret == 0)
|
|
{
|
|
ret = bb_capacity(&secondaryAddress, 1);
|
|
}
|
|
if (ret == 0 && (ret = dlms_getAddressBytes(settings->serverAddress, &primaryAddress)) == 0)
|
|
{
|
|
ret = dlms_getAddressBytes(settings->clientAddress, &secondaryAddress);
|
|
}
|
|
}
|
|
|
|
// Add BOP
|
|
if (ret == 0 && (ret = bb_capacity(reply, 8)) == 0)
|
|
{
|
|
ret = bb_setUInt8(reply, HDLC_FRAME_START_END);
|
|
}
|
|
|
|
frameSize = settings->maxInfoTX;
|
|
if (data != NULL && data->position == 0)
|
|
{
|
|
frameSize -= 3;
|
|
}
|
|
// If no data
|
|
if (ret == 0 && (data == NULL || data->size == 0))
|
|
{
|
|
len = 0;
|
|
ret = bb_setUInt8(reply, 0xA0);
|
|
}
|
|
else if (ret == 0 && data->size - data->position <= frameSize)
|
|
{
|
|
// Is last packet.
|
|
len = bb_available(data);
|
|
ret = bb_setUInt8(reply, (unsigned char)(0xA0 | (((7 + primaryAddress.size + secondaryAddress.size + len) >> 8) & 0x7)));
|
|
}
|
|
else if (ret == 0)
|
|
{
|
|
// More data to left.
|
|
len = frameSize;
|
|
ret = bb_setUInt8(reply, (unsigned char)(0xA8 | (((7 + primaryAddress.size + secondaryAddress.size + len) >> 8) & 0x7)));
|
|
}
|
|
// Frame len.
|
|
if (ret == 0 && len == 0)
|
|
{
|
|
ret = bb_setUInt8(reply, (unsigned char)(5 + primaryAddress.size + secondaryAddress.size + len));
|
|
}
|
|
else if (ret == 0)
|
|
{
|
|
if ((ret = bb_capacity(reply, (uint16_t)(11 + len))) == 0)
|
|
{
|
|
ret = bb_setUInt8(reply, (unsigned char)(7 + primaryAddress.size + secondaryAddress.size + len));
|
|
}
|
|
}
|
|
// Add primary address.
|
|
if (ret == 0 && (ret = bb_set2(reply, &primaryAddress, 0, primaryAddress.size)) == 0)
|
|
{
|
|
// Add secondary address.
|
|
ret = bb_set2(reply, &secondaryAddress, 0, secondaryAddress.size);
|
|
}
|
|
|
|
// Add frame ID.
|
|
if (ret == 0 && frame == 0)
|
|
{
|
|
ret = bb_setUInt8(reply, getNextSend(settings, 1));
|
|
}
|
|
else if (ret == 0)
|
|
{
|
|
ret = bb_setUInt8(reply, (unsigned char)frame);
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
// Add header CRC.
|
|
crc = countCRC(reply, 1, (reply->size - 1));
|
|
ret = bb_setUInt16(reply, crc);
|
|
}
|
|
if (ret == 0 && len != 0)
|
|
{
|
|
// Add data.
|
|
if ((ret = bb_set2(reply, data, data->position, len)) == 0)
|
|
{
|
|
// Add data CRC.
|
|
crc = countCRC(reply, 1, (reply->size - 1));
|
|
ret = bb_setUInt16(reply, crc);
|
|
}
|
|
}
|
|
// Add EOP
|
|
if (ret == 0)
|
|
{
|
|
ret = bb_setUInt8(reply, HDLC_FRAME_START_END);
|
|
}
|
|
// Remove sent data in server side.
|
|
if (ret == 0 && settings->server)
|
|
{
|
|
if (data != NULL)
|
|
{
|
|
//If all data is sent.
|
|
if (data->size == data->position)
|
|
{
|
|
ret = bb_clear(data);
|
|
}
|
|
else
|
|
{
|
|
//Remove sent data.
|
|
ret = bb_move(data, data->position, 0, data->size - data->position);
|
|
data->position = 0;
|
|
}
|
|
}
|
|
}
|
|
bb_clear(&primaryAddress);
|
|
bb_clear(&secondaryAddress);
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
|
|
#ifndef DLMS_IGNORE_PLC
|
|
int dlms_getPlcFrame(
|
|
dlmsSettings* settings,
|
|
unsigned char creditFields,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int frameSize = bb_available(data);
|
|
//Max frame size is 124 bytes.
|
|
if (frameSize > 134)
|
|
{
|
|
frameSize = 134;
|
|
}
|
|
//PAD Length.
|
|
unsigned char padLen = (unsigned char)((36 - ((11 + frameSize) % 36)) % 36);
|
|
bb_capacity(reply, 15 + frameSize + padLen);
|
|
//Add STX
|
|
bb_setUInt8(reply, 2);
|
|
//Length.
|
|
bb_setUInt8(reply, (unsigned char)(11 + frameSize));
|
|
//Length.
|
|
bb_setUInt8(reply, 0x50);
|
|
//Add Credit fields.
|
|
bb_setUInt8(reply, creditFields);
|
|
//Add source and target MAC addresses.
|
|
bb_setUInt8(reply, (unsigned char)(settings->plcSettings.macSourceAddress >> 4));
|
|
int val = settings->plcSettings.macSourceAddress << 12;
|
|
val |= settings->plcSettings.macDestinationAddress & 0xFFF;
|
|
bb_setUInt16(reply, (uint16_t)val);
|
|
bb_setUInt8(reply, padLen);
|
|
//Control byte.
|
|
bb_setUInt8(reply, DLMS_PLC_DATA_LINK_DATA_REQUEST);
|
|
bb_setUInt8(reply, (unsigned char)settings->serverAddress);
|
|
bb_setUInt8(reply, (unsigned char)settings->clientAddress);
|
|
bb_set(reply, data->data + data->position, frameSize);
|
|
data->position += frameSize;
|
|
//Add padding.
|
|
while (padLen != 0)
|
|
{
|
|
bb_setUInt8(reply, 0);
|
|
--padLen;
|
|
}
|
|
//Checksum.
|
|
uint16_t crc = countCRC(reply, 0, reply->size);
|
|
bb_setUInt16(reply, crc);
|
|
//Remove sent data in server side.
|
|
if (settings->server)
|
|
{
|
|
if (data->size == data->position)
|
|
{
|
|
bb_clear(data);
|
|
}
|
|
else
|
|
{
|
|
bb_move(data, data->position, 0, data->size - data->position);
|
|
data->position = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Reserved for internal use.
|
|
const uint32_t CRCPOLY = 0xD3B6BA00;
|
|
uint32_t dlms_countFCS24(unsigned char* buff, int index, int count)
|
|
{
|
|
unsigned char i, j;
|
|
uint32_t crcreg = 0;
|
|
for (j = 0; j < count; ++j)
|
|
{
|
|
unsigned char b = buff[index + j];
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
crcreg >>= 1;
|
|
if ((b & 0x80) != 0)
|
|
{
|
|
crcreg |= 0x80000000;
|
|
}
|
|
if ((crcreg & 0x80) != 0)
|
|
{
|
|
crcreg = crcreg ^ CRCPOLY;
|
|
}
|
|
b <<= 1;
|
|
}
|
|
}
|
|
return crcreg >> 8;
|
|
}
|
|
|
|
int dlms_getMacHdlcFrame(
|
|
dlmsSettings* settings,
|
|
unsigned char frame,
|
|
unsigned char creditFields,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
if (settings->maxInfoTX > 126)
|
|
{
|
|
settings->maxInfoTX = 86;
|
|
};
|
|
int ret;
|
|
gxByteBuffer tmp;
|
|
BYTE_BUFFER_INIT(&tmp);
|
|
//Lenght is updated last.
|
|
bb_setUInt16(reply, 0);
|
|
//Add Credit fields.
|
|
bb_setUInt8(reply, creditFields);
|
|
//Add source and target MAC addresses.
|
|
bb_setUInt8(reply, (unsigned char)(settings->plcSettings.macSourceAddress >> 4));
|
|
int val = settings->plcSettings.macSourceAddress << 12;
|
|
val |= settings->plcSettings.macDestinationAddress & 0xFFF;
|
|
bb_setUInt16(reply, (uint16_t)val);
|
|
if ((ret = dlms_getHdlcFrame(settings, frame, data, &tmp)) == 0)
|
|
{
|
|
unsigned char padLen = (unsigned char)((36 - ((10 + tmp.size) % 36)) % 36);
|
|
bb_setUInt8(reply, padLen);
|
|
bb_set(reply, tmp.data, tmp.size);
|
|
//Add padding.
|
|
while (padLen != 0)
|
|
{
|
|
bb_setUInt8(reply, 0);
|
|
--padLen;
|
|
}
|
|
//Checksum.
|
|
uint32_t crc = dlms_countFCS24(reply->data, 2, reply->size - 2 - padLen);
|
|
bb_setUInt8(reply, (unsigned char)(crc >> 16));
|
|
bb_setUInt16(reply, (uint16_t)crc);
|
|
//Add NC
|
|
val = reply->size / 36;
|
|
if (reply->size % 36 != 0)
|
|
{
|
|
++val;
|
|
}
|
|
if (val == 1)
|
|
{
|
|
val = DLMS_PLC_MAC_SUB_FRAMES_ONE;
|
|
}
|
|
else if (val == 2)
|
|
{
|
|
val = DLMS_PLC_MAC_SUB_FRAMES_TWO;
|
|
}
|
|
else if (val == 3)
|
|
{
|
|
val = DLMS_PLC_MAC_SUB_FRAMES_THREE;
|
|
}
|
|
else if (val == 4)
|
|
{
|
|
val = DLMS_PLC_MAC_SUB_FRAMES_FOUR;
|
|
}
|
|
else if (val == 5)
|
|
{
|
|
val = DLMS_PLC_MAC_SUB_FRAMES_FIVE;
|
|
}
|
|
else if (val == 6)
|
|
{
|
|
val = DLMS_PLC_MAC_SUB_FRAMES_SIX;
|
|
}
|
|
else if (val == 7)
|
|
{
|
|
val = DLMS_PLC_MAC_SUB_FRAMES_SEVEN;
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
ret = bb_setUInt16ByIndex(reply, 0, (uint16_t)val);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dlms_getMacFrame(
|
|
dlmsSettings* settings,
|
|
unsigned char frame,
|
|
unsigned char creditFields,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC)
|
|
{
|
|
return dlms_getPlcFrame(settings, creditFields, data, reply);
|
|
}
|
|
return dlms_getMacHdlcFrame(settings, frame, creditFields, data, reply);
|
|
}
|
|
|
|
#endif //DLMS_IGNORE_PLC
|
|
|
|
int dlms_getDataFromFrame(
|
|
gxByteBuffer* reply,
|
|
gxReplyData* data,
|
|
unsigned char hdlc)
|
|
{
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t offset = data->data.size;
|
|
uint32_t cnt;
|
|
#else
|
|
uint16_t offset = data->data.size;
|
|
uint16_t cnt;
|
|
#endif
|
|
if (data->packetLength < reply->position)
|
|
{
|
|
cnt = 0;
|
|
}
|
|
else
|
|
{
|
|
cnt = data->packetLength - reply->position;
|
|
}
|
|
if (cnt != 0)
|
|
{
|
|
int ret;
|
|
if ((ret = bb_capacity(&data->data, offset + cnt)) != 0 ||
|
|
(ret = bb_set2(&data->data, reply, reply->position, cnt)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (hdlc)
|
|
{
|
|
reply->position += 3;
|
|
}
|
|
}
|
|
// Set position to begin of new data.
|
|
data->data.position = offset;
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
|
|
int dlms_getHdlcData(
|
|
unsigned char server,
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* reply,
|
|
gxReplyData* data,
|
|
unsigned char* frame,
|
|
unsigned char preEstablished,
|
|
unsigned char first)
|
|
{
|
|
int ret;
|
|
unsigned char ch;
|
|
uint16_t eopPos;
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t pos, frameLen = 0;
|
|
uint32_t packetStartID = reply->position;
|
|
#else
|
|
uint16_t pos, frameLen = 0;
|
|
uint16_t packetStartID = (uint16_t)reply->position;
|
|
#endif
|
|
uint16_t crc, crcRead;
|
|
// If whole frame is not received yet.
|
|
if (reply->size - reply->position < 9)
|
|
{
|
|
data->complete = 0;
|
|
return 0;
|
|
}
|
|
data->complete = 1;
|
|
// Find start of HDLC frame.
|
|
for (pos = (uint16_t)reply->position; pos < reply->size; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(reply, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch == HDLC_FRAME_START_END)
|
|
{
|
|
packetStartID = pos;
|
|
break;
|
|
}
|
|
}
|
|
// Not a HDLC frame.
|
|
// Sometimes meters can send some strange data between DLMS frames.
|
|
if (reply->position == reply->size)
|
|
{
|
|
data->complete = 0;
|
|
// Not enough data to parse;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_getUInt8(reply, frame)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((*frame & 0xF0) != 0xA0)
|
|
{
|
|
--reply->position;
|
|
// If same data.
|
|
return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first);
|
|
}
|
|
// Check frame length.
|
|
if ((*frame & 0x7) != 0)
|
|
{
|
|
frameLen = ((*frame & 0x7) << 8);
|
|
}
|
|
if ((ret = bb_getUInt8(reply, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// If not enough data.
|
|
frameLen += ch;
|
|
if ((reply->size - reply->position + 1) < frameLen)
|
|
{
|
|
data->complete = 0;
|
|
reply->position = packetStartID;
|
|
// Not enough data to parse;
|
|
return 0;
|
|
}
|
|
eopPos = (uint16_t)(frameLen + packetStartID + 1);
|
|
if ((ret = bb_getUInt8ByIndex(reply, eopPos, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != HDLC_FRAME_START_END)
|
|
{
|
|
reply->position -= 2;
|
|
return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first);
|
|
}
|
|
|
|
// Check addresses.
|
|
ret = dlms_checkHdlcAddress(server, settings, reply, eopPos);
|
|
if (ret != 0)
|
|
{
|
|
//If pre-established client address has change.
|
|
if (ret == DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
else
|
|
{
|
|
if (ret == DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS &&
|
|
reply->position + 4 == reply->size)
|
|
{
|
|
data->packetLength = 0;
|
|
bb_clear(reply);
|
|
return ret;
|
|
}
|
|
if (ret == DLMS_ERROR_CODE_FALSE)
|
|
{
|
|
// If echo or reply to other meter.
|
|
return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first);
|
|
}
|
|
reply->position = packetStartID + 1;
|
|
ret = dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first);
|
|
return ret;
|
|
}
|
|
}
|
|
// Is there more data available.
|
|
unsigned char moreData = (*frame & 0x8) != 0;
|
|
// Get frame type.
|
|
if ((ret = bb_getUInt8(reply, frame)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
// Is there more data available.
|
|
if (moreData)
|
|
{
|
|
data->moreData |= DLMS_DATA_REQUEST_TYPES_FRAME;
|
|
}
|
|
else
|
|
{
|
|
data->moreData = ((DLMS_DATA_REQUEST_TYPES)(data->moreData & ~DLMS_DATA_REQUEST_TYPES_FRAME));
|
|
}
|
|
|
|
if (!preEstablished
|
|
#ifndef DLMS_IGNORE_HDLC_CHECK
|
|
&& !checkFrame(settings, *frame)
|
|
#endif //DLMS_IGNORE_HDLC_CHECK
|
|
)
|
|
{
|
|
reply->position = eopPos + 1;
|
|
if (settings->server)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_FRAME_NUMBER;
|
|
}
|
|
return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first);
|
|
}
|
|
// Check that header CRC is correct.
|
|
crc = countCRC(reply, packetStartID + 1,
|
|
reply->position - packetStartID - 1);
|
|
|
|
if ((ret = bb_getUInt16(reply, &crcRead)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (crc != crcRead)
|
|
{
|
|
if (reply->size - reply->position > 8)
|
|
{
|
|
return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first);
|
|
}
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Invalid CRC. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
return DLMS_ERROR_CODE_WRONG_CRC;
|
|
}
|
|
// Check that packet CRC match only if there is a data part.
|
|
if (reply->position != packetStartID + frameLen + 1)
|
|
{
|
|
crc = countCRC(reply, packetStartID + 1, frameLen - 2);
|
|
if ((ret = bb_getUInt16ByIndex(reply, packetStartID + frameLen - 1, &crcRead)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (crc != crcRead)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Invalid CRC. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
return DLMS_ERROR_CODE_WRONG_CRC;
|
|
}
|
|
// Remove CRC and EOP from packet length.
|
|
data->packetLength = eopPos - 2;
|
|
}
|
|
else
|
|
{
|
|
data->packetLength = eopPos - 2;
|
|
}
|
|
|
|
|
|
if (*frame != 0x13 && *frame != 0x3 && (*frame & HDLC_FRAME_TYPE_U_FRAME) == HDLC_FRAME_TYPE_U_FRAME)
|
|
{
|
|
// Get Eop if there is no data.
|
|
if (reply->position == packetStartID + frameLen + 1)
|
|
{
|
|
// Get EOP.
|
|
if ((ret = bb_getUInt8(reply, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
data->command = (DLMS_COMMAND)*frame;
|
|
switch (data->command)
|
|
{
|
|
case DLMS_COMMAND_SNRM:
|
|
case DLMS_COMMAND_UA:
|
|
case DLMS_COMMAND_DISCONNECT_MODE:
|
|
case DLMS_COMMAND_REJECTED:
|
|
case DLMS_COMMAND_DISC:
|
|
break;
|
|
default:
|
|
//Unknown command.
|
|
return DLMS_ERROR_CODE_REJECTED;
|
|
}
|
|
}
|
|
else if (*frame != 0x13 && *frame != 0x3 && (*frame & HDLC_FRAME_TYPE_S_FRAME) == HDLC_FRAME_TYPE_S_FRAME)
|
|
{
|
|
// If S-frame
|
|
int tmp = (*frame >> 2) & 0x3;
|
|
// If frame is rejected.
|
|
if (tmp == HDLC_CONTROL_FRAME_REJECT)
|
|
{
|
|
return DLMS_ERROR_CODE_REJECTED;
|
|
}
|
|
else if (tmp == HDLC_CONTROL_FRAME_RECEIVE_NOT_READY)
|
|
{
|
|
return DLMS_ERROR_CODE_REJECTED;
|
|
}
|
|
else if (tmp == HDLC_CONTROL_FRAME_RECEIVE_READY)
|
|
{
|
|
}
|
|
// Get Eop if there is no data.
|
|
if (reply->position == packetStartID + frameLen + 1)
|
|
{
|
|
// Get EOP.
|
|
if ((ret = bb_getUInt8(reply, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// I-frame
|
|
// Get Eop if there is no data.
|
|
if (reply->position == packetStartID + frameLen + 1)
|
|
{
|
|
// Get EOP.
|
|
if ((ret = bb_getUInt8(reply, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((*frame & 0x1) == 0x1)
|
|
{
|
|
data->moreData = DLMS_DATA_REQUEST_TYPES_FRAME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dlms_checkLLCBytes(settings, reply);
|
|
}
|
|
}
|
|
if (settings->server && (first || data->command == DLMS_COMMAND_SNRM))
|
|
{
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
// Check is data send to this server.
|
|
if (!svr_isTarget(settings, settings->serverAddress, settings->clientAddress))
|
|
{
|
|
settings->serverAddress = 0;
|
|
settings->clientAddress = 0;
|
|
if (reply->size - reply->position > 8)
|
|
{
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t pos = reply->position;
|
|
#else
|
|
uint16_t pos = reply->position;
|
|
#endif //!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))
|
|
ret = dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first);
|
|
if (settings->serverAddress != 0 && settings->clientAddress != 0)
|
|
{
|
|
reply->position = pos;
|
|
}
|
|
return ret;
|
|
}
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
int dlms_checkWrapperAddress(dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
gxReplyData* data,
|
|
gxReplyData* notify,
|
|
unsigned char* isNotify)
|
|
{
|
|
int ret;
|
|
uint16_t value;
|
|
*isNotify = 0;
|
|
if (settings->server)
|
|
{
|
|
if ((ret = bb_getUInt16(buff, &value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Check that client addresses match.
|
|
if (settings->clientAddress != 0 && settings->clientAddress != value)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
else
|
|
{
|
|
settings->clientAddress = value;
|
|
}
|
|
|
|
if ((ret = bb_getUInt16(buff, &value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Check that server addresses match.
|
|
if (settings->serverAddress != 0 && settings->serverAddress != value)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS;
|
|
}
|
|
else
|
|
{
|
|
settings->serverAddress = value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_getUInt16(buff, &value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Check that server addresses match.
|
|
if (settings->serverAddress != 0 && settings->serverAddress != value)
|
|
{
|
|
if (notify == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS;
|
|
}
|
|
notify->serverAddress = value;
|
|
*isNotify = 1;
|
|
}
|
|
else
|
|
{
|
|
settings->serverAddress = value;
|
|
}
|
|
|
|
if ((ret = bb_getUInt16(buff, &value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Check that client addresses match.
|
|
if (settings->clientAddress != 0 && settings->clientAddress != value)
|
|
{
|
|
if (notify == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
notify->clientAddress = value;
|
|
*isNotify = 1;
|
|
}
|
|
else
|
|
{
|
|
settings->clientAddress = value;
|
|
}
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
int dlms_getTcpData(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
gxReplyData* data,
|
|
gxReplyData* notify,
|
|
unsigned char* isNotify)
|
|
{
|
|
int ret, pos;
|
|
uint16_t value;
|
|
*isNotify = 0;
|
|
// If whole frame is not received yet.
|
|
if (buff->size - buff->position < 8)
|
|
{
|
|
data->complete = 0;
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
pos = buff->position;
|
|
data->complete = 0;
|
|
if (notify != NULL)
|
|
{
|
|
notify->complete = 0;
|
|
}
|
|
while (buff->position != buff->size)
|
|
{
|
|
// Get version
|
|
if ((ret = bb_getUInt16(buff, &value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (value == 1)
|
|
{
|
|
// Check TCP/IP addresses.
|
|
if ((ret = dlms_checkWrapperAddress(settings, buff, data, notify, isNotify)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//If notify.
|
|
if (notify != NULL && *isNotify != 0)
|
|
{
|
|
data = notify;
|
|
}
|
|
// Get length.
|
|
if ((ret = bb_getUInt16(buff, &value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
data->complete = !((buff->size - buff->position) < value);
|
|
if (!data->complete)
|
|
{
|
|
buff->position = pos;
|
|
}
|
|
else
|
|
{
|
|
data->packetLength = buff->position + value;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
--buff->position;
|
|
}
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
|
|
#ifndef DLMS_IGNORE_WIRELESS_MBUS
|
|
int dlms_getMBusData(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
gxReplyData* data)
|
|
{
|
|
int ret;
|
|
unsigned char len, ch;
|
|
//L-field.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Some meters are counting length to frame size.
|
|
if (buff->size < (unsigned char)(len - 1))
|
|
{
|
|
data->complete = 0;
|
|
buff->position = buff->position - 1;
|
|
}
|
|
else
|
|
{
|
|
//Some meters are counting length to frame size.
|
|
if (buff->size < len)
|
|
{
|
|
--len;
|
|
}
|
|
data->packetLength = len;
|
|
data->complete = 1;
|
|
//C-field.
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// DLMS_MBUS_COMMAND cmd = (DLMS_MBUS_COMMAND)ch;
|
|
//M-Field.
|
|
uint16_t manufacturerID;
|
|
if ((ret = bb_getUInt16(buff, &manufacturerID)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//A-Field.
|
|
uint32_t id;
|
|
if ((ret = bb_getUInt32(buff, &id)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
unsigned char meterVersion;
|
|
if ((ret = bb_getUInt8(buff, &meterVersion)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// DLMS_MBUS_METER_TYPE type = (DLMS_MBUS_METER_TYPE)ch;
|
|
// CI-Field
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// DLMS_MBUS_CONTROL_INFO ci = (DLMS_MBUS_CONTROL_INFO)ch;
|
|
//Access number.
|
|
unsigned char frameId;
|
|
if ((ret = bb_getUInt8(buff, &frameId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//State of the meter
|
|
unsigned char state;
|
|
if ((ret = bb_getUInt8(buff, &state)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Configuration word.
|
|
uint16_t configurationWord;
|
|
if ((ret = bb_getUInt16(buff, &configurationWord)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//unsigned char encryptedBlocks = (unsigned char)(configurationWord >> 12);
|
|
// DLMS_MBUS_ENCRYPTION_MODE encryption = (DLMS_MBUS_ENCRYPTION_MODE)(configurationWord & 7);
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
settings->clientAddress = ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
settings->serverAddress = ch;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#endif //DLMS_IGNORE_WIRELESS_MBUS
|
|
|
|
#ifndef DLMS_IGNORE_PLC
|
|
|
|
int dlms_getPlcData(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
gxReplyData* data)
|
|
{
|
|
if (bb_available(buff) < 9)
|
|
{
|
|
data->complete = 0;
|
|
return 0;
|
|
}
|
|
unsigned char ch;
|
|
int ret;
|
|
unsigned short pos;
|
|
int packetStartID = buff->position;
|
|
// Find STX.
|
|
unsigned char stx;
|
|
for (pos = (unsigned short)buff->position; pos < buff->size; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &stx)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (stx == 2)
|
|
{
|
|
packetStartID = pos;
|
|
break;
|
|
}
|
|
}
|
|
// Not a PLC frame.
|
|
if (buff->position == buff->size)
|
|
{
|
|
// Not enough data to parse;
|
|
data->complete = 0;
|
|
buff->position = packetStartID;
|
|
return 0;
|
|
}
|
|
unsigned char len;
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
int index = buff->position;
|
|
if (bb_available(buff) < len)
|
|
{
|
|
data->complete = 0;
|
|
buff->position = buff->position - 2;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Credit fields. IC, CC, DC
|
|
unsigned char credit;
|
|
if ((ret = bb_getUInt8(buff, &credit)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//MAC Addresses.
|
|
uint32_t mac;
|
|
if ((ret = bb_getUInt24(buff, &mac)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//SA.
|
|
short macSa = (short)(mac >> 12);
|
|
//DA.
|
|
short macDa = (short)(mac & 0xFFF);
|
|
//PAD length.
|
|
unsigned char padLen;
|
|
if ((ret = bb_getUInt8(buff, &padLen)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (buff->size < (unsigned short)(len + padLen + 2))
|
|
{
|
|
data->complete = 0;
|
|
buff->position = buff->position - index - 6;
|
|
}
|
|
else
|
|
{
|
|
//DL.Data.request
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != DLMS_PLC_DATA_LINK_DATA_REQUEST)
|
|
{
|
|
//Parsing MAC LLC data failed. Invalid DataLink data request.
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
unsigned char da, sa;
|
|
if ((ret = bb_getUInt8(buff, &da)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &sa)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (settings->server)
|
|
{
|
|
data->complete =
|
|
(macDa == DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL || macDa == settings->plcSettings.macSourceAddress) &&
|
|
(macSa == DLMS_PLC_SOURCE_ADDRESS_INITIATOR || macSa == settings->plcSettings.macDestinationAddress);
|
|
data->serverAddress = macDa;
|
|
data->clientAddress = macSa;
|
|
}
|
|
else
|
|
{
|
|
data->complete =
|
|
macDa == DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL ||
|
|
macDa == DLMS_PLC_SOURCE_ADDRESS_INITIATOR ||
|
|
macDa == settings->plcSettings.macDestinationAddress;
|
|
data->clientAddress = macDa;
|
|
data->serverAddress = macSa;
|
|
}
|
|
//Skip padding.
|
|
if (data->complete)
|
|
{
|
|
uint16_t crcCount, crc;
|
|
crcCount = countCRC(buff, 0, len + padLen);
|
|
if ((ret = bb_getUInt16ByIndex(buff, len + padLen, &crc)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Check CRC.
|
|
if (crc != crcCount)
|
|
{
|
|
//Invalid data checksum.
|
|
return DLMS_ERROR_CODE_WRONG_CRC;
|
|
}
|
|
data->packetLength = len;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dlms_getPlcHdlcData(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
gxReplyData* data,
|
|
unsigned char* frame)
|
|
{
|
|
if (bb_available(buff) < 2)
|
|
{
|
|
data->complete = 0;
|
|
return 0;
|
|
}
|
|
int ret;
|
|
*frame = 0;
|
|
unsigned char frameLen;
|
|
//SN field.
|
|
uint16_t ns;
|
|
if ((ret = bb_getUInt16(buff, &ns)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
switch (ns)
|
|
{
|
|
case DLMS_PLC_MAC_SUB_FRAMES_ONE:
|
|
frameLen = 36;
|
|
break;
|
|
case DLMS_PLC_MAC_SUB_FRAMES_TWO:
|
|
frameLen = 2 * 36;
|
|
break;
|
|
case DLMS_PLC_MAC_SUB_FRAMES_THREE:
|
|
frameLen = 3 * 36;
|
|
break;
|
|
case DLMS_PLC_MAC_SUB_FRAMES_FOUR:
|
|
frameLen = 4 * 36;
|
|
break;
|
|
case DLMS_PLC_MAC_SUB_FRAMES_FIVE:
|
|
frameLen = 5 * 36;
|
|
break;
|
|
case DLMS_PLC_MAC_SUB_FRAMES_SIX:
|
|
frameLen = 6 * 36;
|
|
break;
|
|
case DLMS_PLC_MAC_SUB_FRAMES_SEVEN:
|
|
frameLen = 7 * 36;
|
|
break;
|
|
default:
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if (bb_available(buff) < (unsigned char)(frameLen - 2))
|
|
{
|
|
data->complete = 0;
|
|
}
|
|
else
|
|
{
|
|
unsigned long index = buff->position;
|
|
//Credit fields. IC, CC, DC
|
|
unsigned char credit;
|
|
if ((ret = bb_getUInt8(buff, &credit)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//MAC Addresses.
|
|
uint32_t mac;
|
|
if ((ret = bb_getUInt24(buff, &mac)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//SA.
|
|
unsigned short sa = (unsigned short)(mac >> 12);
|
|
//DA.
|
|
unsigned short da = (unsigned short)(mac & 0xFFF);
|
|
if (settings->server)
|
|
{
|
|
data->complete = (da == DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL || da == settings->plcSettings.macSourceAddress) &&
|
|
(sa == DLMS_PLC_HDLC_SOURCE_ADDRESS_INITIATOR || sa == settings->plcSettings.macDestinationAddress);
|
|
data->serverAddress = da;
|
|
data->clientAddress = sa;
|
|
}
|
|
else
|
|
{
|
|
data->complete = da == DLMS_PLC_HDLC_SOURCE_ADDRESS_INITIATOR || da == settings->plcSettings.macDestinationAddress;
|
|
data->serverAddress = da;
|
|
data->clientAddress = sa;
|
|
}
|
|
if (data->complete)
|
|
{
|
|
//PAD length.
|
|
unsigned char padLen;
|
|
if ((ret = bb_getUInt8(buff, &padLen)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = dlms_getHdlcData(settings->server, settings, buff, data, frame, 0, 1)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
dlms_getDataFromFrame(buff, data, dlms_useHdlc(settings->interfaceType));
|
|
buff->position = buff->position + padLen;
|
|
uint32_t crcCount = dlms_countFCS24(buff->data, index, buff->position - index);
|
|
uint32_t crc;
|
|
if ((ret = bb_getUInt24ByIndex(buff, buff->position, &crc)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Check CRC.
|
|
if (crc != crcCount)
|
|
{
|
|
//Invalid data checksum.
|
|
return DLMS_ERROR_CODE_WRONG_CRC;
|
|
}
|
|
data->packetLength = (uint16_t)(2 + buff->position - index);
|
|
}
|
|
else
|
|
{
|
|
buff->position = (uint16_t)(buff->position + frameLen - index - 4);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Check is this PLC S-FSK message.
|
|
// buff: Received data.
|
|
// Returns True, if this is PLC message.
|
|
unsigned char dlms_isPlcSfskData(gxByteBuffer* buff)
|
|
{
|
|
if (bb_available(buff) < 2)
|
|
{
|
|
return 0;
|
|
}
|
|
int ret;
|
|
uint16_t len;
|
|
if ((ret = bb_getUInt16ByIndex(buff, buff->position, &len)) != 0)
|
|
{
|
|
return (unsigned char)ret;
|
|
}
|
|
switch (len)
|
|
{
|
|
case DLMS_PLC_MAC_SUB_FRAMES_ONE:
|
|
case DLMS_PLC_MAC_SUB_FRAMES_TWO:
|
|
case DLMS_PLC_MAC_SUB_FRAMES_THREE:
|
|
case DLMS_PLC_MAC_SUB_FRAMES_FOUR:
|
|
case DLMS_PLC_MAC_SUB_FRAMES_FIVE:
|
|
case DLMS_PLC_MAC_SUB_FRAMES_SIX:
|
|
case DLMS_PLC_MAC_SUB_FRAMES_SEVEN:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_PLC
|
|
|
|
int dlms_getDataFromBlock(gxByteBuffer* data, uint16_t index)
|
|
{
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t pos, len = data->position - index;
|
|
#else
|
|
uint16_t pos, len = data->position - index;
|
|
#endif
|
|
if (data->size == data->position)
|
|
{
|
|
bb_clear(data);
|
|
return 0;
|
|
}
|
|
pos = data->position;
|
|
bb_move(data, data->position, data->position - len, data->size - data->position);
|
|
data->position = pos - len;
|
|
return 0;
|
|
}
|
|
|
|
int dlms_receiverReady(
|
|
dlmsSettings* settings,
|
|
DLMS_DATA_REQUEST_TYPES type,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret;
|
|
DLMS_COMMAND cmd;
|
|
message tmp;
|
|
gxByteBuffer bb;
|
|
bb_clear(reply);
|
|
if (type == DLMS_DATA_REQUEST_TYPES_NONE)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
// Get next frame.
|
|
if ((type & DLMS_DATA_REQUEST_TYPES_FRAME) != 0)
|
|
{
|
|
unsigned char id = getReceiverReady(settings);
|
|
switch (settings->interfaceType)
|
|
{
|
|
#ifndef DLMS_IGNORE_PLC
|
|
case DLMS_INTERFACE_TYPE_PLC_HDLC:
|
|
ret = dlms_getMacHdlcFrame(settings, id, 0, NULL, reply);
|
|
break;
|
|
#endif //DLMS_IGNORE_PLC
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
case DLMS_INTERFACE_TYPE_HDLC:
|
|
ret = dlms_getHdlcFrame(settings, id, NULL, reply);
|
|
break;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
default:
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
// Get next block.
|
|
if (settings->useLogicalNameReferencing)
|
|
{
|
|
if (settings->server)
|
|
{
|
|
cmd = DLMS_COMMAND_GET_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GET_REQUEST;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
if (settings->server)
|
|
{
|
|
cmd = DLMS_COMMAND_READ_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_READ_REQUEST;
|
|
}
|
|
#else
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
unsigned char buff[40];
|
|
bb_attach(&bb, buff, 0, sizeof(buff));
|
|
#else
|
|
BYTE_BUFFER_INIT(&bb);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
if (settings->useLogicalNameReferencing)
|
|
{
|
|
bb_setUInt32(&bb, settings->blockIndex);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt16(&bb, (uint16_t)settings->blockIndex);
|
|
}
|
|
++settings->blockIndex;
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
gxByteBuffer* p[] = { reply };
|
|
mes_attach(&tmp, p, 1);
|
|
#else
|
|
mes_init(&tmp);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (settings->useLogicalNameReferencing)
|
|
{
|
|
gxLNParameters p;
|
|
params_initLN(&p, settings, 0, cmd, DLMS_GET_COMMAND_TYPE_NEXT_DATA_BLOCK, &bb, NULL, 0xFF, DLMS_COMMAND_NONE, 0, 0);
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
p.serializedPdu = &bb;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
ret = dlms_getLnMessages(&p, &tmp);
|
|
}
|
|
else
|
|
{
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
gxSNParameters p;
|
|
params_initSN(&p, settings, cmd, 1, DLMS_VARIABLE_ACCESS_SPECIFICATION_BLOCK_NUMBER_ACCESS, &bb, NULL, DLMS_COMMAND_NONE);
|
|
ret = dlms_getSnMessages(&p, &tmp);
|
|
#else
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (ret == 0)
|
|
{
|
|
ret = bb_set2(reply, (gxByteBuffer*)tmp.data[0], 0, tmp.data[0]->size);
|
|
}
|
|
bb_clear(&bb);
|
|
mes_clear(&tmp);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
int dlms_getWrapperFrame(
|
|
dlmsSettings* settings,
|
|
DLMS_COMMAND command,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret;
|
|
if ((ret = bb_clear(reply)) == 0 &&
|
|
(ret = bb_capacity(reply, 8 + data->size - data->position)) == 0 &&
|
|
// Add version.
|
|
(ret = bb_setUInt16(reply, 1)) == 0)
|
|
{
|
|
if (settings->server)
|
|
{
|
|
if ((ret = bb_setUInt16(reply, (unsigned short)settings->serverAddress)) == 0)
|
|
{
|
|
if (((dlmsServerSettings*)settings)->pushClientAddress != 0 && (command == DLMS_COMMAND_DATA_NOTIFICATION || command == DLMS_COMMAND_EVENT_NOTIFICATION))
|
|
{
|
|
ret = bb_setUInt16(reply, ((dlmsServerSettings*)settings)->pushClientAddress);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_setUInt16(reply, settings->clientAddress);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_setUInt16(reply, settings->clientAddress)) == 0)
|
|
{
|
|
ret = bb_setUInt16(reply, (unsigned short)settings->serverAddress);
|
|
}
|
|
}
|
|
// Data length.
|
|
if (ret == 0 && (ret = bb_setUInt16(reply, (uint16_t)data->size)) == 0)
|
|
{
|
|
// Data
|
|
ret = bb_set2(reply, data, data->position, data->size - data->position);
|
|
}
|
|
// Remove sent data in server side.
|
|
if (ret == 0 && settings->server)
|
|
{
|
|
if (data->size == data->position)
|
|
{
|
|
ret = bb_clear(data);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_move(data, data->position, 0, data->size - data->position);
|
|
data->position = 0;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
|
|
int dlms_verifyInvokeId(dlmsSettings* settings, gxReplyData* reply)
|
|
{
|
|
if (settings->autoIncreaseInvokeID && reply->invokeId != dlms_getInvokeIDPriority(settings, 0))
|
|
{
|
|
//Invalid invoke ID.
|
|
return DLMS_ERROR_CODE_INVALID_INVOKE_ID;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dlms_handleGetResponse(
|
|
dlmsSettings* settings,
|
|
gxReplyData* reply,
|
|
uint16_t index)
|
|
{
|
|
int ret;
|
|
uint16_t count;
|
|
unsigned char ch;
|
|
uint32_t number;
|
|
short type;
|
|
// Get type.
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
type = ch;
|
|
// Get invoke ID and priority.
|
|
if ((ret = bb_getUInt8(&reply->data, &reply->invokeId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = dlms_verifyInvokeId(settings, reply)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Response normal
|
|
if (type == 1)
|
|
{
|
|
// Result
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return ch;
|
|
}
|
|
ret = dlms_getDataFromBlock(&reply->data, 0);
|
|
}
|
|
else if (type == 2)
|
|
{
|
|
// GetResponsewithDataBlock
|
|
// Is Last block.
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch == 0)
|
|
{
|
|
reply->moreData = (DLMS_DATA_REQUEST_TYPES)(reply->moreData | DLMS_DATA_REQUEST_TYPES_BLOCK);
|
|
}
|
|
else
|
|
{
|
|
reply->moreData =
|
|
(DLMS_DATA_REQUEST_TYPES)(reply->moreData & ~DLMS_DATA_REQUEST_TYPES_BLOCK);
|
|
}
|
|
// Get Block number.
|
|
if ((ret = bb_getUInt32(&reply->data, &number)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// If meter's block index is zero based or Actaris is read.
|
|
// Actaris SL7000 might return wrong block index sometimes.
|
|
// It's not reseted to 1.
|
|
if (number != 1 && settings->blockIndex == 1)
|
|
{
|
|
settings->blockIndex = number;
|
|
}
|
|
else if (number != settings->blockIndex)
|
|
{
|
|
return DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID;
|
|
}
|
|
// Get status.
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return ch;
|
|
}
|
|
else
|
|
{
|
|
// Get data size.
|
|
if ((ret = hlp_getObjectCount2(&reply->data, &count)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// if whole block is read.
|
|
if ((reply->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0)
|
|
{
|
|
// Check Block length.
|
|
if (count > (uint16_t)(bb_available(&reply->data)))
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
reply->command = DLMS_COMMAND_NONE;
|
|
}
|
|
if (count == 0)
|
|
{
|
|
// If meter sends empty data block.
|
|
reply->data.size = index;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = dlms_getDataFromBlock(&reply->data, index)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
// If last packet and data is not try to peek.
|
|
if (reply->moreData == DLMS_DATA_REQUEST_TYPES_NONE)
|
|
{
|
|
if (!reply->peek)
|
|
{
|
|
reply->data.position = 0;
|
|
resetBlockIndex(settings);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (type == 3)
|
|
{
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
else
|
|
{
|
|
//Invalid Get response.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int handleWriteResponse(gxReplyData* data)
|
|
{
|
|
unsigned char ch;
|
|
int ret;
|
|
uint16_t count, pos;
|
|
if (hlp_getObjectCount2(&data->data, &count) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
for (pos = 0; pos != count; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return ch;
|
|
}
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
int dlms_handleWriteResponse(
|
|
gxReplyData* data)
|
|
{
|
|
unsigned char ch;
|
|
int ret;
|
|
uint16_t pos, count;
|
|
if (hlp_getObjectCount2(&data->data, &count) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
for (pos = 0; pos != count; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return ch;
|
|
}
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
int dlms_getValueFromData(dlmsSettings* settings,
|
|
gxReplyData* reply)
|
|
{
|
|
uint16_t index;
|
|
int ret;
|
|
#if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
int pos;
|
|
dlmsVARIANT_PTR tmp;
|
|
#endif //!defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
dlmsVARIANT value;
|
|
gxDataInfo info;
|
|
di_init(&info);
|
|
var_init(&value);
|
|
if (reply->dataValue.vt == DLMS_DATA_TYPE_ARRAY)
|
|
{
|
|
info.type = DLMS_DATA_TYPE_ARRAY;
|
|
info.count = (uint16_t)reply->totalCount;
|
|
info.index = (uint16_t)reply->data.size;
|
|
}
|
|
index = (uint16_t)(reply->data.position);
|
|
reply->data.position = reply->readPosition;
|
|
if ((ret = dlms_getData(&reply->data, &info, &value)) != 0)
|
|
{
|
|
var_clear(&value);
|
|
return ret;
|
|
}
|
|
// If new data.
|
|
if (value.vt != DLMS_DATA_TYPE_NONE)
|
|
{
|
|
if (value.vt != DLMS_DATA_TYPE_ARRAY && value.vt != DLMS_DATA_TYPE_STRUCTURE)
|
|
{
|
|
reply->dataType = info.type;
|
|
reply->dataValue = value;
|
|
reply->totalCount = 0;
|
|
if (reply->command == DLMS_COMMAND_DATA_NOTIFICATION)
|
|
{
|
|
reply->readPosition = reply->data.position;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (reply->dataValue.vt == DLMS_DATA_TYPE_NONE)
|
|
{
|
|
reply->dataValue = value;
|
|
}
|
|
else
|
|
{
|
|
#if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
for (pos = 0; pos != value.Arr->size; ++pos)
|
|
{
|
|
if ((ret = va_getByIndex(value.Arr, pos, &tmp)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
va_push(reply->dataValue.Arr, tmp);
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
}
|
|
}
|
|
reply->readPosition = reply->data.position;
|
|
// Element count.
|
|
reply->totalCount = info.count;
|
|
}
|
|
else if (info.complete
|
|
&& reply->command == DLMS_COMMAND_DATA_NOTIFICATION)
|
|
{
|
|
// If last item is null. This is a special case.
|
|
reply->readPosition = reply->data.position;
|
|
}
|
|
reply->data.position = index;
|
|
|
|
// If last data frame of the data block is read.
|
|
if (reply->command != DLMS_COMMAND_DATA_NOTIFICATION
|
|
&& info.complete && reply->moreData == DLMS_DATA_REQUEST_TYPES_NONE)
|
|
{
|
|
// If all blocks are read.
|
|
resetBlockIndex(settings);
|
|
reply->data.position = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dlms_readResponseDataBlockResult(
|
|
dlmsSettings* settings,
|
|
gxReplyData* reply,
|
|
uint16_t index)
|
|
{
|
|
int ret;
|
|
uint16_t number;
|
|
uint16_t blockLength;
|
|
unsigned char lastBlock;
|
|
if ((ret = bb_getUInt8(&reply->data, &lastBlock)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get Block number.
|
|
if ((ret = bb_getUInt16(&reply->data, &number)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (hlp_getObjectCount2(&reply->data, &blockLength) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
// Is Last block.
|
|
if (!lastBlock)
|
|
{
|
|
reply->moreData |= DLMS_DATA_REQUEST_TYPES_BLOCK;
|
|
}
|
|
else
|
|
{
|
|
reply->moreData &= ~DLMS_DATA_REQUEST_TYPES_BLOCK;
|
|
}
|
|
// If meter's block index is zero based.
|
|
if (number != 1 && settings->blockIndex == 1)
|
|
{
|
|
settings->blockIndex = number;
|
|
}
|
|
if (number != settings->blockIndex)
|
|
{
|
|
//Invalid Block number
|
|
return DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID;
|
|
}
|
|
// If whole block is not read.
|
|
if ((reply->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) != 0)
|
|
{
|
|
dlms_getDataFromBlock(&reply->data, index);
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
if (blockLength != bb_available(&reply->data))
|
|
{
|
|
//Invalid block length.
|
|
return DLMS_ERROR_CODE_DATA_BLOCK_UNAVAILABLE;
|
|
}
|
|
reply->command = DLMS_COMMAND_NONE;
|
|
|
|
dlms_getDataFromBlock(&reply->data, index);
|
|
reply->totalCount = 0;
|
|
// If last packet and data is not try to peek.
|
|
if (reply->moreData == DLMS_DATA_REQUEST_TYPES_NONE)
|
|
{
|
|
resetBlockIndex(settings);
|
|
}
|
|
return ret;
|
|
}
|
|
int dlms_handleReadResponse(
|
|
dlmsSettings* settings,
|
|
gxReplyData* reply,
|
|
uint16_t index)
|
|
{
|
|
int ret;
|
|
unsigned char ch;
|
|
uint16_t number;
|
|
uint16_t pos, cnt = reply->totalCount;
|
|
DLMS_SINGLE_READ_RESPONSE type;
|
|
variantArray values;
|
|
|
|
// If we are reading value first time or block is handed.
|
|
unsigned char first = reply->totalCount == 0 || reply->commandType == DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT;
|
|
if (first)
|
|
{
|
|
if (hlp_getObjectCount2(&reply->data, &cnt) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
reply->totalCount = cnt;
|
|
}
|
|
if (cnt != 1)
|
|
{
|
|
//Parse data after all data is received when readlist is used.
|
|
if (reply->moreData != DLMS_DATA_REQUEST_TYPES_NONE)
|
|
{
|
|
if ((ret = dlms_getDataFromBlock(&reply->data, 0)) == 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
return ret;
|
|
}
|
|
if (!first)
|
|
{
|
|
reply->data.position = 0;
|
|
first = 1;
|
|
}
|
|
}
|
|
va_init(&values);
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
// Get status code. Status code is begin of each PDU.
|
|
if (first)
|
|
{
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
reply->commandType = ch;
|
|
type = (DLMS_SINGLE_READ_RESPONSE)ch;
|
|
}
|
|
else
|
|
{
|
|
type = (DLMS_SINGLE_READ_RESPONSE)reply->commandType;
|
|
}
|
|
switch (type)
|
|
{
|
|
case DLMS_SINGLE_READ_RESPONSE_DATA:
|
|
if (cnt == 1)
|
|
{
|
|
ret = dlms_getDataFromBlock(&reply->data, 0);
|
|
}
|
|
else
|
|
{
|
|
// If read multiple items.
|
|
reply->readPosition = reply->data.position;
|
|
dlms_getValueFromData(settings, reply);
|
|
reply->data.position = reply->readPosition;
|
|
va_push(&values, &reply->dataValue);
|
|
var_clear(&reply->dataValue);
|
|
}
|
|
break;
|
|
case DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR:
|
|
// Get error code.
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return ch;
|
|
case DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT:
|
|
if ((ret = dlms_readResponseDataBlockResult(settings, reply, index)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
break;
|
|
case DLMS_SINGLE_READ_RESPONSE_BLOCK_NUMBER:
|
|
// Get Block number.
|
|
if ((ret = bb_getUInt16(&reply->data, &number)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (number != settings->blockIndex)
|
|
{
|
|
//Invalid Block number
|
|
return DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID;
|
|
}
|
|
++settings->blockIndex;
|
|
reply->moreData |= DLMS_DATA_REQUEST_TYPES_BLOCK;
|
|
break;
|
|
default:
|
|
//HandleReadResponse failed. Invalid tag.
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
}
|
|
if (values.size != 0)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
reply->dataValue.vt = DLMS_DATA_TYPE_ARRAY;
|
|
reply->dataValue.Arr = gxmalloc(sizeof(variantArray));
|
|
va_init(reply->dataValue.Arr);
|
|
reply->dataValue.Arr->capacity = values.capacity;
|
|
va_copyArray(reply->dataValue.Arr, &values);
|
|
gxfree(values.data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
if (cnt != 1)
|
|
{
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dlms_handleMethodResponse(
|
|
dlmsSettings* settings,
|
|
gxReplyData* data)
|
|
{
|
|
int ret;
|
|
unsigned char ch, type;
|
|
// Get type.
|
|
if ((ret = bb_getUInt8(&data->data, &type)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get invoke ID and priority.
|
|
if ((ret = bb_getUInt8(&data->data, &data->invokeId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = dlms_verifyInvokeId(settings, data)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Action-Response-Normal
|
|
if (type == 1)
|
|
{
|
|
//Get Action-Result
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
return ch;
|
|
}
|
|
resetBlockIndex(settings);
|
|
// Get data if exists. Some meters do not return here anything.
|
|
if (data->data.position < data->data.size)
|
|
{
|
|
//Get-Data-Result.
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//If data.
|
|
if (ch == 0)
|
|
{
|
|
return dlms_getDataFromBlock(&data->data, 0);
|
|
}
|
|
else if (ch == 1) //Data-Access-Result
|
|
{
|
|
//Get Data-Access-Result
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
if (ch == 9)
|
|
{
|
|
//Handle Texas Instrument missing byte here.
|
|
if ((ret = bb_getUInt8ByIndex(&data->data, data->data.position, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch == 16)
|
|
{
|
|
--data->data.position;
|
|
return dlms_getDataFromBlock(&data->data, 0);
|
|
}
|
|
}
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return ch;
|
|
}
|
|
return dlms_getDataFromBlock(&data->data, 0);
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
}
|
|
}
|
|
//Action-Response-With-Pblock
|
|
else if (type == 2)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
// Action-Response-With-List.
|
|
else if (type == 3)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
//Action-Response-Next-Pblock
|
|
else if (type == 4)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
|
|
int dlms_handlePush(gxReplyData* reply)
|
|
{
|
|
unsigned char ch;
|
|
uint32_t ul;
|
|
int ret;
|
|
uint16_t index = (uint16_t)(reply->data.position - 1);
|
|
// Is last block
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if ((ch & 0x80) == 0)
|
|
{
|
|
reply->moreData = (DLMS_DATA_REQUEST_TYPES)(reply->moreData | DLMS_DATA_REQUEST_TYPES_BLOCK);
|
|
}
|
|
else
|
|
{
|
|
reply->moreData = (DLMS_DATA_REQUEST_TYPES)(reply->moreData & ~DLMS_DATA_REQUEST_TYPES_BLOCK);
|
|
}
|
|
// Get block number sent.
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get block number acknowledged
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get APU tag.
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Addl fields
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Data-Notification
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ch & 0x0F) == 0)
|
|
{
|
|
//Invalid data.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
// Long-Invoke-Id-And-Priority
|
|
if ((ret = bb_getUInt32(&reply->data, &ul)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get date time and skip it if used.
|
|
if ((ret = bb_getUInt8(&reply->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
reply->data.position = reply->data.position + ch;
|
|
}
|
|
return dlms_getDataFromBlock(&reply->data, index);
|
|
}
|
|
|
|
int dlms_handleSetResponse(
|
|
dlmsSettings* settings,
|
|
gxReplyData* data)
|
|
{
|
|
unsigned char ch, type;
|
|
int ret;
|
|
if ((ret = bb_getUInt8(&data->data, &type)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
//Invoke ID and priority.
|
|
if ((ret = bb_getUInt8(&data->data, &data->invokeId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = dlms_verifyInvokeId(settings, data)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// SetResponseNormal
|
|
if (type == DLMS_SET_RESPONSE_TYPE_NORMAL)
|
|
{
|
|
ret = bb_getUInt8(&data->data, &ch);
|
|
if (ret == 0 && ch != 0)
|
|
{
|
|
return ch;
|
|
}
|
|
}
|
|
else if (type == DLMS_SET_RESPONSE_TYPE_DATA_BLOCK || type == DLMS_SET_RESPONSE_TYPE_LAST_DATA_BLOCK)
|
|
{
|
|
uint32_t tmp;
|
|
ret = bb_getUInt32(&data->data, &tmp);
|
|
}
|
|
else if (type == DLMS_SET_RESPONSE_TYPE_WITH_LIST)
|
|
{
|
|
uint16_t pos, cnt;
|
|
if (hlp_getObjectCount2(&data->data, &cnt) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (ch != 0)
|
|
{
|
|
ret = ch;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Invalid data type.
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int dlms_changeType2(
|
|
dlmsVARIANT* value,
|
|
DLMS_DATA_TYPE type,
|
|
dlmsVARIANT* newValue)
|
|
{
|
|
gxByteBuffer bb;
|
|
if (value->byteArr != NULL)
|
|
{
|
|
bb_attach(&bb, value->byteArr->data, value->byteArr->size, value->byteArr->size);
|
|
bb.position = value->byteArr->position;
|
|
}
|
|
return dlms_changeType(&bb, type, newValue);
|
|
|
|
}
|
|
|
|
int dlms_changeType(
|
|
gxByteBuffer* value,
|
|
DLMS_DATA_TYPE type,
|
|
dlmsVARIANT* newValue)
|
|
{
|
|
int ret;
|
|
gxDataInfo info;
|
|
di_init(&info);
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
var_clear(newValue);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (value->size == 0)
|
|
{
|
|
if (type == DLMS_DATA_TYPE_STRING || type == DLMS_DATA_TYPE_STRING_UTF8)
|
|
{
|
|
newValue->vt = type;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
if (type == DLMS_DATA_TYPE_NONE)
|
|
{
|
|
#if !defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC)
|
|
char* tmp = bb_toHexString(value);
|
|
newValue->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
|
|
BYTE_BUFFER_INIT(newValue->strVal);
|
|
bb_addString(newValue->strVal, tmp);
|
|
gxfree(tmp);
|
|
newValue->vt = DLMS_DATA_TYPE_STRING;
|
|
return DLMS_ERROR_CODE_OK;
|
|
#else
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //!defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC)
|
|
}
|
|
info.type = type;
|
|
if ((ret = dlms_getData(value, &info, newValue)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
value->position = 0;
|
|
if (!info.complete)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
if (type == DLMS_DATA_TYPE_OCTET_STRING && newValue->vt == DLMS_DATA_TYPE_OCTET_STRING)
|
|
{
|
|
#if !defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC)
|
|
char* tmp = bb_toHexString(value);
|
|
newValue->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
|
|
BYTE_BUFFER_INIT(newValue->strVal);
|
|
bb_addString(newValue->strVal, tmp);
|
|
gxfree(tmp);
|
|
newValue->vt = DLMS_DATA_TYPE_STRING;
|
|
#else
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //!defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC)
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Handle data notification get data from block and/or update error status.
|
|
*
|
|
* @param reply
|
|
* Received data from the client.
|
|
*/
|
|
int dlms_handleDataNotification(
|
|
dlmsSettings* settings,
|
|
gxReplyData* reply)
|
|
{
|
|
uint32_t id;
|
|
int ret;
|
|
unsigned char len;
|
|
gxByteBuffer tmp;
|
|
dlmsVARIANT t;
|
|
uint16_t start = (uint16_t)(reply->data.position - 1);
|
|
// Get invoke id.
|
|
if ((ret = bb_getUInt32(&reply->data, &id)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get date time.
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
reply->time = 0;
|
|
#else
|
|
memset(&reply->time, 0, sizeof(struct tm));
|
|
#endif //DLMS_USE_EPOCH_TIME
|
|
if ((ret = bb_getUInt8(&reply->data, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (len != 0)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
var_init(&t);
|
|
#else
|
|
gxtime tm;
|
|
GX_DATETIME(t) = &tm;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
unsigned char buff[12];
|
|
bb_attach(&tmp, buff, 0, sizeof(buff));
|
|
if ((ret = bb_set2(&tmp, &reply->data, reply->data.position, len)) != 0 ||
|
|
(ret = dlms_changeType(&tmp, DLMS_DATA_TYPE_DATETIME, &t)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
reply->time = t.dateTime->value;
|
|
#else
|
|
reply->time = tm.value;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
#else
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
reply->time = t.dateTime->value;
|
|
#else
|
|
reply->time = ((gxtime*)t.pVal)->value;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
#endif // DLMS_USE_EPOCH_TIME
|
|
}
|
|
if ((ret = dlms_getDataFromBlock(&reply->data, start)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
return dlms_getValueFromData(settings, reply);
|
|
#else
|
|
//Client app must do the data parsing if malloc is not used.
|
|
return 0;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
|
|
/**
|
|
* Handle General block transfer message.
|
|
*
|
|
* @param settings
|
|
* DLMS settings.
|
|
* @param data
|
|
* received data.
|
|
*/
|
|
int dlms_handleGbt(
|
|
dlmsSettings* settings,
|
|
gxReplyData* data)
|
|
{
|
|
int ret;
|
|
unsigned char bc;
|
|
uint16_t bn, bna;
|
|
uint16_t index = (uint16_t)(data->data.position - 1);
|
|
if ((ret = bb_getUInt8(&data->data, &bc)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Is streaming active.
|
|
data->streaming = (bc & 0x40) != 0;
|
|
data->windowSize = (bc & 0x3F);
|
|
// Block number.
|
|
if ((ret = bb_getUInt16(&data->data, &bn)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Block number acknowledged.
|
|
if ((ret = bb_getUInt16(&data->data, &bna)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Remove existing data when first block is received.
|
|
if (bn == 1)
|
|
{
|
|
index = 0;
|
|
}
|
|
else if (bna != settings->blockIndex - 1)
|
|
{
|
|
// If this block is already received.
|
|
data->data.size = index;
|
|
data->command = DLMS_COMMAND_NONE;
|
|
return 0;
|
|
}
|
|
|
|
data->blockNumber = bn;
|
|
// Block number acknowledged.
|
|
data->blockNumberAck = bna;
|
|
data->command = DLMS_COMMAND_NONE;
|
|
uint16_t len;
|
|
if (hlp_getObjectCount2(&data->data, &len) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
if (len != (data->data.size - data->data.position))
|
|
{
|
|
data->complete = 0;
|
|
return 0;
|
|
}
|
|
if ((ret = dlms_getDataFromBlock(&data->data, index)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
// Is Last block.
|
|
if ((bc & 0x80) == 0) {
|
|
data->moreData |= DLMS_DATA_REQUEST_TYPES_GBT;
|
|
}
|
|
else
|
|
{
|
|
data->moreData &= ~DLMS_DATA_REQUEST_TYPES_GBT;
|
|
if (data->data.size != 0)
|
|
{
|
|
data->data.position = 0;
|
|
if ((ret = dlms_getPdu(settings, data, 0)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get data if all data is read or we want to peek data.
|
|
if (data->data.position != data->data.size
|
|
&& (
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
data->command == DLMS_COMMAND_READ_RESPONSE ||
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
data->command == DLMS_COMMAND_GET_RESPONSE)
|
|
&& (data->moreData == DLMS_DATA_REQUEST_TYPES_NONE || data->peek))
|
|
{
|
|
data->data.position = 0;
|
|
ret = dlms_getValueFromData(settings, data);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if !defined(DLMS_IGNORE_SERVER)
|
|
int dlms_handleGloDedRequest(dlmsSettings* settings,
|
|
gxReplyData* data)
|
|
{
|
|
int ret = 0;
|
|
#ifdef DLMS_IGNORE_HIGH_GMAC
|
|
ret = DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
DLMS_SECURITY_SUITE suite;
|
|
uint64_t invocationCounter;
|
|
// If all frames are read.
|
|
if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0)
|
|
{
|
|
unsigned char ch;
|
|
DLMS_SECURITY security;
|
|
--data->data.position;
|
|
unsigned char emptySourceSystemTile;
|
|
emptySourceSystemTile = memcmp(settings->sourceSystemTitle, EMPTY_SYSTEM_TITLE, 8) == 0;
|
|
if (dlms_useDedicatedKey(settings) && (settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0)
|
|
{
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
settings->sourceSystemTitle,
|
|
settings->cipher.dedicatedKey,
|
|
&data->data,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
//If pre-set connection is made.
|
|
else if (dlms_usePreEstablishedConnection(settings) && emptySourceSystemTile)
|
|
{
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
if (settings->server && settings->connected == DLMS_CONNECTION_STATE_NONE && !data->preEstablished)
|
|
{
|
|
// Check is data send to this server.
|
|
if (!svr_isTarget(settings, settings->serverAddress, settings->clientAddress))
|
|
{
|
|
if ((settings->connected & DLMS_CONNECTION_STATE_DLMS) == 0)
|
|
{
|
|
settings->serverAddress = settings->clientAddress = 0;
|
|
}
|
|
return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS;
|
|
}
|
|
if ((ret = svr_connected((dlmsServerSettings*)settings)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
settings->preEstablishedSystemTitle->data,
|
|
&settings->cipher.blockCipherKey,
|
|
#else
|
|
settings->preEstablishedSystemTitle,
|
|
settings->cipher.blockCipherKey,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
& data->data,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (data->preEstablished == 0)
|
|
{
|
|
data->preEstablished = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
settings->sourceSystemTitle,
|
|
&settings->cipher.blockCipherKey,
|
|
#else
|
|
settings->sourceSystemTitle,
|
|
settings->cipher.blockCipherKey,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
& data->data,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
//If IC value is wrong.
|
|
if (settings->expectedInvocationCounter != NULL)
|
|
{
|
|
if (invocationCounter < *settings->expectedInvocationCounter)
|
|
{
|
|
return DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL;
|
|
}
|
|
//Update IC.
|
|
#ifdef DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
* settings->expectedInvocationCounter = (1 + invocationCounter);
|
|
#else
|
|
* settings->expectedInvocationCounter = (uint32_t)(1 + invocationCounter);
|
|
#endif //DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
}
|
|
// Get command.
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
data->encryptedCommand = data->command;
|
|
data->command = (DLMS_COMMAND)ch;
|
|
}
|
|
else
|
|
{
|
|
data->data.position -= 1;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
return ret;
|
|
}
|
|
#endif // !defined(DLMS_IGNORE_SERVER)
|
|
|
|
#if !defined(DLMS_IGNORE_CLIENT)
|
|
int dlms_handleGloDedResponse(dlmsSettings* settings,
|
|
gxReplyData* data, uint32_t index)
|
|
{
|
|
#ifdef DLMS_IGNORE_HIGH_GMAC
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
int ret = 0;
|
|
DLMS_SECURITY_SUITE suite;
|
|
uint64_t invocationCounter;
|
|
if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0)
|
|
{
|
|
DLMS_SECURITY security;
|
|
--data->data.position;
|
|
data->data.position = index;
|
|
gxByteBuffer bb;
|
|
bb_attach(&bb, data->data.data + index, bb_available(&data->data), bb_getCapacity(&data->data));
|
|
if ((settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0 && dlms_useDedicatedKey(settings))
|
|
{
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
settings->sourceSystemTitle,
|
|
settings->cipher.dedicatedKey,
|
|
&bb,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
settings->sourceSystemTitle,
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
& settings->cipher.blockCipherKey,
|
|
#else
|
|
settings->cipher.blockCipherKey,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
& bb,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
data->data.size = bb.size + index;
|
|
//If target is sending data ciphered using different security policy.
|
|
if (settings->cipher.security != security)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR;
|
|
}
|
|
if (settings->expectedInvocationCounter != NULL)
|
|
{
|
|
//If data is ciphered using invalid invocation counter value.
|
|
if (invocationCounter != *settings->expectedInvocationCounter)
|
|
{
|
|
return DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL;
|
|
}
|
|
#ifdef DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
* settings->expectedInvocationCounter = (1 + invocationCounter);
|
|
#else
|
|
* settings->expectedInvocationCounter = (uint32_t)(1 + invocationCounter);
|
|
#endif //DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
}
|
|
data->command = DLMS_COMMAND_NONE;
|
|
ret = dlms_getPdu(settings, data, 0);
|
|
data->cipherIndex = (uint16_t)data->data.size;
|
|
}
|
|
return ret;
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_CLIENT)
|
|
|
|
#if !defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC)
|
|
int dlms_handleGeneralCiphering(
|
|
dlmsSettings* settings,
|
|
gxReplyData* data)
|
|
{
|
|
#ifdef DLMS_IGNORE_HIGH_GMAC
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
unsigned char ch;
|
|
int ret;
|
|
// If all frames are read.
|
|
if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0)
|
|
{
|
|
--data->data.position;
|
|
DLMS_SECURITY security;
|
|
DLMS_SECURITY_SUITE suite;
|
|
uint64_t invocationCounter;
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
settings->sourceSystemTitle,
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
& settings->cipher.blockCipherKey,
|
|
#else
|
|
settings->cipher.blockCipherKey,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
& data->data,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get command
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
data->command = DLMS_COMMAND_NONE;
|
|
if (security != DLMS_SECURITY_NONE)
|
|
{
|
|
if ((ret = dlms_getPdu(settings, data, 0)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC)
|
|
|
|
#if !defined(DLMS_IGNORE_SERVER)
|
|
int32_t dlms_handleConfirmedServiceError(gxByteBuffer* data)
|
|
{
|
|
int32_t ret;
|
|
unsigned char ch;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
DLMS_CONFIRMED_SERVICE_ERROR service = (DLMS_CONFIRMED_SERVICE_ERROR)ch;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
DLMS_SERVICE_ERROR type = (DLMS_SERVICE_ERROR)ch;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ret = service;
|
|
ret <<= 16;
|
|
ret |= DLMS_ERROR_TYPE_CONFIRMED_SERVICE_ERROR;
|
|
ret |= type << 8;
|
|
ret |= ch;
|
|
return ret;
|
|
}
|
|
|
|
int dlms_handleExceptionResponse(gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
unsigned char ch;
|
|
// DLMS_EXCEPTION_STATE_ERROR state;
|
|
DLMS_EXCEPTION_SERVICE_ERROR error;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// state = (DLMS_EXCEPTION_STATE_ERROR)ch;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
error = (DLMS_EXCEPTION_SERVICE_ERROR)ch;
|
|
uint32_t value = 0;
|
|
if (error == DLMS_EXCEPTION_SERVICE_ERROR_INVOCATION_COUNTER_ERROR && bb_available(data) > 3)
|
|
{
|
|
bb_getUInt32(data, &value);
|
|
}
|
|
return DLMS_ERROR_TYPE_EXCEPTION_RESPONSE | value << 8 | error;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_SERVER)
|
|
|
|
|
|
int dlms_getPdu(
|
|
dlmsSettings* settings,
|
|
gxReplyData* data,
|
|
unsigned char first)
|
|
{
|
|
int ret = DLMS_ERROR_CODE_OK;
|
|
#if !defined(DLMS_IGNORE_CLIENT)
|
|
uint32_t index;
|
|
#endif //!defined(DLMS_IGNORE_CLIENT)
|
|
unsigned char ch;
|
|
DLMS_COMMAND cmd = data->command;
|
|
// If header is not read yet or GBT message.
|
|
if (cmd == DLMS_COMMAND_NONE)
|
|
{
|
|
// If PDU is missing.
|
|
if (bb_available(&data->data) == 0)
|
|
{
|
|
// Invalid PDU.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
#if !defined(DLMS_IGNORE_CLIENT)
|
|
index = data->data.position;
|
|
#endif //!defined(DLMS_IGNORE_CLIENT)
|
|
// Get command.
|
|
if ((ret = bb_getUInt8(&data->data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
cmd = (DLMS_COMMAND)ch;
|
|
data->command = cmd;
|
|
switch (cmd)
|
|
{
|
|
#if !defined(DLMS_IGNORE_CLIENT)
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_READ_RESPONSE:
|
|
if ((ret = dlms_handleReadResponse(settings, data, (uint16_t)index)) != 0)
|
|
{
|
|
if (ret == DLMS_ERROR_CODE_FALSE)
|
|
{
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
return ret;
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_GET_RESPONSE:
|
|
if ((ret = dlms_handleGetResponse(settings, data, (uint16_t)index)) != 0)
|
|
{
|
|
if (ret == DLMS_ERROR_CODE_FALSE)
|
|
{
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
return ret;
|
|
}
|
|
break;
|
|
case DLMS_COMMAND_SET_RESPONSE:
|
|
ret = dlms_handleSetResponse(settings, data);
|
|
break;
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_WRITE_RESPONSE:
|
|
ret = dlms_handleWriteResponse(data);
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_METHOD_RESPONSE:
|
|
ret = dlms_handleMethodResponse(settings, data);
|
|
break;
|
|
case DLMS_COMMAND_GENERAL_BLOCK_TRANSFER:
|
|
ret = dlms_handleGbt(settings, data);
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_CLIENT)
|
|
case DLMS_COMMAND_AARQ:
|
|
case DLMS_COMMAND_AARE:
|
|
// This is parsed later.
|
|
data->data.position -= 1;
|
|
break;
|
|
case DLMS_COMMAND_RELEASE_RESPONSE:
|
|
break;
|
|
#if !defined(DLMS_IGNORE_SERVER)
|
|
case DLMS_COMMAND_CONFIRMED_SERVICE_ERROR:
|
|
ret = dlms_handleConfirmedServiceError(&data->data);
|
|
break;
|
|
case DLMS_COMMAND_EXCEPTION_RESPONSE:
|
|
ret = dlms_handleExceptionResponse(&data->data);
|
|
break;
|
|
case DLMS_COMMAND_GET_REQUEST:
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_READ_REQUEST:
|
|
case DLMS_COMMAND_WRITE_REQUEST:
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_SET_REQUEST:
|
|
case DLMS_COMMAND_METHOD_REQUEST:
|
|
case DLMS_COMMAND_RELEASE_REQUEST:
|
|
// Server handles this.
|
|
if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) != 0)
|
|
{
|
|
break;
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_SERVER)
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
#if !defined(DLMS_IGNORE_SERVER)
|
|
case DLMS_COMMAND_GLO_READ_REQUEST:
|
|
case DLMS_COMMAND_GLO_WRITE_REQUEST:
|
|
case DLMS_COMMAND_GLO_GET_REQUEST:
|
|
case DLMS_COMMAND_GLO_SET_REQUEST:
|
|
case DLMS_COMMAND_GLO_METHOD_REQUEST:
|
|
case DLMS_COMMAND_DED_GET_REQUEST:
|
|
case DLMS_COMMAND_DED_SET_REQUEST:
|
|
case DLMS_COMMAND_DED_METHOD_REQUEST:
|
|
ret = dlms_handleGloDedRequest(settings, data);
|
|
// Server handles this.
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_SERVER)
|
|
#if !defined(DLMS_IGNORE_CLIENT)
|
|
case DLMS_COMMAND_GLO_READ_RESPONSE:
|
|
case DLMS_COMMAND_GLO_WRITE_RESPONSE:
|
|
case DLMS_COMMAND_GLO_GET_RESPONSE:
|
|
case DLMS_COMMAND_GLO_SET_RESPONSE:
|
|
case DLMS_COMMAND_GLO_METHOD_RESPONSE:
|
|
case DLMS_COMMAND_DED_GET_RESPONSE:
|
|
case DLMS_COMMAND_DED_SET_RESPONSE:
|
|
case DLMS_COMMAND_DED_EVENT_NOTIFICATION:
|
|
case DLMS_COMMAND_DED_METHOD_RESPONSE:
|
|
// If all frames are read.
|
|
ret = dlms_handleGloDedResponse(settings, data, index);
|
|
break;
|
|
#endif // !defined(DLMS_IGNORE_CLIENT)
|
|
case DLMS_COMMAND_GENERAL_GLO_CIPHERING:
|
|
case DLMS_COMMAND_GENERAL_DED_CIPHERING:
|
|
#if !defined(DLMS_IGNORE_SERVER)
|
|
if (settings->server)
|
|
{
|
|
if ((settings->connected & DLMS_CONNECTION_STATE_DLMS) == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR;
|
|
}
|
|
ret = dlms_handleGloDedRequest(settings, data);
|
|
}
|
|
#endif// !defined(DLMS_IGNORE_CLIENT)
|
|
#if !defined(DLMS_IGNORE_CLIENT)
|
|
#if !defined(DLMS_IGNORE_SERVER)
|
|
else
|
|
#endif // !defined(DLMS_IGNORE_SERVER)
|
|
{
|
|
ret = dlms_handleGloDedResponse(settings, data, index);
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_CLIENT)
|
|
break;
|
|
#if !defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC)
|
|
case DLMS_COMMAND_GENERAL_CIPHERING:
|
|
ret = dlms_handleGeneralCiphering(settings, data);
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC)
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
case DLMS_COMMAND_DATA_NOTIFICATION:
|
|
ret = dlms_handleDataNotification(settings, data);
|
|
// Client handles this.
|
|
break;
|
|
case DLMS_COMMAND_EVENT_NOTIFICATION:
|
|
// Client handles this.
|
|
break;
|
|
case DLMS_COMMAND_INFORMATION_REPORT:
|
|
// Client handles this.
|
|
break;
|
|
default:
|
|
// Invalid command.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0)
|
|
{
|
|
// Is whole block is read and if last packet and data is not try to
|
|
// peek.
|
|
if (!data->peek && data->moreData == DLMS_DATA_REQUEST_TYPES_NONE)
|
|
{
|
|
if (!settings->server || data->command == DLMS_COMMAND_AARE || data->command == DLMS_COMMAND_AARQ)
|
|
{
|
|
data->data.position = 0;
|
|
}
|
|
else
|
|
{
|
|
data->data.position = 1;
|
|
}
|
|
}
|
|
if (cmd == DLMS_COMMAND_GENERAL_BLOCK_TRANSFER)
|
|
{
|
|
data->data.position = data->cipherIndex + 1;
|
|
ret = dlms_handleGbt(settings, data);
|
|
data->cipherIndex = (uint16_t)data->data.size;
|
|
data->command = DLMS_COMMAND_NONE;
|
|
}
|
|
// Get command if operating as a server.
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
if (settings->server)
|
|
{
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
// Ciphered messages are handled after whole PDU is received.
|
|
switch (cmd)
|
|
{
|
|
case DLMS_COMMAND_GLO_READ_REQUEST:
|
|
case DLMS_COMMAND_GLO_WRITE_REQUEST:
|
|
case DLMS_COMMAND_GLO_GET_REQUEST:
|
|
case DLMS_COMMAND_GLO_SET_REQUEST:
|
|
case DLMS_COMMAND_GLO_METHOD_REQUEST:
|
|
data->command = DLMS_COMMAND_NONE;
|
|
data->data.position = (data->cipherIndex);
|
|
ret = dlms_getPdu(settings, data, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
}
|
|
else
|
|
#endif //DLMS_IGNORE_SERVER
|
|
{
|
|
// Client do not need a command any more.
|
|
data->command = DLMS_COMMAND_NONE;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
// Ciphered messages are handled after whole PDU is received.
|
|
switch (cmd)
|
|
{
|
|
case DLMS_COMMAND_GLO_READ_RESPONSE:
|
|
case DLMS_COMMAND_GLO_WRITE_RESPONSE:
|
|
case DLMS_COMMAND_GLO_GET_RESPONSE:
|
|
case DLMS_COMMAND_GLO_SET_RESPONSE:
|
|
case DLMS_COMMAND_GLO_METHOD_RESPONSE:
|
|
case DLMS_COMMAND_DED_GET_RESPONSE:
|
|
case DLMS_COMMAND_DED_SET_RESPONSE:
|
|
case DLMS_COMMAND_DED_METHOD_RESPONSE:
|
|
case DLMS_COMMAND_GENERAL_GLO_CIPHERING:
|
|
case DLMS_COMMAND_GENERAL_DED_CIPHERING:
|
|
data->data.position = data->cipherIndex;
|
|
ret = dlms_getPdu(settings, data, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
}
|
|
}
|
|
|
|
#if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME)
|
|
// Get data only blocks if SN is used. This is faster.
|
|
if (ret == 0 && cmd == DLMS_COMMAND_READ_RESPONSE
|
|
&& data->commandType == DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT
|
|
&& (data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME)
|
|
// Get data if all data is read or we want to peek data.
|
|
if (ret == 0 && !data->ignoreValue && data->data.position != data->data.size
|
|
&& (
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME)
|
|
cmd == DLMS_COMMAND_READ_RESPONSE ||
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME)
|
|
cmd == DLMS_COMMAND_GET_RESPONSE)
|
|
&& (data->moreData == DLMS_DATA_REQUEST_TYPES_NONE
|
|
|| data->peek))
|
|
{
|
|
ret = dlms_getValueFromData(settings, data);
|
|
}
|
|
#else
|
|
data->dataValue.byteArr = &data->data;
|
|
data->dataValue.vt = DLMS_DATA_TYPE_BYREF | DLMS_DATA_TYPE_OCTET_STRING;
|
|
#endif //!defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
/**
|
|
* Add LLC bytes to generated message.
|
|
*
|
|
* @param settings
|
|
* DLMS settings.
|
|
* @param data
|
|
* Data where bytes are added.
|
|
*/
|
|
int dlms_addLLCBytes(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
if (settings->server)
|
|
{
|
|
ret = bb_insert(LLC_REPLY_BYTES, 3, data, 0);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_insert(LLC_SEND_BYTES, 3, data, 0);
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
|
|
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
int dlms_updateSendTime(uint32_t value, gxByteBuffer* reply)
|
|
#else
|
|
int dlms_updateSendTime(struct tm* value, gxByteBuffer* reply)
|
|
#endif // DLMS_USE_EPOCH_TIME
|
|
{
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
if (value == 0)
|
|
#else
|
|
if (value == NULL)
|
|
#endif // DLMS_USE_EPOCH_TIME
|
|
{
|
|
return bb_setUInt8(reply, (unsigned char)DLMS_DATA_TYPE_NONE);
|
|
}
|
|
// Data is send in octet string. Remove data type.
|
|
int ret;
|
|
uint16_t pos = (uint16_t)reply->size;
|
|
dlmsVARIANT tmp;
|
|
gxtime t;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
tmp.dateTime = &t;
|
|
tmp.vt = DLMS_DATA_TYPE_DATETIME;
|
|
#else
|
|
GX_DATETIME(tmp) = &t;
|
|
#endif // DLMS_IGNORE_MALLOC
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
time_initUnix(&t, value);
|
|
#else
|
|
time_initUnix(&t, 0);
|
|
t.value = *value;
|
|
#endif //DLMS_USE_EPOCH_TIME
|
|
if ((ret = dlms_setData(reply, DLMS_DATA_TYPE_OCTET_STRING, &tmp)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Remove data type.
|
|
return bb_move(reply, pos + 1, pos, reply->size - pos - 1);
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
int dlms_appendMultipleSNBlocks(
|
|
gxSNParameters* p,
|
|
gxByteBuffer* reply)
|
|
{
|
|
uint32_t maxSize;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
unsigned char ciphering = p->settings->cipher.security != DLMS_SECURITY_NONE;
|
|
#else
|
|
unsigned char ciphering = 0;
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
uint32_t hSize = reply->size + 3;
|
|
// Add LLC bytes.
|
|
if (p->command == DLMS_COMMAND_WRITE_REQUEST
|
|
|| p->command == DLMS_COMMAND_READ_REQUEST)
|
|
{
|
|
hSize += 1 + hlp_getObjectCountSizeInBytes(p->count);
|
|
}
|
|
maxSize = p->settings->maxPduSize - hSize;
|
|
if (ciphering)
|
|
{
|
|
maxSize -= CIPHERING_HEADER_SIZE;
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (dlms_useHdlc(p->settings->interfaceType))
|
|
{
|
|
maxSize -= 3;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
}
|
|
maxSize -= hlp_getObjectCountSizeInBytes(maxSize);
|
|
if ((uint16_t)(p->data->size - p->data->position) > maxSize)
|
|
{
|
|
// More blocks.
|
|
bb_setUInt8(reply, 0);
|
|
}
|
|
else
|
|
{
|
|
// Last block.
|
|
bb_setUInt8(reply, p->lastBlock);
|
|
maxSize = p->data->size - p->data->position;
|
|
}
|
|
// Add block index.
|
|
bb_setUInt16(reply, p->blockIndex);
|
|
if (p->command == DLMS_COMMAND_WRITE_REQUEST)
|
|
{
|
|
++p->blockIndex;
|
|
hlp_setObjectCount(p->count, reply);
|
|
bb_setUInt8(reply, DLMS_DATA_TYPE_OCTET_STRING);
|
|
}
|
|
else if (p->command == DLMS_COMMAND_READ_REQUEST)
|
|
{
|
|
++p->blockIndex;
|
|
}
|
|
|
|
hlp_setObjectCount(maxSize, reply);
|
|
return maxSize;
|
|
}
|
|
|
|
int dlms_getSNPdu(
|
|
gxSNParameters* p,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret = 0, cnt = 0;
|
|
unsigned char cipherSize = 0;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
unsigned char ciphering = p->command != DLMS_COMMAND_AARQ && p->command != DLMS_COMMAND_AARE && p->settings->cipher.security != DLMS_SECURITY_NONE;
|
|
#else
|
|
unsigned char ciphering = 0;
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
gxByteBuffer* h;
|
|
if (p->settings->server)
|
|
{
|
|
gxByteBuffer header;
|
|
bb_attach(&header, pduAttributes, 0, sizeof(pduAttributes));
|
|
h = &header;
|
|
}
|
|
else
|
|
{
|
|
h = reply;
|
|
}
|
|
if (ciphering)
|
|
{
|
|
cipherSize = CIPHERING_HEADER_SIZE;
|
|
}
|
|
if (p->data != NULL)
|
|
{
|
|
cnt = p->data->size - p->data->position;
|
|
}
|
|
// Add command.
|
|
if (p->command == DLMS_COMMAND_INFORMATION_REPORT)
|
|
{
|
|
bb_setUInt8(h, (unsigned char)p->command);
|
|
// Add date time.
|
|
if ((ret = dlms_updateSendTime(p->time, reply)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
hlp_setObjectCount(p->count, reply);
|
|
bb_set2(reply, p->attributeDescriptor, 0, p->attributeDescriptor->size);
|
|
}
|
|
else if (p->command != DLMS_COMMAND_AARQ && p->command != DLMS_COMMAND_AARE)
|
|
{
|
|
bb_setUInt8(h, (unsigned char)p->command);
|
|
if (p->count != 0xFF)
|
|
{
|
|
hlp_setObjectCount(p->count, h);
|
|
}
|
|
if (!p->multipleBlocks)
|
|
{
|
|
if (p->requestType != 0xFF)
|
|
{
|
|
bb_setUInt8(h, p->requestType);
|
|
}
|
|
if (p->attributeDescriptor != NULL)
|
|
{
|
|
bb_set2(h, p->attributeDescriptor, 0, p->attributeDescriptor->size);
|
|
}
|
|
p->multipleBlocks = h->size + cipherSize + cnt > p->settings->maxPduSize;
|
|
// If reply data is not fit to one PDU.
|
|
if (p->multipleBlocks)
|
|
{
|
|
reply->size = 0;
|
|
if (p->command == DLMS_COMMAND_WRITE_REQUEST)
|
|
{
|
|
p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_WRITE_DATA_BLOCK_ACCESS;
|
|
}
|
|
else if (p->command == DLMS_COMMAND_READ_REQUEST)
|
|
{
|
|
p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_READ_DATA_BLOCK_ACCESS;
|
|
}
|
|
else if (p->command == DLMS_COMMAND_READ_RESPONSE)
|
|
{
|
|
p->requestType = DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT;
|
|
}
|
|
else
|
|
{
|
|
//Invalid command.
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
bb_setUInt8(reply, (unsigned char)p->command);
|
|
// Set object count.
|
|
bb_setUInt8(reply, 1);
|
|
if (p->requestType != 0xFF)
|
|
{
|
|
bb_setUInt8(reply, p->requestType);
|
|
}
|
|
cnt = dlms_appendMultipleSNBlocks(p, h);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (p->command == DLMS_COMMAND_WRITE_REQUEST)
|
|
{
|
|
p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_WRITE_DATA_BLOCK_ACCESS;
|
|
}
|
|
else if (p->command == DLMS_COMMAND_READ_REQUEST)
|
|
{
|
|
p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_READ_DATA_BLOCK_ACCESS;
|
|
}
|
|
else if (p->command == DLMS_COMMAND_READ_RESPONSE)
|
|
{
|
|
p->requestType = DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT;
|
|
}
|
|
else
|
|
{
|
|
//Invalid command.
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
if (p->requestType != 0xFF)
|
|
{
|
|
bb_setUInt8(h, p->requestType);
|
|
}
|
|
if (p->attributeDescriptor != NULL)
|
|
{
|
|
bb_set2(h, p->attributeDescriptor, 0, p->attributeDescriptor->size);
|
|
}
|
|
cnt = dlms_appendMultipleSNBlocks(p, h);
|
|
}
|
|
}
|
|
// Add data.
|
|
if (p->settings->server)
|
|
{
|
|
bb_insert(h->data, h->size, reply, 0);
|
|
}
|
|
else if (p->data != NULL)
|
|
{
|
|
bb_set2(reply, p->data, p->data->position, cnt);
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
// If Ciphering is used.
|
|
if (ciphering && p->command != DLMS_COMMAND_AARQ
|
|
&& p->command != DLMS_COMMAND_AARE)
|
|
{
|
|
ret = cip_encrypt(
|
|
&p->settings->cipher,
|
|
p->settings->cipher.security,
|
|
DLMS_COUNT_TYPE_PACKET,
|
|
p->settings->cipher.invocationCounter,
|
|
dlms_getGloMessage(p->settings, p->command, p->encryptedCommand),
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
p->settings->cipher.systemTitle.data,
|
|
&p->settings->cipher.blockCipherKey,
|
|
#else
|
|
p->settings->cipher.systemTitle,
|
|
p->settings->cipher.blockCipherKey,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
reply);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
reply->position = reply->size = 0;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (ret == 0 && dlms_useHdlc(p->settings->interfaceType))
|
|
{
|
|
ret = dlms_addLLCBytes(p->settings, reply);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
return 0;
|
|
}
|
|
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
|
|
|
|
/**
|
|
* Check is all data fit to one data block.
|
|
*
|
|
* @param p
|
|
* LN parameters.
|
|
* @param reply
|
|
* Generated reply.
|
|
*/
|
|
void dlms_multipleBlocks(
|
|
gxLNParameters* p,
|
|
gxByteBuffer* reply,
|
|
unsigned char ciphering)
|
|
{
|
|
// Check is all data fit to one message if data is given.
|
|
int len = bb_available(p->data);
|
|
if (p->attributeDescriptor != NULL)
|
|
{
|
|
len += p->attributeDescriptor->size;
|
|
}
|
|
if (ciphering)
|
|
{
|
|
len += CIPHERING_HEADER_SIZE;
|
|
}
|
|
if (!p->multipleBlocks)
|
|
{
|
|
// Add command type and invoke and priority.
|
|
p->multipleBlocks = 2 + reply->size + len > p->settings->maxPduSize;
|
|
}
|
|
if (p->lastBlock)
|
|
{
|
|
// Add command type and invoke and priority.
|
|
p->lastBlock = !(8 + reply->size + len > p->settings->maxPduSize);
|
|
}
|
|
}
|
|
|
|
int dlms_getLNPdu(
|
|
gxLNParameters* p,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret = 0;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
unsigned char ciphering = (p->command != DLMS_COMMAND_AARQ && p->command != DLMS_COMMAND_AARE &&
|
|
p->settings->cipher.security != DLMS_SECURITY_NONE) || p->encryptedCommand != DLMS_COMMAND_NONE;
|
|
#else
|
|
unsigned char ciphering = 0;
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t len = 0;
|
|
#else
|
|
uint16_t len = 0;
|
|
#endif
|
|
if (p->command == DLMS_COMMAND_AARQ)
|
|
{
|
|
//Data is already added to reply when malloc is not used.
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if ((ret = bb_set2(reply, p->attributeDescriptor, 0, p->attributeDescriptor->size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
else
|
|
{
|
|
gxByteBuffer header;
|
|
gxByteBuffer* h;
|
|
if (p->settings->server)
|
|
{
|
|
bb_attach(&header, pduAttributes, 0, sizeof(pduAttributes));
|
|
h = &header;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
bb_attach(&header, pduAttributes, 0, sizeof(pduAttributes));
|
|
h = &header;
|
|
#else
|
|
h = reply;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
// Add command.
|
|
if (p->command != DLMS_COMMAND_GENERAL_BLOCK_TRANSFER)
|
|
{
|
|
ret = bb_setUInt8(h, (unsigned char)p->command);
|
|
}
|
|
if (p->command == DLMS_COMMAND_EVENT_NOTIFICATION ||
|
|
p->command == DLMS_COMMAND_DATA_NOTIFICATION ||
|
|
p->command == DLMS_COMMAND_ACCESS_REQUEST ||
|
|
p->command == DLMS_COMMAND_ACCESS_RESPONSE)
|
|
{
|
|
// Add Long-Invoke-Id-And-Priority
|
|
if (p->command != DLMS_COMMAND_EVENT_NOTIFICATION)
|
|
{
|
|
if (p->invokeId != 0)
|
|
{
|
|
ret = bb_setUInt32(h, p->invokeId);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_setUInt32(h, dlms_getLongInvokeIDPriority(p->settings));
|
|
}
|
|
}
|
|
// Add date time.
|
|
ret = dlms_updateSendTime(p->time, h);
|
|
}
|
|
else if (p->command != DLMS_COMMAND_RELEASE_REQUEST)
|
|
{
|
|
// Get request size can be bigger than PDU size.
|
|
if (p->command != DLMS_COMMAND_GET_REQUEST && p->data != NULL
|
|
&& p->data->size != 0)
|
|
{
|
|
dlms_multipleBlocks(p, h, ciphering);
|
|
}
|
|
// Change Request type if Set request and multiple blocks is
|
|
// needed.
|
|
if (p->command == DLMS_COMMAND_SET_REQUEST)
|
|
{
|
|
if (p->multipleBlocks)
|
|
{
|
|
if (p->requestType == 1)
|
|
{
|
|
p->requestType = 2;
|
|
}
|
|
else if (p->requestType == 2)
|
|
{
|
|
p->requestType = 3;
|
|
}
|
|
}
|
|
}
|
|
// Change request type If get response and multiple blocks is
|
|
// needed.
|
|
if (p->command == DLMS_COMMAND_GET_RESPONSE)
|
|
{
|
|
if (p->multipleBlocks)
|
|
{
|
|
if (p->requestType == 1)
|
|
{
|
|
p->requestType = 2;
|
|
}
|
|
}
|
|
}
|
|
ret = bb_setUInt8(h, p->requestType);
|
|
// Add Invoke Id And Priority.
|
|
if (p->invokeId != 0)
|
|
{
|
|
ret = bb_setUInt8(h, p->invokeId);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_setUInt8(h, dlms_getInvokeIDPriority(p->settings, p->settings->autoIncreaseInvokeID));
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
// Add attribute descriptor.
|
|
if (ret == 0 && p->attributeDescriptor != NULL)
|
|
{
|
|
ret = bb_set(reply, p->attributeDescriptor->data, p->attributeDescriptor->size);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (ret == 0 &&
|
|
p->command != DLMS_COMMAND_EVENT_NOTIFICATION &&
|
|
p->command != DLMS_COMMAND_DATA_NOTIFICATION &&
|
|
(p->settings->negotiatedConformance & DLMS_CONFORMANCE_GENERAL_BLOCK_TRANSFER) == 0)
|
|
{
|
|
int totalLength;
|
|
// If multiple blocks.
|
|
if (p->multipleBlocks)
|
|
{
|
|
// Is last block.
|
|
if (p->lastBlock)
|
|
{
|
|
ret = bb_setUInt8(h, 1);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_setUInt8(h, 0);
|
|
}
|
|
// Block index.
|
|
ret = bb_setUInt32(h, p->blockIndex);
|
|
p->blockIndex = p->blockIndex + 1;
|
|
// Add status if reply.
|
|
if (p->status != 0xFF)
|
|
{
|
|
if (p->status != 0 && p->command == DLMS_COMMAND_GET_RESPONSE)
|
|
{
|
|
ret = bb_setUInt8(h, 1);
|
|
}
|
|
ret = bb_setUInt8(h, p->status);
|
|
}
|
|
// Block size.
|
|
if (p->data != NULL)
|
|
{
|
|
len = (uint16_t)(p->data->size - p->data->position);
|
|
}
|
|
else
|
|
{
|
|
len = 0;
|
|
}
|
|
totalLength = len + h->size;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (ciphering)
|
|
{
|
|
totalLength += CIPHERING_HEADER_SIZE;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
if (totalLength > p->settings->maxPduSize)
|
|
{
|
|
len = (uint16_t)(p->settings->maxPduSize - h->size);
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (ciphering)
|
|
{
|
|
len -= CIPHERING_HEADER_SIZE;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
len -= hlp_getObjectCountSizeInBytes(len);
|
|
}
|
|
ret = hlp_setObjectCount(len, h);
|
|
if (ret == 0)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if ((ret = bb_insert(h->data, h->size, reply, 0)) != 0)
|
|
{
|
|
//If this fails PDU buffer size must be bigger.
|
|
return ret;
|
|
}
|
|
#else
|
|
if (p->settings->server)
|
|
{
|
|
if ((ret = bb_insert(h->data, h->size, reply, 0)) != 0)
|
|
{
|
|
//If this fails PDU buffer size must be bigger.
|
|
return ret;
|
|
}
|
|
}
|
|
else if (p->data != NULL)
|
|
{
|
|
ret = bb_set2(reply, p->data, p->data->position, len);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
}
|
|
}
|
|
// Add data that fits to one block.
|
|
if (ret == 0 && len == 0)
|
|
{
|
|
// Add status if reply.
|
|
if (p->status != 0xFF)
|
|
{
|
|
if (p->status != 0
|
|
&& (p->command == DLMS_COMMAND_GET_RESPONSE))
|
|
{
|
|
ret = bb_setUInt8(h, 1);
|
|
}
|
|
ret = bb_setUInt8(h, p->status);
|
|
}
|
|
if (ret == 0 && p->data != NULL && p->data->size != 0)
|
|
{
|
|
len = bb_available(p->data);
|
|
if (len + reply->size > p->settings->maxPduSize)
|
|
{
|
|
len = (uint16_t)(p->settings->maxPduSize - h->size - p->data->size - p->data->position);
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
ret = bb_insert(h->data, h->size, reply, 0);
|
|
if (!p->settings->server)
|
|
{
|
|
p->data->position += len;
|
|
}
|
|
#else
|
|
if (p->settings->server)
|
|
{
|
|
ret = bb_insert(h->data, h->size, reply, 0);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_set2(reply, p->data, p->data->position, len);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
else
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
ret = bb_insert(h->data, h->size, reply, 0);
|
|
#else
|
|
if (p->settings->server)
|
|
{
|
|
ret = bb_insert(h->data, h->size, reply, 0);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (ret == 0 && ciphering && reply->size != 0 && p->command != DLMS_COMMAND_RELEASE_REQUEST)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
gxByteBuffer* key;
|
|
#else
|
|
unsigned char* key;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (p->settings->cipher.broacast)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
key = &p->settings->cipher.broadcastBlockCipherKey;
|
|
#else
|
|
key = p->settings->cipher.broadcastBlockCipherKey;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
else if (dlms_useDedicatedKey(p->settings) && (p->settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0)
|
|
{
|
|
key = p->settings->cipher.dedicatedKey;
|
|
}
|
|
else
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
key = &p->settings->cipher.blockCipherKey;
|
|
#else
|
|
key = p->settings->cipher.blockCipherKey;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
ret = cip_encrypt(
|
|
&p->settings->cipher,
|
|
p->settings->cipher.security,
|
|
DLMS_COUNT_TYPE_PACKET,
|
|
p->settings->cipher.invocationCounter,
|
|
dlms_getGloMessage(p->settings, p->command, p->encryptedCommand),
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
p->settings->cipher.systemTitle.data,
|
|
#else
|
|
p->settings->cipher.systemTitle,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
key,
|
|
reply);
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC1
|
|
|
|
if (p->command == DLMS_COMMAND_GENERAL_BLOCK_TRANSFER ||
|
|
(p->multipleBlocks && (p->settings->negotiatedConformance & DLMS_CONFORMANCE_GENERAL_BLOCK_TRANSFER) != 0))
|
|
{
|
|
bb_setUInt8(h, DLMS_COMMAND_GENERAL_BLOCK_TRANSFER);
|
|
dlms_multipleBlocks(p, h, ciphering);
|
|
// Is last block
|
|
if (!p->lastBlock)
|
|
{
|
|
bb_setUInt8(h, 0);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(h, 0x80);
|
|
}
|
|
// Set block number sent.
|
|
bb_setUInt8(h, 0);
|
|
// Set block number acknowledged
|
|
bb_setUInt8(h, (unsigned char)p->blockIndex);
|
|
p->blockIndex = p->blockIndex + 1;
|
|
// Add APU tag.
|
|
bb_setUInt8(h, 0);
|
|
// Add Addl fields
|
|
bb_setUInt8(h, 0);
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (ret == 0 && dlms_useHdlc(p->settings->interfaceType))
|
|
{
|
|
ret = dlms_addLLCBytes(p->settings, reply);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
return ret;
|
|
}
|
|
|
|
int dlms_getLnMessages(
|
|
gxLNParameters* p,
|
|
message* messages)
|
|
{
|
|
int ret;
|
|
gxByteBuffer* pdu;
|
|
gxByteBuffer* it;
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
unsigned char frame = 0;
|
|
if (p->command == DLMS_COMMAND_DATA_NOTIFICATION ||
|
|
p->command == DLMS_COMMAND_EVENT_NOTIFICATION)
|
|
{
|
|
frame = 0x13;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
pdu = p->serializedPdu;
|
|
#else
|
|
gxByteBuffer reply;
|
|
if (p->serializedPdu == NULL)
|
|
{
|
|
BYTE_BUFFER_INIT(&reply);
|
|
pdu = &reply;
|
|
}
|
|
else
|
|
{
|
|
pdu = p->serializedPdu;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
do
|
|
{
|
|
if ((ret = dlms_getLNPdu(p, pdu)) == 0)
|
|
{
|
|
p->lastBlock = 1;
|
|
if (p->attributeDescriptor == NULL)
|
|
{
|
|
++p->settings->blockIndex;
|
|
}
|
|
}
|
|
while (ret == 0 && pdu->position != pdu->size)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (!(messages->size < messages->capacity))
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
it = messages->data[messages->size];
|
|
++messages->size;
|
|
bb_clear(it);
|
|
#else
|
|
if (messages->attached)
|
|
{
|
|
if (messages->size < messages->capacity)
|
|
{
|
|
it = messages->data[messages->size];
|
|
++messages->size;
|
|
bb_clear(it);
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
it = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
|
|
if (it == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
BYTE_BUFFER_INIT(it);
|
|
mes_push(messages, it);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
switch (p->settings->interfaceType)
|
|
{
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
case DLMS_INTERFACE_TYPE_WRAPPER:
|
|
ret = dlms_getWrapperFrame(p->settings, p->command, pdu, it);
|
|
break;
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
case DLMS_INTERFACE_TYPE_HDLC:
|
|
case DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E:
|
|
ret = dlms_getHdlcFrame(p->settings, frame, pdu, it);
|
|
if (ret == 0 && pdu->position != pdu->size)
|
|
{
|
|
frame = getNextSend(p->settings, 0);
|
|
}
|
|
break;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
case DLMS_INTERFACE_TYPE_PDU:
|
|
ret = bb_set2(it, pdu, 0, pdu->size);
|
|
break;
|
|
#ifndef DLMS_IGNORE_PLC
|
|
case DLMS_INTERFACE_TYPE_PLC:
|
|
ret = dlms_getPlcFrame(p->settings, 0x90, pdu, it);
|
|
break;
|
|
case DLMS_INTERFACE_TYPE_PLC_HDLC:
|
|
ret = dlms_getMacHdlcFrame(p->settings, frame, 0, pdu, it);
|
|
break;
|
|
#endif //DLMS_IGNORE_PLC
|
|
default:
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
bb_clear(pdu);
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
frame = 0;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
} while (ret == 0 && p->data != NULL && p->data->position != p->data->size);
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
int dlms_getSnMessages(
|
|
gxSNParameters* p,
|
|
message* messages)
|
|
{
|
|
int ret;
|
|
gxByteBuffer data;
|
|
gxByteBuffer* it;
|
|
unsigned char frame = 0x0;
|
|
if (p->command == DLMS_COMMAND_INFORMATION_REPORT)
|
|
{
|
|
if ((p->settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0)
|
|
{
|
|
//If connection is established.
|
|
frame = 0x13;
|
|
}
|
|
else
|
|
{
|
|
frame = 0x3;
|
|
}
|
|
}
|
|
else if (p->command == DLMS_COMMAND_NONE)
|
|
{
|
|
frame = getNextSend(p->settings, 1);
|
|
}
|
|
BYTE_BUFFER_INIT(&data);
|
|
mes_clear(messages);
|
|
do
|
|
{
|
|
ret = dlms_getSNPdu(p, &data);
|
|
// Command is not add to next PDUs.
|
|
while (ret == 0 && data.position != data.size)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
it = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer));
|
|
#else
|
|
if (!(messages->size < messages->capacity))
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
it = messages->data[messages->size];
|
|
++messages->size;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
BYTE_BUFFER_INIT(it);
|
|
if (p->settings->interfaceType == DLMS_INTERFACE_TYPE_WRAPPER)
|
|
{
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
ret = dlms_getWrapperFrame(p->settings, p->command, &data, it);
|
|
#else
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
}
|
|
else
|
|
{
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
ret = dlms_getHdlcFrame(p->settings, frame, &data, it);
|
|
if (data.position != data.size)
|
|
{
|
|
frame = getNextSend(p->settings, 0);
|
|
}
|
|
#else
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
mes_push(messages, it);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
bb_clear(&data);
|
|
frame = 0;
|
|
} while (ret == 0 && p->data != NULL && p->data->position != p->data->size);
|
|
return 0;
|
|
}
|
|
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
|
|
int dlms_getData2(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* reply,
|
|
gxReplyData* data,
|
|
unsigned char first)
|
|
{
|
|
unsigned char isNotify;
|
|
return dlms_getData3(settings, reply, data, NULL, first, &isNotify);
|
|
}
|
|
|
|
int dlms_getData3(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* reply,
|
|
gxReplyData* data,
|
|
gxReplyData* notify,
|
|
unsigned char first,
|
|
unsigned char* isNotify)
|
|
{
|
|
int ret;
|
|
unsigned char frame = 0;
|
|
if (isNotify != NULL)
|
|
{
|
|
*isNotify = 0;
|
|
}
|
|
switch (settings->interfaceType)
|
|
{
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
case DLMS_INTERFACE_TYPE_HDLC:
|
|
case DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E:
|
|
ret = dlms_getHdlcData(settings->server, settings, reply, data, &frame, data->preEstablished, first);
|
|
break;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
case DLMS_INTERFACE_TYPE_WRAPPER:
|
|
ret = dlms_getTcpData(settings, reply, data, notify, isNotify);
|
|
break;
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
#ifndef DLMS_IGNORE_WIRELESS_MBUS
|
|
case DLMS_INTERFACE_TYPE_WIRELESS_MBUS:
|
|
ret = dlms_getMBusData(settings, reply, data);
|
|
break;
|
|
#endif //DLMS_IGNORE_WIRELESS_MBUS
|
|
case DLMS_INTERFACE_TYPE_PDU:
|
|
data->packetLength = reply->size;
|
|
data->complete = reply->size != 0;
|
|
ret = 0;
|
|
break;
|
|
#ifndef DLMS_IGNORE_PLC
|
|
case DLMS_INTERFACE_TYPE_PLC:
|
|
ret = dlms_getPlcData(settings, reply, data);
|
|
break;
|
|
case DLMS_INTERFACE_TYPE_PLC_HDLC:
|
|
ret = dlms_getPlcHdlcData(settings, reply, data, &frame);
|
|
#endif //DLMS_IGNORE_PLC
|
|
break;
|
|
default:
|
|
// Invalid Interface type.
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (*isNotify && notify != NULL)
|
|
{
|
|
if (!notify->complete)
|
|
{
|
|
// If all data is not read yet.
|
|
return 0;
|
|
}
|
|
data = notify;
|
|
}
|
|
else if (!data->complete)
|
|
{
|
|
// If all data is not read yet.
|
|
return 0;
|
|
}
|
|
if (settings->interfaceType != DLMS_INTERFACE_TYPE_PLC_HDLC)
|
|
{
|
|
if ((ret = dlms_getDataFromFrame(reply, data, dlms_useHdlc(settings->interfaceType))) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
// If keepalive or get next frame request.
|
|
if (((frame != 0x13 && frame != 0x3) || data->moreData != DLMS_DATA_REQUEST_TYPES_NONE) && (frame & 0x1) != 0)
|
|
{
|
|
if (dlms_useHdlc(settings->interfaceType) && data->data.size != 0)
|
|
{
|
|
if (reply->position != reply->size)
|
|
{
|
|
reply->position += 3;
|
|
}
|
|
}
|
|
if (data->command == DLMS_COMMAND_REJECTED)
|
|
{
|
|
return DLMS_ERROR_CODE_REJECTED;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
ret = dlms_getPdu(settings, data, first);
|
|
if (ret == 0 && notify != NULL && !isNotify)
|
|
{
|
|
//Check command to make sure it's not notify message.
|
|
switch (data->command)
|
|
{
|
|
case DLMS_COMMAND_DATA_NOTIFICATION:
|
|
case DLMS_COMMAND_GLO_EVENT_NOTIFICATION_REQUEST:
|
|
case DLMS_COMMAND_INFORMATION_REPORT:
|
|
case DLMS_COMMAND_EVENT_NOTIFICATION:
|
|
case DLMS_COMMAND_DED_EVENT_NOTIFICATION:
|
|
*isNotify = 1;
|
|
notify->complete = data->complete;
|
|
notify->moreData = data->moreData;
|
|
notify->command = data->command;
|
|
data->command = DLMS_COMMAND_NONE;
|
|
notify->time = data->time;
|
|
#ifdef DLMS_USE_EPOCH_TIME
|
|
data->time = 0;
|
|
#else
|
|
memset(&data->time, 0, sizeof(data->time));
|
|
#endif // DLMS_USE_EPOCH_TIME
|
|
bb_set2(¬ify->data, &data->data, data->data.position, bb_available(&data->data));
|
|
bb_clear(&data->data);
|
|
notify->dataValue = data->dataValue;
|
|
data->dataValue.vt = DLMS_DATA_TYPE_NONE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dlms_generateChallenge(
|
|
gxByteBuffer* challenge)
|
|
{
|
|
// Random challenge is 8 to 64 bytes.
|
|
// Texas Instruments accepts only 16 byte int32_t challenge.
|
|
// For this reason challenge size is 16 bytes at the moment.
|
|
int ret = 0, pos, len = 16;//hlp_rand() % 58 + 8;
|
|
bb_clear(challenge);
|
|
for (pos = 0; pos != len; ++pos)
|
|
{
|
|
if ((ret = bb_setUInt8(challenge, hlp_rand())) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
int dlms_getActionInfo(
|
|
DLMS_OBJECT_TYPE objectType,
|
|
unsigned char* value,
|
|
unsigned char* count)
|
|
{
|
|
switch (objectType)
|
|
{
|
|
case DLMS_OBJECT_TYPE_DATA:
|
|
case DLMS_OBJECT_TYPE_ACTION_SCHEDULE:
|
|
case DLMS_OBJECT_TYPE_AUTO_ANSWER:
|
|
case DLMS_OBJECT_TYPE_AUTO_CONNECT:
|
|
case DLMS_OBJECT_TYPE_MAC_ADDRESS_SETUP:
|
|
case DLMS_OBJECT_TYPE_GPRS_SETUP:
|
|
case DLMS_OBJECT_TYPE_IEC_HDLC_SETUP:
|
|
case DLMS_OBJECT_TYPE_IEC_LOCAL_PORT_SETUP:
|
|
case DLMS_OBJECT_TYPE_IEC_TWISTED_PAIR_SETUP:
|
|
case DLMS_OBJECT_TYPE_MODEM_CONFIGURATION:
|
|
case DLMS_OBJECT_TYPE_PPP_SETUP:
|
|
case DLMS_OBJECT_TYPE_REGISTER_MONITOR:
|
|
case DLMS_OBJECT_TYPE_ZIG_BEE_SAS_STARTUP:
|
|
case DLMS_OBJECT_TYPE_ZIG_BEE_SAS_JOIN:
|
|
case DLMS_OBJECT_TYPE_ZIG_BEE_SAS_APS_FRAGMENTATION:
|
|
case DLMS_OBJECT_TYPE_ZIG_BEE_NETWORK_CONTROL:
|
|
case DLMS_OBJECT_TYPE_SCHEDULE:
|
|
case DLMS_OBJECT_TYPE_SMTP_SETUP:
|
|
case DLMS_OBJECT_TYPE_STATUS_MAPPING:
|
|
case DLMS_OBJECT_TYPE_TCP_UDP_SETUP:
|
|
case DLMS_OBJECT_TYPE_UTILITY_TABLES:
|
|
*value = 0;
|
|
*count = 0;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_MBUS_DIAGNOSTIC:
|
|
*value = 48;
|
|
*count = 1;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_IMAGE_TRANSFER:
|
|
*value = 0x40;
|
|
*count = 4;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_ACTIVITY_CALENDAR:
|
|
*value = 0x50;
|
|
*count = 1;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME:
|
|
*value = 0x60;
|
|
*count = 4;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME:
|
|
*value = 0x20;
|
|
*count = 8;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_CLOCK:
|
|
*value = 0x60;
|
|
*count = 6;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_DEMAND_REGISTER:
|
|
*value = 0x48;
|
|
*count = 2;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_EXTENDED_REGISTER:
|
|
*value = 0x38;
|
|
*count = 1;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_IP4_SETUP:
|
|
*value = 0x60;
|
|
*count = 3;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_MBUS_SLAVE_PORT_SETUP:
|
|
*value = 0x60;
|
|
*count = 8;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_PROFILE_GENERIC:
|
|
*value = 0x58;
|
|
*count = 4;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_REGISTER:
|
|
*value = 0x28;
|
|
*count = 1;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_REGISTER_ACTIVATION:
|
|
*value = 0x30;
|
|
*count = 3;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_REGISTER_TABLE:
|
|
*value = 0x28;
|
|
*count = 2;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_SAP_ASSIGNMENT:
|
|
case DLMS_OBJECT_TYPE_SCRIPT_TABLE:
|
|
*value = 0x20;
|
|
*count = 1;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_SPECIAL_DAYS_TABLE:
|
|
*value = 0x10;
|
|
*count = 2;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_DISCONNECT_CONTROL:
|
|
*value = 0x20;
|
|
*count = 2;
|
|
break;
|
|
case DLMS_OBJECT_TYPE_PUSH_SETUP:
|
|
*value = 0x38;
|
|
*count = 1;
|
|
break;
|
|
default:
|
|
*count = *value = 0;
|
|
break;
|
|
}
|
|
return DLMS_ERROR_CODE_OK;
|
|
}
|
|
#endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
|
|
int dlms_secure(
|
|
dlmsSettings* settings,
|
|
int32_t ic,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* secret,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret = 0;
|
|
gxByteBuffer challenge;
|
|
bb_clear(reply);
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
bb_attach(&challenge, pduAttributes, 0, sizeof(pduAttributes));
|
|
#else
|
|
BYTE_BUFFER_INIT(&challenge);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (settings->authentication == DLMS_AUTHENTICATION_HIGH)
|
|
{
|
|
#ifndef DLMS_IGNORE_AES
|
|
unsigned char tmp[16];
|
|
gxByteBuffer s;
|
|
uint16_t len = (uint16_t)data->size;
|
|
bb_attach(&s, tmp, 0, sizeof(tmp));
|
|
if (len % 16 != 0)
|
|
{
|
|
len += (16 - (data->size % 16));
|
|
}
|
|
if (secret->size > data->size)
|
|
{
|
|
len = (uint16_t)secret->size;
|
|
if (len % 16 != 0)
|
|
{
|
|
len += (16 - (secret->size % 16));
|
|
}
|
|
}
|
|
if ((ret = bb_set(&s, secret->data, secret->size)) == 0 &&
|
|
(ret = bb_zero(&s, s.size, len - s.size)) == 0 &&
|
|
(ret = bb_set(&challenge, data->data, data->size)) == 0 &&
|
|
(ret = bb_zero(&challenge, challenge.size, len - challenge.size)) == 0 &&
|
|
(ret = bb_capacity(reply, challenge.size)) == 0)
|
|
{
|
|
gxaes_ecb_encrypt(challenge.data, s.data, reply->data, s.size);
|
|
}
|
|
reply->size = s.size;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_clear(&s);
|
|
bb_clear(&challenge);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
#else
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#endif //DLMS_IGNORE_AES
|
|
}
|
|
// Get server Challenge.
|
|
// Get shared secret
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->authentication != DLMS_AUTHENTICATION_HIGH_GMAC)
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
{
|
|
if (settings->authentication == DLMS_AUTHENTICATION_HIGH_SHA256)
|
|
{
|
|
//If SHA256 is not used.
|
|
#ifdef DLMS_IGNORE_HIGH_SHA256
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if ((ret = bb_set(&challenge, secret->data, secret->size)) != 0 ||
|
|
(ret = bb_set(&challenge, settings->cipher.systemTitle, 8)) != 0 ||
|
|
(ret = bb_set(&challenge, settings->sourceSystemTitle, 8)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#else
|
|
if ((ret = bb_set(&challenge, secret->data, secret->size)) != 0 ||
|
|
(ret = bb_set(&challenge, settings->cipher.systemTitle.data, settings->cipher.systemTitle.size)) != 0 ||
|
|
(ret = bb_set(&challenge, settings->sourceSystemTitle, 8)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (settings->server)
|
|
{
|
|
if ((ret = bb_set(&challenge, settings->ctoSChallenge.data, settings->ctoSChallenge.size)) != 0 ||
|
|
(ret = bb_set(&challenge, settings->stoCChallenge.data, settings->stoCChallenge.size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_set(&challenge, settings->stoCChallenge.data, settings->stoCChallenge.size)) != 0 ||
|
|
(ret = bb_set(&challenge, settings->ctoSChallenge.data, settings->ctoSChallenge.size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
#endif //DLMS_IGNORE_HIGH_SHA256
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_set(&challenge, data->data, data->size)) != 0 ||
|
|
(ret = bb_set(&challenge, secret->data, secret->size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
if (settings->authentication == DLMS_AUTHENTICATION_HIGH_MD5)
|
|
{
|
|
//If MD5 is not used.
|
|
#ifdef DLMS_IGNORE_HIGH_MD5
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
ret = gxmd5_encrypt(&challenge, reply);
|
|
bb_clear(&challenge);
|
|
return ret;
|
|
#endif //DLMS_IGNORE_HIGH_MD5
|
|
}
|
|
else if (settings->authentication == DLMS_AUTHENTICATION_HIGH_SHA1)
|
|
{
|
|
//If SHA1 is not used.
|
|
#ifdef DLMS_IGNORE_HIGH_SHA1
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
ret = gxsha1_encrypt(&challenge, reply);
|
|
bb_clear(&challenge);
|
|
return ret;
|
|
#endif //DLMS_IGNORE_HIGH_SHA1
|
|
}
|
|
else if (settings->authentication == DLMS_AUTHENTICATION_HIGH_SHA256)
|
|
{
|
|
//If SHA256 is not used.
|
|
#ifdef DLMS_IGNORE_HIGH_SHA256
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
ret = gxsha256_encrypt(&challenge, reply);
|
|
bb_clear(&challenge);
|
|
return ret;
|
|
#endif //DLMS_IGNORE_HIGH_SHA256
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
else if (settings->authentication == DLMS_AUTHENTICATION_HIGH_GMAC)
|
|
{
|
|
//If GMAC is not used.
|
|
#ifdef DLMS_IGNORE_HIGH_GMAC
|
|
bb_clear(&challenge);
|
|
return DLMS_ERROR_CODE_NOT_IMPLEMENTED;
|
|
#else
|
|
ret = cip_encrypt(
|
|
&settings->cipher,
|
|
DLMS_SECURITY_AUTHENTICATION,
|
|
DLMS_COUNT_TYPE_TAG,
|
|
ic,
|
|
0,
|
|
secret->data,
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
settings->cipher.blockCipherKey,
|
|
#else
|
|
& settings->cipher.blockCipherKey,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
data);
|
|
if (ret == 0)
|
|
{
|
|
if ((ret = bb_setUInt8(reply, DLMS_SECURITY_AUTHENTICATION | settings->cipher.suite)) != 0 ||
|
|
(ret = bb_setUInt32(reply, ic)) != 0 ||
|
|
(ret = bb_set2(reply, data, data->size - 12, 12)) != 0)
|
|
{
|
|
|
|
}
|
|
}
|
|
bb_clear(&challenge);
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
return ret;
|
|
}
|
|
|
|
int dlms_parseSnrmUaResponse(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
uint32_t value;
|
|
unsigned char ch, id, len;
|
|
uint16_t tmp;
|
|
int ret;
|
|
//If default settings are used.
|
|
if (data->size - data->position == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
// Skip FromatID
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Skip Group ID.
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Skip Group len
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
while (data->position < data->size)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &id)) != 0 ||
|
|
(ret = bb_getUInt8(data, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
switch (len)
|
|
{
|
|
case 1:
|
|
ret = bb_getUInt8(data, &ch);
|
|
value = ch;
|
|
break;
|
|
case 2:
|
|
ret = bb_getUInt16(data, &tmp);
|
|
value = tmp;
|
|
break;
|
|
case 4:
|
|
ret = bb_getUInt32(data, &value);
|
|
break;
|
|
default:
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if (ret != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
// RX / TX are delivered from the partner's point of view =>
|
|
// reversed to ours
|
|
switch (id)
|
|
{
|
|
case HDLC_INFO_MAX_INFO_TX:
|
|
if (value < settings->maxInfoRX)
|
|
{
|
|
settings->maxInfoRX = (uint16_t)value;
|
|
}
|
|
break;
|
|
case HDLC_INFO_MAX_INFO_RX:
|
|
if (value < settings->maxInfoTX)
|
|
{
|
|
settings->maxInfoTX = (uint16_t)value;
|
|
}
|
|
break;
|
|
case HDLC_INFO_WINDOW_SIZE_TX:
|
|
if (value < settings->windowSizeRX)
|
|
{
|
|
settings->windowSizeRX = (unsigned char)value;
|
|
}
|
|
break;
|
|
case HDLC_INFO_WINDOW_SIZE_RX:
|
|
if (value < settings->windowSizeTX)
|
|
{
|
|
settings->windowSizeTX = (unsigned char)value;
|
|
}
|
|
break;
|
|
default:
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dlms_appendHdlcParameter(gxByteBuffer* data, uint16_t value)
|
|
{
|
|
if (value < 0x100)
|
|
{
|
|
bb_setUInt8(data, 1);
|
|
bb_setUInt8(data, (unsigned char)value);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(data, 2);
|
|
bb_setUInt16(data, value);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dlms_isPduFull(dlmsSettings* settings, gxByteBuffer* data, uint16_t* size)
|
|
{
|
|
unsigned char ret;
|
|
if (bb_isAttached(data))
|
|
{
|
|
uint16_t len = 0;
|
|
if (size != NULL)
|
|
{
|
|
if (*size == 0)
|
|
{
|
|
*size = (uint16_t)data->size;
|
|
}
|
|
len = *size;
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->cipher.security != DLMS_SECURITY_NONE)
|
|
{
|
|
len += 20 + CIPHERING_HEADER_SIZE + (uint16_t)data->size;
|
|
}
|
|
else
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
{
|
|
len += 20 + (uint16_t)data->size;
|
|
}
|
|
ret = settings->maxPduSize < len;
|
|
}
|
|
else
|
|
{
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|