From 4aaced263951ca7e223adfbcad7d62f2f26484b1 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 6 Mar 2020 18:53:18 +0100 Subject: [PATCH 1/9] - IEC 61850 server: unselect control when oper is not accepted - IEC 61850 server: send select-failed for control when origin parameter is not valid --- src/iec61850/server/mms_mapping/control.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index 169a5b00..19b7185a 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -1435,6 +1435,13 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari if (checkValidityOfOriginParameter(origin) == false) { indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + + ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR, + ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true); + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: SBOw - invalid origin value\n"); + goto free_and_return; } @@ -1571,6 +1578,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, ctlNum, origin, true); + unselectObject(controlObject); + goto free_and_return; } } @@ -1654,6 +1663,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari setOpRcvd(controlObject, false); abortControlOperation(controlObject); + + if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) + unselectObject(controlObject); } } From 3fd4fed886f7ac410ed9e99318a520bf6ddd1207 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 7 Mar 2020 10:40:42 +0100 Subject: [PATCH 2/9] - .NET API: fixed memory management issue in MmsValue.SetElement (see #213) --- dotnet/IEC61850forCSharp/MmsValue.cs | 77 +++++++++++++++++----------- dotnet/tests/Test.cs | 44 ++++++++++++++-- 2 files changed, 87 insertions(+), 34 deletions(-) diff --git a/dotnet/IEC61850forCSharp/MmsValue.cs b/dotnet/IEC61850forCSharp/MmsValue.cs index 4e883fa5..5d6a90c7 100644 --- a/dotnet/IEC61850forCSharp/MmsValue.cs +++ b/dotnet/IEC61850forCSharp/MmsValue.cs @@ -533,43 +533,60 @@ namespace IEC61850 throw new MmsValueException ("Value is of wrong type"); } - /// - /// Sets the element of an array of structure - /// - /// index of the element starting with 0 - /// MmsValue instance that will be used as element value - /// This exception is thrown if the value has the wrong type. - /// This exception is thrown if the index is out of range. - public void SetElement(int index, MmsValue elementValue) - { - MmsType elementType = GetType (); + /// + /// Sets the element of an array or structure + /// + /// + /// After calling this function the native memory of the element will be managed by the array or structure. + /// Therefore an element can only be used in a single array or structure. + /// When the value is required in multiple arrays or structures + /// a clone has to be created before using this function! + /// To be save, always use a clone for setting the element. + /// + /// index of the element starting with 0 + /// MmsValue instance that will be used as element value + /// This exception is thrown if the value has the wrong type. + /// This exception is thrown if the index is out of range. + public void SetElement(int index, MmsValue elementValue) + { + MmsType elementType = GetType(); + + if ((elementType == MmsType.MMS_ARRAY) || (elementType == MmsType.MMS_STRUCTURE)) + { + + if ((index >= 0) && (index < Size())) + { - if ((elementType == MmsType.MMS_ARRAY) || (elementType == MmsType.MMS_STRUCTURE)) { - - if ((index >= 0) && (index < Size ())) { if (elementValue != null) - MmsValue_setElement (valueReference, index, elementValue.valueReference); + { + MmsValue_setElement(valueReference, index, elementValue.valueReference); + + /* will be deleted by structure */ + elementValue.responsableForDeletion = false; + } else - MmsValue_setElement (valueReference, index, IntPtr.Zero); + MmsValue_setElement(valueReference, index, IntPtr.Zero); - } else - throw new MmsValueException ("Index out of bounds"); - - } else - throw new MmsValueException ("Value is of wrong type"); + } + else + throw new MmsValueException("Index out of bounds"); - } + } + else + throw new MmsValueException("Value is of wrong type"); + } - public MmsDataAccessError GetDataAccessError () - { - if (GetType () == MmsType.MMS_DATA_ACCESS_ERROR) { - int errorCode = MmsValue_getDataAccessError (valueReference); + public MmsDataAccessError GetDataAccessError() + { + if (GetType() == MmsType.MMS_DATA_ACCESS_ERROR) + { + int errorCode = MmsValue_getDataAccessError(valueReference); - return (MmsDataAccessError)errorCode; - } - else - throw new MmsValueException ("Value is of wrong type"); - } + return (MmsDataAccessError)errorCode; + } + else + throw new MmsValueException("Value is of wrong type"); + } /// /// Gets the timestamp value as UTC time in s (UNIX time stamp). diff --git a/dotnet/tests/Test.cs b/dotnet/tests/Test.cs index a835d92b..8b945f7d 100644 --- a/dotnet/tests/Test.cs +++ b/dotnet/tests/Test.cs @@ -294,9 +294,6 @@ namespace tests { IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("../../model.cfg"); - - - ModelNode ind1 = iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.Ind1.stVal"); Assert.IsTrue (ind1.GetType ().Equals (typeof(IEC61850.Server.DataAttribute))); @@ -556,6 +553,45 @@ namespace tests Assert.AreEqual (Validity.QUESTIONABLE, q.Validity); } - } + + [Test()] + public void MmsValaueCreateStructureAndAddElement() + { + MmsValue structure1 = MmsValue.NewEmptyStructure(1); + MmsValue structure2 = MmsValue.NewEmptyStructure(1); + MmsValue element = MmsValue.NewEmptyStructure(1); + + structure1.SetElement(0, element); + + /* Clone is required when adding the value to another structure or element */ + MmsValue elementClone = element.Clone(); + structure2.SetElement(0, elementClone); + + element.Dispose(); + + structure1.Dispose(); + structure2.Dispose(); + + Assert.AreEqual(true, true); + } + + [Test()] + public void MmsValueClone() + { + MmsValue boolValue = new MmsValue(true); + + MmsValue boolClone = boolValue.Clone(); + + boolValue.Dispose(); + boolClone.Dispose(); + + MmsValue structure = MmsValue.NewEmptyStructure(1); + MmsValue structureClone = structure.Clone(); + + structure.Dispose(); + structureClone.Dispose(); + } + + } } From 800c76c50e2bf29f76c93926ccf03202c0c0227b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 7 Mar 2020 11:08:04 +0100 Subject: [PATCH 3/9] - update cmake files --- CMakeLists.txt | 22 ++++++++++++++++++++++ src/CMakeLists.txt | 21 --------------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18dbf549..956464e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,3 +169,25 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") include(CPack) endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + +set(BINDIR "bin") +set(LIBDIR "lib") +if(UNIX) + # GNUInstallDirs is required for Debian multiarch + include(GNUInstallDirs) + set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) + set(BINDIR ${CMAKE_INSTALL_BINDIR}) + + configure_file( + ${CMAKE_CURRENT_LIST_DIR}/src/libiec61850.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/src/libiec61850.pc @ONLY + ) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/src/libiec61850.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig") +endif() + +install (TARGETS iec61850 iec61850-shared hal hal-shared + RUNTIME DESTINATION ${BINDIR} COMPONENT Applications + ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries + LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries +) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4cd9022c..82d62dc8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -345,22 +345,6 @@ target_link_libraries(iec61850-shared ) ENDIF(WITH_WPCAP) - -set(BINDIR "bin") -set(LIBDIR "lib") -if(UNIX) - # GNUInstallDirs is required for Debian multiarch - include(GNUInstallDirs) - set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) - set(BINDIR ${CMAKE_INSTALL_BINDIR}) - - configure_file( - ${CMAKE_CURRENT_LIST_DIR}/libiec61850.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc @ONLY - ) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig") -endif() - find_package(Doxygen) if(DOXYGEN_FOUND) configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) @@ -371,8 +355,3 @@ if(DOXYGEN_FOUND) endif(DOXYGEN_FOUND) -install (TARGETS iec61850 iec61850-shared hal hal-shared - RUNTIME DESTINATION ${BINDIR} COMPONENT Applications - ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries - LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries -) From 922f3055b4a35a61dba66649eec7467f7b7828ff Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 7 Mar 2020 11:42:46 +0100 Subject: [PATCH 4/9] - updated cmake files to work with cmake 2.8 --- CMakeLists.txt | 22 +--------------------- hal/CMakeLists.txt | 15 +++++++++++++++ src/CMakeLists.txt | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 956464e3..85caf202 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 2.8.12) # automagically detect if we should cross-compile if(DEFINED ENV{TOOLCHAIN}) @@ -170,24 +170,4 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") include(CPack) endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") -set(BINDIR "bin") -set(LIBDIR "lib") -if(UNIX) - # GNUInstallDirs is required for Debian multiarch - include(GNUInstallDirs) - set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) - set(BINDIR ${CMAKE_INSTALL_BINDIR}) - - configure_file( - ${CMAKE_CURRENT_LIST_DIR}/src/libiec61850.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/src/libiec61850.pc @ONLY - ) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/src/libiec61850.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig") -endif() - -install (TARGETS iec61850 iec61850-shared hal hal-shared - RUNTIME DESTINATION ${BINDIR} COMPONENT Applications - ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries - LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries -) diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt index bdd1a9c6..65e8e404 100644 --- a/hal/CMakeLists.txt +++ b/hal/CMakeLists.txt @@ -164,3 +164,18 @@ target_link_libraries(hal ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/packet.lib ) ENDIF(WITH_WPCAP) + +set(BINDIR "bin") +set(LIBDIR "lib") +if(UNIX) + # GNUInstallDirs is required for Debian multiarch + include(GNUInstallDirs) + set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) + set(BINDIR ${CMAKE_INSTALL_BINDIR}) +endif() + +install (TARGETS hal hal-shared + RUNTIME DESTINATION ${BINDIR} COMPONENT Applications + ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries + LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries +) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 82d62dc8..ff2ee08b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -355,3 +355,24 @@ if(DOXYGEN_FOUND) endif(DOXYGEN_FOUND) +set(BINDIR "bin") +set(LIBDIR "lib") +if(UNIX) + # GNUInstallDirs is required for Debian multiarch + include(GNUInstallDirs) + set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) + set(BINDIR ${CMAKE_INSTALL_BINDIR}) + + configure_file( + ${CMAKE_CURRENT_LIST_DIR}/libiec61850.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc @ONLY + ) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig") +endif() + +install (TARGETS iec61850 iec61850-shared + RUNTIME DESTINATION ${BINDIR} COMPONENT Applications + ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries + LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries +) + From 2dba040c1aa1844f13cf375dace9a33c193efacd Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 7 Mar 2020 12:47:25 +0100 Subject: [PATCH 5/9] - CDC helper functions: added stSeld and attributes from ControlTestingCDC to control CDCs --- src/iec61850/server/model/cdc.c | 64 +++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c index 9b5038b2..a5293970 100644 --- a/src/iec61850/server/model/cdc.c +++ b/src/iec61850/server/model/cdc.c @@ -554,6 +554,19 @@ addOriginatorAndCtlNumOptions(ModelNode* parent, uint32_t controlOptions) DataAttribute_create("ctlNum", parent, IEC61850_INT8U, IEC61850_FC_ST, 0, 0, 0); } +static void +addCommonControlAttributes(DataObject* dobj, uint32_t controlOptions) +{ + if (controlOptions & CDC_CTL_OPTION_OP_RCVD) + DataAttribute_create("opRcvd", (ModelNode*) dobj, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0); + + if (controlOptions & CDC_CTL_OPTION_OP_OK) + DataAttribute_create("opOk", (ModelNode*) dobj, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0); + + if (controlOptions & CDC_CTL_OPTION_T_OP_OK) + DataAttribute_create("tOpOk", (ModelNode*) dobj, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0); +} + /** * * CDC_OPTION_IS_TIME_ACTICATED @@ -574,6 +587,11 @@ CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, addControls(newSPC, IEC61850_BOOLEAN, controlOptions); + if (controlOptions & CDC_CTL_OPTION_ST_SELD) + DataAttribute_create("stSeld", (ModelNode*) newSPC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); + + addCommonControlAttributes(newSPC, controlOptions); + if (options & CDC_OPTION_PICS_SUBST) CDC_addOptionPicsSubst(newSPC, IEC61850_BOOLEAN); @@ -605,6 +623,11 @@ CDC_DPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, addControls(newDPC, IEC61850_BOOLEAN, controlOptions); + if (controlOptions & CDC_CTL_OPTION_ST_SELD) + DataAttribute_create("stSeld", (ModelNode*) newDPC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); + + addCommonControlAttributes(newDPC, controlOptions); + if (options & CDC_OPTION_PICS_SUBST) CDC_addOptionPicsSubst(newDPC, IEC61850_CODEDENUM); @@ -661,16 +684,22 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot } } +static void +addControlStatusAttributesForAnalogControl(DataObject* dobj, uint32_t controlOptions) +{ + if (controlOptions & CDC_CTL_OPTION_ORIGIN) + addOriginator("origin", (ModelNode*) dobj, IEC61850_FC_MX); + + if (controlOptions & CDC_CTL_OPTION_CTL_NUM) + DataAttribute_create("ctlNum", (ModelNode*) dobj, IEC61850_INT8U, IEC61850_FC_MX, 0, 0, 0); +} + DataObject* CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) { DataObject* newAPC = DataObject_create(dataObjectName, parent, 0); - if (controlOptions & CDC_CTL_OPTION_ORIGIN) - addOriginator("origin", (ModelNode*) newAPC, IEC61850_FC_MX); - - if (controlOptions & CDC_CTL_OPTION_CTL_NUM) - DataAttribute_create("ctlNum", (ModelNode*) newAPC, IEC61850_INT8U, IEC61850_FC_MX, 0, 0, 0); + addControlStatusAttributesForAnalogControl(newAPC, controlOptions); CAC_AnalogueValue_create("mxVal", (ModelNode*) newAPC, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, isIntegerNotFloat); @@ -679,14 +708,7 @@ CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, if (controlOptions & CDC_CTL_OPTION_ST_SELD) DataAttribute_create("stSeld", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, 0, 0); - if (controlOptions & CDC_CTL_OPTION_OP_RCVD) - DataAttribute_create("opRcvd", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0); - - if (controlOptions & CDC_CTL_OPTION_OP_OK) - DataAttribute_create("opOk", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0); - - if (controlOptions & CDC_CTL_OPTION_T_OP_OK) - DataAttribute_create("tOpOk", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0); + addCommonControlAttributes(newAPC, controlOptions); if (options & CDC_OPTION_PICS_SUBST) { DataAttribute_create("subEna", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_SV, 0, 0, 0); @@ -705,7 +727,6 @@ CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, return newAPC; } - DataObject* CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { @@ -717,6 +738,11 @@ CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, addControls(newINC, IEC61850_INT32, controlOptions); + if (controlOptions & CDC_CTL_OPTION_ST_SELD) + DataAttribute_create("stSeld", (ModelNode*) newINC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); + + addCommonControlAttributes(newINC, controlOptions); + if (options & CDC_OPTION_PICS_SUBST) CDC_addOptionPicsSubst(newINC, IEC61850_INT32); @@ -748,6 +774,11 @@ CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, addControls(newENC, IEC61850_ENUMERATED, controlOptions); + if (controlOptions & CDC_CTL_OPTION_ST_SELD) + DataAttribute_create("stSeld", (ModelNode*) newENC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); + + addCommonControlAttributes(newENC, controlOptions); + if (options & CDC_OPTION_PICS_SUBST) CDC_addOptionPicsSubst(newENC, IEC61850_ENUMERATED); @@ -773,6 +804,11 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, addControls(newBSC, IEC61850_CODEDENUM, controlOptions); + if (controlOptions & CDC_CTL_OPTION_ST_SELD) + DataAttribute_create("stSeld", (ModelNode*) newBSC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); + + addCommonControlAttributes(newBSC, controlOptions); + if (options & CDC_OPTION_PICS_SUBST) CDC_addOptionPicsSubstValWithTrans(newBSC, hasTransientIndicator); From ae07c77d39c9f3e27027ff5e9f37830efaf47cc8 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 7 Mar 2020 15:05:11 +0100 Subject: [PATCH 6/9] - CDC helpers: added functions to create ISC and BAC CDCs --- src/iec61850/inc/iec61850_cdc.h | 44 +++++++++++++++++++ src/iec61850/server/model/cdc.c | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/src/iec61850/inc/iec61850_cdc.h b/src/iec61850/inc/iec61850_cdc.h index 8615af13..04a077bf 100644 --- a/src/iec61850/inc/iec61850_cdc.h +++ b/src/iec61850/inc/iec61850_cdc.h @@ -496,11 +496,33 @@ CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, LIB61850_API DataObject* CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator); +/** + * \brief Integer controlled step position information (ISC) + * + * CDC_OPTION_IS_TIME_ACTICATED + * + * substitution options + * CDC_OPTION_BLK_ENA + * standard description and namespace options + * + * \param dataObjectName the name of the new object + * \param parent the parent of the new data object (either a LogicalNode or another DataObject) + * \param options bit mask to encode required optional elements + * \param controlOptions specify which control model to set as default and other control specific options + * \param hasTransientIndicator specifies if the step position information contains the transient indicator + * + */ +LIB61850_API DataObject* +CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator); + /** * \brief Controllable analogue process value (APC) * * CDC_OPTION_IS_TIME_ACTICATED * + * CDC_OPTION_MIN + * CDC_OPTION_MAX + * * substitution options * CDC_OPTION_BLK_ENA * standard description and namespace options @@ -514,6 +536,28 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, LIB61850_API DataObject* CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat); +/** + * \brief Binary controlled ananlogue process value (BAC) + * + * CDC_OPTION_IS_TIME_ACTICATED + * + * CDC_OPTION_MIN + * CDC_OPTION_MAX + * CDC_OPTION_STEP_SIZE + * + * substitution options + * CDC_OPTION_BLK_ENA + * standard description and namespace options + * + * \param dataObjectName the name of the new object + * \param parent the parent of the new data object (either a LogicalNode or another DataObject) + * \param options bit mask to encode required optional elements + * \param controlOptions specify which control model to set as default and other control specific options + * \param isIntegerNotFloat + */ +LIB61850_API DataObject* +CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat); + /** Minimum measured value */ #define CDC_OPTION_61400_MIN_MX_VAL (1 << 10) diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c index a5293970..f3f27f11 100644 --- a/src/iec61850/server/model/cdc.c +++ b/src/iec61850/server/model/cdc.c @@ -820,6 +820,84 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, return newBSC; } +DataObject* +CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator) +{ + DataObject* newISC = DataObject_create(dataObjectName, parent, 0); + + addOriginatorAndCtlNumOptions((ModelNode*) newISC, controlOptions); + + CAC_ValWithTrans_create("valWTr", (ModelNode*) newISC, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, hasTransientIndicator); + CDC_addTimeQuality(newISC, IEC61850_FC_ST); + + addControls(newISC, IEC61850_INT8, controlOptions); + + if (controlOptions & CDC_CTL_OPTION_ST_SELD) + DataAttribute_create("stSeld", (ModelNode*) newISC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); + + addCommonControlAttributes(newISC, controlOptions); + + if (options & CDC_OPTION_PICS_SUBST) + CDC_addOptionPicsSubstValWithTrans(newISC, hasTransientIndicator); + + if (options & CDC_OPTION_BLK_ENA) + DataAttribute_create("blkEna", (ModelNode*) newISC, IEC61850_BOOLEAN, IEC61850_FC_BL, 0, 0, 0); + + if (options & CDC_OPTION_MIN) + DataAttribute_create("minVal", (ModelNode*) newISC, IEC61850_INT32, IEC61850_FC_CF, 0, 0, 0); + + if (options & CDC_OPTION_MAX) + DataAttribute_create("maxVal", (ModelNode*) newISC, IEC61850_INT32, IEC61850_FC_CF, 0, 0, 0); + + CDC_addStandardOptions(newISC, options); + + return newISC; +} + +DataObject* +CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) +{ + DataObject* newBAC = DataObject_create(dataObjectName, parent, 0); + + addControlStatusAttributesForAnalogControl(newBAC, controlOptions); + + CAC_AnalogueValue_create("mxVal", (ModelNode*) newBAC, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, isIntegerNotFloat); + + CDC_addTimeQuality(newBAC, IEC61850_FC_MX); + + if (controlOptions & CDC_CTL_OPTION_ST_SELD) + DataAttribute_create("stSeld", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, 0, 0); + + addControls(newBAC, IEC61850_INT8, controlOptions); + + if (options & CDC_OPTION_PICS_SUBST) { + DataAttribute_create("subEna", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_SV, 0, 0, 0); + CAC_AnalogueValue_create("subVal", (ModelNode*) newBAC, IEC61850_FC_SV, 0, isIntegerNotFloat); + DataAttribute_create("subQ", (ModelNode*) newBAC, IEC61850_QUALITY, IEC61850_FC_SV, 0, 0, 0); + DataAttribute_create("subID", (ModelNode*) newBAC, IEC61850_VISIBLE_STRING_64, IEC61850_FC_SV, 0, 0, 0); + } + + if (options & CDC_OPTION_BLK_ENA) + DataAttribute_create("blkEna", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_BL, 0, 0, 0); + + DataAttribute_create("persistent", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); + + addAnalogControls(newBAC, controlOptions, isIntegerNotFloat); + + if (options & CDC_OPTION_MIN) + CAC_AnalogueValue_create("minVal", (ModelNode*) newBAC, IEC61850_FC_CF, 0, isIntegerNotFloat); + + if (options & CDC_OPTION_MAX) + CAC_AnalogueValue_create("maxVal", (ModelNode*) newBAC, IEC61850_FC_CF, 0, isIntegerNotFloat); + + if (options & CDC_OPTION_STEP_SIZE) + CAC_AnalogueValue_create("stepSize", (ModelNode*) newBAC, IEC61850_FC_CF, 0, isIntegerNotFloat); + + CDC_addStandardOptions(newBAC, options); + + return newBAC; +} + DataObject* CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { From 09b23aaa745294c53828484dcfa419bbf40ce561 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 7 Mar 2020 16:05:18 +0100 Subject: [PATCH 7/9] - IEC 618580 server: Added function IedServer_setServerIdentity to set values for MMS identity service --- config/stack_config.h | 3 ++ config/stack_config.h.cmake | 3 ++ .../server_example_basic_io.c | 3 ++ src/iec61850/inc/iec61850_server.h | 13 +++++++ src/iec61850/inc_private/ied_server_private.h | 6 ++++ src/iec61850/server/impl/ied_server.c | 34 +++++++++++++++++++ 6 files changed, 62 insertions(+) diff --git a/config/stack_config.h b/config/stack_config.h index 54907198..1ecd9da9 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -164,6 +164,9 @@ /* allow user to control read access by callback */ #define CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL 1 +/* allow application to set server identity (for MMS identity service) at runtime */ +#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1 + /* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index 45963309..164e2602 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -154,6 +154,9 @@ /* allow user to control read access by callback */ #cmakedefine01 CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL +/* allow application to set server identity (for MMS identity service) at runtime */ +#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1 + /* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c index 98bcf49a..3bd87ee2 100644 --- a/examples/server_example_basic_io/server_example_basic_io.c +++ b/examples/server_example_basic_io/server_example_basic_io.c @@ -114,6 +114,9 @@ main(int argc, char** argv) /* configuration object is no longer required */ IedServerConfig_destroy(config); + /* set the identity values for MMS identify service */ + IedServer_setServerIdentity(iedServer, "MZ", "basic io", "1.4.2"); + /* Install handler for operate command */ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, (ControlHandler) controlHandlerForBinaryOutput, diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index f34e67ea..1e067ecb 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -347,6 +347,19 @@ IedServer_destroy(IedServer self); LIB61850_API void IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress); +/** + * \brief Set the identify for the MMS identify service + * + * CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY required + * + * \param self the IedServer instance + * \param vendor the IED vendor name + * \param model the IED model name + * \param revision the IED revision/version number + */ +LIB61850_API void +IedServer_setServerIdentity(IedServer self, const char* vendor, const char* model, const char* revision); + /** * \brief Set the virtual filestore basepath for the MMS file services * diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index 55271c85..e866a503 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -61,6 +61,12 @@ struct sIedServer Thread serverThread; #endif +#if (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1) + char* vendorName; + char* modelName; + char* revision; +#endif + uint8_t edition; bool running; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 2f950ac9..d7f622db 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -548,6 +548,18 @@ IedServer_destroy(IedServer self) Semaphore_destroy(self->clientConnectionsLock); #endif +#if (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1) + + if (self->vendorName) + GLOBAL_FREEMEM(self->vendorName); + + if (self->modelName) + GLOBAL_FREEMEM(self->modelName); + + if (self->revision) + GLOBAL_FREEMEM(self->revision); +#endif /* (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1) */ + GLOBAL_FREEMEM(self); } @@ -1530,6 +1542,28 @@ IedServer_setLogStorage(IedServer self, const char* logRef, LogStorage logStorag #endif } +void +IedServer_setServerIdentity(IedServer self, const char* vendor, const char* model, const char* revision) +{ +#if (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1) + + if (self->vendorName) + GLOBAL_FREEMEM(self->vendorName); + + if (self->modelName) + GLOBAL_FREEMEM(self->modelName); + + if (self->revision) + GLOBAL_FREEMEM(self->revision); + + self->vendorName = StringUtils_copyString(vendor); + self->modelName = StringUtils_copyString(model); + self->revision = StringUtils_copyString(revision); + + MmsServer_setServerIdentity(self->mmsServer, self->vendorName, self->modelName, self->revision); +#endif +} + ClientConnection private_IedServer_getClientConnectionByHandle(IedServer self, void* serverConnectionHandle) { From bfef9ce5e901432507b0100f8363f6f1fa08f84e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 7 Mar 2020 16:15:23 +0100 Subject: [PATCH 8/9] - .NET API: added IedServer:setServerIdentity method --- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 8e58b129..7faf2af1 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1098,7 +1098,10 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateQuality(IntPtr self, IntPtr dataAttribute, ushort value); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_setServerIdentity(IntPtr self, string vendor, string model, string revision); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -1372,12 +1375,27 @@ namespace IEC61850 internalConnectionHandler = null; } + /// + /// Set the identify for the MMS identify service + /// + /// the IED vendor name + /// the IED model name + /// the IED revision/version number + public void SetServerIdentity(string vendor, string model, string revision) + { + IedServer_setServerIdentity(self, vendor, model, revision); + } + + /// + /// Check if server is running (accepting client connections) + /// + /// true, if running, false otherwise. public bool IsRunning() - { - return IedServer_isRunning(self); - } + { + return IedServer_isRunning(self); + } - private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject) + private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject) { ControlHandlerInfo info; From 0ba4a70d33849960280dcea8ba301172b3c42d3e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 11 Mar 2020 10:42:46 +0100 Subject: [PATCH 9/9] - IEC 61850 server: fixed missing report timestamp update for unbuffered reporting --- src/iec61850/server/mms_mapping/reporting.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index ea73a7d8..e8341ff7 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -2104,6 +2104,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ ReportBufferEntry* entry = (ReportBufferEntry*) entryBufPos; + entry->timeOfEntry = timeOfEntry; + if (isBuffered) { /* ENTRY_ID is set to system time in ms! */ uint64_t entryId = timeOfEntry; @@ -2111,8 +2113,6 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ if (entryId <= reportControl->lastEntryId) entryId = reportControl->lastEntryId + 1; - entry->timeOfEntry = entryId; - #if (ORDER_LITTLE_ENDIAN == 1) memcpyReverseByteOrder(entry->entryId, (uint8_t*) &entryId, 8); #else