diff --git a/examples/server_example_goose/server_example_goose.c b/examples/server_example_goose/server_example_goose.c index d6548c35..865f01c1 100644 --- a/examples/server_example_goose/server_example_goose.c +++ b/examples/server_example_goose/server_example_goose.c @@ -51,9 +51,20 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v } } +static void +goCbEventHandler(MmsGooseControlBlock goCb, int event, void* parameter) +{ + printf("Access to GoCB: %s\n", MmsGooseControlBlock_getName(goCb)); + printf(" GoEna: %i\n", MmsGooseControlBlock_getGoEna(goCb)); +} + int main(int argc, char** argv) { - iedServer = IedServer_create(&iedModel); + IedServerConfig config = IedServerConfig_create(); + + iedServer = IedServer_createWithConfig(&iedModel, NULL, config); + + IedServerConfig_destroy(config); if (argc > 1) { char* ethernetIfcID = argv[1]; @@ -73,6 +84,7 @@ int main(int argc, char** argv) { IedServer_setGooseInterfaceIdEx(iedServer, IEDMODEL_GenericIO_LLN0, "gcbAnalogValues", ethernetIfcID); } + IedServer_setGoCBHandler(iedServer, goCbEventHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 419431b8..2d80c811 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -75,6 +75,9 @@ struct sIedServerConfig /** when true (default) enable log service */ bool enableLogService; + /** when true (default) the integrated GOOSE publisher is used */ + bool useIntegratedGoosePublisher; + /** IEC 61850 edition (0 = edition 1, 1 = edition 2, 2 = edition 2.1, ...) */ uint8_t edition; @@ -269,11 +272,21 @@ IedServerConfig_getMaxDatasSetEntries(IedServerConfig self); /** * \brief Enable/disable the log service for MMS * - * \param[in] enable set true to enable dynamic data set service, otherwise false + * \param[in] enable set true to enable log service, otherwise false */ LIB61850_API void IedServerConfig_enableLogService(IedServerConfig self, bool enable); +/** + * \brief Enable/disable using the integrated GOOSE publisher for configured GoCBs + * + * This is enabled by default. Disable it when you want to use a separate GOOSE publisher + * + * \param[in] enable set true to enable the integrated GOOSE publisher, otherwise false + */ +LIB61850_API void +IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable); + /** * \brief Is the log service for MMS enabled or disabled * @@ -1446,6 +1459,48 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler /**@}*/ +/** + * @defgroup IEC61850_SERVER_GOCB Server side GOOSE control block (GoCB) handling + * + * @{ + */ + +typedef struct sMmsGooseControlBlock* MmsGooseControlBlock; + +/** Control block has been enabled by client */ +#define IEC61850_GOCB_EVENT_ENABLE 1 + +/** Control block has been disabled by client */ +#define IEC61850_GOCB_EVENT_DISABLE 0 + +typedef void (*GoCBEventHandler) (MmsGooseControlBlock goCb, int event, void* parameter); + +LIB61850_API void +IedServer_setGoCBHandler(IedServer self, GoCBEventHandler handler, void* parameter); + +LIB61850_API char* +MmsGooseControlBlock_getName(MmsGooseControlBlock self); + +LIB61850_API LogicalNode* +MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self); + +LIB61850_API DataSet* +MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self); + +LIB61850_API bool +MmsGooseControlBlock_getGoEna(MmsGooseControlBlock self); + +LIB61850_API int +MmsGooseControlBlock_getMinTime(MmsGooseControlBlock self); + +LIB61850_API int +MmsGooseControlBlock_getMaxTime(MmsGooseControlBlock self); + +LIB61850_API bool +MmsGooseControlBlock_getFixedOffs(MmsGooseControlBlock self); + +/**@}*/ + /** * @defgroup IEC61850_SERVER_EXTERNAL_ACCESS Handle external access to data model and access control * diff --git a/src/iec61850/inc_private/mms_goose.h b/src/iec61850/inc_private/mms_goose.h index 326e35dc..e2c6f93f 100644 --- a/src/iec61850/inc_private/mms_goose.h +++ b/src/iec61850/inc_private/mms_goose.h @@ -24,8 +24,6 @@ #ifndef MMS_GOOSE_H_ #define MMS_GOOSE_H_ -typedef struct sMmsGooseControlBlock* MmsGooseControlBlock; - LIB61850_INTERNAL MmsGooseControlBlock MmsGooseControlBlock_create(void); @@ -41,15 +39,9 @@ MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag) LIB61850_INTERNAL void MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId); -LIB61850_INTERNAL LogicalNode* -MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self); - LIB61850_INTERNAL char* MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self); -LIB61850_INTERNAL char* -MmsGooseControlBlock_getName(MmsGooseControlBlock self); - LIB61850_INTERNAL MmsValue* MmsGooseControlBlock_getGCBValue(MmsGooseControlBlock self, char* elementName); @@ -59,9 +51,6 @@ MmsGooseControlBlock_getMmsValues(MmsGooseControlBlock self); LIB61850_INTERNAL MmsVariableSpecification* MmsGooseControlBlock_getVariableSpecification(MmsGooseControlBlock self); -LIB61850_INTERNAL DataSet* -MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self); - LIB61850_INTERNAL bool MmsGooseControlBlock_isEnabled(MmsGooseControlBlock self); @@ -75,10 +64,10 @@ LIB61850_INTERNAL void MmsGooseControlBlock_publishNewState(MmsGooseControlBlock self); LIB61850_INTERNAL void -MmsGooseControlBlock_enable(MmsGooseControlBlock self); +MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping); LIB61850_INTERNAL void -MmsGooseControlBlock_disable(MmsGooseControlBlock self); +MmsGooseControlBlock_disable(MmsGooseControlBlock self, MmsMapping* mmsMapping); LIB61850_INTERNAL void GOOSE_sendPendingEvents(MmsMapping* self); diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index 4590eb34..195624a8 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -106,6 +106,9 @@ MmsMapping_enableGoosePublishing(MmsMapping* self); LIB61850_INTERNAL void MmsMapping_disableGoosePublishing(MmsMapping* self); +LIB61850_INTERNAL void +MmsMapping_useIntegratedGoosePublisher(MmsMapping* self, bool enable); + LIB61850_INTERNAL void MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbName, bool useVlanTag); diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 8fe3baa4..95433498 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -270,8 +270,13 @@ struct sMmsMapping { #endif #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) + bool useIntegratedPublisher; + LinkedList gseControls; const char* gooseInterfaceId; + + GoCBEventHandler goCbHandler; + void* goCbHandlerParameter; #endif #if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index adfe596c..16ec66f4 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -533,6 +533,13 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio #if (CONFIG_IEC61850_SETTING_GROUPS == 1) MmsMapping_configureSettingGroups(self->mmsMapping); +#endif + +#if (CONFIG_INCLUDE_GOOSE_SUPPORT) + if (serverConfiguration) { + MmsMapping_useIntegratedGoosePublisher(self->mmsMapping, serverConfiguration->useIntegratedGoosePublisher); + } + #endif } @@ -885,6 +892,17 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler #endif /* (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) */ +#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) + +void +IedServer_setGoCBHandler(IedServer self, GoCBEventHandler handler, void* parameter) +{ + self->mmsMapping->goCbHandler = handler; + self->mmsMapping->goCbHandlerParameter = parameter; +} + +#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ + MmsValue* IedServer_getAttributeValue(IedServer self, DataAttribute* dataAttribute) { diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c index cd84bc22..35db9b59 100644 --- a/src/iec61850/server/impl/ied_server_config.c +++ b/src/iec61850/server/impl/ied_server_config.c @@ -51,6 +51,7 @@ IedServerConfig_create() self->maxDomainSpecificDataSets = CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS; self->maxDataSetEntries = CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS; self->enableLogService = true; + self->useIntegratedGoosePublisher = true; self->edition = IEC_61850_EDITION_2; self->maxMmsConnections = 5; } @@ -185,6 +186,12 @@ IedServerConfig_enableLogService(IedServerConfig self, bool enable) self->enableLogService = enable; } +void +IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable) +{ + self->useIntegratedGoosePublisher = enable; +} + bool IedServerConfig_isLogServiceEnabled(IedServerConfig self) { diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index f0ffaee8..437a0b5d 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -1,7 +1,7 @@ /* * mms_goose.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2020 Michael Zillgith * * This file is part of libIEC61850. * @@ -40,9 +40,9 @@ struct sMmsGooseControlBlock { char* name; - int goEna:1; - int isDynamicDataSet:1; - int useVlanTag:1; + bool goEna; + unsigned int isDynamicDataSet:1; + unsigned int useVlanTag:1; char* dstAddress; @@ -76,6 +76,71 @@ struct sMmsGooseControlBlock { bool stateChangePending; }; +bool +MmsGooseControlBlock_getGoEna(MmsGooseControlBlock self) +{ + bool retVal = false; + + if (self->mmsValue) { + MmsValue* goEnaValue = MmsValue_getElement(self->mmsValue, 0); + + if (goEnaValue) { + retVal = MmsValue_getBoolean(goEnaValue); + } + } + + return retVal; +} + + +int +MmsGooseControlBlock_getMinTime(MmsGooseControlBlock self) +{ + int retVal = -1; + + if (self->mmsValue) { + MmsValue* minTimeValue = MmsValue_getElement(self->mmsValue, 6); + + if (minTimeValue) { + retVal = MmsValue_toInt32(minTimeValue); + } + } + + return retVal; +} + +int +MmsGooseControlBlock_getMaxTime(MmsGooseControlBlock self) +{ + int retVal = -1; + + if (self->mmsValue) { + MmsValue* maxTimeValue = MmsValue_getElement(self->mmsValue, 7); + + if (maxTimeValue) { + retVal = MmsValue_toInt32(maxTimeValue); + } + } + + return retVal; +} + +bool +MmsGooseControlBlock_getFixedOffs(MmsGooseControlBlock self) +{ + bool retVal = false; + + if (self->mmsValue) { + MmsValue* fixedOffsValue = MmsValue_getElement(self->mmsValue, 8); + + if (fixedOffsValue) { + retVal = MmsValue_getBoolean(fixedOffsValue); + } + } + + return retVal; +} + #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) static void @@ -264,7 +329,7 @@ MmsGooseControlBlock_isEnabled(MmsGooseControlBlock self) } void -MmsGooseControlBlock_enable(MmsGooseControlBlock self) +MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->publisherMutex); @@ -326,58 +391,65 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self) memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6); - if (self->gooseInterfaceId) - self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag); - else - self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag); + if (mmsMapping->useIntegratedPublisher) { - if (self->publisher) { - self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6)); - self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7)); + if (self->gooseInterfaceId) + self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag); + else + self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag); - GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3); + if (self->publisher) { + self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6)); + self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7)); - GoosePublisher_setDataSetRef(self->publisher, self->dataSetRef); + GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3); - GoosePublisher_setGoCbRef(self->publisher, self->goCBRef); + GoosePublisher_setDataSetRef(self->publisher, self->dataSetRef); - uint32_t confRev = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 3)); + GoosePublisher_setGoCbRef(self->publisher, self->goCBRef); - GoosePublisher_setConfRev(self->publisher, confRev); + uint32_t confRev = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 3)); - bool needsCom = MmsValue_getBoolean(MmsValue_getElement(self->mmsValue, 4)); + GoosePublisher_setConfRev(self->publisher, confRev); - GoosePublisher_setNeedsCommission(self->publisher, needsCom); + bool needsCom = MmsValue_getBoolean(MmsValue_getElement(self->mmsValue, 4)); - if (self->goId != NULL) - GoosePublisher_setGoID(self->publisher, self->goId); + GoosePublisher_setNeedsCommission(self->publisher, needsCom); - /* prepare data set values */ - self->dataSetValues = LinkedList_create(); + if (self->goId != NULL) + GoosePublisher_setGoID(self->publisher, self->goId); - DataSetEntry* dataSetEntry = self->dataSet->fcdas; + /* prepare data set values */ + self->dataSetValues = LinkedList_create(); - while (dataSetEntry != NULL) { - LinkedList_add(self->dataSetValues, dataSetEntry->value); - dataSetEntry = dataSetEntry->sibling; + DataSetEntry* dataSetEntry = self->dataSet->fcdas; + + while (dataSetEntry != NULL) { + LinkedList_add(self->dataSetValues, dataSetEntry->value); + dataSetEntry = dataSetEntry->sibling; + } + + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: Failed to create GOOSE publisher!\n"); } + } - self->goEna = true; + self->goEna = true; #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) - MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; - copyGCBValuesToTrackingObject(self); - updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, retVal); + MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; + copyGCBValuesToTrackingObject(self); + updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, retVal); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ - } - else { - if (DEBUG_IED_SERVER) - printf("IED_SERVER: Failed to create GOOSE publisher!\n"); - } } } + else { + printf("GoCB already enabled!\n"); + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->publisherMutex); @@ -385,7 +457,7 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self) } void -MmsGooseControlBlock_disable(MmsGooseControlBlock self) +MmsGooseControlBlock_disable(MmsGooseControlBlock self, MmsMapping* mmsMapping) { if (MmsGooseControlBlock_isEnabled(self)) { MmsValue* goEna = MmsValue_getElement(self->mmsValue, 0); @@ -398,11 +470,13 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self) Semaphore_wait(self->publisherMutex); #endif - if (self->publisher != NULL) { - GoosePublisher_destroy(self->publisher); - self->publisher = NULL; - LinkedList_destroyStatic(self->dataSetValues); - self->dataSetValues = NULL; + if (mmsMapping->useIntegratedPublisher) { + if (self->publisher != NULL) { + GoosePublisher_destroy(self->publisher); + self->publisher = NULL; + LinkedList_destroyStatic(self->dataSetValues); + self->dataSetValues = NULL; + } } #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -602,13 +676,15 @@ createDataSetReference(char* domainName, char* lnName, char* dataSetName) void GOOSE_sendPendingEvents(MmsMapping* self) { - LinkedList element = self->gseControls; + if (self->useIntegratedPublisher) { + LinkedList element = self->gseControls; - while ((element = LinkedList_getNext(element)) != NULL) { - MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; + while ((element = LinkedList_getNext(element)) != NULL) { + MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; - if (MmsGooseControlBlock_isEnabled(gcb)) { - MmsGooseControlBlock_publishNewState(gcb); + if (MmsGooseControlBlock_isEnabled(gcb)) { + MmsGooseControlBlock_publishNewState(gcb); + } } } } diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 0830c1e9..2d533be6 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -715,7 +715,6 @@ MmsMapping_initializeControlObjects(MmsMapping* self) void MmsMapping_configureSettingGroups(MmsMapping* self) { - LinkedList settingGroupElement = LinkedList_getNext(self->settingGroups); while (settingGroupElement != NULL) { @@ -747,6 +746,12 @@ MmsMapping_configureSettingGroups(MmsMapping* self) } } +void +MmsMapping_useIntegratedGoosePublisher(MmsMapping* self, bool enable) +{ + self->useIntegratedPublisher = enable; +} + void MmsMapping_setSgChangedHandler(MmsMapping* self, SettingGroupControlBlock* sgcb, ActiveSettingGroupChangedHandler handler, void* parameter) @@ -1958,8 +1963,13 @@ MmsMapping_create(IedModel* model, IedServer iedServer) #endif #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) + self->useIntegratedPublisher = true; + self->gseControls = LinkedList_create(); self->gooseInterfaceId = NULL; + + self->goCbHandler = NULL; + self->goCbHandlerParameter = NULL; #endif #if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) @@ -2277,10 +2287,18 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable if (MmsValue_getType(value) != MMS_BOOLEAN) return DATA_ACCESS_ERROR_TYPE_INCONSISTENT; - if (MmsValue_getBoolean(value)) - MmsGooseControlBlock_enable(mmsGCB); - else - MmsGooseControlBlock_disable(mmsGCB); + if (MmsValue_getBoolean(value)) { + MmsGooseControlBlock_enable(mmsGCB, self); + + if (self->goCbHandler) + self->goCbHandler(mmsGCB, IEC61850_GOCB_EVENT_ENABLE, self->goCbHandlerParameter); + } + else { + MmsGooseControlBlock_disable(mmsGCB, self); + + if (self->goCbHandler) + self->goCbHandler(mmsGCB, IEC61850_GOCB_EVENT_ENABLE, self->goCbHandlerParameter); + } return DATA_ACCESS_ERROR_SUCCESS; } @@ -3635,7 +3653,7 @@ MmsMapping_enableGoosePublishing(MmsMapping* self) while ((element = LinkedList_getNext(element)) != NULL) { MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; - MmsGooseControlBlock_enable(gcb); + MmsGooseControlBlock_enable(gcb, self); } } @@ -3686,7 +3704,7 @@ MmsMapping_disableGoosePublishing(MmsMapping* self) while ((element = LinkedList_getNext(element)) != NULL) { MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; - MmsGooseControlBlock_disable(gcb); + MmsGooseControlBlock_disable(gcb, self); } } @@ -3732,7 +3750,8 @@ processPeriodicTasks(MmsMapping* self) uint64_t currentTimeInMs = Hal_getTimeInMs(); #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) - GOOSE_processGooseEvents(self, currentTimeInMs); + if (self->useIntegratedPublisher) + GOOSE_processGooseEvents(self, currentTimeInMs); #endif #if (CONFIG_IEC61850_CONTROL_SERVICE == 1)