From 0ce4838ca284fadc3ee14f031e657b46f60d9888 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 21 Aug 2025 19:24:11 +0100 Subject: [PATCH 1/5] - IedServer: added global write access handler (LIB61850-528) --- src/iec61850/inc/iec61850_server.h | 3 + src/iec61850/inc_private/mms_mapping.h | 3 + .../inc_private/mms_mapping_internal.h | 5 +- src/iec61850/server/mms_mapping/mms_mapping.c | 94 +++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index cacbc524..29b615a1 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1955,6 +1955,9 @@ IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* da LIB61850_API void IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter); +LIB61850_API void +IedServer_handleWriteAccessGlobally(IedServer self, WriteAccessHandler handler, void* parameter); + typedef enum { ACCESS_POLICY_ALLOW, ACCESS_POLICY_DENY diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index 340a2d81..d2d2d831 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -142,6 +142,9 @@ MmsMapping_setConnectionIndicationHandler(MmsMapping* self, IedConnectionIndicat LIB61850_INTERNAL void MmsMapping_setLogStorage(MmsMapping* self, const char* logRef, LogStorage logStorage); +LIB61850_INTERNAL void +MmsMapping_installGlobalWriteAccessHandler(MmsMapping* self, WriteAccessHandler handler, void* parameter); + LIB61850_INTERNAL void MmsMapping_installWriteAccessHandler(MmsMapping* self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter); diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 1830b87c..6c3aa355 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -287,7 +287,10 @@ struct sMmsMapping { LinkedList controlObjects; uint64_t nextControlTimeout; /* next timeout in one of the control state machines */ - LinkedList attributeAccessHandlers; + LinkedList attributeAccessHandlers; /* write access handlers for individual data attribtues */ + + WriteAccessHandler writeAccessHandler; /* global write access handler */ + void* writeAccessHandlerParam; #if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) ReadAccessHandler readAccessHandler; diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index cca93cb7..896f47e2 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3273,6 +3273,93 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int writeHandlerListElement = LinkedList_getNext(writeHandlerListElement); } + /* Call global write access handler */ + if (self->writeAccessHandler) + { + /* lookup data attribute */ + IedModel* model = self->iedServer->model; + + ModelNode* ld = IedModel_getModelNodeByObjectReference(model, domain->domainName); + + if (ld) + { + *separator = 0; + + ModelNode* ln = ModelNode_getChild(ld, variableId); + + if (ln) + { + char* daRef = separator + 4; + + /* replace "$" with "."*/ + + StringUtils_replace(daRef, '$', '.'); + + ModelNode* da = ModelNode_getChild(ln, daRef); + + if (da) + { + if (arrayIdx != -1) + { + da = ModelNode_getChildWithIdx(da, arrayIdx); + + if (da == NULL) + { + printf("array idx not found\n"); + } + + if (componentId) + { + StringUtils_replace(componentId, '$', '.'); + + da = ModelNode_getChild(da, componentId); + + if (da == NULL) + { + printf("component %s not found\n", componentId); + } + } + } + + if (da) + { + if (da->modelType != DataAttributeModelType) + { + printf("model node not a data attribute!\n"); + } + else + { + ClientConnection clientConnection = + private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + MmsDataAccessError handlerResult = + self->writeAccessHandler((DataAttribute*)da, value, clientConnection, self->writeAccessHandlerParam); + + if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || + (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) + { + handlerFound = true; + + if (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE) + updateValue = false; + } + else + return handlerResult; + } + } + } + else + { + printf("da %s no found\n", daRef); + } + } + else + { + printf("ln %s not found\n", variableId); + } + } + } + /* DENY access if no handler is found and default policy is DENY */ if (!handlerFound) { @@ -3317,6 +3404,13 @@ getAccessHandlerForAttribute(MmsMapping* self, DataAttribute* dataAttribute) return NULL; } +void +MmsMapping_installGlobalWriteAccessHandler(MmsMapping* self, WriteAccessHandler handler, void* parameter) +{ + self->writeAccessHandler = handler; + self->writeAccessHandlerParam = parameter; +} + void MmsMapping_installWriteAccessHandler(MmsMapping* self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter) From 2a4ca1eab065662cbec8f771f8b25e97b6f4d84c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 21 Aug 2025 19:33:04 +0100 Subject: [PATCH 2/5] - added file missing from last commit (LIB61850-528) --- src/iec61850/server/impl/ied_server.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 9b00d334..b88d284c 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1812,6 +1812,15 @@ IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPo } } +void +IedServer_handleWriteAccessGlobally(IedServer self, WriteAccessHandler handler, void* parameter) +{ + if (self && self->mmsMapping) + { + MmsMapping_installGlobalWriteAccessHandler(self->mmsMapping, handler, parameter); + } +} + void IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter) { From 12493e11949ff3b4abe8fa17f65c6d834cf961bc Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 22 Aug 2025 11:27:20 +0100 Subject: [PATCH 3/5] - removed debug code (LIB61850-528) - added example for global write access handler (LIB61850-528) --- .../server_example_ca.c | 42 ++++++++++++++----- src/iec61850/server/mms_mapping/mms_mapping.c | 32 +++----------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/examples/server_example_complex_array/server_example_ca.c b/examples/server_example_complex_array/server_example_ca.c index cf3f86dc..ac76a13c 100644 --- a/examples/server_example_complex_array/server_example_ca.c +++ b/examples/server_example_complex_array/server_example_ca.c @@ -27,32 +27,46 @@ updateCMVArrayElement(IedServer server, DataObject* phsAHar, int idx, float magn { DataObject* phsAHarArrayElem = (DataObject*)ModelNode_getChildWithIdx((ModelNode*)phsAHar, idx); - if (phsAHarArrayElem) { - + if (phsAHarArrayElem) + { DataAttribute* mag = (DataAttribute*)ModelNode_getChild((ModelNode*)phsAHarArrayElem, "cVal.mag.f"); DataAttribute* ang = (DataAttribute*)ModelNode_getChild((ModelNode*)phsAHarArrayElem, "cVal.ang.f"); DataAttribute* q = (DataAttribute*)ModelNode_getChild((ModelNode*)phsAHarArrayElem, "q"); DataAttribute* t = (DataAttribute*)ModelNode_getChild((ModelNode*)phsAHarArrayElem, "t"); - if (mag && ang && q && t) { + if (mag && ang && q && t) + { IedServer_updateQuality(server, q, quality); IedServer_updateTimestampAttributeValue(server, t, ×tamp); IedServer_updateFloatAttributeValue(server, mag, magnitude); IedServer_updateFloatAttributeValue(server, ang, angle); } - else { + else + { printf("one of mag, ang, q, t not found\n"); } } - else { + else + { printf("Element with index %i not found\n", idx); } } +static MmsDataAccessError +writeAccessHandler(DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter) +{ + char objRef[200]; + char valueBuf[200]; + + ModelNode_getObjectReference((ModelNode*)dataAttribute, objRef); + printf("Write access - %s: %s\n", objRef, MmsValue_printToBuffer(value, valueBuf, sizeof(valueBuf))); + + return DATA_ACCESS_ERROR_SUCCESS; +} + int main(int argc, char **argv) { - int tcpPort = 102; if (argc > 1) { @@ -61,6 +75,8 @@ main(int argc, char **argv) IedServer iedServer = IedServer_create(&iedModel); + IedServer_handleWriteAccessGlobally(iedServer, writeAccessHandler, NULL); + /* Get access to the MHAI1.HA data object handle - for static and dynamic model*/ DataObject* mhai1_ha_phsAHar = (DataObject*) IedModel_getModelNodeByShortObjectReference(&iedModel, "ComplexArray/MHAI1.HA.phsAHar"); @@ -75,7 +91,8 @@ main(int argc, char **argv) Timestamp_setTimeInMilliseconds(×tamp, Hal_getTimeInMs()); int i; - for (i = 0; i < 16; i++) { + for (i = 0; i < 16; i++) + { updateCMVArrayElement(iedServer, mhai1_ha_phsAHar, i, mag, angle, quality, timestamp); mag += 1.f; angle += 0.01f; @@ -84,7 +101,8 @@ main(int argc, char **argv) /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, tcpPort); - if (!IedServer_isRunning(iedServer)) { + if (!IedServer_isRunning(iedServer)) + { printf("Starting server failed! Exit.\n"); IedServer_destroy(iedServer); exit(-1); @@ -96,14 +114,16 @@ main(int argc, char **argv) int counter = 0; - while (running) { + while (running) + { Thread_sleep(1000); Timestamp_setTimeInMilliseconds(×tamp, Hal_getTimeInMs()); IedServer_lockDataModel(iedServer); - for (i = 0; i < 16; i++) { + for (i = 0; i < 16; i++) + { updateCMVArrayElement(iedServer, mhai1_ha_phsAHar, i, mag, angle, quality, timestamp); mag += 0.1f; angle += 0.05f; @@ -126,4 +146,4 @@ main(int argc, char **argv) IedServer_destroy(iedServer); return 0; -} /* main() */ +} diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 896f47e2..79e8b9d2 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3291,8 +3291,6 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int { char* daRef = separator + 4; - /* replace "$" with "."*/ - StringUtils_replace(daRef, '$', '.'); ModelNode* da = ModelNode_getChild(ln, daRef); @@ -3303,31 +3301,21 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int { da = ModelNode_getChildWithIdx(da, arrayIdx); - if (da == NULL) + if (da && componentId) { - printf("array idx not found\n"); - } + char compIdBuf[65]; - if (componentId) - { - StringUtils_replace(componentId, '$', '.'); + StringUtils_copyStringMax(compIdBuf, sizeof(compIdBuf), componentId); - da = ModelNode_getChild(da, componentId); + StringUtils_replace(compIdBuf, '$', '.'); - if (da == NULL) - { - printf("component %s not found\n", componentId); - } + da = ModelNode_getChild(da, compIdBuf); } } if (da) { - if (da->modelType != DataAttributeModelType) - { - printf("model node not a data attribute!\n"); - } - else + if (da->modelType == DataAttributeModelType) { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -3348,14 +3336,6 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int } } } - else - { - printf("da %s no found\n", daRef); - } - } - else - { - printf("ln %s not found\n", variableId); } } } From 37ce6374bbcd63b2668ba8760ad615896ccdfcfe Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 28 Aug 2025 17:08:53 +0100 Subject: [PATCH 4/5] - removed empty line --- src/iec61850/server/impl/ied_server.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index b88d284c..e071d39b 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -948,7 +948,6 @@ IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress) MmsServer_setLocalIpAddress(self->mmsServer, self->localIpAddress); } - void IedServer_startThreadless(IedServer self, int tcpPort) { From 3db03b41b932cc4baff64647257678b3982bf9db Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 1 Sep 2025 18:00:25 +0100 Subject: [PATCH 5/5] - IED server: allow write access to FCD with global write access handler (LIB61850-531) --- src/iec61850/server/mms_mapping/mms_mapping.c | 94 +++++++++++++++---- 1 file changed, 78 insertions(+), 16 deletions(-) diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 79e8b9d2..ee8cd263 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2807,6 +2807,71 @@ getAccessPolicyForFC(MmsMapping* self, FunctionalConstraint fc) return ACCESS_POLICY_DENY; } +static MmsDataAccessError +getWriteAccessResultForFCDO(MmsMapping* self, DataObject* dataObject, FunctionalConstraint fc, MmsValue* value, ClientConnection clientConnection) +{ + MmsDataAccessError result = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + ModelNode* child = dataObject->firstChild; + + int idx = 0; + + while (child) + { + if (child->modelType == DataAttributeModelType) + { + DataAttribute* da = (DataAttribute*)child; + + if (da->fc == fc) + { + MmsValue* subValue = MmsValue_getElement(value, idx); + + if (subValue) + { + if (self->writeAccessHandler) + { + result = self->writeAccessHandler(da, subValue, clientConnection, self->writeAccessHandlerParam); + + if (result == DATA_ACCESS_ERROR_SUCCESS) + { + IedServer_updateAttributeValue(self->iedServer, da, subValue); + } + + if ((result != DATA_ACCESS_ERROR_SUCCESS) && (result != DATA_ACCESS_ERROR_SUCCESS)) + { + return result; + } + } + } + + idx++; + } + } + else if (child->modelType == DataObjectModelType) + { + DataObject* subDataObject = (DataObject*)child; + + if (DataObject_hasFCData(subDataObject, fc)) + { + MmsValue* subValue = MmsValue_getElement(value, idx); + + result = getWriteAccessResultForFCDO(self, subDataObject, fc, subValue, clientConnection); + + if ((result != DATA_ACCESS_ERROR_SUCCESS) && (result != DATA_ACCESS_ERROR_SUCCESS)) + { + return result; + } + + idx++; + } + } + + child = child->sibling; + } + + return result; +} + static MmsDataAccessError mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int arrayIdx, const char* componentId, MmsValue* value, MmsServerConnection connection) @@ -3274,7 +3339,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int } /* Call global write access handler */ - if (self->writeAccessHandler) + if (self->writeAccessHandler && (handlerFound == false)) { /* lookup data attribute */ IedModel* model = self->iedServer->model; @@ -3313,27 +3378,24 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int } } + MmsDataAccessError handlerResult = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + if (da) { + ClientConnection clientConnection = + private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + if (da->modelType == DataAttributeModelType) { - ClientConnection clientConnection = - private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - - MmsDataAccessError handlerResult = + handlerResult = self->writeAccessHandler((DataAttribute*)da, value, clientConnection, self->writeAccessHandlerParam); - - if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || - (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) - { - handlerFound = true; - - if (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE) - updateValue = false; - } - else - return handlerResult; } + else if (da->modelType == DataObjectModelType) + { + handlerResult = getWriteAccessResultForFCDO(self, (DataObject*)da, fc, value, clientConnection); + } + + return handlerResult; } } }