From 0c042f2ba480583c9d4059808508d4e72a4c157d Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 30 May 2016 00:08:59 +0200 Subject: [PATCH] - added server examples logging - added logging API - added sqlite3 driver for logging --- examples/server_example_logging/Makefile | 30 + .../server_example_logging.c | 232 ++ .../simpleIO_direct_control.icd | 281 +++ .../server_example_logging/static_model.c | 2003 +++++++++++++++++ .../server_example_logging/static_model.h | 301 +++ src/iec61850/client/ied_connection.c | 4 + src/iec61850/common/iec61850_common.c | 8 + src/iec61850/inc/iec61850_common.h | 2 + src/iec61850/inc_private/logging.h | 24 + src/iec61850/server/impl/ied_server.c | 13 + src/iec61850/server/mms_mapping/logging.c | 527 +++-- src/iec61850/server/mms_mapping/mms_mapping.c | 106 +- src/logging/drivers/README | 3 + .../drivers/sqlite/log_storage_sqlite.c | 455 ++++ src/logging/logging_api.h | 2 +- tools/model_generator/genmodel.jar | Bin 84111 -> 84127 bytes .../tools/StaticModelGenerator.java | 8 +- 17 files changed, 3814 insertions(+), 185 deletions(-) create mode 100644 examples/server_example_logging/Makefile create mode 100644 examples/server_example_logging/server_example_logging.c create mode 100644 examples/server_example_logging/simpleIO_direct_control.icd create mode 100644 examples/server_example_logging/static_model.c create mode 100644 examples/server_example_logging/static_model.h create mode 100644 src/logging/drivers/README create mode 100644 src/logging/drivers/sqlite/log_storage_sqlite.c diff --git a/examples/server_example_logging/Makefile b/examples/server_example_logging/Makefile new file mode 100644 index 00000000..7c1e389f --- /dev/null +++ b/examples/server_example_logging/Makefile @@ -0,0 +1,30 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = server_example_logging +PROJECT_SOURCES = server_example_logging.c +PROJECT_SOURCES += static_model.c +PROJECT_SOURCES += $(LIBIEC_HOME)/src/logging/drivers/sqlite/log_storage_sqlite.c + +PROJECT_ICD_FILE = simpleIO_direct_control.icd + +include $(LIBIEC_HOME)/make/target_system.mk +include $(LIBIEC_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIBIEC_HOME)/make/common_targets.mk + +LDLIBS += -lm -lsqlite3 + +CP = cp + +model: $(PROJECT_ICD_FILE) + java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE) + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + +clean: + rm -f $(PROJECT_BINARY_NAME) + + diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c new file mode 100644 index 00000000..76c71d82 --- /dev/null +++ b/examples/server_example_logging/server_example_logging.c @@ -0,0 +1,232 @@ +/* + * server_example_logging.c + * + * - How to use a server with logging service + * - How to store arbitrary data in a log + * + */ + +#include "iec61850_server.h" +#include "hal_thread.h" +#include +#include +#include +#include + +#include "static_model.h" + +#include "logging_api.h" + +/* import IEC 61850 device model created from SCL-File */ +extern IedModel iedModel; + +static int running = 0; +static IedServer iedServer = NULL; + +void +sigint_handler(int signalId) +{ + running = 0; +} + +static ControlHandlerResult +controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test) +{ + if (test) + return CONTROL_RESULT_FAILED; + + if (MmsValue_getType(value) == MMS_BOOLEAN) { + printf("received binary control command: "); + + if (MmsValue_getBoolean(value)) + printf("on\n"); + else + printf("off\n"); + } + else + return CONTROL_RESULT_FAILED; + + uint64_t timeStamp = Hal_getTimeInMs(); + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, value); + } + + return CONTROL_RESULT_OK; +} + + +static void +connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) +{ + if (connected) + printf("Connection opened\n"); + else + printf("Connection closed\n"); +} + +static bool +entryCallback(void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFollow) +{ +#if 0 + if (moreFollow) + printf("Found entry ID:%llu timestamp:%llu\n", entryID, timestamp); +#endif + return true; +} + +static bool +entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow) +{ +#if 0 + if (moreFollow) { + printf(" EntryData: ref: %s\n", dataRef); + + MmsValue* value = MmsValue_decodeMmsData(data, 0, dataSize); + + char buffer[256]; + + MmsValue_printToBuffer(value, buffer, 256); + + printf(" value: %s\n", buffer); + + MmsValue_delete(value); + } +#endif + + return true; +} + +int +main(int argc, char** argv) +{ + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); + + iedServer = IedServer_create(&iedModel); + + /* Install handler for operate command */ + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO1); + + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO2); + + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO3); + + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO4); + + IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + + LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); + + IedServer_setLogStorage(iedServer, "GenericIO/LLN0$EventLog", statusLog); + + uint64_t entryID = LogStorage_addEntry(statusLog, Hal_getTimeInMs()); + + MmsValue* value = MmsValue_newIntegerFromInt32(123); + + uint8_t blob[256]; + + int blobSize = MmsValue_encodeMmsData(value, blob, 0, true); + + + LogStorage_addEntryData(statusLog, entryID, "simpleIOGenerioIO/GPIO1$ST$SPCSO1$stVal", blob, blobSize, 0); + + MmsValue_delete(value); + + value = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); + + blobSize = MmsValue_encodeMmsData(value, blob, 0, true); + + MmsValue_delete(value); + + LogStorage_addEntryData(statusLog, entryID, "simpleIOGenerioIO/GPIO1$ST$SPCSO1$t", blob, blobSize, 0); + + LogStorage_getEntries(statusLog, 0, Hal_getTimeInMs(), entryCallback, entryDataCallback, NULL); + + /* MMS server will be instructed to start listening to client connections. */ + IedServer_start(iedServer, 102); + + if (!IedServer_isRunning(iedServer)) { + printf("Starting server failed! Exit.\n"); + IedServer_destroy(iedServer); + exit(-1); + } + + running = 1; + + signal(SIGINT, sigint_handler); + + float t = 0.f; + + while (running) { + uint64_t timestamp = Hal_getTimeInMs(); + + t += 0.1f; + + float an1 = sinf(t); + float an2 = sinf(t + 1.f); + float an3 = sinf(t + 2.f); + float an4 = sinf(t + 3.f); + + IedServer_lockDataModel(iedServer); + + Timestamp iecTimestamp; + + Timestamp_clearFlags(&iecTimestamp); + Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp); + Timestamp_setLeapSecondKnown(&iecTimestamp, true); + + /* toggle clock-not-synchronized flag in timestamp */ + if (((int) t % 2) == 0) + Timestamp_setClockNotSynchronized(&iecTimestamp, true); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, an2); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f, an3); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f, an4); + + IedServer_unlockDataModel(iedServer); + + Thread_sleep(100); + } + + /* stop MMS server - close TCP server socket and all client sockets */ + IedServer_stop(iedServer); + + /* Cleanup - free all resources */ + IedServer_destroy(iedServer); + + /* Release connection to database and free resources */ + LogStorage_destroy(statusLog); + +} /* main() */ diff --git a/examples/server_example_logging/simpleIO_direct_control.icd b/examples/server_example_logging/simpleIO_direct_control.icd new file mode 100644 index 00000000..b48a383e --- /dev/null +++ b/examples/server_example_logging/simpleIO_direct_control.icd @@ -0,0 +1,281 @@ + + +
+
+ + + Station bus + 10 + +
+

10.0.0.2

+

255.255.255.0

+

10.0.0.1

+

0001

+

00000001

+

0001

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + MZ Automation + + + 0.7.3 + + + libiec61850 server example + + + + + + + + status-only + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + +
diff --git a/examples/server_example_logging/static_model.c b/examples/server_example_logging/static_model.c new file mode 100644 index 00000000..07b5b4a4 --- /dev/null +++ b/examples/server_example_logging/static_model.c @@ -0,0 +1,2003 @@ +/* + * static_model.c + * + * automatically generated from simpleIO_direct_control.icd + */ +#include "static_model.h" + +static void initializeValues(); + +extern DataSet iedModelds_GenericIO_LLN0_Events; +extern DataSet iedModelds_GenericIO_LLN0_Events2; +extern DataSet iedModelds_GenericIO_LLN0_Measurements; + + +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO1$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO3$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO4$stVal", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Events = { + "GenericIO", + "LLN0$Events", + 4, + &iedModelds_GenericIO_LLN0_Events_fcda0, + &iedModelds_GenericIO_LLN0_Events2 +}; + +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO1", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO3", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO4", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Events2 = { + "GenericIO", + "LLN0$Events2", + 4, + &iedModelds_GenericIO_LLN0_Events2_fcda0, + &iedModelds_GenericIO_LLN0_Measurements +}; + +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0 = { + "GenericIO", + false, + "GGIO1$MX$AnIn1$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1 = { + "GenericIO", + false, + "GGIO1$MX$AnIn1$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2 = { + "GenericIO", + false, + "GGIO1$MX$AnIn2$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3 = { + "GenericIO", + false, + "GGIO1$MX$AnIn2$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda4 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4 = { + "GenericIO", + false, + "GGIO1$MX$AnIn3$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda5 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5 = { + "GenericIO", + false, + "GGIO1$MX$AnIn3$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda6 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6 = { + "GenericIO", + false, + "GGIO1$MX$AnIn4$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda7 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7 = { + "GenericIO", + false, + "GGIO1$MX$AnIn4$q", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Measurements = { + "GenericIO", + "LLN0$Measurements", + 8, + &iedModelds_GenericIO_LLN0_Measurements_fcda0, + NULL +}; + +LogicalDevice iedModel_GenericIO = { + LogicalDeviceModelType, + "GenericIO", + (ModelNode*) &iedModel, + NULL, + (ModelNode*) &iedModel_GenericIO_LLN0 +}; + +LogicalNode iedModel_GenericIO_LLN0 = { + LogicalNodeModelType, + "LLN0", + (ModelNode*) &iedModel_GenericIO, + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, +}; + +DataObject iedModel_GenericIO_LLN0_Mod = { + DataObjectModelType, + "Mod", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_ctlModel, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + NULL, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_Health = { + DataObjectModelType, + "Health", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_LLN0_Health_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Health_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Health_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_NamPlt = { + DataObjectModelType, + "NamPlt", + (ModelNode*) &iedModel_GenericIO_LLN0, + NULL, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_vendor, + 0 +}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_swRev, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev = { + DataAttributeModelType, + "swRev", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_d, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_d = { + DataAttributeModelType, + "d", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_configRev, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev = { + DataAttributeModelType, + "configRev", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_ldNs, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs = { + DataAttributeModelType, + "ldNs", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + NULL, + NULL, + 0, + IEC61850_FC_EX, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +LogicalNode iedModel_GenericIO_LPHD1 = { + LogicalNodeModelType, + "LPHD1", + (ModelNode*) &iedModel_GenericIO, + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam, +}; + +DataObject iedModel_GenericIO_LPHD1_PhyNam = { + DataObjectModelType, + "PhyNam", + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam_vendor, + 0 +}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam, + NULL, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LPHD1_PhyHealth = { + DataObjectModelType, + "PhyHealth", + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LPHD1_Proxy = { + DataObjectModelType, + "Proxy", + (ModelNode*) &iedModel_GenericIO_LPHD1, + NULL, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +LogicalNode iedModel_GenericIO_GGIO1 = { + LogicalNodeModelType, + "GGIO1", + (ModelNode*) &iedModel_GenericIO, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, +}; + +DataObject iedModel_GenericIO_GGIO1_Mod = { + DataObjectModelType, + "Mod", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_q, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_ctlModel, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + NULL, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Health = { + DataObjectModelType, + "Health", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_NamPlt = { + DataObjectModelType, + "NamPlt", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_vendor, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_swRev, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev = { + DataAttributeModelType, + "swRev", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_d, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d = { + DataAttributeModelType, + "d", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + NULL, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn1 = { + DataObjectModelType, + "AnIn1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn2 = { + DataObjectModelType, + "AnIn2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn3 = { + DataObjectModelType, + "AnIn3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn4 = { + DataObjectModelType, + "AnIn4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO1 = { + DataObjectModelType, + "SPCSO1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_t, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO2 = { + DataObjectModelType, + "SPCSO2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_t, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO3 = { + DataObjectModelType, + "SPCSO3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_t, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO4 = { + DataObjectModelType, + "SPCSO4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_t, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind1 = { + DataObjectModelType, + "Ind1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind2 = { + DataObjectModelType, + "Ind2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind3 = { + DataObjectModelType, + "Ind3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind4 = { + DataObjectModelType, + "Ind4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_stVal, + 0 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +extern ReportControlBlock iedModel_GenericIO_LLN0_report0; +extern ReportControlBlock iedModel_GenericIO_LLN0_report1; +extern ReportControlBlock iedModel_GenericIO_LLN0_report2; +extern ReportControlBlock iedModel_GenericIO_LLN0_report3; +extern ReportControlBlock iedModel_GenericIO_LLN0_report4; +extern ReportControlBlock iedModel_GenericIO_LLN0_report5; +extern ReportControlBlock iedModel_GenericIO_LLN0_report6; + +ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 4294967295, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report1}; +ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report2}; +ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report3}; +ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report4}; +ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "Measurements01", "Measurements", true, "Measurements", 1, 16, 111, 50, 1000, &iedModel_GenericIO_LLN0_report5}; +ReportControlBlock iedModel_GenericIO_LLN0_report5 = {&iedModel_GenericIO_LLN0, "Measurements02", "Measurements", true, "Measurements", 1, 16, 111, 50, 1000, &iedModel_GenericIO_LLN0_report6}; +ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, "Measurements03", "Measurements", true, "Measurements", 1, 16, 111, 50, 1000, NULL}; + + + + +extern LogControlBlock iedModel_GenericIO_LLN0_lcb0; +extern LogControlBlock iedModel_GenericIO_LLN0_lcb1; +LogControlBlock iedModel_GenericIO_LLN0_lcb0 = {&iedModel_GenericIO_LLN0, "EventLog", "Events", "GenericIO/LLN0$EventLog", 3, 0, true, true, &iedModel_GenericIO_LLN0_lcb1}; +LogControlBlock iedModel_GenericIO_LLN0_lcb1 = {&iedModel_GenericIO_LLN0, "GeneralLog", NULL, NULL, 3, 0, true, true, NULL}; + +extern Log iedModel_GenericIO_LLN0_log0; +extern Log iedModel_GenericIO_LLN0_log1; +Log iedModel_GenericIO_LLN0_log0 = {&iedModel_GenericIO_LLN0, "GeneralLog", &iedModel_GenericIO_LLN0_log1}; +Log iedModel_GenericIO_LLN0_log1 = {&iedModel_GenericIO_LLN0, "EventLog", NULL}; + + +IedModel iedModel = { + "simpleIO", + &iedModel_GenericIO, + &iedModelds_GenericIO_LLN0_Events, + &iedModel_GenericIO_LLN0_report0, + NULL, + NULL, + NULL, + &iedModel_GenericIO_LLN0_lcb0, + &iedModel_GenericIO_LLN0_log0, + initializeValues +}; + +static void +initializeValues() +{ + +iedModel_GenericIO_LLN0_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_GenericIO_LLN0_NamPlt_vendor.mmsValue = MmsValue_newVisibleString("MZ Automation"); + +iedModel_GenericIO_LLN0_NamPlt_swRev.mmsValue = MmsValue_newVisibleString("0.7.3"); + +iedModel_GenericIO_LLN0_NamPlt_d.mmsValue = MmsValue_newVisibleString("libiec61850 server example"); + +iedModel_GenericIO_GGIO1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_GenericIO_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO3_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO4_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); +} diff --git a/examples/server_example_logging/static_model.h b/examples/server_example_logging/static_model.h new file mode 100644 index 00000000..b5670e9f --- /dev/null +++ b/examples/server_example_logging/static_model.h @@ -0,0 +1,301 @@ +/* + * static_model.h + * + * automatically generated from simpleIO_direct_control.icd + */ + +#ifndef STATIC_MODEL_H_ +#define STATIC_MODEL_H_ + +#include +#include "iec61850_model.h" + +extern IedModel iedModel; +extern LogicalDevice iedModel_GenericIO; +extern LogicalNode iedModel_GenericIO_LLN0; +extern DataObject iedModel_GenericIO_LLN0_Mod; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_q; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_t; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel; +extern DataObject iedModel_GenericIO_LLN0_Beh; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_q; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_t; +extern DataObject iedModel_GenericIO_LLN0_Health; +extern DataAttribute iedModel_GenericIO_LLN0_Health_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Health_q; +extern DataAttribute iedModel_GenericIO_LLN0_Health_t; +extern DataObject iedModel_GenericIO_LLN0_NamPlt; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_d; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs; +extern LogicalNode iedModel_GenericIO_LPHD1; +extern DataObject iedModel_GenericIO_LPHD1_PhyNam; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor; +extern DataObject iedModel_GenericIO_LPHD1_PhyHealth; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t; +extern DataObject iedModel_GenericIO_LPHD1_Proxy; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_q; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_t; +extern LogicalNode iedModel_GenericIO_GGIO1; +extern DataObject iedModel_GenericIO_GGIO1_Mod; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_t; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel; +extern DataObject iedModel_GenericIO_GGIO1_Beh; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_t; +extern DataObject iedModel_GenericIO_GGIO1_Health; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_t; +extern DataObject iedModel_GenericIO_GGIO1_NamPlt; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d; +extern DataObject iedModel_GenericIO_GGIO1_AnIn1; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn2; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn3; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn4; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO1; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO2; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO3; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO4; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind1; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind2; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind3; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind4; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; + + + +#define IEDMODEL_GenericIO (&iedModel_GenericIO) +#define IEDMODEL_GenericIO_LLN0 (&iedModel_GenericIO_LLN0) +#define IEDMODEL_GenericIO_LLN0_Mod (&iedModel_GenericIO_LLN0_Mod) +#define IEDMODEL_GenericIO_LLN0_Mod_stVal (&iedModel_GenericIO_LLN0_Mod_stVal) +#define IEDMODEL_GenericIO_LLN0_Mod_q (&iedModel_GenericIO_LLN0_Mod_q) +#define IEDMODEL_GenericIO_LLN0_Mod_t (&iedModel_GenericIO_LLN0_Mod_t) +#define IEDMODEL_GenericIO_LLN0_Mod_ctlModel (&iedModel_GenericIO_LLN0_Mod_ctlModel) +#define IEDMODEL_GenericIO_LLN0_Beh (&iedModel_GenericIO_LLN0_Beh) +#define IEDMODEL_GenericIO_LLN0_Beh_stVal (&iedModel_GenericIO_LLN0_Beh_stVal) +#define IEDMODEL_GenericIO_LLN0_Beh_q (&iedModel_GenericIO_LLN0_Beh_q) +#define IEDMODEL_GenericIO_LLN0_Beh_t (&iedModel_GenericIO_LLN0_Beh_t) +#define IEDMODEL_GenericIO_LLN0_Health (&iedModel_GenericIO_LLN0_Health) +#define IEDMODEL_GenericIO_LLN0_Health_stVal (&iedModel_GenericIO_LLN0_Health_stVal) +#define IEDMODEL_GenericIO_LLN0_Health_q (&iedModel_GenericIO_LLN0_Health_q) +#define IEDMODEL_GenericIO_LLN0_Health_t (&iedModel_GenericIO_LLN0_Health_t) +#define IEDMODEL_GenericIO_LLN0_NamPlt (&iedModel_GenericIO_LLN0_NamPlt) +#define IEDMODEL_GenericIO_LLN0_NamPlt_vendor (&iedModel_GenericIO_LLN0_NamPlt_vendor) +#define IEDMODEL_GenericIO_LLN0_NamPlt_swRev (&iedModel_GenericIO_LLN0_NamPlt_swRev) +#define IEDMODEL_GenericIO_LLN0_NamPlt_d (&iedModel_GenericIO_LLN0_NamPlt_d) +#define IEDMODEL_GenericIO_LLN0_NamPlt_configRev (&iedModel_GenericIO_LLN0_NamPlt_configRev) +#define IEDMODEL_GenericIO_LLN0_NamPlt_ldNs (&iedModel_GenericIO_LLN0_NamPlt_ldNs) +#define IEDMODEL_GenericIO_LPHD1 (&iedModel_GenericIO_LPHD1) +#define IEDMODEL_GenericIO_LPHD1_PhyNam (&iedModel_GenericIO_LPHD1_PhyNam) +#define IEDMODEL_GenericIO_LPHD1_PhyNam_vendor (&iedModel_GenericIO_LPHD1_PhyNam_vendor) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth (&iedModel_GenericIO_LPHD1_PhyHealth) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_stVal (&iedModel_GenericIO_LPHD1_PhyHealth_stVal) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_q (&iedModel_GenericIO_LPHD1_PhyHealth_q) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_t (&iedModel_GenericIO_LPHD1_PhyHealth_t) +#define IEDMODEL_GenericIO_LPHD1_Proxy (&iedModel_GenericIO_LPHD1_Proxy) +#define IEDMODEL_GenericIO_LPHD1_Proxy_stVal (&iedModel_GenericIO_LPHD1_Proxy_stVal) +#define IEDMODEL_GenericIO_LPHD1_Proxy_q (&iedModel_GenericIO_LPHD1_Proxy_q) +#define IEDMODEL_GenericIO_LPHD1_Proxy_t (&iedModel_GenericIO_LPHD1_Proxy_t) +#define IEDMODEL_GenericIO_GGIO1 (&iedModel_GenericIO_GGIO1) +#define IEDMODEL_GenericIO_GGIO1_Mod (&iedModel_GenericIO_GGIO1_Mod) +#define IEDMODEL_GenericIO_GGIO1_Mod_q (&iedModel_GenericIO_GGIO1_Mod_q) +#define IEDMODEL_GenericIO_GGIO1_Mod_t (&iedModel_GenericIO_GGIO1_Mod_t) +#define IEDMODEL_GenericIO_GGIO1_Mod_ctlModel (&iedModel_GenericIO_GGIO1_Mod_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_Beh (&iedModel_GenericIO_GGIO1_Beh) +#define IEDMODEL_GenericIO_GGIO1_Beh_stVal (&iedModel_GenericIO_GGIO1_Beh_stVal) +#define IEDMODEL_GenericIO_GGIO1_Beh_q (&iedModel_GenericIO_GGIO1_Beh_q) +#define IEDMODEL_GenericIO_GGIO1_Beh_t (&iedModel_GenericIO_GGIO1_Beh_t) +#define IEDMODEL_GenericIO_GGIO1_Health (&iedModel_GenericIO_GGIO1_Health) +#define IEDMODEL_GenericIO_GGIO1_Health_stVal (&iedModel_GenericIO_GGIO1_Health_stVal) +#define IEDMODEL_GenericIO_GGIO1_Health_q (&iedModel_GenericIO_GGIO1_Health_q) +#define IEDMODEL_GenericIO_GGIO1_Health_t (&iedModel_GenericIO_GGIO1_Health_t) +#define IEDMODEL_GenericIO_GGIO1_NamPlt (&iedModel_GenericIO_GGIO1_NamPlt) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_vendor (&iedModel_GenericIO_GGIO1_NamPlt_vendor) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_swRev (&iedModel_GenericIO_GGIO1_NamPlt_swRev) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_d (&iedModel_GenericIO_GGIO1_NamPlt_d) +#define IEDMODEL_GenericIO_GGIO1_AnIn1 (&iedModel_GenericIO_GGIO1_AnIn1) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag (&iedModel_GenericIO_GGIO1_AnIn1_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f (&iedModel_GenericIO_GGIO1_AnIn1_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_q (&iedModel_GenericIO_GGIO1_AnIn1_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_t (&iedModel_GenericIO_GGIO1_AnIn1_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn2 (&iedModel_GenericIO_GGIO1_AnIn2) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag (&iedModel_GenericIO_GGIO1_AnIn2_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f (&iedModel_GenericIO_GGIO1_AnIn2_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_q (&iedModel_GenericIO_GGIO1_AnIn2_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_t (&iedModel_GenericIO_GGIO1_AnIn2_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn3 (&iedModel_GenericIO_GGIO1_AnIn3) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag (&iedModel_GenericIO_GGIO1_AnIn3_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f (&iedModel_GenericIO_GGIO1_AnIn3_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_q (&iedModel_GenericIO_GGIO1_AnIn3_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_t (&iedModel_GenericIO_GGIO1_AnIn3_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn4 (&iedModel_GenericIO_GGIO1_AnIn4) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag (&iedModel_GenericIO_GGIO1_AnIn4_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f (&iedModel_GenericIO_GGIO1_AnIn4_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_q (&iedModel_GenericIO_GGIO1_AnIn4_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_t (&iedModel_GenericIO_GGIO1_AnIn4_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1 (&iedModel_GenericIO_GGIO1_SPCSO1) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal (&iedModel_GenericIO_GGIO1_SPCSO1_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_q (&iedModel_GenericIO_GGIO1_SPCSO1_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper (&iedModel_GenericIO_GGIO1_SPCSO1_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO1_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_t (&iedModel_GenericIO_GGIO1_SPCSO1_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2 (&iedModel_GenericIO_GGIO1_SPCSO2) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal (&iedModel_GenericIO_GGIO1_SPCSO2_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_q (&iedModel_GenericIO_GGIO1_SPCSO2_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper (&iedModel_GenericIO_GGIO1_SPCSO2_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO2_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_t (&iedModel_GenericIO_GGIO1_SPCSO2_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3 (&iedModel_GenericIO_GGIO1_SPCSO3) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal (&iedModel_GenericIO_GGIO1_SPCSO3_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_q (&iedModel_GenericIO_GGIO1_SPCSO3_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper (&iedModel_GenericIO_GGIO1_SPCSO3_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO3_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_t (&iedModel_GenericIO_GGIO1_SPCSO3_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4 (&iedModel_GenericIO_GGIO1_SPCSO4) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal (&iedModel_GenericIO_GGIO1_SPCSO4_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_q (&iedModel_GenericIO_GGIO1_SPCSO4_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper (&iedModel_GenericIO_GGIO1_SPCSO4_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO4_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_t (&iedModel_GenericIO_GGIO1_SPCSO4_t) +#define IEDMODEL_GenericIO_GGIO1_Ind1 (&iedModel_GenericIO_GGIO1_Ind1) +#define IEDMODEL_GenericIO_GGIO1_Ind1_stVal (&iedModel_GenericIO_GGIO1_Ind1_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind1_q (&iedModel_GenericIO_GGIO1_Ind1_q) +#define IEDMODEL_GenericIO_GGIO1_Ind1_t (&iedModel_GenericIO_GGIO1_Ind1_t) +#define IEDMODEL_GenericIO_GGIO1_Ind2 (&iedModel_GenericIO_GGIO1_Ind2) +#define IEDMODEL_GenericIO_GGIO1_Ind2_stVal (&iedModel_GenericIO_GGIO1_Ind2_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind2_q (&iedModel_GenericIO_GGIO1_Ind2_q) +#define IEDMODEL_GenericIO_GGIO1_Ind2_t (&iedModel_GenericIO_GGIO1_Ind2_t) +#define IEDMODEL_GenericIO_GGIO1_Ind3 (&iedModel_GenericIO_GGIO1_Ind3) +#define IEDMODEL_GenericIO_GGIO1_Ind3_stVal (&iedModel_GenericIO_GGIO1_Ind3_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind3_q (&iedModel_GenericIO_GGIO1_Ind3_q) +#define IEDMODEL_GenericIO_GGIO1_Ind3_t (&iedModel_GenericIO_GGIO1_Ind3_t) +#define IEDMODEL_GenericIO_GGIO1_Ind4 (&iedModel_GenericIO_GGIO1_Ind4) +#define IEDMODEL_GenericIO_GGIO1_Ind4_stVal (&iedModel_GenericIO_GGIO1_Ind4_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind4_q (&iedModel_GenericIO_GGIO1_Ind4_q) +#define IEDMODEL_GenericIO_GGIO1_Ind4_t (&iedModel_GenericIO_GGIO1_Ind4_t) + +#endif /* STATIC_MODEL_H_ */ + diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 7c9eaa04..e6c46d1c 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1440,6 +1440,10 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, addVariablesWithFc("GO", logicalNodeName, ld->variables, lnDirectory); break; + case ACSI_CLASS_LCB: + addVariablesWithFc("LG", logicalNodeName, ld->variables, lnDirectory); + break; + case ACSI_CLASS_DATA_SET: { LinkedList dataSet = LinkedList_getNext(ld->dataSets); diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index f8e0f016..e16eb9a1 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -133,6 +133,8 @@ FunctionalConstraint_toString(FunctionalConstraint fc) { return "RP"; case IEC61850_FC_BR: return "BR"; + case IEC61850_FC_LG: + return "LG"; default: return NULL; } @@ -212,6 +214,12 @@ FunctionalConstraint_fromString(const char* fcString) return IEC61850_FC_NONE; } + if (fcString[0] == 'L') { + if (fcString[1] == 'G') + return IEC61850_FC_LG; + return IEC61850_FC_NONE; + } + return IEC61850_FC_NONE; } diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index 929074a0..5b73f337 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -234,6 +234,8 @@ typedef enum eFunctionalConstraint { IEC61850_FC_RP = 15, /** Buffered report */ IEC61850_FC_BR = 16, + /** Log control blocks */ + IEC61850_FC_LG = 17, /** All FCs - wildcard value */ IEC61850_FC_ALL = 99, diff --git a/src/iec61850/inc_private/logging.h b/src/iec61850/inc_private/logging.h index afa3084b..365f7a07 100644 --- a/src/iec61850/inc_private/logging.h +++ b/src/iec61850/inc_private/logging.h @@ -29,6 +29,8 @@ typedef struct { char* name; LogicalNode* parentLN; + bool locked; + LogStorage logStorage; uint64_t newEntryId; @@ -62,7 +64,10 @@ typedef struct { bool enabled; + uint64_t nextIntegrityScan; + int triggerOps; + uint32_t intgPd; } LogControl; @@ -76,12 +81,24 @@ LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage); void LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* value, uint8_t flag); +uint64_t +LogInstance_logEntryStart(LogInstance* self); + +void +LogInstance_logEntryData(LogInstance* self, uint64_t entryID, const char* dataRef, MmsValue* value, uint8_t flag); + +void +LogInstance_logEntryFinished(LogInstance* self, uint64_t entryID); + void LogInstance_destroy(LogInstance* self); LogControl* LogControl_create(LogicalNode* parentLN, MmsMapping* mmsMapping); +void +LogControl_setLog(LogControl* self, LogInstance* logInstance); + void LogControl_destroy(LogControl* self); @@ -92,7 +109,14 @@ MmsVariableSpecification* Logging_createLCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int lcbCount); +void +Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs); + MmsValue* LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig); +MmsDataAccessError +LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, + MmsValue* value, MmsServerConnection connection); + #endif /* LIBIEC61850_SRC_IEC61850_INC_PRIVATE_LOGGING_H_ */ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 5a2a467b..7dd9eec1 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -447,6 +447,19 @@ IedServer_destroy(IedServer self) { MmsServer_destroy(self->mmsServer); IsoServer_destroy(self->isoServer); + +#if (CONFIG_MMS_SINGLE_THREADED == 1) + + /* trigger stopping background task thread */ + self->mmsMapping->reportThreadRunning = false; + + /* waiting for thread to finish */ + while (self->mmsMapping->reportThreadFinished == false) { + Thread_sleep(10); + } + +#endif + MmsMapping_destroy(self->mmsMapping); LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy); diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index 7222373a..6df0be28 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -35,6 +35,7 @@ #include "mms_mapping_internal.h" #include "mms_value_internal.h" +#if (CONFIG_IEC61850_LOG_SERVICE == 1) LogInstance* LogInstance_create(LogicalNode* parentLN, const char* name) @@ -44,6 +45,7 @@ LogInstance_create(LogicalNode* parentLN, const char* name) self->name = copyString(name); self->parentLN = parentLN; self->logStorage = NULL; + self->locked = false; self->oldEntryId = 0; self->oldEntryTime = 0; @@ -67,7 +69,13 @@ LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* valu if (logStorage != NULL) { - printf("Log value - dataRef: %s flag: %i\n", dataRef, flag); + while (self->locked) + Thread_sleep(1); + + self->locked = true; + + //if (DEBUG_IED_SERVER) + printf("IED_SERVER: Log value - dataRef: %s flag: %i\n", dataRef, flag); uint64_t timestamp = Hal_getTimeInMs(); @@ -81,6 +89,8 @@ LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* valu LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag); + self->locked = false; + GLOBAL_FREEMEM(data); self->newEntryId = entryID; @@ -88,9 +98,62 @@ LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* valu } else - printf("no log storage available!\n"); + if (DEBUG_IED_SERVER) + printf("IED_SERVER: no log storage available for logging!\n"); } +uint64_t +LogInstance_logEntryStart(LogInstance* self) +{ + LogStorage logStorage = self->logStorage; + + if (logStorage != NULL) { + + while (self->locked) + Thread_sleep(1); + + self->locked = true; + + uint64_t timestamp = Hal_getTimeInMs(); + + uint64_t entryID = LogStorage_addEntry(logStorage, timestamp); + + return entryID; + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: no log storage available for logging!\n"); + + return 0; + } +} + +void +LogInstance_logEntryData(LogInstance* self, uint64_t entryID, const char* dataRef, MmsValue* value, uint8_t flag) +{ + LogStorage logStorage = self->logStorage; + + if (logStorage != NULL) { + + int dataSize = MmsValue_encodeMmsData(value, NULL, 0, false); + + uint8_t* data = GLOBAL_MALLOC(dataSize); + + MmsValue_encodeMmsData(value, data, 0, true); + + LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag); + + self->locked = false; + + GLOBAL_FREEMEM(data); + } +} + +void +LogInstance_logEntryFinished(LogInstance* self, uint64_t entryID) +{ + self->locked = false; +} void LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage) @@ -113,6 +176,7 @@ LogControl_create(LogicalNode* parentLN, MmsMapping* mmsMapping) self->mmsMapping = mmsMapping; self->dataSetRef = NULL; self->logInstance = NULL; + self->intgPd = 0; return self; } @@ -138,6 +202,45 @@ LogControl_setLog(LogControl* self, LogInstance* logInstance) self->logInstance = logInstance; } +static void +prepareLogControl(LogControl* logControl) +{ + if (logControl->dataSetRef == NULL) { + printf(" no data set specified!\n"); + return; + } + + DataSet* dataSet = IedModel_lookupDataSet(logControl->mmsMapping->model, logControl->dataSetRef); + + if (dataSet == NULL) { + printf(" data set (%s) not found!\n", logControl->dataSetRef); + return; + } + else + logControl->dataSet = dataSet; +} + +static bool +enableLogging(LogControl* self) +{ + if ((self->dataSet != NULL) && (self->logInstance != NULL)) { + self->enabled = true; + + if ((self->triggerOps & TRG_OPT_INTEGRITY) && (self->intgPd != 0)) + self->nextIntegrityScan = Hal_getTimeInMs(); + else + self->nextIntegrityScan = 0; + + MmsValue* enabled = MmsValue_getSubElement(self->mmsValue, self->mmsType, "LogEna"); + + MmsValue_setBoolean(enabled, true); + + return true; + } + else + return false; +} + static LogControlBlock* getLCBForLogicalNodeWithIndex(MmsMapping* self, LogicalNode* logicalNode, int index) { @@ -183,6 +286,59 @@ lookupLogControl(MmsMapping* self, MmsDomain* domain, char* lnName, char* object return NULL; } +static LogInstance* +getLogInstanceByLogRef(MmsMapping* self, const char* logRef) +{ + char refStr[130]; + char* domainName; + char* lnName; + char* logName; + + strncpy(refStr, logRef, 129); + + domainName = refStr; + + lnName = strchr(refStr, '/'); + + if (lnName == NULL) + return NULL; + + if ((lnName - domainName) > 64) + return NULL; + + lnName[0] = 0; + lnName++; + + logName = strchr(lnName, '$'); + + if (logName == NULL) + return NULL; + + logName[0] = 0; + logName++; + + LinkedList instance = LinkedList_getNext(self->logInstances); + + while (instance != NULL) { + + LogInstance* logInstance = LinkedList_getData(instance); + + if (strcmp(logInstance->name, logName) == 0) { + + if (strcmp(lnName, logInstance->parentLN->name) == 0) { + LogicalDevice* ld = (LogicalDevice*) logInstance->parentLN->parent; + + if (strcmp(ld->name, domainName) == 0) + return logInstance; + } + } + + instance = LinkedList_getNext(instance); + } + + return NULL; +} + static void updateLogStatusInLCB(LogControl* self) { @@ -199,13 +355,168 @@ updateLogStatusInLCB(LogControl* self) } +MmsDataAccessError +LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, + MmsValue* value, MmsServerConnection connection) +{ + bool updateValue = false; + + char variableId[130]; + + strncpy(variableId, variableIdOrig, 129); + + char* separator = strchr(variableId, '$'); + + *separator = 0; + + char* lnName = variableId; + + if (lnName == NULL) + return DATA_ACCESS_ERROR_INVALID_ADDRESS; + + char* objectName = MmsMapping_getNextNameElement(separator + 1); + + if (objectName == NULL) + return DATA_ACCESS_ERROR_INVALID_ADDRESS; + + char* varName = MmsMapping_getNextNameElement(objectName); + + if (varName != NULL) + *(varName - 1) = 0; + + LogControl* logControl = lookupLogControl(self, domain, lnName, objectName); + + if (logControl == NULL) { + return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + + if (strcmp(varName, "LogEna") == 0) { + bool logEna = MmsValue_getBoolean(value); + + if (logEna == false) { + logControl->enabled = false; + } + else { + + if (enableLogging(logControl)) { + logControl->enabled = true; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: enabled log control %s\n", logControl->name); + } + else + return DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; + } + + updateValue = true; + } + else if (strcmp(varName, "LogRef") == 0) { + + if (logControl->enabled == false) { + /* check if logRef is valid or NULL */ + const char* logRef = MmsValue_toString(value); + + if (logRef == NULL) { + + logControl->logInstance = NULL; + + updateValue = true; + } + else { + if (strcmp(logRef, "") == 0) { + logControl->logInstance = NULL; + updateValue = true; + } + else { + + /* remove IED name from logRef */ + char* iedName = self->mmsDevice->deviceName; + + uint32_t iedNameLen = strlen(iedName); + + if (iedNameLen < strlen(logRef)) { + if (memcmp(iedName, logRef, iedNameLen) == 0) { + logRef = logRef + iedNameLen; + } + } + + LogInstance* logInstance = getLogInstanceByLogRef(self, logRef); + + if (logInstance != NULL) { + + logControl->logInstance = logInstance; + updateValue = true; + } + else + return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + + } + } + } + else + return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + } + else if (strcmp(varName, "DatSet") == 0) { + + if (logControl->enabled == false) { + /* check if datSet is valid or NULL/empty */ + + const char* dataSetRef = MmsValue_toString(value); + + if (strlen(dataSetRef) == 0) { + logControl->dataSet = NULL; + updateValue = true; + } + else { + DataSet* dataSet = IedModel_lookupDataSet(logControl->mmsMapping->model, dataSetRef); + + if (dataSet == NULL) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: data set (%s) not found!\n", logControl->dataSetRef); + return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + } + else { + logControl->dataSet = dataSet; + updateValue = true; + } + } + } + else + return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + } + else if (strcmp(varName, "IntgPd") == 0) { + if (logControl->enabled == false) { + logControl->intgPd = MmsValue_toUint32(value); + updateValue = true; + } + else + return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + } + else if (strcmp(varName, "TrgOps") == 0) { + if (logControl->enabled == false) { + logControl->triggerOps = (MmsValue_getBitStringAsInteger(value) / 2); + updateValue = true; + } + else + return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + } + + if (updateValue) { + MmsValue* element = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); + + MmsValue_update(element, value); + + return DATA_ACCESS_ERROR_SUCCESS; + } + + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; +} + MmsValue* LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) { MmsValue* value = NULL; - printf("READ ACCESS LOG CB\n"); - char variableId[130]; strncpy(variableId, variableIdOrig, 129); @@ -261,10 +572,10 @@ createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logCon } static MmsValue* -createTrgOps(LogControlBlock* reportControlBlock) { +createTrgOps(LogControlBlock* logControlBlock) { MmsValue* trgOps = MmsValue_newBitString(-6); - uint8_t triggerOps = reportControlBlock->trgOps; + uint8_t triggerOps = logControlBlock->trgOps; if (triggerOps & TRG_OPT_DATA_CHANGED) MmsValue_setBitStringBit(trgOps, 1, true); @@ -278,29 +589,7 @@ createTrgOps(LogControlBlock* reportControlBlock) { return trgOps; } -static bool -enableLogging(LogControl* logControl) -{ - printf("Enable LCB %s...\n", logControl->name); - - if (logControl->dataSetRef == NULL) { - printf(" no data set specified!\n"); - return false; - } - - DataSet* dataSet = IedModel_lookupDataSet(logControl->mmsMapping->model, logControl->dataSetRef); - if (dataSet == NULL) { - printf(" data set (%s) not found!\n", logControl->dataSetRef); - return false; - } - else - logControl->dataSet = dataSet; - - printf(" enabled\n"); - - return true; -} static MmsVariableSpecification* createLogControlBlock(MmsMapping* self, LogControlBlock* logControlBlock, @@ -440,6 +729,7 @@ createLogControlBlock(MmsMapping* self, LogControlBlock* logControlBlock, mmsValue->value.structure.components[8] = MmsValue_newUnsignedFromUint32(logControlBlock->intPeriod); + logControl->intgPd = logControlBlock->intPeriod; logControl->mmsType = lcb; logControl->mmsValue = mmsValue; @@ -448,138 +738,109 @@ createLogControlBlock(MmsMapping* self, LogControlBlock* logControlBlock, logControl->enabled = logControlBlock->logEna; - if (logControl->enabled) { + prepareLogControl(logControl); + + if (logControl->enabled) enableLogging(logControl); - } return lcb; } -static LogInstance* -getLogInstanceByLogRef(MmsMapping* self, const char* logRef) +MmsVariableSpecification* +Logging_createLCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, + int lcbCount) { - char refStr[130]; - char* domainName; - char* lnName; - char* logName; - - strncpy(refStr, logRef, 129); - - printf("getLogInst... %s\n", refStr); - - domainName = refStr; - - lnName = strchr(refStr, '/'); + MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, + sizeof(MmsVariableSpecification)); + namedVariable->name = copyString("LG"); + namedVariable->type = MMS_STRUCTURE; - if (lnName == NULL) - return NULL; + namedVariable->typeSpec.structure.elementCount = lcbCount; + namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(lcbCount, + sizeof(MmsVariableSpecification*)); - if ((lnName - domainName) > 64) - return NULL; + int currentLcb = 0; - lnName[0] = 0; - lnName++; + while (currentLcb < lcbCount) { - logName = strchr(lnName, '$'); + LogControl* logControl = LogControl_create(logicalNode, self); - if (logName == NULL) - return NULL; + LogControlBlock* logControlBlock = getLCBForLogicalNodeWithIndex(self, logicalNode, currentLcb); - logName[0] = 0; - logName++; + logControl->name = createString(3, logicalNode->name, "$LG$", logControlBlock->name); + logControl->domain = domain; - printf("LOG: dn: %s ln: %s name: %s\n", domainName, lnName, logName); + namedVariable->typeSpec.structure.elements[currentLcb] = + createLogControlBlock(self, logControlBlock, logControl); - LinkedList instance = LinkedList_getNext(self->logInstances); + if (logControlBlock->logRef != NULL) + logControl->logInstance = getLogInstanceByLogRef(self, logControlBlock->logRef); - while (instance != NULL) { + LinkedList_add(self->logControls, logControl); - LogInstance* logInstance = LinkedList_getData(instance); + currentLcb++; + } - printf("logInstance: %s\n", logInstance->name); + return namedVariable; +} - if (strcmp(logInstance->name, logName) == 0) { - printf (" lnName: %s\n", logInstance->parentLN->name); +static void +LogControl_logAllDatasetEntries(LogControl* self, const char* iedName) +{ + if (self->dataSet == NULL) + return; - if (strcmp(lnName, logInstance->parentLN->name) == 0) { - LogicalDevice* ld = (LogicalDevice*) logInstance->parentLN->parent; + if (self->logInstance != NULL) { - printf(" ldName: %s\n", ld->name); + char dataRef[130]; - if (strcmp(ld->name, domainName) == 0) - return logInstance; - } - } + LogInstance* log = self->logInstance; - instance = LinkedList_getNext(instance); - } + uint64_t entryID = LogInstance_logEntryStart(log); - return NULL; -} + DataSetEntry* dataSetEntry = self->dataSet->fcdas; -#if 0 -static LogInstance* -getLogInstance(MmsMapping* self, LogicalNode* logicalNode, const char* logName) -{ - if (logName == NULL) - return NULL; + while (dataSetEntry != NULL) { - LinkedList logInstance = LinkedList_getNext(self->logInstances); + sprintf(dataRef, "%s%s/%s", iedName, dataSetEntry->logicalDeviceName, dataSetEntry->variableName); - while (logInstance != NULL) { - LogInstance* instance = (LogInstance*) LinkedList_getData(logInstance); + LogInstance_logEntryData(log, entryID, dataRef, dataSetEntry->value, TRG_OPT_INTEGRITY * 2); - printf("LOG: %s (%s)\n", instance->name, logName); + dataSetEntry = dataSetEntry->sibling; + } - if ((strcmp(instance->name, logName) == 0) && (logicalNode == instance->parentLN)) - return instance; + LogInstance_logEntryFinished(log, entryID); - logInstance = LinkedList_getNext(logInstance); } - - return NULL; } -#endif -MmsVariableSpecification* -Logging_createLCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, - int lcbCount) +void +Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs) { - MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, - sizeof(MmsVariableSpecification)); - namedVariable->name = copyString("LG"); - namedVariable->type = MMS_STRUCTURE; - - namedVariable->typeSpec.structure.elementCount = lcbCount; - namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(lcbCount, - sizeof(MmsVariableSpecification*)); + LinkedList logControlElem = LinkedList_getNext(self->logControls); - int currentLcb = 0; + while (logControlElem != NULL) { - while (currentLcb < lcbCount) { - - LogControl* logControl = LogControl_create(logicalNode, self); + LogControl* logControl = (LogControl*) LinkedList_getData(logControlElem); - LogControlBlock* logControlBlock = getLCBForLogicalNodeWithIndex(self, logicalNode, currentLcb); + if (logControl->enabled) { - logControl->name = createString(3, logicalNode->name, "$LG$", logControlBlock->name); - logControl->domain = domain; + if (logControl->nextIntegrityScan != 0) { - namedVariable->typeSpec.structure.elements[currentLcb] = - createLogControlBlock(self, logControlBlock, logControl); + if (currentTimeInMs >= logControl->nextIntegrityScan) { - //getLogInstanceByLogRef(self, logControlBlock->logRef); - //logControl->logInstance = getLogInstance(self, logicalNode, logControlBlock->logRef); + if (DEBUG_IED_SERVER) + printf("IED_SERVER: INTEGRITY SCAN for log %s\n", logControl->name); - if (logControlBlock->logRef != NULL) - logControl->logInstance = getLogInstanceByLogRef(self, logControlBlock->logRef); + LogControl_logAllDatasetEntries(logControl, self->mmsDevice->deviceName); - LinkedList_add(self->logControls, logControl); + logControl->nextIntegrityScan += logControl->intgPd; + } + } + } - currentLcb++; + logControlElem = LinkedList_getNext(logControlElem); } - - return namedVariable; } void @@ -590,29 +851,23 @@ MmsMapping_setLogStorage(MmsMapping* self, const char* logRef, LogStorage logSto if (logInstance != NULL) { LogInstance_setLogStorage(logInstance, logStorage); -#if 1 char domainName[65]; MmsMapping_getMmsDomainFromObjectReference(logRef, domainName); - char domainName2[65]; + char domainNameWithIEDName[65]; - strcpy(domainName2, self->model->name); - strcat(domainName2, domainName); + strcpy(domainNameWithIEDName, self->model->name); + strcat(domainNameWithIEDName, domainName); - MmsDomain* mmsDomain = MmsDevice_getDomain(self->mmsDevice, domainName2); + MmsDomain* mmsDomain = MmsDevice_getDomain(self->mmsDevice, domainNameWithIEDName); if (mmsDomain == NULL) { - printf("IED_SERVER: MmsMapping_setLogStorage: domain %s not found!\n", domainName2); - } -#if 0 - char journalName[65]; - - strcpy(journalName, self->parentLN->name); - strcat(journalName, "$"); - strcat(journalName, self->name); -#endif + if (DEBUG_IED_SERVER) + printf("IED_SERVER: MmsMapping_setLogStorage: domain %s not found!\n", domainNameWithIEDName); + return; + } MmsJournal mmsJournal = NULL; @@ -626,12 +881,14 @@ MmsMapping_setLogStorage(MmsMapping* self, const char* logRef, LogStorage logSto if (mmsJournal != NULL) mmsJournal->logStorage = logStorage; else - printf("IED_SERVER: Failed to retrieve MMS journal for log!\n"); -#endif - + if (DEBUG_IED_SERVER) + printf("IED_SERVER: Failed to retrieve MMS journal for log!\n"); } - //if (DEBUG_IED_SERVER) + if (DEBUG_IED_SERVER) if (logInstance == NULL) printf("IED_SERVER: MmsMapping_setLogStorage no matching log for %s found!\n", logRef); } + +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ + diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 32c159c3..92f497a8 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1799,6 +1799,13 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, #endif /* (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) */ +#if (CONFIG_IEC61850_LOG_SERVICE == 1) + + /* Log control block - LG */ + if (isLogControlBlock(separator)) + return LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(self, domain, variableId, value, connection); + +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ #if (CONFIG_IEC61850_REPORT_SERVICE == 1) /* Report control blocks - BR, RP */ @@ -2625,51 +2632,6 @@ DataSet_isMemberValueWithRef(DataSet* dataSet, MmsValue* value, char* dataRef, c return false; } -#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ - -#if (CONFIG_IEC61850_REPORT_SERVICE == 1) -void -MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, ReportInclusionFlag flag) -{ - LinkedList element = self->reportControls; - - while ((element = LinkedList_getNext(element)) != NULL) { - ReportControl* rc = (ReportControl*) element->data; - - if (rc->enabled || (rc->buffered && rc->dataSet != NULL)) { - int index; - - switch (flag) { - case REPORT_CONTROL_VALUE_UPDATE: - if ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0) - continue; - break; - case REPORT_CONTROL_VALUE_CHANGED: - if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) && - ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0)) - continue; - break; - case REPORT_CONTROL_QUALITY_CHANGED: - if ((rc->triggerOps & TRG_OPT_QUALITY_CHANGED) == 0) - continue; - break; - default: - continue; - } - - if (DataSet_isMemberValue(rc->dataSet, value, &index)) { - ReportControl_valueUpdated(rc, index, flag); - } - } - } -} - -#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ - -#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) - -#if (CONFIG_IEC61850_LOG_SERVICE == 1) - void MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag flag) { @@ -2680,12 +2642,16 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl if ((lc->enabled) && (lc->dataSet != NULL)) { + uint8_t reasonCode; + switch (flag) { case LOG_CONTROL_VALUE_UPDATE: if ((lc->triggerOps & TRG_OPT_DATA_UPDATE) == 0) continue; + reasonCode = TRG_OPT_DATA_UPDATE * 2; + break; case LOG_CONTROL_VALUE_CHANGED: @@ -2693,12 +2659,16 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl ((lc->triggerOps & TRG_OPT_DATA_UPDATE) == 0)) continue; + reasonCode = TRG_OPT_DATA_CHANGED * 2; + break; case LOG_CONTROL_QUALITY_CHANGED: if ((lc->triggerOps & TRG_OPT_QUALITY_CHANGED) == 0) continue; + reasonCode = TRG_OPT_QUALITY_CHANGED * 2; + break; default: @@ -2710,7 +2680,7 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl if (DataSet_isMemberValueWithRef(lc->dataSet, value, dataRef, self->model->name)) { if (lc->logInstance != NULL) { - LogInstance_logSingleData(lc->logInstance, dataRef, value, flag); + LogInstance_logSingleData(lc->logInstance, dataRef, value, reasonCode); } else printf("No log instance available!\n"); @@ -2723,6 +2693,46 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl #endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ +#if (CONFIG_IEC61850_REPORT_SERVICE == 1) +void +MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, ReportInclusionFlag flag) +{ + LinkedList element = self->reportControls; + + while ((element = LinkedList_getNext(element)) != NULL) { + ReportControl* rc = (ReportControl*) element->data; + + if (rc->enabled || (rc->buffered && rc->dataSet != NULL)) { + int index; + + switch (flag) { + case REPORT_CONTROL_VALUE_UPDATE: + if ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0) + continue; + break; + case REPORT_CONTROL_VALUE_CHANGED: + if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) && + ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0)) + continue; + break; + case REPORT_CONTROL_QUALITY_CHANGED: + if ((rc->triggerOps & TRG_OPT_QUALITY_CHANGED) == 0) + continue; + break; + default: + continue; + } + + if (DataSet_isMemberValue(rc->dataSet, value, &index)) { + ReportControl_valueUpdated(rc, index, flag); + } + } + } +} + +#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ + +#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) void MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value) @@ -2950,6 +2960,10 @@ processPeriodicTasks(MmsMapping* self) #if (CONFIG_IEC61850_SETTING_GROUPS == 1) MmsMapping_checkForSettingGroupReservationTimeouts(self, currentTimeInMs); #endif + +#if (CONFIG_IEC61850_LOG_SERVICE == 1) + Logging_processIntegrityLogs(self, currentTimeInMs); +#endif } void diff --git a/src/logging/drivers/README b/src/logging/drivers/README new file mode 100644 index 00000000..dd1e95dd --- /dev/null +++ b/src/logging/drivers/README @@ -0,0 +1,3 @@ +This directory contains the log driver implementation that implements the logging API. The logging API is a service provider interface that has to be implemented by the log driver. +An application can use different log drivers at the same time. +Each log driver provides a public constructor for the driver specific implementation of the LogStorage class. The constructor has to fill the virtual function table of the LogStorage instance with its own implementation functions. diff --git a/src/logging/drivers/sqlite/log_storage_sqlite.c b/src/logging/drivers/sqlite/log_storage_sqlite.c new file mode 100644 index 00000000..13d76138 --- /dev/null +++ b/src/logging/drivers/sqlite/log_storage_sqlite.c @@ -0,0 +1,455 @@ +/* + * log_storage_sqlite.c + * + * Copyright 2016 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + + +#include "logging_api.h" +#include "libiec61850_platform_includes.h" + +#include + +#ifndef DEBUG_LOG_STORAGE_DRIVER +#define DEBUG_LOG_STORAGE_DRIVER 0 +#endif + +static uint64_t +SqliteLogStorage_addEntry(LogStorage self, uint64_t timestamp); + +static bool +SqliteLogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode); + +static bool +SqliteLogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime, + LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter); + +static bool +SqliteLogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID, + LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter); + +static bool +SqliteLogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime, + uint64_t* oldEntry, uint64_t* oldEntryTime); + +static void +SqliteLogStorage_destroy(LogStorage self); + + +typedef struct sSqliteLogStorage { + char* filename; + sqlite3* db; + sqlite3_stmt* insertEntryStmt; + sqlite3_stmt* insertEntryDataStmt; + sqlite3_stmt* getEntriesWithRange; + sqlite3_stmt* getEntriesAfter; + sqlite3_stmt* getEntryData; + sqlite3_stmt* getOldEntry; + sqlite3_stmt* getNewEntry; +} SqliteLogStorage; + +static const char* CREATE_TABLE_ENTRYS = "create table if not exists Entries (entryID integer primary key, timeOfEntry integer)"; +static const char* CREATE_TABLE_ENTRY_DATA = "create table if not exists EntryData (entryID integer, dataRef text, value blob, reasonCode integer)"; +static const char* INSERT_ENTRY = "insert into Entries (timeOfEntry) values (?)"; +static const char* INSERT_ENTRY_DATA = "insert into EntryData (entryID, dataRef, value, reasonCode) values (?,?,?,?)"; +static const char* GET_ENTRIES_WITH_RANGE = "select entryID, timeOfEntry from Entries where timeOfEntry >= ? and timeOfEntry <= ?"; +static const char* GET_ENTRIES_AFTER = "select entryID, timeOfEntry from Entries where entryID > ?"; +static const char* GET_ENTRY_DATA = "select dataRef, value, reasonCode from EntryData where entryID = ?"; + +static const char* GET_OLD_ENTRY = "select * from Entries where entryID = (select min(entryID) from Entries where timeOfEntry = (select min(timeOfEntry) from Entries))"; +static const char* GET_NEW_ENTRY = "select * from Entries where entryID = (select max(entryID) from Entries where timeOfEntry = (select max(timeOfEntry) from Entries))"; + +LogStorage +SqliteLogStorage_createInstance(const char* filename) +{ + + sqlite3* db = NULL; + sqlite3_stmt* insertEntryStmt = NULL; + sqlite3_stmt* insertEntryDataStmt = NULL; + sqlite3_stmt* getEntriesWithRange = NULL; + sqlite3_stmt* getEntriesAfter = NULL; + sqlite3_stmt* getEntryData = NULL; + sqlite3_stmt* getOldEntry = NULL; + sqlite3_stmt* getNewEntry = NULL; + char *zErrMsg = 0; + + int rc = sqlite3_open(filename, &db); + + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_exec(db, CREATE_TABLE_ENTRYS, NULL, 0, &zErrMsg); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_exec(db, CREATE_TABLE_ENTRY_DATA, NULL, 0, &zErrMsg); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_prepare(db, INSERT_ENTRY, -1, &insertEntryStmt, NULL); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_prepare(db, INSERT_ENTRY_DATA, -1, &insertEntryDataStmt, NULL); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_prepare_v2(db, GET_ENTRIES_WITH_RANGE, -1, &getEntriesWithRange, NULL); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_prepare_v2(db, GET_ENTRIES_AFTER, -1, &getEntriesAfter, NULL); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_prepare_v2(db, GET_ENTRY_DATA, -1, &getEntryData, NULL); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_prepare_v2(db, GET_OLD_ENTRY, -1, &getOldEntry, NULL); + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_prepare_v2(db, GET_NEW_ENTRY, -1, &getNewEntry, NULL); + if (rc != SQLITE_OK) + goto exit_with_error; + + LogStorage self = (LogStorage) GLOBAL_CALLOC(1, sizeof(struct sLogStorage)); + + SqliteLogStorage* instanceData = (SqliteLogStorage*) GLOBAL_CALLOC(1, sizeof(struct sSqliteLogStorage)); + instanceData->filename = copyString(filename); + instanceData->db = db; + instanceData->insertEntryStmt = insertEntryStmt; + instanceData->insertEntryDataStmt = insertEntryDataStmt; + instanceData->getEntriesWithRange = getEntriesWithRange; + instanceData->getEntriesAfter = getEntriesAfter; + instanceData->getEntryData = getEntryData; + instanceData->getOldEntry = getOldEntry; + instanceData->getNewEntry = getNewEntry; + + self->instanceData = (void*) instanceData; + + self->addEntry = SqliteLogStorage_addEntry; + self->addEntryData = SqliteLogStorage_addEntryData; + self->getEntries = SqliteLogStorage_getEntries; + self->getEntriesAfter = SqliteLogStorage_getEntriesAfter; + self->getOldestAndNewestEntries = SqliteLogStorage_getOldestAndNewestEntries; + self->destroy = SqliteLogStorage_destroy; + + return self; + +exit_with_error: + if (DEBUG_LOG_STORAGE_DRIVER) + printf("LOG_STORAGE_DRIVER: sqlite - failed to create LogStorage instance!\n"); + + return NULL; +} + + +static uint64_t +SqliteLogStorage_addEntry(LogStorage self, uint64_t timestamp) +{ + SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); + + sqlite3* db = instanceData->db; + int rc; + char *zErrMsg = 0; + + rc = sqlite3_bind_int64(instanceData->insertEntryStmt, 1, (sqlite_int64) timestamp); + + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_step(instanceData->insertEntryStmt); + + if (rc != SQLITE_DONE) + goto exit_with_error; + + uint64_t id = sqlite3_last_insert_rowid(db); + + if (DEBUG_LOG_STORAGE_DRIVER) + printf("LOG_STORAGE_DRIVER: sqlite - new entry with ID = %lu\n", id); + + rc = sqlite3_reset(instanceData->insertEntryStmt); + + if (rc != SQLITE_OK) + goto exit_with_error; + + return id; + +exit_with_error: + if (DEBUG_LOG_STORAGE_DRIVER) + printf("LOG_STORAGE_DRIVER: sqlite - failed to add entry to log!\n"); + + return 0; +} + +static bool +SqliteLogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode) +{ + SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); + + sqlite3* db = instanceData->db; + + int rc; + + char *zErrMsg = 0; + + rc = sqlite3_bind_int64(instanceData->insertEntryDataStmt, 1, (sqlite_int64) entryID); + + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_bind_text(instanceData->insertEntryDataStmt, 2, dataRef, -1, SQLITE_STATIC); + + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_bind_blob(instanceData->insertEntryDataStmt, 3, data, dataSize, SQLITE_STATIC); + + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_bind_int(instanceData->insertEntryDataStmt, 4, reasonCode); + + if (rc != SQLITE_OK) + goto exit_with_error; + + rc = sqlite3_step(instanceData->insertEntryDataStmt); + + if (rc != SQLITE_DONE) + goto exit_with_error; + + rc = sqlite3_reset(instanceData->insertEntryDataStmt); + + if (rc != SQLITE_OK) + goto exit_with_error; + + return true; + +exit_with_error: + if (DEBUG_LOG_STORAGE_DRIVER) + printf("LOG_STORAGE_DRIVER: sqlite - failed to add entry data!\n"); + + return false; +} + +static void +getEntryData(LogStorage self, uint64_t entryID, LogEntryDataCallback entryDataCallback, void* parameter) +{ + SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); + + int rc; + + rc = sqlite3_bind_int64(instanceData->getEntryData, 1, entryID); + + if (rc != SQLITE_OK) + printf("getEntryData 1 rc:%i\n", rc); + + bool sendFinalEvent = true; + + while ((rc = sqlite3_step(instanceData->getEntryData)) == SQLITE_ROW) { + + const char* dataRef = sqlite3_column_text(instanceData->getEntryData, 0); + const uint8_t* data = sqlite3_column_blob(instanceData->getEntryData, 1); + int dataSize = sqlite3_column_bytes(instanceData->getEntryData, 1); + int reasonCode = sqlite3_column_int(instanceData->getEntryData, 2); + + if (entryDataCallback != NULL) { + if (entryDataCallback(parameter, dataRef, data, dataSize, (uint8_t) reasonCode, true) == false) { + sendFinalEvent = false; + break; + } + + } + } + + if (sendFinalEvent) { + if (entryDataCallback != NULL) + entryDataCallback(parameter, NULL, NULL, 0, (uint8_t) 0, false); + } + + rc = sqlite3_reset(instanceData->getEntryData); + + if (rc != SQLITE_OK) + printf("getEntryData reset rc:%i\n", rc); +} + +static bool +SqliteLogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime, + LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter) +{ + SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); + + sqlite3* db = instanceData->db; + + int rc; + + rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 1, startingTime); + + if (rc != SQLITE_OK) + printf("SqliteLogStorage_getEntries 1 rc:%i\n", rc); + + rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 2, endingTime); + + if (rc != SQLITE_OK) + printf("SqliteLogStorage_getEntries 2 rc:%i\n", rc); + + bool sendFinalEvent = true; + + while((rc = sqlite3_step(instanceData->getEntriesWithRange)) == SQLITE_ROW) { + int col; + + uint64_t entryID = sqlite3_column_int64(instanceData->getEntriesWithRange, 0); + uint64_t timestamp = sqlite3_column_int64(instanceData->getEntriesWithRange, 1); + + if (entryCallback != NULL) { + if (entryCallback(parameter, timestamp, entryID, true) == false) { + sendFinalEvent = false; + break; + } + + } + + getEntryData(self, entryID, entryDataCallback, parameter); + } + + if (sendFinalEvent) + if (entryCallback != NULL) + entryCallback(parameter, 0, 0, false); + + + rc = sqlite3_reset(instanceData->getEntriesWithRange); + + if (rc != SQLITE_OK) + printf("SqliteLogStorage_getEntries reset rc:%i\n", rc); + + return true; +} + +static bool +SqliteLogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime, + uint64_t* oldEntry, uint64_t* oldEntryTime) +{ + bool validOldEntry = false; + bool validNewEntry = false; + + SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); + + sqlite3* db = instanceData->db; + + int rc; + + /* Get oldest entry */ + + rc = sqlite3_step(instanceData->getOldEntry); + + if (rc == SQLITE_ROW) { + *oldEntry = sqlite3_column_int64(instanceData->getOldEntry, 0); + *oldEntryTime = sqlite3_column_int64(instanceData->getOldEntry, 1); + validNewEntry = true; + } + + sqlite3_reset(instanceData->getOldEntry); + + /* Get newest entry */ + + rc = sqlite3_step(instanceData->getNewEntry); + + if (rc == SQLITE_ROW) { + *newEntry = sqlite3_column_int64(instanceData->getNewEntry, 0); + *newEntryTime = sqlite3_column_int64(instanceData->getNewEntry, 1); + validOldEntry = true; + } + + sqlite3_reset(instanceData->getNewEntry); + + return (validOldEntry && validNewEntry); +} + +static bool +SqliteLogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID, + LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter) +{ + SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); + + sqlite3* db = instanceData->db; + + int rc; + + rc = sqlite3_bind_int64(instanceData->getEntriesAfter, 1, entryID); + + if (rc != SQLITE_OK) + printf("SqliteLogStorage_getEntriesAfter 1 rc:%i\n", rc); + + bool sendFinalEvent = true; + + while ((rc = sqlite3_step(instanceData->getEntriesAfter)) == SQLITE_ROW) { + int col; + + uint64_t entryID = sqlite3_column_int64(instanceData->getEntriesAfter, 0); + uint64_t timestamp = sqlite3_column_int64(instanceData->getEntriesAfter, 1); + + if (entryCallback != NULL) { + if (entryCallback(parameter, timestamp, entryID, true) == false) { + sendFinalEvent = false; + break; + } + } + + getEntryData(self, entryID, entryDataCallback, parameter); + } + + if (sendFinalEvent) + if (entryCallback != NULL) + entryCallback(parameter, 0, 0, false); + + rc = sqlite3_reset(instanceData->getEntriesAfter); + + if (rc != SQLITE_OK) + printf("SqliteLogStorage_getEntriesAfter reset rc:%i\n", rc); + + return true; +} + +static void +SqliteLogStorage_destroy(LogStorage self) +{ + SqliteLogStorage* instanceData = (SqliteLogStorage*) self->instanceData; + + sqlite3_finalize(instanceData->insertEntryStmt); + sqlite3_finalize(instanceData->insertEntryDataStmt); + sqlite3_finalize(instanceData->getEntriesWithRange); + sqlite3_finalize(instanceData->getEntriesAfter); + sqlite3_finalize(instanceData->getEntryData); + sqlite3_finalize(instanceData->getOldEntry); + sqlite3_finalize(instanceData->getNewEntry); + + if (sqlite3_close(instanceData->db) != SQLITE_OK) + printf("SqliteLogStorage: failed to close database %s!\n", instanceData->filename); + + GLOBAL_FREEMEM(instanceData->filename); + GLOBAL_FREEMEM(instanceData); + GLOBAL_FREEMEM(self); +} + + + + diff --git a/src/logging/logging_api.h b/src/logging/logging_api.h index 078b9bb7..726e25f5 100644 --- a/src/logging/logging_api.h +++ b/src/logging/logging_api.h @@ -37,7 +37,7 @@ typedef struct sLogStorage* LogStorage; */ typedef bool (*LogEntryCallback) (void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFollow); -typedef bool (*LogEntryDataCallback) (void* parameter, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow); +typedef bool (*LogEntryDataCallback) (void* parameter, const char* dataRef, const uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow); struct sLogStorage { diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar index 89a9b09a43118f6b7b68e33ad46650d67c4d7b44..b90d292c8e6608df2fe9fed192053992ba0f513b 100644 GIT binary patch delta 15865 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 NRrs*xT+h9S_vMn8gySoKs21W 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 16) + triggerOps = triggerOps - 16; + lcbString += triggerOps + ", "; if (lcb.getIntgPd() != 0)