From ad98531b1bc4e77bb02b27d066f5f642f86de278 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 15 May 2017 15:44:10 +0200 Subject: [PATCH 1/4] - fixed SDO bug in dynamic code generator --- .../client_example_reporting.c | 4 +++- src/iec61850/inc/iec61850_client.h | 2 ++ tools/model_generator/gendyncode.jar | Bin 88037 -> 88158 bytes .../tools/DynamicCodeGenerator.java | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/iec61850_client_example_reporting/client_example_reporting.c b/examples/iec61850_client_example_reporting/client_example_reporting.c index 1f53117..00c0472 100644 --- a/examples/iec61850_client_example_reporting/client_example_reporting.c +++ b/examples/iec61850_client_example_reporting/client_example_reporting.c @@ -132,7 +132,9 @@ int main(int argc, char** argv) { Thread_sleep(1000); - IedConnection_triggerGIReport(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01"); + /* Trigger GI Report */ + ClientReportControlBlock_setGI(rcb, true); + IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true); if (error != IED_ERROR_OK) { printf("Error triggering a GI report (code: %i)\n", error); diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 8d2b46b..26f3428 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -810,6 +810,8 @@ IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReferenc * * The RCB must have been enabled and GI set as trigger option before this command can be performed. * + * \deprecated Use ClientReportControlBlock_setGI instead + * * \param connection the connection object * \param error the error code if an error occurs * \param rcbReference object reference of the report control block diff --git a/tools/model_generator/gendyncode.jar b/tools/model_generator/gendyncode.jar index 0721d44f989a3038c79a74469a0882a3a10fc082..ba2bd0fafd303f4ae6d4315a0dba8e736b5875c8 100644 GIT binary patch delta 33876 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 Date: Wed, 17 May 2017 10:33:08 +0200 Subject: [PATCH 2/4] - added Timestamp_toMmsValue function --- examples/sv_publisher/sv_publisher_example.c | 2 +- src/iec61850/common/iec61850_common.c | 14 ++++++++++++++ src/iec61850/inc/iec61850_common.h | 9 +++++++++ src/vs/libiec61850-wo-goose.def | 3 ++- src/vs/libiec61850.def | 3 ++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/examples/sv_publisher/sv_publisher_example.c b/examples/sv_publisher/sv_publisher_example.c index b048bef..6c4cf68 100644 --- a/examples/sv_publisher/sv_publisher_example.c +++ b/examples/sv_publisher/sv_publisher_example.c @@ -20,7 +20,7 @@ void sigint_handler(int signalId) int main(int argc, char** argv) { - SampledValuesPublisher svPublisher = SampledValuesPublisher_create("vboxnet0"); + SampledValuesPublisher svPublisher = SampledValuesPublisher_create("eth0"); SV_ASDU asdu1 = SampledValuesPublisher_addASDU(svPublisher, "svpub1", NULL, 1); diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index 3189203..d2a6898 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -405,6 +405,20 @@ Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue) memcpy(self->val, mmsValue->value.utcTime, 8); } +MmsValue* +Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue) +{ + MmsValue* convertedValue = mmsValue; + + if (convertedValue == NULL) + convertedValue = MmsValue_newUtcTime(0); + + if (convertedValue != NULL) + memcpy(convertedValue->value.utcTime, self->val, 8); + + return convertedValue; +} + char* LibIEC61850_getVersionString() { diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index 47131c4..a48e8ed 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -406,6 +406,15 @@ Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch); void Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue); +/** + * \brief Set an MmsValue instance of type UTCTime to the timestamp value + * + * \param self the Timestamp instance + * \param mmsValue the mmsValue instance, if NULL a new instance will be created + */ +MmsValue* +Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue); + /** * \brief Get the version of the library as string * diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index 9711a2c..4a8cfc1 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -561,4 +561,5 @@ EXPORTS Timestamp_create Timestamp_destroy Timestamp_setByMmsUtcTime - IedServer_setLocalIpAddress \ No newline at end of file + IedServer_setLocalIpAddress + Timestamp_toMmsValue diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index 45dc4d2..1769ee9 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -639,4 +639,5 @@ EXPORTS Timestamp_create Timestamp_destroy Timestamp_setByMmsUtcTime - IedServer_setLocalIpAddress \ No newline at end of file + IedServer_setLocalIpAddress + Timestamp_toMmsValue From 2a40798f10014452a63561333f7a7f2e835fbb83 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 30 May 2017 07:43:31 +0200 Subject: [PATCH 3/4] - server: added MmsFileAccessHandler for server side monitoring and control of file services - file services: added support to set filestore basepath at runtime - added IedConnection_getFileDirectoryEx to better control receiving file directories --- CHANGELOG | 16 + config/stack_config.h | 5 + config/stack_config.h.cmake | 33 +- examples/CMakeLists.txt | 1 + examples/Makefile | 2 + examples/iec61850_9_2_LE_example/README | 9 + .../iec61850_9_2_LE_example.c | 15 +- examples/mms_utility/mms_utility.c | 19 +- examples/server_example3/server_example3.c | 4 + examples/server_example_files/CMakeLists.txt | 21 + examples/server_example_files/Makefile | 31 + .../server_example_files.c | 98 + .../simpleIO_direct_control.icd | 281 +++ examples/server_example_files/static_model.c | 2003 +++++++++++++++++ examples/server_example_files/static_model.h | 301 +++ .../server_example_files/vmd-filestore/test | 1 + src/iec61850/client/ied_connection.c | 33 + src/iec61850/inc/iec61850_client.h | 45 + src/iec61850/inc/iec61850_server.h | 13 + src/iec61850/server/impl/ied_server.c | 7 + src/mms/inc/mms_client_connection.h | 13 + src/mms/inc/mms_server.h | 54 +- src/mms/inc_private/mms_client_internal.h | 8 + src/mms/inc_private/mms_common_internal.h | 11 +- src/mms/inc_private/mms_server_connection.h | 3 + src/mms/inc_private/mms_server_internal.h | 9 + .../iso_mms/client/mms_client_connection.c | 41 + src/mms/iso_mms/client/mms_client_files.c | 58 +- src/mms/iso_mms/common/mms_common_msg.c | 72 +- src/mms/iso_mms/server/mms_file_service.c | 200 +- src/mms/iso_mms/server/mms_server.c | 40 +- .../iso_mms/server/mms_server_connection.c | 17 + src/vs/libiec61850-wo-goose.def | 6 + src/vs/libiec61850.def | 5 + 34 files changed, 3317 insertions(+), 158 deletions(-) create mode 100644 examples/iec61850_9_2_LE_example/README create mode 100644 examples/server_example_files/CMakeLists.txt create mode 100644 examples/server_example_files/Makefile create mode 100644 examples/server_example_files/server_example_files.c create mode 100644 examples/server_example_files/simpleIO_direct_control.icd create mode 100644 examples/server_example_files/static_model.c create mode 100644 examples/server_example_files/static_model.h create mode 100644 examples/server_example_files/vmd-filestore/test diff --git a/CHANGELOG b/CHANGELOG index ea68a97..de833d8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,19 @@ +Changes to version 1.0.2 +------------------------ +- server: added MmsFileAccessHandler for server side monitoring and control of file services +- file services: added support to set filestore basepath at runtime +- added IedConnection_getFileDirectoryEx to better control receiving file directories +- common: added timestamp handling functions Timestamp_create, Timestamp_destroy, Timestamp_setByMmsUtcTime, Timestamp_toMmsValue +- .NET API: Added Timestamp class +- .NET API: Added missing UpdateAttribute methods to IedServer +- common: added support for Currency BasicType +- MMS file services: fixed problem with negative frsmId +- java scl tools: fixed bug with SDOs in dynamic code generator +- java scl tools: fixed parsing bug in OptionFields.java +- server: added functions to restrict local IP address IedServer_setLocalIpAddress, IedServer.SetLocalIpAddress +- .NET: IedConnection: added internal reference to IsoConnectionParameters object to avoid deletion of internal AcseAuthenticationParameters by garbage collector + + Changes to version 1.0.1 ------------------------ - server: fixed problem in COTP src/dst reference handling (returning zero src reference) diff --git a/config/stack_config.h b/config/stack_config.h index a305280..c11d32f 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -224,4 +224,9 @@ /* Support user access to raw messages */ #define CONFIG_MMS_RAW_MESSAGE_LOGGING 1 +/* Allow to set the virtual filestore basepath for MMS file services at runtime with the + * MmsServer_setFilestoreBasepath function + */ +#define CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME 1 + #endif /* STACK_CONFIG_H_ */ diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index c9ea73d..c85e15d 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -29,6 +29,9 @@ #cmakedefine01 DEBUG_MMS_SERVER #cmakedefine01 DEBUG_GOOSE_SUBSCRIBER #cmakedefine01 DEBUG_GOOSE_PUBLISHER +#cmakedefine01 DEBUG_SV_SUBSCRIBER 0 +#cmakedefine01 DEBUG_SV_PUBLISHER 0 +#cmakedefine01 DEBUG_HAL_ETHERNET 0 /* 1 ==> server runs in single threaded mode (one dedicated thread for the server) * 0 ==> server runs in multi threaded mode (one thread for each connection and @@ -103,14 +106,14 @@ #define CONFIG_GOOSE_EVENT_RETRANSMISSION_COUNT 2 /* Define if GOOSE control block elements are writable (1) or read-only (0) */ -#define CONFIG_GOOSE_GOID_WRITABLE 1 -#define CONFIG_GOOSE_DATSET_WRITABLE 1 -#define CONFIG_GOOSE_CONFREV_WRITABLE 1 -#define CONFIG_GOOSE_NDSCOM_WRITABLE 1 -#define CONFIG_GOOSE_DSTADDRESS_WRITABLE 1 -#define CONFIG_GOOSE_MINTIME_WRITABLE 1 -#define CONFIG_GOOSE_MAXTIME_WRITABLE 1 -#define CONFIG_GOOSE_FIXEDOFFS_WRITABLE 1 +#define CONFIG_GOOSE_GOID_WRITABLE 0 +#define CONFIG_GOOSE_DATSET_WRITABLE 0 +#define CONFIG_GOOSE_CONFREV_WRITABLE 0 +#define CONFIG_GOOSE_NDSCOM_WRITABLE 0 +#define CONFIG_GOOSE_DSTADDRESS_WRITABLE 0 +#define CONFIG_GOOSE_MINTIME_WRITABLE 0 +#define CONFIG_GOOSE_MAXTIME_WRITABLE 0 +#define CONFIG_GOOSE_FIXEDOFFS_WRITABLE 0 /* The default value for the priority field of the 802.1Q header (allowed range 0-7) */ #define CONFIG_GOOSE_DEFAULT_PRIORITY 4 @@ -133,6 +136,9 @@ /* include support for IEC 61850 reporting services */ #cmakedefine01 CONFIG_IEC61850_REPORT_SERVICE +/* support buffered report control blocks with ResvTms field */ +#define CONFIG_IEC61850_BRCB_WITH_RESVTMS 0 + /* The default buffer size of buffered RCBs in bytes */ #cmakedefine CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE @CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE@ @@ -194,10 +200,21 @@ #define CONFIG_IEC61850_CONTROL_SERVICE 0 #endif +/* Sort getNameList response according to the MMS specified collation order - this is required by the standard + * Set to 0 only for performance reasons and when no certification is required! */ +#define CONFIG_MMS_SORT_NAME_LIST 1 + +#define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 + /* use short FC defines as in old API */ #define CONFIG_PROVIDE_OLD_FC_DEFINES 0 /* Support user acccess to raw messages */ #cmakedefine01 CONFIG_MMS_RAW_MESSAGE_LOGGING +/* Allow to set the virtual filestore basepath for MMS file services at runtime with the + * MmsServer_setFilestoreBasepath function + */ +#define CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME 1 + #endif /* STACK_CONFIG_H_ */ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 398e901..a09192d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(server_example_threadless) add_subdirectory(server_example_61400_25) add_subdirectory(server_example_setting_groups) add_subdirectory(server_example_logging) +add_subdirectory(server_example_files) add_subdirectory(iec61850_client_example1) add_subdirectory(iec61850_client_example2) add_subdirectory(iec61850_client_example3) diff --git a/examples/Makefile b/examples/Makefile index 3826221..3e8fcc3 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -25,6 +25,7 @@ EXAMPLE_DIRS += server_example_61400_25 EXAMPLE_DIRS += server_example_threadless EXAMPLE_DIRS += server_example_setting_groups EXAMPLE_DIRS += server_example_logging +EXAMPLE_DIRS += server_example_files EXAMPLE_DIRS += goose_subscriber EXAMPLE_DIRS += goose_publisher EXAMPLE_DIRS += sv_subscriber @@ -47,6 +48,7 @@ MODEL_DIRS += server_example_61400_25 MODEL_DIRS += server_example_threadless MODEL_DIRS += server_example_setting_groups MODEL_DIRS += server_example_logging +MODEL_DIRS += server_example_files MODEL_DIRS += iec61850_9_2_LE_example all: examples diff --git a/examples/iec61850_9_2_LE_example/README b/examples/iec61850_9_2_LE_example/README new file mode 100644 index 0000000..ca64b3a --- /dev/null +++ b/examples/iec61850_9_2_LE_example/README @@ -0,0 +1,9 @@ + +The default ethernet interface is eth0. You can specify another interface at the command line. + + ./sv_9_2LE_example eth1 + +In order for the SV publisher to start the SVCB has to be enabled by an MMS client. + + + diff --git a/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c b/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c index 7122302..b2c3ed0 100644 --- a/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c +++ b/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c @@ -36,7 +36,7 @@ extern IedModel iedModel; static int running = 0; -static int svcbEnabled = 0; +static int svcbEnabled = 1; void sigint_handler(int signalId) { @@ -57,9 +57,9 @@ static SampledValuesPublisher svPublisher; static SV_ASDU asdu; static void -setupSVPublisher() +setupSVPublisher(const char* svInterface) { - svPublisher = SampledValuesPublisher_create("eth1"); + svPublisher = SampledValuesPublisher_create(svInterface); asdu = SampledValuesPublisher_addASDU(svPublisher, "xxxxMUnn01", NULL, 1); @@ -87,6 +87,13 @@ static void sVCBEventHandler (SVControlBlock* svcb, int event, void* parameter) int main(int argc, char** argv) { + char* svInterface; + + if (argc > 1) + svInterface = argv[1]; + else + svInterface = "eth0"; + IedServer iedServer = IedServer_create(&iedModel); /* MMS server will be instructed to start listening to client connections. */ @@ -102,7 +109,7 @@ main(int argc, char** argv) signal(SIGINT, sigint_handler); - setupSVPublisher(); + setupSVPublisher(svInterface); int voltage = 1; int current = 1; diff --git a/examples/mms_utility/mms_utility.c b/examples/mms_utility/mms_utility.c index da6ca12..e2511c6 100644 --- a/examples/mms_utility/mms_utility.c +++ b/examples/mms_utility/mms_utility.c @@ -21,6 +21,7 @@ print_help() printf("-a specify domain for read or write command\n"); printf("-f show file list\n"); printf("-g get file attributes\n"); + printf("-x delete file\n"); printf("-j read journal\n"); printf("-m print raw MMS messages\n"); } @@ -115,10 +116,11 @@ int main(int argc, char** argv) { int getFileAttributes = 0; int readJournal = 0; int printRawMmsMessages = 0; + int deleteFile = 0; int c; - while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:")) != -1) + while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:")) != -1) switch (c) { case 'm': printRawMmsMessages = 1; @@ -158,6 +160,10 @@ int main(int argc, char** argv) { getFileAttributes = 1; filename = StringUtils_copyString(optarg); break; + case 'x': + deleteFile = 1; + filename = StringUtils_copyString(optarg); + break; case 'j': readJournal = 1; @@ -351,6 +357,17 @@ int main(int argc, char** argv) { MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL); } + if (deleteFile) { + MmsConnection_fileDelete(con, &error, filename); + + if (error != MMS_ERROR_NONE) { + printf("Delete file failed: (ERROR %i)\n", error); + } + else { + printf("File deleted\n"); + } + } + exit: free(hostname); MmsConnection_destroy(con); diff --git a/examples/server_example3/server_example3.c b/examples/server_example3/server_example3.c index 2352bc6..2e49ef9 100644 --- a/examples/server_example3/server_example3.c +++ b/examples/server_example3/server_example3.c @@ -85,6 +85,10 @@ main(int argc, char** argv) iedServer = IedServer_create(&iedModel); + /* Set the base path for the MMS file services */ + MmsServer mmsServer = IedServer_getMmsServer(iedServer); + MmsServer_setFilestoreBasepath(mmsServer, "./vmd-filestore/"); + /* Install handler for operate command */ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, (ControlHandler) controlHandlerForBinaryOutput, diff --git a/examples/server_example_files/CMakeLists.txt b/examples/server_example_files/CMakeLists.txt new file mode 100644 index 0000000..a0dfc35 --- /dev/null +++ b/examples/server_example_files/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories( + . +) + +set(server_example_SRCS + server_example_files.c + static_model.c +) + +IF(WIN32) +set_source_files_properties(${server_example_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(WIN32) + +add_executable(server_example_files + ${server_example_SRCS} +) + +target_link_libraries(server_example_files + iec61850 +) diff --git a/examples/server_example_files/Makefile b/examples/server_example_files/Makefile new file mode 100644 index 0000000..bcc7b4c --- /dev/null +++ b/examples/server_example_files/Makefile @@ -0,0 +1,31 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = server_example_files +PROJECT_SOURCES = server_example_files.c +PROJECT_SOURCES += static_model.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 + +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) + $(CP) $(PROJECT_BINARY_NAME) vmd-filestore/IEDSERVER.BIN + +clean: + rm -f $(PROJECT_BINARY_NAME) + rm -f vmd-filestore/IEDSERVER.BIN + + diff --git a/examples/server_example_files/server_example_files.c b/examples/server_example_files/server_example_files.c new file mode 100644 index 0000000..9b263bb --- /dev/null +++ b/examples/server_example_files/server_example_files.c @@ -0,0 +1,98 @@ +/* + * server_example_files.c + * + * - How to use some special MMS file service features + * - How to control how files file services can be used + */ + +#include "iec61850_server.h" +#include "hal_thread.h" +#include +#include +#include +#include + +#include "static_model.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 void +connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) +{ + if (connected) + printf("Connection opened\n"); + else + printf("Connection closed\n"); +} + +static MmsError +fileAccessHandler (void* parameter, MmsServerConnection connection, MmsFileServiceType service, + const char* localFilename, const char* otherFilename) +{ + printf("fileAccessHanlder: service = %i, local-file: %s other-file: %s\n", service, localFilename, otherFilename); + + /* Don't allow client to rename files */ + if (service == MMS_FILE_ACCESS_TYPE_RENAME) + return MMS_ERROR_FILE_FILE_ACCESS_DENIED; + + /* Don't allow client to delete fike "IEDSERVER.BIN" */ + if (service == MMS_FILE_ACCESS_TYPE_DELETE) { + if (strcmp(localFilename, "IEDSERVER.BIN") == 0) + return MMS_ERROR_FILE_FILE_ACCESS_DENIED; + } + + /* allow all other accesses */ + return MMS_ERROR_NONE; +} + +int +main(int argc, char** argv) +{ + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); + + iedServer = IedServer_create(&iedModel); + + /* Set the base path for the MMS file services */ + MmsServer mmsServer = IedServer_getMmsServer(iedServer); + MmsServer_setFilestoreBasepath(mmsServer, "./vmd-filestore/"); + + /* Set a callback handler to control file accesses */ + MmsServer_installFileAccessHandler(mmsServer, fileAccessHandler, NULL); + + IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, 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); + + + while (running) + Thread_sleep(100); + + + /* stop MMS server - close TCP server socket and all client sockets */ + IedServer_stop(iedServer); + + /* Cleanup - free all resources */ + IedServer_destroy(iedServer); + +} /* main() */ diff --git a/examples/server_example_files/simpleIO_direct_control.icd b/examples/server_example_files/simpleIO_direct_control.icd new file mode 100644 index 0000000..0a0c635 --- /dev/null +++ b/examples/server_example_files/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_files/static_model.c b/examples/server_example_files/static_model.c new file mode 100644 index 0000000..0592602 --- /dev/null +++ b/examples/server_example_files/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, 239, 50, 1000, &iedModel_GenericIO_LLN0_report1}; +ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 24, 239, 50, 1000, &iedModel_GenericIO_LLN0_report2}; +ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 24, 239, 50, 1000, &iedModel_GenericIO_LLN0_report3}; +ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 24, 239, 50, 1000, &iedModel_GenericIO_LLN0_report4}; +ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "Measurements01", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, &iedModel_GenericIO_LLN0_report5}; +ReportControlBlock iedModel_GenericIO_LLN0_report5 = {&iedModel_GenericIO_LLN0, "Measurements02", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, &iedModel_GenericIO_LLN0_report6}; +ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, "Measurements03", "Measurements", true, "Measurements", 1, 16, 239, 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_files/static_model.h b/examples/server_example_files/static_model.h new file mode 100644 index 0000000..b5670e9 --- /dev/null +++ b/examples/server_example_files/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/examples/server_example_files/vmd-filestore/test b/examples/server_example_files/vmd-filestore/test new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/examples/server_example_files/vmd-filestore/test @@ -0,0 +1 @@ +test diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 63c6033..29ba1b4 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1131,6 +1131,32 @@ IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const return fileNames; } +LinkedList /**/ +IedConnection_getFileDirectoryEx(IedConnection self, IedClientError* error, const char* directoryName, const char* continueAfter, + bool* moreFollows) +{ + *error = IED_ERROR_OK; + + MmsError mmsError = MMS_ERROR_NONE; + + LinkedList fileNames = LinkedList_create(); + + bool moreFollowsInternal = MmsConnection_getFileDirectory(self->connection, &mmsError, directoryName, continueAfter, + mmsFileDirectoryHandler, fileNames); + + if (mmsError != MMS_ERROR_NONE) { + *error = iedConnection_mapMmsErrorToIedError(mmsError); + LinkedList_destroyDeep(fileNames, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); + + return NULL; + } + + if (moreFollows != NULL) + *moreFollows = moreFollowsInternal; + + return fileNames; +} + struct sClientProvidedFileReadHandler { IedClientGetFileHandler handler; @@ -1201,6 +1227,13 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil return clientFileReadHandler.byteReceived; } +void +IedConnection_setFilestoreBasepath(IedConnection self, const char* basepath) +{ + /* simply pass the call to MMS client API */ + MmsConnection_setFilestoreBasepath(self->connection, basepath); +} + void IedConnection_setFile(IedConnection self, IedClientError* error, const char* sourceFilename, const char* destinationFilename) { diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 26f3428..3319eba 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1871,6 +1871,38 @@ FileDirectoryEntry_getLastModified(FileDirectoryEntry self); LinkedList /**/ IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const char* directoryName); + +/** + * \brief returns the directory entries of the specified file directory returned by a single file directory request. + * + * This function will only create a single request and the result may only be the directory that fits + * into a single MMS PDU. If the server contains more directory entries, this will be indicated by setting + * the moreFollows variable (if provided by the caller). If the directory entry does not fit into a single MMS + * PDU the next part of the directory list can be requested by setting the continueAfter parameter with the value + * of the last filename of the received list. + * + * Requires the server to support file services. + * + * NOTE: the returned linked list has to be freed by the user. You can user the following statement + * to free the list of directory entries: + * + * LinkedList_destroyDeep(fileNames, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); + * + * where fileNames is the return value of this function. + * + * \param self the connection object + * \param error the error code if an error occurs + * \param directoryName the name of the directory or NULL to get the entries of the root directory + * \param continueAfter last received filename to continue after, or NULL for the first request + * \param moreFollows if provided by the caller (non NULL) the function will indicate if more directory entries + * are available. + * + * \return the list of directory entries. The return type is a LinkedList with FileDirectoryEntry elements + */ +LinkedList /**/ +IedConnection_getFileDirectoryEx(IedConnection self, IedClientError* error, const char* directoryName, const char* continueAfter, + bool* moreFollows); + /** * \brief user provided handler to receive the data of the GetFile request * @@ -1903,6 +1935,19 @@ uint32_t IedConnection_getFile(IedConnection self, IedClientError* error, const char* fileName, IedClientGetFileHandler handler, void* handlerParameter); +/** + * \brief Set the virtual filestore basepath for the setFile service + * + * All external file service accesses will be mapped to paths relative to the base directory. + * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME + * option in stack_config.h is set. + * + * \param self the connection object + * \param basepath the new virtual filestore basepath + */ +void +IedConnection_setFilestoreBasepath(IedConnection, const char* basepath); + /** * \brief Implementation of the SetFile ACSI service * diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index cf409da..90b266c 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -85,6 +85,19 @@ IedServer_destroy(IedServer self); void IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress); +/** + * \brief Set the virtual filestore basepath for the MMS file services + * + * All external file service accesses will be mapped to paths relative to the base directory. + * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME + * option in stack_config.h is set. + * + * \param self the IedServer instance + * \param basepath the new virtual filestore basepath + */ +void +IedServer_setFilestoreBasepath(IedServer self, const char* basepath); + /** * \brief Start handling client connections * diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index ebf1cc2..e82af82 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -604,6 +604,13 @@ IedServer_stop(IedServer self) } #endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */ +void +IedServer_setFilestoreBasepath(IedServer self, const char* basepath) +{ + /* simply pass to MMS server API */ + MmsServer_setFilestoreBasepath(self->mmsServer, basepath); +} + void IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress) { diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index 972f0fe..1f60c1c 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -106,6 +106,19 @@ typedef void (*MmsRawMessageHandler) (void* parameter, uint8_t* message, int mes void MmsConnection_setRawMessageHandler(MmsConnection self, MmsRawMessageHandler handler, void* parameter); +/** + * \brief Set the virtual filestore basepath for the MMS obtain file services + * + * All external file service accesses will be mapped to paths relative to the base directory. + * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME + * option in stack_config.h is set. + * + * \param self the MmsServer instance + * \param basepath the new virtual filestore basepath + */ +void +MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath); + /** * \brief Set the request timeout in ms for this connection * diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index 7aa4d67..23d128d 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -164,13 +164,61 @@ typedef void (*MmsGetFileCompleteHandler)(void* parameter, MmsServerConnection c * \brief Install callback handler that is invoked when the file upload (obtainFile service) is completed and the * file has been uploaded. * - * \param self the MmsServer instance to operate on + * \param self the MmsServer instance * \param handler the callback handler function * \param parameter user provided parameter that is passed to the callback handler */ void MmsServer_installGetFileCompleteHandler(MmsServer self, MmsGetFileCompleteHandler handler, void* parameter); + +typedef enum { + MMS_FILE_ACCESS_TYPE_READ_DIRECTORY, + MMS_FILE_ACCESS_TYPE_OPEN, + MMS_FILE_ACCESS_TYPE_OBTAIN, + MMS_FILE_ACCESS_TYPE_DELETE, + MMS_FILE_ACCESS_TYPE_RENAME +} MmsFileServiceType; + +/** + * \brief MmsFileAccessHandler callback function. Use to monitor and control file access + * + * \param parameter user provided parameter that is passed to the callback handler + * \param connection the connection that requested the service + * \param service the requested file service + * \param localFilename the requested file or directory name at the server + * \param otherFilename a second file name parameter (e.g. source file of the ObtainFile or new file of rename file) + * + * \return MMS_ERROR_NONE when the request is accepted, otherwise use the appropriate error code (e.g. MMS_ERROR_FILE_FILE_ACCESS_DENIED) + */ +typedef MmsError (*MmsFileAccessHandler) (void* parameter, MmsServerConnection connection, MmsFileServiceType service, + const char* localFilename, const char* otherFilename); + + +/** + * \brief Install a callback handler this is invoked when the client requests a file server. This function can be + * used to monitor and control file access + * + * \param self the MmsServer instance + * \param handler the callback handler function + * \param parameter user provided parameter that is passed to the callback handler + */ +void +MmsServer_installFileAccessHandler(MmsServer self, MmsFileAccessHandler handler, void* parameter); + +/** + * \brief Set the virtual filestore basepath for the MMS file services + * + * All external file service accesses will be mapped to paths relative to the base directory. + * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME + * option in stack_config.h is set. + * + * \param self the MmsServer instance + * \param basepath the new virtual filestore basepath + */ +void +MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath); + /** * \brief lock the cached server data model * @@ -198,9 +246,6 @@ void MmsServer_insertIntoCache(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value); -void -MmsServer_setDevice(MmsServer self, MmsDevice* device); - /*************************************************** * Functions for multi-threaded operation mode ***************************************************/ @@ -391,6 +436,7 @@ MmsServerConnection_getClientAddress(MmsServerConnection self); IsoConnection MmsServerConnection_getIsoConnection(MmsServerConnection self); + /**@}*/ #ifdef __cplusplus diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index af992ad..8eb23a4 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -102,7 +102,12 @@ struct sMmsConnection { #if (MMS_OBTAIN_FILE_SERVICE == 1) int32_t nextFrsmId; MmsFileReadStateMachine frsms[CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION]; + +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char* filestoreBasepath; #endif + +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ }; @@ -116,6 +121,9 @@ typedef enum { MMS_OBJECT_CLASS_DOMAIN = 9 } MmsObjectClass; +char* +MmsConnection_getFilestoreBasepath(MmsConnection self); + MmsValue* mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSize, bool createArray); diff --git a/src/mms/inc_private/mms_common_internal.h b/src/mms/inc_private/mms_common_internal.h index 0c97dde..e987fd1 100644 --- a/src/mms/inc_private/mms_common_internal.h +++ b/src/mms/inc_private/mms_common_internal.h @@ -28,6 +28,7 @@ #include "MmsPdu.h" #include "conversions.h" #include "byte_buffer.h" +#include "mms_server.h" #if (MMS_FILE_SERVICE == 1) @@ -60,8 +61,16 @@ void mmsMsg_createFileCloseResponse(uint32_t invokeId, ByteBuffer* response); void -mmsMsg_createFileOpenResponse(uint32_t invokeId, ByteBuffer* response, char* fullPath, MmsFileReadStateMachine* frsm); +mmsMsg_createFileOpenResponse(const char* basepath, uint32_t invokeId, ByteBuffer* response, char* fullPath, MmsFileReadStateMachine* frsm); +bool +mmsMsg_parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos , uint32_t invokeId, ByteBuffer* response); + +void +mmsMsg_createExtendedFilename(const char* basepath, char* extendedFileName, char* fileName); + +FileHandle +mmsMsg_openFile(const char* basepath, char* fileName, bool readWrite); #endif /* (MMS_FILE_SERVICE == 1) */ diff --git a/src/mms/inc_private/mms_server_connection.h b/src/mms/inc_private/mms_server_connection.h index 01901ec..e65c78a 100644 --- a/src/mms/inc_private/mms_server_connection.h +++ b/src/mms/inc_private/mms_server_connection.h @@ -101,6 +101,9 @@ MmsServerConnection_getLastInvokeId(MmsServerConnection self); uint32_t MmsServerConnection_getNextRequestInvokeId(MmsServerConnection self); +const char* +MmsServerConnection_getFilesystemBasepath(MmsServerConnection self); + #endif /* MMS_SERVER_CONNECTION_H_ */ diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 3393501..58655c8 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -156,6 +156,15 @@ struct sMmsServer { struct sMmsObtainFileTask fileUploadTasks[CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS]; #endif +#if (MMS_FILE_SERVICE == 1) + MmsFileAccessHandler fileAccessHandler; + void* fileAccessHandlerParameter; +#endif + +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char* filestoreBasepath; +#endif + }; struct sMmsServerConnection { diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index 00ad03f..3428c19 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -947,9 +947,50 @@ MmsConnection_destroy(MmsConnection self) GLOBAL_FREEMEM(self->outstandingCalls); +#if (MMS_OBTAIN_FILE_SERVICE == 1) +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + if (self->filestoreBasepath != NULL) + GLOBAL_FREEMEM(self->filestoreBasepath); +#endif +#endif + GLOBAL_FREEMEM(self); } +void +MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath) +{ +#if (MMS_OBTAIN_FILE_SERVICE == 1) +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + if (self->filestoreBasepath != NULL) { + GLOBAL_FREEMEM(self->filestoreBasepath); + self->filestoreBasepath = NULL; + } + + if (basepath != NULL) + self->filestoreBasepath = StringUtils_copyString(basepath); +#endif +#endif +} + +char* +MmsConnection_getFilestoreBasepath(MmsConnection self) +{ +#if (MMS_OBTAIN_FILE_SERVICE == 1) +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + if (self->filestoreBasepath != NULL) + return self->filestoreBasepath; + else + return CONFIG_VIRTUAL_FILESTORE_BASEPATH; +#else + return CONFIG_VIRTUAL_FILESTORE_BASEPATH; +#endif + +#else + return CONFIG_VIRTUAL_FILESTORE_BASEPATH; +#endif +} + void MmsConnection_setRawMessageHandler(MmsConnection self, MmsRawMessageHandler handler, void* parameter) { diff --git a/src/mms/iso_mms/client/mms_client_files.c b/src/mms/iso_mms/client/mms_client_files.c index b0e7549..5ac38e3 100644 --- a/src/mms/iso_mms/client/mms_client_files.c +++ b/src/mms/iso_mms/client/mms_client_files.c @@ -80,57 +80,6 @@ getNextFrsmId(MmsConnection connection) return nextFrsmId; } -//TODO remove redundancy (with server implementation) -static void -createExtendedFilename(char* extendedFileName, char* fileName) -{ - strcpy(extendedFileName, CONFIG_VIRTUAL_FILESTORE_BASEPATH); - strncat(extendedFileName, fileName, sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256); -} - -//TODO remove redundancy (with server implementation) -static FileHandle -openFile(char* fileName, bool readWrite) -{ - char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; - - createExtendedFilename(extendedFileName, fileName); - - return FileSystem_openFile(extendedFileName, readWrite); -} - -//TODO remove redundancy (with server implementation) -static bool -parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos , uint32_t invokeId, ByteBuffer* response) -{ - uint8_t tag = buffer[(*bufPos)++]; - int length; - - if (tag != 0x19) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return false; - } - - *bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos); - - if (*bufPos < 0) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return false; - } - - if (length > 255) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - return false; - } - - memcpy(filename, buffer + *bufPos, length); - filename[length] = 0; - *bufPos += length; - - return true; -} - - void mmsClient_handleFileOpenRequest( MmsConnection connection, @@ -152,7 +101,7 @@ mmsClient_handleFileOpenRequest( switch(tag) { case 0xa0: /* filename */ - if (!parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response)) return; hasFileName = true; @@ -175,14 +124,15 @@ mmsClient_handleFileOpenRequest( MmsFileReadStateMachine* frsm = getFreeFrsm(connection); if (frsm != NULL) { - FileHandle fileHandle = openFile(filename, false); + FileHandle fileHandle = mmsMsg_openFile(MmsConnection_getFilestoreBasepath(connection), filename, false); if (fileHandle != NULL) { frsm->fileHandle = fileHandle; frsm->readPosition = filePosition; frsm->frsmId = getNextFrsmId(connection); - mmsMsg_createFileOpenResponse(invokeId, response, filename, frsm); + mmsMsg_createFileOpenResponse(MmsConnection_getFilestoreBasepath(connection), + invokeId, response, filename, frsm); } else mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT); diff --git a/src/mms/iso_mms/common/mms_common_msg.c b/src/mms/iso_mms/common/mms_common_msg.c index 36bdd7e..55da14d 100644 --- a/src/mms/iso_mms/common/mms_common_msg.c +++ b/src/mms/iso_mms/common/mms_common_msg.c @@ -1,7 +1,7 @@ /* * mms_common_msg.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013 - 2017 Michael Zillgith * * This file is part of libIEC61850. * @@ -25,6 +25,7 @@ #include "mms_common_internal.h" #include "stack_config.h" #include "mms_value_internal.h" +#include "ber_decode.h" static void mmsMsg_createFloatData(MmsValue* value, int* size, uint8_t** buf) @@ -342,3 +343,72 @@ mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, i buffer[0] = 0; } } + +void +mmsMsg_createExtendedFilename(const char* basepath, char* extendedFileName, char* fileName) +{ +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + // strncpy(extendedFileName, MmsServerConnection_getFilesystemBasepath(self), 512); + strncpy(extendedFileName, basepath, 512); + strncat(extendedFileName, fileName, 512); +#else + strcpy(extendedFileName, CONFIG_VIRTUAL_FILESTORE_BASEPATH); + strncat(extendedFileName, fileName, sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256); +#endif +} + + + +FileHandle +mmsMsg_openFile(const char* basepath, char* fileName, bool readWrite) +{ +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char extendedFileName[512]; +#else + char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; +#endif + + mmsMsg_createExtendedFilename(basepath, extendedFileName, fileName); + + return FileSystem_openFile(extendedFileName, readWrite); +} + + +bool +mmsMsg_parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos , uint32_t invokeId, ByteBuffer* response) +{ + uint8_t tag = buffer[(*bufPos)++]; + int length; + + if (tag != 0x19) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return false; + } + + *bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos); + + if (*bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return false; + } + + if (length > 255) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + return false; + } + + memcpy(filename, buffer + *bufPos, length); + filename[length] = 0; + *bufPos += length; + + /* Check if path contains invalid characters (prevent escaping the virtual filestore by using "..") + * TODO this may be platform dependent. Also depending of the platform there might be other evil + * characters. + */ + if (strstr(filename, "..") != NULL) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILENAME_SYNTAX_ERROR); + return false; + } + + return true; +} diff --git a/src/mms/iso_mms/server/mms_file_service.c b/src/mms/iso_mms/server/mms_file_service.c index ead7571..0bc5dcc 100644 --- a/src/mms/iso_mms/server/mms_file_service.c +++ b/src/mms/iso_mms/server/mms_file_service.c @@ -122,69 +122,85 @@ encodeFileAttributes(uint8_t tag, uint32_t fileSize, char* gtString, uint8_t* bu } } -static void -createExtendedFilename(char* extendedFileName, char* fileName) -{ - strcpy(extendedFileName, CONFIG_VIRTUAL_FILESTORE_BASEPATH); - strncat(extendedFileName, fileName, sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256); -} - static bool -getFileInfo(char* filename, uint32_t* fileSize, uint64_t* lastModificationTimestamp) +getFileInfo(const char* basepath, char* filename, uint32_t* fileSize, uint64_t* lastModificationTimestamp) { +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char extendedFileName[512]; +#else char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; +#endif - createExtendedFilename(extendedFileName, filename); + mmsMsg_createExtendedFilename(basepath, extendedFileName, filename); return FileSystem_getFileInfo(extendedFileName, fileSize, lastModificationTimestamp); } static FileHandle -openFile(char* fileName, bool readWrite) +openFile(const char* basepath, char* fileName, bool readWrite) { +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char extendedFileName[512]; +#else char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; +#endif - createExtendedFilename(extendedFileName, fileName); + mmsMsg_createExtendedFilename(basepath, extendedFileName, fileName); return FileSystem_openFile(extendedFileName, readWrite); } static DirectoryHandle -openDirectory(char* directoryName) +openDirectory(const char* basepath, char* directoryName) { +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char extendedFileName[512]; +#else char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; +#endif - createExtendedFilename(extendedFileName, directoryName); + mmsMsg_createExtendedFilename(basepath, extendedFileName, directoryName); return FileSystem_openDirectory(extendedFileName); } static bool -renameFile(char* oldFilename, char* newFilename) { +renameFile(const char* basepath, char* oldFilename, char* newFilename) { + +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char extendedOldFileName[512]; + char extendedNewFileName[512]; +#else char extendedOldFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; char extendedNewFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; +#endif - createExtendedFilename(extendedOldFileName, oldFilename); - createExtendedFilename(extendedNewFileName, newFilename); + mmsMsg_createExtendedFilename(basepath, extendedOldFileName, oldFilename); + mmsMsg_createExtendedFilename(basepath, extendedNewFileName, newFilename); return FileSystem_renameFile(extendedOldFileName, extendedNewFileName); } static bool -deleteFile(char* fileName) { +deleteFile(const char* basepath, char* fileName) { +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char extendedFileName[512]; +#else char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; +#endif - createExtendedFilename(extendedFileName, fileName); + mmsMsg_createExtendedFilename(basepath, extendedFileName, fileName); return FileSystem_deleteFile(extendedFileName); } void -mmsMsg_createFileOpenResponse(uint32_t invokeId, ByteBuffer* response, char* fullPath, MmsFileReadStateMachine* frsm) +mmsMsg_createFileOpenResponse(const char* basepath, uint32_t invokeId, ByteBuffer* response, + char* filename, MmsFileReadStateMachine* frsm) { uint64_t msTime; - getFileInfo(fullPath, &(frsm->fileSize), &msTime); + getFileInfo(basepath, filename, &(frsm->fileSize), &msTime); char gtString[30]; @@ -219,35 +235,7 @@ mmsMsg_createFileOpenResponse(uint32_t invokeId, ByteBuffer* response, char* ful response->size = bufPos; } -static bool -parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos , uint32_t invokeId, ByteBuffer* response) -{ - uint8_t tag = buffer[(*bufPos)++]; - int length; - if (tag != 0x19) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return false; - } - - *bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos); - - if (*bufPos < 0) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return false; - } - - if (length > 255) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - return false; - } - - memcpy(filename, buffer + *bufPos, length); - filename[length] = 0; - *bufPos += length; - - return true; -} void mmsServer_handleFileDeleteRequest( @@ -271,7 +259,17 @@ mmsServer_handleFileDeleteRequest( if (DEBUG_MMS_SERVER) printf("MMS_SERVER: mms_file_service.c: Delete file (%s)\n", filename); - if (!getFileInfo(filename, NULL, NULL)) { + if (connection->server->fileAccessHandler != NULL) { + MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter, + connection, MMS_FILE_ACCESS_TYPE_DELETE, filename, NULL); + + if (access != MMS_ERROR_NONE) { + mmsMsg_createServiceErrorPdu(invokeId, response, access); + return; + } + } + + if (!getFileInfo(MmsServerConnection_getFilesystemBasepath(connection), filename, NULL, NULL)) { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: mms_file_service.c: File (%s) not found\n", filename); @@ -279,7 +277,7 @@ mmsServer_handleFileDeleteRequest( return; } - if (!deleteFile(filename)) { + if (!deleteFile(MmsServerConnection_getFilesystemBasepath(connection), filename)) { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: mms_file_service.c: Delete file (%s) failed\n", filename); @@ -316,7 +314,7 @@ mmsServer_handleFileOpenRequest( switch(tag) { case 0xa0: /* filename */ - if (!parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response)) return; hasFileName = true; @@ -336,17 +334,28 @@ mmsServer_handleFileOpenRequest( if (hasFileName) { + if (connection->server->fileAccessHandler != NULL) { + MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter, + connection, MMS_FILE_ACCESS_TYPE_OPEN, filename, NULL); + + if (access != MMS_ERROR_NONE) { + mmsMsg_createServiceErrorPdu(invokeId, response, access); + return; + } + } + MmsFileReadStateMachine* frsm = getFreeFrsm(connection); if (frsm != NULL) { - FileHandle fileHandle = openFile(filename, false); + FileHandle fileHandle = openFile(MmsServerConnection_getFilesystemBasepath(connection), filename, false); if (fileHandle != NULL) { frsm->fileHandle = fileHandle; frsm->readPosition = filePosition; frsm->frsmId = getNextFrsmId(connection); - mmsMsg_createFileOpenResponse(invokeId, response, filename, frsm); + mmsMsg_createFileOpenResponse(MmsServerConnection_getFilesystemBasepath(connection), + invokeId, response, filename, frsm); } else mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT); @@ -394,9 +403,6 @@ createObtainFileResponse(uint32_t invokeId, ByteBuffer* response) void mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task) { - - //printf("mmsServer_fileUploadTask: state: %i\n", task->state); - switch (task->state) { case MMS_FILE_UPLOAD_STATE_NOT_USED: @@ -412,7 +418,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task) task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; FileSystem_closeFile(task->fileHandle); - deleteFile(task->destinationFilename); + deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename); } } break; @@ -446,7 +452,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task) task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; FileSystem_closeFile(task->fileHandle); - deleteFile(task->destinationFilename); + deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename); } break; @@ -479,7 +485,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task) task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; FileSystem_closeFile(task->fileHandle); - deleteFile(task->destinationFilename); + deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename); } break; @@ -568,7 +574,7 @@ mmsServer_handleObtainFileRequest( case 0xa1: /* source filename */ - if (!parseFileName(sourceFilename, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(sourceFilename, buffer, &bufPos, bufPos + length, invokeId, response)) return; hasSourceFileName = true; @@ -577,7 +583,7 @@ mmsServer_handleObtainFileRequest( case 0xa2: /* destination filename */ - if (!parseFileName(destinationFilename, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(destinationFilename, buffer, &bufPos, bufPos + length, invokeId, response)) return; hasDestinationFilename = true; @@ -592,14 +598,31 @@ mmsServer_handleObtainFileRequest( if (hasSourceFileName && hasDestinationFilename) { - /* call callback to check if access is allowed */ + /* Call user to check if access is allowed */ + if (connection->server->fileAccessHandler != NULL) { + MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter, + connection, MMS_FILE_ACCESS_TYPE_OBTAIN, destinationFilename, sourceFilename); + + if (access != MMS_ERROR_NONE) { + mmsMsg_createServiceErrorPdu(invokeId, response, access); + return; + } + } + + /* call callback to check if access is allowed -- DEPRECATED */ if (connection->server->obtainFileHandler) if (connection->server->obtainFileHandler(connection->server->obtainFileHandlerParameter, connection, sourceFilename, destinationFilename) == false) goto exit_access_denied; /* check if destination file already exists. If exists return error message */ + +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + char extendedFileName[512]; +#else char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; - createExtendedFilename(extendedFileName, destinationFilename); +#endif + mmsMsg_createExtendedFilename(MmsServerConnection_getFilesystemBasepath(connection), + extendedFileName, destinationFilename); if (FileSystem_getFileInfo(extendedFileName, NULL, NULL)) { if (DEBUG_MMS_SERVER) @@ -614,7 +637,8 @@ mmsServer_handleObtainFileRequest( if (task != NULL) { - FileHandle fileHandle = openFile(destinationFilename, true); + FileHandle fileHandle = openFile(MmsServerConnection_getFilesystemBasepath(connection), + destinationFilename, true); if (fileHandle == NULL) { task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_DESTINATION; @@ -792,11 +816,11 @@ encodeFileSpecification(uint8_t tag, char* fileSpecification, uint8_t* buffer, i } static int -addFileEntriesToResponse(uint8_t* buffer, int bufPos, int maxBufSize, char* directoryName, char** continueAfterFileName, bool* moreFollows) +addFileEntriesToResponse(const char* basepath, uint8_t* buffer, int bufPos, int maxBufSize, char* directoryName, char** continueAfterFileName, bool* moreFollows) { int directoryNameLength = strlen(directoryName); - DirectoryHandle directory = openDirectory(directoryName); + DirectoryHandle directory = openDirectory(basepath, directoryName); if (directory != NULL) { @@ -813,7 +837,7 @@ addFileEntriesToResponse(uint8_t* buffer, int bufPos, int maxBufSize, char* dire strcat(directoryName, fileName); - bufPos = addFileEntriesToResponse(buffer, bufPos, maxBufSize, directoryName, continueAfterFileName, moreFollows); + bufPos = addFileEntriesToResponse(basepath, buffer, bufPos, maxBufSize, directoryName, continueAfterFileName, moreFollows); if (*moreFollows == true) break; @@ -835,7 +859,7 @@ addFileEntriesToResponse(uint8_t* buffer, int bufPos, int maxBufSize, char* dire uint32_t fileSize; - if (getFileInfo(directoryName, &fileSize, &msTime)) { + if (getFileInfo(basepath, directoryName, &fileSize, &msTime)) { char gtString[30]; Conversions_msTimeToGeneralizedTime(msTime, (uint8_t*) gtString); @@ -873,7 +897,7 @@ addFileEntriesToResponse(uint8_t* buffer, int bufPos, int maxBufSize, char* dire } static void -createFileDirectoryResponse(uint32_t invokeId, ByteBuffer* response, int maxPduSize, char* directoryName, char* continueAfterFileName) +createFileDirectoryResponse(const char* basepath, uint32_t invokeId, ByteBuffer* response, int maxPduSize, char* directoryName, char* continueAfterFileName) { int maxSize = maxPduSize - 3; /* reserve space for moreFollows */ uint8_t* buffer = response->buffer; @@ -889,7 +913,7 @@ createFileDirectoryResponse(uint32_t invokeId, ByteBuffer* response, int maxPduS continueAfterFileName = NULL; } - tempCurPos = addFileEntriesToResponse(buffer, tempCurPos, maxSize, directoryName, &continueAfterFileName, &moreFollows); + tempCurPos = addFileEntriesToResponse(basepath, buffer, tempCurPos, maxSize, directoryName, &continueAfterFileName, &moreFollows); if (tempCurPos < 0) { @@ -965,7 +989,7 @@ mmsServer_handleFileRenameRequest( switch(tag) { case 0xa0: /* currentFilename */ - if (!parseFileName(currentFileName, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(currentFileName, buffer, &bufPos, bufPos + length, invokeId, response)) return; if (DEBUG_MMS_SERVER) @@ -974,7 +998,7 @@ mmsServer_handleFileRenameRequest( break; case 0xa1: /* newFilename */ - if (!parseFileName(newFileName, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(newFileName, buffer, &bufPos, bufPos + length, invokeId, response)) return; if (DEBUG_MMS_SERVER) @@ -991,7 +1015,19 @@ mmsServer_handleFileRenameRequest( } if ((strlen(currentFileName) != 0) && (strlen(newFileName) != 0)) { - if (renameFile(currentFileName, newFileName)){ + + /* Call user to check if access is allowed */ + if (connection->server->fileAccessHandler != NULL) { + MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter, + connection, MMS_FILE_ACCESS_TYPE_RENAME, currentFileName, newFileName); + + if (access != MMS_ERROR_NONE) { + mmsMsg_createServiceErrorPdu(invokeId, response, access); + return; + } + } + + if (renameFile(MmsServerConnection_getFilesystemBasepath(connection), currentFileName, newFileName)){ /* send positive response */ createNullResponseExtendedTag(invokeId, response, 0x4b); } @@ -1037,7 +1073,7 @@ mmsServer_handleFileDirectoryRequest( switch(tag) { case 0xa0: /* filename */ - if (!parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response)) return; /* check for wildcard character(*) */ @@ -1047,7 +1083,7 @@ mmsServer_handleFileDirectoryRequest( break; case 0xa1: /* continue-after */ - if (!parseFileName(continueAfterFileName, buffer, &bufPos, bufPos + length, invokeId, response)) + if (!mmsMsg_parseFileName(continueAfterFileName, buffer, &bufPos, bufPos + length, invokeId, response)) return; continueAfter = continueAfterFileName; @@ -1066,7 +1102,19 @@ mmsServer_handleFileDirectoryRequest( int maxPduSize = connection->maxPduSize; - createFileDirectoryResponse(invokeId, response, maxPduSize, filename, continueAfter); + /* Call user to check if access is allowed */ + if (connection->server->fileAccessHandler != NULL) { + MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter, + connection, MMS_FILE_ACCESS_TYPE_READ_DIRECTORY, filename, continueAfter); + + if (access != MMS_ERROR_NONE) { + mmsMsg_createServiceErrorPdu(invokeId, response, access); + return; + } + } + + createFileDirectoryResponse(MmsServerConnection_getFilesystemBasepath(connection), + invokeId, response, maxPduSize, filename, continueAfter); } #endif /* MMS_FILE_SERVICE == 1 */ diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index efb7dbb..75985d6 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -50,9 +50,7 @@ createValueCaches(MmsDevice* device) MmsServer MmsServer_create(IsoServer isoServer, MmsDevice* device) { - MmsServer self = (MmsServer) GLOBAL_MALLOC(sizeof(struct sMmsServer)); - - memset(self, 0, sizeof(struct sMmsServer)); + MmsServer self = (MmsServer) GLOBAL_CALLOC(1, sizeof(struct sMmsServer)); self->isoServer = isoServer; self->device = device; @@ -72,6 +70,22 @@ MmsServer_create(IsoServer isoServer, MmsDevice* device) return self; } + +void +MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath) +{ +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + if (self->filestoreBasepath != NULL) { + GLOBAL_FREEMEM(self->filestoreBasepath); + self->filestoreBasepath = NULL; + } + + if (basepath != NULL) + self->filestoreBasepath = StringUtils_copyString(basepath); +#endif /* (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) */ +} + + void MmsServer_lockModel(MmsServer self) { @@ -169,6 +183,15 @@ MmsServer_setClientAuthenticator(MmsServer self, AcseAuthenticator authenticator IsoServer_setAuthenticator(self->isoServer, authenticator, authenticatorParameter); } +#if (MMS_FILE_SERVICE == 1) +void +MmsServer_installFileAccessHandler(MmsServer self, MmsFileAccessHandler handler, void* parameter) +{ + self->fileAccessHandler = handler; + self->fileAccessHandlerParameter = parameter; +} +#endif /* (MMS_FILE_SERVICE == 1) */ + #if (MMS_OBTAIN_FILE_SERVICE == 1) void MmsServer_installObtainFileHandler(MmsServer self, MmsObtainFileHandler handler, void* parameter) @@ -212,6 +235,11 @@ MmsServer_destroy(MmsServer self) ByteBuffer_destroy(self->transmitBuffer); +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + if (self->filestoreBasepath != NULL) + GLOBAL_FREEMEM(self->filestoreBasepath); +#endif + GLOBAL_FREEMEM(self); } @@ -298,12 +326,6 @@ MmsServer_getDevice(MmsServer self) return self->device; } -inline void -MmsServer_setDevice(MmsServer server, MmsDevice* device) -{ - server->device = device; -} - static void /* will be called by ISO server stack */ isoConnectionIndicationHandler(IsoConnectionIndication indication, void* parameter, IsoConnection connection) diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c index 87249e7..494a606 100644 --- a/src/mms/iso_mms/server/mms_server_connection.c +++ b/src/mms/iso_mms/server/mms_server_connection.c @@ -634,3 +634,20 @@ MmsServerConnection_getNextRequestInvokeId(MmsServerConnection self) return self->lastRequestInvokeId; } #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ + + +const char* +MmsServerConnection_getFilesystemBasepath(MmsServerConnection self) +{ +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + if (self->server->filestoreBasepath != NULL) + return self->server->filestoreBasepath; + else + return CONFIG_VIRTUAL_FILESTORE_BASEPATH; +#else + return CONFIG_VIRTUAL_FILESTORE_BASEPATH; +#endif +} + + + diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index 4a8cfc1..0efd583 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -563,3 +563,9 @@ EXPORTS Timestamp_setByMmsUtcTime IedServer_setLocalIpAddress Timestamp_toMmsValue + MmsServer_installFileAccessHandler + MmsServer_setFilestoreBasepath + MmsConnection_setFilestoreBasepath + IedConnection_setFilestoreBasepath + IedServer_setFilestoreBasepath + \ No newline at end of file diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index 1769ee9..4853466 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -641,3 +641,8 @@ EXPORTS Timestamp_setByMmsUtcTime IedServer_setLocalIpAddress Timestamp_toMmsValue + MmsServer_installFileAccessHandler + MmsServer_setFilestoreBasepath + MmsConnection_setFilestoreBasepath + IedConnection_setFilestoreBasepath + IedServer_setFilestoreBasepath From 6badf692fed506323cf6dabe0ddc4bda3c1fa16e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 30 May 2017 11:50:30 +0200 Subject: [PATCH 4/4] - updated client example for IEC 61850 file services --- .../CMakeLists.txt | 6 +- .../iec61850_client_example_files/Makefile | 4 +- .../client_example_files.c | 107 ------- .../iec61850_client_example_files/file-tool.c | 261 ++++++++++++++++++ .../server_example_files.c | 2 +- 5 files changed, 267 insertions(+), 113 deletions(-) delete mode 100644 examples/iec61850_client_example_files/client_example_files.c create mode 100644 examples/iec61850_client_example_files/file-tool.c diff --git a/examples/iec61850_client_example_files/CMakeLists.txt b/examples/iec61850_client_example_files/CMakeLists.txt index 36cba27..9178d24 100644 --- a/examples/iec61850_client_example_files/CMakeLists.txt +++ b/examples/iec61850_client_example_files/CMakeLists.txt @@ -1,6 +1,6 @@ set(iec61850_client_example_files_SRCS - client_example_files.c + file-tool.c ) IF(MSVC) @@ -8,10 +8,10 @@ set_source_files_properties(${iec61850_client_example_files_SRCS} PROPERTIES LANGUAGE CXX) ENDIF(MSVC) -add_executable(iec61850_client_example_files +add_executable(file-tool ${iec61850_client_example_files_SRCS} ) -target_link_libraries(iec61850_client_example_files +target_link_libraries(file-tool iec61850 ) diff --git a/examples/iec61850_client_example_files/Makefile b/examples/iec61850_client_example_files/Makefile index eea5d9f..c4d0ecb 100644 --- a/examples/iec61850_client_example_files/Makefile +++ b/examples/iec61850_client_example_files/Makefile @@ -1,7 +1,7 @@ LIBIEC_HOME=../.. -PROJECT_BINARY_NAME = client_example_files -PROJECT_SOURCES = client_example_files.c +PROJECT_BINARY_NAME = file-tool +PROJECT_SOURCES = file-tool.c include $(LIBIEC_HOME)/make/target_system.mk include $(LIBIEC_HOME)/make/stack_includes.mk diff --git a/examples/iec61850_client_example_files/client_example_files.c b/examples/iec61850_client_example_files/client_example_files.c deleted file mode 100644 index f321f83..0000000 --- a/examples/iec61850_client_example_files/client_example_files.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * client_example_files.c - * - * This example demonstrates the usage of the file services - * - * - How to browse the file system of the server - * - How to download a file from the server - * - * Note: intended to be used with server_example3 - * - */ - -#include "iec61850_client.h" - -#include -#include - -#include "hal_thread.h" - -#define MAX_BUFFER_SIZE 2000000 - -static uint8_t downloadBuffer[MAX_BUFFER_SIZE]; -static int bufferPosition = 0; - -static bool -downloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead) -{ - printf("received %i bytes\n", bytesRead); - - if (bufferPosition + bytesRead < MAX_BUFFER_SIZE) { - memcpy(downloadBuffer + bufferPosition, buffer, bytesRead); - - bufferPosition += bytesRead; - - return true; - } - else - return false; -} - -int main(int argc, char** argv) { - - char* hostname; - int tcpPort = 102; - - if (argc > 1) - hostname = argv[1]; - else - hostname = "localhost"; - - if (argc > 2) - tcpPort = atoi(argv[2]); - - IedClientError error; - - IedConnection con = IedConnection_create(); - - IedConnection_connect(con, &error, hostname, tcpPort); - - if (error == IED_ERROR_OK) { - - /* Get the root directory */ - LinkedList rootDirectory = - IedConnection_getFileDirectory(con, &error, NULL); - - if (error != IED_ERROR_OK) { - printf("Error retrieving file directory\n"); - goto abort_connection; - } - - - LinkedList directoryEntry = LinkedList_getNext(rootDirectory); - - while (directoryEntry != NULL) { - - FileDirectoryEntry entry = (FileDirectoryEntry) directoryEntry->data; - - printf("%s %i\n", FileDirectoryEntry_getFileName(entry), FileDirectoryEntry_getFileSize(entry)); - - directoryEntry = LinkedList_getNext(directoryEntry); - } - - - /* Download a file from the server */ - IedConnection_getFile(con, &error, "IEDSERVER.BIN", downloadHandler, NULL); - - if (error != IED_ERROR_OK) - printf("Failed to get file!\n"); - - /* Delete file at server */ - IedConnection_deleteFile(con, &error, "IEDSERVER.BIN"); - - if (error != IED_ERROR_OK) - printf("Failed to delete file! (code=%i)\n", error); - - abort_connection: - - IedConnection_abort(con, &error); - } - else { - printf("Failed to connect to %s:%i\n", hostname, tcpPort); - } - - IedConnection_destroy(con); -} - - diff --git a/examples/iec61850_client_example_files/file-tool.c b/examples/iec61850_client_example_files/file-tool.c new file mode 100644 index 0000000..492dd0c --- /dev/null +++ b/examples/iec61850_client_example_files/file-tool.c @@ -0,0 +1,261 @@ +/* + * file-tool.c + * + * This example demonstrates the usage of the file services + * + * - How to browse the file system of the server + * - How to download a file from the server + * + * Note: intended to be used with server_example3 or server_example_files + * + */ + +#include "iec61850_client.h" + +#include +#include +#include + +static char* hostname = "localhost"; +static int tcpPort = 102; +static char* filename = NULL; + +typedef enum { + FileOperationType_None = 0, + FileOperationType_Dir, + FileOperationType_Info, + FileOperationType_Del, + FileOperationType_Get, + FileOperationType_Set +} FileOperationType; + +static FileOperationType operation = FileOperationType_None; + + + +static bool +downloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead) +{ + FILE* fp = (FILE*) parameter; + + printf("received %i bytes\n", bytesRead); + + if (fwrite(buffer, bytesRead, 1, fp) == 1) + return true; + else { + printf("Failed to write local file!\n"); + return false; + } + +} + +static void +printHelp() +{ + printf("file-tool [options] []\n"); + printf(" Options:\n"); + printf(" -h \n"); + printf(" -p portnumber\n"); + printf(" Operations\n"); + printf(" dir - show directory\n"); + printf(" info - show file info\n"); + printf(" del - delete file\n"); + printf(" get - get file\n"); + printf(" set - set file\n"); +} + + +static int +parseOptions(int argc, char** argv) +{ + int currentArgc = 1; + + int retVal = 0; + + while (currentArgc < argc) { + if (strcmp(argv[currentArgc], "-h") == 0) { + hostname = argv[++currentArgc]; + } + else if (strcmp(argv[currentArgc], "-p") == 0) { + tcpPort = atoi(argv[++currentArgc]); + } + else if (strcmp(argv[currentArgc], "del") == 0) { + operation = FileOperationType_Del; + filename = argv[++currentArgc]; + } + else if (strcmp(argv[currentArgc], "dir") == 0) { + operation = FileOperationType_Dir; + } + else if (strcmp(argv[currentArgc], "info") == 0) { + operation = FileOperationType_Info; + filename = argv[++currentArgc]; + } + else if (strcmp(argv[currentArgc], "get") == 0) { + operation = FileOperationType_Get; + filename = argv[++currentArgc]; + } + else if (strcmp(argv[currentArgc], "set") == 0) { + operation = FileOperationType_Set; + filename = argv[++currentArgc]; + } + else { + printf("Unknown operation!\n"); + return 1; + } + + currentArgc++; + } + + return retVal; +} + +void +showDirectory(IedConnection con) +{ + IedClientError error; + + /* Get the root directory */ + LinkedList rootDirectory = + IedConnection_getFileDirectory(con, &error, NULL); + + if (error != IED_ERROR_OK) { + printf("Error retrieving file directory\n"); + } + else { + LinkedList directoryEntry = LinkedList_getNext(rootDirectory); + + while (directoryEntry != NULL) { + + FileDirectoryEntry entry = (FileDirectoryEntry) directoryEntry->data; + + printf("%s %i\n", FileDirectoryEntry_getFileName(entry), FileDirectoryEntry_getFileSize(entry)); + + directoryEntry = LinkedList_getNext(directoryEntry); + } + + LinkedList_destroyDeep(rootDirectory, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); + } +} + +void +getFile(IedConnection con) +{ + IedClientError error; + + char* bname = strdup(filename); + + char* localFilename = basename(bname); + + FILE* fp = fopen(localFilename, "w"); + + if (fp != NULL) { + + /* Download a file from the server */ + IedConnection_getFile(con, &error, filename, downloadHandler, (void*) fp); + + if (error != IED_ERROR_OK) + printf("Failed to get file!\n"); + + fclose(fp); + } + else + printf("Failed to open file %s\n", localFilename); + + free(bname); +} + +void +setFile(IedConnection con) +{ + IedClientError error; + + char* dirc = strdup(filename); + char* basec = strdup(filename); + + char* localDirName = dirname(dirc); + char* localFileName = basename(basec); + + printf("local dir: %s\n", localDirName); + printf("local file: %s\n", localFileName); + + /* IedConnection_setFilestoreBasepath requires the file separator at the end! */ + strcpy(dirc, localDirName); + strcat(dirc, "/"); + + printf("filestore basepath: %s\n", dirc); + + IedConnection_setFilestoreBasepath(con, dirc); + + IedConnection_setFile(con, &error, localFileName, localFileName); + + if (error != IED_ERROR_OK) + printf("Failed to set file! (code=%i)\n", error); + + free(dirc); + free(basec); +} + +void +deleteFile(IedConnection con) +{ + IedClientError error; + + /* Delete file at server */ + IedConnection_deleteFile(con, &error, filename); + + if (error != IED_ERROR_OK) + printf("Failed to delete file! (code=%i)\n", error); +} + +int +main(int argc, char** argv) +{ + if (argc < 2) { + printHelp(); + return 0; + } + + parseOptions(argc, argv); + + if (operation == FileOperationType_None) { + printHelp(); + return 0; + } + + IedClientError error; + + IedConnection con = IedConnection_create(); + + IedConnection_connect(con, &error, hostname, tcpPort); + + if (error == IED_ERROR_OK) { + + + switch (operation) { + case FileOperationType_Dir: + showDirectory(con); + break; + case FileOperationType_Get: + getFile(con); + break; + case FileOperationType_Del: + deleteFile(con); + break; + case FileOperationType_Info: + break; + case FileOperationType_Set: + setFile(con); + break; + } + + + IedConnection_abort(con, &error); + } + else { + printf("Failed to connect to %s:%i\n", hostname, tcpPort); + } + + IedConnection_destroy(con); +} + + diff --git a/examples/server_example_files/server_example_files.c b/examples/server_example_files/server_example_files.c index 9b263bb..b2a1b07 100644 --- a/examples/server_example_files/server_example_files.c +++ b/examples/server_example_files/server_example_files.c @@ -39,7 +39,7 @@ static MmsError fileAccessHandler (void* parameter, MmsServerConnection connection, MmsFileServiceType service, const char* localFilename, const char* otherFilename) { - printf("fileAccessHanlder: service = %i, local-file: %s other-file: %s\n", service, localFilename, otherFilename); + printf("fileAccessHandler: service = %i, local-file: %s other-file: %s\n", service, localFilename, otherFilename); /* Don't allow client to rename files */ if (service == MMS_FILE_ACCESS_TYPE_RENAME)