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/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 80a71101..406fa07a 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1964,6 +1964,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 f0752bbc..3108b1b8 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -288,7 +288,10 @@ struct sMmsMapping { uint64_t nextControlTimeout; /* next monotonic time timeout in one of the control state machines (e.g. for select timeout) */ uint64_t nextRealTimeControlTimeout; /* next real time clock based timeout in one of the control state machines (e.g. for operTm)*/ - 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/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 7496c495..e83c8eac 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -954,7 +954,6 @@ IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress) MmsServer_setLocalIpAddress(self->mmsServer, self->localIpAddress); } - void IedServer_startThreadless(IedServer self, int tcpPort) { @@ -1820,6 +1819,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) { diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index b6415dce..378f9087 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2808,6 +2808,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,6 +3339,70 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, const char* variableId, int writeHandlerListElement = LinkedList_getNext(writeHandlerListElement); } + /* Call global write access handler */ + if (self->writeAccessHandler && (handlerFound == false)) + { + /* 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; + + StringUtils_replace(daRef, '$', '.'); + + ModelNode* da = ModelNode_getChild(ln, daRef); + + if (da) + { + if (arrayIdx != -1) + { + da = ModelNode_getChildWithIdx(da, arrayIdx); + + if (da && componentId) + { + char compIdBuf[65]; + + StringUtils_copyStringMax(compIdBuf, sizeof(compIdBuf), componentId); + + StringUtils_replace(compIdBuf, '$', '.'); + + da = ModelNode_getChild(da, compIdBuf); + } + } + + MmsDataAccessError handlerResult = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + if (da) + { + ClientConnection clientConnection = + private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (da->modelType == DataAttributeModelType) + { + handlerResult = + self->writeAccessHandler((DataAttribute*)da, value, clientConnection, self->writeAccessHandlerParam); + } + else if (da->modelType == DataObjectModelType) + { + handlerResult = getWriteAccessResultForFCDO(self, (DataObject*)da, fc, value, clientConnection); + } + + return handlerResult; + } + } + } + } + } + /* DENY access if no handler is found and default policy is DENY */ if (!handlerFound) { @@ -3318,6 +3447,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)