diff --git a/CMakeLists.txt b/CMakeLists.txt index 18dbf549..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}) @@ -169,3 +169,5 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") include(CPack) endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + + 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/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 64f3b564..90748455 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1114,7 +1114,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)] @@ -1388,10 +1391,25 @@ 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); + } /// /// Get number of open MMS connections @@ -1402,7 +1420,7 @@ namespace IEC61850 return IedServer_getNumberOfOpenConnections(self); } - private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject) + private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject) { ControlHandlerInfo info; 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(); + } + + } } 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/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 4cd9022c..ff2ee08b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -345,6 +345,15 @@ target_link_libraries(iec61850-shared ) ENDIF(WITH_WPCAP) +find_package(Doxygen) +if(DOXYGEN_FOUND) + configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM) + + configure_file(doxygen/Doxyfile.NET.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET @ONLY) + add_custom_target(doc-net ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM) + +endif(DOXYGEN_FOUND) set(BINDIR "bin") set(LIBDIR "lib") @@ -361,18 +370,9 @@ if(UNIX) 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) - add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM) - - configure_file(doxygen/Doxyfile.NET.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET @ONLY) - add_custom_target(doc-net ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM) - -endif(DOXYGEN_FOUND) - -install (TARGETS iec61850 iec61850-shared hal hal-shared +install (TARGETS iec61850 iec61850-shared RUNTIME DESTINATION ${BINDIR} COMPONENT Applications ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries ) + 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/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) { 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); } } 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 diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c index 9b5038b2..f3f27f11 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); @@ -784,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) {