5109 lines
160 KiB
C
5109 lines
160 KiB
C
//
|
|
// --------------------------------------------------------------------------
|
|
// Gurux Ltd
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
// 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"
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(__linux__)
|
|
#include <assert.h>
|
|
#if _MSC_VER > 1400
|
|
#include <stdlib.h>
|
|
#include <crtdbg.h>
|
|
#endif
|
|
#endif
|
|
#include "gxmem.h"
|
|
#include "apdu.h"
|
|
#include "server.h"
|
|
#include "dlms.h"
|
|
#include "cosem.h"
|
|
#include "enums.h"
|
|
#include "gxset.h"
|
|
#include "gxget.h"
|
|
#include "gxinvoke.h"
|
|
#include "gxserializer.h"
|
|
|
|
typedef struct
|
|
{
|
|
/**
|
|
* Is attribute index or action index
|
|
*/
|
|
unsigned char action;
|
|
|
|
/**
|
|
* Attribute index.
|
|
*/
|
|
unsigned char index;
|
|
|
|
/**
|
|
* COSEM object.
|
|
*/
|
|
gxObject* item;
|
|
} gxSNInfo;
|
|
|
|
//Is interface type HDLC or HDLC with mode E.
|
|
#define IS_HDLC(interfaceType) (interfaceType == DLMS_INTERFACE_TYPE_HDLC || interfaceType == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E)
|
|
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
/**
|
|
* Handle read request.
|
|
*/
|
|
int svr_handleReadRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data);
|
|
|
|
/**
|
|
* Declare a prototype for a svr_handleWriteRequest.
|
|
*/
|
|
int svr_handleWriteRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data);
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
|
|
#if !defined(DLMS_IGNORE_MALLOC)
|
|
//Copy association view.
|
|
void svr_copyAssociationView(objectArray* target, objectArray* source)
|
|
{
|
|
uint16_t cnt = 0, pos;
|
|
oa_empty(target);
|
|
oa_capacity(target, source->size);
|
|
for (pos = 0; pos != source->size; ++pos)
|
|
{
|
|
if (source->data[pos]->objectType != DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME &&
|
|
source->data[pos]->objectType != DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME)
|
|
{
|
|
target->data[cnt] = source->data[pos];
|
|
++cnt;
|
|
}
|
|
}
|
|
target->size = cnt;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_MALLOC)
|
|
|
|
int sr_initialize(
|
|
gxServerReply* sr,
|
|
unsigned char* data,
|
|
uint16_t dataSize,
|
|
gxByteBuffer* reply)
|
|
{
|
|
/*Received data from the client.*/
|
|
sr->data = data;
|
|
/*Data size.*/
|
|
sr->dataSize = dataSize;
|
|
/*Server reply for the client.*/
|
|
sr->reply = reply;
|
|
/*Is GBT streaming in progress.*/
|
|
sr->moreData = DLMS_DATA_REQUEST_TYPES_NONE;
|
|
/*GBT Message count to send.*/
|
|
sr->gbtCount = 0;
|
|
/*HDLC window count to send.*/
|
|
sr->hdlcWindowCount = 0;
|
|
/*Received command.*/
|
|
sr->command = DLMS_COMMAND_NONE;
|
|
#ifndef DLMS_IGNORE_IEC
|
|
/*Baudrate is changed when optical probe is used.*/
|
|
sr->newBaudRate = 0;
|
|
#endif //DLMS_IGNORE_IEC
|
|
return 0;
|
|
}
|
|
|
|
int svr_initialize(
|
|
dlmsServerSettings* settings)
|
|
{
|
|
uint16_t pos;
|
|
int ret;
|
|
gxObject* associationObject = NULL, * it;
|
|
settings->initialized = 1;
|
|
if (settings->base.maxPduSize < 64)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &it)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (memcmp(it->logicalName, EMPTY_LN, sizeof(EMPTY_LN)) == 0)
|
|
{
|
|
//Invalid Logical Name.
|
|
return DLMS_ERROR_CODE_INVALID_LOGICAL_NAME;
|
|
}
|
|
if (it->objectType == DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME
|
|
&& !settings->base.useLogicalNameReferencing)
|
|
{
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
objectArray* list = &((gxAssociationShortName*)it)->objectList;
|
|
if (list->size == 0)
|
|
{
|
|
svr_copyAssociationView(list, &settings->base.objects);
|
|
}
|
|
associationObject = it;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
}
|
|
else if (it->objectType == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME
|
|
&& settings->base.useLogicalNameReferencing)
|
|
{
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
gxAssociationLogicalName* ln = (gxAssociationLogicalName*)it;
|
|
objectArray* list = &ln->objectList;
|
|
if (list->size == 0)
|
|
{
|
|
svr_copyAssociationView(list, &settings->base.objects);
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
associationObject = it;
|
|
}
|
|
}
|
|
if (associationObject == NULL)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
objectArray* list;
|
|
if (settings->base.useLogicalNameReferencing)
|
|
{
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
gxAssociationLogicalName* ln;
|
|
if ((ret = cosem_createObject(DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, (gxObject**)&ln)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
list = &((gxAssociationLogicalName*)ln)->objectList;
|
|
oa_push(&settings->base.objects, (gxObject*)ln);
|
|
//Add object to released objects list.
|
|
ret = oa_push(&settings->base.releasedObjects, (gxObject*)ln);
|
|
oa_copy(list, &settings->base.objects);
|
|
#endif //DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
}
|
|
else
|
|
{
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
gxAssociationShortName* it2;
|
|
if ((ret = cosem_createObject(DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME, (gxObject**)&it2)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
list = &((gxAssociationShortName*)it2)->objectList;
|
|
oa_push(&settings->base.objects, (gxObject*)it2);
|
|
oa_copy(list, &settings->base.objects);
|
|
//Add object to released objects list.
|
|
ret = oa_push(&settings->base.releasedObjects, (gxObject*)it2);
|
|
#endif // DLMS_IGNORE_ASSOCIATION_SHORT_NAME
|
|
}
|
|
#else
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
// Arrange items by Short Name.
|
|
if (!settings->base.useLogicalNameReferencing)
|
|
{
|
|
return svr_updateShortNames(settings, 0);
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
int svr_updateShortNames(
|
|
dlmsServerSettings* settings,
|
|
unsigned char force)
|
|
{
|
|
gxObject* it;
|
|
uint16_t sn = 0xA0;
|
|
uint16_t pos;
|
|
int ret;
|
|
unsigned char offset, count;
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, (gxObject**)&it)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (it->objectType == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME ||
|
|
it->objectType == DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME)
|
|
{
|
|
continue;
|
|
}
|
|
// Generate Short Name if not given.
|
|
if (force != 0 || it->shortName == 0)
|
|
{
|
|
it->shortName = sn;
|
|
// Add method index addresses.
|
|
if ((ret = dlms_getActionInfo((DLMS_OBJECT_TYPE)it->objectType, &offset, &count)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (count != 0)
|
|
{
|
|
sn += offset + (8 * count);
|
|
}
|
|
else
|
|
{
|
|
// If there are no methods.
|
|
// Add attribute index addresses.
|
|
sn += (uint16_t)(8 * obj_attributeCount(it));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sn = (uint16_t)(it->shortName + (8 * obj_attributeCount(it)));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
|
|
void svr_setInitialize(dlmsServerSettings* settings)
|
|
{
|
|
settings->base.protocolVersion = 0;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (settings->base.cipher.dedicatedKey != NULL)
|
|
{
|
|
bb_clear(settings->base.cipher.dedicatedKey);
|
|
gxfree(settings->base.cipher.dedicatedKey);
|
|
settings->base.cipher.dedicatedKey = NULL;
|
|
}
|
|
#else
|
|
memset(settings->base.cipher.dedicatedKey, 0, 16);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
memset(settings->base.sourceSystemTitle, 0, sizeof(settings->base.sourceSystemTitle));
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
trans_clear(&settings->transaction);
|
|
settings->base.blockIndex = 1;
|
|
settings->base.connected = DLMS_CONNECTION_STATE_NONE;
|
|
settings->base.authentication = DLMS_AUTHENTICATION_NONE;
|
|
settings->base.isAuthenticationRequired = 0;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
settings->base.cipher.security = DLMS_SECURITY_NONE;
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
bb_clear(&settings->base.ctoSChallenge);
|
|
bb_clear(&settings->base.stoCChallenge);
|
|
}
|
|
|
|
void svr_reset(
|
|
dlmsServerSettings* settings)
|
|
{
|
|
svr_setInitialize(settings);
|
|
resetFrameSequence(&settings->base);
|
|
reply_clear2(&settings->info, 1);
|
|
settings->base.serverAddress = 0;
|
|
settings->base.clientAddress = 0;
|
|
settings->dataReceived = 0;
|
|
settings->frameReceived = 0;
|
|
}
|
|
|
|
int svr_generateExceptionResponse(
|
|
dlmsSettings* settings,
|
|
DLMS_EXCEPTION_STATE_ERROR state,
|
|
int error,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
if ((ret = bb_setUInt8(data, DLMS_COMMAND_EXCEPTION_RESPONSE)) == 0 &&
|
|
(ret = bb_setUInt8(data, state)) == 0)
|
|
{
|
|
if (error == DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL)
|
|
{
|
|
if ((ret = bb_setUInt8(data, DLMS_EXCEPTION_SERVICE_ERROR_INVOCATION_COUNTER_ERROR)) == 0)
|
|
{
|
|
#ifdef DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
ret = bb_setUInt64(data, *settings->expectedInvocationCounter);
|
|
#else
|
|
ret = bb_setUInt32(data, (uint32_t)*settings->expectedInvocationCounter);
|
|
#endif //DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
}
|
|
}
|
|
else if (error == DLMS_ERROR_CODE_INVALID_COMMAND)
|
|
{
|
|
ret = bb_setUInt8(data, DLMS_EXCEPTION_SERVICE_ERROR_SERVICE_NOT_SUPPORTED);
|
|
}
|
|
else
|
|
{
|
|
ret = bb_setUInt8(data, DLMS_EXCEPTION_SERVICE_ERROR_DECIPHERING_ERROR);
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->cipher.security != DLMS_SECURITY_NONE)
|
|
{
|
|
unsigned char cmd = DLMS_COMMAND_GLO_CONFIRMED_SERVICE_ERROR;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
gxByteBuffer* key;
|
|
#else
|
|
unsigned char* key;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (dlms_useDedicatedKey(settings) && (settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
key = settings->cipher.dedicatedKey;
|
|
#else
|
|
key = settings->cipher.dedicatedKey;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
cmd = DLMS_COMMAND_DED_CONFIRMED_SERVICE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
key = &settings->cipher.blockCipherKey;
|
|
#else
|
|
key = settings->cipher.blockCipherKey;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
ret = cip_encrypt(
|
|
&settings->cipher,
|
|
settings->cipher.security,
|
|
DLMS_COUNT_TYPE_PACKET,
|
|
settings->cipher.invocationCounter,
|
|
cmd,
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
settings->cipher.systemTitle.data,
|
|
#else
|
|
settings->cipher.systemTitle,
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
key,
|
|
data);
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (ret == 0 && IS_HDLC(settings->interfaceType))
|
|
{
|
|
ret = dlms_addLLCBytes(settings, data);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Parse AARQ request that client send and returns AARE request.
|
|
*
|
|
* @return Reply to the client.
|
|
*/
|
|
int svr_HandleAarqRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
unsigned char command = 0;
|
|
int ret;
|
|
unsigned char ERROR_BUFF[4];
|
|
gxByteBuffer error;
|
|
BB_ATTACH(error, ERROR_BUFF, 0);
|
|
DLMS_ASSOCIATION_RESULT result;
|
|
unsigned char diagnostic;
|
|
// Reset settings for wrapper and PDU.
|
|
if (settings->base.interfaceType == DLMS_INTERFACE_TYPE_WRAPPER ||
|
|
settings->base.interfaceType == DLMS_INTERFACE_TYPE_PDU)
|
|
{
|
|
svr_setInitialize(settings);
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
else
|
|
{
|
|
settings->base.cipher.security = DLMS_SECURITY_NONE;
|
|
}
|
|
#endif
|
|
//If client is not called SNRM.
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (IS_HDLC(settings->base.interfaceType) && (settings->base.connected & DLMS_CONNECTION_STATE_HDLC) == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_REJECTED;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
ret = apdu_parsePDU(&settings->base, data, &result, &diagnostic, &command);
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("parsePDU", ret);
|
|
#endif //DLMS_DEBUG
|
|
bb_clear(data);
|
|
if (ret == DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL ||
|
|
ret == DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR ||
|
|
ret == DLMS_ERROR_CODE_INVALID_SECURITY_SUITE)
|
|
{
|
|
return svr_generateExceptionResponse(
|
|
&settings->base,
|
|
DLMS_EXCEPTION_STATE_ERROR_SERVICE_UNKNOWN,
|
|
ret,
|
|
data);
|
|
}
|
|
else if (ret == 0 && result == DLMS_ASSOCIATION_RESULT_ACCEPTED)
|
|
{
|
|
if (settings->base.dlmsVersionNumber < 6)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Invalid DLMS version number.", DLMS_INITIATE_DLMS_VERSION_TOO_LOW);
|
|
#endif //DLMS_DEBUG
|
|
result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
diagnostic = DLMS_SOURCE_DIAGNOSTIC_NO_REASON_GIVEN;
|
|
bb_setUInt8(&error, 0xE);
|
|
bb_setUInt8(&error, DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR);
|
|
bb_setUInt8(&error, DLMS_SERVICE_ERROR_INITIATE);
|
|
bb_setUInt8(&error, DLMS_INITIATE_DLMS_VERSION_TOO_LOW);
|
|
settings->base.dlmsVersionNumber = 6;
|
|
}
|
|
else if (settings->base.maxPduSize < 64)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Max PDU size is too short.", DLMS_INITIATE_PDU_SIZE_TOOSHORT);
|
|
#endif //DLMS_DEBUG
|
|
result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
diagnostic = DLMS_SOURCE_DIAGNOSTIC_NO_REASON_GIVEN;
|
|
bb_setUInt8(&error, 0xE);
|
|
bb_setUInt8(&error, DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR);
|
|
bb_setUInt8(&error, DLMS_SERVICE_ERROR_INITIATE);
|
|
bb_setUInt8(&error, DLMS_INITIATE_PDU_SIZE_TOOSHORT);
|
|
settings->base.maxPduSize = 64;
|
|
}
|
|
else if (settings->base.negotiatedConformance == 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Invalid negotiated conformance.", DLMS_INITIATE_INCOMPATIBLE_CONFORMANCE);
|
|
#endif //DLMS_DEBUG
|
|
result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
diagnostic = DLMS_SOURCE_DIAGNOSTIC_NO_REASON_GIVEN;
|
|
bb_setUInt8(&error, 0xE);
|
|
bb_setUInt8(&error, DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR);
|
|
bb_setUInt8(&error, DLMS_SERVICE_ERROR_INITIATE);
|
|
bb_setUInt8(&error, DLMS_INITIATE_INCOMPATIBLE_CONFORMANCE);
|
|
}
|
|
else if (diagnostic != DLMS_SOURCE_DIAGNOSTIC_NONE)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Connection rejected.", -1);
|
|
#endif //DLMS_DEBUG
|
|
result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
diagnostic = DLMS_SOURCE_DIAGNOSTIC_APPLICATION_CONTEXT_NAME_NOT_SUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("svr_validateAuthentication.", 0);
|
|
#endif //DLMS_DEBUG
|
|
diagnostic = svr_validateAuthentication(
|
|
settings,
|
|
settings->base.authentication,
|
|
&settings->base.password);
|
|
if (diagnostic != DLMS_SOURCE_DIAGNOSTIC_NONE)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Connection rejected.", -1);
|
|
#endif //DLMS_DEBUG
|
|
svr_invalidConnection(settings);
|
|
result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
}
|
|
else if (settings->base.authentication > DLMS_AUTHENTICATION_LOW)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("High authentication is used.", 0);
|
|
#endif //DLMS_DEBUG
|
|
// If High authentication is used.
|
|
result = DLMS_ASSOCIATION_RESULT_ACCEPTED;
|
|
diagnostic = DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_REQUIRED;
|
|
}
|
|
}
|
|
// Generate AARE packet.
|
|
if (settings->base.authentication > DLMS_AUTHENTICATION_LOW && settings->base.customChallenges == 0)
|
|
{
|
|
// If High authentication is used.
|
|
if ((ret = dlms_generateChallenge(&settings->base.stoCChallenge)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("generateChallenge ", ret);
|
|
#endif //DLMS_DEBUG
|
|
bb_clear(&error);
|
|
return ret;
|
|
}
|
|
if (settings->base.useLogicalNameReferencing)
|
|
{
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
gxAssociationLogicalName* it;
|
|
unsigned char ln[] = { 0, 0, 40, 0, 0, 255 };
|
|
if ((ret = oa_findByLN(&settings->base.objects, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln, (gxObject**)&it)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("oa_findByLN ", ret);
|
|
#endif //DLMS_DEBUG
|
|
return ret;
|
|
}
|
|
if (it == NULL)
|
|
{
|
|
gxValueEventArg e;
|
|
ve_init(&e);
|
|
svr_findObject(&settings->base, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, 0, ln, &e);
|
|
it = (gxAssociationLogicalName*)e.target;
|
|
}
|
|
if (it != NULL)
|
|
{
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->base.cipher.security == DLMS_SECURITY_NONE)
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
{
|
|
it->applicationContextName.contextId = DLMS_APPLICATION_CONTEXT_NAME_LOGICAL_NAME;
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
else
|
|
{
|
|
it->applicationContextName.contextId = DLMS_APPLICATION_CONTEXT_NAME_LOGICAL_NAME_WITH_CIPHERING;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
it->authenticationMechanismName.mechanismId = settings->base.authentication;
|
|
it->associationStatus = DLMS_ASSOCIATION_STATUS_ASSOCIATION_PENDING;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Association logical name not found. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
#endif //DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
}
|
|
}
|
|
else if (result == DLMS_ASSOCIATION_RESULT_ACCEPTED)
|
|
{
|
|
if (settings->base.useLogicalNameReferencing)
|
|
{
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
gxAssociationLogicalName* it;
|
|
unsigned char ln[] = { 0, 0, 40, 0, 0, 255 };
|
|
if ((ret = oa_findByLN(&settings->base.objects, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln, (gxObject**)&it)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (it == NULL)
|
|
{
|
|
gxValueEventArg e;
|
|
ve_init(&e);
|
|
svr_findObject(&settings->base, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, 0, ln, &e);
|
|
it = (gxAssociationLogicalName*)e.target;
|
|
}
|
|
if (it != NULL)
|
|
{
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->base.cipher.security == DLMS_SECURITY_NONE)
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
{
|
|
it->applicationContextName.contextId = DLMS_APPLICATION_CONTEXT_NAME_LOGICAL_NAME;
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
else
|
|
{
|
|
it->applicationContextName.contextId = DLMS_APPLICATION_CONTEXT_NAME_LOGICAL_NAME_WITH_CIPHERING;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
it->authenticationMechanismName.mechanismId = settings->base.authentication;
|
|
it->associationStatus = DLMS_ASSOCIATION_STATUS_ASSOCIATED;
|
|
}
|
|
#endif //DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
}
|
|
}
|
|
}
|
|
else if (diagnostic == DLMS_SOURCE_DIAGNOSTIC_NONE)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Permanent rejected.", -1);
|
|
#endif //DLMS_DEBUG
|
|
result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
diagnostic = DLMS_SOURCE_DIAGNOSTIC_NO_REASON_GIVEN;
|
|
bb_setUInt8(&error, 0xE);
|
|
bb_setUInt8(&error, DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR);
|
|
bb_setUInt8(&error, DLMS_SERVICE_ERROR_INITIATE);
|
|
bb_setUInt8(&error, DLMS_INITIATE_OTHER);
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (IS_HDLC(settings->base.interfaceType))
|
|
{
|
|
dlms_addLLCBytes(&settings->base, data);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
ret = apdu_generateAARE(&settings->base, data, result, diagnostic, &error, NULL, command);
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("apdu_generateAARE.", ret);
|
|
#endif //DLMS_DEBUG
|
|
bb_clear(&error);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Parse SNRM Request. If server do not accept client empty byte array is
|
|
* returned.
|
|
*
|
|
* @return Returns returned UA packet.
|
|
*/
|
|
int svr_handleSnrmRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
unsigned char len;
|
|
uint16_t clientAddress;
|
|
uint32_t serverAddress;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
DLMS_SECURITY security;
|
|
#endif
|
|
//Initialize default settings.
|
|
if (settings->hdlc != NULL)
|
|
{
|
|
settings->base.maxInfoRX = settings->hdlc->maximumInfoLengthReceive;
|
|
settings->base.maxInfoTX = settings->hdlc->maximumInfoLengthTransmit;
|
|
settings->base.windowSizeRX = settings->hdlc->windowSizeReceive;
|
|
settings->base.windowSizeTX = settings->hdlc->windowSizeTransmit;
|
|
}
|
|
else
|
|
{
|
|
settings->base.maxInfoRX = DEFAULT_MAX_INFO_RX;
|
|
settings->base.maxInfoTX = DEFAULT_MAX_INFO_TX;
|
|
settings->base.windowSizeRX = DEFAULT_MAX_WINDOW_SIZE_RX;
|
|
settings->base.windowSizeTX = DEFAULT_MAX_WINDOW_SIZE_TX;
|
|
|
|
}
|
|
if ((ret = dlms_parseSnrmUaResponse(&settings->base, data)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
bb_clear(data);
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
security = settings->base.cipher.security;
|
|
#endif
|
|
serverAddress = settings->base.serverAddress;
|
|
clientAddress = settings->base.clientAddress;
|
|
svr_reset(settings);
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
settings->base.cipher.security = security;
|
|
#endif
|
|
settings->base.serverAddress = serverAddress;
|
|
settings->base.clientAddress = clientAddress;
|
|
bb_setUInt8(data, 0x81); // FromatID
|
|
bb_setUInt8(data, 0x80); // GroupID
|
|
bb_setUInt8(data, 0); // Length
|
|
bb_setUInt8(data, HDLC_INFO_MAX_INFO_TX);
|
|
dlms_appendHdlcParameter(data, settings->base.maxInfoTX);
|
|
|
|
bb_setUInt8(data, HDLC_INFO_MAX_INFO_RX);
|
|
dlms_appendHdlcParameter(data, settings->base.maxInfoRX);
|
|
|
|
bb_setUInt8(data, HDLC_INFO_WINDOW_SIZE_TX);
|
|
bb_setUInt8(data, 4);
|
|
bb_setUInt32(data, settings->base.windowSizeTX);
|
|
|
|
bb_setUInt8(data, HDLC_INFO_WINDOW_SIZE_RX);
|
|
bb_setUInt8(data, 4);
|
|
bb_setUInt32(data, settings->base.windowSizeRX);
|
|
|
|
len = (unsigned char)(data->size - 3);
|
|
// Update length.
|
|
bb_setUInt8ByIndex(data, 2, len);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Generates disconnect request.
|
|
*
|
|
* @return Disconnect request.
|
|
*/
|
|
int svr_generateDisconnectRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret, len;
|
|
if (settings->base.interfaceType == DLMS_INTERFACE_TYPE_WRAPPER)
|
|
{
|
|
bb_setUInt8(data, 0x63);
|
|
ret = bb_setUInt8(data, 0x0);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(data, 0x81); // FromatID
|
|
bb_setUInt8(data, 0x80); // GroupID
|
|
bb_setUInt8(data, 0); // Length
|
|
|
|
bb_setUInt8(data, HDLC_INFO_MAX_INFO_TX);
|
|
dlms_appendHdlcParameter(data, settings->base.maxInfoTX);
|
|
|
|
bb_setUInt8(data, HDLC_INFO_MAX_INFO_RX);
|
|
dlms_appendHdlcParameter(data, settings->base.maxInfoRX);
|
|
|
|
bb_setUInt8(data, HDLC_INFO_WINDOW_SIZE_TX);
|
|
bb_setUInt8(data, 4);
|
|
bb_setUInt32(data, settings->base.windowSizeTX);
|
|
|
|
bb_setUInt8(data, HDLC_INFO_WINDOW_SIZE_RX);
|
|
bb_setUInt8(data, 4);
|
|
bb_setUInt32(data, settings->base.windowSizeRX);
|
|
|
|
len = data->size - 3;
|
|
// Update length.
|
|
ret = bb_setUInt8ByIndex(data, 2, (unsigned char)len);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//Add PDU to frame.
|
|
int dlms_addFrame(
|
|
dlmsSettings* settings,
|
|
DLMS_COMMAND command,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret;
|
|
switch (settings->interfaceType)
|
|
{
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
case DLMS_INTERFACE_TYPE_HDLC:
|
|
case DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E:
|
|
{
|
|
unsigned char frame = 0;
|
|
if (command == DLMS_COMMAND_UA)
|
|
{
|
|
frame = (unsigned char)DLMS_COMMAND_UA;
|
|
}
|
|
if ((ret = dlms_getHdlcFrame(settings, frame, data, reply)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("getHdlcFrame failed. ", ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
}
|
|
break;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
case DLMS_INTERFACE_TYPE_WRAPPER:
|
|
if ((ret = dlms_getWrapperFrame(settings, command, data, reply)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("dlms_getWrapperFrame failed. ", ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
break;
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
#ifndef DLMS_IGNORE_PLC
|
|
case DLMS_INTERFACE_TYPE_PLC:
|
|
case DLMS_INTERFACE_TYPE_PLC_HDLC:
|
|
ret = dlms_getMacFrame(settings, 0, 0, data, reply);
|
|
break;
|
|
#endif //DLMS_IGNORE_PLC
|
|
case DLMS_INTERFACE_TYPE_PDU:
|
|
ret = bb_set2(reply, data, 0, bb_size(data));
|
|
break;
|
|
default:
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Unknown frame type. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int svr_reportError(
|
|
dlmsServerSettings* settings,
|
|
DLMS_COMMAND command,
|
|
DLMS_ERROR_CODE error,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret;
|
|
DLMS_COMMAND cmd;
|
|
gxByteBuffer data;
|
|
switch (command)
|
|
{
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_READ_REQUEST:
|
|
cmd = DLMS_COMMAND_READ_RESPONSE;
|
|
break;
|
|
case DLMS_COMMAND_WRITE_REQUEST:
|
|
cmd = DLMS_COMMAND_WRITE_RESPONSE;
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_GET_REQUEST:
|
|
cmd = DLMS_COMMAND_GET_RESPONSE;
|
|
break;
|
|
case DLMS_COMMAND_SET_REQUEST:
|
|
cmd = DLMS_COMMAND_SET_RESPONSE;
|
|
break;
|
|
case DLMS_COMMAND_METHOD_REQUEST:
|
|
cmd = DLMS_COMMAND_METHOD_RESPONSE;
|
|
break;
|
|
default:
|
|
if (settings->info.encryptedCommand != 0)
|
|
{
|
|
//Do nothing if ciphered content is invalid.
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// Return HW error and close connection.
|
|
cmd = DLMS_COMMAND_NONE;
|
|
}
|
|
break;
|
|
}
|
|
BYTE_BUFFER_INIT(&data);
|
|
if (settings->base.useLogicalNameReferencing)
|
|
{
|
|
gxLNParameters p;
|
|
params_initLN(&p, &settings->base, 0, cmd, 1,
|
|
NULL, NULL, error, DLMS_COMMAND_NONE, 0, 0);
|
|
ret = dlms_getLNPdu(&p, &data);
|
|
if (ret != 0)
|
|
{
|
|
bb_clear(&data);
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
gxSNParameters p;
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_setUInt8(&bb, error);
|
|
params_initSN(&p, &settings->base, cmd, 1, 1, NULL, &bb, settings->info.encryptedCommand);
|
|
p.lastBlock = 1;
|
|
ret = dlms_getSNPdu(&p, &data);
|
|
bb_clear(&bb);
|
|
if (ret != 0)
|
|
{
|
|
bb_clear(&data);
|
|
return ret;
|
|
}
|
|
#else
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
}
|
|
ret = dlms_addFrame(&settings->base, command, &data, reply);
|
|
bb_clear(&data);
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_SET
|
|
int svr_handleSetRequest2(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
unsigned char type,
|
|
gxLNParameters* p)
|
|
{
|
|
gxValueEventArg* e;
|
|
gxValueEventCollection list;
|
|
int ret;
|
|
DLMS_OBJECT_TYPE ci;
|
|
unsigned char ch;
|
|
uint16_t tmp;
|
|
unsigned char* ln = NULL;
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
list = settings->transaction.targets;
|
|
e = &list.data[0];
|
|
ve_clear(e);
|
|
#else
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_init(&list);
|
|
vec_push(&list, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
if ((ret = bb_getUInt16(data, &tmp)) != 0)
|
|
{
|
|
vec_clear(&list);
|
|
return ret;
|
|
}
|
|
ci = (DLMS_OBJECT_TYPE)tmp;
|
|
ln = data->data + data->position;
|
|
data->position += 6;
|
|
// Attribute index.
|
|
if ((ret = bb_getUInt8(data, &e->index)) != 0)
|
|
{
|
|
vec_clear(&list);
|
|
return ret;
|
|
}
|
|
// Get Access Selection.
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
vec_clear(&list);
|
|
return ret;
|
|
}
|
|
if (type == DLMS_SET_COMMAND_TYPE_FIRST_DATABLOCK)
|
|
{
|
|
uint32_t blockNumber;
|
|
uint16_t size;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
vec_clear(&list);
|
|
return ret;
|
|
}
|
|
p->multipleBlocks = ch == 0;
|
|
ret = bb_getUInt32(data, &blockNumber);
|
|
if (ret != 0)
|
|
{
|
|
vec_clear(&list);
|
|
return ret;
|
|
}
|
|
if (blockNumber != settings->base.blockIndex)
|
|
{
|
|
vec_clear(&list);
|
|
return DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID;
|
|
}
|
|
++settings->base.blockIndex;
|
|
if (hlp_getObjectCount2(data, &size) != 0)
|
|
{
|
|
vec_clear(&list);
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
//If received date is not what expected.
|
|
if (size != data->size - data->position)
|
|
{
|
|
vec_clear(&list);
|
|
return DLMS_ERROR_CODE_DATA_BLOCK_UNAVAILABLE;
|
|
}
|
|
}
|
|
#if defined(DLMS_IGNORE_MALLOC) || defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
e->value.byteArr = data;
|
|
e->value.vt = DLMS_DATA_TYPE_OCTET_STRING | DLMS_DATA_TYPE_BYREF;
|
|
#else
|
|
if (!p->multipleBlocks)
|
|
{
|
|
gxDataInfo di;
|
|
di_init(&di);
|
|
resetBlockIndex(&settings->base);
|
|
ret = dlms_getData(data, &di, &e->value);
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES)
|
|
if (ret == 0)
|
|
{
|
|
if ((ret = oa_findByLN(&settings->base.objects, ci, ln, &e->target)) == 0)
|
|
{
|
|
if (e->target == NULL)
|
|
{
|
|
ret = svr_findObject(&settings->base, ci, 0, ln, e);
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_clear(data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (ret == 0)
|
|
{
|
|
// If target is unknown.
|
|
if (e->target == NULL)
|
|
{
|
|
// Device reports a undefined object.
|
|
ret = DLMS_ERROR_CODE_UNAVAILABLE_OBJECT;
|
|
}
|
|
else
|
|
{
|
|
DLMS_ACCESS_MODE am = svr_getAttributeAccess(&settings->base, e->target, e->index);
|
|
// If write is denied.
|
|
if (am != DLMS_ACCESS_MODE_WRITE && am != DLMS_ACCESS_MODE_READ_WRITE)
|
|
{
|
|
//Read Write denied.
|
|
ret = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
}
|
|
else
|
|
{
|
|
svr_preWrite(&settings->base, &list);
|
|
if (p->multipleBlocks)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
settings->transaction.targets.size = 1;
|
|
#else
|
|
bb_clear(&settings->transaction.data);
|
|
vec_clear(&settings->transaction.targets);
|
|
settings->transaction.targets = list;
|
|
var_clear(&e->value);
|
|
vec_init(&list);
|
|
if (!bb_isAttached(data))
|
|
{
|
|
ret = bb_set2(&settings->transaction.data, data, data->position, data->size - data->position);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
settings->transaction.command = DLMS_COMMAND_SET_REQUEST;
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
if (e->error != 0)
|
|
{
|
|
ret = e->error;
|
|
}
|
|
else if (!e->handled && !p->multipleBlocks)
|
|
{
|
|
ret = cosem_setValue(&settings->base, e);
|
|
if (ret != 0 && e->error == 0)
|
|
{
|
|
e->error = ret;
|
|
}
|
|
svr_postWrite(&settings->base, &list);
|
|
if (e->error != 0)
|
|
{
|
|
ret = e->error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (type == DLMS_SET_COMMAND_TYPE_NORMAL)
|
|
{
|
|
bb_clear(data);
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&list);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
int svr_getTarget(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* status,
|
|
gxValueEventArg* e,
|
|
unsigned char seek)
|
|
{
|
|
int ret;
|
|
DLMS_OBJECT_TYPE ci;
|
|
unsigned char ch;
|
|
uint16_t tmp;
|
|
unsigned char* ln = NULL;
|
|
if ((ret = bb_getUInt16(data, &tmp)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ci = (DLMS_OBJECT_TYPE)tmp;
|
|
ln = data->data + data->position;
|
|
data->position += 6;
|
|
// Attribute index.
|
|
if ((ret = bb_getUInt8(data, &e->index)) == 0 &&
|
|
// Get Access Selection.
|
|
(ret = bb_getUInt8(data, &ch)) == 0)
|
|
{
|
|
if ((ret = oa_findByLN(&settings->base.objects, ci, ln, &e->target)) == 0)
|
|
{
|
|
if (e->target == NULL)
|
|
{
|
|
ret = svr_findObject(&settings->base, ci, 0, ln, e);
|
|
}
|
|
if (!seek)
|
|
{
|
|
if (ret == 0)
|
|
{
|
|
// If target is unknown.
|
|
if (e->target == NULL)
|
|
{
|
|
// Device reports a undefined object.
|
|
bb_setUInt8(status, DLMS_ERROR_CODE_UNAVAILABLE_OBJECT);
|
|
}
|
|
else
|
|
{
|
|
DLMS_ACCESS_MODE am = svr_getAttributeAccess(&settings->base, e->target, e->index);
|
|
// If write is denied.
|
|
if (am != DLMS_ACCESS_MODE_WRITE && am != DLMS_ACCESS_MODE_READ_WRITE)
|
|
{
|
|
//Read Write denied.
|
|
bb_setUInt8(status, DLMS_ERROR_CODE_READ_WRITE_DENIED);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(status, DLMS_ERROR_CODE_OK);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(status, DLMS_ERROR_CODE_UNAVAILABLE_OBJECT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleSetRequestWithList(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
gxLNParameters* p)
|
|
{
|
|
gxValueEventArg* e;
|
|
gxValueEventCollection list;
|
|
int ret;
|
|
unsigned char ch;
|
|
uint16_t pos, count, targetPos, tmp;
|
|
gxByteBuffer status;
|
|
bb_init(&status);
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
unsigned char STATUS_BUFF[20];
|
|
bb_attach(&status, STATUS_BUFF, 0, sizeof(STATUS_BUFF));
|
|
list = settings->transaction.targets;
|
|
list.size = 1;
|
|
e = &list.data[0];
|
|
ve_clear(e);
|
|
#else
|
|
bb_capacity(&status, 10);
|
|
vec_init(&list);
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_push(&list, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if ((ret = hlp_getObjectCount2(data, &count)) == 0)
|
|
{
|
|
targetPos = (uint16_t)data->position;
|
|
for (pos = 0; pos != count; ++pos)
|
|
{
|
|
if ((ret = svr_getTarget(settings, data, &status, e, 0)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (ret == 0 &&
|
|
(ret = hlp_getObjectCount2(data, &count)) == 0)
|
|
{
|
|
gxDataInfo di;
|
|
for (pos = 0; pos != count; ++pos)
|
|
{
|
|
di_init(&di);
|
|
resetBlockIndex(&settings->base);
|
|
tmp = (uint16_t)data->position;
|
|
//Target must seek from the byte buffer.
|
|
data->position = targetPos;
|
|
ret = svr_getTarget(settings, data, &status, e, 1);
|
|
targetPos = (uint16_t)data->position;
|
|
data->position = tmp;
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
if ((ret = dlms_getData(data, &di, &e->value)) != 0)
|
|
{
|
|
bb_setUInt8ByIndex(&status, pos, ret);
|
|
}
|
|
if (bb_getUInt8(&status, &ch) != 0 ||
|
|
ch != 0)
|
|
{
|
|
continue;
|
|
}
|
|
//Objects are invoked one at the time when malloc is not used.
|
|
//This must be done because octet-strings cause problems.
|
|
svr_preWrite(&settings->base, &list);
|
|
if (p->multipleBlocks)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
settings->transaction.targets.size = 1;
|
|
#else
|
|
bb_clear(&settings->transaction.data);
|
|
vec_clear(&settings->transaction.targets);
|
|
settings->transaction.targets = list;
|
|
var_clear(&e->value);
|
|
vec_init(&list);
|
|
if (!bb_isAttached(data))
|
|
{
|
|
ret = bb_set2(&settings->transaction.data, data, data->position, data->size - data->position);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
settings->transaction.command = DLMS_COMMAND_SET_REQUEST;
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
if (e->error != 0)
|
|
{
|
|
bb_setUInt8ByIndex(&status, pos, e->error);
|
|
}
|
|
else if (!e->handled && !p->multipleBlocks)
|
|
{
|
|
ret = cosem_setValue(&settings->base, e);
|
|
if (ret != 0 && e->error == 0)
|
|
{
|
|
bb_setUInt8ByIndex(&status, pos, ret);
|
|
}
|
|
svr_postWrite(&settings->base, &list);
|
|
if (e->error != 0)
|
|
{
|
|
bb_setUInt8ByIndex(&status, pos, e->error);
|
|
}
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
var_clear(&e->value);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&list);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
bb_clear(data);
|
|
p->status = 0xFF;
|
|
hlp_setObjectCount(status.size, data);
|
|
for (pos = 0; pos != status.size; ++pos)
|
|
{
|
|
bb_setUInt8(data, status.data[pos]);
|
|
}
|
|
bb_clear(&status);
|
|
p->requestType = DLMS_SET_RESPONSE_TYPE_WITH_LIST;
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_SET
|
|
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
int svr_hanleSetRequestWithDataBlock(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
gxLNParameters* p)
|
|
{
|
|
int ret;
|
|
uint32_t blockNumber;
|
|
uint16_t size;
|
|
unsigned char ch;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
p->multipleBlocks = ch == 0;
|
|
if ((ret = bb_getUInt32(data, &blockNumber)) == 0)
|
|
{
|
|
if (blockNumber != settings->base.blockIndex)
|
|
{
|
|
ret = DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID;
|
|
}
|
|
else
|
|
{
|
|
++settings->base.blockIndex;
|
|
if (hlp_getObjectCount2(data, &size) != 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
else if (size != data->size - data->position)
|
|
{
|
|
ret = DLMS_ERROR_CODE_DATA_BLOCK_UNAVAILABLE;
|
|
}
|
|
if (ret == 0 && (ret = bb_set2(&settings->transaction.data, data, data->position, data->size - data->position)) == 0)
|
|
{
|
|
// If all data is received.
|
|
if (!p->multipleBlocks)
|
|
{
|
|
gxDataInfo di;
|
|
gxValueEventArg* e = settings->transaction.targets.data[0];
|
|
di_init(&di);
|
|
if ((ret != dlms_getData(&settings->transaction.data, &di, &e->value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
svr_preWrite(&settings->base, &settings->transaction.targets);
|
|
if (!e->handled && !p->multipleBlocks)
|
|
{
|
|
if ((ret = cosem_setValue(&settings->base, e)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
svr_postWrite(&settings->base, &settings->transaction.targets);
|
|
}
|
|
trans_clear(&settings->transaction);
|
|
resetBlockIndex(&settings->base);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p->multipleBlocks = 1;
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
/**
|
|
* Generate confirmed service error.
|
|
*
|
|
* @param service
|
|
* Confirmed service error.
|
|
* @param type
|
|
* Service error.
|
|
* @param code
|
|
* code
|
|
* @return
|
|
*/
|
|
int svr_generateConfirmedServiceError(
|
|
dlmsServerSettings* settings,
|
|
DLMS_CONFIRMED_SERVICE_ERROR service,
|
|
DLMS_SERVICE_ERROR type,
|
|
unsigned char code,
|
|
gxByteBuffer* data)
|
|
{
|
|
bb_clear(data);
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (IS_HDLC(settings->base.interfaceType))
|
|
{
|
|
dlms_addLLCBytes(&settings->base, data);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
bb_setUInt8(data, DLMS_COMMAND_CONFIRMED_SERVICE_ERROR);
|
|
bb_setUInt8(data, service);
|
|
bb_setUInt8(data, type);
|
|
bb_setUInt8(data, code);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_SET
|
|
int svr_handleSetRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
gxLNParameters p;
|
|
unsigned char invokeId, type;
|
|
int ret;
|
|
// Get type.
|
|
if ((ret = bb_getUInt8(data, &type)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get invoke ID and priority.
|
|
if ((ret = bb_getUInt8(data, &invokeId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
updateInvokeId(settings, invokeId);
|
|
params_initLN(&p, &settings->base, invokeId, DLMS_COMMAND_SET_RESPONSE, type, NULL, data, 0, settings->info.encryptedCommand, 0, 0);
|
|
switch (type)
|
|
{
|
|
case DLMS_SET_COMMAND_TYPE_NORMAL:
|
|
case DLMS_SET_COMMAND_TYPE_FIRST_DATABLOCK:
|
|
ret = svr_handleSetRequest2(settings, data, type, &p);
|
|
break;
|
|
case DLMS_SET_COMMAND_TYPE_WITH_DATABLOCK:
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
//All data must fit to one PDU at the moment if malloc is not used.
|
|
ret = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
#else
|
|
// Set Request With Data Block
|
|
ret = svr_hanleSetRequestWithDataBlock(settings, data, &p);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
break;
|
|
case DLMS_SET_COMMAND_TYPE_WITH_LIST:
|
|
ret = svr_handleSetRequestWithList(settings, data, &p);
|
|
break;
|
|
default:
|
|
p.requestType = 1;
|
|
ret = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
break;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
// Access Error : Device reports Read-Write denied.
|
|
bb_clear(data);
|
|
resetBlockIndex(&settings->base);
|
|
//If error is DLMS error code.
|
|
if (ret > 0 && ret <= DLMS_ERROR_CODE_OTHER_REASON)
|
|
{
|
|
p.status = ret;
|
|
}
|
|
else
|
|
{
|
|
p.status = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
}
|
|
p.multipleBlocks = 0;
|
|
p.requestType = 1;
|
|
}
|
|
return dlms_getLNPdu(&p, data);
|
|
}
|
|
|
|
#endif //DLMS_IGNORE_SET
|
|
|
|
int svr_getRequestNormal(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
unsigned char invokeId)
|
|
{
|
|
gxValueEventCollection* arr;
|
|
uint16_t ci;
|
|
gxValueEventArg* e;
|
|
unsigned char index;
|
|
int ret;
|
|
unsigned char* ln;
|
|
DLMS_ERROR_CODE status = DLMS_ERROR_CODE_OK;
|
|
resetBlockIndex(&settings->base);
|
|
// CI
|
|
if ((ret = bb_getUInt16(data, &ci)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ln = data->data + data->position;
|
|
data->position += 6;
|
|
// Attribute Id
|
|
if ((ret = bb_getUInt8(data, &index)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (index < 1)
|
|
{
|
|
//Attribute0 Supported With Get is not supported.
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
arr = &settings->transaction.targets;
|
|
if (arr->capacity == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
e = &arr->data[0];
|
|
ve_clear(e);
|
|
arr->size = 1;
|
|
e->value.byteArr = data;
|
|
e->value.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
#else
|
|
gxValueEventCollection list;
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
e->value.byteArr = data;
|
|
e->value.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
e->index = index;
|
|
vec_init(&list);
|
|
vec_push(&list, e);
|
|
arr = &list;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
e->index = index;
|
|
// Access selection
|
|
unsigned char selection;
|
|
if ((ret = bb_getUInt8(data, &selection)) != 0)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
vec_clear(arr);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
if (selection != 0)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if ((ret = bb_getUInt8(data, &e->selector)) != 0)
|
|
{
|
|
vec_clear(arr);
|
|
return ret;
|
|
}
|
|
e->parameters.byteArr = data;
|
|
e->parameters.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
#else
|
|
gxDataInfo di;
|
|
di_init(&di);
|
|
if ((ret = bb_getUInt8(data, &e->selector)) != 0 ||
|
|
(ret = dlms_getData(data, &di, &e->parameters)) != 0)
|
|
{
|
|
vec_clear(arr);
|
|
return ret;
|
|
}
|
|
bb_clear(data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
else
|
|
{
|
|
bb_clear(data);
|
|
}
|
|
ret = oa_findByLN(&settings->base.objects, (DLMS_OBJECT_TYPE)ci, ln, &e->target);
|
|
if (ret != 0)
|
|
{
|
|
bb_clear(data);
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
vec_clear(arr);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
if (e->target == NULL)
|
|
{
|
|
ret = svr_findObject(&settings->base, (DLMS_OBJECT_TYPE)ci, 0, ln, e);
|
|
}
|
|
if (e->target == NULL)
|
|
{
|
|
// Access Error : Device reports a undefined object.
|
|
status = DLMS_ERROR_CODE_UNDEFINED_OBJECT;
|
|
}
|
|
else
|
|
{
|
|
DLMS_ACCESS_MODE mode = svr_getAttributeAccess(&settings->base, e->target, index);
|
|
if ((mode & DLMS_ACCESS_MODE_READ) == 0)
|
|
{
|
|
status = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
}
|
|
else
|
|
{
|
|
//Handle default Association LN read as a special case.
|
|
if (ci == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME && index == 1 && memcmp(DEFAULT_ASSOCIATION, ln, 6) == 0)
|
|
{
|
|
e->byteArray = 1;
|
|
if ((ret = bb_setUInt8(data, DLMS_DATA_TYPE_OCTET_STRING)) != 0 ||
|
|
(ret = bb_setUInt8(data, 6)) != 0 ||
|
|
(ret = bb_set(data, DEFAULT_ASSOCIATION, 6)) != 0)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
vec_clear(arr);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
svr_preRead(&settings->base, arr);
|
|
if (!e->handled)
|
|
{
|
|
if ((ret = cosem_getValue(&settings->base, e)) != 0)
|
|
{
|
|
status = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
svr_postRead(&settings->base, arr);
|
|
}
|
|
}
|
|
if (status == 0)
|
|
{
|
|
status = e->error;
|
|
}
|
|
if (status == 0)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (e->value.vt != DLMS_DATA_TYPE_OCTET_STRING)
|
|
{
|
|
if ((ret = dlms_setData(data, e->value.vt, &e->value)) != 0)
|
|
{
|
|
status = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
}
|
|
#else
|
|
if (!e->byteArray)
|
|
{
|
|
if ((ret = dlms_setData(data, e->value.vt, &e->value)) != 0)
|
|
{
|
|
status = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
}
|
|
else if (!bb_isAttached(e->value.byteArr))
|
|
{
|
|
if ((ret = bb_set2(data, e->value.byteArr, 0, e->value.byteArr->size)) != 0)
|
|
{
|
|
status = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
var_clear(&e->value);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
else if (status != 0)
|
|
{
|
|
bb_clear(data);
|
|
}
|
|
}
|
|
}
|
|
//PDU is used for serialization. Set data type to none so size is not changed.
|
|
if (e->byteArray || (e->value.vt == DLMS_DATA_TYPE_OCTET_STRING && bb_isAttached(e->value.byteArr)))
|
|
{
|
|
e->value.vt = DLMS_DATA_TYPE_NONE;
|
|
}
|
|
unsigned char moreData = e->transactionStartIndex != e->transactionEndIndex;
|
|
gxLNParameters p;
|
|
params_initLN(&p, &settings->base, invokeId, DLMS_COMMAND_GET_RESPONSE, 1, NULL, data, status, settings->info.encryptedCommand, moreData, !moreData);
|
|
ret = dlms_getLNPdu(&p, data);
|
|
if (e->transactionStartIndex != e->transactionEndIndex || data->size > settings->base.maxPduSize
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
|| ((settings->base.interfaceType == DLMS_INTERFACE_TYPE_HDLC ||
|
|
settings->base.interfaceType == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E) && data->size > settings->base.maxInfoTX)
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
trans_clear(&settings->transaction);
|
|
settings->transaction.targets = list;
|
|
vec_init(&list);
|
|
if (!bb_isAttached(data))
|
|
{
|
|
ret = bb_set2(&settings->transaction.data, data, data->position, data->size - data->position);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
e->transaction = 1;
|
|
settings->transaction.command = DLMS_COMMAND_GET_REQUEST;
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(arr);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
int svr_getRequestNextDataBlock(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
unsigned char invokeId)
|
|
{
|
|
gxValueEventArg* e;
|
|
gxLNParameters p;
|
|
unsigned char moreData;
|
|
int ret;
|
|
uint32_t index, pos;
|
|
// Get block index.
|
|
if ((ret = bb_getUInt32(data, &index)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
bb_clear(data);
|
|
if (index != settings->base.blockIndex)
|
|
{
|
|
params_initLN(&p, &settings->base, invokeId, DLMS_COMMAND_GET_RESPONSE, 2,
|
|
NULL, NULL, DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID, settings->info.encryptedCommand, 0, 0);
|
|
return dlms_getLNPdu(&p, data);
|
|
}
|
|
else
|
|
{
|
|
++settings->base.blockIndex;
|
|
params_initLN(&p, &settings->base, invokeId, DLMS_COMMAND_GET_RESPONSE, 2, NULL, data, DLMS_ERROR_CODE_OK, settings->info.encryptedCommand, 1, 1);
|
|
// If transaction is not in progress.
|
|
if (settings->transaction.command == DLMS_COMMAND_NONE)
|
|
{
|
|
p.status = DLMS_ERROR_CODE_NO_LONG_GET_OR_READ_IN_PROGRESS;
|
|
}
|
|
else
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_set2(data, &settings->transaction.data, 0, settings->transaction.data.size);
|
|
bb_clear(&settings->transaction.data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
moreData = 1;
|
|
// If there is multiple blocks on the buffer.
|
|
// This might happen when Max PDU size is very small.
|
|
if (data->size < settings->base.maxPduSize)
|
|
{
|
|
for (pos = 0; pos != settings->transaction.targets.size; ++pos)
|
|
{
|
|
if ((ret = vec_getByIndex(&settings->transaction.targets, pos, &e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
e->value.byteArr = data;
|
|
e->value.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
svr_preRead(&settings->base, &settings->transaction.targets);
|
|
if (!e->handled)
|
|
{
|
|
if ((ret = cosem_getValue(&settings->base, e)) != 0)
|
|
{
|
|
p.status = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
svr_postRead(&settings->base, &settings->transaction.targets);
|
|
}
|
|
// Add data.
|
|
if (e->byteArray)
|
|
{
|
|
e->value.vt = DLMS_DATA_TYPE_NONE;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = dlms_setData(data, e->value.vt, &e->value)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
var_clear(&e->value);
|
|
}
|
|
}
|
|
p.lastBlock = !(e->transactionStartIndex < e->transactionEndIndex);
|
|
}
|
|
ret = dlms_getLNPdu(&p, data);
|
|
if (moreData || data->size - data->position != 0)
|
|
{
|
|
//Append data to transaction data if buffer is not static.
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (!bb_isAttached(data))
|
|
{
|
|
bb_set2(&settings->transaction.data, data, data->position, data->size - data->position);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
else
|
|
{
|
|
trans_clear(&settings->transaction);
|
|
resetBlockIndex(&settings->base);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int svr_getRequestWithList(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
unsigned char invokeId)
|
|
{
|
|
int ret = 0;
|
|
gxValueEventCollection* arr;
|
|
gxValueEventArg* e;
|
|
DLMS_OBJECT_TYPE ci;
|
|
gxLNParameters p;
|
|
unsigned char attributeIndex;
|
|
uint16_t id;
|
|
uint16_t pos, cnt;
|
|
unsigned char* ln;
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (hlp_getObjectCount2(data, &cnt) != 0 || cnt > settings->transaction.targets.capacity)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
arr = &settings->transaction.targets;
|
|
arr->size = (unsigned char)cnt;
|
|
#else
|
|
gxValueEventCollection list;
|
|
if (hlp_getObjectCount2(data, &cnt) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
vec_init(&list);
|
|
arr = &list;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt16(data, &id)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
ci = (DLMS_OBJECT_TYPE)id;
|
|
ln = data->data + data->position;
|
|
data->position += 6;
|
|
if ((ret = bb_getUInt8(data, &attributeIndex)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if ((ret = vec_getByIndex(arr, pos, &e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
ve_clear(e);
|
|
#else
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_push(&list, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
e->index = attributeIndex;
|
|
if ((ret = oa_findByLN(&settings->base.objects, ci, ln, &e->target)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (e->target == NULL)
|
|
{
|
|
ret = svr_findObject(&settings->base, ci, 0, ln, e);
|
|
}
|
|
if (e->target == NULL)
|
|
{
|
|
// Access Error : Device reports a undefined object.
|
|
e->error = DLMS_ERROR_CODE_UNDEFINED_OBJECT;
|
|
}
|
|
else
|
|
{
|
|
if (svr_getAttributeAccess(&settings->base, e->target, attributeIndex) == DLMS_ACCESS_MODE_NONE)
|
|
{
|
|
// Read Write denied.
|
|
e->error = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
}
|
|
else
|
|
{
|
|
// AccessSelection
|
|
unsigned char selection;
|
|
if ((ret = bb_getUInt8(data, &selection)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (selection != 0)
|
|
{
|
|
gxDataInfo di;
|
|
di_init(&di);
|
|
if ((ret = bb_getUInt8(data, &e->selector)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if ((ret = dlms_getData(data, &di, &e->parameters)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bb_clear(data);
|
|
svr_preRead(&settings->base, arr);
|
|
hlp_setObjectCount(cnt, data);
|
|
unsigned char moreData = 0;
|
|
for (pos = 0; pos != arr->size; ++pos)
|
|
{
|
|
if ((ret = vec_getByIndex(arr, pos, &e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
e->value.byteArr = data;
|
|
e->value.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (e->error == 0)
|
|
{
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t pos2 = data->size;
|
|
#else
|
|
uint16_t pos2 = data->size;
|
|
#endif
|
|
bb_setUInt8(data, (unsigned char)e->error);
|
|
if (!e->handled)
|
|
{
|
|
if ((ret = cosem_getValue(&settings->base, e)) != 0)
|
|
{
|
|
e->error = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
bb_setUInt8ByIndex(data, pos2, (unsigned char)e->error);
|
|
}
|
|
}
|
|
}
|
|
if (e->error == 0)
|
|
{
|
|
if (!e->byteArray)
|
|
{
|
|
if ((ret = dlms_setData(data, e->value.vt, &e->value)) != 0)
|
|
{
|
|
e->error = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
}
|
|
else if (!bb_isAttached(e->value.byteArr))
|
|
{
|
|
if ((ret = bb_set2(data, e->value.byteArr, 0, e->value.byteArr->size)) != 0)
|
|
{
|
|
e->error = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
var_clear(&e->value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(data, 1);
|
|
bb_setUInt8(data, (unsigned char)e->error);
|
|
}
|
|
//PDU is used for serialization. Set data type to none so size is not changed.
|
|
if (e->byteArray || (e->value.vt == DLMS_DATA_TYPE_OCTET_STRING && bb_isAttached(e->value.byteArr)))
|
|
{
|
|
e->value.vt = DLMS_DATA_TYPE_NONE;
|
|
}
|
|
if (e->transactionStartIndex != e->transactionEndIndex)
|
|
{
|
|
e->transaction = 1;
|
|
settings->transaction.command = DLMS_COMMAND_GET_REQUEST;
|
|
moreData = 1;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
trans_clear(&settings->transaction);
|
|
settings->transaction.targets = list;
|
|
var_clear(&e->value);
|
|
vec_init(&list);
|
|
if (!bb_isAttached(data))
|
|
{
|
|
bb_set2(&settings->transaction.data, data, data->position, data->size - data->position);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
}
|
|
svr_postRead(&settings->base, arr);
|
|
params_initLN(&p, &settings->base, invokeId, DLMS_COMMAND_GET_RESPONSE, 3, NULL, data, 0xFF, settings->info.encryptedCommand, moreData, !moreData);
|
|
ret = dlms_getLNPdu(&p, data);
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&list);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleGetRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
DLMS_GET_COMMAND_TYPE type;
|
|
unsigned char invokeId, ch;
|
|
// Get type.
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
type = (DLMS_GET_COMMAND_TYPE)ch;
|
|
// Get invoke ID and priority.
|
|
if ((ret = bb_getUInt8(data, &invokeId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
updateInvokeId(settings, invokeId);
|
|
// GetRequest normal
|
|
if (type == DLMS_GET_COMMAND_TYPE_NORMAL)
|
|
{
|
|
ret = svr_getRequestNormal(settings, data, invokeId);
|
|
if (ret == DLMS_ERROR_CODE_INVALID_COMMAND)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
else if (type == DLMS_GET_COMMAND_TYPE_NEXT_DATA_BLOCK)
|
|
{
|
|
// Get request for next data block
|
|
ret = svr_getRequestNextDataBlock(settings, data, invokeId);
|
|
}
|
|
else if (type == DLMS_GET_COMMAND_TYPE_WITH_LIST)
|
|
{
|
|
if ((settings->base.negotiatedConformance & DLMS_CONFORMANCE_MULTIPLE_REFERENCES) == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
// Get request with a list.
|
|
ret = svr_getRequestWithList(settings, data, invokeId);
|
|
}
|
|
else
|
|
{
|
|
ret = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
type = DLMS_GET_COMMAND_TYPE_NORMAL;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
//If error is not DLMS error code.
|
|
if (!(ret > 0 && ret <= DLMS_ERROR_CODE_OTHER_REASON))
|
|
{
|
|
// Access Error : Device reports Read-Write denied.
|
|
ret = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
}
|
|
gxLNParameters p;
|
|
params_initLN(&p, &settings->base, invokeId, DLMS_COMMAND_GET_RESPONSE, type, NULL, data, ret, settings->info.encryptedCommand, 0, 0);
|
|
bb_clear(data);
|
|
ret = dlms_getLNPdu(&p, data);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
/**
|
|
* Find Short Name object.
|
|
*
|
|
* @param sn
|
|
*/
|
|
int svr_findSNObject(dlmsServerSettings* settings, int sn, gxSNInfo* i)
|
|
{
|
|
uint16_t pos;
|
|
int ret;
|
|
gxObject* it;
|
|
unsigned char offset, count;
|
|
i->action = 0;
|
|
i->item = NULL;
|
|
i->index = 0;
|
|
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &it)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (sn >= it->shortName)
|
|
{
|
|
// If attribute is accessed.
|
|
if (sn < it->shortName + obj_attributeCount(it) * 8)
|
|
{
|
|
i->action = 0;
|
|
i->item = it;
|
|
i->index = (unsigned char)(((sn - it->shortName) / 8) + 1);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// If method is accessed.
|
|
dlms_getActionInfo((DLMS_OBJECT_TYPE)it->objectType, &offset, &count);
|
|
if (sn < it->shortName + offset + (8 * count))
|
|
{
|
|
i->item = it;
|
|
i->action = 1;
|
|
i->index = (unsigned char)((sn - it->shortName - offset) / 8 + 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (i->item == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int svr_handleRead(
|
|
dlmsServerSettings* settings,
|
|
DLMS_VARIABLE_ACCESS_SPECIFICATION type,
|
|
gxByteBuffer* data,
|
|
gxValueEventCollection* list)
|
|
{
|
|
gxSNInfo info;
|
|
gxValueEventArg* e;
|
|
int ret;
|
|
gxDataInfo di;
|
|
uint16_t sn;
|
|
di_init(&di);
|
|
if ((ret = bb_getUInt16(data, &sn)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = svr_findSNObject(settings, sn, &info)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (settings->transaction.targets.capacity < 1)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
list = &settings->transaction.targets;
|
|
list->size = 1;
|
|
e = &list->data[0];
|
|
ve_clear(e);
|
|
#else
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_push(list, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
e->target = info.item;
|
|
e->index = info.index;
|
|
e->action = info.action;
|
|
if (type == DLMS_VARIABLE_ACCESS_SPECIFICATION_PARAMETERISED_ACCESS)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &e->selector)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = dlms_getData(data, &di, &e->parameters)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
if (svr_getAttributeAccess(&settings->base, info.item, info.index) == DLMS_ACCESS_MODE_NONE)
|
|
{
|
|
e->error = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
e->handled = 1;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //#ifdef !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
|
|
/**
|
|
* Get data for Read command.
|
|
*
|
|
* @param settings
|
|
* DLMS settings.
|
|
* @param list
|
|
* received objects.
|
|
* @param data
|
|
* Data as byte array.
|
|
* @param type
|
|
* Response type.
|
|
*/
|
|
int svr_getReadData(
|
|
dlmsServerSettings* settings,
|
|
gxValueEventCollection* list,
|
|
gxByteBuffer* data,
|
|
DLMS_SINGLE_READ_RESPONSE* type,
|
|
unsigned char* multipleBlocks)
|
|
{
|
|
int ret;
|
|
gxValueEventArg* e;
|
|
uint32_t pos;
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t statusindex;
|
|
#else
|
|
uint16_t statusindex;
|
|
#endif
|
|
unsigned char first = 1;
|
|
*type = DLMS_SINGLE_READ_RESPONSE_DATA;
|
|
bb_clear(data);
|
|
for (pos = 0; pos != list->size; ++pos)
|
|
{
|
|
if ((ret = vec_getByIndex(list, pos, &e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
statusindex = data->size;
|
|
if (!e->handled)
|
|
{
|
|
if (e->action)
|
|
{
|
|
if ((ret = cosem_invoke(settings, e)) != 0)
|
|
{
|
|
e->error = (DLMS_ERROR_CODE)ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = cosem_getValue(&settings->base, e)) != 0)
|
|
{
|
|
e->error = (DLMS_ERROR_CODE)ret;
|
|
}
|
|
}
|
|
}
|
|
if (e->error == DLMS_ERROR_CODE_OK)
|
|
{
|
|
if (!first && list->size != 1)
|
|
{
|
|
bb_insertUInt8(data, statusindex, DLMS_SINGLE_READ_RESPONSE_DATA);
|
|
}
|
|
if (e->byteArray)
|
|
{
|
|
e->value.vt = DLMS_DATA_TYPE_NONE;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = dlms_setData(data, e->value.vt, &e->value)) != 0)
|
|
{
|
|
var_clear(&e->value);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!first && list->size != 1)
|
|
{
|
|
bb_insertUInt8(data, statusindex, DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR);
|
|
}
|
|
bb_insertUInt8(data, statusindex + 1, e->error);
|
|
*type = DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR;
|
|
}
|
|
var_clear(&e->value);
|
|
first = 0;
|
|
*multipleBlocks |= e->transactionEndIndex != e->transactionStartIndex;
|
|
e->transaction = *multipleBlocks;
|
|
}
|
|
return 0;
|
|
}
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
int svr_handleReadBlockNumberAccess(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
gxSNParameters p;
|
|
gxValueEventArg* e;
|
|
uint16_t pos, blockNumber;
|
|
unsigned char lastBlock = 1;
|
|
int ret;
|
|
unsigned char multipleBlocks;
|
|
if ((ret = bb_getUInt16(data, &blockNumber)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (blockNumber != settings->base.blockIndex)
|
|
{
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_setUInt8(&bb, DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID);
|
|
params_initSN(&p, &settings->base,
|
|
DLMS_COMMAND_READ_RESPONSE, 1,
|
|
DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR, &bb, NULL, settings->info.encryptedCommand);
|
|
ret = dlms_getSNPdu(&p, data);
|
|
resetBlockIndex(&settings->base);
|
|
bb_clear(&bb);
|
|
return ret;
|
|
}
|
|
gxValueEventCollection reads;
|
|
gxValueEventCollection actions;
|
|
vec_init(&reads);
|
|
vec_init(&actions);
|
|
for (pos = 0; pos != settings->transaction.targets.size; ++pos)
|
|
{
|
|
if ((ret = vec_getByIndex(&settings->transaction.targets, pos, &e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (e->action)
|
|
{
|
|
vec_push(&actions, e);
|
|
}
|
|
else
|
|
{
|
|
vec_push(&reads, e);
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
DLMS_SINGLE_READ_RESPONSE requestType;
|
|
if (reads.size != 0)
|
|
{
|
|
svr_preRead(&settings->base, &reads);
|
|
}
|
|
|
|
if (actions.size != 0)
|
|
{
|
|
svr_preAction(&settings->base, &actions);
|
|
}
|
|
ret = svr_getReadData(settings, &settings->transaction.targets,
|
|
data, &requestType, &multipleBlocks);
|
|
if (reads.size != 0)
|
|
{
|
|
svr_postRead(&settings->base, &reads);
|
|
}
|
|
if (actions.size != 0)
|
|
{
|
|
svr_postAction(&settings->base, &actions);
|
|
}
|
|
if (lastBlock)
|
|
{
|
|
lastBlock = e->transactionStartIndex == e->transactionEndIndex;
|
|
}
|
|
}
|
|
vec_empty(&reads);
|
|
vec_empty(&actions);
|
|
if (ret == 0)
|
|
{
|
|
++settings->base.blockIndex;
|
|
params_initSN(&p, &settings->base, DLMS_COMMAND_READ_RESPONSE, 1,
|
|
DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT, NULL, data, settings->info.encryptedCommand);
|
|
p.multipleBlocks = 1;
|
|
p.lastBlock = lastBlock;
|
|
ret = dlms_getSNPdu(&p, data);
|
|
// If all data is sent.
|
|
if (p.lastBlock && settings->transaction.data.size == settings->transaction.data.position)
|
|
{
|
|
trans_clear(&settings->transaction);
|
|
resetBlockIndex(&settings->base);
|
|
}
|
|
else
|
|
{
|
|
bb_trim(&settings->transaction.data);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleReadDataBlockAccess(
|
|
dlmsServerSettings* settings,
|
|
DLMS_COMMAND command,
|
|
gxByteBuffer* data,
|
|
int cnt)
|
|
{
|
|
gxSNParameters p;
|
|
int ret;
|
|
uint16_t size;
|
|
uint16_t blockNumber;
|
|
unsigned char isLast, ch;
|
|
unsigned char count = 1, type = DLMS_DATA_TYPE_OCTET_STRING;
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
isLast = ch != 0;
|
|
if ((ret = bb_getUInt16(data, &blockNumber)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (blockNumber != settings->base.blockIndex)
|
|
{
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_setUInt8(&bb, DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID);
|
|
params_initSN(&p, &settings->base, command, 1, DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR, &bb, NULL, settings->info.encryptedCommand);
|
|
ret = dlms_getSNPdu(&p, data);
|
|
resetBlockIndex(&settings->base);
|
|
bb_clear(&bb);
|
|
return ret;
|
|
}
|
|
if (command == DLMS_COMMAND_WRITE_RESPONSE)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &count)) != 0 ||
|
|
(ret = bb_getUInt8(data, &type)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
if (hlp_getObjectCount2(data, &size) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if (count != 1 || type != DLMS_DATA_TYPE_OCTET_STRING ||
|
|
size != data->size - data->position)
|
|
{
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_setUInt8(&bb, DLMS_ERROR_CODE_DATA_BLOCK_UNAVAILABLE);
|
|
params_initSN(&p, &settings->base, command, cnt,
|
|
DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR, &bb, NULL, settings->info.encryptedCommand);
|
|
ret = dlms_getSNPdu(&p, data);
|
|
bb_clear(&bb);
|
|
resetBlockIndex(&settings->base);
|
|
return ret;
|
|
}
|
|
settings->transaction.command = command;
|
|
bb_set2(&settings->transaction.data, data, data->position, data->size - data->position);
|
|
if (!isLast)
|
|
{
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_setUInt16(&bb, blockNumber);
|
|
++settings->base.blockIndex;
|
|
if (command == DLMS_COMMAND_READ_RESPONSE)
|
|
{
|
|
type = DLMS_SINGLE_READ_RESPONSE_BLOCK_NUMBER;
|
|
}
|
|
else
|
|
{
|
|
type = DLMS_SINGLE_WRITE_RESPONSE_BLOCK_NUMBER;
|
|
}
|
|
params_initSN(&p, &settings->base, command, cnt, type, NULL, &bb, settings->info.encryptedCommand);
|
|
ret = dlms_getSNPdu(&p, data);
|
|
bb_clear(&bb);
|
|
}
|
|
else
|
|
{
|
|
data->position = data->size = 0;
|
|
bb_set2(data, &settings->transaction.data,
|
|
settings->transaction.data.position,
|
|
settings->transaction.data.size - settings->transaction.data.position);
|
|
trans_clear(&settings->transaction);
|
|
if (command == DLMS_COMMAND_READ_RESPONSE)
|
|
{
|
|
ret = svr_handleReadRequest(settings, data);
|
|
}
|
|
else
|
|
{
|
|
ret = svr_handleWriteRequest(settings, data);
|
|
}
|
|
resetBlockIndex(&settings->base);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int svr_returnSNError(
|
|
dlmsServerSettings* settings,
|
|
DLMS_COMMAND cmd,
|
|
DLMS_ERROR_CODE error,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
gxByteBuffer bb;
|
|
gxSNParameters p;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_setUInt8(&bb, error);
|
|
params_initSN(&p, &settings->base, cmd, 1,
|
|
DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR, &bb, NULL, settings->info.encryptedCommand);
|
|
ret = dlms_getSNPdu(&p, data);
|
|
resetBlockIndex(&settings->base);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Handle read request.
|
|
*/
|
|
int svr_handleReadRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
DLMS_SINGLE_READ_RESPONSE requestType;
|
|
gxSNParameters p;
|
|
int ret = 0;
|
|
unsigned char ch, multipleBlocks = 0;
|
|
uint16_t pos, cnt = 0;
|
|
DLMS_VARIABLE_ACCESS_SPECIFICATION type;
|
|
gxValueEventCollection list;
|
|
// If get next frame.
|
|
if (data->size == 0)
|
|
{
|
|
if (settings->transaction.command != DLMS_COMMAND_NONE)
|
|
{
|
|
return 0;
|
|
}
|
|
list = settings->transaction.targets;
|
|
}
|
|
else
|
|
{
|
|
vec_init(&list);
|
|
if (hlp_getObjectCount2(data, &cnt) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
type = (DLMS_VARIABLE_ACCESS_SPECIFICATION)ch;
|
|
switch (type)
|
|
{
|
|
case DLMS_VARIABLE_ACCESS_SPECIFICATION_VARIABLE_NAME:
|
|
case DLMS_VARIABLE_ACCESS_SPECIFICATION_PARAMETERISED_ACCESS:
|
|
ret = svr_handleRead(settings, type, data, &list);
|
|
break;
|
|
case DLMS_VARIABLE_ACCESS_SPECIFICATION_BLOCK_NUMBER_ACCESS:
|
|
vec_clear(&list);
|
|
return svr_handleReadBlockNumberAccess(settings, data);
|
|
case DLMS_VARIABLE_ACCESS_SPECIFICATION_READ_DATA_BLOCK_ACCESS:
|
|
vec_clear(&list);
|
|
return svr_handleReadDataBlockAccess(settings, DLMS_COMMAND_READ_RESPONSE, data, cnt);
|
|
default:
|
|
vec_clear(&list);
|
|
return svr_returnSNError(settings, DLMS_COMMAND_READ_RESPONSE, DLMS_ERROR_CODE_READ_WRITE_DENIED, data);
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
if (list.size != 0)
|
|
{
|
|
svr_preRead(&settings->base, &list);
|
|
}
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
ret = svr_getReadData(settings, &list, data, &requestType, &multipleBlocks);
|
|
if (ret == 0)
|
|
{
|
|
if (list.size != 0)
|
|
{
|
|
svr_postRead(&settings->base, &list);
|
|
}
|
|
params_initSN(&p, &settings->base, DLMS_COMMAND_READ_RESPONSE, cnt,
|
|
requestType, NULL, data, settings->info.encryptedCommand);
|
|
p.multipleBlocks = multipleBlocks;
|
|
p.lastBlock = list.data[0]->transactionStartIndex == list.data[0]->transactionEndIndex;
|
|
ret = dlms_getSNPdu(&p, data);
|
|
if (ret == 0)
|
|
{
|
|
if (list.data[0]->transactionStartIndex != list.data[0]->transactionEndIndex)
|
|
{
|
|
settings->transaction.targets = list;
|
|
vec_init(&list);
|
|
settings->transaction.command = DLMS_COMMAND_READ_REQUEST;
|
|
if (!bb_isAttached(data))
|
|
{
|
|
bb_set2(&settings->transaction.data, data, data->position, data->size - data->position);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vec_clear(&list);
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleWriteRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
gxSNParameters p;
|
|
int ret = 0;
|
|
gxValueEventArg* e;
|
|
unsigned char ch;
|
|
uint16_t sn;
|
|
uint16_t cnt, pos;
|
|
gxDataInfo di;
|
|
DLMS_ACCESS_MODE am;
|
|
gxSNInfo i;
|
|
gxValueEventCollection list;
|
|
gxValueEventCollection writes;
|
|
gxValueEventCollection actions;
|
|
DLMS_VARIABLE_ACCESS_SPECIFICATION type;
|
|
gxByteBuffer results;
|
|
// Get object count.
|
|
if (hlp_getObjectCount2(data, &cnt) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
BYTE_BUFFER_INIT(&results);
|
|
bb_capacity(&results, cnt);
|
|
vec_init(&list);
|
|
vec_init(&writes);
|
|
vec_init(&actions);
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
type = (DLMS_VARIABLE_ACCESS_SPECIFICATION)ch;
|
|
switch (type)
|
|
{
|
|
case DLMS_VARIABLE_ACCESS_SPECIFICATION_VARIABLE_NAME:
|
|
if ((ret = bb_getUInt16(data, &sn)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if ((ret = svr_findSNObject(settings, sn, &i)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
e->target = i.item;
|
|
e->index = i.index;
|
|
e->action = i.action;
|
|
vec_push(&list, e);
|
|
if (e->action)
|
|
{
|
|
vec_push(&actions, e);
|
|
}
|
|
else
|
|
{
|
|
vec_push(&writes, e);
|
|
}
|
|
// Return error if connection is not established.
|
|
if (!settings->info.preEstablished && (settings->base.connected & DLMS_CONNECTION_STATE_DLMS) == 0
|
|
&& (!e->action || e->target->shortName != 0xFA00 || e->index != 8))
|
|
{
|
|
ret = svr_generateConfirmedServiceError(
|
|
settings,
|
|
DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR,
|
|
DLMS_SERVICE_ERROR_SERVICE,
|
|
DLMS_SERVICE_UNSUPPORTED, data);
|
|
break;
|
|
}
|
|
// If target is unknown.
|
|
if (i.item == NULL)
|
|
{
|
|
// Device reports a undefined object.
|
|
bb_setUInt8(&results, DLMS_ERROR_CODE_UNDEFINED_OBJECT);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(&results, DLMS_ERROR_CODE_OK);
|
|
}
|
|
break;
|
|
case DLMS_VARIABLE_ACCESS_SPECIFICATION_WRITE_DATA_BLOCK_ACCESS:
|
|
// Return error if connection is not established.
|
|
if (!settings->info.preEstablished && (settings->base.connected & DLMS_CONNECTION_STATE_DLMS) == 0)
|
|
{
|
|
ret = svr_generateConfirmedServiceError(
|
|
settings,
|
|
DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR,
|
|
DLMS_SERVICE_ERROR_SERVICE,
|
|
DLMS_SERVICE_UNSUPPORTED, data);
|
|
break;
|
|
}
|
|
bb_clear(&results);
|
|
return svr_handleReadDataBlockAccess(settings, DLMS_COMMAND_WRITE_RESPONSE, data, cnt);
|
|
default:
|
|
// Device reports a HW error.
|
|
ret = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
}
|
|
// Get data count.
|
|
if (hlp_getObjectCount2(data, &cnt) != 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
cnt = 0;
|
|
}
|
|
di_init(&di);
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8ByIndex(&results, pos, &ch)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (ch == 0)
|
|
{
|
|
// If object has found.
|
|
if ((ret = vec_getByIndex(&list, pos, &e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (e->action)
|
|
{
|
|
if ((ret = dlms_getData(data, &di, &e->parameters)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if ((ret = dlms_getData(data, &di, &e->value)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
di_init(&di);
|
|
am = svr_getAttributeAccess(&settings->base, e->target, e->index);
|
|
// If write is denied.
|
|
if (am != DLMS_ACCESS_MODE_WRITE && am != DLMS_ACCESS_MODE_READ_WRITE)
|
|
{
|
|
bb_setUInt8ByIndex(&results, pos, DLMS_ERROR_CODE_READ_WRITE_DENIED);
|
|
}
|
|
else
|
|
{
|
|
if (writes.size != 0)
|
|
{
|
|
if (pos == 0)
|
|
{
|
|
svr_preWrite(&settings->base, &list);
|
|
}
|
|
if (e->error != 0)
|
|
{
|
|
bb_setUInt8ByIndex(&results, pos, e->error);
|
|
}
|
|
else if (!e->handled)
|
|
{
|
|
if ((ret = cosem_setValue(&settings->base, e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
//Call post write after all values are written.
|
|
if (pos == cnt - 1)
|
|
{
|
|
svr_postWrite(&settings->base, &list);
|
|
if (e->error != 0)
|
|
{
|
|
bb_setUInt8ByIndex(&results, pos, e->error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (actions.size != 0)
|
|
{
|
|
if (pos == 0)
|
|
{
|
|
svr_preAction(&settings->base, &actions);
|
|
}
|
|
ret = cosem_invoke(settings, e);
|
|
// If High level authentication fails.
|
|
if (e->target != NULL && e->target->objectType == DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME && e->index == 1)
|
|
{
|
|
if ((settings->base.connected & DLMS_CONNECTION_STATE_DLMS) != 0)
|
|
{
|
|
svr_connected(settings);
|
|
}
|
|
else
|
|
{
|
|
svr_invalidConnection(settings);
|
|
}
|
|
}
|
|
//Call post action after all values are invoked.
|
|
if (pos == cnt - 1)
|
|
{
|
|
svr_postAction(&settings->base, &actions);
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
// Add parameters error code.
|
|
if (ret > 0 && ret < DLMS_ERROR_CODE_OTHER_REASON + 1)
|
|
{
|
|
bb_setUInt8ByIndex(&results, pos, ret);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8ByIndex(&results, pos, DLMS_ERROR_CODE_READ_WRITE_DENIED);
|
|
}
|
|
}
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_capacity(&bb, 2 * cnt);
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
if ((ret = bb_getUInt8(&results, &ch)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
// If meter returns error.
|
|
if (ch != 0)
|
|
{
|
|
bb_setUInt8(&bb, 1);
|
|
}
|
|
bb_setUInt8(&bb, ch);
|
|
}
|
|
params_initSN(&p, &settings->base, DLMS_COMMAND_WRITE_RESPONSE, cnt, 0xFF, &bb, NULL, settings->info.encryptedCommand);
|
|
p.lastBlock = e->transactionStartIndex == e->transactionEndIndex;
|
|
ret = dlms_getSNPdu(&p, data);
|
|
bb_clear(&bb);
|
|
// If all data is transfered.
|
|
bb_clear(&results);
|
|
vec_empty(&writes);
|
|
vec_empty(&actions);
|
|
vec_clear(&list);
|
|
return ret;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
|
|
#ifndef DLMS_IGNORE_ACTION
|
|
/**
|
|
* Handle action request.
|
|
*
|
|
* @param reply
|
|
* Received data from the client.
|
|
* @return Reply.
|
|
*/
|
|
int svr_handleMethodRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
DLMS_OBJECT_TYPE ci;
|
|
gxValueEventArg* e;
|
|
unsigned char* ln;
|
|
int error = DLMS_ERROR_CODE_OK;
|
|
int ret;
|
|
unsigned char invokeId, ch, id;
|
|
uint16_t tmp;
|
|
gxValueEventCollection* list;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
gxValueEventCollection arr;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
// Get type.
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get invoke ID and priority.
|
|
if ((ret = bb_getUInt8(data, &invokeId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
updateInvokeId(settings, invokeId);
|
|
// CI
|
|
if ((ret = bb_getUInt16(data, &tmp)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ci = (DLMS_OBJECT_TYPE)tmp;
|
|
ln = data->data + data->position;
|
|
data->position += 6;
|
|
// Attribute
|
|
if ((ret = bb_getUInt8(data, &id)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Get parameters.
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (settings->transaction.targets.capacity < 1)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
list = &settings->transaction.targets;
|
|
list->size = 1;
|
|
e = &list->data[0];
|
|
ve_clear(e);
|
|
e->value.byteArr = &settings->info.data;
|
|
e->value.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
#else
|
|
list = &arr;
|
|
vec_init(list);
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_push(list, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
e->index = id;
|
|
if (ch != 0)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
e->parameters.byteArr = data;
|
|
e->parameters.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
gxDataInfo di;
|
|
di_init(&di);
|
|
if ((ret = dlms_getData(data, &di, &e->parameters)) != 0)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_clear(data);
|
|
vec_clear(&arr);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
}
|
|
if (ci == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME)
|
|
{
|
|
e->target = NULL;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = oa_findByLN(&settings->base.objects, ci, ln, &e->target)) != 0)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_clear(data);
|
|
vec_clear(&arr);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
}
|
|
if (e->target == NULL)
|
|
{
|
|
ret = svr_findObject(&settings->base, ci, 0, ln, e);
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_clear(data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (e->target == NULL)
|
|
{
|
|
error = DLMS_ERROR_CODE_UNDEFINED_OBJECT;
|
|
}
|
|
else
|
|
{
|
|
#if !defined(DLMS_ITALIAN_STANDARD)
|
|
//In Italian standard reply_to_HLS_authentication can be called without access rights.
|
|
if (!(ci == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME && id == 1) &&
|
|
svr_getMethodAccess(&settings->base, e->target, id) == DLMS_METHOD_ACCESS_MODE_NONE)
|
|
#else
|
|
if (svr_getMethodAccess(&settings->base, e->target, id) == DLMS_METHOD_ACCESS_MODE_NONE)
|
|
#endif //defined(DLMS_ITALIAN_STANDARD)
|
|
{
|
|
e->error = DLMS_ERROR_CODE_READ_WRITE_DENIED;
|
|
}
|
|
else
|
|
{
|
|
svr_preAction(&settings->base, list);
|
|
if (!e->handled)
|
|
{
|
|
if ((ret = cosem_invoke(settings, e)) != 0)
|
|
{
|
|
e->error = (DLMS_ERROR_CODE)ret;
|
|
ret = 0;
|
|
}
|
|
svr_postAction(&settings->base, list);
|
|
}
|
|
}
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (data->position == data->size)
|
|
{
|
|
bb_clear(data);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
// Set default action reply if not given.
|
|
if (e->error == DLMS_ERROR_CODE_OK)
|
|
{
|
|
if (// Add return parameters
|
|
(ret = bb_insertUInt8(data, 0, 0)) != 0 ||
|
|
//Add parameters error code.
|
|
(ret = bb_insertUInt8(data, 0, 1)) != 0)
|
|
{
|
|
error = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
if (e->byteArray)
|
|
{
|
|
if (!bb_isAttached(e->value.byteArr) && e->value.vt == DLMS_DATA_TYPE_OCTET_STRING)
|
|
{
|
|
if ((ret = bb_set2(data, e->value.byteArr, 0, bb_size(e->value.byteArr))) != 0)
|
|
{
|
|
error = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
var_clear(&e->value);
|
|
}
|
|
else
|
|
{
|
|
e->value.vt = DLMS_DATA_TYPE_NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = dlms_setData(data, e->value.vt, &e->value)) != 0)
|
|
{
|
|
error = DLMS_ERROR_CODE_HARDWARE_FAULT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Add parameters error code.
|
|
if (e->error > 0 && e->error < DLMS_ERROR_CODE_OTHER_REASON + 1)
|
|
{
|
|
error = e->error;
|
|
}
|
|
else
|
|
{
|
|
error = DLMS_ERROR_CODE_INCONSISTENT_CLASS_OR_OBJECT;
|
|
}
|
|
bb_clear(data);
|
|
bb_setUInt8(data, 0);
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
gxLNParameters p;
|
|
params_initLN(&p, &settings->base, invokeId, DLMS_COMMAND_METHOD_RESPONSE, 1, NULL, data, error, settings->info.encryptedCommand, 0, 0);
|
|
ret = dlms_getLNPdu(&p, data);
|
|
// If High level authentication fails.
|
|
if (e->target != NULL && e->target->objectType == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME && id == 1)
|
|
{
|
|
#ifndef DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
if (((gxAssociationLogicalName*)e->target)->associationStatus == DLMS_ASSOCIATION_STATUS_ASSOCIATED)
|
|
{
|
|
settings->base.connected |= DLMS_CONNECTION_STATE_DLMS;
|
|
svr_connected(settings);
|
|
}
|
|
else
|
|
{
|
|
svr_invalidConnection(settings);
|
|
settings->base.connected &= ~DLMS_CONNECTION_STATE_DLMS;
|
|
}
|
|
#endif //DLMS_IGNORE_ASSOCIATION_LOGICAL_NAME
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&arr);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_ACTION
|
|
|
|
/**
|
|
* Handles release request.
|
|
*
|
|
* @param settings
|
|
* DLMS settings.
|
|
* @param data
|
|
* Received data.
|
|
*/
|
|
int svr_handleReleaseRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data) {
|
|
int ret;
|
|
unsigned char ch, len;
|
|
gxByteBuffer tmp;
|
|
//Get len.
|
|
if ((ret = bb_getUInt8(data, &len)) != 0 ||
|
|
(ret = bb_getUInt8(data, &ch)) != 0 ||
|
|
(ret = bb_getUInt8(data, &ch)) != 0 ||
|
|
//Get reason.
|
|
(ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
unsigned char userInfo = len != 3;
|
|
bb_clear(data);
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
unsigned char offset = IS_HDLC(settings->base.interfaceType) ? 12 : 9;
|
|
bb_attach(&tmp, data->data + offset, 0, data->capacity - offset);
|
|
#else
|
|
BYTE_BUFFER_INIT(&tmp);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (!(userInfo && (ret = apdu_getUserInformation(&settings->base, &tmp, 0)) != 0))
|
|
{
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (IS_HDLC(settings->base.interfaceType))
|
|
{
|
|
dlms_addLLCBytes(&settings->base, data);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
if ((ret = bb_setUInt8(data, 0x63)) == 0 &&
|
|
//Len.
|
|
(ret = bb_setUInt8(data, (unsigned char)(tmp.size + 3))) == 0 &&
|
|
(ret = bb_setUInt8(data, 0x80)) == 0 &&
|
|
(ret = bb_setUInt8(data, 0x01)) == 0 &&
|
|
//Reason
|
|
(ret = bb_setUInt8(data, ch)) == 0)
|
|
{
|
|
if (tmp.size != 0)
|
|
{
|
|
if ((ret = bb_setUInt8(data, 0xBE)) == 0 &&
|
|
(ret = bb_setUInt8(data, (unsigned char)(tmp.size + 1))) == 0 &&
|
|
(ret = bb_setUInt8(data, 4)) == 0 &&
|
|
(ret = bb_setUInt8(data, (unsigned char)tmp.size)) == 0)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
ret = bb_set(data, tmp.data, tmp.size);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_PLC
|
|
int svr_registerRequest(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* initiatorSystemTitle,
|
|
gxByteBuffer* systemTitle,
|
|
gxByteBuffer* data)
|
|
{
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
bb_setUInt8(&bb, DLMS_COMMAND_REGISTER_REQUEST);
|
|
bb_set(&bb, initiatorSystemTitle->data, initiatorSystemTitle->size);
|
|
// LEN
|
|
bb_setUInt8(&bb, 0x1);
|
|
bb_set(&bb, systemTitle->data, systemTitle->size);
|
|
// MAC address.
|
|
bb_setUInt16(&bb, settings->plcSettings.macSourceAddress);
|
|
int ret;
|
|
int val = settings->plcSettings.initialCredit << 5;
|
|
val |= settings->plcSettings.currentCredit << 2;
|
|
val |= settings->plcSettings.deltaCredit & 0x3;
|
|
int clientAddress = settings->clientAddress;
|
|
int serverAddress = settings->serverAddress;
|
|
int macSourceAddress = settings->plcSettings.macSourceAddress;
|
|
int macTargetAddress = settings->plcSettings.macDestinationAddress;
|
|
// 10.4.6.4 Source and destination APs and addresses of CI-PDUs
|
|
// Client address is No-station in discoverReport.
|
|
if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC_HDLC)
|
|
{
|
|
settings->plcSettings.initialCredit = 0;
|
|
settings->plcSettings.currentCredit = 0;
|
|
settings->plcSettings.macSourceAddress = 0xC01;
|
|
settings->plcSettings.macDestinationAddress = 0xFFF;
|
|
settings->clientAddress = 0x66;
|
|
// All-station
|
|
settings->serverAddress = 0x33FF;
|
|
}
|
|
else
|
|
{
|
|
settings->clientAddress = 1;
|
|
settings->serverAddress = 0;
|
|
settings->plcSettings.macSourceAddress = 0xC00;
|
|
settings->plcSettings.macDestinationAddress = 0xFFF;
|
|
}
|
|
ret = dlms_getMacFrame(settings, 0x13, val, &bb, data);
|
|
settings->clientAddress = clientAddress;
|
|
settings->serverAddress = serverAddress;
|
|
settings->plcSettings.macSourceAddress = macSourceAddress;
|
|
settings->plcSettings.macDestinationAddress = macTargetAddress;
|
|
return ret;
|
|
}
|
|
|
|
int svr_parseRegisterRequest(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* value)
|
|
{
|
|
int ret;
|
|
unsigned char pos, count;
|
|
// Get System title.
|
|
bb_get(value, settings->plcSettings.systemTitle.data, settings->plcSettings.systemTitle.size);
|
|
if ((ret = bb_getUInt8(value, &count)) == 0)
|
|
{
|
|
for (pos = 0; pos != count; ++pos)
|
|
{
|
|
// MAC address.
|
|
if ((ret = bb_get(value, settings->plcSettings.systemTitle.data, settings->plcSettings.systemTitle.size)) != 0 ||
|
|
(ret = bb_getUInt16(value, &settings->plcSettings.macSourceAddress)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int svr_parseDiscoverRequest(
|
|
gxByteBuffer* value,
|
|
gxPlcRegister* reg)
|
|
{
|
|
int ret;
|
|
if ((ret = bb_getUInt8(value, ®->responseProbability)) != 0 ||
|
|
(ret = bb_getUInt16(value, ®->allowedTimeSlots)) != 0 ||
|
|
(ret = bb_getUInt8(value, ®->discoverReportInitialCredit)) != 0 ||
|
|
(ret = bb_getUInt8(value, ®->icEqualCredit)) != 0)
|
|
{
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dlms_pingRequest(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* systemTitle,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
// Control byte.
|
|
if ((ret = bb_setUInt8(&bb, DLMS_COMMAND_PING_REQUEST)) != 0 ||
|
|
(ret = bb_set(&bb, systemTitle->data, systemTitle->size)) != 0 ||
|
|
(ret = dlms_getMacFrame(settings, 0x13, 0, &bb, data)) != 0)
|
|
{
|
|
bb_clear(&bb);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dlm_parsePing(gxByteBuffer* data, gxByteBuffer* value)
|
|
{
|
|
bb_clear(data);
|
|
return bb_set(value, data->data + data->position + 1, 6);
|
|
}
|
|
|
|
int dlms_repeaterCallRequest(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
// Control byte.
|
|
if ((ret = bb_setUInt8(&bb, DLMS_COMMAND_REPEAT_CALL_REQUEST)) != 0 ||
|
|
// MaxAdrMac.
|
|
(ret = bb_setUInt16(&bb, 0x63)) != 0 ||
|
|
// Nb_Tslot_For_New
|
|
(ret = bb_setUInt8(&bb, 0)) != 0 ||
|
|
// Reception-Threshold default value
|
|
(ret = bb_setUInt8(&bb, 0)) != 0 ||
|
|
(ret = dlms_getMacFrame(settings, 0x13, 0xFC, &bb, data)) != 0)
|
|
{
|
|
}
|
|
bb_clear(&bb);
|
|
return ret;
|
|
}
|
|
|
|
int svr_discoverReport(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* systemTitle,
|
|
unsigned char newMeter,
|
|
gxByteBuffer* data)
|
|
{
|
|
if (settings->interfaceType != DLMS_INTERFACE_TYPE_PLC && settings->interfaceType != DLMS_INTERFACE_TYPE_PLC_HDLC)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
gxByteBuffer bb;
|
|
BYTE_BUFFER_INIT(&bb);
|
|
int ret;
|
|
unsigned char alarmDescription;
|
|
if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC)
|
|
{
|
|
alarmDescription = (newMeter ? 1 : 0x82);
|
|
}
|
|
else
|
|
{
|
|
alarmDescription = 0;
|
|
}
|
|
if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC_HDLC)
|
|
{
|
|
ret = dlms_addLLCBytes(settings, &bb);
|
|
}
|
|
bb_setUInt8(&bb, DLMS_COMMAND_DISCOVER_REPORT);
|
|
bb_setUInt8(&bb, 1);
|
|
bb_set(&bb, settings->plcSettings.systemTitle.data, settings->plcSettings.systemTitle.size);
|
|
if (alarmDescription != 0)
|
|
{
|
|
bb_setUInt8(&bb, 1);
|
|
}
|
|
bb_setUInt8(&bb, alarmDescription);
|
|
int clientAddress = settings->clientAddress;
|
|
int serverAddress = settings->serverAddress;
|
|
int macSourceAddress = settings->plcSettings.macSourceAddress;
|
|
int macTargetAddress = settings->plcSettings.macDestinationAddress;
|
|
// 10.4.6.4 Source and destination APs and addresses of CI-PDUs
|
|
// Client address is No-station in discoverReport.
|
|
if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC_HDLC)
|
|
{
|
|
settings->plcSettings.macDestinationAddress = DLMS_PLC_HDLC_SOURCE_ADDRESS_INITIATOR;
|
|
}
|
|
else
|
|
{
|
|
settings->clientAddress = 0;
|
|
settings->serverAddress = 0xFD;
|
|
}
|
|
ret = dlms_getMacFrame(settings, 0x13, 0, &bb, data);
|
|
//Restore original values.
|
|
settings->clientAddress = clientAddress;
|
|
settings->serverAddress = serverAddress;
|
|
settings->plcSettings.macSourceAddress = macSourceAddress;
|
|
settings->plcSettings.macDestinationAddress = macTargetAddress;
|
|
return ret;
|
|
}
|
|
|
|
#endif //DLMS_IGNORE_PLC
|
|
|
|
int svr_handleCommand(
|
|
dlmsServerSettings* settings,
|
|
DLMS_COMMAND cmd,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
int ret = 0;
|
|
unsigned char frame = 0;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (dlms_useHdlc(settings->base.interfaceType) && bb_size(&settings->transaction.data) != 0)
|
|
{
|
|
//Get next frame.
|
|
frame = getNextSend(&settings->base, 0);
|
|
}
|
|
#else
|
|
if (dlms_useHdlc(settings->base.interfaceType) && bb_size(reply) != 0)
|
|
{
|
|
//Get next frame.
|
|
frame = getNextSend(&settings->base, 0);
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
switch (cmd)
|
|
{
|
|
#ifndef DLMS_IGNORE_SET
|
|
case DLMS_COMMAND_SET_REQUEST:
|
|
//If connection is not established.
|
|
if ((!settings->info.preEstablished && (settings->base.connected & DLMS_CONNECTION_STATE_DLMS) == 0) ||
|
|
//If service is not negotiated.
|
|
(settings->base.negotiatedConformance & DLMS_CONFORMANCE_SET) == 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
ret = svr_handleSetRequest(settings, data);
|
|
}
|
|
break;
|
|
#endif //DLMS_IGNORE_SET
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_WRITE_REQUEST:
|
|
//Connection establised is checked inside of the function because of HLS.
|
|
if ((settings->base.negotiatedConformance & DLMS_CONFORMANCE_WRITE) == 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
ret = svr_handleWriteRequest(settings, data);
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_GET_REQUEST:
|
|
//If connection is not established.
|
|
if ((!settings->info.preEstablished && (settings->base.connected & DLMS_CONNECTION_STATE_DLMS) == 0) ||
|
|
//If service is not negotiated.
|
|
(settings->base.negotiatedConformance & DLMS_CONFORMANCE_GET) == 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
//Check is client reading frames.
|
|
if (settings->transaction.command != DLMS_COMMAND_NONE &&
|
|
data->position == data->size)
|
|
{
|
|
data->position = 0;
|
|
}
|
|
else
|
|
{
|
|
ret = svr_handleGetRequest(settings, data);
|
|
}
|
|
}
|
|
break;
|
|
#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
case DLMS_COMMAND_READ_REQUEST:
|
|
//If connection is not established.
|
|
if ((!settings->info.preEstablished && (settings->base.connected & DLMS_CONNECTION_STATE_DLMS) == 0) ||
|
|
//If service is not negotiated.
|
|
(settings->base.negotiatedConformance & DLMS_CONFORMANCE_READ) == 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
if (settings->transaction.command != DLMS_COMMAND_NONE &&
|
|
data->position == data->size)
|
|
{
|
|
data->position = 0;
|
|
}
|
|
else
|
|
{
|
|
ret = svr_handleReadRequest(settings, data);
|
|
}
|
|
}
|
|
break;
|
|
#endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC)
|
|
#ifndef DLMS_IGNORE_ACTION
|
|
case DLMS_COMMAND_METHOD_REQUEST:
|
|
//Connection established is checked inside of the function because of HLS.
|
|
//If service is not negotiated.
|
|
if ((settings->base.negotiatedConformance & DLMS_CONFORMANCE_ACTION) == 0)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
ret = svr_handleMethodRequest(settings, data);
|
|
if (ret != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("handleMethodRequest failed. ", ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
}
|
|
break;
|
|
#endif //DLMS_IGNORE_ACTION
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
case DLMS_COMMAND_SNRM:
|
|
if ((ret = svr_handleSnrmRequest(settings, data)) == 0)
|
|
{
|
|
settings->base.connected = DLMS_CONNECTION_STATE_HDLC;
|
|
frame = DLMS_COMMAND_UA;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("handleSnrmRequest failed. ", ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
break;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
case DLMS_COMMAND_AARQ:
|
|
ret = svr_HandleAarqRequest(settings, data);
|
|
if (ret == 0 && settings->base.authentication < DLMS_AUTHENTICATION_HIGH)
|
|
{
|
|
settings->base.connected |= DLMS_CONNECTION_STATE_DLMS;
|
|
svr_connected(settings);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("HandleAarqRequest failed. ", ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
break;
|
|
case DLMS_COMMAND_RELEASE_REQUEST:
|
|
ret = svr_handleReleaseRequest(settings, data);
|
|
svr_disconnected(settings);
|
|
settings->base.connected &= ~DLMS_CONNECTION_STATE_DLMS;
|
|
break;
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
case DLMS_COMMAND_DISC:
|
|
ret = svr_generateDisconnectRequest(settings, data);
|
|
if (settings->base.connected != DLMS_CONNECTION_STATE_NONE)
|
|
{
|
|
svr_disconnected(settings);
|
|
settings->base.connected = DLMS_CONNECTION_STATE_NONE;
|
|
}
|
|
frame = DLMS_COMMAND_UA;
|
|
break;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
#ifndef DLMS_IGNORE_PLC
|
|
case DLMS_COMMAND_DISCOVER_REQUEST:
|
|
{
|
|
gxPlcRegister r;
|
|
svr_parseDiscoverRequest(data, &r);
|
|
unsigned char newMeter = settings->base.plcSettings.macSourceAddress == 0xFFE && settings->base.plcSettings.macDestinationAddress == 0xFFF;
|
|
return svr_discoverReport(&settings->base, &settings->base.plcSettings.systemTitle, newMeter, reply);
|
|
}
|
|
case DLMS_COMMAND_REGISTER_REQUEST:
|
|
svr_parseRegisterRequest(&settings->base, data);
|
|
return svr_discoverReport(&settings->base, &settings->base.plcSettings.systemTitle, 0, reply);
|
|
case DLMS_COMMAND_PING_REQUEST:
|
|
break;
|
|
#endif //DLMS_IGNORE_PLC
|
|
case DLMS_COMMAND_NONE:
|
|
//Get next frame.
|
|
data->position = 0;
|
|
frame = getNextSend(&settings->base, 0);
|
|
break;
|
|
default:
|
|
//Invalid command.
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Unknown command. ", cmd);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_COMMAND;
|
|
}
|
|
if (ret == DLMS_ERROR_CODE_INVALID_COMMAND)
|
|
{
|
|
bb_clear(data);
|
|
if (settings->base.useLogicalNameReferencing)
|
|
{
|
|
ret = svr_generateExceptionResponse(&settings->base,
|
|
DLMS_EXCEPTION_STATE_ERROR_SERVICE_NOT_ALLOWED,
|
|
DLMS_ERROR_CODE_INVALID_COMMAND,
|
|
data);
|
|
}
|
|
else
|
|
{
|
|
ret = svr_generateConfirmedServiceError(
|
|
settings,
|
|
DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR,
|
|
DLMS_SERVICE_ERROR_SERVICE, DLMS_SERVICE_UNSUPPORTED,
|
|
data);
|
|
}
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((settings->base.interfaceType == DLMS_INTERFACE_TYPE_HDLC ||
|
|
settings->base.interfaceType == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E) &&
|
|
bb_available(data) > settings->hdlc->maximumInfoLengthTransmit)
|
|
{
|
|
settings->info.moreData |= DLMS_DATA_REQUEST_TYPES_FRAME;
|
|
}
|
|
ret = dlms_addFrame(&settings->base, frame, data, reply);
|
|
if (cmd == DLMS_COMMAND_DISC ||
|
|
(settings->base.interfaceType == DLMS_INTERFACE_TYPE_WRAPPER && cmd == DLMS_COMMAND_RELEASE_REQUEST))
|
|
{
|
|
svr_reset(settings);
|
|
}
|
|
#ifdef DLMS_DEBUG
|
|
if (ret != 0)
|
|
{
|
|
svr_notifyTrace("svr_handleCommand", ret);
|
|
}
|
|
#endif //DLMS_DEBUG
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleRequest(
|
|
dlmsServerSettings* settings,
|
|
gxByteBuffer* data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
if (data == NULL || data->data == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
return svr_handleRequest2(settings, data->data, (uint16_t)bb_available(data), reply);
|
|
}
|
|
|
|
int svr_handleRequest3(
|
|
dlmsServerSettings* settings,
|
|
unsigned char data,
|
|
gxByteBuffer* reply)
|
|
{
|
|
return svr_handleRequest2(settings, &data, 1, reply);
|
|
}
|
|
|
|
int svr_handleRequest2(
|
|
dlmsServerSettings* settings,
|
|
unsigned char* buff,
|
|
uint16_t size,
|
|
gxByteBuffer* reply)
|
|
{
|
|
gxServerReply sr;
|
|
sr.data = buff;
|
|
sr.dataSize = size;
|
|
sr.reply = reply;
|
|
return svr_handleRequest4(settings, &sr);
|
|
}
|
|
|
|
/* Find IEC frame.Sometimes there are extra bytes or multiple packets on the data so they are removed.*/
|
|
int svr_getIecPacket(dlmsServerSettings* settings)
|
|
{
|
|
if (settings->receivedData.size < 5)
|
|
{
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
int ret;
|
|
int pos;
|
|
unsigned char ch;
|
|
int eop = -1;
|
|
int bop = -1;
|
|
//Find EOP.
|
|
for (pos = settings->receivedData.size - 2; pos != 2; --pos)
|
|
{
|
|
if ((ret = bb_getUInt8ByIndex(&settings->receivedData, pos, &ch) == 0) &&
|
|
ch == 0x0D &&
|
|
(ret = bb_getUInt8ByIndex(&settings->receivedData, pos + 1, &ch) == 0) &&
|
|
ch == 0x0A)
|
|
{
|
|
eop = pos;
|
|
break;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (eop == -1)
|
|
{
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
//Find BOP
|
|
for (pos = eop - 1; pos >= 0; --pos)
|
|
{
|
|
if ((ret = bb_getUInt8ByIndex(&settings->receivedData, pos, &ch) != 0))
|
|
{
|
|
break;
|
|
}
|
|
if (ch == 6 ||
|
|
(pos + 2 < (int)settings->receivedData.size &&
|
|
ch == '/' &&
|
|
(ret = bb_getUInt8ByIndex(&settings->receivedData, pos + 1, &ch)) == 0 &&
|
|
ch == (unsigned char)'?' &&
|
|
(ret = bb_getUInt8ByIndex(&settings->receivedData, pos + 2, &ch)) == 0 &&
|
|
ch == (unsigned char)'!'))
|
|
{
|
|
bop = pos;
|
|
break;
|
|
}
|
|
}
|
|
if (bop == -1)
|
|
{
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
settings->receivedData.position = bop;
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleRequest4(
|
|
dlmsServerSettings* settings,
|
|
gxServerReply* sr)
|
|
{
|
|
if (sr == NULL)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
int ret;
|
|
unsigned char first;
|
|
bb_clear(sr->reply);
|
|
if (sr->data == NULL || sr->dataSize == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
if (!settings->initialized)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Server not Initialized.", -1);
|
|
#endif //DLMS_DEBUG
|
|
//Server not Initialized.
|
|
return DLMS_ERROR_CODE_NOT_INITIALIZED;
|
|
}
|
|
//Check frame using inter Charachter Timeout.
|
|
#if !defined(DLMS_IGNORE_HDLC) || !defined(DLMS_IGNORE_IEC_HDLC_SETUP)
|
|
if (IS_HDLC(settings->base.interfaceType) && settings->hdlc != NULL && settings->hdlc->inactivityTimeout != 0)
|
|
{
|
|
uint32_t now = time_elapsed();
|
|
uint16_t elapsed = (uint16_t)(now - settings->frameReceived) / 1000;
|
|
//If frame shoud be fully received.
|
|
if (elapsed >= settings->hdlc->inactivityTimeout)
|
|
{
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
}
|
|
settings->frameReceived = now;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_HDLC) || !defined(DLMS_IGNORE_IEC_HDLC_SETUP)
|
|
if (bb_isAttached(&settings->receivedData) &&
|
|
settings->receivedData.size + sr->dataSize > bb_getCapacity(&settings->receivedData))
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("svr_handleRequest2 bb_isAttached failed. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
//Send U-Frame Frame Reject if we have received more data that can fit to one HDLC frame.
|
|
if (IS_HDLC(settings->base.interfaceType))
|
|
{
|
|
ret = dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_REJECTED, NULL, sr->reply);
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
reply_clear2(&settings->info, 1);
|
|
}
|
|
else
|
|
{
|
|
ret = 0;
|
|
}
|
|
#else
|
|
ret = 0;
|
|
#endif //DLMS_IGNORE_HDLC
|
|
return ret;
|
|
}
|
|
if (bb_set(&settings->receivedData, sr->data, sr->dataSize) != 0)
|
|
{
|
|
//If client is sending junk data.
|
|
bb_clear(&settings->receivedData);
|
|
return 0;
|
|
}
|
|
first = settings->base.serverAddress == 0
|
|
&& settings->base.clientAddress == 0;
|
|
#if !defined(DLMS_IGNORE_IEC) && !defined(DLMS_IGNORE_IEC_LOCAL_PORT_SETUP)
|
|
//If using optical probe.
|
|
if (settings->base.interfaceType == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E)
|
|
{
|
|
unsigned char ch;
|
|
if (settings->base.connected == DLMS_CONNECTION_STATE_NONE)
|
|
{
|
|
//If IEC packet not found or it's not fully received.
|
|
if (svr_getIecPacket(settings) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
if (bb_getUInt8ByIndex(&settings->receivedData, settings->receivedData.position, &ch) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
sr->newBaudRate = 0;
|
|
if (ch == 6)
|
|
{
|
|
//User changes the baud rate.
|
|
//Only Mode E is allowed.
|
|
if (bb_getUInt8ByIndex(&settings->receivedData, settings->receivedData.position + 1, &ch) != 0 ||
|
|
ch != 0x32 ||
|
|
bb_getUInt8ByIndex(&settings->receivedData, settings->receivedData.position + 3, &ch) != 0 ||
|
|
ch != 0x32)
|
|
{
|
|
return 0;
|
|
}
|
|
if (bb_getUInt8ByIndex(&settings->receivedData, settings->receivedData.position + 2, &ch) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
DLMS_BAUD_RATE baudrate = (DLMS_BAUD_RATE)ch - '0';
|
|
if (baudrate > settings->localPortSetup->proposedBaudrate)
|
|
{
|
|
baudrate = settings->localPortSetup->proposedBaudrate;
|
|
}
|
|
bb_clear(&settings->receivedData);
|
|
//Return used baud rate.
|
|
settings->base.connected = DLMS_CONNECTION_STATE_IEC;
|
|
//"2" //(HDLC protocol procedure) (Binary mode)
|
|
//Set mode E.
|
|
unsigned char tmp[] = { 0x06,
|
|
//"2" HDLC protocol procedure (Mode E)
|
|
(unsigned char)'2',
|
|
//Send Baud rate character
|
|
(unsigned char)('0' + baudrate),
|
|
//Mode control character
|
|
(unsigned char)'2', 13, 10 };
|
|
if ((ret = bb_set(sr->reply, tmp, sizeof(tmp))) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
//Change the baud rate.
|
|
sr->newBaudRate = 300 << (uint16_t)baudrate;
|
|
}
|
|
else if (bb_getUInt8ByIndex(&settings->receivedData, settings->receivedData.position, &ch) == 0 &&
|
|
ch == '/')
|
|
{
|
|
gxByteBuffer meterAddress;
|
|
bb_attach(&meterAddress, settings->receivedData.data, settings->receivedData.position + 3, bb_available(&settings->receivedData) - 5);
|
|
//If meter address is wrong.
|
|
if (bb_size(&meterAddress) != 0 && bb_compare(&settings->localPortSetup->deviceAddress, meterAddress.data, meterAddress.size) != 0)
|
|
{
|
|
bb_clear(&settings->receivedData);
|
|
return 0;
|
|
}
|
|
bb_clear(&settings->receivedData);
|
|
bb_clear(sr->reply);
|
|
if (bb_setUInt8(sr->reply, '/') != 0 ||
|
|
//Add flag ID.
|
|
bb_set(sr->reply, settings->flagId, 3) != 0 ||
|
|
//Add proposed baud rate.
|
|
bb_setUInt8(sr->reply, '0' + settings->localPortSetup->proposedBaudrate) != 0 ||
|
|
//Add device address.
|
|
bb_set(sr->reply, settings->localPortSetup->deviceAddress.data, settings->localPortSetup->deviceAddress.size) != 0 ||
|
|
bb_setUInt8(sr->reply, '\r') != 0 ||
|
|
bb_setUInt8(sr->reply, '\n') != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
#endif// !defined(DLMS_IGNORE_IEC) && !defined(DLMS_IGNORE_IEC_LOCAL_PORT_SETUP)
|
|
if ((ret = dlms_getData2(&settings->base, &settings->receivedData, &settings->info, first)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("svr_handleRequest2 dlms_getData2 failed. ", ret);
|
|
#endif //DLMS_DEBUG
|
|
if (ret == DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL ||
|
|
ret == DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR ||
|
|
ret == DLMS_ERROR_CODE_INVALID_SECURITY_SUITE)
|
|
{
|
|
bb_clear(sr->reply);
|
|
gxByteBuffer data;
|
|
unsigned char tmp[10];
|
|
bb_attach(&data, tmp, 0, sizeof(tmp));
|
|
if ((ret = svr_generateConfirmedServiceError(
|
|
settings,
|
|
DLMS_CONFIRMED_SERVICE_ERROR_INITIATE_ERROR,
|
|
DLMS_SERVICE_ERROR_APPLICATION_REFERENCE,
|
|
DLMS_APPLICATION_REFERENCE_DECIPHERING_ERROR,
|
|
&data)) != 0)
|
|
{
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
return ret;
|
|
}
|
|
return dlms_addFrame(&settings->base, 0, &data, sr->reply);
|
|
}
|
|
else if (ret == DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Invalid server address. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
return 0;
|
|
}
|
|
else if (ret == DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Invalid client address. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
if (settings->info.preEstablished)
|
|
{
|
|
svr_disconnected(settings);
|
|
svr_reset(settings);
|
|
first = 1;
|
|
settings->receivedData.position = 0;
|
|
if ((ret = dlms_getData2(&settings->base, &settings->receivedData, &settings->info, first)) != 0)
|
|
{
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
return ret;
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
else if (IS_HDLC(settings->base.interfaceType) &&
|
|
(settings->base.connected & DLMS_CONNECTION_STATE_HDLC) != 0)
|
|
{
|
|
ret = dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_REJECTED, NULL, sr->reply);
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
reply_clear2(&settings->info, 1);
|
|
return 0;
|
|
}
|
|
else if (ret == DLMS_ERROR_CODE_INVALID_FRAME_NUMBER)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Invalid frame number. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if ((settings->base.connected & DLMS_CONNECTION_STATE_HDLC) != 0)
|
|
{
|
|
settings->dataReceived = time_elapsed();
|
|
ret = dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_REJECTED, NULL, sr->reply);
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
reply_clear2(&settings->info, 1);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (ret != DLMS_ERROR_CODE_WRONG_CRC && IS_HDLC(settings->base.interfaceType) &&
|
|
(settings->base.connected & DLMS_CONNECTION_STATE_HDLC) != 0)
|
|
{
|
|
ret = dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_REJECTED, NULL, sr->reply);
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
reply_clear2(&settings->info, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
// If all data is not received yet.
|
|
if (!settings->info.complete)
|
|
{
|
|
return 0;
|
|
}
|
|
bb_clear(&settings->receivedData);
|
|
if (settings->info.command == DLMS_COMMAND_DISC && (settings->base.connected & DLMS_CONNECTION_STATE_HDLC) == 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Disconnecting from the meter. ", -1);
|
|
#endif //DLMS_DEBUG
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
ret = dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_DISCONNECT_MODE, NULL, sr->reply);
|
|
#endif //DLMS_IGNORE_HDLC
|
|
reply_clear2(&settings->info, 1);
|
|
return ret;
|
|
}
|
|
|
|
if ((first || settings->info.command == DLMS_COMMAND_SNRM ||
|
|
(settings->base.interfaceType == DLMS_INTERFACE_TYPE_WRAPPER && settings->info.command == DLMS_COMMAND_AARQ)) &&
|
|
settings->base.interfaceType != DLMS_INTERFACE_TYPE_PDU)
|
|
{
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (IS_HDLC(settings->base.interfaceType) && settings->info.preEstablished)
|
|
{
|
|
svr_disconnected(settings);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
// Check is data send to this server.
|
|
if (settings->base.interfaceType == DLMS_INTERFACE_TYPE_WRAPPER && settings->info.command == DLMS_COMMAND_AARQ)
|
|
{
|
|
if (!svr_isTarget(&settings->base, settings->base.serverAddress, settings->base.clientAddress))
|
|
{
|
|
if ((settings->base.connected & DLMS_CONNECTION_STATE_DLMS) == 0)
|
|
{
|
|
settings->base.serverAddress = settings->base.clientAddress = 0;
|
|
}
|
|
reply_clear2(&settings->info, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (IS_HDLC(settings->base.interfaceType))
|
|
{
|
|
settings->base.connected |= DLMS_CONNECTION_STATE_HDLC;
|
|
svr_connected(settings);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
}
|
|
// If client want next frame.
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if ((settings->info.moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == DLMS_DATA_REQUEST_TYPES_FRAME)
|
|
{
|
|
settings->dataReceived = time_elapsed();
|
|
return dlms_getHdlcFrame(&settings->base, getReceiverReady(&settings->base), NULL, sr->reply);
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC
|
|
// Update command if transaction and next frame is asked.
|
|
if (settings->info.command == DLMS_COMMAND_NONE)
|
|
{
|
|
if (settings->transaction.command != DLMS_COMMAND_NONE)
|
|
{
|
|
//If client wants next PDU.
|
|
if (settings->info.data.size == 0)
|
|
{
|
|
settings->info.command = settings->transaction.command;
|
|
}
|
|
else
|
|
{
|
|
settings->info.data.position = 0;
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
//Return rest of frame.
|
|
return dlms_getHdlcFrame(&settings->base, getNextSend(&settings->base, 0), &settings->info.data, sr->reply);
|
|
#endif //DLMS_IGNORE_HDLC
|
|
}
|
|
}
|
|
else if (settings->info.data.size == 0)
|
|
{
|
|
settings->dataReceived = time_elapsed();
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
return dlms_getHdlcFrame(&settings->base, getKeepAlive(&settings->base), NULL, sr->reply);
|
|
#endif //DLMS_IGNORE_HDLC
|
|
}
|
|
}
|
|
//Check inactivity timeout.
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
#ifndef DLMS_IGNORE_IEC_HDLC_SETUP
|
|
if (IS_HDLC(settings->base.interfaceType) &&
|
|
settings->hdlc != NULL && settings->hdlc->inactivityTimeout != 0)
|
|
{
|
|
if (settings->info.command != DLMS_COMMAND_SNRM)
|
|
{
|
|
uint32_t elapsed = time_elapsed() - settings->dataReceived;
|
|
elapsed /= 1000;
|
|
//If inactivity time out is elapsed.
|
|
if (elapsed >= settings->hdlc->inactivityTimeout)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Inactivity timeout. ", 0);
|
|
#endif //DLMS_DEBUG
|
|
if (!settings->info.preEstablished)
|
|
{
|
|
if (settings->info.command == DLMS_COMMAND_DISC)
|
|
{
|
|
dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_DISCONNECT_MODE, NULL, sr->reply);
|
|
}
|
|
svr_disconnected(settings);
|
|
svr_reset(settings);
|
|
return 0;
|
|
}
|
|
if ((settings->base.connected & DLMS_CONNECTION_STATE_HDLC) != 0)
|
|
{
|
|
svr_disconnected(settings);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // DLMS_IGNORE_IEC_HDLC_SETUP
|
|
#endif //DLMS_IGNORE_HDLC
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
#ifndef DLMS_IGNORE_TCP_UDP_SETUP
|
|
if (settings->base.interfaceType == DLMS_INTERFACE_TYPE_WRAPPER &&
|
|
settings->wrapper != NULL && settings->wrapper->inactivityTimeout != 0)
|
|
{
|
|
if (settings->info.command != DLMS_COMMAND_AARQ)
|
|
{
|
|
uint32_t elapsed = time_elapsed() - settings->dataReceived;
|
|
elapsed /= 1000;
|
|
//If inactivity time out is elapsed.
|
|
if (elapsed >= settings->wrapper->inactivityTimeout)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace("Inactivity timeout. ", 0);
|
|
#endif //DLMS_DEBUG
|
|
if (!settings->info.preEstablished)
|
|
{
|
|
svr_disconnected(settings);
|
|
svr_reset(settings);
|
|
return 0;
|
|
}
|
|
if ((settings->base.connected & DLMS_CONNECTION_STATE_HDLC) != 0)
|
|
{
|
|
svr_disconnected(settings);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // DLMS_IGNORE_TCP_UDP_SETUP
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
ret = svr_handleCommand(settings, settings->info.command, &settings->info.data, sr->reply);
|
|
if (ret != 0)
|
|
{
|
|
bb_clear(sr->reply);
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
if (IS_HDLC(settings->base.interfaceType))
|
|
{
|
|
if (ret == DLMS_ERROR_CODE_REJECTED)
|
|
{
|
|
ret = dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_DISCONNECT_MODE, NULL, sr->reply);
|
|
}
|
|
else
|
|
{
|
|
ret = dlms_getHdlcFrame(&settings->base, DLMS_COMMAND_REJECTED, NULL, sr->reply);
|
|
}
|
|
settings->receivedData.position = settings->receivedData.size = 0;
|
|
}
|
|
else
|
|
#endif //DLMS_IGNORE_HDLC
|
|
{
|
|
ret = svr_reportError(settings, settings->info.command, DLMS_ERROR_CODE_OTHER_REASON, sr->reply);
|
|
}
|
|
}
|
|
DLMS_COMMAND cmd = settings->info.command;
|
|
DLMS_DATA_REQUEST_TYPES moreData = settings->info.moreData;
|
|
reply_clear2(&settings->info, 0);
|
|
// Save command if there is more data available.
|
|
// This is needed when Windows size is bigger than one.
|
|
if ((moreData & DLMS_DATA_REQUEST_TYPES_FRAME) != 0)
|
|
{
|
|
settings->transaction.command = cmd;
|
|
}
|
|
settings->dataReceived = time_elapsed();
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleInactivityTimeout(
|
|
dlmsServerSettings* settings,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
//If connection is established.
|
|
if (settings->info.preEstablished || (settings->base.connected & DLMS_CONNECTION_STATE_DLMS) != 0)
|
|
{
|
|
//Check inactivity timeout.
|
|
unsigned char inactivity = 0;
|
|
//Delay in seconds from last message.
|
|
uint32_t elapsed = (time_elapsed() - settings->dataReceived);
|
|
elapsed /= 1000;
|
|
uint32_t timeout = 0xFFFFFFFF;
|
|
#ifndef DLMS_IGNORE_HDLC
|
|
#ifndef DLMS_IGNORE_IEC_HDLC_SETUP
|
|
if (IS_HDLC(settings->base.interfaceType) &&
|
|
settings->hdlc != NULL && settings->hdlc->inactivityTimeout != 0)
|
|
{
|
|
inactivity = !(elapsed < settings->hdlc->inactivityTimeout);
|
|
timeout = settings->hdlc->inactivityTimeout - elapsed;
|
|
}
|
|
#endif // DLMS_IGNORE_IEC_HDLC_SETUP
|
|
#endif //DLMS_IGNORE_HDLC
|
|
#ifndef DLMS_IGNORE_WRAPPER
|
|
#ifndef DLMS_IGNORE_TCP_UDP_SETUP
|
|
if (settings->base.interfaceType == DLMS_INTERFACE_TYPE_WRAPPER &&
|
|
settings->wrapper != NULL && settings->wrapper->inactivityTimeout != 0)
|
|
{
|
|
inactivity = !(elapsed < settings->wrapper->inactivityTimeout);
|
|
timeout = settings->wrapper->inactivityTimeout - elapsed;
|
|
}
|
|
#endif // DLMS_IGNORE_TCP_UDP_SETUP
|
|
#endif //DLMS_IGNORE_WRAPPER
|
|
//If inactivity timeout is elapsed.
|
|
if (inactivity)
|
|
{
|
|
svr_disconnected(settings);
|
|
svr_reset(settings);
|
|
}
|
|
else if (timeout != 0xFFFFFFFF)
|
|
{
|
|
if (*next > time + timeout)
|
|
{
|
|
*next = time + timeout;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int svr_invoke(
|
|
dlmsServerSettings* settings,
|
|
unsigned char isAction,
|
|
gxObject* target,
|
|
unsigned char index,
|
|
dlmsVARIANT* value,
|
|
uint32_t time,
|
|
gxtime* start,
|
|
gxtime* end,
|
|
uint32_t* executed,
|
|
uint32_t* next)
|
|
{
|
|
unsigned char exec = start == NULL;
|
|
int ret = 0;
|
|
if (!exec)
|
|
{
|
|
//Execute in exact time if end time is not given.
|
|
if (end == NULL)
|
|
{
|
|
//Ignore deviation and status for single action script.
|
|
exec = *executed < time && time_compareWithDiff(start, time, 0) == 0;
|
|
}
|
|
else if (*executed < time)
|
|
{
|
|
exec = time_compare2(start, time) != 1 && time_compare2(end, time) != -1;
|
|
}
|
|
}
|
|
if (exec)
|
|
{
|
|
*executed = time;
|
|
gxValueEventCollection args;
|
|
gxValueEventArg* e;
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
gxValueEventArg tmp[1];
|
|
ve_init(&tmp[0]);
|
|
vec_attach(&args, tmp, 1, 1);
|
|
e = &tmp[0];
|
|
#else
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_init(&args);
|
|
vec_push(&args, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
e->target = target;
|
|
e->index = index;
|
|
if (value != NULL)
|
|
{
|
|
e->value = *value;
|
|
}
|
|
if (isAction)
|
|
{
|
|
svr_preAction(&settings->base, &args);
|
|
if (!e->handled)
|
|
{
|
|
ret = cosem_invoke(settings, e);
|
|
svr_postAction(&settings->base, &args);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
svr_preWrite(&settings->base, &args);
|
|
if (!e->handled)
|
|
{
|
|
ret = cosem_setValue(&settings->base, e);
|
|
svr_postWrite(&settings->base, &args);
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&args);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
//Increase time by one second so next scheduled date is retreaved.
|
|
++time;
|
|
}
|
|
if (start != NULL)
|
|
{
|
|
uint32_t tmp = time_getNextScheduledDate(time, start);
|
|
if (tmp < *next)
|
|
{
|
|
*next = tmp;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_PROFILE_GENERIC
|
|
|
|
int svr_handleProfileGeneric(
|
|
dlmsServerSettings* settings,
|
|
gxProfileGeneric* object,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
int ret = 0;
|
|
if (object->capturePeriod != 0)
|
|
{
|
|
//Get seconds.
|
|
uint32_t tm = time % 60L;
|
|
//Time where seconds part is zero.
|
|
uint32_t tm2 = time - tm;
|
|
tm = time % object->capturePeriod;
|
|
if (tm == 0)
|
|
{
|
|
if (*next == (uint32_t)-1 || *next > time + object->capturePeriod)
|
|
{
|
|
*next = time + object->capturePeriod;
|
|
}
|
|
ret = svr_invoke(
|
|
settings,
|
|
1,
|
|
(gxObject*)object,
|
|
2,
|
|
NULL,
|
|
time,
|
|
NULL,
|
|
NULL,
|
|
&object->executedTime,
|
|
next);
|
|
}
|
|
else if (tm2 + object->capturePeriod < *next)
|
|
{
|
|
tm = time - tm2;
|
|
tm %= object->capturePeriod;
|
|
uint32_t offset = object->capturePeriod - tm;
|
|
if (time + offset < *next)
|
|
{
|
|
*next = time + offset;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_PROFILE_GENERIC
|
|
|
|
|
|
#if !defined(DLMS_IGNORE_ACTION_SCHEDULE) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
|
|
int svr_handleSingleActionSchedule(
|
|
dlmsServerSettings* settings,
|
|
gxActionSchedule* object,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
//Execution time is saved in case there are multiple actions in one schedule.
|
|
//If it's not returned only the first action is executed.
|
|
uint32_t originalExecutedTime = object->executedTime;
|
|
gxtime* s;
|
|
int ret = 0;
|
|
int pos;
|
|
for (pos = 0; pos != object->executionTime.size; ++pos)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if ((ret = arr_getByIndex(&object->executionTime, pos, (void**)&s)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#else
|
|
if ((ret = arr_getByIndex(&object->executionTime, pos, (void**)&s, sizeof(gxtime))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (object->executedScript != NULL)
|
|
{
|
|
gxScript* it;
|
|
gxScriptAction* a;
|
|
int posS;
|
|
if (settings->defaultClock != NULL)
|
|
{
|
|
s->deviation = settings->defaultClock->timeZone;
|
|
s->status = settings->defaultClock->status;
|
|
}
|
|
for (posS = 0; posS != object->executedScript->scripts.size; ++posS)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if ((ret = arr_getByIndex(&object->executedScript->scripts, posS, (void**)&it, sizeof(gxScript))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#else
|
|
if ((ret = arr_getByIndex(&object->executedScript->scripts, posS, (void**)&it)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (it->id == object->executedScriptSelector)
|
|
{
|
|
int posA;
|
|
for (posA = 0; posA != it->actions.size; ++posA)
|
|
{
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if ((ret = arr_getByIndex(&it->actions, posA, (void**)&a, sizeof(gxScriptAction))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#else
|
|
if ((ret = arr_getByIndex(&it->actions, posA, (void**)&a)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
//Execution time is returned in case there are multiple actions in one schedule.
|
|
object->executedTime = originalExecutedTime;
|
|
if ((ret = svr_invoke(
|
|
settings,
|
|
a->type == DLMS_SCRIPT_ACTION_TYPE_EXECUTE,
|
|
(gxObject*)a->target,
|
|
a->index,
|
|
&a->parameter,
|
|
time,
|
|
s,
|
|
NULL,
|
|
&object->executedTime,
|
|
next)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ACTION_SCHEDULE) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
|
|
#if !defined(DLMS_IGNORE_ACTIVITY_CALENDAR) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
|
|
unsigned char equal(gxByteBuffer* a, gxByteBuffer* b)
|
|
{
|
|
if (a->size == b->size)
|
|
{
|
|
return memcmp(a->data, b->data, a->size) == 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int svr_invokeScript(
|
|
dlmsServerSettings* settings,
|
|
gxArray* dayProfiles,
|
|
uint16_t id,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
uint16_t pos, pos2;
|
|
int ret = 0;
|
|
gxDayProfile* dp;
|
|
gxDayProfileAction* da;
|
|
gxValueEventCollection args;
|
|
gxValueEventArg* e;
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
gxValueEventArg tmp[1];
|
|
ve_init(&tmp[0]);
|
|
vec_attach(&args, tmp, 1, 1);
|
|
e = &tmp[0];
|
|
#else
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_init(&args);
|
|
vec_push(&args, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
for (pos = 0; pos != dayProfiles->size; ++pos)
|
|
{
|
|
if ((ret = arr_getByIndex2(dayProfiles, pos, (void**)&dp, sizeof(gxDayProfile))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
for (pos2 = 0; pos2 != dp->daySchedules.size; ++pos2)
|
|
{
|
|
if ((ret = arr_getByIndex2(&dp->daySchedules, pos2, (void**)&da, sizeof(gxDayProfileAction))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (dp->dayId == id && time_compare2(&da->startTime, time) == 0)
|
|
{
|
|
e->target = da->script;
|
|
e->index = 1;
|
|
if ((ret = var_setInt8(&e->parameters, (unsigned char)da->scriptSelector)) != 0 ||
|
|
(ret = invoke_ScriptTable(settings, e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//Update next execution time.
|
|
uint32_t tmp = time_getNextScheduledDate(time, &da->startTime);
|
|
if (tmp < *next)
|
|
{
|
|
*next = tmp;
|
|
}
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&args);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
int svr_handleActivityCalendar(
|
|
dlmsServerSettings* settings,
|
|
gxActivityCalendar* object,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
gxSeasonProfile* sp;
|
|
gxWeekProfile* wp;
|
|
int pos, ret = 0;
|
|
uint16_t pos2;
|
|
gxtime tm;
|
|
gxObject* obj;
|
|
gxSpecialDay* sd;
|
|
//Check activate passive calendar time.
|
|
//The activate passive calendar time is never execute if all the values are ignored.
|
|
if ((object->time.skip & (DATETIME_SKIPS_YEAR | DATETIME_SKIPS_MONTH | DATETIME_SKIPS_DAY | DATETIME_SKIPS_HOUR | DATETIME_SKIPS_MINUTE | DATETIME_SKIPS_SECOND)) !=
|
|
(DATETIME_SKIPS_YEAR | DATETIME_SKIPS_MONTH | DATETIME_SKIPS_DAY | DATETIME_SKIPS_HOUR | DATETIME_SKIPS_MINUTE | DATETIME_SKIPS_SECOND))
|
|
{
|
|
if (time_compareWithDiff(&object->time, time, 0) == 0)
|
|
{
|
|
//Executed time is not needed.
|
|
uint32_t executed;
|
|
if ((ret = svr_invoke(
|
|
settings,
|
|
1,
|
|
(gxObject*)object,
|
|
1,
|
|
NULL,
|
|
time,
|
|
NULL,
|
|
NULL,
|
|
&executed,
|
|
next)) != 0)
|
|
{
|
|
//Save inforation that invoke failed.
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check that today is not a special day.
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_SPECIAL_DAYS_TABLE)
|
|
{
|
|
for (pos2 = 0; pos2 != ((gxSpecialDaysTable*)obj)->entries.size; ++pos2)
|
|
{
|
|
if ((ret = arr_getByIndex2(&((gxSpecialDaysTable*)obj)->entries,
|
|
pos2, (void**)&sd, sizeof(gxSpecialDay))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (time_compare2(&sd->date, time) == 0)
|
|
{
|
|
//Invoke day profile
|
|
if ((ret = svr_invokeScript(settings,
|
|
&object->dayProfileTableActive, sd->dayId, time, next)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (object->seasonProfileActive.size != 0)
|
|
{
|
|
uint16_t activeSeason = object->seasonProfileActive.size - 1;
|
|
gxtime* start = NULL;
|
|
gxtime* end = NULL;
|
|
//Find active season.
|
|
for (pos = 0; pos != object->seasonProfileActive.size; ++pos)
|
|
{
|
|
if ((ret = arr_getByIndex2(&object->seasonProfileActive,
|
|
pos, (void**)&sp, sizeof(gxSeasonProfile))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
//The season is never start if all the values are ignored.
|
|
if ((sp->start.skip & (DATETIME_SKIPS_YEAR | DATETIME_SKIPS_MONTH | DATETIME_SKIPS_DAY | DATETIME_SKIPS_HOUR | DATETIME_SKIPS_MINUTE | DATETIME_SKIPS_SECOND)) ==
|
|
(DATETIME_SKIPS_YEAR | DATETIME_SKIPS_MONTH | DATETIME_SKIPS_DAY | DATETIME_SKIPS_HOUR | DATETIME_SKIPS_MINUTE | DATETIME_SKIPS_SECOND))
|
|
{
|
|
continue;
|
|
}
|
|
//In season_start, wildcards are allowed. If all fields are wildcards, the season will never start.
|
|
tm = sp->start;
|
|
tm.deviation = 0x8000;
|
|
tm.skip |= DATETIME_SKIPS_SECOND | DATETIME_SKIPS_MINUTE | DATETIME_SKIPS_HOUR | DATETIME_SKIPS_MS;
|
|
if (time_compare2(&tm, time) != 1)
|
|
{
|
|
start = &sp->start;
|
|
//Reset end time.
|
|
end = NULL;
|
|
activeSeason = pos;
|
|
}
|
|
else if (start != NULL && end == NULL)
|
|
{
|
|
end = &sp->start;
|
|
}
|
|
}
|
|
if ((ret = arr_getByIndex2(&object->seasonProfileActive,
|
|
activeSeason, (void**)&sp, sizeof(gxSeasonProfile))) == 0)
|
|
{
|
|
for (pos2 = 0; pos2 != object->weekProfileTableActive.size; ++pos2)
|
|
{
|
|
if ((ret = arr_getByIndex2(&object->weekProfileTableActive,
|
|
pos2, (void**)&wp, sizeof(gxWeekProfile))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
//If week name matches.
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
if (memcmp(&wp->name, &sp->weekName, sizeof(gxWeekProfileName)) == 0)
|
|
#else
|
|
if (equal(&wp->name, &sp->weekName))
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
{
|
|
unsigned char dayId;
|
|
switch (time_dayOfWeek(time_getYears2(time), time_getMonths2(time), time_getDays2(time)))
|
|
{
|
|
case 7:
|
|
dayId = wp->sunday;
|
|
break;
|
|
case 1:
|
|
dayId = wp->monday;
|
|
break;
|
|
case 2:
|
|
dayId = wp->tuesday;
|
|
break;
|
|
case 3:
|
|
dayId = wp->wednesday;
|
|
break;
|
|
case 4:
|
|
dayId = wp->thursday;
|
|
break;
|
|
case 5:
|
|
dayId = wp->friday;
|
|
break;
|
|
case 6:
|
|
dayId = wp->saturday;
|
|
break;
|
|
default:
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
ret = svr_invokeScript(settings, &object->dayProfileTableActive,
|
|
dayId, time, next);
|
|
//If week name matches.
|
|
break;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ACTIVITY_CALENDAR) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
|
|
#ifndef DLMS_IGNORE_PUSH_SETUP
|
|
|
|
int svr_handlePushSetup(
|
|
dlmsServerSettings* settings,
|
|
gxPushSetup* object,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
gxtime* s, * e;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
gxKey* k;
|
|
#else
|
|
gxTimePair* k;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
int ret = 0;
|
|
int pos;
|
|
for (pos = 0; pos != object->communicationWindow.size; ++pos)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if ((ret = arr_getByIndex(&object->communicationWindow, pos, (void**)&k)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
s = (gxtime*)k->key;
|
|
e = (gxtime*)k->value;
|
|
#else
|
|
if ((ret = arr_getByIndex(&object->communicationWindow, pos, (void**)&k, sizeof(gxTimePair))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
s = &k->first;
|
|
e = &k->second;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if ((ret = svr_invoke(
|
|
settings,
|
|
1,
|
|
(gxObject*)object,
|
|
1,
|
|
NULL,
|
|
time,
|
|
s,
|
|
e,
|
|
&object->executedTime,
|
|
next)) != 0)
|
|
{
|
|
//Save infor that invoke failed.
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_PUSH_SETUP
|
|
|
|
#ifndef DLMS_IGNORE_AUTO_CONNECT
|
|
int svr_handleAutoConnect(
|
|
dlmsServerSettings* settings,
|
|
gxAutoConnect* object,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
gxtime* s, * e;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
gxKey* k;
|
|
#else
|
|
gxTimePair* k;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
int ret = 0;
|
|
int pos;
|
|
for (pos = 0; pos != object->callingWindow.size; ++pos)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if ((ret = arr_getByIndex(&object->callingWindow, pos, (void**)&k)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
s = (gxtime*)k->key;
|
|
e = (gxtime*)k->value;
|
|
#else
|
|
if ((ret = arr_getByIndex(&object->callingWindow, pos, (void**)&k, sizeof(gxTimePair))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
s = &k->first;
|
|
e = &k->second;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if ((ret = svr_invoke(
|
|
settings,
|
|
1,
|
|
(gxObject*)object,
|
|
1,
|
|
NULL,
|
|
time,
|
|
s,
|
|
e,
|
|
&object->executedTime,
|
|
next)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_AUTO_CONNECT
|
|
|
|
int svr_run(
|
|
dlmsServerSettings* settings,
|
|
uint32_t time,
|
|
uint32_t* next)
|
|
{
|
|
uint16_t pos;
|
|
int ret = 0;
|
|
gxObject* obj;
|
|
*next = (uint32_t)-1;
|
|
#ifndef DLMS_IGNORE_PROFILE_GENERIC
|
|
//profile Generic objects.
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_PROFILE_GENERIC)
|
|
{
|
|
svr_handleProfileGeneric(settings, (gxProfileGeneric*)obj, time, next);
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_PROFILE_GENERIC
|
|
|
|
#if !defined(DLMS_IGNORE_ACTION_SCHEDULE) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
//Single action schedules.
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_ACTION_SCHEDULE)
|
|
{
|
|
svr_handleSingleActionSchedule(settings, (gxActionSchedule*)obj, time, next);
|
|
}
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ACTION_SCHEDULE) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
|
|
#if !defined(DLMS_IGNORE_ACTIVITY_CALENDAR) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
//Single activity calendars.
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_ACTIVITY_CALENDAR)
|
|
{
|
|
svr_handleActivityCalendar(settings, (gxActivityCalendar*)obj, time, next);
|
|
}
|
|
}
|
|
#endif //!defined(DLMS_IGNORE_ACTIVITY_CALENDAR) && !defined(DLMS_IGNORE_OBJECT_POINTERS)
|
|
|
|
#ifndef DLMS_IGNORE_PUSH_SETUP
|
|
//Push objects.
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_PUSH_SETUP)
|
|
{
|
|
svr_handlePushSetup(settings, (gxPushSetup*)obj, time, next);
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_PUSH_SETUP
|
|
#ifndef DLMS_IGNORE_AUTO_CONNECT
|
|
//Get auto connect objects.
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_AUTO_CONNECT)
|
|
{
|
|
//Handle calling window execution times. Ignore errors.
|
|
svr_handleAutoConnect(settings, (gxAutoConnect*)obj, time, next);
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_AUTO_CONNECT
|
|
return svr_handleInactivityTimeout(settings, time, next);
|
|
}
|
|
|
|
uint32_t svr_isChangedWithAction(DLMS_OBJECT_TYPE objectType, unsigned char methodIndex)
|
|
{
|
|
uint32_t ret = 0;
|
|
switch (objectType)
|
|
{
|
|
case DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME:
|
|
switch (methodIndex)
|
|
{
|
|
case 2:
|
|
//Secret.
|
|
ret = GET_ATTRIBUTE(7);
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
//Object.
|
|
ret = GET_ATTRIBUTE(2);
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
//User.
|
|
ret = GET_ATTRIBUTE(10);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case DLMS_OBJECT_TYPE_SECURITY_SETUP:
|
|
switch (methodIndex)
|
|
{
|
|
case 1:
|
|
//Activate Security policy.
|
|
ret = GET_ATTRIBUTE(2);
|
|
break;
|
|
case 2:
|
|
//Key transfer. TODO: Check this when SS 1 is released.
|
|
ret = GET_ATTRIBUTE(7);
|
|
break;
|
|
case 4:
|
|
//Generate key pair. TODO: Check this when SS 1 is released.
|
|
ret = GET_ATTRIBUTE(2);
|
|
break;
|
|
case 6:
|
|
case 8:
|
|
//Import or export certificate.
|
|
ret = GET_ATTRIBUTE(6);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
//SAP assignment is added or removed.
|
|
case DLMS_OBJECT_TYPE_SAP_ASSIGNMENT:
|
|
ret = GET_ATTRIBUTE(2);
|
|
break;
|
|
//Connection state is changed.
|
|
case DLMS_OBJECT_TYPE_DISCONNECT_CONTROL:
|
|
//Register activation is changed.
|
|
ret = GET_ATTRIBUTE(2, 3);
|
|
break;
|
|
case DLMS_OBJECT_TYPE_REGISTER_ACTIVATION:
|
|
if (methodIndex == 1)
|
|
{
|
|
//Register is assigned.
|
|
ret = GET_ATTRIBUTE(2);
|
|
}
|
|
else
|
|
{
|
|
//Mask is added.
|
|
ret = GET_ATTRIBUTE(3);
|
|
}
|
|
break;
|
|
case DLMS_OBJECT_TYPE_SPECIAL_DAYS_TABLE:
|
|
ret = GET_ATTRIBUTE(2);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_REGISTER_MONITOR
|
|
int svr_monitor(dlmsServerSettings* settings, gxRegisterMonitor* object)
|
|
{
|
|
int ret;
|
|
unsigned char pos;
|
|
gxValueEventCollection args;
|
|
gxValueEventArg* e;
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
gxValueEventArg tmp[1];
|
|
ve_init(&tmp[0]);
|
|
vec_attach(&args, tmp, 1, 1);
|
|
e = &tmp[0];
|
|
#else
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_init(&args);
|
|
vec_push(&args, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
e->target = object->monitoredValue.target;
|
|
e->index = object->monitoredValue.attributeIndex;
|
|
e->action = 1;
|
|
svr_preRead(&settings->base, &args);
|
|
ret = e->error;
|
|
if (!e->handled && ret == 0)
|
|
{
|
|
if ((ret = cosem_getValue(&settings->base, e)) == 0)
|
|
{
|
|
svr_postRead(&settings->base, &args);
|
|
ret = e->error;
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
//Save value.
|
|
dlmsVARIANT value = e->value;
|
|
dlmsVARIANT* threshold;
|
|
dlmsVARIANT* lastValue;
|
|
unsigned char buff[1];
|
|
gxByteBuffer bb;
|
|
bb_attach(&bb, buff, 0, 1);
|
|
e->parameters.vt = DLMS_DATA_TYPE_OCTET_STRING;
|
|
e->parameters.byteArr = &bb;
|
|
gxActionSet* act;
|
|
e->index = 1;
|
|
unsigned char index = 0, cnt = (unsigned char)object->thresholds.size;
|
|
if (object->actions.size < cnt)
|
|
{
|
|
cnt = (unsigned char)object->actions.size;
|
|
}
|
|
double currentValue = var_toDouble(&e->value);
|
|
for (pos = 0; pos != cnt; ++pos)
|
|
{
|
|
e->target = NULL;
|
|
if ((ret = va_getByIndex(&object->thresholds, pos, &threshold)) != 0 ||
|
|
(ret = va_getByIndex(&object->lastValues, pos, &lastValue)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
double thresholdsValue = var_toDouble(threshold);
|
|
//If value is down.
|
|
if (currentValue < thresholdsValue)
|
|
{
|
|
if (lastValue->vt == DLMS_DATA_TYPE_NONE || var_toDouble(lastValue) > thresholdsValue)
|
|
{
|
|
if ((ret = arr_getByIndex2(&object->actions, pos, (void**)&act, sizeof(gxActionSet))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
e->target = &act->actionDown.script->base;
|
|
index = (unsigned char)act->actionDown.scriptSelector;
|
|
ret = var_copy(lastValue, &value);
|
|
}
|
|
}
|
|
//If value is up.
|
|
else if (currentValue > thresholdsValue)
|
|
{
|
|
if (lastValue->vt == DLMS_DATA_TYPE_NONE || var_toDouble(lastValue) < thresholdsValue)
|
|
{
|
|
if ((ret = arr_getByIndex2(&object->actions, pos, (void**)&act, sizeof(gxActionSet))) != 0)
|
|
{
|
|
break;
|
|
}
|
|
e->target = &act->actionUp.script->base;
|
|
index = (unsigned char)act->actionUp.scriptSelector;
|
|
ret = var_copy(lastValue, &value);
|
|
}
|
|
}
|
|
if (e->target != NULL)
|
|
{
|
|
bb_clear(&bb);
|
|
if ((ret = var_setInt8(&e->parameters, index)) != 0 ||
|
|
(ret = invoke_ScriptTable(settings, e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&args);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
int svr_monitorAll(dlmsServerSettings* settings)
|
|
{
|
|
int ret = 0, pos;
|
|
gxObject* obj;
|
|
//Single activity calendars.
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
break;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_REGISTER_MONITOR)
|
|
{
|
|
if ((ret = svr_monitor(settings, (gxRegisterMonitor*)obj)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_REGISTER_MONITOR
|
|
|
|
|
|
#ifndef DLMS_IGNORE_LIMITER
|
|
|
|
int svr_invokeLimiterAction(
|
|
dlmsServerSettings* settings,
|
|
gxValueEventArg* e,
|
|
gxActionItem* action)
|
|
{
|
|
int pos, ret = 0;
|
|
gxObject* obj;
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
break;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_SCRIPT_TABLE &&
|
|
memcmp(obj->logicalName, action->script->base.logicalName, 6) == 0)
|
|
{
|
|
e->target = (gxObject*)action->script;
|
|
e->index = 1;
|
|
if ((ret = var_setInt16(&e->parameters, action->scriptSelector)) != 0 ||
|
|
(ret = invoke_ScriptTable(settings, e)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int svr_limiter(dlmsServerSettings* settings,
|
|
gxLimiter* object,
|
|
uint32_t now)
|
|
{
|
|
uint16_t pos;
|
|
int ret;
|
|
gxValueEventCollection args;
|
|
gxValueEventArg* e;
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
uint16_t* id;
|
|
gxValueEventArg tmp[1];
|
|
ve_init(&tmp[0]);
|
|
vec_attach(&args, tmp, 1, 1);
|
|
e = &tmp[0];
|
|
#else
|
|
dlmsVARIANT* id;
|
|
e = (gxValueEventArg*)gxmalloc(sizeof(gxValueEventArg));
|
|
ve_init(e);
|
|
vec_init(&args);
|
|
vec_push(&args, e);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
e->target = object->monitoredValue;
|
|
e->index = object->selectedAttributeIndex;
|
|
e->action = 1;
|
|
svr_preRead(&settings->base, &args);
|
|
ret = e->error;
|
|
if (!e->handled && ret == 0)
|
|
{
|
|
if ((ret = cosem_getValue(&settings->base, e)) == 0)
|
|
{
|
|
svr_postRead(&settings->base, &args);
|
|
ret = e->error;
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
//Save value.
|
|
double currentValue = var_toDouble(&e->value);
|
|
double activeValue;
|
|
double normalValue = var_toDouble(&object->thresholdNormal);
|
|
double emergencyValue = var_toDouble(&object->thresholdEmergency);
|
|
if (!object->emergencyProfileActive)
|
|
{
|
|
//Use normal threshold in normal mode.
|
|
activeValue = normalValue;
|
|
}
|
|
else
|
|
{
|
|
//Use emergency threshold in emergency mode.
|
|
activeValue = emergencyValue;
|
|
}
|
|
|
|
if (currentValue < activeValue)
|
|
{
|
|
//If active value is under threshold or it changes from over to under.
|
|
if (object->activationTime == 0 || (object->overThreshold & 0x1) == 1)
|
|
{
|
|
if (object->activationTime == 0)
|
|
{
|
|
//Server is started and limiter is set to IDLE mode.
|
|
object->activationTime = now;
|
|
// Threshold is NOT invoked when server is started.
|
|
object->overThreshold = 0x80;
|
|
}
|
|
else
|
|
{
|
|
object->activationTime = now;
|
|
object->overThreshold = 0;
|
|
}
|
|
}
|
|
else if (now - object->activationTime >= object->minUnderThresholdDuration)
|
|
{
|
|
if ((object->actionUnderThreshold.script != NULL) && (0 == object->overThreshold))
|
|
{
|
|
ret = svr_invokeLimiterAction(settings, e, &object->actionUnderThreshold);
|
|
// Threshold is invoked only once.
|
|
// The highest bit is set to indicate that the action has been completed.
|
|
object->overThreshold = 0x80;
|
|
}
|
|
}
|
|
}
|
|
else if (currentValue > activeValue)
|
|
{
|
|
//If active value is over threshold or it changes from under to over.
|
|
if (object->activationTime == 0 || (object->overThreshold & 0x1) == 0)
|
|
{
|
|
if (object->activationTime == 0)
|
|
{
|
|
//Server is started and limiter is set to IDLE mode.
|
|
object->activationTime = now;
|
|
// Threshold is NOT invoked when server is started.
|
|
object->overThreshold = 0x81;
|
|
}
|
|
else
|
|
{
|
|
object->activationTime = now;
|
|
object->overThreshold = 1;
|
|
}
|
|
}
|
|
else if (now - object->activationTime >= object->minOverThresholdDuration)
|
|
{
|
|
if ((object->actionOverThreshold.script != NULL) && (1 == object->overThreshold))
|
|
{
|
|
ret = svr_invokeLimiterAction(settings, e, &object->actionOverThreshold);
|
|
//Threshold is invoked only once.
|
|
// The highest bit is set to indicate that the action has been completed.
|
|
object->overThreshold = 0x81;
|
|
}
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
if (!object->emergencyProfileActive)
|
|
{
|
|
//Limiter is in normal mode.
|
|
if ((now >= time_toUnixTime2(&object->emergencyProfile.activationTime)) &&
|
|
(now < (time_toUnixTime2(&object->emergencyProfile.activationTime) + (object->emergencyProfile.duration))))
|
|
{
|
|
//Search emergency profile group ID and activate the emergency if it is found.
|
|
for (pos = 0; pos != object->emergencyProfileGroupIDs.size; ++pos)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (va_getByIndex(&object->emergencyProfileGroupIDs, pos, &id) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#else
|
|
if (arr_getByIndex(&object->emergencyProfileGroupIDs, pos, (void**)&id, sizeof(uint16_t)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (object->emergencyProfile.id == id->iVal)
|
|
#else
|
|
if (object->emergencyProfile.id == *id)
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
{
|
|
//Activate emergency mode.
|
|
object->emergencyProfileActive = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Limiter is in emergency mode.
|
|
if ((now - time_toUnixTime2(&object->emergencyProfile.activationTime)) >
|
|
(object->emergencyProfile.duration))
|
|
{
|
|
//The emergency duration elapsed and emergency is over.
|
|
object->emergencyProfileActive = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
vec_clear(&args);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
return ret;
|
|
}
|
|
|
|
int svr_limiterAll(dlmsServerSettings* settings, uint32_t now)
|
|
{
|
|
int ret = 0, pos;
|
|
gxObject* obj;
|
|
for (pos = 0; pos != settings->base.objects.size; ++pos)
|
|
{
|
|
if ((ret = oa_getByIndex(&settings->base.objects, pos, &obj)) != DLMS_ERROR_CODE_OK)
|
|
{
|
|
break;
|
|
}
|
|
if (obj->objectType == DLMS_OBJECT_TYPE_LIMITER)
|
|
{
|
|
if ((ret = svr_limiter(settings, (gxLimiter*)obj, now)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_LIMITER
|
|
|
|
#endif //DLMS_IGNORE_SERVER
|