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)
{