From da0af0ba0f92d6959c541786e50ff213c1fdec00 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 29 Jan 2016 15:16:57 +0100 Subject: [PATCH] - extented SV publisher code / 9-2LE example --- config/stack_config.h | 2 +- .../iec61850_9_2_LE_example.c | 22 +- .../sv_subscriber/test_publisher/Makefile | 22 - .../test_publisher/sv_publisher.c | 571 ------------------ src/iec61850/inc/iec61850_model.h | 3 + src/iec61850/inc/iec61850_server.h | 34 ++ src/iec61850/inc_private/mms_sv.h | 3 + src/iec61850/server/impl/ied_server.c | 11 + src/iec61850/server/mms_mapping/mms_sv.c | 45 +- src/iec61850/server/model/model.c | 24 + src/sampled_values/sv_publisher.c | 94 +-- src/sampled_values/sv_publisher.h | 14 +- src/vs/libiec61850-wo-goose.def | 4 +- src/vs/libiec61850.def | 4 +- 14 files changed, 211 insertions(+), 642 deletions(-) delete mode 100644 examples/sv_subscriber/test_publisher/Makefile delete mode 100644 examples/sv_subscriber/test_publisher/sv_publisher.c diff --git a/config/stack_config.h b/config/stack_config.h index fb68518..61ae9e6 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -20,7 +20,7 @@ #define DEBUG_IED_SERVER 1 #define DEBUG_IED_CLIENT 0 #define DEBUG_MMS_CLIENT 0 -#define DEBUG_MMS_SERVER 1 +#define DEBUG_MMS_SERVER 0 #define DEBUG_GOOSE_SUBSCRIBER 0 #define DEBUG_GOOSE_PUBLISHER 0 #define DEBUG_SV_SUBSCRIBER 0 diff --git a/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c b/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c index 86c1cdb..7122302 100644 --- a/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c +++ b/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c @@ -36,6 +36,7 @@ extern IedModel iedModel; static int running = 0; +static int svcbEnabled = 0; void sigint_handler(int signalId) { @@ -75,13 +76,19 @@ setupSVPublisher() SampledValuesPublisher_setupComplete(svPublisher); } +static void sVCBEventHandler (SVControlBlock* svcb, int event, void* parameter) +{ + if (event == IEC61850_SVCB_EVENT_ENABLE) + svcbEnabled = 1; + else if (event == IEC61850_SVCB_EVENT_DISABLE) + svcbEnabled = 0; +} + int main(int argc, char** argv) { IedServer iedServer = IedServer_create(&iedModel); - // TODO set initial measurement and status values from process - /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); @@ -100,6 +107,15 @@ main(int argc, char** argv) int voltage = 1; int current = 1; + SVControlBlock* svcb = IedModel_getSVControlBlock(&iedModel, IEDMODEL_MUnn_LLN0, "MSVCB01"); + + if (svcb == NULL) { + printf("Lookup svcb failed!\n"); + exit(1); + } + + IedServer_setSVCBHandler(iedServer, svcb, sVCBEventHandler, NULL); + while (running) { uint64_t timeval = Hal_getTimeInMs(); @@ -118,7 +134,7 @@ main(int argc, char** argv) IedServer_unlockDataModel(iedServer); - if (1) { + if (svcbEnabled) { SV_ASDU_setINT32(asdu, amp1, current); SV_ASDU_setINT32(asdu, amp2, current); diff --git a/examples/sv_subscriber/test_publisher/Makefile b/examples/sv_subscriber/test_publisher/Makefile deleted file mode 100644 index 56c2062..0000000 --- a/examples/sv_subscriber/test_publisher/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -LIBIEC_HOME=../../../ - -PROJECT_BINARY_NAME = sv_test_publisher -PROJECT_SOURCES = sv_publisher.c -# PROJECT_SOURCES += remote_control.c - -INCLUDES += -I. - -include $(LIBIEC_HOME)/make/target_system.mk -include $(LIBIEC_HOME)/make/stack_includes.mk - -all: $(PROJECT_BINARY_NAME) - -include $(LIBIEC_HOME)/make/common_targets.mk - -$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) - $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) - -clean: - rm -f $(PROJECT_BINARY_NAME) - - diff --git a/examples/sv_subscriber/test_publisher/sv_publisher.c b/examples/sv_subscriber/test_publisher/sv_publisher.c deleted file mode 100644 index 0fea09b..0000000 --- a/examples/sv_subscriber/test_publisher/sv_publisher.c +++ /dev/null @@ -1,571 +0,0 @@ -/* - * sv_publisher.c - * - * Copyright 2013 Michael Zillgith - * - * This file is part of libIEC61850. - * - * libIEC61850 is free 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, either version 3 of the License, or - * (at your option) any later version. - * - * libIEC61850 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. - * - * You should have received a copy of the GNU General Public License - * along with libIEC61850. If not, see . - * - * See COPYING file for the complete license text. - */ - -#include "stack_config.h" -#include "libiec61850_platform_includes.h" - -#include "hal_ethernet.h" - -#define DEBUG_SV_PUBLISHER 1 - -#define CONFIG_SV_DEFAULT_DST_ADDRESS CONFIG_GOOSE_DEFAULT_DST_ADDRESS - -#define CONFIG_SV_DEFAULT_PRIORITY 4 -#define CONFIG_SV_DEFAULT_VLAN_ID 0 -#define CONFIG_SV_DEFAULT_APPID 0x4000 - -#define SV_MAX_MESSAGE_SIZE 1518 - -#define IEC61850_SV_SMPSYNC_NOT_SYNCHRONIZED 0 -#define IEC61850_SV_SMPSYNC_SYNCED_UNSPEC_LOCAL_CLOCK 1 -#define IEC61850_SV_SMPSYNC_SYNCED_GLOBAL_CLOCK 2 - -#define IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD 0 -#define IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND 1 -#define IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE 2 - -typedef struct sCommParameters { - uint8_t vlanPriority; - uint16_t vlanId; - uint16_t appId; - uint8_t dstAddress[6]; -} CommParameters; - -typedef struct sSV_ASDU* SV_ASDU; - -struct sSV_ASDU { - char* svID; - char* datset; - int dataSize; - - bool hasRefrTm; - bool hasSmpRate; - bool hasSmpMod; - - uint8_t* _dataBuffer; - - uint8_t smpSynch; - uint16_t smpCnt; - uint32_t confRev; - - uint8_t* smpCntBuf; - - SV_ASDU _next; -}; - -typedef struct sSampledValuesPublisher* SampledValuesPublisher; - -struct sSampledValuesPublisher { - uint8_t* buffer; - uint16_t appId; - EthernetSocket ethernetSocket; - - int lengthField; /* can probably be removed since packets have fixed size! */ - int payloadStart; - - int payloadLength; /* length of payload buffer */ - - int asduCount; /* number of ASDUs in the APDU */ - SV_ASDU asduLIst; - - -}; - - -static void -preparePacketBuffer(SampledValuesPublisher self, CommParameters* parameters, char* interfaceID) -{ - uint8_t srcAddr[6]; - - if (interfaceID != NULL) - Ethernet_getInterfaceMACAddress(interfaceID, srcAddr); - else - Ethernet_getInterfaceMACAddress(CONFIG_ETHERNET_INTERFACE_ID, srcAddr); - - uint8_t defaultDstAddr[] = CONFIG_SV_DEFAULT_DST_ADDRESS; - - uint8_t* dstAddr; - uint8_t priority; - uint16_t vlanId; - uint16_t appId; - - if (parameters == NULL) { - dstAddr = defaultDstAddr; - priority = CONFIG_SV_DEFAULT_PRIORITY; - vlanId = CONFIG_SV_DEFAULT_VLAN_ID; - appId = CONFIG_SV_DEFAULT_APPID; - } - else { - dstAddr = parameters->dstAddress; - priority = parameters->vlanPriority; - vlanId = parameters->vlanId; - appId = parameters->appId; - } - - if (interfaceID != NULL) - self->ethernetSocket = Ethernet_createSocket(interfaceID, dstAddr); - else - self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr); - - self->buffer = (uint8_t*) GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); - - memcpy(self->buffer, dstAddr, 6); - memcpy(self->buffer + 6, srcAddr, 6); - - int bufPos = 12; - - /* Priority tag - IEEE 802.1Q */ - self->buffer[bufPos++] = 0x81; - self->buffer[bufPos++] = 0x00; - - uint8_t tci1 = priority << 5; - tci1 += vlanId / 256; - - uint8_t tci2 = vlanId % 256; - - self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ - self->buffer[bufPos++] = tci2; /* VLAN-ID */ - - /* EtherType Sampled Values */ - self->buffer[bufPos++] = 0x88; - self->buffer[bufPos++] = 0xBa; - - /* APPID */ - self->buffer[bufPos++] = appId / 256; - self->buffer[bufPos++] = appId % 256; - - self->lengthField = bufPos; - - /* Length */ - self->buffer[bufPos++] = 0x00; - self->buffer[bufPos++] = 0x08; - - /* Reserved1 */ - self->buffer[bufPos++] = 0x00; - self->buffer[bufPos++] = 0x00; - - /* Reserved2 */ - self->buffer[bufPos++] = 0x00; - self->buffer[bufPos++] = 0x00; - - self->payloadStart = bufPos; -} - - -static int -encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos) -{ - uint8_t* valueArray = (uint8_t*) &value; - -#if (ORDER_LITTLE_ENDIAN == 1) - buffer[bufPos++] = valueArray[1]; - buffer[bufPos++] = valueArray[0]; -#else - buffer[bufPos++] = valueArray[0]; - buffer[bufPos++] = valueArray[1]; -#endif - - return bufPos; -} - -static int -encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos) -{ - uint8_t* valueArray = (uint8_t*) &value; - -#if (ORDER_LITTLE_ENDIAN == 1) - buffer[bufPos++] = valueArray[3]; - buffer[bufPos++] = valueArray[2]; - buffer[bufPos++] = valueArray[1]; - buffer[bufPos++] = valueArray[0]; -#else - buffer[bufPos++] = valueArray[0]; - buffer[bufPos++] = valueArray[1]; - buffer[bufPos++] = valueArray[2]; - buffer[bufPos++] = valueArray[3]; -#endif - - return bufPos; -} - - -SampledValuesPublisher -SampledValuesPublisher_create() -{ - SampledValuesPublisher self = GLOBAL_CALLOC(1, sizeof(struct sSampledValuesPublisher)); - - self->asduLIst = NULL; - - preparePacketBuffer(self, NULL, "eth0"); - - return self; -} - -SV_ASDU -SampledValuesPublisher_addASDU(SampledValuesPublisher self, char* svID, char* datset, uint32_t confRev) -{ - SV_ASDU newAsdu = GLOBAL_CALLOC(1, sizeof(struct sSV_ASDU)); - - newAsdu->svID = svID; - newAsdu->datset = datset; - newAsdu->confRev = confRev; - - newAsdu->_next = NULL; - - /* append new ASDU to list */ - if (self->asduLIst == NULL) - self->asduLIst = newAsdu; - else { - SV_ASDU lastAsdu = self->asduLIst; - - while (lastAsdu->_next != NULL) - lastAsdu = lastAsdu->_next; - - lastAsdu->_next = newAsdu; - } - - return newAsdu; -} - -static int -SV_ASDU_getEncodedSize(SV_ASDU self) -{ - int encodedSize = 0; - - /* svID */ - encodedSize += ( 2 + strlen(self->svID) ); - - /* datset */ - if (self->datset != NULL) - encodedSize += ( 2 + strlen(self->datset) ); - - /* smpCnt */ - encodedSize += 4; - - /* confRef */ - encodedSize += 6; - - /* refrTm */ - if (self->hasRefrTm) - encodedSize += 10; /* ??? */ - - /* smpSynch */ - encodedSize += 3; - - /* smpRate */ - if (self->hasSmpRate) - encodedSize += 4; - - /* sample */ - encodedSize += 2; - encodedSize += self->dataSize; - - /* smpMod */ - if (self->hasSmpMod) - encodedSize += 4; - - return encodedSize; -} - -static int -SV_ASDU_encodeToBuffer(SV_ASDU self, uint8_t* buffer, int bufPos) -{ - int encodedSize = SV_ASDU_getEncodedSize(self); - - /* tag and length field */ - bufPos = BerEncoder_encodeTL(0x30, encodedSize, buffer, bufPos); - - /* svID */ - bufPos = BerEncoder_encodeStringWithTag(0x80, self->svID, buffer, bufPos); - - /* DatSet */ - if (self->datset != NULL) - bufPos = BerEncoder_encodeStringWithTag(0x81, self->datset, buffer, bufPos); - - uint8_t octetString[4]; - - /* SmpCnt */ - bufPos = BerEncoder_encodeTL(0x82, 2, buffer, bufPos); - self->smpCntBuf = buffer + bufPos; - bufPos = encodeUInt16FixedSize(self->smpCnt, buffer, bufPos); - - /* ConfRev */ - bufPos = BerEncoder_encodeTL(0x83, 4, buffer, bufPos); - bufPos = encodeUInt32FixedSize(self->confRev, buffer, bufPos); - - /* RefrTm */ - //TODO implement me - - /* SmpSynch */ - bufPos = BerEncoder_encodeTL(0x85, 1, buffer, bufPos); - buffer[bufPos++] = self->smpSynch; - - /* SmpRate */ - //TODO implement me - - /* Sample */ - bufPos = BerEncoder_encodeTL(0x87, self->dataSize, buffer, bufPos); - - self->_dataBuffer = buffer + bufPos; - - bufPos += self->dataSize; /* data has to inserted by user before sending message */ - - /* SmpMod */ - //TODO implement me - - return bufPos; -} - -void -SampledValuesPublisher_setupComplete(SampledValuesPublisher self) -{ - int numberOfAsdu = 0; - - /* determine number of ASDUs and length of all ASDUs */ - SV_ASDU nextAsdu = self->asduLIst; - int totalASDULength = 0; - - while (nextAsdu != NULL) { - numberOfAsdu++; - int asduLength = SV_ASDU_getEncodedSize(nextAsdu); - - /* tag and length field */ - asduLength += BerEncoder_determineLengthSize(asduLength); - asduLength++; - - totalASDULength += asduLength; - - nextAsdu = nextAsdu->_next; - } - - /* encode frame to buffer */ - int sequenceSize = 1 + BerEncoder_determineLengthSize(totalASDULength) + totalASDULength; - - int innerSize = 2 + BerEncoder_UInt32determineEncodedSize(numberOfAsdu) + sequenceSize; - - uint8_t* buffer = self->buffer + self->payloadStart; - - int bufPos = BerEncoder_encodeTL(0x60, innerSize, buffer, 0); - - /* noASDU */ - bufPos = BerEncoder_encodeUInt32WithTL(0x80, numberOfAsdu, buffer, bufPos); - - /* seqASDU */ - bufPos = BerEncoder_encodeTL(0xa2, totalASDULength, buffer, bufPos); - - nextAsdu = self->asduLIst; - - while (nextAsdu != NULL) { - bufPos = SV_ASDU_encodeToBuffer(nextAsdu, buffer, bufPos); - - nextAsdu = nextAsdu->_next; - } - - /* Update length field */ - int payloadLength = bufPos; - - size_t msgLength = payloadLength + 8; - - int lengthIndex = self->lengthField; - - self->buffer[lengthIndex] = msgLength / 256; - self->buffer[lengthIndex + 1] = msgLength & 0xff; - - self->payloadLength = payloadLength; - -} - - -void -SampledValuesPublisher_publish(SampledValuesPublisher self) -{ - if (DEBUG_SV_PUBLISHER) - printf("SV_PUBLISHER: send SV message\n"); - - Ethernet_sendPacket(self->ethernetSocket, self->buffer, self->payloadStart + self->payloadLength); - -} - - -void -SampledValuesPublisher_destroy(SampledValuesPublisher self) -{ - GLOBAL_FREEMEM(self->buffer); -} - - -void -SV_ASDU_resetBuffer(SV_ASDU self) -{ - self->dataSize = 0; -} - - -int -SV_ASDU_addINT8(SV_ASDU self) -{ - int index = self->dataSize; - - self->dataSize += 1; - - return index; -} - -void -SV_ASDU_setINT8(SV_ASDU self, int index, int8_t value) -{ - self->_dataBuffer[index] = value; -} - -int -SV_ASDU_addINT32(SV_ASDU self) -{ - int index = self->dataSize; - - self->dataSize += 4; - - return index; -} - -int -SV_ASDU_addFLOAT(SV_ASDU self) -{ - int index = self->dataSize; - - self->dataSize += 4; - - return index; -} - -void -SV_ASDU_setFLOAT(SV_ASDU self, int index, float value) -{ - uint8_t* buf = (uint8_t*) &value; - - -#if (ORDER_LITTLE_ENDIAN == 1) - BerEncoder_revertByteOrder(buf, 4); -#endif - - int i; - - uint8_t* buffer = self->_dataBuffer + index; - - for (i = 0; i < 4; i++) { - buffer[i] = buf[i]; - } -} - - -void -SV_ASDU_setSmpCnt(SV_ASDU self, uint16_t value) -{ - //TODO write value to correct field in buffer -} - -void -SV_ASDU_increaseSmpCnt(SV_ASDU self) -{ - self->smpCnt++; - - encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0); -} - -#if 0 -void -SV_ASDU_setRefrTm(SV_ASDU self, Timestamp refrTm) -{ -} -#endif - - -int -main(int argc, char** argv) -{ - SampledValuesPublisher svPublisher = SampledValuesPublisher_create(); - - SV_ASDU asdu1 = SampledValuesPublisher_addASDU(svPublisher, "svpub1", NULL, 1); - - int float1 = SV_ASDU_addFLOAT(asdu1); - int float2 = SV_ASDU_addFLOAT(asdu1); - - SV_ASDU asdu2 = SampledValuesPublisher_addASDU(svPublisher, "svpub2", NULL, 1); - - int float3 = SV_ASDU_addFLOAT(asdu2); - int float4 = SV_ASDU_addFLOAT(asdu2); - - SampledValuesPublisher_setupComplete(svPublisher); - - float fVal1 = 1234.5678f; - float fVal2 = 0.12345f; - - int i; - - for (i = 0; i < 10; i++) { - SV_ASDU_setFLOAT(asdu1, float1, fVal1); - SV_ASDU_setFLOAT(asdu1, float2, fVal2); - - SV_ASDU_increaseSmpCnt(asdu1); - SV_ASDU_increaseSmpCnt(asdu2); - - fVal1 += 1.1f; - fVal2 += 0.1f; - - SampledValuesPublisher_publish(svPublisher); - } - - SampledValuesPublisher_destroy(svPublisher); -} - -int -main1(int argc, char** argv) -{ - SampledValuesPublisher svPublisher = SampledValuesPublisher_create(); - - SV_ASDU asdu = SampledValuesPublisher_addASDU(svPublisher, "svpub1", NULL, 1); - - int float1 = SV_ASDU_addFLOAT(asdu); - int float2 = SV_ASDU_addFLOAT(asdu); - - SampledValuesPublisher_setupComplete(svPublisher); - - - float fVal1 = 1234.5678f; - float fVal2 = 0.12345f; - - int i; - - for (i = 0; i < 10; i++) { - SV_ASDU_setFLOAT(asdu, float1, fVal1); - SV_ASDU_setFLOAT(asdu, float2, fVal2); - SV_ASDU_increaseSmpCnt(asdu); - - fVal1 += 1.1f; - fVal2 += 0.1f; - - SampledValuesPublisher_publish(svPublisher); - } - - SampledValuesPublisher_destroy(svPublisher); -} - diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h index cd6828b..54fd086 100644 --- a/src/iec61850/inc/iec61850_model.h +++ b/src/iec61850/inc/iec61850_model.h @@ -383,6 +383,9 @@ IedModel_setIedName(IedModel* self, const char* iedName); ModelNode* IedModel_getModelNodeByObjectReference(IedModel* model, const char* objectReference); +SVControlBlock* +IedModel_getSVControlBlock(IedModel* self, LogicalNode* parentLN, const char* svcbName); + /** * \brief Lookup a model node by its short (normalized) reference * diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 1af1dba..0c9f7ca 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -906,6 +906,40 @@ IedServer_setWaitForExecutionHandler(IedServer self, DataObject* node, ControlWa /**@}*/ +/** + * @defgroup IEC61850_SERVER_SVCB Server side sampled values control block (SVCB) handling + * + * @{ + */ + +/** Control block has been enabled by client */ +#define IEC61850_SVCB_EVENT_ENABLE 1 + +/** Control block has been disabled by client */ +#define IEC61850_SVCB_EVENT_DISABLE 0 + +/** + * \brief callback handler for SVCB events. + * + * \param svcb the related SVCB instance + * \param the event type + * \param user defined parameter + */ +typedef void (*SVCBEventHandler) (SVControlBlock* svcb, int event, void* parameter); + +/** + * \brief Set a handler for SVCB control block events (enable/disable) + * + * \param self the instance of IedServer to operate on. + * \param svcb the SVCB control block instance + * \param handler the event handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +void +IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler handler, void* parameter); + +/**@}*/ + /** * @defgroup IEC61850_SERVER_EXTERNAL_ACCESS Handle external access to data model and access control * diff --git a/src/iec61850/inc_private/mms_sv.h b/src/iec61850/inc_private/mms_sv.h index f868d75..edb67d1 100644 --- a/src/iec61850/inc_private/mms_sv.h +++ b/src/iec61850/inc_private/mms_sv.h @@ -44,4 +44,7 @@ MmsDataAccessError LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsValue* value, MmsServerConnection connection); +void +LIBIEC61850_SV_setSVCBHandler(MmsMapping* self, SVControlBlock* svcb, SVCBEventHandler handler, void* parameter); + #endif /* LIBIEC61850_SRC_IEC61850_INC_PRIVATE_MMS_SV_H_ */ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 1a23648..199e108 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -32,6 +32,7 @@ #include "reporting.h" #include "libiec61850_platform_includes.h" +#include "mms_sv.h" #ifndef DEBUG_IED_SERVER #define DEBUG_IED_SERVER 0 @@ -661,6 +662,16 @@ IedServer_setWaitForExecutionHandler(IedServer self, DataObject* node, ControlWa } #endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ +#if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) + +void +IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler handler, void* parameter) +{ + LIBIEC61850_SV_setSVCBHandler(self->mmsMapping, svcb, handler, parameter); +} + +#endif /* (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) */ + MmsValue* IedServer_getAttributeValue(IedServer self, DataAttribute* dataAttribute) { diff --git a/src/iec61850/server/mms_mapping/mms_sv.c b/src/iec61850/server/mms_mapping/mms_sv.c index 49ca481..b367994 100644 --- a/src/iec61850/server/mms_mapping/mms_sv.c +++ b/src/iec61850/server/mms_mapping/mms_sv.c @@ -35,7 +35,7 @@ #include "mms_mapping_internal.h" struct sMmsSampledValueControlBlock { - char* name; + SVControlBlock* svcb; bool svEna; MmsServerConnection reservedByClient; @@ -50,6 +50,10 @@ struct sMmsSampledValueControlBlock { MmsValue* svEnaValue; MmsValue* resvValue; + + + SVCBEventHandler eventHandler; + void* eventHandlerParameter; }; MmsSampledValueControlBlock @@ -79,7 +83,7 @@ lookupSVCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName) if (mmsSVCB->domain == domain) { if (strcmp(mmsSVCB->logicalNode->name, lnName) == 0) { - if (strcmp(mmsSVCB->name, objectName) == 0) { + if (strcmp(mmsSVCB->svcb->name, objectName) == 0) { return mmsSVCB; } } @@ -94,17 +98,25 @@ lookupSVCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName) static void MmsSampledValueControlBlock_enable(MmsSampledValueControlBlock self) { - //TODO call application callback handler self->svEna = true; MmsValue_setBoolean(self->svEnaValue, true); + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: enable SVCB %s\n", self->svcb->name); + + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_ENABLE, self->eventHandlerParameter); } static void MmsSampledValueControlBlock_disable(MmsSampledValueControlBlock self) { - //TODO call application callback handler self->svEna = false; MmsValue_setBoolean(self->svEnaValue, false); + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: disable SVCB %s\n", self->svcb->name); + + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_DISABLE, self->eventHandlerParameter); } static bool @@ -364,6 +376,29 @@ createDataSetReference(char* buffer, char* domainName, char* lnName, char* dataS StringUtils_createStringInBuffer(buffer, 5, domainName, "/", lnName, "$", dataSetName); } +void +LIBIEC61850_SV_setSVCBHandler(MmsMapping* self, SVControlBlock* svcb, SVCBEventHandler handler, void* parameter) +{ + LinkedList svcbElement = LinkedList_getNext(self->svControls); + + while (svcbElement != NULL) { + MmsSampledValueControlBlock mmsSVCB = (MmsSampledValueControlBlock) svcbElement->data; + + if (mmsSVCB->svcb == svcb) { + mmsSVCB->eventHandler = handler; + mmsSVCB->eventHandlerParameter = parameter; + break; + } + + svcbElement = LinkedList_getNext(svcbElement); + } + + if (DEBUG_IED_SERVER) { + if (svcbElement == NULL) + printf("IED_SERVER: setSVCBHandler failed\n"); + } +} + MmsVariableSpecification* LIBIEC61850_SV_createSVControlBlocks(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int svCount, bool unicast) @@ -480,7 +515,7 @@ LIBIEC61850_SV_createSVControlBlocks(MmsMapping* self, MmsDomain* domain, mmsSvCb->mmsType = svTypeSpec; mmsSvCb->domain = domain; mmsSvCb->logicalNode = logicalNode; - mmsSvCb->name = svControlBlock->name; + mmsSvCb->svcb = svControlBlock; LinkedList_add(self->svControls, (void*) mmsSvCb); diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c index 3a5a858..9b96b70 100644 --- a/src/iec61850/server/model/model.c +++ b/src/iec61850/server/model/model.c @@ -276,6 +276,30 @@ IedModel_getModelNodeByObjectReference(IedModel* model, const char* objectRefere return ModelNode_getChild((ModelNode*) ld, separator + 1); } +#if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) + +SVControlBlock* +IedModel_getSVControlBlock(IedModel* self, LogicalNode* parentLN, const char* svcbName) +{ + SVControlBlock* retVal = NULL; + + SVControlBlock* svCb = self->svCBs; + + while (svCb != NULL) { + if ((svCb->parent == parentLN) && (strcmp(svCb->name, svcbName) == 0)) { + retVal = svCb; + break; + } + + + svCb = svCb->sibling; + } + + return retVal; +} + +#endif /* (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) */ + ModelNode* IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectReference) { diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c index 67e65f8..2fbdb6d 100644 --- a/src/sampled_values/sv_publisher.c +++ b/src/sampled_values/sv_publisher.c @@ -24,7 +24,7 @@ #include "stack_config.h" #include "libiec61850_platform_includes.h" - +#include #include "sv_publisher.h" #include "hal_ethernet.h" @@ -64,6 +64,9 @@ struct sSV_ASDU { uint16_t smpCnt; uint32_t confRev; + uint64_t refrTm; + uint8_t smpMod; + uint8_t* smpCntBuf; SV_ASDU _next; @@ -224,6 +227,39 @@ encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos) return bufPos; } +static int +encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos) +{ + uint32_t timeval32 = (timeval / 1000LL); + + uint8_t* timeArray = (uint8_t*) &timeval32; + uint8_t* valueArray = buffer + bufPos; + +#if (ORDER_LITTLE_ENDIAN == 1) + valueArray[0] = timeArray[3]; + valueArray[1] = timeArray[2]; + valueArray[2] = timeArray[1]; + valueArray[3] = timeArray[0]; +#else + valueArray[0] = timeArray[0]; + valueArray[1] = timeArray[1]; + valueArray[2] = timeArray[2]; + valueArray[3] = timeArray[3]; +#endif + + uint32_t remainder = (timeval % 1000LL); + uint32_t fractionOfSecond = (remainder) * 16777 + ((remainder * 216) / 1000); + + /* encode fraction of second */ + valueArray[4] = ((fractionOfSecond >> 16) & 0xff); + valueArray[5] = ((fractionOfSecond >> 8) & 0xff); + valueArray[6] = (fractionOfSecond & 0xff); + + /* encode time quality */ + valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */ + + return bufPos + 8; +} SampledValuesPublisher SampledValuesPublisher_create(const char* interfaceId) @@ -330,7 +366,10 @@ SV_ASDU_encodeToBuffer(SV_ASDU self, uint8_t* buffer, int bufPos) bufPos = encodeUInt32FixedSize(self->confRev, buffer, bufPos); /* RefrTm */ - //TODO implement me + if (self->hasRefrTm) { + bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos); + bufPos = encodeUtcTime(self->refrTm, buffer, bufPos); + } /* SmpSynch */ bufPos = BerEncoder_encodeTL(0x85, 1, buffer, bufPos); @@ -347,7 +386,14 @@ SV_ASDU_encodeToBuffer(SV_ASDU self, uint8_t* buffer, int bufPos) bufPos += self->dataSize; /* data has to inserted by user before sending message */ /* SmpMod */ - //TODO implement me + if (self->hasSmpMod) { + bufPos = BerEncoder_encodeTL(0x84, 4, buffer, bufPos); + buffer[bufPos++] = 0; + buffer[bufPos++] = 0; + buffer[bufPos++] = 0; + buffer[bufPos++] = self->smpMod; + + } return bufPos; } @@ -515,43 +561,17 @@ SV_ASDU_increaseSmpCnt(SV_ASDU self) encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0); } -#if 0 void -SV_ASDU_setRefrTm(SV_ASDU self, Timestamp refrTm) +SV_ASDU_setRefrTm(SV_ASDU self, uint64_t refrTm) { + self->hasRefrTm = true; + self->refrTm = refrTm; } -#endif -#if 0 -int -main1(int argc, char** argv) +void +SV_ASDU_setSmpMod(SV_ASDU self, uint8_t smpMod) { - SampledValuesPublisher svPublisher = SampledValuesPublisher_create("eth1"); - - SV_ASDU asdu = SampledValuesPublisher_addASDU(svPublisher, "svpub1", NULL, 1); - - int float1 = SV_ASDU_addFLOAT(asdu); - int float2 = SV_ASDU_addFLOAT(asdu); - - SampledValuesPublisher_setupComplete(svPublisher); - - - float fVal1 = 1234.5678f; - float fVal2 = 0.12345f; - - int i; - - for (i = 0; i < 10; i++) { - SV_ASDU_setFLOAT(asdu, float1, fVal1); - SV_ASDU_setFLOAT(asdu, float2, fVal2); - SV_ASDU_increaseSmpCnt(asdu); - - fVal1 += 1.1f; - fVal2 += 0.1f; - - SampledValuesPublisher_publish(svPublisher); - } - - SampledValuesPublisher_destroy(svPublisher); + self->hasSmpMod = true; + self->smpMod = smpMod; } -#endif + diff --git a/src/sampled_values/sv_publisher.h b/src/sampled_values/sv_publisher.h index aeb890b..c27fc6f 100644 --- a/src/sampled_values/sv_publisher.h +++ b/src/sampled_values/sv_publisher.h @@ -81,7 +81,19 @@ SV_ASDU_setSmpCnt(SV_ASDU self, uint16_t value); void SV_ASDU_increaseSmpCnt(SV_ASDU self); +void +SV_ASDU_setRefrTm(SV_ASDU self, uint64_t refrTm); - +/** + * \brief Set the sample mode of the ASDU + * + * If not set the transmitted ASDU will not contain an sampleMod value + * + * \param self the SV_ASDU + * + * \param smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE + */ +void +SV_ASDU_setSmpMod(SV_ASDU self, uint8_t smpMod); #endif /* LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ */ diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index c886c74..1a87ef6 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -513,4 +513,6 @@ EXPORTS ClientSVControlBlock_getDstAddress ClientSVControlBlock_getOptFlds ClientSVControlBlock_getSmpMod - ClientSVControlBlock_getNoASDU \ No newline at end of file + ClientSVControlBlock_getNoASDU + IedServer_setSVCBHandler + IedModel_getSVControlBlock \ No newline at end of file diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index abde790..8ad4188 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -563,4 +563,6 @@ EXPORTS ClientSVControlBlock_getDstAddress ClientSVControlBlock_getOptFlds ClientSVControlBlock_getSmpMod - ClientSVControlBlock_getNoASDU \ No newline at end of file + ClientSVControlBlock_getNoASDU + IedServer_setSVCBHandler + IedModel_getSVControlBlock \ No newline at end of file