// // -------------------------------------------------------------------------- // 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 #if _MSC_VER > 1400 #include #include #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