diff --git a/CHANGELOG b/CHANGELOG index 6fc41699b..7f6e4b20d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,42 @@ +Changes to version 1.3.0 +------------------------ +- IEC 61850 server: more features configurable at runtime +- IEC 61850 server: control objects - fixed bug in select response for SBO control model +- IEC 61850 client: add support for single array element access (with component specification) +- MMS server: add support for array element (index) access with nested component +- IEC 61850 server: made IEC 61850 edition configurable at runtime +- IEC 61850 server: added ReadAccessHandler to control read access +- HAL: unified platform abstraction layer (to simplify using the library together with lib60870) +- IEC 61850 server: fixed bug when calling write access handler (wrong pointer for ClientConnection object) +- updated IEC 61850-9-2 LE example to be more realistic +- added server side example for the substitution service +- MMS server: fixed wrong preprocessor defines that can cause problems in some configurations (unlimited number of client connections/ multi-threaded server) +- IEC 61850 client: added new function ControlObjectClient_getCtlValType to simplify control handling +- IEC 61850 server: reporting - don't delete pending events when buffered report is enabled and dataset didn't change +- fixed bug in MmsValue_update +- MMS server: fixed bug in delete variable list service - scope of delete was not considered optional +- some more small bug fixes and optimizations + + +Changes to version 1.2.2 +------------------------ + +- IEC 61850 server: added support to configure report buffer size at runtime +- IEC 61850 server: new IedServerConfig type and new IedServer constructor +- .NET API: added support for IedServerConfig +- IEC 61850 server: prevent sending reports when data model is locked (updated) +- TLS client: fixed problem with high CPU load +- ISO connection: fixed race condition that can cause corrupted messages +- .NET API: added project files for .NET core 2.0 +- .NET API: added server side support for TLS + +Changes to version 1.2.1 +------------------------ + +- IEC 61850 server: fixed bug in report module when RCB was enabled multiple times (was new in 1.2.0) +- .NET API: Added destructor and Dispose method to ReportControlBlock (fixed memory leak) +- .NET API: Changed ReportControlBlock access to IedConnection to improve stability when connection closes unexpectedly + Changes to version 1.2.0 ------------------------ @@ -5,6 +44,44 @@ Changes to version 1.2.0 - IEC 61850/MMS server: removed deprecated AttributeChangedHandler - Added pkg-config file - The Sampled Values APIs have been renamed. The old version of the API is deprecated but still supported and will be removed in the next major version of the library. +- SV Publisher/Subscriber: a lot of small fixed and improvements +- .NET API: Added support for sampled values (SV) subscriber +- .NET API: Added support for GOOSE subscriber +- SV subscriber: added function SVReceiver_enableDestAddrCheck +- IEC 61850 server: fixed bug in buffered report module - report can be lost under some circumstances when BRCB is enabled +- SV subscriber: replaced code that caused unaligned memory access +- IEC 61850 server: added memory alignement for buffered reporting + +Changes to version 1.1.2 +------------------------ + +- MMS client: fixed parsing initiate response message +- SV publisher: conditional encoding for SmpRate +- MmsValue_update function now allows adjusting octet-string size of target object +- .NET API: Added DeleteFile +- CDC helper functions: added helper functions for VSS and VSG CDC +- added additional locks in client and server + +Changes to version 1.1.1 +------------------------ + +- IEC 61850 client: fixed bug in APC control handling +- IEC 61850 client: ClientReportControlBlock now accepts "$" and "." as seperator for RCB object reference +- MMS client: fixed bug in MmsConnection_connect (COTP payload buffer was not reset in case of an error during connect -> connection failed in case of reuse of MmsConnection object +- MMS client: delete named variable list service supports VMD specific lists +- SV subscriber/publisher: additional features and bug fixes +- SV: fixed data type for smpRate +- SV: fixed encoding of optional smpMod attribute +- SV receiver: Added semaphore to make subscriber list thread-safe +- .NET API: ControlObject implements IDisposable interface +- IED server: added new function IedServer_udpateDbposValue +- fixed problem with cmake include folders +- MMS client: file services -fixed encoding problem with long file names +- MMS server: ACSE authenticator passes application reference (ap-title and ae-qualifier) +- example directory cleanup +- MMS: fixed potential memory leak in asn1 code that can be caused by malformed MMS messages +- MMS client: MmsConnection_getVariableAccessAttributes support for VMD specific variables +- Java SCL parser: added support for "Val" elements for Octet64 types Changes to version 1.1.0 ------------------------ diff --git a/CMakeLists.txt b/CMakeLists.txt index e6b0160e3..c5c53a154 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(libiec61850) ENABLE_TESTING() set(LIB_VERSION_MAJOR "1") -set(LIB_VERSION_MINOR "2") +set(LIB_VERSION_MINOR "3") set(LIB_VERSION_PATCH "0") set(LIB_VERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}") @@ -42,7 +42,7 @@ option(CONFIG_IEC61850_REPORT_SERVICE "Build with support for IEC 61850 reportin option(CONFIG_IEC61850_LOG_SERVICE "Build with support for IEC 61850 logging services" ON) option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON) -set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "8000" CACHE STRING "Default buffer size for buffered reports in byte" ) +set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "65536" CACHE STRING "Default buffer size for buffered reports in byte" ) # advanced options option(DEBUG "Enable debugging mode (include assertions)" OFF) @@ -60,48 +60,32 @@ option(DEBUG_SV_SUBSCRIBER "Enable Sampled Values subscriber debugging" ${DEBUG} option(DEBUG_SV_PUBLISHER "Enable Sampled Values publisher debugging" ${DEBUG}) option(DEBUG_HAL_ETHERNET "Enable Ethernet HAL printf debugging" ${DEBUG}) -#mark_as_advanced( -# DEBUG_SOCKET -# DEBUG_COTP -# DEBUG_ISO_SERVER -# DEBUG_ISO_CLIENT -# DEBUG_IED_SERVER -# DEBUG_IED_CLIENT -# DEBUG_MMS_SERVER -# DEBUG_MMS_CLIENT -# DEBUG_GOOSE_SUBSCRIBER -# DEBUG_GOOSE_PUBLISHER -# DEBUG_SV_SUBSCRIBER -# DEBUG_SV_PUBLISHER -# DEBUG_HAL_ETHERNET -#) - include_directories( ${CMAKE_CURRENT_BINARY_DIR}/config - src/common/inc - src/goose - src/sampled_values - src/hal/inc - src/iec61850/inc - src/iec61850/inc_private - src/mms/inc - src/mms/inc_private - src/mms/iso_mms/asn1c - src/logging - src/tls + ${CMAKE_CURRENT_LIST_DIR}/src/common/inc + ${CMAKE_CURRENT_LIST_DIR}/src/goose + ${CMAKE_CURRENT_LIST_DIR}/src/sampled_values + ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc + ${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc + ${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc_private + ${CMAKE_CURRENT_LIST_DIR}/src/mms/inc + ${CMAKE_CURRENT_LIST_DIR}/src/mms/inc_private + ${CMAKE_CURRENT_LIST_DIR}/src/mms/iso_mms/asn1c + ${CMAKE_CURRENT_LIST_DIR}/src/logging ) -set(API_HEADERS - src/hal/inc/hal_time.h - src/hal/inc/hal_thread.h - src/hal/inc/hal_filesystem.h - src/hal/inc/hal_ethernet.h - src/hal/inc/platform_endian.h +set(API_HEADERS + hal/inc/hal_time.h + hal/inc/hal_thread.h + hal/inc/hal_filesystem.h + hal/inc/hal_ethernet.h + hal/inc/tls_config.h + hal/inc/platform_endian.h + hal/inc/lib_memory.h src/common/inc/libiec61850_common_api.h src/common/inc/libiec61850_platform_includes.h src/common/inc/linked_list.h src/common/inc/byte_buffer.h - src/common/inc/lib_memory.h src/common/inc/string_utilities.h src/iec61850/inc/iec61850_client.h src/iec61850/inc/iec61850_common.h @@ -127,16 +111,13 @@ set(API_HEADERS src/goose/goose_publisher.h src/sampled_values/sv_subscriber.h src/sampled_values/sv_publisher.h - src/sampled_values/sv_publisher_deprecated.h - src/sampled_values/sv_subscriber_deprecated.h src/logging/logging_api.h - src/tls/tls_api.h ${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h ) if(MSVC) include_directories( - src/vs + ${CMAKE_CURRENT_LIST_DIR}/src/vs ) endif(MSVC) @@ -145,15 +126,8 @@ set(WITH_MBEDTLS 1) endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0) if(WITH_MBEDTLS) -include_directories( - src/tls/mbedtls - third_party/mbedtls/mbedtls-2.6.0/include -) - -file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0/library/*.c) add_definitions(-DCONFIG_MMS_SUPPORT_TLS=1) -add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h") endif(WITH_MBEDTLS) @@ -163,8 +137,14 @@ configure_file( ${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h ) +include_directories( + ${CMAKE_CURRENT_LIST_DIR}/hal/inc +) + +add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/hal") + if(BUILD_EXAMPLES) - add_subdirectory(examples) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/examples) endif(BUILD_EXAMPLES) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src) diff --git a/Makefile b/Makefile index f4ccb697d..1c6ce5714 100644 --- a/Makefile +++ b/Makefile @@ -31,30 +31,35 @@ LIB_SOURCE_DIRS += src/iec61850/server/model LIB_SOURCE_DIRS += src/iec61850/server/mms_mapping LIB_SOURCE_DIRS += src/iec61850/server/impl ifeq ($(HAL_IMPL), WIN32) -LIB_SOURCE_DIRS += src/hal/socket/win32 -LIB_SOURCE_DIRS += src/hal/thread/win32 -LIB_SOURCE_DIRS += src/hal/ethernet/win32 -LIB_SOURCE_DIRS += src/hal/filesystem/win32 -LIB_SOURCE_DIRS += src/hal/time/win32 +LIB_SOURCE_DIRS += hal/socket/win32 +LIB_SOURCE_DIRS += hal/thread/win32 +LIB_SOURCE_DIRS += hal/ethernet/win32 +LIB_SOURCE_DIRS += hal/filesystem/win32 +LIB_SOURCE_DIRS += hal/time/win32 +LIB_SOURCE_DIRS += hal/serial/win32 +LIB_SOURCE_DIRS += hal/memory else ifeq ($(HAL_IMPL), POSIX) -LIB_SOURCE_DIRS += src/hal/socket/linux -LIB_SOURCE_DIRS += src/hal/thread/linux -LIB_SOURCE_DIRS += src/hal/ethernet/linux -LIB_SOURCE_DIRS += src/hal/filesystem/linux -LIB_SOURCE_DIRS += src/hal/time/unix +LIB_SOURCE_DIRS += hal/socket/linux +LIB_SOURCE_DIRS += hal/thread/linux +LIB_SOURCE_DIRS += hal/ethernet/linux +LIB_SOURCE_DIRS += hal/filesystem/linux +LIB_SOURCE_DIRS += hal/time/unix +LIB_SOURCE_DIRS += hal/serial/linux +LIB_SOURCE_DIRS += hal/memory else ifeq ($(HAL_IMPL), BSD) -LIB_SOURCE_DIRS += src/hal/socket/bsd -LIB_SOURCE_DIRS += src/hal/thread/bsd -LIB_SOURCE_DIRS += src/hal/ethernet/bsd -LIB_SOURCE_DIRS += src/hal/filesystem/linux -LIB_SOURCE_DIRS += src/hal/time/unix +LIB_SOURCE_DIRS += hal/socket/bsd +LIB_SOURCE_DIRS += hal/thread/bsd +LIB_SOURCE_DIRS += hal/ethernet/bsd +LIB_SOURCE_DIRS += hal/filesystem/linux +LIB_SOURCE_DIRS += hal/time/unix +LIB_SOURCE_DIRS += hal/memory endif LIB_INCLUDE_DIRS += config +LIB_INCLUDE_DIRS += hal/inc LIB_INCLUDE_DIRS += src/common/inc LIB_INCLUDE_DIRS += src/mms/iso_mms/asn1c LIB_INCLUDE_DIRS += src/mms/inc LIB_INCLUDE_DIRS += src/mms/inc_private -LIB_INCLUDE_DIRS += src/hal/inc LIB_INCLUDE_DIRS += src/goose LIB_INCLUDE_DIRS += src/sampled_values LIB_INCLUDE_DIRS += src/iec61850/inc @@ -67,9 +72,9 @@ endif ifdef WITH_MBEDTLS LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.6.0/library -LIB_SOURCE_DIRS += src/tls/mbedtls +LIB_SOURCE_DIRS += hal/tls/mbedtls LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.6.0/include -LIB_INCLUDE_DIRS += src/tls/mbedtls +LIB_INCLUDE_DIRS += hal/tls/mbedtls CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"' CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1' endif @@ -80,9 +85,10 @@ ifndef INSTALL_PREFIX INSTALL_PREFIX = ./.install endif -LIB_API_HEADER_FILES = src/hal/inc/hal_time.h -LIB_API_HEADER_FILES += src/hal/inc/hal_thread.h -LIB_API_HEADER_FILES += src/hal/inc/hal_filesystem.h +LIB_API_HEADER_FILES = hal/inc/hal_time.h +LIB_API_HEADER_FILES += hal/inc/hal_thread.h +LIB_API_HEADER_FILES += hal/inc/hal_filesystem.h +LIB_API_HEADER_FILES += hal/inc/tls_config.h LIB_API_HEADER_FILES += src/common/inc/libiec61850_common_api.h LIB_API_HEADER_FILES += src/common/inc/linked_list.h LIB_API_HEADER_FILES += src/common/inc/byte_buffer.h @@ -112,7 +118,6 @@ LIB_API_HEADER_FILES += src/goose/goose_publisher.h LIB_API_HEADER_FILES += src/sampled_values/sv_subscriber.h LIB_API_HEADER_FILES += src/sampled_values/sv_publisher.h LIB_API_HEADER_FILES += src/logging/logging_api.h -LIB_API_HEADER_FILES += src/tls/tls_api.h get_sources_from_directory = $(wildcard $1/*.c) get_sources = $(foreach dir, $1, $(call get_sources_from_directory,$(dir))) diff --git a/README.md b/README.md index 69dfb2ab6..d03dfbd23 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,19 @@ This file is part of the documentation of **libIEC61850**. More documentation ca Content: -* Overview -* Building and running the examples -* Installing the library and the API headers -* Building on Windows with GOOSE support -* Building with the cmake build script -* Using the log service with sqlite -* C# API -* Licensing -* Contributing -* Third-party contributions +* [Overview](#overview) +* [Features](#features) +* [Building and running the examples](#building-and-running-the-examples-with-the-provided-makefiles) +* [Building the library with TLS support](#building-the-library-with-tls-support) +* [Installing the library and the API headers](#installing-the-library-and-the-api-headers) +* [Building on Windows with GOOSE support](#building-on-windows-with-goose-support) +* [Building with the cmake build script](#building-with-the-cmake-build-script) +* [Using the log service with sqlite](#using-the-log-service-with-sqlite) +* [C# API](#c-api) +* [Experimental Python bindings](#experimental-python-bindings) +* [Licensing](#commercial-licenses-and-support) +* [Contributing](#contributing) +* [Third-party contributions](#third-party-contributions) ## Overview @@ -25,9 +28,33 @@ libiec61850 is an open-source (GPLv3) implementation of an IEC 61850 client and For commercial projects licenses and support is provided by MZ Automation GmbH. Please contact info@mz-automation.de for more details on licensing options. + +## Features + +The library support the following IEC 61850 protocol features: + +* MMS client/server, GOOSE (IEC 61850-8-1) +* Sampled Values (SV - IEC 61850-9-2) +* Support for buffered and unbuffered reports +* Online report control block configuration +* Data access service (get data, set data) +* online data model discovery and browsing +* all data set services (get values, set values, browse) +* dynamic data set services (create and delete) +* log service +** flexible API to connect custom data bases +** comes with sqlite implementation +* MMS file services (browse, get file, set file, delete/rename file) +** required to download COMTRADE files +* Setting group handling +* GOOSE and SV control block handling +* TLS support +* C and C#/.NET API + + ## Building and running the examples with the provided makefiles -In the project root directoy type +In the project root directory type ``` make examples @@ -38,8 +65,8 @@ If the build succeeds you can find a few binary files in the projects root direc Run the sample applications in the example folders. E.g.: ``` -cd examples/server_example1 -sudo ./server_example1 +cd examples/server_example_basic_io +sudo ./server_example_basic_io ``` on the Linux command line. @@ -54,7 +81,7 @@ In the main libiec61850 folder run ``` make WITH_MBEDTLS=1 -``` +``` ## Installing the library and the API headers diff --git a/config/stack_config.h b/config/stack_config.h index b25c7da11..8446eb22e 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -39,6 +39,10 @@ */ #define CONFIG_MMS_SINGLE_THREADED 1 +#if (WITH_MBEDTLS == 1) +#define CONFIG_MMS_SUPPORT_TLS 1 +#endif + /* * Optimize stack for threadless operation - don't use semaphores * @@ -47,7 +51,7 @@ #define CONFIG_MMS_THREADLESS_STACK 0 /* number of concurrent MMS client connections the server accepts, -1 for no limit */ -#define CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS 5 +#define CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS 100 /* activate TCP keep alive mechanism. 1 -> activate */ #define CONFIG_ACTIVATE_TCP_KEEPALIVE 1 @@ -157,6 +161,12 @@ /* include support for IEC 61850 log services */ #define CONFIG_IEC61850_LOG_SERVICE 1 +/* allow user to control read access by callback */ +#define CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL 1 + +/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ +#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 + /* overwrite default results for MMS identify service */ //#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com" //#define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850" @@ -198,20 +208,13 @@ #define MMS_STATUS_SERVICE 1 #define MMS_IDENTIFY_SERVICE 1 #define MMS_FILE_SERVICE 1 -#define MMS_OBTAIN_FILE_SERVICE 1 +#define MMS_OBTAIN_FILE_SERVICE 1 /* requires MMS_FILE_SERVICE */ #endif /* MMS_DEFAULT_PROFILE */ -#if (MMS_WRITE_SERVICE != 1) -#undef CONFIG_IEC61850_CONTROL_SERVICE -#define CONFIG_IEC61850_CONTROL_SERVICE 0 -#endif /* support flat named variable name space required by IEC 61850-8-1 MMS mapping */ #define CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE 1 -/* VMD scope named variables are not used by IEC 61850 (one application is ICCP) */ -#define CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES 0 - /* 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 @@ -224,9 +227,47 @@ /* 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 +/* Allow to set the virtual file store base path for MMS file services at runtime with the + * MmsServer_setFilestoreBasepath function. */ #define CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME 1 +/* enable to configure MmsServer at runtime */ +#define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1 + +/************************************************************************************ + * Check configuration for consistency - DO NOT MODIFY THIS PART! + ************************************************************************************/ + +#if (MMS_JOURNAL_SERVICE != 1) + +#if (CONFIG_IEC61850_LOG_SERVICE == 1) +#warning "Invalid configuration: CONFIG_IEC61850_LOG_SERVICE requires MMS_JOURNAL_SERVICE!" +#endif + +#undef CONFIG_IEC61850_LOG_SERVICE +#define CONFIG_IEC61850_LOG_SERVICE 0 + +#endif + +#if (MMS_WRITE_SERVICE != 1) + +#if (CONFIG_IEC61850_CONTROL_SERVICE == 1) +#warning "Invalid configuration: CONFIG_IEC61850_CONTROL_SERVICE requires MMS_WRITE_SERVICE!" +#endif + +#undef CONFIG_IEC61850_CONTROL_SERVICE +#define CONFIG_IEC61850_CONTROL_SERVICE 0 +#endif + +#if (MMS_FILE_SERVICE != 1) + +#if (MMS_OBTAIN_FILE_SERVICE == 1) +#warning "Invalid configuration: MMS_OBTAIN_FILE_SERVICE requires MMS_FILE_SERVICE!" +#endif + +#undef MMS_OBTAIN_FILE_SERVICE +#define MMS_OBTAIN_FILE_SERVICE 0 +#endif + #endif /* STACK_CONFIG_H_ */ diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index c36f782da..b3fcadf7c 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -151,6 +151,12 @@ /* include support for IEC 61850 log services */ #cmakedefine01 CONFIG_IEC61850_LOG_SERVICE +/* allow user to control read access by callback */ +#cmakedefine01 CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL + +/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ +#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 + /* default results for MMS identify service */ #define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com" #define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850" @@ -195,11 +201,6 @@ #define MMS_OBTAIN_FILE_SERVICE 1 #endif /* MMS_DEFAULT_PROFILE */ -#if (MMS_WRITE_SERVICE != 1) -#undef CONFIG_IEC61850_CONTROL_SERVICE -#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 @@ -217,4 +218,42 @@ */ #define CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME 1 +/* enable to configure MmsServer at runtime */ +#define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1 + +/************************************************************************************ + * Check configuration for consistency - DO NOT MODIFY THIS PART! + ************************************************************************************/ + +#if (MMS_JOURNAL_SERVICE != 1) + +#if (CONFIG_IEC61850_LOG_SERVICE == 1) +#warning "Invalid configuration: CONFIG_IEC61850_LOG_SERVICE requires MMS_JOURNAL_SERVICE!" +#endif + +#undef CONFIG_IEC61850_LOG_SERVICE +#define CONFIG_IEC61850_LOG_SERVICE 0 + +#endif + +#if (MMS_WRITE_SERVICE != 1) + +#if (CONFIG_IEC61850_CONTROL_SERVICE == 1) +#warning "Invalid configuration: CONFIG_IEC61850_CONTROL_SERVICE requires MMS_WRITE_SERVICE!" +#endif + +#undef CONFIG_IEC61850_CONTROL_SERVICE +#define CONFIG_IEC61850_CONTROL_SERVICE 0 +#endif + +#if (MMS_FILE_SERVICE != 1) + +#if (MMS_OBTAIN_FILE_SERVICE == 1) +#warning "Invalid configuration: MMS_OBTAIN_FILE_SERVICE requires MMS_FILE_SERVICE!" +#endif + +#undef MMS_OBTAIN_FILE_SERVICE +#define MMS_OBTAIN_FILE_SERVICE 0 +#endif + #endif /* STACK_CONFIG_H_ */ diff --git a/demos/beaglebone/beagle_demo.icd b/demos/beaglebone/beagle_demo.icd index b76febab1..57cdbb999 100644 --- a/demos/beaglebone/beagle_demo.icd +++ b/demos/beaglebone/beagle_demo.icd @@ -270,7 +270,7 @@ - + diff --git a/dotnet/IEC61850forCSharp/Control.cs b/dotnet/IEC61850forCSharp/Control.cs index 711542c7f..c629ad90b 100644 --- a/dotnet/IEC61850forCSharp/Control.cs +++ b/dotnet/IEC61850forCSharp/Control.cs @@ -118,6 +118,9 @@ public class ControlObject : IDisposable [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] private static extern int ControlObjectClient_getControlModel(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern int ControlObjectClient_getCtlValType(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] private static extern bool ControlObjectClient_operate(IntPtr self, IntPtr ctlVal, UInt64 operTime); @@ -138,13 +141,13 @@ public class ControlObject : IDisposable private static extern void ControlObjectClient_setOrigin(IntPtr self, string orIdent, int orCat); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - private static extern void ControlObjectClient_setInterlockCheck(IntPtr self, bool value); + private static extern void ControlObjectClient_setInterlockCheck(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - private static extern void ControlObjectClient_setSynchroCheck(IntPtr self, bool value); + private static extern void ControlObjectClient_setSynchroCheck(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - private static extern void ControlObjectClient_setTestMode(IntPtr self, bool value); + private static extern void ControlObjectClient_setTestMode(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void InternalCommandTerminationHandler(IntPtr parameter,IntPtr controlClient); @@ -196,6 +199,17 @@ public ControlModel GetControlModel () return controlModel; } + /// + /// Get the type of ctlVal. + /// + /// MmsType required for the ctlVal value. + public MmsType GetCtlValType () + { + MmsType ctlValType = (MmsType) ControlObjectClient_getCtlValType (controlObject); + + return ctlValType; + } + /// /// Sets the origin parameter used by control commands. /// diff --git a/dotnet/IEC61850forCSharp/GooseControlBlock.cs b/dotnet/IEC61850forCSharp/GooseControlBlock.cs index e1f4583ed..e0bfbe1cd 100644 --- a/dotnet/IEC61850forCSharp/GooseControlBlock.cs +++ b/dotnet/IEC61850forCSharp/GooseControlBlock.cs @@ -1,455 +1,261 @@ -using IEC61850.Common; +/* + * GooseControlBlock.cs + * + * Copyright 2017 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; +using System.Diagnostics; + +using IEC61850.Common; namespace IEC61850 { - namespace Client - { - /// - /// Goose control block (GoCB) representation. - /// - /// - /// This class is used as a client side representation (copy) of a goose control block (GoCB). - /// Values from the server will only be read when the GetGoCBValues method is called. - /// Values at the server are only affected when the SetGoCBValues method is called. - /// - public class GooseControlBlock - { - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr ClientGooseControlBlock_create(string dataAttributeReference); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_destroy(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr IedConnection_getGoCBValues(IntPtr connection, out int error, string goCBReference, IntPtr updateGoCB); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void IedConnection_setGoCBValues(IntPtr connection, out int error, IntPtr goCB, UInt32 parametersMask, bool singleRequest); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool ClientGooseControlBlock_getGoEna(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_setGoEna(IntPtr self, bool rptEna); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr ClientGooseControlBlock_getDatSet(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_setDatSet(IntPtr self, string dataSetReference); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr ClientGooseControlBlock_getGoID(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_setGoID(IntPtr self, string goId); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern UInt32 ClientGooseControlBlock_getConfRev(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool ClientGooseControlBlock_getNdsComm(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern UInt32 ClientGooseControlBlock_getMinTime(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern UInt32 ClientGooseControlBlock_getMaxTime(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool ClientGooseControlBlock_getFixedOffs(IntPtr self); - - /* MMS_OCTET_STRING */ - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr ClientGooseControlBlock_getDstAddress_addr(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_setDstAddress_addr(IntPtr self, IntPtr macAddr); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern Byte ClientGooseControlBlock_getDstAddress_priority(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_setDstAddress_priority(IntPtr self, Byte priorityValue); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern UInt16 ClientGooseControlBlock_getDstAddress_vid(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_setDstAddress_vid(IntPtr self, UInt16 vidValue); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern UInt16 ClientGooseControlBlock_getDstAddress_appid(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientGooseControlBlock_setDstAddress_appid(IntPtr self, UInt16 appidValue); - - private IntPtr self; - private IntPtr connection; - private IedConnection iedConnection = null; - private string objectReference; - - - private bool flagGoEna = false; - private bool flagGoId = false; - private bool flagDataSetReference = false; - private bool flagConfRev = false; - private bool flagNdsComm = false; - private bool flagDstAddressAddr = false; - private bool flagDstAddressPriority = false; - private bool flagdstAddressVid = false; - private bool flagdstAddressAppId = false; - private bool flagMinTime = false; - private bool flagMaxTime = false; - private bool flagFixedOffs = false; - - private void resetSendFlags() - { - flagGoEna = false; - flagGoId = false; - flagDataSetReference = false; - flagConfRev = false; - flagNdsComm = false; - flagDstAddressAddr = false; - flagDstAddressPriority = false; - flagdstAddressVid = false; - flagdstAddressAppId = false; - flagMinTime = false; - flagMaxTime = false; - flagFixedOffs = false; - } - - internal GooseControlBlock(string objectReference, IedConnection iedConnection, IntPtr connection) - { - self = ClientGooseControlBlock_create(objectReference); - this.iedConnection = iedConnection; - this.connection = connection; - this.objectReference = objectReference; - } - - ~GooseControlBlock() - { - if (self != IntPtr.Zero) - { - ClientGooseControlBlock_destroy(self); - self = IntPtr.Zero; - } - } - - public string GetObjectReference() - { - return this.objectReference; - } - - /// - /// Read access to attributes of a GOOSE control block (GoCB) at the connected server. A GoCB contains the configuration values for a single GOOSE publisher. - /// - /// This exception is thrown if there is a connection or service error - public void GetGoCBValues() - { - int error; - - IedConnection_getGoCBValues(connection, out error, objectReference, self); - - if (error != 0) - throw new IedConnectionException("getGoCBValues service failed", error); - } - - /// - /// Write changed RCB values to the server. - /// - /// - /// This function will only write the GoCB values that were set by one of the setter methods. - /// The GoCB values are sent by a single MMS write request. - /// - /// This exception is thrown if there is a connection or service error - public void SetGoCBValues() - { - SetGoCBValues(true); - } - - /// - /// Write changed GoCB values to the server. - /// - /// - /// This function will only write the GoCB values that were set by one of the setter methods. - /// - /// This exception is thrown if there is a connection or service error - /// - /// If true the values are sent by single MMS write request. Otherwise the values are all sent by their own MMS write requests. - /// - public void SetGoCBValues(bool singleRequest) - { - UInt32 parametersMask = 0; - - if (flagGoEna) - parametersMask += 1; - - if (flagGoId) - parametersMask += 2; - - if (flagDataSetReference) - parametersMask += 4; - - if (flagConfRev) - parametersMask += 8; - - if (flagNdsComm) - parametersMask += 16; - - if (flagDstAddressAddr || flagdstAddressAppId || flagDstAddressPriority || flagdstAddressVid) - parametersMask += 32; - - if (flagMinTime) - parametersMask += 64; - - if (flagMaxTime) - parametersMask += 128; - - if (flagFixedOffs) - parametersMask += 256; - - int error; - - IedConnection_setGoCBValues(connection, out error, self, parametersMask, singleRequest); - - resetSendFlags(); - - if (error != 0) - throw new IedConnectionException("setGoCBValues service failed", error); - - } - - /// - /// Check if goosing is currently enabled - /// - /// - /// true, if goosing is enabled, false otherwise - /// - public bool GetGoEna() - { - return ClientGooseControlBlock_getGoEna(self); - } - - /// - /// Sets goose enable flag. Use this to enable goosing - /// - /// - /// true to enable goosing, false to disable - /// - public void SetGoEna(bool goEna) - { - ClientGooseControlBlock_setGoEna(self, goEna); - flagGoEna = true; - } - - /// - /// Gets the goose identifier. - /// - /// - /// The goose identifier. - /// - public string GetGoId() - { - IntPtr rptIdPtr = ClientGooseControlBlock_getGoID(self); - - return Marshal.PtrToStringAnsi(rptIdPtr); - } - - /// - /// Sets the GoId (goose ID) of the GoCB - /// - /// - /// The new GoId - /// - public void SetGoId(string goId) - { - ClientGooseControlBlock_setGoID(self, goId); - flagGoId = true; - } - - /// - /// Gets the data set reference of the associated data set - /// - /// - /// The data set reference. - /// - public string GetDataSetReference() - { - IntPtr dataSetRefPtr = ClientGooseControlBlock_getDatSet(self); - - return Marshal.PtrToStringAnsi(dataSetRefPtr); - } - - /// - /// Sets the data set reference. Use this method to select the associated data set for the GoCB - /// - /// The data set reference. - public void SetDataSetReference(string dataSetReference) - { - ClientGooseControlBlock_setDatSet(self, dataSetReference); - - flagDataSetReference = true; - } - - /// - /// Gets the configuration revision of the GoCB - /// - /// - /// The conf rev. - /// - public UInt32 GetConfRev() - { - return ClientGooseControlBlock_getConfRev(self); - } - - /// - /// Check if GoCB needs commissioning - /// - /// - /// true, the GoCB needs commissioning - /// - public bool GetNdsComm() - { - return ClientGooseControlBlock_getNdsComm(self); - } - - /// - /// Gets the destination adress of GoCB - /// - /// Returns the destination adress of the last received GetGoValues service response. - /// - /// The destination adress - public byte[] GetDstAddressAddr() - { - IntPtr getDstAddressAddrRef = ClientGooseControlBlock_getDstAddress_addr(self); - - if (getDstAddressAddrRef == IntPtr.Zero) - return null; - else - { - MmsValue dstAddressAddr = new MmsValue(getDstAddressAddrRef); - - return dstAddressAddr.getOctetString(); - } - } - - /// - /// Sets the destination adress of GoCB - /// - /// The destination adress - public void SetDstAddressAddr(byte[] dstAddressAddr) - { - flagDstAddressAddr = true; - - MmsValue dstAddress_Addr = MmsValue.NewOctetString(dstAddressAddr.Length); - - dstAddress_Addr.setOctetString(dstAddressAddr); - - ClientGooseControlBlock_setDstAddress_addr(self, dstAddress_Addr.valueReference); - } - - /// - /// Gets the VLAN priority of GoCB - /// - /// The VLAN priority - public Byte GetDstAddressPriority() - { - return ClientGooseControlBlock_getDstAddress_priority(self); - } - - /// - /// Sets the VLAN priority of GoCB - /// - /// The new VLAN priority - public void GetDstAddressPriority(Byte dstAddressPriority) - { - flagDstAddressPriority = true; - - ClientGooseControlBlock_setDstAddress_priority(self, dstAddressPriority); - } - - /// - /// Gets the VLAN identifier of GoCB - /// - /// The VLAN identifier - public UInt16 GetDstAddressVid() - { - return ClientGooseControlBlock_getDstAddress_vid(self); - } - - /// - /// Sets the VLAN identifier of GoCB - /// - /// The new VLAN identifier - public void SetDstAddressVid(UInt16 dstAddressVid) - { - flagdstAddressVid = true; - - ClientGooseControlBlock_setDstAddress_vid(self, dstAddressVid); - } - - /// - /// Gets the application identifier of GoCB - /// - /// The application identifier - public UInt16 GetDstAddressAppId() - { - return ClientGooseControlBlock_getDstAddress_appid(self); - } - - /// - /// Sets the application identifier of GoCB - /// - /// The new application identifier - public void setDstAddressAppId(UInt16 dstAddressAppId) - { - flagdstAddressAppId = true; - - ClientGooseControlBlock_setDstAddress_appid(self, dstAddressAppId); - } - - /// - /// Gets the minimum time - /// - /// - /// The time in ms. - /// + namespace Client + { + + public class GooseControlBlock { + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ClientGooseControlBlock_create (string dataAttributeReference); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void ClientGooseControlBlock_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedConnection_getGoCBValues (IntPtr connection, out int error, string rcbReference, IntPtr updateRcb); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_setGoCBValues (IntPtr connection, out int error, IntPtr rcb, UInt32 parametersMask, [MarshalAs(UnmanagedType.I1)] bool singleRequest); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientGooseControlBlock_getGoEna (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void ClientGooseControlBlock_setGoEna(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool rptEna); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ClientGooseControlBlock_getGoID (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void ClientGooseControlBlock_setGoID (IntPtr self, string goId); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ClientGooseControlBlock_getDatSet (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void ClientGooseControlBlock_setDatSet (IntPtr self, string datSet); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ClientGooseControlBlock_getConfRev (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientGooseControlBlock_getNdsComm (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ClientGooseControlBlock_getMinTime (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ClientGooseControlBlock_getMaxTime (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientGooseControlBlock_getFixedOffs (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern PhyComAddress ClientGooseControlBlock_getDstAddress (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void ClientGooseControlBlock_setDstAddress (IntPtr self, PhyComAddress value); + + + private IntPtr self; + private IntPtr connection; + private string objectReference; + + private bool isDisposed = false; + + private bool flagGoEna = false; + private bool flagGoID = false; + private bool flagDatSet = false; + private bool flagDstAddress = false; + + internal GooseControlBlock(string objectReference, IntPtr connection) + { + self = ClientGooseControlBlock_create (objectReference); + this.connection = connection; + this.objectReference = objectReference; + } + + public string GetObjectReference () + { + return this.objectReference; + } + + /// + /// Read all GoCB values from the server + /// + /// This exception is thrown if there is a connection or service error + public void GetCBValues () + { + int error; + + IedConnection_getGoCBValues (connection, out error, objectReference, self); + + if (error != 0) + throw new IedConnectionException ("getGoCBValues service failed", error); + } + + private void + resetSendFlags() + { + flagGoEna = false; + flagGoID = false; + flagDatSet = false; + flagDstAddress = false; + } + + public void SetCBValues (bool singleRequest) + { + UInt32 parametersMask = 0; + + if (flagGoEna) + parametersMask += 1; + + if (flagGoID) + parametersMask += 2; + + if (flagDatSet) + parametersMask += 4; + + if (flagDstAddress) + parametersMask += 32; + + int error; + + IedConnection_setGoCBValues (connection, out error, self, parametersMask, singleRequest); + + resetSendFlags (); + + if (error != 0) + throw new IedConnectionException ("setGoCBValues service failed", error); + } + + public void SetCBValues () + { + SetCBValues (true); + } + + public bool GetGoEna() + { + return ClientGooseControlBlock_getGoEna (self); + } + + public void SetGoEna(bool value) + { + ClientGooseControlBlock_setGoEna (self, value); + + flagGoEna = true; + } + + public string GetGoID() + { + IntPtr goIdRef = ClientGooseControlBlock_getGoID (self); + + return Marshal.PtrToStringAnsi (goIdRef); + } + + public void SetGoID (string goID) + { + ClientGooseControlBlock_setGoID (self, goID); + + flagGoID = true; + } + + public string GetDatSet() + { + IntPtr datSetRef = ClientGooseControlBlock_getDatSet (self); + + return Marshal.PtrToStringAnsi (datSetRef); + } + + public void SetDataSet(string datSet) + { + ClientGooseControlBlock_setDatSet (self, datSet); + + flagDatSet = true; + } + + public UInt32 GetConfRev() + { + return ClientGooseControlBlock_getConfRev (self); + } + + public bool GetNdsComm() + { + return ClientGooseControlBlock_getNdsComm (self); + } + public UInt32 GetMinTime() - { - return ClientGooseControlBlock_getMinTime(self); - } - - /// - /// Gets the maximum time. - /// - /// - /// The buffer time in ms. - /// + { + return ClientGooseControlBlock_getMinTime (self); + } + public UInt32 GetMaxTime() - { - return ClientGooseControlBlock_getMaxTime(self); - } - - /// - /// Check if GoCB is fixed encoded - /// - /// - /// true, the GoCB is fixed encoded - /// - public bool GetFixedOffs() - { - return ClientGooseControlBlock_getFixedOffs(self); - } + { + return ClientGooseControlBlock_getMaxTime (self); + } - } - } -} + public bool GetFixedOffs() + { + return ClientGooseControlBlock_getFixedOffs (self); + } + + public PhyComAddress GetDstAddress() + { + return ClientGooseControlBlock_getDstAddress (self); + } + + public void SetDstAddress(PhyComAddress value) + { + ClientGooseControlBlock_setDstAddress (self, value); + + flagDstAddress = true; + } + + public void Dispose() + { + if (isDisposed == false) { + isDisposed = true; + ClientGooseControlBlock_destroy (self); + self = IntPtr.Zero; + } + } + + ~GooseControlBlock() + { + Dispose (); + } + + } + } +} \ No newline at end of file diff --git a/dotnet/IEC61850forCSharp/GooseSubscriber.cs b/dotnet/IEC61850forCSharp/GooseSubscriber.cs new file mode 100644 index 000000000..9b2c6e691 --- /dev/null +++ b/dotnet/IEC61850forCSharp/GooseSubscriber.cs @@ -0,0 +1,313 @@ +/* + * GooseSubscriber.cs + * + * Copyright 2017 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +using System; +using System.Runtime.InteropServices; +using IEC61850.Common; + +namespace IEC61850 +{ + namespace GOOSE + { + + namespace Subscriber + { + + /// + /// GOOSE listener. + /// + public delegate void GooseListener (GooseSubscriber report, object parameter); + + public class GooseReceiver : IDisposable + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GooseReceiver_create (); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseReceiver_addSubscriber(IntPtr self, IntPtr subscriber); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseReceiver_removeSubscriber(IntPtr self, IntPtr subscriber); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseReceiver_start(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseReceiver_stop(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool GooseReceiver_isRunning (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseReceiver_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseReceiver_setInterfaceId(IntPtr self, string interfaceId); + + private IntPtr self; + + private bool isDisposed = false; + + public GooseReceiver() + { + self = GooseReceiver_create (); + } + + public void SetInterfaceId(string interfaceId) + { + GooseReceiver_setInterfaceId (self, interfaceId); + } + + public void AddSubscriber(GooseSubscriber subscriber) + { + GooseReceiver_addSubscriber (self, subscriber.self); + } + + public void RemoveSubscriber(GooseSubscriber subscriber) + { + GooseReceiver_removeSubscriber (self, subscriber.self); + } + + public void Start() + { + GooseReceiver_start (self); + } + + public void Stop() + { + GooseReceiver_stop (self); + } + + public bool IsRunning() + { + return GooseReceiver_isRunning (self); + } + + public void Dispose() + { + if (isDisposed == false) { + isDisposed = true; + GooseReceiver_destroy (self); + self = IntPtr.Zero; + } + } + + ~GooseReceiver() + { + Dispose (); + } + } + + + /// + /// Representing a GOOSE subscriber + /// + /// + /// NOTE: After SetListener is called, do not call any function outside of + /// the callback handler! + /// + public class GooseSubscriber : IDisposable + { + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void InternalGooseListener (IntPtr subscriber, IntPtr parameter); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GooseSubscriber_create (string goCbRef, IntPtr dataSetValue); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseSubscriber_setAppId(IntPtr self, UInt16 appId); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool GooseSubscriber_isValid (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt32 GooseSubscriber_getStNum (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt32 GooseSubscriber_getSqNum (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool GooseSubscriber_isTest (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt32 GooseSubscriber_getConfRev (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool GooseSubscriber_needsCommission (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt32 GooseSubscriber_getTimeAllowedToLive (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt64 GooseSubscriber_getTimestamp (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GooseSubscriber_getDataSetValues(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseSubscriber_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void GooseSubscriber_setListener (IntPtr self, InternalGooseListener listener, IntPtr parameter); + + internal IntPtr self; + + private bool isDisposed = false; + + private GooseListener listener = null; + private object listenerParameter = null; + + private event InternalGooseListener internalListener = null; + + private void internalGooseListener (IntPtr subscriber, IntPtr parameter) + { + try { + + if (listener != null) { + listener(this, listenerParameter); + } + + } catch (Exception e) + { + // older versions of mono 2.10 (for linux?) cause this exception + Console.WriteLine(e.Message); + } + } + + public GooseSubscriber(string goCbRef) + { + self = GooseSubscriber_create (goCbRef, IntPtr.Zero); + } + + public void SetAppId(UInt16 appId) + { + GooseSubscriber_setAppId (self, appId); + } + + public bool IsValid () + { + return GooseSubscriber_isValid (self); + } + + + public void SetListener(GooseListener listener, object parameter) + { + this.listener = listener; + this.listenerParameter = parameter; + + if (internalListener == null) { + internalListener = new InternalGooseListener (internalGooseListener); + + GooseSubscriber_setListener (self, internalListener, IntPtr.Zero); + } + } + + public UInt32 GetStNum() + { + return GooseSubscriber_getStNum (self); + } + + public UInt32 GetSqNum() + { + return GooseSubscriber_getSqNum (self); + } + + public bool IsTest() + { + return GooseSubscriber_isTest (self); + } + + public UInt32 GetConfRev() + { + return GooseSubscriber_getConfRev (self); + } + + public bool NeedsCommission() + { + return GooseSubscriber_needsCommission (self); + } + + public UInt32 GetTimeAllowedToLive() + { + return GooseSubscriber_getTimeAllowedToLive (self); + } + + public UInt64 GetTimestamp () + { + return GooseSubscriber_getTimestamp (self); + } + + public DateTimeOffset GetTimestampsDateTimeOffset () + { + UInt64 entryTime = GetTimestamp (); + + DateTimeOffset retVal = new DateTimeOffset (1970, 1, 1, 0, 0, 0, TimeSpan.Zero); + + return retVal.AddMilliseconds (entryTime); + } + + /// + /// Get the values of the GOOSE data set from the last received GOOSE message + /// + /// + /// The MmsValue instance is only valid in the context of the GooseLister callback. + /// Do not store for outside use! + /// + /// The data set values. + public MmsValue GetDataSetValues() + { + IntPtr mmsValueRef = GooseSubscriber_getDataSetValues (self); + + return (new MmsValue (mmsValueRef)); + } + + /// + /// Releases all resource used by the object. + /// + /// > + /// This function has only to be called when the + /// has not been added to the GooseReceiver or has been removed from the GooseReceiver. + /// When the GooseReceiver holds a reference it will take care for releasing the resources. + /// In this case Dispose MUST not be called! Otherwise the natice resources will be + /// released twice. + /// + public void Dispose() + { + if (isDisposed == false) { + isDisposed = true; + GooseSubscriber_destroy (self); + self = IntPtr.Zero; + } + } + + } + + } + + } +} \ No newline at end of file diff --git a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj index ca47a26a0..b86b020dd 100644 --- a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj +++ b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj @@ -65,6 +65,11 @@ + + + + + \ No newline at end of file diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 44772ea03..edc177645 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -317,7 +317,7 @@ public partial class IedConnection static extern IntPtr IedConnection_getLogicalNodeDirectory (IntPtr self, out int error, string logicalNodeReference, int acsiClass); [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] - static extern IntPtr IedConnection_getServerDirectory (IntPtr self, out int error, bool getFileNames); + static extern IntPtr IedConnection_getServerDirectory (IntPtr self, out int error, [MarshalAs(UnmanagedType.I1)] bool getFileNames); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_getDeviceModelFromServer(IntPtr self, out int error); @@ -346,6 +346,7 @@ public partial class IedConnection static extern IntPtr IedConnection_createDataSet (IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.Bool)] static extern bool IedConnection_deleteDataSet (IntPtr self, out int error, string dataSetReference); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] @@ -365,12 +366,26 @@ public partial class IedConnection [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedConnection_queryLogAfter(IntPtr self, out int error, string logReference, - IntPtr entryID, ulong timeStamp, out bool moreFollows); + IntPtr entryID, ulong timeStamp, [MarshalAs(UnmanagedType.I1)] out bool moreFollows); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedConnection_queryLogByTime (IntPtr self, out int error, string logReference, - ulong startTime, ulong endTime, out bool moreFollows); + ulong startTime, ulong endTime, [MarshalAs(UnmanagedType.I1)] out bool moreFollows); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedConnection_getRCBValues (IntPtr connection, out int error, string rcbReference, IntPtr updateRcb); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_setRCBValues (IntPtr connection, out int error, IntPtr rcb, UInt32 parametersMask, [MarshalAs(UnmanagedType.I1)] bool singleRequest); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_installReportHandler (IntPtr connection, string rcbReference, string rptId, InternalReportHandler handler, + IntPtr handlerParameter); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_uninstallReportHandler(IntPtr connection, string rcbReference); + /******************** * FileDirectoryEntry @@ -436,7 +451,6 @@ public IedConnection (TLSConfiguration tlsConfig) public void Dispose() { if (connection != IntPtr.Zero) { - cleanupRCBs (); IedConnection_destroy (connection); @@ -446,11 +460,7 @@ public void Dispose() ~IedConnection () { - if (connection != IntPtr.Zero) { - cleanupRCBs (); - - IedConnection_destroy (connection); - } + Dispose (); } private IsoConnectionParameters isoConnectionParameters = null; @@ -531,10 +541,28 @@ public ControlObject CreateControlObject (string objectReference) return controlObject; } + /// + /// Creates a new SampledValuesControlBlock instance. + /// + /// > + /// This function will also read the SVCB values from the server. + /// + /// The new SVCB instance + /// The object reference of the SVCB + public SampledValuesControlBlock GetSvControlBlock (string svcbObjectReference) + { + return new SampledValuesControlBlock (connection, svcbObjectReference); + } - - - + /// + /// Creates a new SampledValuesControlBlock instance. + /// + /// The new GoCB instance + /// The object reference of the GoCB + public GooseControlBlock GetGooseControlBlock (string gocbObjectReference) + { + return new GooseControlBlock (gocbObjectReference, connection); + } /// /// Updates the device model by quering the server. @@ -1452,6 +1480,39 @@ public List GetDataSetDirectory (string dataSetReference, out bool isDel return newList; } + + internal void UninstallReportHandler (string objectReference) + { + if (connection != IntPtr.Zero) { + IedConnection_uninstallReportHandler (connection, objectReference); + } + } + + internal void InstallReportHandler (string objectReference, string reportId, InternalReportHandler internalHandler) + { + if (connection != IntPtr.Zero) { + IedConnection_installReportHandler (connection, objectReference, reportId, internalHandler, IntPtr.Zero); + } + } + + internal void GetRCBValues(out int error, string objectReference, IntPtr updateRcb) + { + if (connection != IntPtr.Zero) { + IedConnection_getRCBValues (connection, out error, objectReference, updateRcb); + } else { + error = 1; /* not connected */ + } + } + + internal void SetRCBValues(out int error, IntPtr rcb, UInt32 parametersMask, bool singleRequest) + { + if (connection != IntPtr.Zero) { + IedConnection_setRCBValues (connection, out error, rcb, parametersMask, singleRequest); + } else { + error = 1; /* not connected */ + } + } + } public class IedConnectionException : Exception diff --git a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs index 1cde40dbe..85f0f3d24 100644 --- a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs @@ -1,4 +1,27 @@ -using System; +/* + * IEC61850CommonAPI.cs + * + * Copyright 2014-2017 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +using System; using System.Runtime.InteropServices; namespace IEC61850 @@ -6,6 +29,13 @@ namespace IEC61850 namespace Common { + public enum Iec61850Edition : byte + { + EDITION_1 = 0, + EDITION_2 = 1, + EDITION_2_1 = 2 + } + public class LibIEC61850 { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] @@ -49,6 +79,17 @@ public static ulong DateTimeToMsTimestamp(DateTime dateTime) } } + [StructLayout(LayoutKind.Sequential)] + public class PhyComAddress + { + public byte vlanPriority; + public UInt16 vlanId; + public UInt16 appId; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)] + public byte[] dstAddress = new byte[6]; + } + /// /// MMS data access error for MmsValue type MMS_DATA_ACCESS_ERROR /// @@ -85,6 +126,29 @@ public enum TriggerOptions { GI = 16 } + /// + /// SmpMod values + /// + public enum SmpMod { + SAMPLES_PER_PERIOD = 0, + SAMPLES_PER_SECOND = 1, + SECONDS_PER_SAMPLE = 2 + } + + /// + /// Values for Sampled Values (SV) OptFlds + /// + [Flags] + public enum SVOptions { + NONE = 0, + REFRESH_TIME = 1, + SAMPLE_SYNC = 2, + SAMPLE_RATE = 4, + DATA_SET = 8, + SECURITY = 16, + ALL = 31 + } + [Flags] public enum ReportOptions { NONE = 0, @@ -96,7 +160,9 @@ public enum ReportOptions { BUFFER_OVERFLOW = 32, ENTRY_ID = 64, CONF_REV = 128, - ALL = 255 + SEGMENTATION = 256, + ALL = SEQ_NUM | TIME_STAMP | REASON_FOR_INCLUSION | DATA_SET | DATA_REFERENCE | + BUFFER_OVERFLOW | ENTRY_ID | CONF_REV | SEGMENTATION } public enum Validity @@ -112,9 +178,27 @@ public enum Validity /// public class Quality { - private UInt16 value; + private const UInt16 QUALITY_DETAIL_OVERFLOW = 4; + private const UInt16 QUALITY_DETAIL_OUT_OF_RANGE = 8; + private const UInt16 QUALITY_DETAIL_BAD_REFERENCE = 16; + private const UInt16 QUALITY_DETAIL_OSCILLATORY = 32; + private const UInt16 QUALITY_DETAIL_FAILURE = 64; + private const UInt16 QUALITY_DETAIL_OLD_DATA = 128; + private const UInt16 QUALITY_DETAIL_INCONSISTENT = 256; + private const UInt16 QUALITY_DETAIL_INACCURATE = 512; + private const UInt16 QUALITY_SOURCE_SUBSTITUTED = 1024; + private const UInt16 QUALITY_TEST = 2048; + private const UInt16 QUALITY_OPERATOR_BLOCKED = 4096; + private const UInt16 QUALITY_DERIVED = 8192; + + public override string ToString () + { + return GetValidity ().ToString (); + + } + public Quality (int bitStringValue) { value = (UInt16)bitStringValue; @@ -138,6 +222,144 @@ public void SetValidity (Validity validity) value += (ushort)validity; } + + public Validity Validity + { + get {return GetValidity ();} + set { SetValidity (value); } + } + + public bool Overflow + { + get { return ((this.value & QUALITY_DETAIL_OVERFLOW) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_OVERFLOW; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OVERFLOW)); + } + } + + public bool OutOfRange + { + get { return ((this.value & QUALITY_DETAIL_OUT_OF_RANGE) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_OUT_OF_RANGE; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OUT_OF_RANGE)); + } + } + + public bool BadReference + { + get { return ((this.value & QUALITY_DETAIL_BAD_REFERENCE) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_BAD_REFERENCE; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_BAD_REFERENCE)); + } + } + + public bool Oscillatory + { + get { return ((this.value & QUALITY_DETAIL_OSCILLATORY) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_OSCILLATORY; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OSCILLATORY)); + } + } + + public bool Failure + { + get { return ((this.value & QUALITY_DETAIL_FAILURE) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_FAILURE; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_FAILURE)); + } + } + + public bool OldData + { + get { return ((this.value & QUALITY_DETAIL_OLD_DATA) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_OLD_DATA; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OLD_DATA)); + } + } + + public bool Inconsistent + { + get { return ((this.value & QUALITY_DETAIL_INCONSISTENT) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_INCONSISTENT; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_INCONSISTENT)); + } + } + + public bool Inaccurate + { + get { return ((this.value & QUALITY_DETAIL_INACCURATE) != 0);} + set { + if (value) + this.value |= QUALITY_DETAIL_INACCURATE; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_INACCURATE)); + } + } + + public bool Substituted + { + get { return ((this.value & QUALITY_SOURCE_SUBSTITUTED) != 0);} + set { + if (value) + this.value |= QUALITY_SOURCE_SUBSTITUTED; + else + this.value = (ushort) ((int) this.value & (~QUALITY_SOURCE_SUBSTITUTED)); + } + } + + public bool Test + { + get { return ((this.value & QUALITY_TEST) != 0);} + set { + if (value) + this.value |= QUALITY_TEST; + else + this.value = (ushort) ((int) this.value & (~QUALITY_TEST)); + } + } + + public bool OperatorBlocked + { + get { return ((this.value & QUALITY_OPERATOR_BLOCKED) != 0);} + set { + if (value) + this.value |= QUALITY_OPERATOR_BLOCKED; + else + this.value = (ushort) ((int) this.value & (~QUALITY_OPERATOR_BLOCKED)); + } + } + + public bool Derived + { + get { return ((this.value & QUALITY_DERIVED) != 0); } + set { + if (value) + this.value |= QUALITY_DERIVED; + else + this.value = (ushort) ((int) this.value & (~QUALITY_DERIVED)); + } + } } /// @@ -148,6 +370,9 @@ public class Timestamp [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr Timestamp_create (); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr Timestamp_createFromByteArray(byte[] byteArry); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void Timestamp_destroy (IntPtr self); @@ -159,21 +384,21 @@ public class Timestamp static extern bool Timestamp_isLeapSecondKnown (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void Timestamp_setLeapSecondKnown (IntPtr self, bool value); + static extern void Timestamp_setLeapSecondKnown (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool Timestamp_hasClockFailure (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void Timestamp_setClockFailure (IntPtr self, bool value); + static extern void Timestamp_setClockFailure (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool Timestamp_isClockNotSynchronized (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void Timestamp_setClockNotSynchronized (IntPtr self, bool value); + static extern void Timestamp_setClockNotSynchronized (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int Timestamp_getSubsecondPrecision (IntPtr self); @@ -198,12 +423,12 @@ public class Timestamp static extern void Timestamp_setByMmsUtcTime (IntPtr self, IntPtr mmsValue); internal IntPtr timestampRef = IntPtr.Zero; - private bool responsableForDeletion; + private bool responsibleForDeletion; internal Timestamp(IntPtr timestampRef, bool selfAllocated) { this.timestampRef = timestampRef; - this.responsableForDeletion = selfAllocated; + this.responsibleForDeletion = selfAllocated; } public Timestamp (DateTime timestamp) : this () @@ -220,12 +445,18 @@ public Timestamp() { timestampRef = Timestamp_create (); LeapSecondKnown = true; - responsableForDeletion = true; + responsibleForDeletion = true; + } + + public Timestamp(byte[] value) + { + timestampRef = Timestamp_createFromByteArray (value); + responsibleForDeletion = true; } ~Timestamp () { - if (responsableForDeletion) + if (responsibleForDeletion) Timestamp_destroy (timestampRef); } @@ -335,6 +566,15 @@ public void SetByMmsUtcTime(MmsValue mmsValue) Timestamp_setByMmsUtcTime (timestampRef, mmsValue.valueReference); } + public DateTime AsDateTime() + { + DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + DateTime retVal = epoch.AddMilliseconds ((double) GetTimeInMilliseconds ()); + + return retVal; + } + } public enum ACSIClass diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index bea7c6bdb..b8a7f4289 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -27,6 +27,7 @@ using System.Collections; using IEC61850.Common; +using IEC61850.TLS; /// /// IEC 61850 API for the libiec61850 .NET wrapper library @@ -43,7 +44,7 @@ public class ConfigFileParser { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr FileSystem_openFile(string filePath, bool readWrite); + static extern IntPtr FileSystem_openFile(string filePath, [MarshalAs(UnmanagedType.I1)] bool readWrite); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] @@ -260,7 +261,7 @@ public class CDC static extern IntPtr CDC_INS_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr CDC_MV_create(string name, IntPtr parent, uint options, bool isIntegerNotFloat); + static extern IntPtr CDC_MV_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_INC_create(string name, IntPtr parent, uint options, uint controlOptions); @@ -487,15 +488,15 @@ public DataSetEntry(DataSet dataSet, string variable, int index, string componen public class ReportControlBlock { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr ReportControlBlock_create(string name, IntPtr parent, string rptId, bool isBuffered, + static extern IntPtr ReportControlBlock_create(string name, IntPtr parent, string rptId, [MarshalAs(UnmanagedType.I1)] bool isBuffered, string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd); public IntPtr self = IntPtr.Zero; public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered, - string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd) + string dataSetName, uint confRev, byte trgOps, byte options, uint bufTm, uint intgPd) { - self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRef, trgOps, options, bufTm, intgPd); + self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd); } } @@ -578,7 +579,7 @@ public delegate CheckHandlerResult CheckHandler (DataObject controlObject, objec public class IedServer { [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] - static extern IntPtr IedServer_create(IntPtr modelRef); + static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setLocalIpAddress(IntPtr self, string localIpAddress); @@ -593,6 +594,7 @@ public class IedServer static extern void IedServer_destroy(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.Bool)] static extern bool IedServer_isRunning(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] @@ -605,7 +607,7 @@ public class IedServer static extern void IedServer_updateAttributeValue(IntPtr self, IntPtr DataAttribute, IntPtr MmsValue); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void IedServer_updateBooleanAttributeValue(IntPtr self, IntPtr dataAttribute, bool value); + static extern void IedServer_updateBooleanAttributeValue(IntPtr self, IntPtr dataAttribute, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateInt32AttributeValue(IntPtr self, IntPtr dataAttribute, int value); @@ -632,13 +634,13 @@ public class IedServer static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate int InternalControlPerformCheckHandler (IntPtr parameter, IntPtr ctlVal, bool test, bool interlockCheck, IntPtr connection); + private delegate int InternalControlPerformCheckHandler (IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck, IntPtr connection); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate int InternalControlWaitForExecutionHandler (IntPtr parameter, IntPtr ctlVal, bool test, bool synchoCheck); + private delegate int InternalControlWaitForExecutionHandler (IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool synchoCheck); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate int InternalControlHandler (IntPtr parameter, IntPtr ctlVal, bool test); + private delegate int InternalControlHandler (IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern bool IedServer_setWaitForExecutionHandler(IntPtr self, IntPtr node, InternalControlWaitForExecutionHandler handler, IntPtr parameter); @@ -671,7 +673,7 @@ public void SetConnectionIndicationHandler(ConnectionIndicationHandler handler, } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void InternalConnectionHandler (IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter); + private delegate void InternalConnectionHandler (IntPtr iedServer, IntPtr clientConnection, [MarshalAs(UnmanagedType.I1)] bool connected, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setConnectionIndicationHandler(IntPtr self, InternalConnectionHandler handler, IntPtr parameter); @@ -804,9 +806,30 @@ private void connectionIndicationHandler (IntPtr iedServer, IntPtr clientConnect private Dictionary clientConnections = new Dictionary (); - public IedServer(IedModel iedModel) + + + public IedServer(IedModel iedModel, IedServerConfig config = null) + { + IntPtr nativeConfig = IntPtr.Zero; + + if (config != null) + nativeConfig = config.self; + + self = IedServer_createWithConfig (iedModel.self, IntPtr.Zero, nativeConfig); + } + + public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) { - self = IedServer_create(iedModel.self); + IntPtr nativeConfig = IntPtr.Zero; + IntPtr nativeTLSConfig = IntPtr.Zero; + + if (config != null) + nativeConfig = config.self; + + if (tlsConfig != null) + nativeTLSConfig = tlsConfig.GetNativeInstance (); + + self = IedServer_createWithConfig (iedModel.self, nativeTLSConfig, nativeConfig); } // causes undefined behavior @@ -855,7 +878,7 @@ public void Start(int tcpPort) /// Start MMS server public void Start () { - Start(102); + Start(-1); } /// diff --git a/dotnet/IEC61850forCSharp/IedServerConfig.cs b/dotnet/IEC61850forCSharp/IedServerConfig.cs new file mode 100644 index 000000000..13588af45 --- /dev/null +++ b/dotnet/IEC61850forCSharp/IedServerConfig.cs @@ -0,0 +1,233 @@ +/* + * IedServerConfig.cs + * + * Copyright 2018 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +using System; +using System.Runtime.InteropServices; +using IEC61850.Common; + +namespace IEC61850.Server +{ + /// + /// IedServer configuration object + /// + public class IedServerConfig : IDisposable + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedServerConfig_create(); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedServerConfig_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setReportBufferSize(IntPtr self, int reportBufferSize); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int IedServerConfig_getReportBufferSize(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setFileServiceBasePath(IntPtr self, string basepath); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedServerConfig_getFileServiceBasePath(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setEdition(IntPtr self, byte edition); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern byte IedServerConfig_getEdition(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setMaxMmsConnections(IntPtr self, int maxConnections); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int IedServerConfig_getMaxMmsConnections(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool IedServerConfig_isDynamicDataSetServiceEnabled(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_enableDynamicDataSetService(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setMaxAssociationSpecificDataSets(IntPtr self, int maxDataSets); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int IedServerConfig_getMaxAssociationSpecificDataSets(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setMaxDomainSpecificDataSets(IntPtr self, int maxDataSets); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int IedServerConfig_getMaxDomainSpecificDataSets(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setMaxDataSetEntries(IntPtr self, int maxDataSetEntries); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int IedServerConfig_getMaxDatasSetEntries(IntPtr self); + + internal IntPtr self; + + public IedServerConfig () + { + self = IedServerConfig_create (); + } + + /// + /// Gets or sets the size of the report buffer for buffered report control blocks + /// + /// The size of the report buffer. + public int ReportBufferSize + { + get { + return IedServerConfig_getReportBufferSize (self); + } + set { + IedServerConfig_setReportBufferSize (self, value); + } + } + + /// + /// Gets or sets the file service base path. + /// + /// The file service base path. + public string FileServiceBasePath + { + get { + return Marshal.PtrToStringAnsi (IedServerConfig_getFileServiceBasePath (self)); + } + set { + IedServerConfig_setFileServiceBasePath (self, value); + } + } + + /// + /// Gets or sets the edition of the IEC 61850 standard to use + /// + /// The IEC 61850 edition to use. + public Iec61850Edition Edition + { + get { + return (Iec61850Edition)IedServerConfig_getEdition (self); + } + set { + IedServerConfig_setEdition (self, (byte) value); + } + } + + /// + /// Gets or sets maximum number of MMS clients + /// + /// The max number of MMS client connections. + public int MaxMmsConnections + { + get { + return IedServerConfig_getMaxMmsConnections (self); + } + set { + IedServerConfig_setMaxMmsConnections (self, value); + } + } + + /// + /// Enable/Disable dynamic data set service for MMS + /// + /// true if dynamic data set service enabled; otherwise, false. + public bool DynamicDataSetServiceEnabled + { + get { + return IedServerConfig_isDynamicDataSetServiceEnabled (self); + } + set { + IedServerConfig_enableDynamicDataSetService (self, value); + } + } + + /// + /// Gets or sets the maximum number of data set entries for dynamic data sets + /// + /// The max. number data set entries. + public int MaxDataSetEntries + { + get { + return IedServerConfig_getMaxDatasSetEntries (self); + } + set { + IedServerConfig_setMaxDataSetEntries (self, value); + } + } + + /// + /// Gets or sets the maximum number of association specific (non-permanent) data sets. + /// + /// The max. number of association specific data sets. + public int MaxAssociationSpecificDataSets + { + get { + return IedServerConfig_getMaxAssociationSpecificDataSets (self); + } + set { + IedServerConfig_setMaxAssociationSpecificDataSets (self, value); + } + } + + /// + /// Gets or sets the maximum number of domain specific (permanent) data sets. + /// + /// The max. numebr of domain specific data sets. + public int MaxDomainSpecificDataSets + { + get { + return IedServerConfig_getMaxDomainSpecificDataSets (self); + } + set { + IedServerConfig_setMaxDomainSpecificDataSets (self, value); + } + } + + /// + /// Releases all resource used by the object. + /// + /// Call when you are finished using the . The + /// method leaves the in an unusable state. After + /// calling , you must release all references to the + /// so the garbage collector can reclaim the memory that the + /// was occupying. + public void Dispose() + { + lock (this) { + if (self != IntPtr.Zero) { + IedServerConfig_destroy (self); + self = IntPtr.Zero; + } + } + } + + ~IedServerConfig() + { + Dispose (); + } + } +} + diff --git a/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs b/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs index 9192eff38..759e7f532 100644 --- a/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs +++ b/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs @@ -142,6 +142,9 @@ public void SetRemoteAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSele for (int i = 0; i < tSelector.Length; i++) nativeTSelector.value[i] = tSelector[i]; + if (sSelector.Length > 16) + throw new ArgumentOutOfRangeException("sSelector", "maximum size (16) exceeded"); + NativeSSelector nativeSSelector; nativeSSelector.size = (byte) sSelector.Length; nativeSSelector.value = new byte[16]; @@ -190,6 +193,9 @@ public void SetLocalAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelec for (int i = 0; i < tSelector.Length; i++) nativeTSelector.value[i] = tSelector[i]; + if (sSelector.Length > 16) + throw new ArgumentOutOfRangeException("sSelector", "maximum size (16) exceeded"); + NativeSSelector nativeSSelector; nativeSSelector.size = (byte) sSelector.Length; nativeSSelector.value = new byte[16]; diff --git a/dotnet/IEC61850forCSharp/MmsValue.cs b/dotnet/IEC61850forCSharp/MmsValue.cs index ed9787ab7..f85b32181 100644 --- a/dotnet/IEC61850forCSharp/MmsValue.cs +++ b/dotnet/IEC61850forCSharp/MmsValue.cs @@ -67,7 +67,7 @@ public class MmsValue : IEnumerable static extern int MmsValue_getBitStringSize(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void MmsValue_setBitStringBit(IntPtr self, int bitPos, bool value); + static extern void MmsValue_setBitStringBit(IntPtr self, int bitPos, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] @@ -113,7 +113,7 @@ public class MmsValue : IEnumerable static extern UInt32 MmsValue_toUnixTimestamp (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr MmsValue_newBoolean (bool value); + static extern IntPtr MmsValue_newBoolean ([MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr MmsValue_newFloat (float value); @@ -136,6 +136,15 @@ public class MmsValue : IEnumerable [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr MmsValue_newVisibleString(string value); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr MmsValue_createArray(IntPtr elementType, int size); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr MmsValue_createEmptyArray(int size); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr MmsValue_createEmptyStructure(int size); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr MmsValue_newOctetString(int size, int maxSize); @@ -156,7 +165,7 @@ public class MmsValue : IEnumerable static extern bool MmsValue_equals(IntPtr self, IntPtr otherValue); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr MmsValue_newBinaryTime (bool timeOfDay); + static extern IntPtr MmsValue_newBinaryTime ([MarshalAs(UnmanagedType.I1)] bool timeOfDay); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void MmsValue_setBinaryTime (IntPtr self, UInt64 timestamp); @@ -164,9 +173,12 @@ public class MmsValue : IEnumerable [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern ulong MmsValue_getBinaryTimeAsUtcMs (IntPtr self); - [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] + [DllImport("iec61850", CallingConvention=CallingConvention.Cdecl)] static extern int MmsValue_getDataAccessError(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void MmsValue_setElement(IntPtr complexValue, int index, IntPtr elementValue); + internal IntPtr valueReference; /* reference to native MmsValue instance */ private bool responsableForDeletion; /* if .NET wrapper is responsable for the deletion of the native MmsValue instance */ @@ -214,6 +226,10 @@ public MmsValue (long value) valueReference = MmsValue_newIntegerFromInt64 (value); } + /// + /// Create a new instance of type MMS_VISIBLE_STRING. + /// + /// Value. public MmsValue (string value) { valueReference = MmsValue_newVisibleString(value); @@ -270,6 +286,53 @@ public MmsValue(byte[] octetString) this.setOctetString (octetString); } + /// + /// Create a new MmsValue instance of type MMS_ARRAY. Array elements have the fiven type + /// + /// the newly created array + /// array element type + /// number of array elements + public static MmsValue NewArray(MmsVariableSpecification elementType, int size) + { + if (size < 1) + throw new MmsValueException ("array requires at least one element"); + + IntPtr newValue = MmsValue_createArray (elementType.self, size); + + return new MmsValue (newValue, true); + } + + /// + /// Create a new MmsValue instance of type MMS_ARRAY. Array elements are not initialized! + /// + /// the newly created array + /// number of array elements + public static MmsValue NewEmptyArray(int size) + { + if (size < 1) + throw new MmsValueException ("array requires at least one element"); + + IntPtr newValue = MmsValue_createEmptyArray (size); + + return new MmsValue (newValue, true); + } + + /// + /// Create a new MmsValue instance of type MMS_STRUCTURE. Structure elements are not initialized! + /// + /// the newly created array + /// number of structure elements + public static MmsValue NewEmptyStructure(int size) + { + if (size < 1) + throw new MmsValueException ("structure requires at least one element"); + + IntPtr newValue = MmsValue_createEmptyStructure (size); + + return new MmsValue (newValue, true); + } + + /// /// Create a new MmsValue instance of type MMS_BINARY_TIME /// @@ -427,12 +490,39 @@ public MmsValue GetElement (int index) throw new MmsValueException ("Value is of wrong type"); } + /// + /// Sets the element of an array of structure + /// + /// index of the element starting with 0 + /// MmsValue instance that will be used as element value + /// This exception is thrown if the value has the wrong type. + /// This exception is thrown if the index is out of range. + public void SetElement(int index, MmsValue elementValue) + { + MmsType elementType = GetType (); + + if ((elementType == MmsType.MMS_ARRAY) || (elementType == MmsType.MMS_STRUCTURE)) { + + if ((index >= 0) && (index < Size ())) { + MmsValue_setElement (valueReference, index, elementValue.valueReference); + + } else + throw new MmsValueException ("Index out of bounds"); + + } else + throw new MmsValueException ("Value is of wrong type"); + + } public MmsDataAccessError GetDataAccessError () { - int errorCode = MmsValue_getDataAccessError (valueReference); + if (GetType () == MmsType.MMS_DATA_ACCESS_ERROR) { + int errorCode = MmsValue_getDataAccessError (valueReference); - return (MmsDataAccessError)errorCode; + return (MmsDataAccessError)errorCode; + } + else + throw new MmsValueException ("Value is of wrong type"); } /// diff --git a/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs b/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs index 159f10e6d..1fe97503d 100644 --- a/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs +++ b/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs @@ -63,7 +63,7 @@ public class MmsVariableSpecification : IEnumerable [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int MmsVariableSpecification_getExponentWidth(IntPtr self); - private IntPtr self; + internal IntPtr self; private bool responsableForDeletion; internal MmsVariableSpecification (IntPtr self) diff --git a/dotnet/IEC61850forCSharp/ReportControlBlock.cs b/dotnet/IEC61850forCSharp/ReportControlBlock.cs index ad9f5da3c..20ee9146b 100644 --- a/dotnet/IEC61850forCSharp/ReportControlBlock.cs +++ b/dotnet/IEC61850forCSharp/ReportControlBlock.cs @@ -1,7 +1,7 @@ /* * ReportControlBlock.cs * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -36,6 +36,9 @@ namespace Client /// public delegate void ReportHandler (Report report, object parameter); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void InternalReportHandler (IntPtr parameter, IntPtr report); + /// /// Report control block (RCB) representation. /// @@ -44,16 +47,13 @@ namespace Client /// Values from the server will only be read when the GetRCBValues method is called. /// Values at the server are only affected when the SetRCBValues method is called. /// - public class ReportControlBlock + public class ReportControlBlock : IDisposable { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientReportControlBlock_create (string dataAttributeReference); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr IedConnection_getRCBValues (IntPtr connection, out int error, string rcbReference, IntPtr updateRcb); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void IedConnection_setRCBValues (IntPtr connection, out int error, IntPtr rcb, UInt32 parametersMask, bool singleRequest); + static extern void ClientReportControlBlock_destroy (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] @@ -70,14 +70,14 @@ public class ReportControlBlock static extern bool ClientReportControlBlock_getRptEna (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientReportControlBlock_setRptEna(IntPtr self, bool rptEna); + static extern void ClientReportControlBlock_setRptEna(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool rptEna); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool ClientReportControlBlock_getResv (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientReportControlBlock_setResv (IntPtr self, bool resv); + static extern void ClientReportControlBlock_setResv (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool resv); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientReportControlBlock_getDataSetReference (IntPtr self); @@ -120,14 +120,14 @@ public class ReportControlBlock static extern bool ClientReportControlBlock_getGI (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientReportControlBlock_setGI (IntPtr self, bool gi); + static extern void ClientReportControlBlock_setGI (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool gi); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool ClientReportControlBlock_getPurgeBuf (IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void ClientReportControlBlock_setPurgeBuf (IntPtr self, bool purgeBuf); + static extern void ClientReportControlBlock_setPurgeBuf (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool purgeBuf); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern Int32 ClientReportControlBlock_getResvTms (IntPtr self); @@ -147,18 +147,7 @@ public class ReportControlBlock [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientReportControlBlock_getOwner (IntPtr self); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void IedConnection_installReportHandler (IntPtr connection, string rcbReference, string rptId, InternalReportHandler handler, - IntPtr handlerParameter); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void IedConnection_uninstallReportHandler(IntPtr connection, string rcbReference); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void InternalReportHandler (IntPtr parameter, IntPtr report); - private IntPtr self; - private IntPtr connection; private IedConnection iedConnection = null; private string objectReference; private bool flagRptId = false; @@ -221,14 +210,11 @@ private void internalReportHandler (IntPtr parameter, IntPtr report) internal ReportControlBlock (string objectReference, IedConnection iedConnection, IntPtr connection) { self = ClientReportControlBlock_create (objectReference); - this.iedConnection = iedConnection; - this.connection = connection; - this.objectReference = objectReference; - } - internal void DisposeInternal() - { - IedConnection_uninstallReportHandler(connection, objectReference); + if (self != IntPtr.Zero) { + this.iedConnection = iedConnection; + this.objectReference = objectReference; + } } /// @@ -239,11 +225,25 @@ internal void DisposeInternal() /// After calling , you must release all references to the /// so the garbage collector can reclaim the memory that the /// was occupying. - public void Dispose() + public void Dispose() { - DisposeInternal (); + lock (this) { + if (self != IntPtr.Zero) { + + iedConnection.UninstallReportHandler (objectReference); + + iedConnection.RemoveRCB (this); + + ClientReportControlBlock_destroy (self); - iedConnection.RemoveRCB (this); + self = IntPtr.Zero; + } + } + } + + ~ReportControlBlock() + { + Dispose (); } public string GetObjectReference () @@ -279,9 +279,10 @@ public void InstallReportHandler (ReportHandler reportHandler, object parameter) { internalHandler = new InternalReportHandler(internalReportHandler); } + + iedConnection.InstallReportHandler (objectReference, reportId, internalHandler); - IedConnection_installReportHandler(this.connection, objectReference, reportId, internalHandler, IntPtr.Zero); - reportHandlerInstalled = true; + reportHandlerInstalled = true; } } @@ -293,7 +294,7 @@ public void GetRCBValues () { int error; - IedConnection_getRCBValues (connection, out error, objectReference, self); + iedConnection.GetRCBValues (out error, objectReference, self); if (error != 0) throw new IedConnectionException ("getRCBValues service failed", error); @@ -370,7 +371,7 @@ public void SetRCBValues (bool singleRequest) int error; - IedConnection_setRCBValues (connection, out error, self, parametersMask, singleRequest); + iedConnection.SetRCBValues (out error, self, parametersMask, singleRequest); resetSendFlags(); diff --git a/dotnet/IEC61850forCSharp/Reporting.cs b/dotnet/IEC61850forCSharp/Reporting.cs index dfbf6d6b3..90ddba2d1 100644 --- a/dotnet/IEC61850forCSharp/Reporting.cs +++ b/dotnet/IEC61850forCSharp/Reporting.cs @@ -35,17 +35,6 @@ public partial class IedConnection private List activeRCBs = null; - private void cleanupRCBs() - { - if (activeRCBs != null) { - - foreach (ReportControlBlock rcb in activeRCBs) { - rcb.DisposeInternal (); - } - } - - } - public ReportControlBlock GetReportControlBlock (string rcbObjectReference) { var newRCB = new ReportControlBlock (rcbObjectReference, this, connection); diff --git a/dotnet/IEC61850forCSharp/SampledValuesControlBlock.cs b/dotnet/IEC61850forCSharp/SampledValuesControlBlock.cs new file mode 100644 index 000000000..dc481140d --- /dev/null +++ b/dotnet/IEC61850forCSharp/SampledValuesControlBlock.cs @@ -0,0 +1,203 @@ +/* + * SampledValuesControlBlock.cs + * + * Copyright 2017 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; + +using IEC61850.Common; + +namespace IEC61850 +{ + namespace Client + { + /// + /// Sampled values control bloc (SvCB) representation. + /// + /// + /// This class is used as a client side representation (copy) of a sampled values control block (SvCB). + /// + public class SampledValuesControlBlock : IDisposable + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ClientSVControlBlock_create (IntPtr iedConnection, string reference); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void ClientSVControlBlock_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int ClientSVControlBlock_getLastComError (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientSVControlBlock_isMulticast (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientSVControlBlock_setSvEna (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientSVControlBlock_setResv (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientSVControlBlock_getSvEna (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ClientSVControlBlock_getResv (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ClientSVControlBlock_getMsvID (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ClientSVControlBlock_getDatSet (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ClientSVControlBlock_getConfRev (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt16 ClientSVControlBlock_getSmpRate (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int ClientSVControlBlock_getOptFlds (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern byte ClientSVControlBlock_getSmpMod(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int ClientSVControlBlock_getNoASDU (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern PhyComAddress ClientSVControlBlock_getDstAddress (IntPtr self); + + private IntPtr self; + private string objectReference; + + private bool isDisposed = false; + + + internal SampledValuesControlBlock(IntPtr iedConnection, string objectReference) + { + self = ClientSVControlBlock_create (iedConnection, objectReference); + this.objectReference = objectReference; + } + + public string GetObjectReference () + { + return this.objectReference; + } + + public IedClientError GetLastComError() + { + return (IedClientError)ClientSVControlBlock_getLastComError (self); + } + + public bool IsMulticast() + { + return ClientSVControlBlock_isMulticast (self); + } + + public bool GetResv() + { + return ClientSVControlBlock_getResv (self); + } + + public bool SetResv(bool value) + { + return ClientSVControlBlock_setResv (self, value); + } + + public bool GetSvEna() + { + return ClientSVControlBlock_getSvEna (self); + } + + public bool SetSvEna(bool value) + { + return ClientSVControlBlock_setSvEna (self, value); + } + + public string GetMsvID () + { + IntPtr msvIdPtr = ClientSVControlBlock_getMsvID (self); + + return Marshal.PtrToStringAnsi (msvIdPtr); + } + + public string GetDatSet () + { + IntPtr datSetPtr = ClientSVControlBlock_getDatSet (self); + + return Marshal.PtrToStringAnsi (datSetPtr); + } + + public UInt32 GetConfRev () + { + return ClientSVControlBlock_getConfRev (self); + } + + public UInt16 GetSmpRate () + { + return ClientSVControlBlock_getSmpRate (self); + } + + public SVOptions GetOptFlds () + { + return (SVOptions)ClientSVControlBlock_getOptFlds (self); + } + + public SmpMod GetSmpMod () + { + return (SmpMod)ClientSVControlBlock_getSmpMod (self); + } + + public int GetNoASDU () + { + return ClientSVControlBlock_getNoASDU (self); + } + + public PhyComAddress GetDstAddress() + { + return ClientSVControlBlock_getDstAddress (self); + } + + public void Dispose() + { + if (isDisposed == false) { + isDisposed = true; + ClientSVControlBlock_destroy (self); + self = IntPtr.Zero; + } + } + + ~SampledValuesControlBlock() + { + Dispose (); + } + + } + + + } +} diff --git a/dotnet/IEC61850forCSharp/SampledValuesSubscriber.cs b/dotnet/IEC61850forCSharp/SampledValuesSubscriber.cs new file mode 100644 index 000000000..5da087a0e --- /dev/null +++ b/dotnet/IEC61850forCSharp/SampledValuesSubscriber.cs @@ -0,0 +1,474 @@ +/* + * SampledValuedSubscriber.cs + * + * Copyright 2017 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +using System; +using System.Runtime.InteropServices; +using IEC61850.Common; + +namespace IEC61850 +{ + namespace SV + { + + namespace Subscriber + { + /// + /// SV receiver. + /// + /// A receiver is responsible for processing all SV message for a single Ethernet interface. + /// In order to process messages from multiple Ethernet interfaces you have to create multiple + /// instances. + public class SVReceiver : IDisposable + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr SVReceiver_create (); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_disableDestAddrCheck(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_enableDestAddrCheck(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_addSubscriber(IntPtr self, IntPtr subscriber); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_removeSubscriber(IntPtr self, IntPtr subscriber); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_start(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_stop(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool SVReceiver_isRunning (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVReceiver_setInterfaceId(IntPtr self, string interfaceId); + + private IntPtr self; + + private bool isDisposed = false; + + /// + /// Initializes a new instance of the class. + /// + + public SVReceiver() + { + self = SVReceiver_create (); + } + + public void SetInterfaceId(string interfaceId) + { + SVReceiver_setInterfaceId (self, interfaceId); + } + + public void DisableDestAddrCheck() + { + SVReceiver_disableDestAddrCheck (self); + } + + public void EnableDestAddrCheck() + { + SVReceiver_enableDestAddrCheck (self); + } + + /// + /// Add a subscriber to handle + /// + /// Subscriber. + public void AddSubscriber(SVSubscriber subscriber) + { + SVReceiver_addSubscriber (self, subscriber.self); + } + + + public void RemoveSubscriber(SVSubscriber subscriber) + { + SVReceiver_removeSubscriber (self, subscriber.self); + } + + /// + /// Start handling SV messages + /// + public void Start() + { + SVReceiver_start (self); + } + + /// + /// Stop handling SV messges + /// + public void Stop() + { + SVReceiver_stop (self); + } + + public bool IsRunning() + { + return SVReceiver_isRunning (self); + } + + /// + /// Releases all resource used by the object. + /// + /// Call when you are finished using the . The + /// method leaves the in an unusable state. + /// After calling , you must release all references to the + /// so the garbage collector can reclaim the memory that the + /// was occupying. + public void Dispose() + { + if (isDisposed == false) { + isDisposed = true; + SVReceiver_destroy (self); + self = IntPtr.Zero; + } + } + + ~SVReceiver() + { + Dispose (); + } + } + + + /// + /// SV listener. + /// + public delegate void SVUpdateListener (SVSubscriber report, object parameter, SVSubscriberASDU asdu); + + /// + /// Sampled Values (SV) Subscriber + /// + /// A subscriber is an instance associated with a single stream of measurement data. It is identified + /// by the Ethernet destination address, the appID value (both are on SV message level) and the svID value + /// that is part of each ASDU. + /// + public class SVSubscriber : IDisposable + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void InternalSVUpdateListener (IntPtr subscriber, IntPtr parameter, IntPtr asdu); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr SVSubscriber_create([Out] byte[] ethAddr, UInt16 appID); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr SVSubscriber_create(IntPtr ethAddr, UInt16 appID); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVSubscriber_setListener(IntPtr self, InternalSVUpdateListener listener, IntPtr parameter); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void SVSubscriber_destroy(IntPtr self); + + internal IntPtr self; + + private bool isDisposed = false; + + private SVUpdateListener listener; + private object listenerParameter = null; + + private event InternalSVUpdateListener internalListener = null; + + private void internalSVUpdateListener (IntPtr subscriber, IntPtr parameter, IntPtr asdu) + { + try { + + if (listener != null) { + listener(this, listenerParameter, new SVSubscriberASDU(asdu)); + } + + } + catch (Exception e) { + // older versions of mono 2.10 (for linux?) cause this exception + Console.WriteLine(e.Message); + } + } + + public SVSubscriber(byte[] ethAddr, UInt16 appID) + { + if (ethAddr == null) { + self = SVSubscriber_create (IntPtr.Zero, appID); + } else { + + if (ethAddr.Length != 6) + throw new ArgumentException ("ethAddr argument has to be of 6 byte size"); + + self = SVSubscriber_create (ethAddr, appID); + } + } + + public void SetListener(SVUpdateListener listener, object parameter) + { + this.listener = listener; + this.listenerParameter = parameter; + + if (internalListener == null) { + internalListener = new InternalSVUpdateListener (internalSVUpdateListener); + + SVSubscriber_setListener (self, internalListener, IntPtr.Zero); + } + } + + public void Dispose() + { + if (isDisposed == false) { + isDisposed = true; + SVSubscriber_destroy (self); + self = IntPtr.Zero; + } + } + } + + + public class SVSubscriberASDU + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt16 SVSubscriber_ASDU_getSmpCnt(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr SVSubscriber_ASDU_getSvId(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr SVSubscriber_ASDU_getDatSet(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt32 SVSubscriber_ASDU_getConfRev(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern byte SVSubscriber_ASDU_getSmpMod(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt16 SVSubscriber_ASDU_getSmpRate(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool SVSubscriber_ASDU_hasDatSet(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool SVSubscriber_ASDU_hasRefrTm(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool SVSubscriber_ASDU_hasSmpMod(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool SVSubscriber_ASDU_hasSmpRate(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt64 SVSubscriber_ASDU_getRefrTmAsMs(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern sbyte SVSubscriber_ASDU_getINT8(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern Int16 SVSubscriber_ASDU_getINT16(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern Int32 SVSubscriber_ASDU_getINT32(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern Int64 SVSubscriber_ASDU_getINT64(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern byte SVSubscriber_ASDU_getINT8U(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt16 SVSubscriber_ASDU_getINT16U(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt32 SVSubscriber_ASDU_getINT32U(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt64 SVSubscriber_ASDU_getINT64U(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern float SVSubscriber_ASDU_getFLOAT32(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern double SVSubscriber_ASDU_getFLOAT64(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt16 SVSubscriber_ASDU_getQuality(IntPtr self, int index); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern int SVSubscriber_ASDU_getDataSize(IntPtr self); + + private IntPtr self; + + internal SVSubscriberASDU (IntPtr self) + { + this.self = self; + } + + public UInt16 GetSmpCnt() + { + return SVSubscriber_ASDU_getSmpCnt (self); + } + + public string GetSvId() + { + return Marshal.PtrToStringAnsi (SVSubscriber_ASDU_getSvId(self)); + } + + public string GetDatSet() + { + return Marshal.PtrToStringAnsi (SVSubscriber_ASDU_getDatSet(self)); + } + + public UInt32 GetConfRev() + { + return SVSubscriber_ASDU_getConfRev (self); + } + + public SmpMod GetSmpMod() + { + return (SmpMod) SVSubscriber_ASDU_getSmpMod (self); + } + + public UInt16 GetSmpRate() + { + return (UInt16)SVSubscriber_ASDU_getSmpRate (self); + } + + public bool HasDatSet() + { + return SVSubscriber_ASDU_hasDatSet (self); + } + + public bool HasRefrRm() + { + return SVSubscriber_ASDU_hasRefrTm (self); + } + + public bool HasSmpMod() + { + return SVSubscriber_ASDU_hasSmpMod (self); + } + + public bool HasSmpRate() + { + return SVSubscriber_ASDU_hasSmpRate (self); + } + + public UInt64 GetRefrTmAsMs() + { + return SVSubscriber_ASDU_getRefrTmAsMs (self); + } + + public sbyte GetINT8(int index) + { + return SVSubscriber_ASDU_getINT8 (self, index); + } + + public Int16 GetINT16(int index) + { + return SVSubscriber_ASDU_getINT16 (self, index); + } + + public Int32 GetINT32(int index) + { + return SVSubscriber_ASDU_getINT32 (self, index); + } + + public Int64 GetINT64(int index) + { + return SVSubscriber_ASDU_getINT64 (self, index); + } + + public byte GetINT8U(int index) + { + return SVSubscriber_ASDU_getINT8U (self, index); + } + + public UInt16 GetINT16U(int index) + { + return SVSubscriber_ASDU_getINT16U (self, index); + } + + public UInt32 GetINT32U(int index) + { + return SVSubscriber_ASDU_getINT32U (self, index); + } + + public UInt64 GetINT64U(int index) + { + return SVSubscriber_ASDU_getINT64U (self, index); + } + + public float GetFLOAT32(int index) + { + return SVSubscriber_ASDU_getFLOAT32 (self, index); + } + + public double GetFLOAT64(int index) + { + return SVSubscriber_ASDU_getFLOAT64 (self, index); + } + + private struct PTimestamp + { + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 8)] + public byte[] val; + } + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern PTimestamp SVSubscriber_ASDU_getTimestamp(IntPtr self, int index); + + public Timestamp GetTimestamp(int index) + { + PTimestamp retVal = SVSubscriber_ASDU_getTimestamp (self, index); + + return new Timestamp (retVal.val); + } + + public Quality GetQuality(int index) + { + UInt16 qValue = SVSubscriber_ASDU_getQuality (self, index); + + return new Quality (qValue); + } + + /// + /// Gets the size of the payload data in bytes. The payload comprises the data set data. + /// + /// The payload data size in byte + public int GetDataSize() + { + return SVSubscriber_ASDU_getDataSize (self); + } + } + } + + } +} + diff --git a/dotnet/IEC61850forCSharp/TLS.cs b/dotnet/IEC61850forCSharp/TLS.cs index a10d4c231..69a273b05 100644 --- a/dotnet/IEC61850forCSharp/TLS.cs +++ b/dotnet/IEC61850forCSharp/TLS.cs @@ -54,42 +54,55 @@ public class TLSConfiguration : IDisposable static extern void TLSConfiguration_destroy(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, bool value); + static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setChainValidation (IntPtr self, bool value); + static extern void TLSConfiguration_setChainValidation (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void TLSConfiguration_setClientMode(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_setOwnKeyFromFile (IntPtr self, string filename, string keyPassword); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename); public TLSConfiguration() { self = TLSConfiguration_create (); } + ~TLSConfiguration() + { + Dispose (); + } + internal IntPtr GetNativeInstance() { return self; @@ -126,7 +139,6 @@ public void SetClientMode() public void SetOwnCertificate(string filename) { if (TLSConfiguration_setOwnCertificateFromFile (self, filename) == false) { - Console.WriteLine ("Failed to read certificate from file!"); throw new CryptographicException ("Failed to read certificate from file"); } } @@ -136,7 +148,6 @@ public void SetOwnCertificate(X509Certificate2 cert) byte[] certBytes = cert.GetRawCertData (); if (TLSConfiguration_setOwnCertificate (self, certBytes, certBytes.Length) == false) { - Console.WriteLine ("Failed to set certificate!"); throw new CryptographicException ("Failed to set certificate"); } } @@ -144,7 +155,6 @@ public void SetOwnCertificate(X509Certificate2 cert) public void AddAllowedCertificate(string filename) { if (TLSConfiguration_addAllowedCertificateFromFile (self, filename) == false) { - Console.WriteLine ("Failed to read allowed certificate from file!"); throw new CryptographicException ("Failed to read allowed certificate from file"); } } @@ -154,7 +164,6 @@ public void AddAllowedCertificate(X509Certificate2 cert) byte[] certBytes = cert.GetRawCertData (); if (TLSConfiguration_addAllowedCertificate (self, certBytes, certBytes.Length) == false) { - Console.WriteLine ("Failed to add allowed certificate!"); throw new CryptographicException ("Failed to add allowed certificate"); } } @@ -162,7 +171,6 @@ public void AddAllowedCertificate(X509Certificate2 cert) public void AddCACertificate(string filename) { if (TLSConfiguration_addCACertificateFromFile (self, filename) == false) { - Console.WriteLine ("Failed to read CA certificate from file!"); throw new CryptographicException ("Failed to read CA certificate from file"); } } @@ -172,7 +180,6 @@ public void AddCACertificate(X509Certificate2 cert) byte[] certBytes = cert.GetRawCertData (); if (TLSConfiguration_addCACertificate (self, certBytes, certBytes.Length) == false) { - Console.WriteLine ("Failed to add CA certificate!"); throw new CryptographicException ("Failed to add CA certificate"); } } @@ -180,7 +187,6 @@ public void AddCACertificate(X509Certificate2 cert) public void SetOwnKey (string filename, string password) { if (TLSConfiguration_setOwnKeyFromFile (self, filename, password) == false) { - Console.WriteLine ("Failed to read own key from file!"); throw new CryptographicException ("Failed to read own key from file"); } } @@ -190,14 +196,18 @@ public void SetOwnKey (X509Certificate2 key, string password) byte[] certBytes = key.Export (X509ContentType.Pkcs12); if (TLSConfiguration_setOwnKey (self, certBytes, certBytes.Length, password) == false) { - Console.WriteLine ("Failed to set own key!"); throw new CryptographicException ("Failed to set own key"); } } public void Dispose() { - TLSConfiguration_destroy (self); + lock (this) { + if (self != IntPtr.Zero) { + TLSConfiguration_destroy (self); + self = IntPtr.Zero; + } + } } } diff --git a/dotnet/control/ControlExample.cs b/dotnet/control/ControlExample.cs index 7bce2eec5..a48d013c9 100644 --- a/dotnet/control/ControlExample.cs +++ b/dotnet/control/ControlExample.cs @@ -40,6 +40,7 @@ public static void Main (string[] args) ControlModel controlModel = control.GetControlModel(); Console.WriteLine(objectReference + " has control model " + controlModel.ToString()); + Console.WriteLine(" type of ctlVal: " + control.GetCtlValType().ToString()); switch (controlModel) { diff --git a/dotnet/core/2.0/IEC61850.NET.core.2.0.sln b/dotnet/core/2.0/IEC61850.NET.core.2.0.sln new file mode 100644 index 000000000..0d5924286 --- /dev/null +++ b/dotnet/core/2.0/IEC61850.NET.core.2.0.sln @@ -0,0 +1,109 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2010 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEC61850.NET.core.2.0", "IEC61850.NET.core.2.0\IEC61850.NET.core.2.0.csproj", "{16C58017-94CC-4C92-AFDC-84AC24C393AD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example1", "client_example1\client_example1.csproj", "{AFDC261C-B293-4650-8D90-A862E27CEC15}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example2", "client_example2\client_example2.csproj", "{4FD69E0A-2548-4BFF-BD15-7ED0612C2FC1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example3", "client_example3\client_example3.csproj", "{11B5EE5D-36AC-4DAD-89E5-251E46A6269E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_files", "client_example_files\client_example_files.csproj", "{3B0970E6-77A3-4D97-A998-50758BE7C4DC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_datasets", "client_example_datasets\client_example_datasets.csproj", "{F61DD20F-4CF4-49FD-836A-AA233B4659B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_control", "client_example_control\client_example_control.csproj", "{53FF9ABD-300B-49F2-80A6-15B98121066D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_authenticate", "client_example_authenticate\client_example_authenticate.csproj", "{6E85A44B-E29D-4819-8AF6-2CEAD7851C1B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_reporting", "client_example_reporting\client_example_reporting.csproj", "{BA077BC8-0B98-4071-8F1F-C97865954477}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_tls", "client_example_tls\client_example_tls.csproj", "{954CCB43-3640-48F1-9A8E-4D736A5EF575}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_model_browsing", "client_example_model_browsing\client_example_model_browsing.csproj", "{A9890431-ABE4-4D80-8D36-132E4096E163}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server_example1", "server_example1\server_example1.csproj", "{18A97E35-2FF9-49F7-934F-F1E9C50DC054}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sv_subscriber_example", "sv_subscriber_example\sv_subscriber_example.csproj", "{D9D8B1B7-B4CB-473D-9C6C-3B2F3C4DB2BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "goose_subscriber_example", "goose_subscriber_example\goose_subscriber_example.csproj", "{ABBF5A82-4F5E-41DA-A727-C2298E8AF650}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_log", "client_example_log\client_example_log.csproj", "{160586BB-3601-4D0C-86D7-414F585041B4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {16C58017-94CC-4C92-AFDC-84AC24C393AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16C58017-94CC-4C92-AFDC-84AC24C393AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16C58017-94CC-4C92-AFDC-84AC24C393AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16C58017-94CC-4C92-AFDC-84AC24C393AD}.Release|Any CPU.Build.0 = Release|Any CPU + {AFDC261C-B293-4650-8D90-A862E27CEC15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFDC261C-B293-4650-8D90-A862E27CEC15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFDC261C-B293-4650-8D90-A862E27CEC15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFDC261C-B293-4650-8D90-A862E27CEC15}.Release|Any CPU.Build.0 = Release|Any CPU + {4FD69E0A-2548-4BFF-BD15-7ED0612C2FC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FD69E0A-2548-4BFF-BD15-7ED0612C2FC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FD69E0A-2548-4BFF-BD15-7ED0612C2FC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FD69E0A-2548-4BFF-BD15-7ED0612C2FC1}.Release|Any CPU.Build.0 = Release|Any CPU + {11B5EE5D-36AC-4DAD-89E5-251E46A6269E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11B5EE5D-36AC-4DAD-89E5-251E46A6269E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11B5EE5D-36AC-4DAD-89E5-251E46A6269E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11B5EE5D-36AC-4DAD-89E5-251E46A6269E}.Release|Any CPU.Build.0 = Release|Any CPU + {3B0970E6-77A3-4D97-A998-50758BE7C4DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B0970E6-77A3-4D97-A998-50758BE7C4DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B0970E6-77A3-4D97-A998-50758BE7C4DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B0970E6-77A3-4D97-A998-50758BE7C4DC}.Release|Any CPU.Build.0 = Release|Any CPU + {F61DD20F-4CF4-49FD-836A-AA233B4659B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61DD20F-4CF4-49FD-836A-AA233B4659B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61DD20F-4CF4-49FD-836A-AA233B4659B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61DD20F-4CF4-49FD-836A-AA233B4659B8}.Release|Any CPU.Build.0 = Release|Any CPU + {53FF9ABD-300B-49F2-80A6-15B98121066D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53FF9ABD-300B-49F2-80A6-15B98121066D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53FF9ABD-300B-49F2-80A6-15B98121066D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53FF9ABD-300B-49F2-80A6-15B98121066D}.Release|Any CPU.Build.0 = Release|Any CPU + {6E85A44B-E29D-4819-8AF6-2CEAD7851C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E85A44B-E29D-4819-8AF6-2CEAD7851C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E85A44B-E29D-4819-8AF6-2CEAD7851C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E85A44B-E29D-4819-8AF6-2CEAD7851C1B}.Release|Any CPU.Build.0 = Release|Any CPU + {BA077BC8-0B98-4071-8F1F-C97865954477}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA077BC8-0B98-4071-8F1F-C97865954477}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA077BC8-0B98-4071-8F1F-C97865954477}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA077BC8-0B98-4071-8F1F-C97865954477}.Release|Any CPU.Build.0 = Release|Any CPU + {954CCB43-3640-48F1-9A8E-4D736A5EF575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {954CCB43-3640-48F1-9A8E-4D736A5EF575}.Debug|Any CPU.Build.0 = Debug|Any CPU + {954CCB43-3640-48F1-9A8E-4D736A5EF575}.Release|Any CPU.ActiveCfg = Release|Any CPU + {954CCB43-3640-48F1-9A8E-4D736A5EF575}.Release|Any CPU.Build.0 = Release|Any CPU + {A9890431-ABE4-4D80-8D36-132E4096E163}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9890431-ABE4-4D80-8D36-132E4096E163}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9890431-ABE4-4D80-8D36-132E4096E163}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9890431-ABE4-4D80-8D36-132E4096E163}.Release|Any CPU.Build.0 = Release|Any CPU + {18A97E35-2FF9-49F7-934F-F1E9C50DC054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18A97E35-2FF9-49F7-934F-F1E9C50DC054}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18A97E35-2FF9-49F7-934F-F1E9C50DC054}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18A97E35-2FF9-49F7-934F-F1E9C50DC054}.Release|Any CPU.Build.0 = Release|Any CPU + {D9D8B1B7-B4CB-473D-9C6C-3B2F3C4DB2BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9D8B1B7-B4CB-473D-9C6C-3B2F3C4DB2BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9D8B1B7-B4CB-473D-9C6C-3B2F3C4DB2BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9D8B1B7-B4CB-473D-9C6C-3B2F3C4DB2BD}.Release|Any CPU.Build.0 = Release|Any CPU + {ABBF5A82-4F5E-41DA-A727-C2298E8AF650}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABBF5A82-4F5E-41DA-A727-C2298E8AF650}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABBF5A82-4F5E-41DA-A727-C2298E8AF650}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABBF5A82-4F5E-41DA-A727-C2298E8AF650}.Release|Any CPU.Build.0 = Release|Any CPU + {160586BB-3601-4D0C-86D7-414F585041B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {160586BB-3601-4D0C-86D7-414F585041B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {160586BB-3601-4D0C-86D7-414F585041B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {160586BB-3601-4D0C-86D7-414F585041B4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BE4487ED-D36A-438B-B1EF-BC3CA409D4BF} + EndGlobalSection +EndGlobal diff --git a/dotnet/core/2.0/IEC61850.NET.core.2.0/IEC61850.NET.core.2.0.csproj b/dotnet/core/2.0/IEC61850.NET.core.2.0/IEC61850.NET.core.2.0.csproj new file mode 100644 index 000000000..7e30a5439 --- /dev/null +++ b/dotnet/core/2.0/IEC61850.NET.core.2.0/IEC61850.NET.core.2.0.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp2.0 + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example1/client_example1.csproj b/dotnet/core/2.0/client_example1/client_example1.csproj new file mode 100644 index 000000000..1226b7915 --- /dev/null +++ b/dotnet/core/2.0/client_example1/client_example1.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example2/client_example2.csproj b/dotnet/core/2.0/client_example2/client_example2.csproj new file mode 100644 index 000000000..61db2c7b4 --- /dev/null +++ b/dotnet/core/2.0/client_example2/client_example2.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example3/client_example3.csproj b/dotnet/core/2.0/client_example3/client_example3.csproj new file mode 100644 index 000000000..bf9d703c4 --- /dev/null +++ b/dotnet/core/2.0/client_example3/client_example3.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_authenticate/client_example_authenticate.csproj b/dotnet/core/2.0/client_example_authenticate/client_example_authenticate.csproj new file mode 100644 index 000000000..c964a84bb --- /dev/null +++ b/dotnet/core/2.0/client_example_authenticate/client_example_authenticate.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_control/client_example_control.csproj b/dotnet/core/2.0/client_example_control/client_example_control.csproj new file mode 100644 index 000000000..8ce176f23 --- /dev/null +++ b/dotnet/core/2.0/client_example_control/client_example_control.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_datasets/client_example_datasets.csproj b/dotnet/core/2.0/client_example_datasets/client_example_datasets.csproj new file mode 100644 index 000000000..f09936c09 --- /dev/null +++ b/dotnet/core/2.0/client_example_datasets/client_example_datasets.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_files/client_example_files.csproj b/dotnet/core/2.0/client_example_files/client_example_files.csproj new file mode 100644 index 000000000..b50d8be89 --- /dev/null +++ b/dotnet/core/2.0/client_example_files/client_example_files.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_log/client_example_log.csproj b/dotnet/core/2.0/client_example_log/client_example_log.csproj new file mode 100644 index 000000000..db171ba0c --- /dev/null +++ b/dotnet/core/2.0/client_example_log/client_example_log.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_model_browsing/client_example_model_browsing.csproj b/dotnet/core/2.0/client_example_model_browsing/client_example_model_browsing.csproj new file mode 100644 index 000000000..04d01c322 --- /dev/null +++ b/dotnet/core/2.0/client_example_model_browsing/client_example_model_browsing.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_reporting/client_example_reporting.csproj b/dotnet/core/2.0/client_example_reporting/client_example_reporting.csproj new file mode 100644 index 000000000..e61df4ce0 --- /dev/null +++ b/dotnet/core/2.0/client_example_reporting/client_example_reporting.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/client_example_tls/client_example_tls.csproj b/dotnet/core/2.0/client_example_tls/client_example_tls.csproj new file mode 100644 index 000000000..c230b6a17 --- /dev/null +++ b/dotnet/core/2.0/client_example_tls/client_example_tls.csproj @@ -0,0 +1,28 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + diff --git a/dotnet/core/2.0/goose_subscriber_example/goose_subscriber_example.csproj b/dotnet/core/2.0/goose_subscriber_example/goose_subscriber_example.csproj new file mode 100644 index 000000000..bf8ca7c9b --- /dev/null +++ b/dotnet/core/2.0/goose_subscriber_example/goose_subscriber_example.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/core/2.0/server_example1/server_example1.csproj b/dotnet/core/2.0/server_example1/server_example1.csproj new file mode 100644 index 000000000..d7e90c277 --- /dev/null +++ b/dotnet/core/2.0/server_example1/server_example1.csproj @@ -0,0 +1,22 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + PreserveNewest + + + + + + + + diff --git a/dotnet/core/2.0/sv_subscriber_example/sv_subscriber_example.csproj b/dotnet/core/2.0/sv_subscriber_example/sv_subscriber_example.csproj new file mode 100644 index 000000000..817353b65 --- /dev/null +++ b/dotnet/core/2.0/sv_subscriber_example/sv_subscriber_example.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + diff --git a/dotnet/files/FileServicesExample.cs b/dotnet/files/FileServicesExample.cs index 6aed827ac..f77743fc5 100644 --- a/dotnet/files/FileServicesExample.cs +++ b/dotnet/files/FileServicesExample.cs @@ -7,6 +7,10 @@ namespace files { + /// + /// This example connects to an IEC 61850 device, list the available files, and then + /// tries to read the file "IEDSERVER.BIN" from the server. + /// class MainClass { public static void printFiles (IedConnection con, string prefix, string parent) @@ -14,11 +18,11 @@ public static void printFiles (IedConnection con, string prefix, string parent) List files = con.GetFileDirectory (parent); foreach (FileDirectoryEntry file in files) { - Console.WriteLine(prefix + file.GetFileName() + "\t" + file.GetFileSize() + "\t" + - MmsValue.MsTimeToDateTimeOffset(file.GetLastModified())); + Console.WriteLine (prefix + file.GetFileName () + "\t" + file.GetFileSize () + "\t" + + MmsValue.MsTimeToDateTimeOffset (file.GetLastModified ())); - if (file.GetFileName().EndsWith("/")) { - printFiles (con, prefix + " ", parent + file.GetFileName()); + if (file.GetFileName ().EndsWith ("/")) { + printFiles (con, prefix + " ", parent + file.GetFileName ()); } } @@ -26,64 +30,60 @@ public static void printFiles (IedConnection con, string prefix, string parent) static bool getFileHandler (object parameter, byte[] data) { - Console.WriteLine("received " + data.Length + " bytes"); + Console.WriteLine ("received " + data.Length + " bytes"); - BinaryWriter binWriter = (BinaryWriter) parameter; + BinaryWriter binWriter = (BinaryWriter)parameter; - binWriter.Write(data); + binWriter.Write (data); return true; } - public static void Main (string[] args) { IedConnection con = new IedConnection (); - string hostname; + string hostname; - if (args.Length > 0) - hostname = args[0]; - else - hostname = "10.0.2.2"; + if (args.Length > 0) + hostname = args [0]; + else + hostname = "10.0.2.2"; - Console.WriteLine("Connect to " + hostname); + Console.WriteLine ("Connect to " + hostname); - try - { - con.Connect(hostname, 102); + try { + con.Connect (hostname, 102); Console.WriteLine ("Files in server root directory:"); - List serverDirectory = con.GetServerDirectory(true); + List serverDirectory = con.GetServerDirectory (true); foreach (string entry in serverDirectory) { - Console.WriteLine(entry); + Console.WriteLine (entry); } - Console.WriteLine(); + Console.WriteLine (); Console.WriteLine ("File directory tree at server:"); - printFiles(con, "", ""); - Console.WriteLine(); + printFiles (con, "", ""); + Console.WriteLine (); string filename = "IEDSERVER.BIN"; - Console.WriteLine("Download file " + filename); + Console.WriteLine ("Download file " + filename); /* Download file from server and write it to a new local file */ - FileStream fs = new FileStream(filename, FileMode.Create); - BinaryWriter w = new BinaryWriter(fs); + FileStream fs = new FileStream (filename, FileMode.Create); + BinaryWriter w = new BinaryWriter (fs); - con.GetFile(filename, new IedConnection.GetFileHandler(getFileHandler), w); + con.GetFile (filename, new IedConnection.GetFileHandler (getFileHandler), w); - fs.Close(); + fs.Close (); - con.Abort(); - } - catch (IedConnectionException e) - { - Console.WriteLine(e.Message); - } + con.Abort (); + } catch (IedConnectionException e) { + Console.WriteLine (e.Message); + } // release all resources - do NOT use the object after this call!! con.Dispose (); diff --git a/dotnet/goose_subscriber/Program.cs b/dotnet/goose_subscriber/Program.cs new file mode 100644 index 000000000..df84f0327 --- /dev/null +++ b/dotnet/goose_subscriber/Program.cs @@ -0,0 +1,68 @@ +using System; + +using IEC61850.GOOSE.Subscriber; +using System.Threading; +using IEC61850.Common; + +namespace goose_subscriber +{ + class MainClass + { + private static void gooseListener (GooseSubscriber subscriber, object parameter) + { + Console.WriteLine ("Received GOOSE message:\n-------------------------"); + + Console.WriteLine (" stNum: " + subscriber.GetStNum ()); + + Console.WriteLine (" sqNum: " + subscriber.GetSqNum ()); + + + MmsValue values = subscriber.GetDataSetValues (); + + Console.WriteLine (" values: " +values.Size ().ToString ()); + + foreach (MmsValue value in values) { + Console.WriteLine (" value: " + value.ToString ()); + } + } + + public static void Main (string[] args) + { + Console.WriteLine ("Starting GOOSE subscriber..."); + + GooseReceiver receiver = new GooseReceiver (); + + receiver.SetInterfaceId ("eth0"); + + GooseSubscriber subscriber = new GooseSubscriber ("simpleIOGenericIO/LLN0$GO$gcbAnalogValues"); + + subscriber.SetAppId(1000); + + subscriber.SetListener (gooseListener, null); + + receiver.AddSubscriber (subscriber); + + receiver.Start (); + + if (receiver.IsRunning ()) { + + bool running = true; + + /* run until Ctrl-C is pressed */ + Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; + running = false; + }; + + while (running) { + Thread.Sleep (100); + } + + receiver.Stop (); + } else + Console.WriteLine ("Failed to start GOOSE receiver. Running as root?"); + + receiver.Dispose (); + } + } +} diff --git a/dotnet/goose_subscriber/Properties/AssemblyInfo.cs b/dotnet/goose_subscriber/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d42b17b9e --- /dev/null +++ b/dotnet/goose_subscriber/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("goose_subscriber")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("mzillgit")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/dotnet/goose_subscriber/goose_subscriber.csproj b/dotnet/goose_subscriber/goose_subscriber.csproj new file mode 100644 index 000000000..077b65dfb --- /dev/null +++ b/dotnet/goose_subscriber/goose_subscriber.csproj @@ -0,0 +1,44 @@ + + + + Debug + AnyCPU + {1285372C-2E62-494A-A661-8D5D3873318C} + Exe + goose_subscriber + goose_subscriber + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + bin\Release + prompt + 4 + true + + + + + + + + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/reporting/ReportingExample.cs b/dotnet/reporting/ReportingExample.cs index 454f4376a..4edb29b89 100644 --- a/dotnet/reporting/ReportingExample.cs +++ b/dotnet/reporting/ReportingExample.cs @@ -1,14 +1,12 @@ using System; -using System.Collections.Generic; using System.Threading; using IEC61850.Client; using IEC61850.Common; -using System.Runtime.Remoting.Metadata.W3cXsd2001; namespace reporting { - class ReportingExample + class ReportingExample { private static void reportHandler (Report report, object parameter) @@ -25,9 +23,7 @@ private static void reportHandler (Report report, object parameter) byte[] entryId = report.GetEntryId (); if (entryId != null) { - SoapHexBinary shb = new SoapHexBinary(entryId); - - Console.WriteLine (" entryID: " + shb.ToString ()); + Console.WriteLine (" entryID: " + BitConverter.ToString(entryId)); } if (report.HasDataSetName ()) { diff --git a/dotnet/server1/Program.cs b/dotnet/server1/Program.cs index 036a4a8b6..62c7c5e36 100644 --- a/dotnet/server1/Program.cs +++ b/dotnet/server1/Program.cs @@ -17,7 +17,7 @@ public static void Main (string[] args) running = false; }; - IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("./model.cfg"); + IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("model.cfg"); if (iedModel == null) { Console.WriteLine ("No valid data model found!"); @@ -26,7 +26,10 @@ public static void Main (string[] args) DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.SPCSO1"); - IedServer iedServer = new IedServer (iedModel); + IedServerConfig config = new IedServerConfig (); + config.ReportBufferSize = 100000; + + IedServer iedServer = new IedServer (iedModel, config); iedServer.SetControlHandler (spcso1, delegate(DataObject controlObject, object parameter, MmsValue ctlVal, bool test) { bool val = ctlVal.GetBoolean(); diff --git a/dotnet/server1/server1.csproj b/dotnet/server1/server1.csproj index fcf848f63..62c43c041 100644 --- a/dotnet/server1/server1.csproj +++ b/dotnet/server1/server1.csproj @@ -60,4 +60,9 @@ IEC61850.NET + + + PreserveNewest + + \ No newline at end of file diff --git a/dotnet/sv_subscriber/Program.cs b/dotnet/sv_subscriber/Program.cs new file mode 100644 index 000000000..17426e6f1 --- /dev/null +++ b/dotnet/sv_subscriber/Program.cs @@ -0,0 +1,74 @@ +using System; + +using IEC61850.SV.Subscriber; +using IEC61850.Common; +using System.Threading; + +namespace sv_subscriber +{ + class MainClass + { + private static void svUpdateListener(SVSubscriber subscriber, object parameter, SVSubscriberASDU asdu) + { + Console.WriteLine ("RECV ASDU:"); + + string svID = asdu.GetSvId (); + + if (svID != null) + Console.WriteLine (" svID=" + svID); + + Console.WriteLine (" smpCnt: " + asdu.GetSmpCnt ()); + Console.WriteLine (" confRev: " + asdu.GetConfRev ()); + + if (asdu.GetDataSize () >= 8) { + Console.WriteLine (" DATA[0]: " + asdu.GetFLOAT32(0)); + Console.WriteLine (" DATA[1]: " + asdu.GetFLOAT32 (4)); + } + + if (asdu.GetDataSize () >= 16) { + Console.WriteLine (" DATA[2]: " + asdu.GetTimestamp (8).AsDateTime().ToString()); + } + + } + + public static void Main (string[] args) + { + Console.WriteLine ("Starting SV subscriber"); + + SVReceiver receiver = new SVReceiver (); + + if (args.Length > 0) { + receiver.SetInterfaceId (args [0]); + } + + SVSubscriber subscriber = new SVSubscriber (null, 0x4000); + + subscriber.SetListener (svUpdateListener, null); + + receiver.AddSubscriber (subscriber); + + receiver.Start (); + + if (receiver.IsRunning ()) { + + bool running = true; + + /* run until Ctrl-C is pressed */ + Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; + running = false; + }; + + while (running) { + Thread.Sleep (100); + } + + receiver.Stop (); + } else + Console.WriteLine ("Failed to start SV receiver. Running as root?"); + + receiver.Dispose (); + + } + } +} diff --git a/dotnet/sv_subscriber/Properties/AssemblyInfo.cs b/dotnet/sv_subscriber/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ae4b1a2a4 --- /dev/null +++ b/dotnet/sv_subscriber/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("sv_subscriber")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("mzillgit")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/dotnet/sv_subscriber/sv_subscriber.csproj b/dotnet/sv_subscriber/sv_subscriber.csproj new file mode 100644 index 000000000..dc8f53fed --- /dev/null +++ b/dotnet/sv_subscriber/sv_subscriber.csproj @@ -0,0 +1,44 @@ + + + + Debug + AnyCPU + {44651D2D-3252-4FD5-8B8B-5552DBE1B499} + Exe + sv_subscriber + sv_subscriber + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + bin\Release + prompt + 4 + true + + + + + + + + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/tests/Test.cs b/dotnet/tests/Test.cs index 8ec403eb1..d0bb0e40f 100644 --- a/dotnet/tests/Test.cs +++ b/dotnet/tests/Test.cs @@ -105,6 +105,50 @@ public void MmsValueDouble() Assert.AreEqual (val.ToFloat (), (float)0.1234); } + [Test()] + public void MmsValueArray() + { + MmsValue val = MmsValue.NewEmptyArray (3); + + val.SetElement (0, new MmsValue (1)); + val.SetElement (1, new MmsValue (2)); + val.SetElement (2, new MmsValue (3)); + + Assert.AreEqual (val.GetType (), MmsType.MMS_ARRAY); + Assert.AreEqual (val.Size (), 3); + + MmsValue elem0 = val.GetElement (0); + + Assert.AreEqual (elem0.GetType (), MmsType.MMS_INTEGER); + Assert.AreEqual (elem0.ToInt32 (), 1); + + MmsValue elem2 = val.GetElement (2); + + Assert.AreEqual (elem2.GetType (), MmsType.MMS_INTEGER); + Assert.AreEqual (elem2.ToInt32 (), 3); + } + + [Test()] + public void MmsValueStructure() + { + MmsValue val = MmsValue.NewEmptyStructure (2); + + val.SetElement (0, new MmsValue(true)); + val.SetElement (1, MmsValue.NewBitString (10)); + + Assert.AreEqual (val.GetType (), MmsType.MMS_STRUCTURE); + Assert.AreEqual (val.Size (), 2); + + MmsValue elem0 = val.GetElement (0); + + Assert.AreEqual (elem0.GetType (), MmsType.MMS_BOOLEAN); + Assert.AreEqual (elem0.GetBoolean(), true); + + MmsValue elem1 = val.GetElement (1); + + Assert.AreEqual (elem1.GetType (), MmsType.MMS_BIT_STRING); + } + [Test ()] public void Timestamps() { @@ -454,6 +498,33 @@ public void ConnectionHandler() iedServer.Stop (); } + + [Test()] + public void Quality() + { + Quality q = new Quality (); + + Assert.AreEqual (false, q.Overflow); + + q.Overflow = true; + + Assert.AreEqual (true, q.Overflow); + + q.Overflow = false; + + Assert.AreEqual (false, q.Overflow); + + Assert.AreEqual (Validity.GOOD, q.Validity); + + q.Substituted = true; + + Assert.AreEqual (true, q.Substituted); + Assert.AreEqual (false, q.Overflow); + + q.Validity = Validity.QUESTIONABLE; + + Assert.AreEqual (Validity.QUESTIONABLE, q.Validity); + } } } diff --git a/dotnet/tls_server_example/Program.cs b/dotnet/tls_server_example/Program.cs new file mode 100644 index 000000000..e7aa0cfce --- /dev/null +++ b/dotnet/tls_server_example/Program.cs @@ -0,0 +1,85 @@ +using System; +using System.Threading; +using System.Security.Cryptography.X509Certificates; + +using IEC61850.Server; +using IEC61850.Common; +using IEC61850.TLS; + +namespace tls_server_example +{ + class MainClass + { + public static void Main (string[] args) + { + bool running = true; + + /* run until Ctrl-C is pressed */ + Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; + running = false; + }; + + IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("model.cfg"); + + if (iedModel == null) { + Console.WriteLine ("No valid data model found!"); + return; + } + + DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.SPCSO1"); + + TLSConfiguration tlsConfig = new TLSConfiguration (); + + tlsConfig.SetOwnCertificate (new X509Certificate2 ("server.cer")); + + tlsConfig.SetOwnKey ("server-key.pem", null); + + // Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false + tlsConfig.AddCACertificate (new X509Certificate2 ("root.cer")); + + // Check if the certificate is signed by a provided CA + tlsConfig.ChainValidation = true; + + // Check that the shown server certificate is in the list of allowed certificates + tlsConfig.AllowOnlyKnownCertificates = false; + + IedServer iedServer = new IedServer (iedModel, tlsConfig); + + iedServer.SetControlHandler (spcso1, delegate(DataObject controlObject, object parameter, MmsValue ctlVal, bool test) { + bool val = ctlVal.GetBoolean(); + + if (val) + Console.WriteLine("received binary control command: on"); + else + Console.WriteLine("received binary control command: off"); + + return ControlHandlerResult.OK; + }, null); + + iedServer.Start (); + Console.WriteLine ("Server started"); + + GC.Collect (); + + DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.AnIn1"); + + DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild ("mag.f"); + DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild ("t"); + + float floatVal = 1.0f; + + while (running) { + floatVal += 1f; + iedServer.UpdateTimestampAttributeValue (ggio1AnIn1T, new Timestamp (DateTime.Now)); + iedServer.UpdateFloatAttributeValue (ggio1AnIn1magF, floatVal); + Thread.Sleep (100); + } + + iedServer.Stop (); + Console.WriteLine ("Server stopped"); + + iedServer.Destroy (); + } + } +} diff --git a/dotnet/tls_server_example/Properties/AssemblyInfo.cs b/dotnet/tls_server_example/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..638df6352 --- /dev/null +++ b/dotnet/tls_server_example/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("tls_server_example")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("mzillgit")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/dotnet/tls_server_example/model.cfg b/dotnet/tls_server_example/model.cfg new file mode 100644 index 000000000..6f332425a --- /dev/null +++ b/dotnet/tls_server_example/model.cfg @@ -0,0 +1,237 @@ +MODEL(simpleIO){ +LD(GenericIO){ +LN(LLN0){ +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Health 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(NamPlt 0){ +DA(vendor 0 20 5 0 0); +DA(swRev 0 20 5 0 0); +DA(d 0 20 5 0 0); +DA(configRev 0 20 5 0 0); +DA(ldNs 0 20 11 0 0); +} +DS(Events){ +DE(GGIO1$ST$SPCSO1$stVal); +DE(GGIO1$ST$SPCSO2$stVal); +DE(GGIO1$ST$SPCSO3$stVal); +DE(GGIO1$ST$SPCSO4$stVal); +} +DS(AnalogValues){ +DE(GGIO1$MX$AnIn1); +DE(GGIO1$MX$AnIn2); +DE(GGIO1$MX$AnIn3); +DE(GGIO1$MX$AnIn4); +} +RC(EventsRCB01 Events 0 Events 1 24 111 50 1000); +RC(AnalogValuesRCB01 AnalogValues 0 AnalogValues 1 24 111 50 1000); +LC(EventLog Events GenericIO/LLN0$EventLog 19 0 0 1); +LC(GeneralLog - - 19 0 0 1); +LOG(GeneralLog); +LOG(EventLog); +GC(gcbEvents events Events 2 0 -1 -1 ){ +PA(4 273 4096 010ccd010001); +} +GC(gcbAnalogValues analog AnalogValues 2 0 -1 -1 ){ +PA(4 273 4096 010ccd010001); +} +} +LN(LPHD1){ +DO(PhyNam 0){ +DA(vendor 0 20 5 0 0); +} +DO(PhyHealth 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Proxy 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +} +LN(GGIO1){ +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Health 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(NamPlt 0){ +DA(vendor 0 20 5 0 0); +DA(swRev 0 20 5 0 0); +DA(d 0 20 5 0 0); +} +DO(AnIn1 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(AnIn2 0){ +DA(mag 0 27 1 1 101){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 102); +} +DO(AnIn3 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(AnIn4 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(SPCSO1 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO2 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO3 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO4 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(Ind1 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind2 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind3 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind4 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +} +LN(PDUP1){ +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Str 0){ +DA(general 0 0 0 1 0); +DA(dirGeneral 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Op 0){ +DA(general 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(OpDlTmms 0){ +DA(setVal 0 3 2 1 0); +} +DO(RsDlTmms 0){ +DA(setVal 0 3 2 1 0); +} +} +} +} diff --git a/dotnet/tls_server_example/root.cer b/dotnet/tls_server_example/root.cer new file mode 100644 index 000000000..876834449 Binary files /dev/null and b/dotnet/tls_server_example/root.cer differ diff --git a/dotnet/tls_server_example/server-key.pem b/dotnet/tls_server_example/server-key.pem new file mode 100644 index 000000000..6fe35295d --- /dev/null +++ b/dotnet/tls_server_example/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAu3Fjxb904UdyV/dDfvs8SFi6zJeNoMYjmpXm/LBcFSH3zGoK +wdcMovrUTED3Cc6Ww84AYpJ5MRMPTct7DfKJkWfSnkzOPmLldTSTv3RvzGVb4NzK +QqA5aSVDqAzPiP5RnFT6Q4KWRe69TMFxpw7zMXCJx9jDggqN1oojGGkmSgYGXnFd +Nc20Mujejh5pihgwnN4Y/bPFyxJwvIMj+D8qr9klhEmXKPTiX9UFd8oLkn9JCB6+ +SiHhNyFIo+Llossn1Q2hxCGty36fAhEWzpfBTaY510VLjie9y4q9GPTwxITDqSQd +xcX8IuvrbxX0DyEK507SMmTJmB9448eF9ZCWFQIDAQABAoIBAC80BuQtqslwrKLq +adz4d93gOmp7X/c07pJnXZwU7ZuEylp3+e2Gsm/4qq3pTkzx8ZWtsvsf19U774av +z3VbtrkfZDLpNKcRUKeLbgmw0NawT8r4zxaoMsz/zWHsl/bv1K2B2ORXZnCGBrXl +oTFo2mWA6bGiLNn6vm1grCXhlPreywyG/kFK3pi2VvkpvG3XZSI7mmZ0Dq/MD3nO +03oOZBqwwnMObfQQdhKE7646/+KgeuF/JsXaUH4bkHmtzYWyocWYMqpC0hjpNWlQ +cKuQ7t1kfmpsGD9aNW4+ND2ok9BdxIiC+rPXS9NDqZxoWLp+a8seU++uqk1l8RPq +tPE3LqECgYEAz1NmemNLiUsKvyemUvjp8+dJupzWtdV7fsnCbYhj/5gDA2UhFKCf +dP9xiHCdNe0797oAqHY7c3JhS4ug8haDy9aDIu5GG2DNYzjX/oYm4ywbCdRx+uEN +RcTw69FjSYVGkObmxWYszwsFybRasV6PYamg65qYR3FlvW2Td4Fndy8CgYEA53L/ +zHtBRQiNGJU9jfMHeX0bTtXIAt622Qn78jw0it/rhXWi2RwG2Cw5Q2aPRJ6uMt9F +yk1+GAPZcwYqwjq/nKRrl71Tn+KDWIk5rz1fNYRkaXtnMLs2MOogqoDTBshW0QBq +tnPrFNsaLKX6V92Az69wHjd2uwvLQLTvS/EuNfsCgYEAr3to/uhytAd3VirKRep3 +o0E+D5zWw1upxrwhPDK4aUuSKVp8sIfvz8iyoQiomE9vdZPTIMPKOEI1BgtuM9pI +vcyYfIVvg5bg4T3o3H9SBPB9BknyG6ZHZKl4PjGht0X+X4GBDM4Z2Tj8Mijcpsph +1AkOsrzMbZQWyEoqCnnWSHMCgYAFEHUcak4BTrCXqxxPsNOnCt/AF9lqhqkFkrxa +joqvxzqGDw7jJUPZEw6ltObJn5c8Mbp7NLrfl6X4aFgjK9npeYeJKHFd/DzXgRks +BnHA4Aa6cCLP5CjJZTYVxP/ZFCUiKZosJ9kq+ahW9cLGjWg2IyaW4qvMZ/OolMzv +onVaZQKBgQCir8u1vDsyA4JQXMytPHBJe27XaLRGULvteNydVB59Vt21a99o5gt1 +5B9gwWArZdZby3/KZiliNmzp8lMCrLJYjTL5WK6dbWdq92X5hCOofKPIjEcgHjhk +mvnAos3HeC83bJQtADXhw9jR7Vr6GJLM9HDcIgeIMzX7+BuqlMgaHA== +-----END RSA PRIVATE KEY----- diff --git a/dotnet/tls_server_example/server.cer b/dotnet/tls_server_example/server.cer new file mode 100644 index 000000000..957bdc30f Binary files /dev/null and b/dotnet/tls_server_example/server.cer differ diff --git a/dotnet/tls_server_example/tls_server_example.csproj b/dotnet/tls_server_example/tls_server_example.csproj new file mode 100644 index 000000000..012dd7601 --- /dev/null +++ b/dotnet/tls_server_example/tls_server_example.csproj @@ -0,0 +1,58 @@ + + + + Debug + AnyCPU + {B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307} + Exe + tls_server_example + tls_server_example + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + bin\Release + prompt + 4 + true + + + + + + + + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850.NET + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2d802c9b1..fafc146b6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -11,18 +11,17 @@ add_subdirectory(server_example_61400_25) add_subdirectory(server_example_setting_groups) add_subdirectory(server_example_logging) add_subdirectory(server_example_files) +add_subdirectory(server_example_substitution) add_subdirectory(iec61850_client_example1) add_subdirectory(iec61850_client_example2) -add_subdirectory(iec61850_client_example3) +add_subdirectory(iec61850_client_example_control) add_subdirectory(iec61850_client_example4) add_subdirectory(iec61850_client_example5) add_subdirectory(iec61850_client_example_reporting) add_subdirectory(iec61850_client_example_log) - -if(NOT WIN32) - add_subdirectory(iec61850_client_example_files) -endif() +add_subdirectory(iec61850_client_example_array) +add_subdirectory(iec61850_client_example_files) if(WIN32) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib") diff --git a/examples/Makefile b/examples/Makefile index 543cb03b3..a6d6d5d92 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,11 +1,11 @@ EXAMPLE_DIRS = iec61850_client_example1 EXAMPLE_DIRS += iec61850_client_example2 -EXAMPLE_DIRS += iec61850_client_example3 EXAMPLE_DIRS += iec61850_client_example4 EXAMPLE_DIRS += iec61850_client_example5 EXAMPLE_DIRS += iec61850_client_example_reporting -EXAMPLE_DIRS += iec61850_client_example_log +EXAMPLE_DIRS += iec61850_client_example_array +EXAMPLE_DIRS += iec61850_client_example_control EXAMPLE_DIRS += server_example_simple EXAMPLE_DIRS += server_example_basic_io EXAMPLE_DIRS += server_example_password_auth @@ -18,8 +18,8 @@ EXAMPLE_DIRS += server_example_complex_array 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 += server_example_substitution EXAMPLE_DIRS += goose_subscriber EXAMPLE_DIRS += goose_publisher EXAMPLE_DIRS += sv_subscriber diff --git a/examples/goose_publisher/goose_publisher_example.c b/examples/goose_publisher/goose_publisher_example.c index 916be7006..f46b3fe77 100644 --- a/examples/goose_publisher/goose_publisher_example.c +++ b/examples/goose_publisher/goose_publisher_example.c @@ -16,6 +16,14 @@ int main(int argc, char** argv) { + char* interface; + + if (argc > 1) + interface = argv[1]; + else + interface = "eth0"; + + printf("Using interface %s\n", interface); LinkedList dataSetValues = LinkedList_create(); @@ -41,7 +49,7 @@ main(int argc, char** argv) * is NULL the interface name as defined with CONFIG_ETHERNET_INTERFACE_ID in * stack_config.h is used. */ - GoosePublisher publisher = GoosePublisher_create(&gooseCommParameters, NULL); + GoosePublisher publisher = GoosePublisher_create(&gooseCommParameters, interface); GoosePublisher_setGoCbRef(publisher, "simpleIOGenericIO/LLN0$GO$gcbAnalogValues"); GoosePublisher_setConfRev(publisher, 1); @@ -58,6 +66,8 @@ main(int argc, char** argv) } GoosePublisher_destroy(publisher); + + LinkedList_destroyDeep(dataSetValues, (LinkedListValueDeleteFunction) MmsValue_delete); } diff --git a/examples/iec61850_9_2_LE_example/Makefile b/examples/iec61850_9_2_LE_example/Makefile index 0e3703ace..2ce35b732 100644 --- a/examples/iec61850_9_2_LE_example/Makefile +++ b/examples/iec61850_9_2_LE_example/Makefile @@ -9,6 +9,8 @@ PROJECT_ICD_FILE = sv.icd include $(LIBIEC_HOME)/make/target_system.mk include $(LIBIEC_HOME)/make/stack_includes.mk +LDFLAGS += -lm + all: $(PROJECT_BINARY_NAME) include $(LIBIEC_HOME)/make/common_targets.mk 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 603e80e46..d63554d86 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 @@ -27,7 +27,7 @@ #include #include #include - +#include /* Include the generated header with the model access handles */ #include "static_model.h" @@ -48,11 +48,21 @@ static int amp2; static int amp3; static int amp4; +static int amp1q; +static int amp2q; +static int amp3q; +static int amp4q; + static int vol1; static int vol2; static int vol3; static int vol4; +static int vol1q; +static int vol2q; +static int vol3q; +static int vol4q; + static SVPublisher svPublisher; static SVPublisher_ASDU asdu; @@ -64,14 +74,24 @@ setupSVPublisher(const char* svInterface) asdu = SVPublisher_addASDU(svPublisher, "xxxxMUnn01", NULL, 1); amp1 = SVPublisher_ASDU_addINT32(asdu); + amp1q = SVPublisher_ASDU_addQuality(asdu); amp2 = SVPublisher_ASDU_addINT32(asdu); + amp2q = SVPublisher_ASDU_addQuality(asdu); amp3 = SVPublisher_ASDU_addINT32(asdu); + amp3q = SVPublisher_ASDU_addQuality(asdu); amp4 = SVPublisher_ASDU_addINT32(asdu); + amp4q = SVPublisher_ASDU_addQuality(asdu); vol1 = SVPublisher_ASDU_addINT32(asdu); + vol1q = SVPublisher_ASDU_addQuality(asdu); vol2 = SVPublisher_ASDU_addINT32(asdu); + vol2q = SVPublisher_ASDU_addQuality(asdu); vol3 = SVPublisher_ASDU_addINT32(asdu); + vol3q = SVPublisher_ASDU_addQuality(asdu); vol4 = SVPublisher_ASDU_addINT32(asdu); + vol4q = SVPublisher_ASDU_addQuality(asdu); + + SVPublisher_ASDU_setSmpCntWrap(asdu, 4000); SVPublisher_setupComplete(svPublisher); } @@ -111,9 +131,6 @@ main(int argc, char** argv) setupSVPublisher(svInterface); - int voltage = 1; - int current = 1; - SVControlBlock* svcb = IedModel_getSVControlBlock(&iedModel, IEDMODEL_MUnn_LLN0, "MSVCB01"); if (svcb == NULL) { @@ -123,45 +140,104 @@ main(int argc, char** argv) IedServer_setSVCBHandler(iedServer, svcb, sVCBEventHandler, NULL); + Quality q = QUALITY_VALIDITY_GOOD; + + int vol = (int) (6350.f * sqrt(2)); + int amp = 0; + float phaseAngle = 0.f; + + int voltageA; + int voltageB; + int voltageC; + int voltageN; + int currentA; + int currentB; + int currentC; + int currentN; + + int sampleCount = 0; + + uint64_t nextCycleStart = Hal_getTimeInMs() + 1000; + while (running) { - uint64_t timeval = Hal_getTimeInMs(); + /* update measurement values */ + int samplePoint = sampleCount % 80; - IedServer_lockDataModel(iedServer); + double angleA = (2 * M_PI / 80) * samplePoint; + double angleB = (2 * M_PI / 80) * samplePoint - ( 2 * M_PI / 3); + double angleC = (2 * M_PI / 80) * samplePoint - ( 4 * M_PI / 3); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR1_Amp_instMag_i, current); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR2_Amp_instMag_i, current); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR3_Amp_instMag_i, current); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR4_Amp_instMag_i, current); + voltageA = (vol * sin(angleA)) * 100; + voltageB = (vol * sin(angleB)) * 100; + voltageC = (vol * sin(angleC)) * 100; + voltageN = voltageA + voltageB + voltageC; - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR1_Vol_instMag_i, voltage); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR2_Vol_instMag_i, voltage); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR3_Vol_instMag_i, voltage); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR4_Vol_instMag_i, voltage); + currentA = (amp * sin(angleA - phaseAngle)) * 1000; + currentB = (amp * sin(angleB - phaseAngle)) * 1000; + currentC = (amp * sin(angleC - phaseAngle)) * 1000; + currentN = currentA + currentB + currentC; - IedServer_unlockDataModel(iedServer); + IedServer_lockDataModel(iedServer); - if (svcbEnabled) { + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR1_Amp_instMag_i, currentA); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TCTR1_Amp_q, q); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR2_Amp_instMag_i, currentB); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TCTR2_Amp_q, q); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR3_Amp_instMag_i, currentC); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TCTR3_Amp_q, q); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TCTR4_Amp_instMag_i, currentN); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TCTR4_Amp_q, q); + + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR1_Vol_instMag_i, voltageA); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TVTR1_Vol_q, q); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR2_Vol_instMag_i, voltageB); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TVTR2_Vol_q, q); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR3_Vol_instMag_i, voltageC); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TVTR3_Vol_q, q); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_MUnn_TVTR4_Vol_instMag_i, voltageN); + IedServer_updateQuality(iedServer, IEDMODEL_MUnn_TVTR4_Vol_q, q); - SVPublisher_ASDU_setINT32(asdu, amp1, current); - SVPublisher_ASDU_setINT32(asdu, amp2, current); - SVPublisher_ASDU_setINT32(asdu, amp3, current); - SVPublisher_ASDU_setINT32(asdu, amp4, current); + IedServer_unlockDataModel(iedServer); - SVPublisher_ASDU_setINT32(asdu, vol1, voltage); - SVPublisher_ASDU_setINT32(asdu, vol2, voltage); - SVPublisher_ASDU_setINT32(asdu, vol3, voltage); - SVPublisher_ASDU_setINT32(asdu, vol4, voltage); + if (svcbEnabled) { - SVPublisher_ASDU_increaseSmpCnt(asdu); + SVPublisher_ASDU_setINT32(asdu, amp1, currentA); + SVPublisher_ASDU_setQuality(asdu, amp1q, q); + SVPublisher_ASDU_setINT32(asdu, amp2, currentB); + SVPublisher_ASDU_setQuality(asdu, amp2q, q); + SVPublisher_ASDU_setINT32(asdu, amp3, currentC); + SVPublisher_ASDU_setQuality(asdu, amp3q, q); + SVPublisher_ASDU_setINT32(asdu, amp4, currentN); + SVPublisher_ASDU_setQuality(asdu, amp4q, q); + + SVPublisher_ASDU_setINT32(asdu, vol1, voltageA); + SVPublisher_ASDU_setQuality(asdu, vol1q, q); + SVPublisher_ASDU_setINT32(asdu, vol2, voltageB); + SVPublisher_ASDU_setQuality(asdu, vol2q, q); + SVPublisher_ASDU_setINT32(asdu, vol3, voltageC); + SVPublisher_ASDU_setQuality(asdu, vol3q, q); + SVPublisher_ASDU_setINT32(asdu, vol4, voltageN); + SVPublisher_ASDU_setQuality(asdu, vol4q, q); + + SVPublisher_ASDU_setSmpCnt(asdu, (uint16_t) sampleCount); SVPublisher_publish(svPublisher); } - voltage++; - current++; + sampleCount = ((sampleCount + 1) % 4000); + + if ((sampleCount % 400) == 0) { + uint64_t timeval = Hal_getTimeInMs(); + + while (timeval < nextCycleStart + 100) { + Thread_sleep(1); - Thread_sleep(500); + timeval = Hal_getTimeInMs(); + } + + nextCycleStart = nextCycleStart + 100; + } } /* stop MMS server - close TCP server socket and all client sockets */ diff --git a/examples/iec61850_9_2_LE_example/static_model.c b/examples/iec61850_9_2_LE_example/static_model.c index f23815c9f..5b3043f53 100644 --- a/examples/iec61850_9_2_LE_example/static_model.c +++ b/examples/iec61850_9_2_LE_example/static_model.c @@ -18,20 +18,11 @@ extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda4; extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda5; extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda6; extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda7; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda8; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda9; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda10; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda11; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda12; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda13; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda14; -extern DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda15; - DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda0 = { "MUnn", false, - "TCTR1$MX$Amp$instMag$i", + "TCTR1$MX$Amp", -1, NULL, NULL, @@ -41,7 +32,7 @@ DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda0 = { DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda1 = { "MUnn", false, - "TCTR1$MX$Amp$q", + "TCTR2$MX$Amp", -1, NULL, NULL, @@ -51,7 +42,7 @@ DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda1 = { DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda2 = { "MUnn", false, - "TCTR2$MX$Amp$instMag$i", + "TCTR3$MX$Amp", -1, NULL, NULL, @@ -61,7 +52,7 @@ DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda2 = { DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda3 = { "MUnn", false, - "TCTR2$MX$Amp$q", + "TCTR4$MX$Amp", -1, NULL, NULL, @@ -71,7 +62,7 @@ DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda3 = { DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda4 = { "MUnn", false, - "TCTR3$MX$Amp$instMag$i", + "TVTR1$MX$Vol", -1, NULL, NULL, @@ -81,7 +72,7 @@ DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda4 = { DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda5 = { "MUnn", false, - "TCTR3$MX$Amp$q", + "TVTR2$MX$Vol", -1, NULL, NULL, @@ -91,7 +82,7 @@ DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda5 = { DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda6 = { "MUnn", false, - "TCTR4$MX$Amp$instMag$i", + "TVTR3$MX$Vol", -1, NULL, NULL, @@ -101,97 +92,17 @@ DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda6 = { DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda7 = { "MUnn", false, - "TCTR4$MX$Amp$q", + "TVTR4$MX$Vol", -1, NULL, NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda8 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda8 = { - "MUnn", - false, - "TVTR1$MX$Vol$instMag$i", - -1, - NULL, - NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda9 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda9 = { - "MUnn", - false, - "TVTR1$MX$Vol$q", - -1, - NULL, - NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda10 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda10 = { - "MUnn", - false, - "TVTR2$MX$Vol$instMag$i", - -1, - NULL, - NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda11 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda11 = { - "MUnn", - false, - "TVTR2$MX$Vol$q", - -1, - NULL, - NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda12 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda12 = { - "MUnn", - false, - "TVTR3$MX$Vol$instMag$i", - -1, - NULL, - NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda13 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda13 = { - "MUnn", - false, - "TVTR3$MX$Vol$q", - -1, - NULL, - NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda14 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda14 = { - "MUnn", - false, - "TVTR4$MX$Vol$instMag$i", - -1, - NULL, - NULL, - &iedModelds_MUnn_LLN0_PhsMeas1_fcda15 -}; - -DataSetEntry iedModelds_MUnn_LLN0_PhsMeas1_fcda15 = { - "MUnn", - false, - "TVTR4$MX$Vol$q", - -1, - NULL, - NULL, - NULL + NULL }; DataSet iedModelds_MUnn_LLN0_PhsMeas1 = { "MUnn", "LLN0$PhsMeas1", - 16, + 8, &iedModelds_MUnn_LLN0_PhsMeas1_fcda0, NULL }; diff --git a/examples/iec61850_9_2_LE_example/sv.icd b/examples/iec61850_9_2_LE_example/sv.icd index 9ef2d1a11..b52f8d92f 100644 --- a/examples/iec61850_9_2_LE_example/sv.icd +++ b/examples/iec61850_9_2_LE_example/sv.icd @@ -74,21 +74,21 @@ SCL.xsd"> + doName="Amp" /> + doName="Amp" /> + doName="Amp" /> + doName="Amp" /> + doName="Vol" /> + doName="Vol" /> + doName="Vol" /> + doName="Vol" /> diff --git a/examples/iec61850_client_example1/client_example1.c b/examples/iec61850_client_example1/client_example1.c index 1142c768c..f5b23575d 100644 --- a/examples/iec61850_client_example1/client_example1.c +++ b/examples/iec61850_client_example1/client_example1.c @@ -1,7 +1,7 @@ /* * client_example1.c * - * This example is intended to be used with server_example3 or server_example_goose. + * This example is intended to be used with server_example_basic_io or server_example_goose. */ #include "iec61850_client.h" @@ -50,8 +50,6 @@ int main(int argc, char** argv) { if (error == IED_ERROR_OK) { - IedConnection_getServerDirectory(con, &error, false); - /* read an analog measurement value from server */ MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX); diff --git a/examples/iec61850_client_example3/CMakeLists.txt b/examples/iec61850_client_example3/CMakeLists.txt deleted file mode 100644 index 37b61ff80..000000000 --- a/examples/iec61850_client_example3/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ - -set(iec61850_client_example3_SRCS - client_example3.c -) - -IF(WIN32) -set_source_files_properties(${iec61850_client_example3_SRCS} - PROPERTIES LANGUAGE CXX) -ENDIF(WIN32) - -add_executable(iec61850_client_example3 - ${iec61850_client_example3_SRCS} -) - -target_link_libraries(iec61850_client_example3 - iec61850 -) diff --git a/examples/iec61850_client_example_array/CMakeLists.txt b/examples/iec61850_client_example_array/CMakeLists.txt new file mode 100644 index 000000000..a4d4b9051 --- /dev/null +++ b/examples/iec61850_client_example_array/CMakeLists.txt @@ -0,0 +1,17 @@ + +set(iec61850_client_example_array_SRCS + client_example_array.c +) + +IF(WIN32) +set_source_files_properties(${iec61850_client_example_array_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(WIN32) + +add_executable(iec61850_client_example_array + ${iec61850_client_example_array_SRCS} +) + +target_link_libraries(iec61850_client_example_array + iec61850 +) diff --git a/examples/iec61850_client_example3/Makefile b/examples/iec61850_client_example_array/Makefile similarity index 82% rename from examples/iec61850_client_example3/Makefile rename to examples/iec61850_client_example_array/Makefile index 138d49700..6e28aaded 100644 --- a/examples/iec61850_client_example3/Makefile +++ b/examples/iec61850_client_example_array/Makefile @@ -1,7 +1,7 @@ LIBIEC_HOME=../.. -PROJECT_BINARY_NAME = client_example3 -PROJECT_SOURCES = client_example3.c +PROJECT_BINARY_NAME = client_example_array +PROJECT_SOURCES = client_example_array.c include $(LIBIEC_HOME)/make/target_system.mk include $(LIBIEC_HOME)/make/stack_includes.mk diff --git a/examples/iec61850_client_example_array/client_example_array.c b/examples/iec61850_client_example_array/client_example_array.c new file mode 100644 index 000000000..ee1d60165 --- /dev/null +++ b/examples/iec61850_client_example_array/client_example_array.c @@ -0,0 +1,99 @@ +/* + * client_example_array.c + * + * SHows how to handle array access from client side + * + * This example is intended to be used with server_example_complex_array + */ + +#include "iec61850_client.h" + +#include +#include + +#include "hal_thread.h" + +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) { + + /* read complete array */ + MmsValue* array = IedConnection_readObject(con, &error, "testComplexArray/MHAI1.HA.phsAHar", IEC61850_FC_MX); + + if (array != NULL) { + MmsValue_delete(array); + } + else + printf("Failed to read array!\n"); + + /* read array element at different component levels */ + char* arrayElementRef = "testComplexArray/MHAI1.HA.phsAHar(2).cVal.mag.f"; + + MmsValue* element = IedConnection_readObject(con, &error, arrayElementRef, IEC61850_FC_MX); + + if (element != NULL) { + MmsValue_delete(element); + } + else + printf("Failed to read array element %s!\n", arrayElementRef); + + + arrayElementRef = "testComplexArray/MHAI1.HA.phsAHar(2).cVal.mag"; + + element = IedConnection_readObject(con, &error, arrayElementRef, IEC61850_FC_MX); + + if (element != NULL) { + MmsValue_delete(element); + } + else + printf("Failed to read array element %s!\n", arrayElementRef); + + arrayElementRef = "testComplexArray/MHAI1.HA.phsAHar(2).cVal"; + + element = IedConnection_readObject(con, &error, arrayElementRef, IEC61850_FC_MX); + + if (element != NULL) { + MmsValue_delete(element); + } + else + printf("Failed to read array element %s!\n", arrayElementRef); + + arrayElementRef = "testComplexArray/MHAI1.HA.phsAHar(2)"; + + element = IedConnection_readObject(con, &error, arrayElementRef, IEC61850_FC_MX); + + if (element != NULL) { + MmsValue_delete(element); + } + else + printf("Failed to read array element %s!\n", arrayElementRef); + + close_connection: + + IedConnection_close(con); + } + else { + printf("Failed to connect to %s:%i\n", hostname, tcpPort); + } + + IedConnection_destroy(con); +} + + diff --git a/examples/iec61850_client_example_control/CMakeLists.txt b/examples/iec61850_client_example_control/CMakeLists.txt new file mode 100644 index 000000000..bb612b44d --- /dev/null +++ b/examples/iec61850_client_example_control/CMakeLists.txt @@ -0,0 +1,17 @@ + +set(iec61850_client_example_control_SRCS + client_example_control.c +) + +IF(WIN32) +set_source_files_properties(${iec61850_client_example_control_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(WIN32) + +add_executable(client_example_control + ${iec61850_client_example_control_SRCS} +) + +target_link_libraries(client_example_control + iec61850 +) diff --git a/examples/iec61850_client_example_control/Makefile b/examples/iec61850_client_example_control/Makefile new file mode 100644 index 000000000..1bb8cd1c8 --- /dev/null +++ b/examples/iec61850_client_example_control/Makefile @@ -0,0 +1,17 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = client_example_control +PROJECT_SOURCES = client_example_control.c + +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 + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + +clean: + rm -f $(PROJECT_BINARY_NAME) diff --git a/examples/iec61850_client_example3/client_example3.c b/examples/iec61850_client_example_control/client_example_control.c similarity index 100% rename from examples/iec61850_client_example3/client_example3.c rename to examples/iec61850_client_example_control/client_example_control.c diff --git a/examples/iec61850_client_example_files/file-tool.c b/examples/iec61850_client_example_files/file-tool.c index 53f23086c..396e8bf9d 100644 --- a/examples/iec61850_client_example_files/file-tool.c +++ b/examples/iec61850_client_example_files/file-tool.c @@ -8,15 +8,73 @@ * * Note: intended to be used with server_example3 or server_example_files * - * Note: DOES NOT WORK WITH VISUAL STUDIO because of libgen.h - * */ #include "iec61850_client.h" #include #include +#ifdef _WIN32 +#include +#else #include +#endif + +#ifdef _WIN32 +static char _dirname[1000]; + +static char* +dirname(char* path) +{ + char* lastSep = NULL; + + int len = strlen(path); + int i = 0; + + while (i < len) { + if (path[i] == '/' || path[i] == ':' || path[i] == '\\') + lastSep = path + i; + + i++; + } + + if (lastSep) { + strcpy(_dirname, path); + _dirname[lastSep - path] = 0; + } + else + strcpy("", path); + + return _dirname; +} + + +static char _basename[1000]; + +static char* +basename(char* path) +{ + char* lastSep = NULL; + + int len = strlen(path); + int i = 0; + + while (i < len) { + if (path[i] == '/' || path[i] == ':' || path[i] == '\\') + lastSep = path + i; + + i++; + } + + if (lastSep) + strcpy(_basename, lastSep + 1); + else + strcpy(_basename, path); + + return _basename; +} + +#endif static char* hostname = "localhost"; static int tcpPort = 102; diff --git a/examples/server_example_basic_io/Makefile b/examples/server_example_basic_io/Makefile index 4e4e59560..f27d9f651 100644 --- a/examples/server_example_basic_io/Makefile +++ b/examples/server_example_basic_io/Makefile @@ -4,7 +4,7 @@ PROJECT_BINARY_NAME = server_example_basic_io PROJECT_SOURCES = server_example_basic_io.c PROJECT_SOURCES += static_model.c -PROJECT_ICD_FILE = simpleIO_direct_control.icd +PROJECT_ICD_FILE = simpleIO_direct_control.cid include $(LIBIEC_HOME)/make/target_system.mk include $(LIBIEC_HOME)/make/stack_includes.mk @@ -22,6 +22,7 @@ model: $(PROJECT_ICD_FILE) $(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + mkdir -p vmd-filestore $(CP) $(PROJECT_BINARY_NAME) vmd-filestore/IEDSERVER.BIN clean: diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c index 30faf6e40..1f9285312 100644 --- a/examples/server_example_basic_io/server_example_basic_io.c +++ b/examples/server_example_basic_io/server_example_basic_io.c @@ -3,6 +3,7 @@ * * - How to use simple control models * - How to serve analog measurement data + * - Using the IedServerConfig object to configure stack features */ #include "iec61850_server.h" @@ -12,7 +13,7 @@ #include #include -#include "../server_example_basic_io/static_model.h" +#include "static_model.h" /* import IEC 61850 device model created from SCL-File */ extern IedModel iedModel; @@ -83,11 +84,34 @@ main(int argc, char** argv) { printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); - iedServer = IedServer_create(&iedModel); + /* Create new server configuration object */ + IedServerConfig config = IedServerConfig_create(); + + /* Set buffer size for buffered report control blocks to 200000 bytes */ + IedServerConfig_setReportBufferSize(config, 200000); + + /* Set stack compliance to a specific edition of the standard (WARNING: data model has also to be checked for compliance) */ + IedServerConfig_setEdition(config, IEC_61850_EDITION_2); /* Set the base path for the MMS file services */ - MmsServer mmsServer = IedServer_getMmsServer(iedServer); - MmsServer_setFilestoreBasepath(mmsServer, "./vmd-filestore/"); + IedServerConfig_setFileServiceBasePath(config, "./vmd-filestore/"); + + /* disable MMS file service */ + IedServerConfig_enableFileService(config, false); + + /* disable dynamic data set service */ + IedServerConfig_enableDynamicDataSetService(config, false); + + IedServerConfig_enableLogService(config, true); + + /* set maximum number of clients */ + IedServerConfig_setMaxMmsConnections(config, 2); + + /* Create a new IEC 61850 server instance */ + iedServer = IedServer_createWithConfig(&iedModel, NULL, config); + + /* configuration object is no longer required */ + IedServerConfig_destroy(config); /* Install handler for operate command */ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, @@ -108,11 +132,11 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); - /* MMS server will be instructed to start listening to client connections. */ + /* MMS server will be instructed to start listening for client connections. */ IedServer_start(iedServer, 102); if (!IedServer_isRunning(iedServer)) { - printf("Starting server failed! Exit.\n"); + printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); IedServer_destroy(iedServer); exit(-1); } @@ -133,8 +157,6 @@ main(int argc, char** argv) float an3 = sinf(t + 2.f); float an4 = sinf(t + 3.f); - IedServer_lockDataModel(iedServer); - Timestamp iecTimestamp; Timestamp_clearFlags(&iecTimestamp); @@ -145,6 +167,8 @@ main(int argc, char** argv) if (((int) t % 2) == 0) Timestamp_setClockNotSynchronized(&iecTimestamp, true); + IedServer_lockDataModel(iedServer); + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp); IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1); diff --git a/examples/server_example_basic_io/simpleIO_sbo_control.icd b/examples/server_example_basic_io/simpleIO_direct_control.cid similarity index 55% rename from examples/server_example_basic_io/simpleIO_sbo_control.icd rename to examples/server_example_basic_io/simpleIO_direct_control.cid index 5f65ff6ab..cb763780c 100644 --- a/examples/server_example_basic_io/simpleIO_sbo_control.icd +++ b/examples/server_example_basic_io/simpleIO_direct_control.cid @@ -8,12 +8,15 @@ 10
-

10.0.0.2

+

0.0.0.0

255.255.255.0

-

10.0.0.1

-

0001

+

192.168.2.1

+

1,3,9999,33

+

33

00000001

0001

+

0001

+

102

@@ -39,73 +42,126 @@ + - + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + on + status-only + + + on + + + + + ok + + + + + + MZ Automation + + + 1.3.0 + + + libiec61850 server example + + - + + + + ok + + + + + on + status-only + + + on + + + + + ok + + - sbo-with-normal-security + direct-with-normal-security - - 30000 - - - operate-once - - sbo-with-normal-security + direct-with-normal-security - - 30000 - - - operate-once - - sbo-with-normal-security + direct-with-normal-security - - 30000 - - - operate-once - - sbo-with-normal-security + direct-with-normal-security - - 30000 - - - operate-once - @@ -113,45 +169,58 @@ + - - - + + + + - + + - - - + + + - - - - + + + + - + + + - - + + + + + + + + + + @@ -159,88 +228,47 @@ + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - @@ -259,6 +287,20 @@ + + on + blocked + test + test/blocked + off + + + + ok + warning + alarm + + status-only direct-with-normal-security @@ -267,11 +309,6 @@ sbo-with-enhanced-security - - operate-once - operate-many - - not-supported bay-control @@ -283,5 +320,6 @@ maintenance process + diff --git a/examples/server_example_basic_io/simpleIO_direct_control.icd b/examples/server_example_basic_io/simpleIO_direct_control.icd index 0a0c63552..258e9ae61 100644 --- a/examples/server_example_basic_io/simpleIO_direct_control.icd +++ b/examples/server_example_basic_io/simpleIO_direct_control.icd @@ -2,23 +2,8 @@
- - - Station bus - 10 - -
-

10.0.0.2

-

255.255.255.0

-

10.0.0.1

-

0001

-

00000001

-

0001

-
-
-
-
- + + @@ -67,13 +52,13 @@ - + - + @@ -82,44 +67,64 @@ - - - - - - - - - - - - - + + + on + status-only + + + on + + + + + ok + + MZ Automation - 0.7.3 + 1.3.0 libiec61850 server example - + + + + ok + + + + + on + status-only + + + on + + + + + ok + + direct-with-normal-security @@ -146,46 +151,58 @@ + - - - + + + + - + + - - - + + + - - - - + + + + - - + + + - - + + + + + + + + + + @@ -193,64 +210,56 @@ + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + @@ -259,6 +268,21 @@ + + + on + blocked + test + test/blocked + off + + + + ok + warning + alarm + + status-only direct-with-normal-security @@ -266,6 +290,7 @@ direct-with-enhanced-security sbo-with-enhanced-security + not-supported bay-control @@ -277,5 +302,6 @@ maintenance process +
diff --git a/examples/server_example_basic_io/static_model.c b/examples/server_example_basic_io/static_model.c index ddc8dd644..63ef37bb4 100644 --- a/examples/server_example_basic_io/static_model.c +++ b/examples/server_example_basic_io/static_model.c @@ -1,9 +1,9 @@ /* * static_model.c * - * automatically generated from simpleIO_direct_control.icd + * automatically generated from simpleIO_direct_control.cid */ -#include "../server_example_basic_io/static_model.h" +#include "static_model.h" static void initializeValues(); @@ -248,7 +248,7 @@ DataAttribute iedModel_GenericIO_LLN0_Mod_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -309,7 +309,7 @@ DataAttribute iedModel_GenericIO_LLN0_Beh_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -357,7 +357,7 @@ DataAttribute iedModel_GenericIO_LLN0_Health_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -509,7 +509,7 @@ DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -601,10 +601,23 @@ DataObject iedModel_GenericIO_GGIO1_Mod = { "Mod", (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, - (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_stVal, 0 }; +DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + DataAttribute iedModel_GenericIO_GGIO1_Mod_q = { DataAttributeModelType, "q", @@ -661,7 +674,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -709,7 +722,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Health_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -1037,10 +1050,62 @@ DataObject iedModel_GenericIO_GGIO1_SPCSO1 = { "SPCSO1", (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, - (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_stVal, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, 0 }; +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat, + 0, + IEC61850_FC_ST, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_stVal, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT8U, + 0, + NULL, + 0}; + DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal = { DataAttributeModelType, "stVal", @@ -1058,7 +1123,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q = { DataAttributeModelType, "q", (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, - (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_t, NULL, 0, IEC61850_FC_ST, @@ -1067,11 +1132,37 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q = { NULL, 0}; +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlModel, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper = { DataAttributeModelType, "Oper", (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, - (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlModel, + NULL, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal, 0, IEC61850_FC_CO, @@ -1184,32 +1275,6 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check = { 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", @@ -1944,10 +2009,10 @@ 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_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 4294967295, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report1}; +ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report2}; +ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report3}; +ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 24, 175, 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}; @@ -1955,15 +2020,7 @@ ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, -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 = { @@ -1974,8 +2031,8 @@ IedModel iedModel = { NULL, NULL, NULL, - &iedModel_GenericIO_LLN0_lcb0, - &iedModel_GenericIO_LLN0_log0, + NULL, + NULL, initializeValues }; @@ -1983,16 +2040,30 @@ static void initializeValues() { +iedModel_GenericIO_LLN0_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + iedModel_GenericIO_LLN0_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); +iedModel_GenericIO_LLN0_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_LLN0_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + 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_swRev.mmsValue = MmsValue_newVisibleString("1.3.0"); iedModel_GenericIO_LLN0_NamPlt_d.mmsValue = MmsValue_newVisibleString("libiec61850 server example"); +iedModel_GenericIO_LPHD1_PhyHealth_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + iedModel_GenericIO_GGIO1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); +iedModel_GenericIO_GGIO1_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + iedModel_GenericIO_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); diff --git a/examples/server_example_basic_io/static_model.h b/examples/server_example_basic_io/static_model.h index b5670e9f3..b6030e514 100644 --- a/examples/server_example_basic_io/static_model.h +++ b/examples/server_example_basic_io/static_model.h @@ -1,7 +1,7 @@ /* * static_model.h * - * automatically generated from simpleIO_direct_control.icd + * automatically generated from simpleIO_direct_control.cid */ #ifndef STATIC_MODEL_H_ @@ -45,6 +45,7 @@ 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_stVal; extern DataAttribute iedModel_GenericIO_GGIO1_Mod_q; extern DataAttribute iedModel_GenericIO_GGIO1_Mod_t; extern DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel; @@ -81,8 +82,14 @@ 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_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin; @@ -92,8 +99,6 @@ 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; @@ -189,6 +194,7 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; #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_stVal (&iedModel_GenericIO_GGIO1_Mod_stVal) #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) @@ -225,8 +231,14 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; #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_origin (&iedModel_GenericIO_GGIO1_SPCSO1_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_ctlNum) #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_t (&iedModel_GenericIO_GGIO1_SPCSO1_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO1_ctlModel) #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) @@ -236,8 +248,6 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; #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) diff --git a/examples/server_example_complex_array/mhai_array.icd b/examples/server_example_complex_array/mhai_array.icd index e6f09be91..4a622f06d 100644 --- a/examples/server_example_complex_array/mhai_array.icd +++ b/examples/server_example_complex_array/mhai_array.icd @@ -1,24 +1,31 @@ -
+ +
+ Station bus 10 - +
-

10.0.0.2

+

0.0.0.0

255.255.255.0

-

10.0.0.1

-

0001

+

192.168.2.1

+

1,3,9999,33

+

33

00000001

0001

+

0001

+

102

+ + @@ -28,16 +35,16 @@ - - - - - + + + + + @@ -46,7 +53,56 @@ - + + + + + ok + + + + + + + + on + + + status-only + + + + + on + + + + + ok + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + @@ -54,39 +110,43 @@ + + + + + + - - - + + + + - + + - - - + + + - - - - - - - + + + @@ -97,6 +157,25 @@ + + + + + + + + + + + + + + + + + + + @@ -104,28 +183,13 @@ - + - - - - - - - - - - - - - - - @@ -133,140 +197,84 @@ + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - + + - - - - - - - - + - - - - - - - + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - + + on + blocked + test + test/blocked + off + + + ok + warning + alarm + status-only @@ -275,10 +283,7 @@ direct-with-enhanced-security sbo-with-enhanced-security - - operate-once - operate-many - + not-supported bay-control @@ -290,5 +295,7 @@ maintenance process + + diff --git a/examples/server_example_complex_array/server_example_ca.c b/examples/server_example_complex_array/server_example_ca.c index ca8bffeac..b2146cc56 100644 --- a/examples/server_example_complex_array/server_example_ca.c +++ b/examples/server_example_complex_array/server_example_ca.c @@ -62,11 +62,6 @@ int main(int argc, char** argv) { DataObject* mhai1_ha_phsAHar = (DataObject*) IedModel_getModelNodeByObjectReference(&iedModel, "testComplexArray/MHAI1.HA.phsAHar"); - /* alternative: only for static model */ -// DataObject* mhai1_ha_phsAHar = IEDMODEL_ComplexArray_MHAI1_HA_phsAHar; - - assert(mhai1_ha_phsAHar != NULL); - /* Get access to the corresponding MMS value data structure - the MX(FC) part of the data object */ MmsValue* mhai1_ha_phsAHar_mx = IedServer_getFunctionalConstrainedData(iedServer, mhai1_ha_phsAHar, IEC61850_FC_MX); diff --git a/examples/server_example_complex_array/static_model.c b/examples/server_example_complex_array/static_model.c index 760061975..d9bc2a48d 100644 --- a/examples/server_example_complex_array/static_model.c +++ b/examples/server_example_complex_array/static_model.c @@ -30,10 +30,23 @@ DataObject iedModel_ComplexArray_LLN0_Mod = { "Mod", (ModelNode*) &iedModel_ComplexArray_LLN0, (ModelNode*) &iedModel_ComplexArray_LLN0_Beh, - (ModelNode*) &iedModel_ComplexArray_LLN0_Mod_q, + (ModelNode*) &iedModel_ComplexArray_LLN0_Mod_stVal, 0 }; +DataAttribute iedModel_ComplexArray_LLN0_Mod_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_LLN0_Mod, + (ModelNode*) &iedModel_ComplexArray_LLN0_Mod_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + DataAttribute iedModel_ComplexArray_LLN0_Mod_q = { DataAttributeModelType, "q", @@ -90,7 +103,7 @@ DataAttribute iedModel_ComplexArray_LLN0_Beh_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -138,7 +151,7 @@ DataAttribute iedModel_ComplexArray_LLN0_Health_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -247,7 +260,7 @@ LogicalNode iedModel_ComplexArray_LPHD1 = { LogicalNodeModelType, "LPHD1", (ModelNode*) &iedModel_ComplexArray, - (ModelNode*) &iedModel_ComplexArray_MHAI1, + (ModelNode*) &iedModel_ComplexArray_GGIO1, (ModelNode*) &iedModel_ComplexArray_LPHD1_PhyNam, }; @@ -290,7 +303,7 @@ DataAttribute iedModel_ComplexArray_LPHD1_PhyHealth_stVal = { NULL, 0, IEC61850_FC_ST, - IEC61850_INT32, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; @@ -369,201 +382,1645 @@ DataAttribute iedModel_ComplexArray_LPHD1_Proxy_t = { NULL, 0}; -LogicalNode iedModel_ComplexArray_MHAI1 = { +LogicalNode iedModel_ComplexArray_GGIO1 = { LogicalNodeModelType, - "MHAI1", + "GGIO1", (ModelNode*) &iedModel_ComplexArray, - NULL, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, -}; - -DataObject iedModel_ComplexArray_MHAI1_HA = { - DataObjectModelType, - "HA", (ModelNode*) &iedModel_ComplexArray_MHAI1, - NULL, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, - 0 + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod, }; -DataObject iedModel_ComplexArray_MHAI1_HA_phsAHar = { +DataObject iedModel_ComplexArray_GGIO1_Mod = { DataObjectModelType, - "phsAHar", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_numHar, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal, - 16 + "Mod", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Beh, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod_stVal, + 0 }; -DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal = { +DataAttribute iedModel_ComplexArray_GGIO1_Mod_stVal = { DataAttributeModelType, - "cVal", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_q, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod_q, + NULL, 0, - IEC61850_FC_MX, - IEC61850_CONSTRUCTED, - 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag = { +DataAttribute iedModel_ComplexArray_GGIO1_Mod_q = { DataAttributeModelType, - "mag", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag_f, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod_t, + NULL, 0, - IEC61850_FC_MX, - IEC61850_CONSTRUCTED, - 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag_f = { +DataAttribute iedModel_ComplexArray_GGIO1_Mod_t = { DataAttributeModelType, - "f", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag, - NULL, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod_ctlModel, NULL, 0, - IEC61850_FC_MX, - IEC61850_FLOAT32, - 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang = { +DataAttribute iedModel_ComplexArray_GGIO1_Mod_ctlModel = { DataAttributeModelType, - "ang", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal, + "ctlModel", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Mod, + NULL, NULL, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang_f, 0, - IEC61850_FC_MX, - IEC61850_CONSTRUCTED, - 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang_f = { +DataObject iedModel_ComplexArray_GGIO1_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Health, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Beh_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_Beh_stVal = { DataAttributeModelType, - "f", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang, - NULL, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Beh, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Beh_q, NULL, 0, - IEC61850_FC_MX, - IEC61850_FLOAT32, - 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_q = { +DataAttribute iedModel_ComplexArray_GGIO1_Beh_q = { DataAttributeModelType, "q", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_t, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Beh, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Beh_t, NULL, 0, - IEC61850_FC_MX, + IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_t = { +DataAttribute iedModel_ComplexArray_GGIO1_Beh_t = { DataAttributeModelType, "t", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Beh, NULL, NULL, 0, - IEC61850_FC_MX, + IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_numHar = { +DataObject iedModel_ComplexArray_GGIO1_Health = { + DataObjectModelType, + "Health", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Health_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_Health_stVal = { DataAttributeModelType, - "numHar", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_numCyc, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Health, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Health_q, NULL, 0, - IEC61850_FC_CF, - IEC61850_INT16U, + IEC61850_FC_ST, + IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_numCyc = { +DataAttribute iedModel_ComplexArray_GGIO1_Health_q = { DataAttributeModelType, - "numCyc", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_evalTm, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Health, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Health_t, NULL, 0, - IEC61850_FC_CF, - IEC61850_INT16U, - 0 + TRG_OPT_DATA_CHANGED, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_evalTm = { +DataAttribute iedModel_ComplexArray_GGIO1_Health_t = { DataAttributeModelType, - "evalTm", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_frequency, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Health, + NULL, NULL, 0, - IEC61850_FC_CF, - IEC61850_INT16U, - 0 + TRG_OPT_DATA_CHANGED, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, NULL, 0}; -DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency = { +DataObject iedModel_ComplexArray_GGIO1_NamPlt = { + DataObjectModelType, + "NamPlt", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt_vendor, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_vendor = { DataAttributeModelType, - "frequency", - (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, - NULL, + "vendor", + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt, + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt_swRev, NULL, 0, - IEC61850_FC_CF, - IEC61850_FLOAT32, - 0 + TRG_OPT_DATA_CHANGED, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, NULL, 0}; +DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_swRev = { + DataAttributeModelType, + "swRev", + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt, + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt_d, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; +DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_d = { + DataAttributeModelType, + "d", + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt, + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt_dU, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; +DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_dU = { + DataAttributeModelType, + "dU", + (ModelNode*) &iedModel_ComplexArray_GGIO1_NamPlt, + NULL, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_UNICODE_STRING_255, + 0, + NULL, + 0}; +DataObject iedModel_ComplexArray_GGIO1_AnIn1 = { + DataObjectModelType, + "AnIn1", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1_mag, + 0 +}; +DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1_q, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; - - - -IedModel iedModel = { - "test", - &iedModel_ComplexArray, +DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1_mag, + NULL, NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1_t, NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn1, NULL, NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, NULL, - initializeValues + 0}; + +DataObject iedModel_ComplexArray_GGIO1_AnIn2 = { + DataObjectModelType, + "AnIn2", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2_mag, + 0 }; -static void -initializeValues() -{ +DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2_q, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; -iedModel_ComplexArray_LLN0_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); +DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn2, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_AnIn3 = { + DataObjectModelType, + "AnIn3", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3_mag, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3_q, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn3, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_AnIn4 = { + DataObjectModelType, + "AnIn4", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4_mag, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4_q, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_AnIn4, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_SPCSO1 = { + DataObjectModelType, + "SPCSO1", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_origin, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_ctlNum, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_origin_orCat, + 0, + IEC61850_FC_ST, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_origin, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_origin_orIdent, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_origin, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_stVal, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_ctlModel, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1, + NULL, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlNum, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO1_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_SPCSO2 = { + DataObjectModelType, + "SPCSO2", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_ctlModel, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlNum, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2_t, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO2, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_SPCSO3 = { + DataObjectModelType, + "SPCSO3", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_ctlModel, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlNum, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3_t, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO3, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_SPCSO4 = { + DataObjectModelType, + "SPCSO4", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_ctlModel, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlVal, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlNum, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orCat, + 0, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orIdent, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_T, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Test, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Check, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_Oper, + NULL, + NULL, + 0, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4_t, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_SPCSO4, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_Ind1 = { + DataObjectModelType, + "Ind1", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind1_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind1_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind1_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind1, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_Ind2 = { + DataObjectModelType, + "Ind2", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind2_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind2_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind2, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind2_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind2, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_Ind3 = { + DataObjectModelType, + "Ind3", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind3_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind3_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind3, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind3_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind3, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_ComplexArray_GGIO1_Ind4 = { + DataObjectModelType, + "Ind4", + (ModelNode*) &iedModel_ComplexArray_GGIO1, + NULL, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind4_stVal, + 0 +}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind4_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind4, + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind4_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_GGIO1_Ind4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_GGIO1_Ind4, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +LogicalNode iedModel_ComplexArray_MHAI1 = { + LogicalNodeModelType, + "MHAI1", + (ModelNode*) &iedModel_ComplexArray, + NULL, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, +}; + +DataObject iedModel_ComplexArray_MHAI1_HA = { + DataObjectModelType, + "HA", + (ModelNode*) &iedModel_ComplexArray_MHAI1, + NULL, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, + 0 +}; + +DataObject iedModel_ComplexArray_MHAI1_HA_phsAHar = { + DataObjectModelType, + "phsAHar", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_numHar, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal, + 16 +}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal = { + DataAttributeModelType, + "cVal", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_q, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang = { + DataAttributeModelType, + "ang", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal, + NULL, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_cVal_ang, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED + TRG_OPT_DATA_UPDATE, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_phsAHar_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_phsAHar, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_numHar = { + DataAttributeModelType, + "numHar", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_numCyc, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_INT16U, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_numCyc = { + DataAttributeModelType, + "numCyc", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_evalTm, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_INT16U, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_evalTm = { + DataAttributeModelType, + "evalTm", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA_frequency, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_INT16U, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency = { + DataAttributeModelType, + "frequency", + (ModelNode*) &iedModel_ComplexArray_MHAI1_HA, + NULL, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + + + + + + + + + +IedModel iedModel = { + "test", + &iedModel_ComplexArray, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + initializeValues +}; + +static void +initializeValues() +{ + +iedModel_ComplexArray_LLN0_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_ComplexArray_LPHD1_PhyHealth_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_ComplexArray_GGIO1_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_ComplexArray_GGIO1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_ComplexArray_GGIO1_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_ComplexArray_GGIO1_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_ComplexArray_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_ComplexArray_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_ComplexArray_GGIO1_SPCSO3_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_ComplexArray_GGIO1_SPCSO4_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); iedModel_ComplexArray_MHAI1_HA_numHar.mmsValue = MmsValue_newUnsignedFromUint32(16); } diff --git a/examples/server_example_complex_array/static_model.h b/examples/server_example_complex_array/static_model.h index 0999d5812..18f5206e1 100644 --- a/examples/server_example_complex_array/static_model.h +++ b/examples/server_example_complex_array/static_model.h @@ -14,6 +14,7 @@ extern IedModel iedModel; extern LogicalDevice iedModel_ComplexArray; extern LogicalNode iedModel_ComplexArray_LLN0; extern DataObject iedModel_ComplexArray_LLN0_Mod; +extern DataAttribute iedModel_ComplexArray_LLN0_Mod_stVal; extern DataAttribute iedModel_ComplexArray_LLN0_Mod_q; extern DataAttribute iedModel_ComplexArray_LLN0_Mod_t; extern DataAttribute iedModel_ComplexArray_LLN0_Mod_ctlModel; @@ -42,6 +43,121 @@ extern DataObject iedModel_ComplexArray_LPHD1_Proxy; extern DataAttribute iedModel_ComplexArray_LPHD1_Proxy_stVal; extern DataAttribute iedModel_ComplexArray_LPHD1_Proxy_q; extern DataAttribute iedModel_ComplexArray_LPHD1_Proxy_t; +extern LogicalNode iedModel_ComplexArray_GGIO1; +extern DataObject iedModel_ComplexArray_GGIO1_Mod; +extern DataAttribute iedModel_ComplexArray_GGIO1_Mod_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_Mod_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_Mod_t; +extern DataAttribute iedModel_ComplexArray_GGIO1_Mod_ctlModel; +extern DataObject iedModel_ComplexArray_GGIO1_Beh; +extern DataAttribute iedModel_ComplexArray_GGIO1_Beh_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_Beh_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_Beh_t; +extern DataObject iedModel_ComplexArray_GGIO1_Health; +extern DataAttribute iedModel_ComplexArray_GGIO1_Health_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_Health_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_Health_t; +extern DataObject iedModel_ComplexArray_GGIO1_NamPlt; +extern DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_vendor; +extern DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_swRev; +extern DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_d; +extern DataAttribute iedModel_ComplexArray_GGIO1_NamPlt_dU; +extern DataObject iedModel_ComplexArray_GGIO1_AnIn1; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_mag; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_mag_f; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn1_t; +extern DataObject iedModel_ComplexArray_GGIO1_AnIn2; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_mag; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_mag_f; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn2_t; +extern DataObject iedModel_ComplexArray_GGIO1_AnIn3; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_mag; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_mag_f; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn3_t; +extern DataObject iedModel_ComplexArray_GGIO1_AnIn4; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_mag; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_mag_f; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_AnIn4_t; +extern DataObject iedModel_ComplexArray_GGIO1_SPCSO1; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_origin; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_origin_orCat; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_origin_orIdent; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_ctlNum; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_t; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_ctlModel; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orCat; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orIdent; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlNum; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_T; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Test; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Check; +extern DataObject iedModel_ComplexArray_GGIO1_SPCSO2; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orCat; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orIdent; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlNum; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_T; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Test; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Check; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_ctlModel; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO2_t; +extern DataObject iedModel_ComplexArray_GGIO1_SPCSO3; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orCat; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orIdent; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlNum; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_T; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Test; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Check; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_ctlModel; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO3_t; +extern DataObject iedModel_ComplexArray_GGIO1_SPCSO4; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orCat; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orIdent; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlNum; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_T; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Test; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Check; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_ctlModel; +extern DataAttribute iedModel_ComplexArray_GGIO1_SPCSO4_t; +extern DataObject iedModel_ComplexArray_GGIO1_Ind1; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind1_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind1_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind1_t; +extern DataObject iedModel_ComplexArray_GGIO1_Ind2; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind2_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind2_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind2_t; +extern DataObject iedModel_ComplexArray_GGIO1_Ind3; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind3_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind3_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind3_t; +extern DataObject iedModel_ComplexArray_GGIO1_Ind4; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind4_stVal; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind4_q; +extern DataAttribute iedModel_ComplexArray_GGIO1_Ind4_t; extern LogicalNode iedModel_ComplexArray_MHAI1; extern DataObject iedModel_ComplexArray_MHAI1_HA; extern DataObject iedModel_ComplexArray_MHAI1_HA_phsAHar; @@ -62,6 +178,7 @@ extern DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency; #define IEDMODEL_ComplexArray (&iedModel_ComplexArray) #define IEDMODEL_ComplexArray_LLN0 (&iedModel_ComplexArray_LLN0) #define IEDMODEL_ComplexArray_LLN0_Mod (&iedModel_ComplexArray_LLN0_Mod) +#define IEDMODEL_ComplexArray_LLN0_Mod_stVal (&iedModel_ComplexArray_LLN0_Mod_stVal) #define IEDMODEL_ComplexArray_LLN0_Mod_q (&iedModel_ComplexArray_LLN0_Mod_q) #define IEDMODEL_ComplexArray_LLN0_Mod_t (&iedModel_ComplexArray_LLN0_Mod_t) #define IEDMODEL_ComplexArray_LLN0_Mod_ctlModel (&iedModel_ComplexArray_LLN0_Mod_ctlModel) @@ -90,6 +207,121 @@ extern DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency; #define IEDMODEL_ComplexArray_LPHD1_Proxy_stVal (&iedModel_ComplexArray_LPHD1_Proxy_stVal) #define IEDMODEL_ComplexArray_LPHD1_Proxy_q (&iedModel_ComplexArray_LPHD1_Proxy_q) #define IEDMODEL_ComplexArray_LPHD1_Proxy_t (&iedModel_ComplexArray_LPHD1_Proxy_t) +#define IEDMODEL_ComplexArray_GGIO1 (&iedModel_ComplexArray_GGIO1) +#define IEDMODEL_ComplexArray_GGIO1_Mod (&iedModel_ComplexArray_GGIO1_Mod) +#define IEDMODEL_ComplexArray_GGIO1_Mod_stVal (&iedModel_ComplexArray_GGIO1_Mod_stVal) +#define IEDMODEL_ComplexArray_GGIO1_Mod_q (&iedModel_ComplexArray_GGIO1_Mod_q) +#define IEDMODEL_ComplexArray_GGIO1_Mod_t (&iedModel_ComplexArray_GGIO1_Mod_t) +#define IEDMODEL_ComplexArray_GGIO1_Mod_ctlModel (&iedModel_ComplexArray_GGIO1_Mod_ctlModel) +#define IEDMODEL_ComplexArray_GGIO1_Beh (&iedModel_ComplexArray_GGIO1_Beh) +#define IEDMODEL_ComplexArray_GGIO1_Beh_stVal (&iedModel_ComplexArray_GGIO1_Beh_stVal) +#define IEDMODEL_ComplexArray_GGIO1_Beh_q (&iedModel_ComplexArray_GGIO1_Beh_q) +#define IEDMODEL_ComplexArray_GGIO1_Beh_t (&iedModel_ComplexArray_GGIO1_Beh_t) +#define IEDMODEL_ComplexArray_GGIO1_Health (&iedModel_ComplexArray_GGIO1_Health) +#define IEDMODEL_ComplexArray_GGIO1_Health_stVal (&iedModel_ComplexArray_GGIO1_Health_stVal) +#define IEDMODEL_ComplexArray_GGIO1_Health_q (&iedModel_ComplexArray_GGIO1_Health_q) +#define IEDMODEL_ComplexArray_GGIO1_Health_t (&iedModel_ComplexArray_GGIO1_Health_t) +#define IEDMODEL_ComplexArray_GGIO1_NamPlt (&iedModel_ComplexArray_GGIO1_NamPlt) +#define IEDMODEL_ComplexArray_GGIO1_NamPlt_vendor (&iedModel_ComplexArray_GGIO1_NamPlt_vendor) +#define IEDMODEL_ComplexArray_GGIO1_NamPlt_swRev (&iedModel_ComplexArray_GGIO1_NamPlt_swRev) +#define IEDMODEL_ComplexArray_GGIO1_NamPlt_d (&iedModel_ComplexArray_GGIO1_NamPlt_d) +#define IEDMODEL_ComplexArray_GGIO1_NamPlt_dU (&iedModel_ComplexArray_GGIO1_NamPlt_dU) +#define IEDMODEL_ComplexArray_GGIO1_AnIn1 (&iedModel_ComplexArray_GGIO1_AnIn1) +#define IEDMODEL_ComplexArray_GGIO1_AnIn1_mag (&iedModel_ComplexArray_GGIO1_AnIn1_mag) +#define IEDMODEL_ComplexArray_GGIO1_AnIn1_mag_f (&iedModel_ComplexArray_GGIO1_AnIn1_mag_f) +#define IEDMODEL_ComplexArray_GGIO1_AnIn1_q (&iedModel_ComplexArray_GGIO1_AnIn1_q) +#define IEDMODEL_ComplexArray_GGIO1_AnIn1_t (&iedModel_ComplexArray_GGIO1_AnIn1_t) +#define IEDMODEL_ComplexArray_GGIO1_AnIn2 (&iedModel_ComplexArray_GGIO1_AnIn2) +#define IEDMODEL_ComplexArray_GGIO1_AnIn2_mag (&iedModel_ComplexArray_GGIO1_AnIn2_mag) +#define IEDMODEL_ComplexArray_GGIO1_AnIn2_mag_f (&iedModel_ComplexArray_GGIO1_AnIn2_mag_f) +#define IEDMODEL_ComplexArray_GGIO1_AnIn2_q (&iedModel_ComplexArray_GGIO1_AnIn2_q) +#define IEDMODEL_ComplexArray_GGIO1_AnIn2_t (&iedModel_ComplexArray_GGIO1_AnIn2_t) +#define IEDMODEL_ComplexArray_GGIO1_AnIn3 (&iedModel_ComplexArray_GGIO1_AnIn3) +#define IEDMODEL_ComplexArray_GGIO1_AnIn3_mag (&iedModel_ComplexArray_GGIO1_AnIn3_mag) +#define IEDMODEL_ComplexArray_GGIO1_AnIn3_mag_f (&iedModel_ComplexArray_GGIO1_AnIn3_mag_f) +#define IEDMODEL_ComplexArray_GGIO1_AnIn3_q (&iedModel_ComplexArray_GGIO1_AnIn3_q) +#define IEDMODEL_ComplexArray_GGIO1_AnIn3_t (&iedModel_ComplexArray_GGIO1_AnIn3_t) +#define IEDMODEL_ComplexArray_GGIO1_AnIn4 (&iedModel_ComplexArray_GGIO1_AnIn4) +#define IEDMODEL_ComplexArray_GGIO1_AnIn4_mag (&iedModel_ComplexArray_GGIO1_AnIn4_mag) +#define IEDMODEL_ComplexArray_GGIO1_AnIn4_mag_f (&iedModel_ComplexArray_GGIO1_AnIn4_mag_f) +#define IEDMODEL_ComplexArray_GGIO1_AnIn4_q (&iedModel_ComplexArray_GGIO1_AnIn4_q) +#define IEDMODEL_ComplexArray_GGIO1_AnIn4_t (&iedModel_ComplexArray_GGIO1_AnIn4_t) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1 (&iedModel_ComplexArray_GGIO1_SPCSO1) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_origin (&iedModel_ComplexArray_GGIO1_SPCSO1_origin) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_origin_orCat (&iedModel_ComplexArray_GGIO1_SPCSO1_origin_orCat) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_origin_orIdent (&iedModel_ComplexArray_GGIO1_SPCSO1_origin_orIdent) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_ctlNum (&iedModel_ComplexArray_GGIO1_SPCSO1_ctlNum) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_stVal (&iedModel_ComplexArray_GGIO1_SPCSO1_stVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_q (&iedModel_ComplexArray_GGIO1_SPCSO1_q) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_t (&iedModel_ComplexArray_GGIO1_SPCSO1_t) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_ctlModel (&iedModel_ComplexArray_GGIO1_SPCSO1_ctlModel) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_ctlVal (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_origin (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_origin_orCat (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orCat) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_origin_orIdent (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_origin_orIdent) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_ctlNum (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_ctlNum) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_T (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_T) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_Test (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Test) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO1_Oper_Check (&iedModel_ComplexArray_GGIO1_SPCSO1_Oper_Check) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2 (&iedModel_ComplexArray_GGIO1_SPCSO2) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_stVal (&iedModel_ComplexArray_GGIO1_SPCSO2_stVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_q (&iedModel_ComplexArray_GGIO1_SPCSO2_q) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_ctlVal (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_origin (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_origin_orCat (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orCat) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_origin_orIdent (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_origin_orIdent) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_ctlNum (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_ctlNum) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_T (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_T) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_Test (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Test) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_Oper_Check (&iedModel_ComplexArray_GGIO1_SPCSO2_Oper_Check) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_ctlModel (&iedModel_ComplexArray_GGIO1_SPCSO2_ctlModel) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO2_t (&iedModel_ComplexArray_GGIO1_SPCSO2_t) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3 (&iedModel_ComplexArray_GGIO1_SPCSO3) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_stVal (&iedModel_ComplexArray_GGIO1_SPCSO3_stVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_q (&iedModel_ComplexArray_GGIO1_SPCSO3_q) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_ctlVal (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_origin (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_origin_orCat (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orCat) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_origin_orIdent (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_origin_orIdent) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_ctlNum (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_ctlNum) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_T (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_T) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_Test (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Test) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_Oper_Check (&iedModel_ComplexArray_GGIO1_SPCSO3_Oper_Check) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_ctlModel (&iedModel_ComplexArray_GGIO1_SPCSO3_ctlModel) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO3_t (&iedModel_ComplexArray_GGIO1_SPCSO3_t) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4 (&iedModel_ComplexArray_GGIO1_SPCSO4) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_stVal (&iedModel_ComplexArray_GGIO1_SPCSO4_stVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_q (&iedModel_ComplexArray_GGIO1_SPCSO4_q) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_ctlVal (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlVal) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_origin (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_origin_orCat (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orCat) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_origin_orIdent (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_origin_orIdent) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_ctlNum (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_ctlNum) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_T (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_T) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_Test (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Test) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_Oper_Check (&iedModel_ComplexArray_GGIO1_SPCSO4_Oper_Check) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_ctlModel (&iedModel_ComplexArray_GGIO1_SPCSO4_ctlModel) +#define IEDMODEL_ComplexArray_GGIO1_SPCSO4_t (&iedModel_ComplexArray_GGIO1_SPCSO4_t) +#define IEDMODEL_ComplexArray_GGIO1_Ind1 (&iedModel_ComplexArray_GGIO1_Ind1) +#define IEDMODEL_ComplexArray_GGIO1_Ind1_stVal (&iedModel_ComplexArray_GGIO1_Ind1_stVal) +#define IEDMODEL_ComplexArray_GGIO1_Ind1_q (&iedModel_ComplexArray_GGIO1_Ind1_q) +#define IEDMODEL_ComplexArray_GGIO1_Ind1_t (&iedModel_ComplexArray_GGIO1_Ind1_t) +#define IEDMODEL_ComplexArray_GGIO1_Ind2 (&iedModel_ComplexArray_GGIO1_Ind2) +#define IEDMODEL_ComplexArray_GGIO1_Ind2_stVal (&iedModel_ComplexArray_GGIO1_Ind2_stVal) +#define IEDMODEL_ComplexArray_GGIO1_Ind2_q (&iedModel_ComplexArray_GGIO1_Ind2_q) +#define IEDMODEL_ComplexArray_GGIO1_Ind2_t (&iedModel_ComplexArray_GGIO1_Ind2_t) +#define IEDMODEL_ComplexArray_GGIO1_Ind3 (&iedModel_ComplexArray_GGIO1_Ind3) +#define IEDMODEL_ComplexArray_GGIO1_Ind3_stVal (&iedModel_ComplexArray_GGIO1_Ind3_stVal) +#define IEDMODEL_ComplexArray_GGIO1_Ind3_q (&iedModel_ComplexArray_GGIO1_Ind3_q) +#define IEDMODEL_ComplexArray_GGIO1_Ind3_t (&iedModel_ComplexArray_GGIO1_Ind3_t) +#define IEDMODEL_ComplexArray_GGIO1_Ind4 (&iedModel_ComplexArray_GGIO1_Ind4) +#define IEDMODEL_ComplexArray_GGIO1_Ind4_stVal (&iedModel_ComplexArray_GGIO1_Ind4_stVal) +#define IEDMODEL_ComplexArray_GGIO1_Ind4_q (&iedModel_ComplexArray_GGIO1_Ind4_q) +#define IEDMODEL_ComplexArray_GGIO1_Ind4_t (&iedModel_ComplexArray_GGIO1_Ind4_t) #define IEDMODEL_ComplexArray_MHAI1 (&iedModel_ComplexArray_MHAI1) #define IEDMODEL_ComplexArray_MHAI1_HA (&iedModel_ComplexArray_MHAI1_HA) #define IEDMODEL_ComplexArray_MHAI1_HA_phsAHar (&iedModel_ComplexArray_MHAI1_HA_phsAHar) diff --git a/examples/server_example_password_auth/server_example_password_auth.c b/examples/server_example_password_auth/server_example_password_auth.c index 40f3d8048..0e94522fb 100644 --- a/examples/server_example_password_auth/server_example_password_auth.c +++ b/examples/server_example_password_auth/server_example_password_auth.c @@ -126,6 +126,40 @@ controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test) MmsValue_delete(timeStamp); } + +static MmsDataAccessError +writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter) +{ + void* securityToken = ClientConnection_getSecurityToken(connection); + + if (dataAttribute == IEDMODEL_GenericIO_LLN0_ModAuto_setVal) + printf("Write access to LLN0.ModAuto.setVal: %i\n", MmsValue_getBoolean(value)); + + if (securityToken != password2) { + printf(" Access denied\n"); + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + + return DATA_ACCESS_ERROR_SUCCESS; +} + +static MmsDataAccessError +readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter) +{ + void* securityToken = ClientConnection_getSecurityToken(connection); + + if (securityToken != password2) { + + if ((dataObject == IEDMODEL_GenericIO_GGIO1_Ind1) || (dataObject == IEDMODEL_GenericIO_GGIO1_Ind2)) { + printf(" Access denied\n"); + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + + return DATA_ACCESS_ERROR_SUCCESS; +} + + int main(int argc, char** argv) { iedServer = IedServer_create(&iedModel); @@ -153,6 +187,15 @@ int main(int argc, char** argv) { IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4, (ControlHandler) controlHandlerForBinaryOutput, IEDMODEL_GenericIO_GGIO1_SPCSO4); + /* Allow only write access to settings that have a handler */ + IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_SP, ACCESS_POLICY_DENY); + + /* Set write access handler */ + IedServer_handleWriteAccess(iedServer, IEDMODEL_GenericIO_LLN0_ModAuto_setVal, writeAccessHandler, NULL); + + /* Set read access handler */ + IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); + /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); diff --git a/examples/server_example_password_auth/simpleIO_direct_control.icd b/examples/server_example_password_auth/simpleIO_direct_control.icd index 3081dffec..7cb91a232 100644 --- a/examples/server_example_password_auth/simpleIO_direct_control.icd +++ b/examples/server_example_password_auth/simpleIO_direct_control.icd @@ -94,6 +94,7 @@ + @@ -186,6 +187,9 @@ + + + diff --git a/examples/server_example_password_auth/static_model.c b/examples/server_example_password_auth/static_model.c index 1db7d7786..4cc943c7b 100644 --- a/examples/server_example_password_auth/static_model.c +++ b/examples/server_example_password_auth/static_model.c @@ -3,7 +3,7 @@ * * automatically generated from simpleIO_direct_control.icd */ -#include "../server_example_password_auth/static_model.h" +#include "static_model.h" static void initializeValues(); @@ -227,7 +227,7 @@ DataObject iedModel_GenericIO_LLN0_NamPlt = { DataObjectModelType, "NamPlt", (ModelNode*) &iedModel_GenericIO_LLN0, - NULL, + (ModelNode*) &iedModel_GenericIO_LLN0_ModAuto, (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_vendor, 0 }; @@ -297,6 +297,28 @@ DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs = { NULL, 0}; +DataObject iedModel_GenericIO_LLN0_ModAuto = { + DataObjectModelType, + "ModAuto", + (ModelNode*) &iedModel_GenericIO_LLN0, + NULL, + (ModelNode*) &iedModel_GenericIO_LLN0_ModAuto_setVal, + 0 +}; + +DataAttribute iedModel_GenericIO_LLN0_ModAuto_setVal = { + DataAttributeModelType, + "setVal", + (ModelNode*) &iedModel_GenericIO_LLN0_ModAuto, + NULL, + NULL, + 0, + IEC61850_FC_SP, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + LogicalNode iedModel_GenericIO_LPHD1 = { LogicalNodeModelType, "LPHD1", @@ -1773,7 +1795,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = { extern ReportControlBlock iedModel_GenericIO_LLN0_report0; -ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events", false, "Events", 1, 24, 111, 50, 1000, NULL}; +ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events", false, "Events", 1, 24, 239, 50, 1000, NULL}; diff --git a/examples/server_example_password_auth/static_model.h b/examples/server_example_password_auth/static_model.h index 58dd28b5f..372674e73 100644 --- a/examples/server_example_password_auth/static_model.h +++ b/examples/server_example_password_auth/static_model.h @@ -31,6 +31,8 @@ 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 DataObject iedModel_GenericIO_LLN0_ModAuto; +extern DataAttribute iedModel_GenericIO_LLN0_ModAuto_setVal; extern LogicalNode iedModel_GenericIO_LPHD1; extern DataObject iedModel_GenericIO_LPHD1_PhyNam; extern DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor; @@ -174,6 +176,8 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; #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_LLN0_ModAuto (&iedModel_GenericIO_LLN0_ModAuto) +#define IEDMODEL_GenericIO_LLN0_ModAuto_setVal (&iedModel_GenericIO_LLN0_ModAuto_setVal) #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) diff --git a/examples/server_example_substitution/CMakeLists.txt b/examples/server_example_substitution/CMakeLists.txt new file mode 100644 index 000000000..d9ae35129 --- /dev/null +++ b/examples/server_example_substitution/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories( + . +) + +set(server_example_SRCS + server_example_substitution.c + static_model.c +) + +IF(WIN32) +set_source_files_properties(${server_example_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(WIN32) + +add_executable(server_example_substitution + ${server_example_SRCS} +) + +target_link_libraries(server_example_substitution + iec61850 +) diff --git a/examples/server_example_substitution/Makefile b/examples/server_example_substitution/Makefile new file mode 100644 index 000000000..f3b779f43 --- /dev/null +++ b/examples/server_example_substitution/Makefile @@ -0,0 +1,30 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = server_example_substitution +PROJECT_SOURCES = server_example_substitution.c +PROJECT_SOURCES += static_model.c + +PROJECT_ICD_FILE = substitution_example.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) + +clean: + rm -f $(PROJECT_BINARY_NAME) + + + diff --git a/examples/server_example_substitution/server_example_substitution.c b/examples/server_example_substitution/server_example_substitution.c new file mode 100644 index 000000000..043280a3b --- /dev/null +++ b/examples/server_example_substitution/server_example_substitution.c @@ -0,0 +1,220 @@ +/* + * server_example_substitution.c + * + * - How to use the IEC 61850 substitution service + * - Two data objects can be substituted: + * -- GGIO1.AnIn1 + * -- GGIO1.Ind1 + */ + +#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; + +static bool subsAnIn1 = false; +static bool subsInd1 = false; + +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 MmsDataAccessError +writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter) +{ + if (dataAttribute == IEDMODEL_LD1_GGIO1_AnIn1_subEna) { + + printf("Received GGIO1.AnIn1.subEna: %i\n", MmsValue_getBoolean(value)); + + if (MmsValue_getBoolean(value)) { + subsAnIn1 = true; + + /* Update values with substituted values */ + + Quality quality = + Quality_fromMmsValue(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subQ)); + + Quality_setFlag(&quality, QUALITY_SOURCE_SUBSTITUTED); + + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, quality); + + IedServer_updateAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, + IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subMag_f)); + } + else { + subsAnIn1 = false; + } + + } + else if (dataAttribute == IEDMODEL_LD1_GGIO1_AnIn1_subMag_f) { + + if (subsAnIn1) { + IedServer_updateAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, value); + } + + } + else if (dataAttribute == IEDMODEL_LD1_GGIO1_AnIn1_subQ) { + + if (subsAnIn1) { + Quality quality = Quality_fromMmsValue(value); + + Quality_setFlag(&quality, QUALITY_SOURCE_SUBSTITUTED); + + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, quality); + } + + } + else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subEna) { + printf("Received GGIO1.Ind1.subEna: %i\n", MmsValue_getBoolean(value)); + + if (MmsValue_getBoolean(value)) { + subsInd1 = true; + + /* Update values with substituted values */ + + Quality quality = + Quality_fromMmsValue(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subQ)); + + Quality_setFlag(&quality, QUALITY_SOURCE_SUBSTITUTED); + + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, quality); + + IedServer_updateAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, + IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal)); + } + else { + subsInd1 = false; + } + } + else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subVal) { + + if (subsInd1) { + IedServer_updateAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, value); + } + + } + else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subQ) { + + if (subsInd1) { + Quality quality = Quality_fromMmsValue(value); + + Quality_setFlag(&quality, QUALITY_SOURCE_SUBSTITUTED); + + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, quality); + } + + } + + return DATA_ACCESS_ERROR_SUCCESS; +} + +int +main(int argc, char** argv) +{ + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); + + + + + /* Create a new IEC 61850 server instance */ + iedServer = IedServer_create(&iedModel); + + IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + + /* Install write callback handler for substitution variables */ + + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subEna, writeAccessHandler, NULL); + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subMag_f, writeAccessHandler, NULL); + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subQ, writeAccessHandler, NULL); + + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subEna, writeAccessHandler, NULL); + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal, writeAccessHandler, NULL); + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subQ, writeAccessHandler, NULL); + + /* MMS server will be instructed to start listening for client connections. */ + IedServer_start(iedServer, 102); + + if (!IedServer_isRunning(iedServer)) { + printf("Starting server failed! Exit.\n"); + IedServer_destroy(iedServer); + exit(-1); + } + + running = 1; + + signal(SIGINT, sigint_handler); + + float t = 0.f; + bool ind1 = true; + + while (running) { + uint64_t timestamp = Hal_getTimeInMs(); + + t += 0.1f; + + float an1 = sinf(t); + + if (ind1) + ind1 = false; + else + ind1 = true; + + Timestamp iecTimestamp; + + Timestamp_clearFlags(&iecTimestamp); + Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp); + Timestamp_setLeapSecondKnown(&iecTimestamp, true); + + + /* toggle clock-not-synchronized flag in timestamp */ + if (((int) t % 2) == 0) + Timestamp_setClockNotSynchronized(&iecTimestamp, true); + + IedServer_lockDataModel(iedServer); + + if (subsAnIn1 == false) { + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_t, &iecTimestamp); + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, an1); + } + + if (subsInd1 == false) { + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_t, &iecTimestamp); + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, QUALITY_VALIDITY_GOOD); + IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, ind1); + } + + IedServer_unlockDataModel(iedServer); + + Thread_sleep(100); + } + + /* stop MMS server - close TCP server socket and all client sockets */ + IedServer_stop(iedServer); + + /* Cleanup - free all resources */ + IedServer_destroy(iedServer); + +} /* main() */ diff --git a/examples/server_example_substitution/static_model.c b/examples/server_example_substitution/static_model.c new file mode 100644 index 000000000..bf53c61a7 --- /dev/null +++ b/examples/server_example_substitution/static_model.c @@ -0,0 +1,998 @@ +/* + * static_model.c + * + * automatically generated from substitution_example.icd + */ +#include "static_model.h" + +static void initializeValues(); + + + +LogicalDevice iedModel_LD1 = { + LogicalDeviceModelType, + "LD1", + (ModelNode*) &iedModel, + NULL, + (ModelNode*) &iedModel_LD1_LLN0 +}; + +LogicalNode iedModel_LD1_LLN0 = { + LogicalNodeModelType, + "LLN0", + (ModelNode*) &iedModel_LD1, + (ModelNode*) &iedModel_LD1_LPHD1, + (ModelNode*) &iedModel_LD1_LLN0_Beh, +}; + +DataObject iedModel_LD1_LLN0_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_LD1_LLN0, + NULL, + (ModelNode*) &iedModel_LD1_LLN0_Beh_stVal, + 0 +}; + +DataAttribute iedModel_LD1_LLN0_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_LD1_LLN0_Beh, + (ModelNode*) &iedModel_LD1_LLN0_Beh_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LLN0_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_LLN0_Beh, + (ModelNode*) &iedModel_LD1_LLN0_Beh_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LLN0_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_LLN0_Beh, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +LogicalNode iedModel_LD1_LPHD1 = { + LogicalNodeModelType, + "LPHD1", + (ModelNode*) &iedModel_LD1, + (ModelNode*) &iedModel_LD1_MMDC1, + (ModelNode*) &iedModel_LD1_LPHD1_PhyNam, +}; + +DataObject iedModel_LD1_LPHD1_PhyNam = { + DataObjectModelType, + "PhyNam", + (ModelNode*) &iedModel_LD1_LPHD1, + (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth, + (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_vendor, + 0 +}; + +DataAttribute iedModel_LD1_LPHD1_PhyNam_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_LD1_LPHD1_PhyNam, + NULL, + NULL, + 0, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataObject iedModel_LD1_LPHD1_PhyHealth = { + DataObjectModelType, + "PhyHealth", + (ModelNode*) &iedModel_LD1_LPHD1, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth_stVal, + 0 +}; + +DataAttribute iedModel_LD1_LPHD1_PhyHealth_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth, + (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_PhyHealth_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth, + (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_PhyHealth_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_LD1_LPHD1_Proxy = { + DataObjectModelType, + "Proxy", + (ModelNode*) &iedModel_LD1_LPHD1, + NULL, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_stVal, + 0 +}; + +DataAttribute iedModel_LD1_LPHD1_Proxy_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_Proxy_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_Proxy_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_subEna, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_Proxy_subEna = { + DataAttributeModelType, + "subEna", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_subVal, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_Proxy_subVal = { + DataAttributeModelType, + "subVal", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_subQ, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_Proxy_subQ = { + DataAttributeModelType, + "subQ", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_subID, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_LPHD1_Proxy_subID = { + DataAttributeModelType, + "subID", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_VISIBLE_STRING_64, + 0, + NULL, + 0}; + +LogicalNode iedModel_LD1_MMDC1 = { + LogicalNodeModelType, + "MMDC1", + (ModelNode*) &iedModel_LD1, + (ModelNode*) &iedModel_LD1_GGIO1, + (ModelNode*) &iedModel_LD1_MMDC1_Beh, +}; + +DataObject iedModel_LD1_MMDC1_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_LD1_MMDC1, + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + (ModelNode*) &iedModel_LD1_MMDC1_Beh_stVal, + 0 +}; + +DataAttribute iedModel_LD1_MMDC1_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_LD1_MMDC1_Beh, + (ModelNode*) &iedModel_LD1_MMDC1_Beh_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_MMDC1_Beh, + (ModelNode*) &iedModel_LD1_MMDC1_Beh_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_MMDC1_Beh, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_LD1_MMDC1_Watt = { + DataObjectModelType, + "Watt", + (ModelNode*) &iedModel_LD1_MMDC1, + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_mag, + 0 +}; + +DataAttribute iedModel_LD1_MMDC1_Watt_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_q, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_MMDC1_Watt_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_subEna, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_subEna = { + DataAttributeModelType, + "subEna", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_subMag, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_subMag = { + DataAttributeModelType, + "subMag", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_subQ, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_subMag_f, + 0, + IEC61850_FC_SV, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_subMag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_MMDC1_Watt_subMag, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_FLOAT32, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_subQ = { + DataAttributeModelType, + "subQ", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_subID, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Watt_subID = { + DataAttributeModelType, + "subID", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_VISIBLE_STRING_64, + 0, + NULL, + 0}; + +DataObject iedModel_LD1_MMDC1_Amp = { + DataObjectModelType, + "Amp", + (ModelNode*) &iedModel_LD1_MMDC1, + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_mag, + 0 +}; + +DataAttribute iedModel_LD1_MMDC1_Amp_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_q, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_MMDC1_Amp_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_subEna, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_subEna = { + DataAttributeModelType, + "subEna", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_subMag, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_subMag = { + DataAttributeModelType, + "subMag", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_subQ, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_subMag_f, + 0, + IEC61850_FC_SV, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_subMag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_MMDC1_Amp_subMag, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_FLOAT32, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_subQ = { + DataAttributeModelType, + "subQ", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_subID, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Amp_subID = { + DataAttributeModelType, + "subID", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_VISIBLE_STRING_64, + 0, + NULL, + 0}; + +DataObject iedModel_LD1_MMDC1_Vol = { + DataObjectModelType, + "Vol", + (ModelNode*) &iedModel_LD1_MMDC1, + NULL, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_mag, + 0 +}; + +DataAttribute iedModel_LD1_MMDC1_Vol_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_q, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_MMDC1_Vol_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_subEna, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_subEna = { + DataAttributeModelType, + "subEna", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_subMag, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_subMag = { + DataAttributeModelType, + "subMag", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_subQ, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_subMag_f, + 0, + IEC61850_FC_SV, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_subMag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_MMDC1_Vol_subMag, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_FLOAT32, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_subQ = { + DataAttributeModelType, + "subQ", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_subID, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_MMDC1_Vol_subID = { + DataAttributeModelType, + "subID", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_VISIBLE_STRING_64, + 0, + NULL, + 0}; + +LogicalNode iedModel_LD1_GGIO1 = { + LogicalNodeModelType, + "GGIO1", + (ModelNode*) &iedModel_LD1, + NULL, + (ModelNode*) &iedModel_LD1_GGIO1_Beh, +}; + +DataObject iedModel_LD1_GGIO1_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_LD1_GGIO1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + (ModelNode*) &iedModel_LD1_GGIO1_Beh_stVal, + 0 +}; + +DataAttribute iedModel_LD1_GGIO1_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_LD1_GGIO1_Beh, + (ModelNode*) &iedModel_LD1_GGIO1_Beh_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_GGIO1_Beh, + (ModelNode*) &iedModel_LD1_GGIO1_Beh_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_GGIO1_Beh, + NULL, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_LD1_GGIO1_Ind1 = { + DataObjectModelType, + "Ind1", + (ModelNode*) &iedModel_LD1_GGIO1, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_stVal, + 0 +}; + +DataAttribute iedModel_LD1_GGIO1_Ind1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_q, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Ind1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_t, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Ind1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_subEna, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Ind1_subEna = { + DataAttributeModelType, + "subEna", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_subVal, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Ind1_subVal = { + DataAttributeModelType, + "subVal", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_subQ, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Ind1_subQ = { + DataAttributeModelType, + "subQ", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_subID, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_Ind1_subID = { + DataAttributeModelType, + "subID", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_VISIBLE_STRING_64, + 0, + NULL, + 0}; + +DataObject iedModel_LD1_GGIO1_AnIn1 = { + DataObjectModelType, + "AnIn1", + (ModelNode*) &iedModel_LD1_GGIO1, + NULL, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_mag, + 0 +}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_q, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_mag_f, + 0, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_mag, + NULL, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_t, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_subEna, + NULL, + 0, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_subEna = { + DataAttributeModelType, + "subEna", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_subMag, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_subMag = { + DataAttributeModelType, + "subMag", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_subQ, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_subMag_f, + 0, + IEC61850_FC_SV, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_subMag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_subMag, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_FLOAT32, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_subQ = { + DataAttributeModelType, + "subQ", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_subID, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_QUALITY, + 0, + NULL, + 0}; + +DataAttribute iedModel_LD1_GGIO1_AnIn1_subID = { + DataAttributeModelType, + "subID", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + NULL, + NULL, + 0, + IEC61850_FC_SV, + IEC61850_VISIBLE_STRING_64, + 0, + NULL, + 0}; + +extern ReportControlBlock iedModel_LD1_LLN0_report0; +extern ReportControlBlock iedModel_LD1_LLN0_report1; + +ReportControlBlock iedModel_LD1_LLN0_report0 = {&iedModel_LD1_LLN0, "urcb01", "13e08c78", false, "", 1, 23, 247, 3000, 5000, &iedModel_LD1_LLN0_report1}; +ReportControlBlock iedModel_LD1_LLN0_report1 = {&iedModel_LD1_LLN0, "urcb02", "13e08c78", false, "", 1, 23, 247, 3000, 5000, NULL}; + + + + + + + +IedModel iedModel = { + "IED1", + &iedModel_LD1, + NULL, + &iedModel_LD1_LLN0_report0, + NULL, + NULL, + NULL, + NULL, + NULL, + initializeValues +}; + +static void +initializeValues() +{ +} diff --git a/examples/server_example_substitution/static_model.h b/examples/server_example_substitution/static_model.h new file mode 100644 index 000000000..1ad6a5e49 --- /dev/null +++ b/examples/server_example_substitution/static_model.h @@ -0,0 +1,177 @@ +/* + * static_model.h + * + * automatically generated from substitution_example.icd + */ + +#ifndef STATIC_MODEL_H_ +#define STATIC_MODEL_H_ + +#include +#include "iec61850_model.h" + +extern IedModel iedModel; +extern LogicalDevice iedModel_LD1; +extern LogicalNode iedModel_LD1_LLN0; +extern DataObject iedModel_LD1_LLN0_Beh; +extern DataAttribute iedModel_LD1_LLN0_Beh_stVal; +extern DataAttribute iedModel_LD1_LLN0_Beh_q; +extern DataAttribute iedModel_LD1_LLN0_Beh_t; +extern LogicalNode iedModel_LD1_LPHD1; +extern DataObject iedModel_LD1_LPHD1_PhyNam; +extern DataAttribute iedModel_LD1_LPHD1_PhyNam_vendor; +extern DataObject iedModel_LD1_LPHD1_PhyHealth; +extern DataAttribute iedModel_LD1_LPHD1_PhyHealth_stVal; +extern DataAttribute iedModel_LD1_LPHD1_PhyHealth_q; +extern DataAttribute iedModel_LD1_LPHD1_PhyHealth_t; +extern DataObject iedModel_LD1_LPHD1_Proxy; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_stVal; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_q; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_t; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_subEna; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_subVal; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_subQ; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_subID; +extern LogicalNode iedModel_LD1_MMDC1; +extern DataObject iedModel_LD1_MMDC1_Beh; +extern DataAttribute iedModel_LD1_MMDC1_Beh_stVal; +extern DataAttribute iedModel_LD1_MMDC1_Beh_q; +extern DataAttribute iedModel_LD1_MMDC1_Beh_t; +extern DataObject iedModel_LD1_MMDC1_Watt; +extern DataAttribute iedModel_LD1_MMDC1_Watt_mag; +extern DataAttribute iedModel_LD1_MMDC1_Watt_mag_f; +extern DataAttribute iedModel_LD1_MMDC1_Watt_q; +extern DataAttribute iedModel_LD1_MMDC1_Watt_t; +extern DataAttribute iedModel_LD1_MMDC1_Watt_subEna; +extern DataAttribute iedModel_LD1_MMDC1_Watt_subMag; +extern DataAttribute iedModel_LD1_MMDC1_Watt_subMag_f; +extern DataAttribute iedModel_LD1_MMDC1_Watt_subQ; +extern DataAttribute iedModel_LD1_MMDC1_Watt_subID; +extern DataObject iedModel_LD1_MMDC1_Amp; +extern DataAttribute iedModel_LD1_MMDC1_Amp_mag; +extern DataAttribute iedModel_LD1_MMDC1_Amp_mag_f; +extern DataAttribute iedModel_LD1_MMDC1_Amp_q; +extern DataAttribute iedModel_LD1_MMDC1_Amp_t; +extern DataAttribute iedModel_LD1_MMDC1_Amp_subEna; +extern DataAttribute iedModel_LD1_MMDC1_Amp_subMag; +extern DataAttribute iedModel_LD1_MMDC1_Amp_subMag_f; +extern DataAttribute iedModel_LD1_MMDC1_Amp_subQ; +extern DataAttribute iedModel_LD1_MMDC1_Amp_subID; +extern DataObject iedModel_LD1_MMDC1_Vol; +extern DataAttribute iedModel_LD1_MMDC1_Vol_mag; +extern DataAttribute iedModel_LD1_MMDC1_Vol_mag_f; +extern DataAttribute iedModel_LD1_MMDC1_Vol_q; +extern DataAttribute iedModel_LD1_MMDC1_Vol_t; +extern DataAttribute iedModel_LD1_MMDC1_Vol_subEna; +extern DataAttribute iedModel_LD1_MMDC1_Vol_subMag; +extern DataAttribute iedModel_LD1_MMDC1_Vol_subMag_f; +extern DataAttribute iedModel_LD1_MMDC1_Vol_subQ; +extern DataAttribute iedModel_LD1_MMDC1_Vol_subID; +extern LogicalNode iedModel_LD1_GGIO1; +extern DataObject iedModel_LD1_GGIO1_Beh; +extern DataAttribute iedModel_LD1_GGIO1_Beh_stVal; +extern DataAttribute iedModel_LD1_GGIO1_Beh_q; +extern DataAttribute iedModel_LD1_GGIO1_Beh_t; +extern DataObject iedModel_LD1_GGIO1_Ind1; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_stVal; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_q; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_t; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_subEna; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_subVal; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_subQ; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_subID; +extern DataObject iedModel_LD1_GGIO1_AnIn1; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_mag; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_mag_f; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_q; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_t; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subEna; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subMag; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subMag_f; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subQ; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; + + + +#define IEDMODEL_LD1 (&iedModel_LD1) +#define IEDMODEL_LD1_LLN0 (&iedModel_LD1_LLN0) +#define IEDMODEL_LD1_LLN0_Beh (&iedModel_LD1_LLN0_Beh) +#define IEDMODEL_LD1_LLN0_Beh_stVal (&iedModel_LD1_LLN0_Beh_stVal) +#define IEDMODEL_LD1_LLN0_Beh_q (&iedModel_LD1_LLN0_Beh_q) +#define IEDMODEL_LD1_LLN0_Beh_t (&iedModel_LD1_LLN0_Beh_t) +#define IEDMODEL_LD1_LPHD1 (&iedModel_LD1_LPHD1) +#define IEDMODEL_LD1_LPHD1_PhyNam (&iedModel_LD1_LPHD1_PhyNam) +#define IEDMODEL_LD1_LPHD1_PhyNam_vendor (&iedModel_LD1_LPHD1_PhyNam_vendor) +#define IEDMODEL_LD1_LPHD1_PhyHealth (&iedModel_LD1_LPHD1_PhyHealth) +#define IEDMODEL_LD1_LPHD1_PhyHealth_stVal (&iedModel_LD1_LPHD1_PhyHealth_stVal) +#define IEDMODEL_LD1_LPHD1_PhyHealth_q (&iedModel_LD1_LPHD1_PhyHealth_q) +#define IEDMODEL_LD1_LPHD1_PhyHealth_t (&iedModel_LD1_LPHD1_PhyHealth_t) +#define IEDMODEL_LD1_LPHD1_Proxy (&iedModel_LD1_LPHD1_Proxy) +#define IEDMODEL_LD1_LPHD1_Proxy_stVal (&iedModel_LD1_LPHD1_Proxy_stVal) +#define IEDMODEL_LD1_LPHD1_Proxy_q (&iedModel_LD1_LPHD1_Proxy_q) +#define IEDMODEL_LD1_LPHD1_Proxy_t (&iedModel_LD1_LPHD1_Proxy_t) +#define IEDMODEL_LD1_LPHD1_Proxy_subEna (&iedModel_LD1_LPHD1_Proxy_subEna) +#define IEDMODEL_LD1_LPHD1_Proxy_subVal (&iedModel_LD1_LPHD1_Proxy_subVal) +#define IEDMODEL_LD1_LPHD1_Proxy_subQ (&iedModel_LD1_LPHD1_Proxy_subQ) +#define IEDMODEL_LD1_LPHD1_Proxy_subID (&iedModel_LD1_LPHD1_Proxy_subID) +#define IEDMODEL_LD1_MMDC1 (&iedModel_LD1_MMDC1) +#define IEDMODEL_LD1_MMDC1_Beh (&iedModel_LD1_MMDC1_Beh) +#define IEDMODEL_LD1_MMDC1_Beh_stVal (&iedModel_LD1_MMDC1_Beh_stVal) +#define IEDMODEL_LD1_MMDC1_Beh_q (&iedModel_LD1_MMDC1_Beh_q) +#define IEDMODEL_LD1_MMDC1_Beh_t (&iedModel_LD1_MMDC1_Beh_t) +#define IEDMODEL_LD1_MMDC1_Watt (&iedModel_LD1_MMDC1_Watt) +#define IEDMODEL_LD1_MMDC1_Watt_mag (&iedModel_LD1_MMDC1_Watt_mag) +#define IEDMODEL_LD1_MMDC1_Watt_mag_f (&iedModel_LD1_MMDC1_Watt_mag_f) +#define IEDMODEL_LD1_MMDC1_Watt_q (&iedModel_LD1_MMDC1_Watt_q) +#define IEDMODEL_LD1_MMDC1_Watt_t (&iedModel_LD1_MMDC1_Watt_t) +#define IEDMODEL_LD1_MMDC1_Watt_subEna (&iedModel_LD1_MMDC1_Watt_subEna) +#define IEDMODEL_LD1_MMDC1_Watt_subMag (&iedModel_LD1_MMDC1_Watt_subMag) +#define IEDMODEL_LD1_MMDC1_Watt_subMag_f (&iedModel_LD1_MMDC1_Watt_subMag_f) +#define IEDMODEL_LD1_MMDC1_Watt_subQ (&iedModel_LD1_MMDC1_Watt_subQ) +#define IEDMODEL_LD1_MMDC1_Watt_subID (&iedModel_LD1_MMDC1_Watt_subID) +#define IEDMODEL_LD1_MMDC1_Amp (&iedModel_LD1_MMDC1_Amp) +#define IEDMODEL_LD1_MMDC1_Amp_mag (&iedModel_LD1_MMDC1_Amp_mag) +#define IEDMODEL_LD1_MMDC1_Amp_mag_f (&iedModel_LD1_MMDC1_Amp_mag_f) +#define IEDMODEL_LD1_MMDC1_Amp_q (&iedModel_LD1_MMDC1_Amp_q) +#define IEDMODEL_LD1_MMDC1_Amp_t (&iedModel_LD1_MMDC1_Amp_t) +#define IEDMODEL_LD1_MMDC1_Amp_subEna (&iedModel_LD1_MMDC1_Amp_subEna) +#define IEDMODEL_LD1_MMDC1_Amp_subMag (&iedModel_LD1_MMDC1_Amp_subMag) +#define IEDMODEL_LD1_MMDC1_Amp_subMag_f (&iedModel_LD1_MMDC1_Amp_subMag_f) +#define IEDMODEL_LD1_MMDC1_Amp_subQ (&iedModel_LD1_MMDC1_Amp_subQ) +#define IEDMODEL_LD1_MMDC1_Amp_subID (&iedModel_LD1_MMDC1_Amp_subID) +#define IEDMODEL_LD1_MMDC1_Vol (&iedModel_LD1_MMDC1_Vol) +#define IEDMODEL_LD1_MMDC1_Vol_mag (&iedModel_LD1_MMDC1_Vol_mag) +#define IEDMODEL_LD1_MMDC1_Vol_mag_f (&iedModel_LD1_MMDC1_Vol_mag_f) +#define IEDMODEL_LD1_MMDC1_Vol_q (&iedModel_LD1_MMDC1_Vol_q) +#define IEDMODEL_LD1_MMDC1_Vol_t (&iedModel_LD1_MMDC1_Vol_t) +#define IEDMODEL_LD1_MMDC1_Vol_subEna (&iedModel_LD1_MMDC1_Vol_subEna) +#define IEDMODEL_LD1_MMDC1_Vol_subMag (&iedModel_LD1_MMDC1_Vol_subMag) +#define IEDMODEL_LD1_MMDC1_Vol_subMag_f (&iedModel_LD1_MMDC1_Vol_subMag_f) +#define IEDMODEL_LD1_MMDC1_Vol_subQ (&iedModel_LD1_MMDC1_Vol_subQ) +#define IEDMODEL_LD1_MMDC1_Vol_subID (&iedModel_LD1_MMDC1_Vol_subID) +#define IEDMODEL_LD1_GGIO1 (&iedModel_LD1_GGIO1) +#define IEDMODEL_LD1_GGIO1_Beh (&iedModel_LD1_GGIO1_Beh) +#define IEDMODEL_LD1_GGIO1_Beh_stVal (&iedModel_LD1_GGIO1_Beh_stVal) +#define IEDMODEL_LD1_GGIO1_Beh_q (&iedModel_LD1_GGIO1_Beh_q) +#define IEDMODEL_LD1_GGIO1_Beh_t (&iedModel_LD1_GGIO1_Beh_t) +#define IEDMODEL_LD1_GGIO1_Ind1 (&iedModel_LD1_GGIO1_Ind1) +#define IEDMODEL_LD1_GGIO1_Ind1_stVal (&iedModel_LD1_GGIO1_Ind1_stVal) +#define IEDMODEL_LD1_GGIO1_Ind1_q (&iedModel_LD1_GGIO1_Ind1_q) +#define IEDMODEL_LD1_GGIO1_Ind1_t (&iedModel_LD1_GGIO1_Ind1_t) +#define IEDMODEL_LD1_GGIO1_Ind1_subEna (&iedModel_LD1_GGIO1_Ind1_subEna) +#define IEDMODEL_LD1_GGIO1_Ind1_subVal (&iedModel_LD1_GGIO1_Ind1_subVal) +#define IEDMODEL_LD1_GGIO1_Ind1_subQ (&iedModel_LD1_GGIO1_Ind1_subQ) +#define IEDMODEL_LD1_GGIO1_Ind1_subID (&iedModel_LD1_GGIO1_Ind1_subID) +#define IEDMODEL_LD1_GGIO1_AnIn1 (&iedModel_LD1_GGIO1_AnIn1) +#define IEDMODEL_LD1_GGIO1_AnIn1_mag (&iedModel_LD1_GGIO1_AnIn1_mag) +#define IEDMODEL_LD1_GGIO1_AnIn1_mag_f (&iedModel_LD1_GGIO1_AnIn1_mag_f) +#define IEDMODEL_LD1_GGIO1_AnIn1_q (&iedModel_LD1_GGIO1_AnIn1_q) +#define IEDMODEL_LD1_GGIO1_AnIn1_t (&iedModel_LD1_GGIO1_AnIn1_t) +#define IEDMODEL_LD1_GGIO1_AnIn1_subEna (&iedModel_LD1_GGIO1_AnIn1_subEna) +#define IEDMODEL_LD1_GGIO1_AnIn1_subMag (&iedModel_LD1_GGIO1_AnIn1_subMag) +#define IEDMODEL_LD1_GGIO1_AnIn1_subMag_f (&iedModel_LD1_GGIO1_AnIn1_subMag_f) +#define IEDMODEL_LD1_GGIO1_AnIn1_subQ (&iedModel_LD1_GGIO1_AnIn1_subQ) +#define IEDMODEL_LD1_GGIO1_AnIn1_subID (&iedModel_LD1_GGIO1_AnIn1_subID) + +#endif /* STATIC_MODEL_H_ */ + diff --git a/examples/server_example_substitution/substitution_example.icd b/examples/server_example_substitution/substitution_example.icd new file mode 100644 index 000000000..32d62c33c --- /dev/null +++ b/examples/server_example_substitution/substitution_example.icd @@ -0,0 +1,229 @@ + + +
+ + + +
+ + + +
+

1 3 9999 33

+

33

+

00 00 00 01

+

00 01

+

00 01

+

102

+

192.168.62.248

+

255.255.255.0

+

192.168.62.254

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + e1 + e2 + e3 + e4 + e5 + + + normal + high + low + high-high + low-low + + + none + m + kg + s + A + K + mol + cd + deg + rad + sr + Gy + q + °C + Sv + F + C + S + H + V + ohm + J + N + Hz + lx + Lm + Wb + T + W + Pa + + + m/s + m/s² + m³/s + m/m³ + M + kg/m³ + m²/s + W/m K + J/K + ppm + 1/s + rad/s + VA + Watts + VAr + theta + cos(theta) + Vs + + As + + A²t + VAh + Wh + VArh + V/Hz + + + Yocto + Zepto + Atto + Femto + Pico + Nano + Micro + Milli + Centi + Deci + zeroNoValue + Deca + Hecto + Kilo + Mega + Giga + Tera + Petra + Exa + Zetta + Yotta + + +
diff --git a/examples/sv_publisher/sv_publisher_example.c b/examples/sv_publisher/sv_publisher_example.c index 280dfee1b..311b6ff6a 100644 --- a/examples/sv_publisher/sv_publisher_example.c +++ b/examples/sv_publisher/sv_publisher_example.c @@ -35,22 +35,31 @@ main(int argc, char** argv) int float1 = SVPublisher_ASDU_addFLOAT(asdu1); int float2 = SVPublisher_ASDU_addFLOAT(asdu1); + int ts1 = SVPublisher_ASDU_addTimestamp(asdu1); SVPublisher_ASDU asdu2 = SVPublisher_addASDU(svPublisher, "svpub2", NULL, 1); int float3 = SVPublisher_ASDU_addFLOAT(asdu2); int float4 = SVPublisher_ASDU_addFLOAT(asdu2); + int ts2 = SVPublisher_ASDU_addTimestamp(asdu2); SVPublisher_setupComplete(svPublisher); float fVal1 = 1234.5678f; float fVal2 = 0.12345f; - int i; - while (running) { + Timestamp ts; + Timestamp_clearFlags(&ts); + Timestamp_setTimeInMilliseconds(&ts, Hal_getTimeInMs()); + SVPublisher_ASDU_setFLOAT(asdu1, float1, fVal1); SVPublisher_ASDU_setFLOAT(asdu1, float2, fVal2); + SVPublisher_ASDU_setTimestamp(asdu1, ts1, ts); + + SVPublisher_ASDU_setFLOAT(asdu2, float3, fVal1 * 2); + SVPublisher_ASDU_setFLOAT(asdu2, float4, fVal2 * 2); + SVPublisher_ASDU_setTimestamp(asdu2, ts2, ts); SVPublisher_ASDU_increaseSmpCnt(asdu1); SVPublisher_ASDU_increaseSmpCnt(asdu2); diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt new file mode 100644 index 000000000..1441d5ac7 --- /dev/null +++ b/hal/CMakeLists.txt @@ -0,0 +1,160 @@ +cmake_minimum_required(VERSION 2.8) + +# automagically detect if we should cross-compile +if(DEFINED ENV{TOOLCHAIN}) + set(CMAKE_C_COMPILER $ENV{TOOLCHAIN}gcc) + set(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN}g++) + set(CMAKE_AR "$ENV{TOOLCHAIN}ar" CACHE FILEPATH "CW archiver" FORCE) +endif() + +project(hal) + +set(LIBHAL_VERSION_MAJOR "2") +set(LIBHAL_VERSION_MINOR "0") +set(LIBHAL_VERSION_PATCH "0") + +# feature checks +include(CheckLibraryExists) +check_library_exists(rt clock_gettime "time.h" CONFIG_SYSTEM_HAS_CLOCK_GETTIME) + +# check if we are on a little or a big endian +include (TestBigEndian) +test_big_endian(PLATFORM_IS_BIGENDIAN) + +if(WIN32) + +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib") +message("Found winpcap -> compile ethernet HAL layer (required for GOOSE/SV support)") +set(WITH_WPCAP 1) +else() +message("winpcap not found -> skip ethernet HAL layer (no GOOSE/SV support)") +endif() + +endif(WIN32) + +include_directories( + ${CMAKE_CURRENT_LIST_DIR}/inc +) + +set (libhal_linux_SRCS + ${CMAKE_CURRENT_LIST_DIR}/socket/linux/socket_linux.c + ${CMAKE_CURRENT_LIST_DIR}/ethernet/linux/ethernet_linux.c + ${CMAKE_CURRENT_LIST_DIR}/thread/linux/thread_linux.c + ${CMAKE_CURRENT_LIST_DIR}/filesystem/linux/file_provider_linux.c + ${CMAKE_CURRENT_LIST_DIR}/time/unix/time.c + ${CMAKE_CURRENT_LIST_DIR}/serial/linux/serial_port_linux.c + ${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c +) + +set (libhal_windows_SRCS + ${CMAKE_CURRENT_LIST_DIR}/socket/win32/socket_win32.c + ${CMAKE_CURRENT_LIST_DIR}/thread/win32/thread_win32.c + ${CMAKE_CURRENT_LIST_DIR}/filesystem/win32/file_provider_win32.c + ${CMAKE_CURRENT_LIST_DIR}/time/win32/time.c + ${CMAKE_CURRENT_LIST_DIR}/serial/win32/serial_port_win32.c + ${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c +) + +if(WITH_WPCAP) +set (libhal_windows_SRCS ${libhal_windows_SRCS} + ${CMAKE_CURRENT_LIST_DIR}/ethernet/win32/ethernet_win32.c +) +endif(WITH_WPCAP) + +set (libhal_bsd_SRCS + ${CMAKE_CURRENT_LIST_DIR}/socket/bsd/socket_bsd.c + ${CMAKE_CURRENT_LIST_DIR}/ethernet/bsd/ethernet_bsd.c + ${CMAKE_CURRENT_LIST_DIR}/thread/bsd/thread_bsd.c + ${CMAKE_CURRENT_LIST_DIR}/filesystem/linux/file_provider_linux.c + ${CMAKE_CURRENT_LIST_DIR}/time/unix/time.c + ${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c +) + +IF(WIN32) + +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib") +message("Found winpcap -> can compile with GOOSE support") +set(WITH_WPCAP 1) +endif() + +IF(MSVC) +set_source_files_properties(${libhal_windows_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF() + +set (libhal_SRCS + ${libhal_windows_SRCS} +) + +ELSEIF(UNIX) +IF(APPLE) +set (libhal_SRCS + ${libhal_bsd_SRCS} +) +ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") +set (libhal_SRCS + ${libhal_bsd_SRCS}} +) +ELSE() +set (libhal_SRCS + ${libhal_linux_SRCS} +) +ENDIF(APPLE) +ENDIF(WIN32) + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC" ) +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC" ) + +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0) +message("Found mbedtls -> can compile with TLS support") +set(WITH_MBEDTLS 1) +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0) + +if(WITH_MBEDTLS) +include_directories( + ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls + ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0/include +) + +file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0/library/*.c) + +add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h") + +set (libhal_SRCS ${libhal_SRCS} + ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls/tls_mbedtls.c +) + +list (APPEND libhal_SRCS ${tls_SRCS}) + +endif(WITH_MBEDTLS) + +add_library (hal STATIC ${libhal_SRCS}) + +add_library (hal-shared STATIC ${libhal_SRCS}) + +SET_TARGET_PROPERTIES(hal-shared PROPERTIES + COMPILE_FLAGS "-fPIC" +) + +IF(UNIX) + IF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME) + target_link_libraries (hal + -lpthread + -lrt + ) + ELSE () + target_link_libraries (hal + -lpthread + ) + ENDIF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME) +ENDIF(UNIX) +IF(MINGW) + target_link_libraries(hal ws2_32 iphlpapi) +ENDIF(MINGW) + +iF(WITH_WPCAP) +target_link_libraries(hal + ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/lib/wpcap.lib + ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/lib/packet.lib +) +ENDIF(WITH_WPCAP) diff --git a/src/hal/ethernet/bsd/ethernet_bsd.c b/hal/ethernet/bsd/ethernet_bsd.c similarity index 99% rename from src/hal/ethernet/bsd/ethernet_bsd.c rename to hal/ethernet/bsd/ethernet_bsd.c index f35d8b5d4..f0eb48dab 100644 --- a/src/hal/ethernet/bsd/ethernet_bsd.c +++ b/hal/ethernet/bsd/ethernet_bsd.c @@ -35,7 +35,7 @@ #include #include -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" #include "hal_ethernet.h" struct sEthernetSocket { diff --git a/src/hal/ethernet/linux/ethernet_linux.c b/hal/ethernet/linux/ethernet_linux.c similarity index 98% rename from src/hal/ethernet/linux/ethernet_linux.c rename to hal/ethernet/linux/ethernet_linux.c index 6715c526d..36dde5cef 100644 --- a/src/hal/ethernet/linux/ethernet_linux.c +++ b/hal/ethernet/linux/ethernet_linux.c @@ -29,10 +29,11 @@ #include #include #include +#include #include -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" #include "hal_ethernet.h" struct sEthernetSocket { @@ -63,7 +64,9 @@ void EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) { if (self != NULL && sock != NULL) { + int i = self->nhandles++; + self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd)); self->handles[i].fd = sock->rawSocket; @@ -75,7 +78,9 @@ void EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock) { if ((self != NULL) && (sock != NULL)) { - unsigned i; + + int i; + for (i = 0; i < self->nhandles; i++) { if (self->handles[i].fd == sock->rawSocket) { memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1)); diff --git a/src/hal/ethernet/win32/ethernet_win32.c b/hal/ethernet/win32/ethernet_win32.c similarity index 97% rename from src/hal/ethernet/win32/ethernet_win32.c rename to hal/ethernet/win32/ethernet_win32.c index 5e434a076..a005c7c7b 100644 --- a/src/hal/ethernet/win32/ethernet_win32.c +++ b/hal/ethernet/win32/ethernet_win32.c @@ -24,10 +24,10 @@ #include "stack_config.h" #include +#include #include "hal_ethernet.h" - -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" #ifndef DEBUG_HAL_ETHERNET #define DEBUG_HAL_ETHERNET 1 @@ -138,7 +138,9 @@ void EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) { if (self != NULL && sock != NULL) { + int i = self->nhandles++; + self->handles = (HANDLE *) realloc(self->handles, self->nhandles * sizeof(HANDLE)); self->handles[i] = pcap_getevent(sock->rawSocket); @@ -151,7 +153,8 @@ EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock if ((self != NULL) && (sock != NULL)) { HANDLE h = pcap_getevent(sock->rawSocket); - unsigned i; + int i; + for (i = 0; i < self->nhandles; i++) { if (self->handles[i] == h) { memmove(&self->handles[i], &self->handles[i+1], sizeof(HANDLE) * (self->nhandles - i - 1)); @@ -212,7 +215,7 @@ getInterfaceName(int interfaceIndex) interfaceName = (char*) malloc(strlen(device->name) + 1); strcpy(interfaceName, device->name); if (DEBUG_HAL_ETHERNET) - printf("Use interface (%s)\n", interfaceName); + printf("Use interface (%s)\n", interfaceName); ifaceFound = true; break; } @@ -223,7 +226,7 @@ getInterfaceName(int interfaceIndex) if (!ifaceFound) { if (DEBUG_HAL_ETHERNET) - printf("No ethernet interfaces found! Make sure WinPcap is installed.\n"); + printf("No ethernet interfaces found! Make sure WinPcap is installed.\n"); return NULL; } @@ -266,7 +269,7 @@ getAdapterMacAddress(char* pcapAdapterName, uint8_t* macAddress) if (strstr(pcapAdapterName, pAddress->AdapterName) != 0) { if (DEBUG_HAL_ETHERNET) - printf(" requested found!"); + printf(" requested found!"); for (i = 0; i < (int) addressLength; i++) { macAddress[i] = pAddress->PhysicalAddress[i]; diff --git a/src/hal/filesystem/linux/file_provider_linux.c b/hal/filesystem/linux/file_provider_linux.c similarity index 96% rename from src/hal/filesystem/linux/file_provider_linux.c rename to hal/filesystem/linux/file_provider_linux.c index 235c59694..e9f03e87f 100644 --- a/src/hal/filesystem/linux/file_provider_linux.c +++ b/hal/filesystem/linux/file_provider_linux.c @@ -1,7 +1,7 @@ /* * file_provider_linux.c * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -23,16 +23,15 @@ #include #include - +#include +#include +#include #include #include #include -#include "libiec61850_platform_includes.h" - #include "hal_filesystem.h" - -#include "stack_config.h" +#include "lib_memory.h" struct sDirectoryHandle { DIR* handle; diff --git a/src/hal/filesystem/win32/file_provider_win32.c b/hal/filesystem/win32/file_provider_win32.c similarity index 98% rename from src/hal/filesystem/win32/file_provider_win32.c rename to hal/filesystem/win32/file_provider_win32.c index 2f531e1d8..1a08e8d19 100644 --- a/src/hal/filesystem/win32/file_provider_win32.c +++ b/hal/filesystem/win32/file_provider_win32.c @@ -24,14 +24,15 @@ #include #include #include +#include #include #include #include "hal_filesystem.h" -#include "libiec61850_platform_includes.h" -#include "stack_config.h" +#include "platform_endian.h" +#include "lib_memory.h" #include diff --git a/src/hal/inc/hal_ethernet.h b/hal/inc/hal_ethernet.h similarity index 100% rename from src/hal/inc/hal_ethernet.h rename to hal/inc/hal_ethernet.h diff --git a/src/hal/inc/hal_filesystem.h b/hal/inc/hal_filesystem.h similarity index 92% rename from src/hal/inc/hal_filesystem.h rename to hal/inc/hal_filesystem.h index 4248810a8..b22ca8460 100644 --- a/src/hal/inc/hal_filesystem.h +++ b/hal/inc/hal_filesystem.h @@ -166,17 +166,6 @@ FileSystem_readDirectory(DirectoryHandle directory, bool* isDirectory); void FileSystem_closeDirectory(DirectoryHandle directory); -/** - * \brief set local file system base path for the MMS VMD - * - * NOTE: the meaning of this functions is platform specific. It was introduced to - * simplify the configuration of the VMD base path at runtime. It may not be supported - * on all platform. Also it is not called by the MMS protocol stack. - * - * \param basePath the local base path of the MMS VMD - */ -void -FileSystem_setBasePath(char* basePath); /*! @} */ diff --git a/hal/inc/hal_serial.h b/hal/inc/hal_serial.h new file mode 100644 index 000000000..b28804d5f --- /dev/null +++ b/hal/inc/hal_serial.h @@ -0,0 +1,155 @@ +/* + * hal_serial.h + * + * Copyright 2017 MZ Automation GmbH + * + * This file is part of lib60870-C + * + * lib60870-C is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * lib60870-C is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with lib60870-C. If not, see . + * + * See COPYING file for the complete license text. + */ + +#ifndef SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_ +#define SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file hal_serial.h + * \brief Abstraction layer for serial ports. + * Has to be implemented for the serial link layer of CS 101. + */ + +/*! \addtogroup hal Platform (Hardware/OS) abstraction layer + * + * @{ + */ + +/** + * @defgroup HAL_SERIAL Access to serial interfaces + * + * Serial interface abstraction layer. This functions have to be implemented to + * port lib60870 to new platforms when the serial link layers are required. + * + * @{ + */ + +typedef struct sSerialPort* SerialPort; + +typedef enum { + SERIAL_PORT_ERROR_NONE = 0, + SERIAL_PORT_ERROR_INVALID_ARGUMENT = 1, + SERIAL_PORT_ERROR_INVALID_BAUDRATE = 2, + SERIAL_PORT_ERROR_OPEN_FAILED = 3, + SERIAL_PORT_ERROR_UNKNOWN = 99 +} SerialPortError; + +/** + * \brief Create a new SerialPort instance + * + * \param interfaceName identifier or name of the serial interface (e.g. "/dev/ttyS1" or "COM4") + * \param baudRate the baud rate in baud (e.g. 9600) + * \param dataBits the number of data bits (usually 8) + * \param parity defines what kind of parity to use ('E' - even parity, 'O' - odd parity, 'N' - no parity) + * \param stopBits the number of stop buts (usually 1) + * + * \return the new SerialPort instance + */ +SerialPort +SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, char parity, uint8_t stopBits); + +/** + * \brief Destroy the SerialPort instance and release all resources + */ +void +SerialPort_destroy(SerialPort self); + +/** + * \brief Open the serial interface + * + * \return true in case of success, false otherwise (use \ref SerialPort_getLastError for a detailed error code) + */ +bool +SerialPort_open(SerialPort self); + +/** + * \brief Close (release) the serial interface + */ +void +SerialPort_close(SerialPort self); + +/** + * \brief Get the baudrate used by the serial interface + * + * \return the baud rate in baud + */ +int +SerialPort_getBaudRate(SerialPort self); + +/** + * \brief Set the timeout used for message reception + * + * \param timeout the timeout value in ms. + */ +void +SerialPort_setTimeout(SerialPort self, int timeout); + +/** + * \brief Discard all data in the input buffer of the serial interface + */ +void +SerialPort_discardInBuffer(SerialPort self); + +/** + * \brief Read a byte from the interface + * + * \return number of read bytes of -1 in case of an error + */ +int +SerialPort_readByte(SerialPort self); + +/** + * \brief Write the number of bytes from the buffer to the serial interface + * + * \param buffer the buffer containing the data to write + * \param startPos start position in the buffer of the data to write + * \param numberOfBytes number of bytes to write + * + * \return number of bytes written, or -1 in case of an error + */ +int +SerialPort_write(SerialPort self, uint8_t* buffer, int startPos, int numberOfBytes); + +/** + * \brief Get the error code of the last operation + */ +SerialPortError +SerialPort_getLastError(SerialPort self); + +/*! @} */ + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /* SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_ */ diff --git a/src/hal/inc/hal_socket.h b/hal/inc/hal_socket.h similarity index 74% rename from src/hal/inc/hal_socket.h rename to hal/inc/hal_socket.h index 3fddd3f24..e1d02bb6c 100644 --- a/src/hal/inc/hal_socket.h +++ b/hal/inc/hal_socket.h @@ -1,7 +1,7 @@ /* * socket_hal.h * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -27,11 +27,21 @@ #include #include +/** + * \file hal_socket.h + * \brief Abstraction layer TCP/IP sockets + * Has to be implemented for CS 104 TCP/IP. + */ + #ifdef __cplusplus extern "C" { #endif -/*! \addtogroup hal Platform (Hardware/OS) abstraction layer +/*! \defgroup hal Platform (Hardware/OS) abstraction layer + * + * Platform abstraction layer. These functions have to be implemented when the library is + * to be ported to new platforms. It might not be required to implement all interfaces + * depending on the required library features. * * @{ */ @@ -40,7 +50,7 @@ extern "C" { * @defgroup HAL_SOCKET Interface to the TCP/IP stack (abstract socket layer) * * Thread and Socket abstraction layer. This functions have to be implemented to - * port libIEC61850 to a new hardware/OS platform. + * port lib60870 to a new hardware/OS platform when TCP/IP is required. * * @{ */ @@ -63,7 +73,13 @@ HandleSet Handleset_new(void); /** - * \brief add a socket to an existing handle set + * \brief Reset the handle set for reuse + */ +void +Handleset_reset(HandleSet self); + +/** + * \brief add a soecket to an existing handle set * * \param self the HandleSet instance * \param sock the socket to add @@ -75,7 +91,10 @@ Handleset_addSocket(HandleSet self, const Socket sock); * \brief wait for a socket to become ready * * This function is corresponding to the BSD socket select function. - * The function will return after \p timeoutMs ms if no data is pending. + * It returns the number of sockets on which data is pending or 0 if no data is pending + * on any of the monitored connections. The function will return after "timeout" ms if no + * data is pending. + * The function shall return -1 if a socket error occures. * * \param self the HandleSet instance * \param timeout in milliseconds (ms) @@ -128,9 +147,21 @@ ServerSocket_listen(ServerSocket self); Socket ServerSocket_accept(ServerSocket self); +/** + * \brief active TCP keep alive for socket and set keep alive parameters + * + * NOTE: implementation is mandatory for IEC 61850 MMS + * + * \param self server socket instance + * \param idleTime time (in s) between last received message and first keep alive message + * \param interval time (in s) between subsequent keep alive messages if no ACK received + * \param count number of not missing keep alive ACKs until socket is considered dead + */ +void +Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count); /** - * \brief set the maximum number of pending connection in the queue + * \brief set the maximum number of pending connections in the queue * * Implementation of this function is OPTIONAL. * @@ -188,7 +219,7 @@ Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs); * \param address the IP address or hostname as C string * \param port the TCP port of the application to connect to * - * \return a new client socket instance. + * \return true if the connection was established successfully, false otherwise */ bool Socket_connect(Socket self, const char* address, int port); @@ -229,7 +260,7 @@ Socket_write(Socket self, uint8_t* buf, int size); * * The peer address has to be returned as * - * Implementation of this function is MANDATORY + * Implementation of this function is MANDATORY (libiec61850) * * \param self the client, connection or server socket instance * @@ -238,6 +269,23 @@ Socket_write(Socket self, uint8_t* buf, int size); char* Socket_getPeerAddress(Socket self); +/** + * \brief Get the address of the peer application (IP address and port number) + * + * The peer address has to be returned as + * + * Implementation of this function is MANDATORY (lib60870) + * + * \param self the client, connection or server socket instance + * \param peerAddressString a string to store the peer address (the string should have space + * for at least 60 characters) + * + * \return the IP address and port number as strings separated by the ':' character. If the + * address is an IPv6 address the IP part is encapsulated in square brackets. + */ +char* +Socket_getPeerAddressStatic(Socket self, char* peerAddressString); + /** * \brief destroy a socket (close the socket if a connection is established) * diff --git a/src/hal/inc/hal_thread.h b/hal/inc/hal_thread.h similarity index 96% rename from src/hal/inc/hal_thread.h rename to hal/inc/hal_thread.h index 7d719f95a..7dbb390af 100644 --- a/src/hal/inc/hal_thread.h +++ b/hal/inc/hal_thread.h @@ -32,6 +32,10 @@ extern "C" { #endif +/** + * \file hal_thread.h + * \brief Abstraction layer for threading and synchronization + */ /*! \addtogroup hal * diff --git a/src/hal/inc/hal_time.h b/hal/inc/hal_time.h similarity index 90% rename from src/hal/inc/hal_time.h rename to hal/inc/hal_time.h index 50fcf505e..5ac8080e7 100644 --- a/src/hal/inc/hal_time.h +++ b/hal/inc/hal_time.h @@ -28,6 +28,12 @@ extern "C" { #endif +#include + +/** + * \file hal_time.h + * \brief Abstraction layer for system time access + */ /*! \addtogroup hal * @@ -48,7 +54,8 @@ extern "C" { * * \return the system time with millisecond resolution. */ -uint64_t Hal_getTimeInMs(void); +uint64_t +Hal_getTimeInMs(void); /*! @} */ diff --git a/src/common/inc/lib_memory.h b/hal/inc/lib_memory.h similarity index 99% rename from src/common/inc/lib_memory.h rename to hal/inc/lib_memory.h index 506a24ca0..95314af02 100644 --- a/src/common/inc/lib_memory.h +++ b/hal/inc/lib_memory.h @@ -1,5 +1,5 @@ /* - * memory.h + * lib_memory.h * * Copyright 2014 Michael Zillgith * diff --git a/src/hal/inc/platform_endian.h b/hal/inc/platform_endian.h similarity index 94% rename from src/hal/inc/platform_endian.h rename to hal/inc/platform_endian.h index 9bc7a5af0..76c410af5 100644 --- a/src/hal/inc/platform_endian.h +++ b/hal/inc/platform_endian.h @@ -1,7 +1,7 @@ /* - * endian.h + * platform_endian.h * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,8 +24,6 @@ #ifndef ENDIAN_H_ #define ENDIAN_H_ -#include "stack_config.h" - #ifndef PLATFORM_IS_BIGENDIAN #ifdef __GNUC__ #ifdef __BYTE_ORDER__ diff --git a/src/tls/tls_api.h b/hal/inc/tls_config.h similarity index 58% rename from src/tls/tls_api.h rename to hal/inc/tls_config.h index 4b0cde9c1..ec70fe9bf 100644 --- a/src/tls/tls_api.h +++ b/hal/inc/tls_config.h @@ -1,29 +1,41 @@ /* - * tls_api.h + * tls_config.h * - * TLS API for TCP/IP protocol stacks + * TLS Configuration API for protocol stacks using TCP/IP * - * Copyright 2017 MZ Automation GmbH + * Copyright 2017-2018 MZ Automation GmbH * - * Abstraction layer for different TLS implementations - * - * Implementation connects the TLS API layer with the socket API layer - * and performs all TLS tasks like handshake, encryption/decryption. + * Abstraction layer for configuration of different TLS implementations * */ -#ifndef SRC_TLS_TLS_API_H_ -#define SRC_TLS_TLS_API_H_ - -#include "hal_socket.h" +#ifndef SRC_TLS_CONFIG_H_ +#define SRC_TLS_CONFIG_H_ #ifdef __cplusplus extern "C" { #endif -typedef struct sTLSConfiguration* TLSConfiguration; +#include +#include + +/** + * \file tls_config.h + * \brief TLS API functions + */ -typedef struct sTLSSocket* TLSSocket; +/*! \addtogroup hal Platform (Hardware/OS) abstraction layer + * + * @{ + */ + +/** + * @defgroup TLS_CONFIG_API TLS configuration + * + * @{ + */ + +typedef struct sTLSConfiguration* TLSConfiguration; /** * \brief Create a new \ref TLSConfiguration object to represent TLS configuration and certificates @@ -108,64 +120,12 @@ TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs); void TLSConfiguration_destroy(TLSConfiguration self); -TLSSocket -TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert); - -/** - * \brief Perform a new TLS handshake/session renegotiation - */ -bool -TLSSocket_performHandshake(TLSSocket self); - -/** - * \brief Access the certificate used by the peer - * - * \param[out] certSize the size of the certificate in bytes - * - * \return the certificate byte buffer - */ -uint8_t* -TLSSocket_getPeerCertificate(TLSSocket self, int* certSize); - -/** - * \brief read from socket to local buffer (non-blocking) - * - * The function shall return immediately if no data is available. In this case - * the function returns 0. If an error happens the function shall return -1. - * - * Implementation of this function is MANDATORY - * - * NOTE: The behaviour of this function changed with version 0.8! - * - * \param self the client, connection or server socket instance - * \param buf the buffer where the read bytes are copied to - * \param size the maximum number of bytes to read (size of the provided buffer) - * - * \return the number of bytes read or -1 if an error occurred - */ -int -TLSSocket_read(TLSSocket self, uint8_t* buf, int size); +/** @} */ -/** - * \brief send a message through the socket - * - * Implementation of this function is MANDATORY - * - * \param self client, connection or server socket instance - * - * \return number of bytes transmitted of -1 in case of an error - */ -int -TLSSocket_write(TLSSocket self, uint8_t* buf, int size); - -/** - * \brief Close the TLS socket and release all resources - */ -void -TLSSocket_close(TLSSocket self); +/** @} */ #ifdef __cplusplus } #endif -#endif /* SRC_TLS_TLS_API_H_ */ +#endif /* SRC_TLS_CONFIG_H_ */ diff --git a/hal/inc/tls_socket.h b/hal/inc/tls_socket.h new file mode 100644 index 000000000..553cb82be --- /dev/null +++ b/hal/inc/tls_socket.h @@ -0,0 +1,122 @@ +/* + * tls_socket.h + * + * TLS socket API for protocol libraries using TCP/IP + * + * Copyright 2017-2018 Michael Zillgith, MZ Automation GmbH + * + * Abstraction layer for different TLS implementations + * + * The implementation has to connect the TLS API layer with the socket API layer + * and perform all TLS tasks like handshake, encryption/decryption. + * + */ + +#ifndef SRC_TLS_SOCKET_API_H_ +#define SRC_TLS_SOCKET_API_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file tls_socket.h + * \brief Abstraction layer for different TLS implementations. + * + * The implementation has to connect the TLS API layer with the socket API layer + * and perform all TLS tasks like handshake, encryption/decryption. + */ + +/*! \addtogroup hal Platform (Hardware/OS) abstraction layer + * + * @{ + */ + +/** + * @defgroup HAL_TLS_SOCKET Abstraction layer for different TLS implementations. + * + * The implementation has to connect the TLS API layer with the socket API layer + * and perform all TLS tasks like handshake, encryption/decryption. + * + * @{ + */ + +#include +#include "tls_config.h" +#include "hal_socket.h" + +typedef struct sTLSSocket* TLSSocket; + +/** + * \brief This function create a new TLSSocket instance using the given Socket instance + * + * NOTE: This function also has to perform the TLS handshake + * + * \param socket the socket instance to use for the TLS connection + * \param configuration the TLS configuration object to use + * \param storeClientCert if true, the client certificate will be stored + * for later access by \ref TLSSocket_getPeerCertificate + * + * \return new TLS connection instance + */ +TLSSocket +TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert); + +/** + * \brief Perform a new TLS handshake/session renegotiation + */ +bool +TLSSocket_performHandshake(TLSSocket self); + +/** + * \brief Access the certificate used by the peer + * + * \param[out] certSize the size of the certificate in bytes + * + * \return the certificate byte buffer + */ +uint8_t* +TLSSocket_getPeerCertificate(TLSSocket self, int* certSize); + +/** + * \brief read from socket to local buffer (non-blocking) + * + * The function shall return immediately if no data is available. In this case + * the function returns 0. If an error happens the function shall return -1. + * + * \param self the client, connection or server socket instance + * \param buf the buffer where the read bytes are copied to + * \param size the maximum number of bytes to read (size of the provided buffer) + * + * \return the number of bytes read or -1 if an error occurred + */ +int +TLSSocket_read(TLSSocket self, uint8_t* buf, int size); + +/** + * \brief send a message through the socket + * + * Implementation of this function is MANDATORY + * + * \param self client, connection or server socket instance + * + * \return number of bytes transmitted of -1 in case of an error + */ +int +TLSSocket_write(TLSSocket self, uint8_t* buf, int size); + +/** + * \brief Closes the TLS connection and released all resources + */ +void +TLSSocket_close(TLSSocket self); + +/*! @} */ + +/*! @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_TLS_SOCKET_API_H_ */ diff --git a/src/common/lib_memory.c b/hal/memory/lib_memory.c similarity index 95% rename from src/common/lib_memory.c rename to hal/memory/lib_memory.c index 0a4106b35..623ba2127 100644 --- a/src/common/lib_memory.c +++ b/hal/memory/lib_memory.c @@ -1,7 +1,7 @@ /* * lib_memory.c * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -21,7 +21,8 @@ * See COPYING file for the complete license text. */ -#include "libiec61850_platform_includes.h" +#include +#include "lib_memory.h" static MemoryExceptionHandler exceptionHandler = NULL; static void* exceptionHandlerParameter = NULL; diff --git a/hal/serial/linux/serial_port_linux.c b/hal/serial/linux/serial_port_linux.c new file mode 100644 index 000000000..e6308c054 --- /dev/null +++ b/hal/serial/linux/serial_port_linux.c @@ -0,0 +1,277 @@ +/* + * serial_port_linux.c + * + * Copyright 2017 MZ Automation GmbH + * + * This file is part of lib60870-C + * + * lib60870-C is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * lib60870-C is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with lib60870-C. If not, see . + * + * See COPYING file for the complete license text. + */ + +#include "lib_memory.h" + +#include +#include +#include +#include +#include +#include + +#include "hal_serial.h" +#include "hal_time.h" + +struct sSerialPort { + char interfaceName[100]; + int fd; + int baudRate; + uint8_t dataBits; + char parity; + uint8_t stopBits; + uint64_t lastSentTime; + struct timeval timeout; + SerialPortError lastError; +}; + + +SerialPort +SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, char parity, uint8_t stopBits) +{ + SerialPort self = (SerialPort) GLOBAL_MALLOC(sizeof(struct sSerialPort)); + + if (self != NULL) { + self->fd = -1; + self->baudRate = baudRate; + self->dataBits = dataBits; + self->stopBits = stopBits; + self->parity = parity; + self->lastSentTime = 0; + self->timeout.tv_sec = 0; + self->timeout.tv_usec = 100000; /* 100 ms */ + strncpy(self->interfaceName, interfaceName, 100); + self->lastError = SERIAL_PORT_ERROR_NONE; + } + + return self; +} + +void +SerialPort_destroy(SerialPort self) +{ + if (self != NULL) { + GLOBAL_FREEMEM(self); + } +} + +bool +SerialPort_open(SerialPort self) +{ + self->fd = open(self->interfaceName, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL); + + if (self->fd == -1) { + self->lastError = SERIAL_PORT_ERROR_OPEN_FAILED; + return false; + } + + struct termios tios; + speed_t baudrate; + + tcgetattr(self->fd, &tios); + + switch (self->baudRate) { + case 110: + baudrate = B110; + break; + case 300: + baudrate = B300; + break; + case 600: + baudrate = B600; + break; + case 1200: + baudrate = B1200; + break; + case 2400: + baudrate = B2400; + break; + case 4800: + baudrate = B4800; + break; + case 9600: + baudrate = B9600; + break; + case 19200: + baudrate = B19200; + break; + case 38400: + baudrate = B38400; + break; + case 57600: + baudrate = B57600; + break; + case 115200: + baudrate = B115200; + break; + default: + baudrate = B9600; + self->lastError = SERIAL_PORT_ERROR_INVALID_BAUDRATE; + } + + /* Set baud rate */ + if ((cfsetispeed(&tios, baudrate) < 0) || (cfsetospeed(&tios, baudrate) < 0)) { + close(self->fd); + self->fd = -1; + self->lastError = SERIAL_PORT_ERROR_INVALID_BAUDRATE; + return false; + } + + tios.c_cflag |= (CREAD | CLOCAL); + + /* Set data bits (5/6/7/8) */ + tios.c_cflag &= ~CSIZE; + switch (self->dataBits) { + case 5: + tios.c_cflag |= CS5; + break; + case 6: + tios.c_cflag |= CS6; + break; + case 7: + tios.c_cflag |= CS7; + break; + case 8: + default: + tios.c_cflag |= CS8; + break; + } + + /* Set stop bits (1/2) */ + if (self->stopBits == 1) + tios.c_cflag &=~ CSTOPB; + else /* 2 */ + tios.c_cflag |= CSTOPB; + + if (self->parity == 'N') { + tios.c_cflag &=~ PARENB; + } else if (self->parity == 'E') { + tios.c_cflag |= PARENB; + tios.c_cflag &=~ PARODD; + } else { /* 'O' */ + tios.c_cflag |= PARENB; + tios.c_cflag |= PARODD; + } + + tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + if (self->parity == 'N') { + tios.c_iflag &= ~INPCK; + } else { + tios.c_iflag |= INPCK; + } + + tios.c_iflag &= ~(IXON | IXOFF | IXANY); + tios.c_iflag |= IGNBRK; /* Set ignore break to allow 0xff characters */ + tios.c_iflag |= IGNPAR; + tios.c_oflag &=~ OPOST; + + tios.c_cc[VMIN] = 0; + tios.c_cc[VTIME] = 0; + + if (tcsetattr(self->fd, TCSANOW, &tios) < 0) { + close(self->fd); + self->fd = -1; + self->lastError = SERIAL_PORT_ERROR_INVALID_ARGUMENT; + + return false; + } + + return true; +} + +void +SerialPort_close(SerialPort self) +{ + if (self->fd != -1) { + close(self->fd); + self->fd = 0; + } +} + +int +SerialPort_getBaudRate(SerialPort self) +{ + return self->baudRate; +} + +void +SerialPort_discardInBuffer(SerialPort self) +{ + tcflush(self->fd, TCIOFLUSH); +} + +void +SerialPort_setTimeout(SerialPort self, int timeout) +{ + self->timeout.tv_sec = timeout / 1000; + self->timeout.tv_usec = (timeout % 1000) * 1000; +} + +SerialPortError +SerialPort_getLastError(SerialPort self) +{ + return self->lastError; +} + +int +SerialPort_readByte(SerialPort self) +{ + uint8_t buf[1]; + fd_set set; + + self->lastError = SERIAL_PORT_ERROR_NONE; + + FD_ZERO(&set); + FD_SET(self->fd, &set); + + int ret = select(self->fd + 1, &set, NULL, NULL, &(self->timeout)); + + if (ret == -1) { + self->lastError = SERIAL_PORT_ERROR_UNKNOWN; + return -1; + } + else if (ret == 0) + return -1; + else { + read(self->fd, (char*) buf, 1); + + return (int) buf[0]; + } +} + +int +SerialPort_write(SerialPort self, uint8_t* buffer, int startPos, int bufSize) +{ + //TODO assure minimum line idle time + + self->lastError = SERIAL_PORT_ERROR_NONE; + + ssize_t result = write(self->fd, buffer + startPos, bufSize); + + tcdrain(self->fd); + + self->lastSentTime = Hal_getTimeInMs(); + + return result; +} diff --git a/hal/serial/win32/serial_port_win32.c b/hal/serial/win32/serial_port_win32.c new file mode 100644 index 000000000..19eb30bf9 --- /dev/null +++ b/hal/serial/win32/serial_port_win32.c @@ -0,0 +1,283 @@ +/* +* serial_port_win32.c +* +* Copyright 2017 MZ Automation GmbH +* +* This file is part of lib60870-C +* +* lib60870-C is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* lib60870-C is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with lib60870-C. If not, see . +* +* See COPYING file for the complete license text. +*/ + +#include +#include + +#include + +#include "lib_memory.h" + +#include "hal_serial.h" +#include "hal_time.h" + +struct sSerialPort { + char interfaceName[100]; + HANDLE comPort; + int baudRate; + uint8_t dataBits; + char parity; + uint8_t stopBits; + uint64_t lastSentTime; + int timeout; + SerialPortError lastError; +}; + +SerialPort +SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, char parity, uint8_t stopBits) +{ + SerialPort self = (SerialPort)GLOBAL_MALLOC(sizeof(struct sSerialPort)); + + if (self != NULL) { + self->comPort = NULL; + self->baudRate = baudRate; + self->dataBits = dataBits; + self->stopBits = stopBits; + self->parity = parity; + self->lastSentTime = 0; + self->timeout = 100; /* 100 ms */ + strncpy(self->interfaceName, interfaceName, 100); + self->lastError = SERIAL_PORT_ERROR_NONE; + } + + return self; +} + +void +SerialPort_destroy(SerialPort self) +{ + if (self != NULL) { + + if (self->comPort != INVALID_HANDLE_VALUE) + SerialPort_close(self); + + GLOBAL_FREEMEM(self); + } +} + +bool +SerialPort_open(SerialPort self) +{ + self->comPort = CreateFile(self->interfaceName, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + + + if (self->comPort == INVALID_HANDLE_VALUE) { + self->lastError = SERIAL_PORT_ERROR_OPEN_FAILED; + return false; + } + + DCB _serialParams = { 0 }; + _serialParams.DCBlength = sizeof(DCB); + + LPDCB serialParams = &_serialParams; + + BOOL status = GetCommState(self->comPort, serialParams); + + if (status == false) { + self->lastError = SERIAL_PORT_ERROR_UNKNOWN; + goto exit_error; + } + + /* set baud rate */ + switch (self->baudRate) { + case 110: + serialParams->BaudRate = CBR_110; + break; + case 300: + serialParams->BaudRate = CBR_300; + break; + case 600: + serialParams->BaudRate = CBR_600; + break; + case 1200: + serialParams->BaudRate = CBR_1200; + break; + case 2400: + serialParams->BaudRate = CBR_2400; + break; + case 4800: + serialParams->BaudRate = CBR_4800; + break; + case 9600: + serialParams->BaudRate = CBR_9600; + break; + case 19200: + serialParams->BaudRate = CBR_19200; + break; + case 38400: + serialParams->BaudRate = CBR_38400; + break; + case 57600: + serialParams->BaudRate = CBR_57600; + break; + case 115200: + serialParams->BaudRate = CBR_115200; + break; + default: + serialParams->BaudRate = CBR_9600; + self->lastError = SERIAL_PORT_ERROR_INVALID_BAUDRATE; + } + + /* Set data bits (5/6/7/8) */ + serialParams->ByteSize = self->dataBits; + + /* Set stop bits (1/2) */ + if (self->stopBits == 1) + serialParams->StopBits = ONESTOPBIT; + else /* 2 */ + serialParams->StopBits = TWOSTOPBITS; + + if (self->parity == 'N') + serialParams->Parity = NOPARITY; + else if (self->parity == 'E') + serialParams->Parity = EVENPARITY; + else /* 'O' */ + serialParams->Parity = ODDPARITY; + + status = SetCommState(self->comPort, serialParams); + + if (status == false) { + self->lastError = SERIAL_PORT_ERROR_INVALID_ARGUMENT; + goto exit_error; + } + + COMMTIMEOUTS timeouts = { 0 }; + + timeouts.ReadIntervalTimeout = 100; + timeouts.ReadTotalTimeoutConstant = 50; + timeouts.ReadTotalTimeoutMultiplier = 10; + timeouts.WriteTotalTimeoutConstant = 100; + timeouts.WriteTotalTimeoutMultiplier = 10; + + status = SetCommTimeouts(self->comPort, &timeouts); + + if (status == false) { + self->lastError = SERIAL_PORT_ERROR_UNKNOWN; + goto exit_error; + } + + status = SetCommMask(self->comPort, EV_RXCHAR); + + if (status == false) { + printf("SetCommMask failed!\n"); + self->lastError = SERIAL_PORT_ERROR_UNKNOWN; + goto exit_error; + } + + self->lastError = SERIAL_PORT_ERROR_NONE; + + return true; + +exit_error: + + if (self->comPort != INVALID_HANDLE_VALUE) { + CloseHandle(self->comPort); + self->comPort = INVALID_HANDLE_VALUE; + } + + return false; +} + + +void +SerialPort_close(SerialPort self) +{ + if (self->comPort != INVALID_HANDLE_VALUE) { + CloseHandle(self->comPort); + self->comPort = INVALID_HANDLE_VALUE; + } +} + +int +SerialPort_getBaudRate(SerialPort self) +{ + return self->baudRate; +} + +void +SerialPort_discardInBuffer(SerialPort self) +{ + PurgeComm(self->comPort, PURGE_RXCLEAR | PURGE_TXCLEAR); +} + +void +SerialPort_setTimeout(SerialPort self, int timeout) +{ + self->timeout = timeout; +} + +SerialPortError +SerialPort_getLastError(SerialPort self) +{ + return self->lastError; +} + +int +SerialPort_readByte(SerialPort self) +{ + uint8_t buf[1]; + + DWORD bytesRead = 0; + + BOOL status = ReadFile(self->comPort, buf, 1, &bytesRead, NULL); + + if (status == false) { + self->lastError = SERIAL_PORT_ERROR_UNKNOWN; + return -1; + } + + self->lastError = SERIAL_PORT_ERROR_NONE; + + if (bytesRead == 0) + return -1; + else + return (int) buf[0]; +} + +int +SerialPort_write(SerialPort self, uint8_t* buffer, int startPos, int bufSize) +{ + //TODO assure minimum line idle time + + self->lastError = SERIAL_PORT_ERROR_NONE; + + DWORD numberOfBytesWritten; + + BOOL status = WriteFile(self->comPort, buffer + startPos, bufSize, &numberOfBytesWritten, NULL); + + if (status == false) { + self->lastError = SERIAL_PORT_ERROR_UNKNOWN; + return -1; + } + + status = FlushFileBuffers(self->comPort); + + if (status == false) { + printf("FlushFileBuffers failed!\n"); + } + + self->lastSentTime = Hal_getTimeInMs(); + + return (int) numberOfBytesWritten; +} diff --git a/src/hal/socket/bsd/socket_bsd.c b/hal/socket/bsd/socket_bsd.c similarity index 82% rename from src/hal/socket/bsd/socket_bsd.c rename to hal/socket/bsd/socket_bsd.c index 800ee4269..18079db7a 100644 --- a/src/hal/socket/bsd/socket_bsd.c +++ b/hal/socket/bsd/socket_bsd.c @@ -1,7 +1,8 @@ /* * socket_bsd.c * - * Copyright 2013, 2014 Michael Zillgith, contributions by Michael Clausen (School of engineering Valais). + * Copyright 2013-2018 Michael Zillgith + * contributions by Michael Clausen (School of engineering Valais). * * This file is part of libIEC61850. * @@ -36,8 +37,7 @@ #include // required for TCP keepalive #include "hal_thread.h" - -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" #ifndef DEBUG_SOCKET #define DEBUG_SOCKET 0 @@ -70,6 +70,14 @@ Handleset_new(void) return result; } +void +Handleset_reset(HandleSet self) +{ + FD_ZERO(&self->handles); + self->maxHandle = -1; +} + + void Handleset_addSocket(HandleSet self, const Socket sock) { @@ -105,24 +113,24 @@ Handleset_destroy(HandleSet self) GLOBAL_FREEMEM(self); } -static void -activateKeepAlive(int sd) +void +Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count) { #if defined SO_KEEPALIVE int optval; socklen_t optlen = sizeof(optval); - optval = CONFIG_TCP_KEEPALIVE_IDLE; - setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); + optval = idleTime; + setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); optval = 1; - setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, optlen); + setsockopt(self->fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, optlen); #if defined TCP_KEEPCNT - optval = CONFIG_TCP_KEEPALIVE_INTERVAL; - setsockopt(sd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen); + optval = interval; + setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen); - optval = CONFIG_TCP_KEEPALIVE_CNT; - setsockopt(sd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen); + optval = count; + setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen); #endif /* TCP_KEEPCNT */ #endif /* SO_KEEPALIVE */ @@ -186,10 +194,6 @@ TcpServerSocket_create(const char* address, int port) return NULL ; } -#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1 - activateKeepAlive(fd); -#endif - setSocketNonBlocking((Socket) serverSocket); } @@ -284,10 +288,6 @@ Socket_connect(Socket self, const char* address, int port) self->fd = socket(AF_INET, SOCK_STREAM, 0); -#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1 - activateKeepAlive(self->fd); -#endif - fd_set fdSet; FD_ZERO(&fdSet); FD_SET(self->fd, &fdSet); @@ -348,11 +348,45 @@ Socket_getPeerAddress(Socket self) return clientConnection; } +char* +Socket_getPeerAddressStatic(Socket self, char* peerAddressString) +{ + struct sockaddr_storage addr; + socklen_t addrLen = sizeof(addr); + + getpeername(self->fd, (struct sockaddr*) &addr, &addrLen); + + char addrString[INET6_ADDRSTRLEN + 7]; + int port; + + bool isIPv6; + + if (addr.ss_family == AF_INET) { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr; + port = ntohs(ipv4Addr->sin_port); + inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); + isIPv6 = false; + } + else if (addr.ss_family == AF_INET6) { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr; + port = ntohs(ipv6Addr->sin6_port); + inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); + isIPv6 = true; + } + else + return NULL ; + + if (isIPv6) + sprintf(peerAddressString, "[%s]:%i", addrString, port); + else + sprintf(peerAddressString, "%s:%i", addrString, port); + + return peerAddressString; +} + int Socket_read(Socket self, uint8_t* buf, int size) { - assert(self != NULL); - if (self->fd == -1) return -1; diff --git a/src/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c similarity index 82% rename from src/hal/socket/linux/socket_linux.c rename to hal/socket/linux/socket_linux.c index cbf6c25d6..69fca6a68 100644 --- a/src/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -1,7 +1,7 @@ /* * socket_linux.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,20 +24,19 @@ #include "hal_socket.h" #include #include +#include #include #include #include #include #include #include - +#include #include - #include // required for TCP keepalive #include "hal_thread.h" - -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" #ifndef DEBUG_SOCKET #define DEBUG_SOCKET 0 @@ -70,6 +69,13 @@ Handleset_new(void) return result; } +void +Handleset_reset(HandleSet self) +{ + FD_ZERO(&self->handles); + self->maxHandle = -1; +} + void Handleset_addSocket(HandleSet self, const Socket sock) { @@ -105,25 +111,25 @@ Handleset_destroy(HandleSet self) GLOBAL_FREEMEM(self); } -static void -activateKeepAlive(int sd) +void +Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count) { #if defined SO_KEEPALIVE int optval; socklen_t optlen = sizeof(optval); optval = 1; - setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); + setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); #if defined TCP_KEEPCNT - optval = CONFIG_TCP_KEEPALIVE_IDLE; - setsockopt(sd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen); + optval = idleTime; + setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen); - optval = CONFIG_TCP_KEEPALIVE_INTERVAL; - setsockopt(sd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen); + optval = interval; + setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen); - optval = CONFIG_TCP_KEEPALIVE_CNT; - setsockopt(sd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen); + optval = count; + setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen); #endif /* TCP_KEEPCNT */ #endif /* SO_KEEPALIVE */ @@ -197,7 +203,7 @@ TcpServerSocket_create(const char* address, int port) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int)); if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) { - serverSocket = GLOBAL_MALLOC(sizeof(struct sServerSocket)); + serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket)); serverSocket->fd = fd; serverSocket->backLog = 0; @@ -207,10 +213,6 @@ TcpServerSocket_create(const char* address, int port) close(fd); return NULL ; } - -#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1 - activateKeepAlive(fd); -#endif } return serverSocket; @@ -281,7 +283,7 @@ ServerSocket_destroy(ServerSocket self) Socket TcpSocket_create() { - Socket self = GLOBAL_MALLOC(sizeof(struct sSocket)); + Socket self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket)); self->fd = -1; self->connectTimeout = 5000; @@ -316,10 +318,6 @@ Socket_connect(Socket self, const char* address, int port) activateTcpNoDelay(self); -#if (CONFIG_ACTIVATE_TCP_KEEPALIVE == 1) - activateKeepAlive(self->fd); -#endif - fcntl(self->fd, F_SETFL, O_NONBLOCK); if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) { @@ -374,7 +372,7 @@ Socket_getPeerAddress(Socket self) else return NULL ; - char* clientConnection = GLOBAL_MALLOC(strlen(addrString) + 9); + char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9); if (isIPv6) @@ -385,11 +383,45 @@ Socket_getPeerAddress(Socket self) return clientConnection; } +char* +Socket_getPeerAddressStatic(Socket self, char* peerAddressString) +{ + struct sockaddr_storage addr; + socklen_t addrLen = sizeof(addr); + + getpeername(self->fd, (struct sockaddr*) &addr, &addrLen); + + char addrString[INET6_ADDRSTRLEN + 7]; + int port; + + bool isIPv6; + + if (addr.ss_family == AF_INET) { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr; + port = ntohs(ipv4Addr->sin_port); + inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); + isIPv6 = false; + } + else if (addr.ss_family == AF_INET6) { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr; + port = ntohs(ipv6Addr->sin6_port); + inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); + isIPv6 = true; + } + else + return NULL ; + + if (isIPv6) + sprintf(peerAddressString, "[%s]:%i", addrString, port); + else + sprintf(peerAddressString, "%s:%i", addrString, port); + + return peerAddressString; +} + int Socket_read(Socket self, uint8_t* buf, int size) { - assert(self != NULL); - if (self->fd == -1) return -1; diff --git a/src/hal/socket/win32/socket_win32.c b/hal/socket/win32/socket_win32.c similarity index 81% rename from src/hal/socket/win32/socket_win32.c rename to hal/socket/win32/socket_win32.c index 777d17c87..c686ddc7a 100644 --- a/src/hal/socket/win32/socket_win32.c +++ b/hal/socket/win32/socket_win32.c @@ -1,7 +1,7 @@ /* * socket_win32.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -25,10 +25,11 @@ #include #include #include +#include #pragma comment (lib, "Ws2_32.lib") -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" #include "hal_socket.h" #include "stack_config.h" @@ -70,6 +71,13 @@ Handleset_new(void) return result; } +void +Handleset_reset(HandleSet self) +{ + FD_ZERO(&self->handles); + self->maxHandle = INVALID_SOCKET; +} + void Handleset_addSocket(HandleSet self, const Socket sock) { @@ -108,25 +116,26 @@ Handleset_destroy(HandleSet self) static bool wsaStartupCalled = false; static int socketCount = 0; -static void -activateKeepAlive(SOCKET s) +void +Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count) { - struct tcp_keepalive keepalive; - DWORD retVal=0; + struct tcp_keepalive keepalive; + DWORD retVal=0; - keepalive.onoff = 1; - keepalive.keepalivetime = CONFIG_TCP_KEEPALIVE_IDLE * 1000; - keepalive.keepaliveinterval = CONFIG_TCP_KEEPALIVE_INTERVAL * 1000; + keepalive.onoff = 1; + keepalive.keepalivetime = CONFIG_TCP_KEEPALIVE_IDLE * 1000; + keepalive.keepaliveinterval = CONFIG_TCP_KEEPALIVE_INTERVAL * 1000; - if (WSAIoctl(s, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), - NULL, 0, &retVal, NULL, NULL) == SOCKET_ERROR) - { - if (DEBUG_SOCKET) + if (WSAIoctl(self->fd, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), + NULL, 0, &retVal, NULL, NULL) == SOCKET_ERROR) + { + if (DEBUG_SOCKET) printf("WIN32_SOCKET: WSAIotcl(SIO_KEEPALIVE_VALS) failed: %d\n", WSAGetLastError()); - } + } } + static void setSocketNonBlocking(Socket self) { @@ -215,10 +224,6 @@ TcpServerSocket_create(const char* address, int port) listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); -#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1 - activateKeepAlive(listen_socket); -#endif - if (listen_socket == INVALID_SOCKET) { if (DEBUG_SOCKET) printf("WIN32_SOCKET: socket failed with error: %i\n", WSAGetLastError()); @@ -318,7 +323,6 @@ bool Socket_connect(Socket self, const char* address, int port) { struct sockaddr_in serverAddress; - int ec; if (wsaStartUp() == false) return false; @@ -328,10 +332,6 @@ Socket_connect(Socket self, const char* address, int port) self->fd = socket(AF_INET, SOCK_STREAM, 0); -#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1 - activateKeepAlive(self->fd); -#endif - setSocketNonBlocking(self); fd_set fdSet; @@ -396,6 +396,47 @@ Socket_getPeerAddress(Socket self) return clientConnection; } +char* +Socket_getPeerAddressStatic(Socket self, char* peerAddressString) +{ + struct sockaddr_storage addr; + int addrLen = sizeof(addr); + + getpeername(self->fd, (struct sockaddr*) &addr, &addrLen); + + char addrString[INET6_ADDRSTRLEN + 7]; + int addrStringLen = INET6_ADDRSTRLEN + 7; + int port; + + bool isIPv6; + + if (addr.ss_family == AF_INET) { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr; + port = ntohs(ipv4Addr->sin_port); + ipv4Addr->sin_port = 0; + WSAAddressToString((LPSOCKADDR) ipv4Addr, sizeof(struct sockaddr_storage), NULL, + (LPSTR) addrString, (LPDWORD) & addrStringLen); + isIPv6 = false; + } + else if (addr.ss_family == AF_INET6) { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr; + port = ntohs(ipv6Addr->sin6_port); + ipv6Addr->sin6_port = 0; + WSAAddressToString((LPSOCKADDR) ipv6Addr, sizeof(struct sockaddr_storage), NULL, + (LPSTR) addrString, (LPDWORD) & addrStringLen); + isIPv6 = true; + } + else + return NULL; + + if (isIPv6) + sprintf(peerAddressString, "[%s]:%i", addrString, port); + else + sprintf(peerAddressString, "%s:%i", addrString, port); + + return peerAddressString; +} + int Socket_read(Socket self, uint8_t* buf, int size) { diff --git a/src/hal/thread/bsd/thread_bsd.c b/hal/thread/bsd/thread_bsd.c similarity index 98% rename from src/hal/thread/bsd/thread_bsd.c rename to hal/thread/bsd/thread_bsd.c index df16fc9c5..7d882d226 100644 --- a/src/hal/thread/bsd/thread_bsd.c +++ b/hal/thread/bsd/thread_bsd.c @@ -26,7 +26,7 @@ #include #include "hal_thread.h" -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" struct sThread { ThreadExecutionFunction function; diff --git a/src/hal/thread/linux/thread_linux.c b/hal/thread/linux/thread_linux.c similarity index 98% rename from src/hal/thread/linux/thread_linux.c rename to hal/thread/linux/thread_linux.c index 0fd19e5a0..488b73393 100644 --- a/src/hal/thread/linux/thread_linux.c +++ b/hal/thread/linux/thread_linux.c @@ -27,7 +27,7 @@ #include #include "hal_thread.h" -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" struct sThread { ThreadExecutionFunction function; diff --git a/src/hal/thread/win32/thread_win32.c b/hal/thread/win32/thread_win32.c similarity index 98% rename from src/hal/thread/win32/thread_win32.c rename to hal/thread/win32/thread_win32.c index 0eb39f659..aa72e613c 100644 --- a/src/hal/thread/win32/thread_win32.c +++ b/hal/thread/win32/thread_win32.c @@ -22,7 +22,7 @@ */ #include -#include "libiec61850_platform_includes.h" +#include "lib_memory.h" #include "hal_thread.h" struct sThread { diff --git a/src/hal/time/unix/time.c b/hal/time/unix/time.c similarity index 94% rename from src/hal/time/unix/time.c rename to hal/time/unix/time.c index d4a1a2cd2..f7951fde0 100644 --- a/src/hal/time/unix/time.c +++ b/hal/time/unix/time.c @@ -21,10 +21,7 @@ * See COPYING file for the complete license text. */ - -#include "libiec61850_platform_includes.h" - -#include "stack_config.h" +#include "hal_time.h" #include diff --git a/src/hal/time/win32/time.c b/hal/time/win32/time.c similarity index 94% rename from src/hal/time/win32/time.c rename to hal/time/win32/time.c index 1af39b5f8..af58793fe 100644 --- a/src/hal/time/win32/time.c +++ b/hal/time/win32/time.c @@ -21,10 +21,7 @@ * See COPYING file for the complete license text. */ - -#include "libiec61850_platform_includes.h" - -#include "stack_config.h" +#include "hal_time.h" #include diff --git a/src/tls/mbedtls/mbedtls_config.h b/hal/tls/mbedtls/mbedtls_config.h similarity index 100% rename from src/tls/mbedtls/mbedtls_config.h rename to hal/tls/mbedtls/mbedtls_config.h diff --git a/src/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c similarity index 86% rename from src/tls/mbedtls/tls_mbedtls.c rename to hal/tls/mbedtls/tls_mbedtls.c index 3c40098a8..2319d647e 100644 --- a/src/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -11,12 +11,11 @@ #include -#include "tls_api.h" +#include "tls_socket.h" #include "hal_thread.h" #include "lib_memory.h" #include "linked_list.h" -//#include "mbedtls/config.h" #include "mbedtls/platform.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" @@ -27,10 +26,10 @@ #include "mbedtls/error.h" #include "mbedtls/debug.h" -#define CONFIG_DEBUG_TLS 0 - #if (CONFIG_DEBUG_TLS == 1) -#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__) +#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, \ + appId, \ + __VA_ARGS__) #else #define DEBUG_PRINT(fmt, ...) {do {} while(0);} #endif @@ -50,6 +49,9 @@ struct sTLSConfiguration { bool chainValidation; bool allowOnlyKnownCertificates; + + /* TLS session renegotioation time in milliseconds */ + int renegotiationTimeInMs; }; struct sTLSSocket { @@ -208,7 +210,7 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen); if (ret != 0) - printf("mbedtls_x509_crt_parse returned %d\n", ret); + DEBUG_PRINT("mbedtls_x509_crt_parse returned %d\n", ret); return (ret == 0); } @@ -304,6 +306,12 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil return (ret == 0); } +void +TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) +{ + self->renegotiationTimeInMs = timeInMs; +} + void TLSConfiguration_destroy(TLSConfiguration self) { @@ -333,8 +341,9 @@ readFunction(void* ctx, unsigned char* buf, size_t len) { int ret = Socket_read((Socket) ctx, buf, len); - if ((ret == 0) && (len > 0)) + if ((ret == 0) && (len > 0)) { return MBEDTLS_ERR_SSL_WANT_READ; + } return ret; } @@ -401,8 +410,6 @@ TLSSocket_getPeerCertificate(TLSSocket self, int* certSize) bool TLSSocket_performHandshake(TLSSocket self) { - //TODO evaluate return value - if (mbedtls_ssl_renegotiate(&(self->ssl)) == 0) return true; else @@ -412,47 +419,30 @@ TLSSocket_performHandshake(TLSSocket self) int TLSSocket_read(TLSSocket self, uint8_t* buf, int size) { - int ret; - int len = size; - - do - { - ret = mbedtls_ssl_read( &(self->ssl), buf, len ); + int ret = mbedtls_ssl_read(&(self->ssl), buf, size); - if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE ) - continue; + if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) + return 0; - if( ret <= 0 ) - { - switch( ret ) - { - case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: - DEBUG_PRINT("TLS", " connection was closed gracefully\n" ); - len = -1; - break; + if (ret < 0) { - case MBEDTLS_ERR_NET_CONN_RESET: - len = -1; - DEBUG_PRINT("TLS", " connection was reset by peer\n" ); - break; - - default: - DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret ); - len = -1; //TODO is this the correct return value? - break; - } - - break; - } + switch (ret) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + DEBUG_PRINT("TLS", " connection was closed gracefully\n"); + return -1; - len = ret; + case MBEDTLS_ERR_NET_CONN_RESET: + DEBUG_PRINT("TLS", " connection was reset by peer\n"); + return -1; - if( ret > 0 ) - break; - } - while( 1 ); + default: + DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret); + return -1; + } + } - return len; + return ret; } int @@ -461,18 +451,17 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size) int ret; int len = size; - - while( ( ret = mbedtls_ssl_write( &(self->ssl), buf, len ) ) <= 0 ) + while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0) { - if( ret == MBEDTLS_ERR_NET_CONN_RESET ) + if (ret == MBEDTLS_ERR_NET_CONN_RESET) { - DEBUG_PRINT("TLS", "peer closed the connection\n" ); + DEBUG_PRINT("TLS", "peer closed the connection\n"); return -1; } - if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) { - DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret ); + DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret); return -1; } } @@ -489,12 +478,11 @@ TLSSocket_close(TLSSocket self) //TODO add timeout? - while( ( ret = mbedtls_ssl_close_notify( &(self->ssl) ) ) < 0 ) + while ((ret = mbedtls_ssl_close_notify(&(self->ssl))) < 0) { - if( ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) { - DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned %d\n", ret ); + DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned %d\n", ret); break; } } diff --git a/make/stack_includes.mk b/make/stack_includes.mk index 8812255b5..74031182b 100644 --- a/make/stack_includes.mk +++ b/make/stack_includes.mk @@ -1,11 +1,11 @@ INCLUDES = -I$(LIBIEC_HOME)/config +INCLUDES += -I$(LIBIEC_HOME)/hal/inc INCLUDES += -I$(LIBIEC_HOME)/src/common/inc INCLUDES += -I$(LIBIEC_HOME)/src/mms/inc INCLUDES += -I$(LIBIEC_HOME)/src/mms/inc_private INCLUDES += -I$(LIBIEC_HOME)/src/mms/asn1 INCLUDES += -I$(LIBIEC_HOME)/src/iec61850/inc INCLUDES += -I$(LIBIEC_HOME)/src/iec61850/inc_private -INCLUDES += -I$(LIBIEC_HOME)/src/hal/inc INCLUDES += -I$(LIBIEC_HOME)/src/goose INCLUDES += -I$(LIBIEC_HOME)/src/sampled_values INCLUDES += -I$(LIBIEC_HOME)/src/logging diff --git a/make/target_system.mk b/make/target_system.mk index b01e98873..b03a0ec7e 100644 --- a/make/target_system.mk +++ b/make/target_system.mk @@ -1,10 +1,10 @@ UNAME := $(shell uname) MIPSEL_TOOLCHAIN_PREFIX=mipsel-openwrt-linux- -ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf- +#ARM_TOOLCHAIN_PREFIX=arm-linux- #ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi- #ARM_TOOLCHAIN_PREFIX=arm-poky-linux-gnueabi- -#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi- +ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf- UCLINUX_ARM_TOOLCHAIN_PREFIX=arm-uclinux-elf- UCLINUX_XPORT_TOOLCHAIN_PREFIX=m68k-uclinux- MINGW_TOOLCHAIN_PREFIX=i586-mingw32msvc- @@ -57,7 +57,7 @@ endif ifeq ($(TARGET), LINUX-ARM) TOOLCHAIN_PREFIX=$(ARM_TOOLCHAIN_PREFIX) -CFLAGS += -mno-unaligned-access +#CFLAGS += -mno-unaligned-access #CFLAGS += -mcpu=arm926ej-s endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81fbd3cc8..e9f4f903b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,11 +1,9 @@ set (lib_common_SRCS ./common/string_map.c -./common/array_list.c ./common/map.c ./common/linked_list.c ./common/byte_buffer.c -./common/lib_memory.c ./common/string_utilities.c ./common/buffer_chain.c ./common/conversions.c @@ -66,6 +64,7 @@ set (lib_common_SRCS ./iec61850/client/ied_connection.c ./iec61850/common/iec61850_common.c ./iec61850/server/impl/ied_server.c +./iec61850/server/impl/ied_server_config.c ./iec61850/server/impl/client_connection.c ./iec61850/server/model/model.c ./iec61850/server/model/dynamic_model.c @@ -80,15 +79,6 @@ set (lib_common_SRCS ./logging/log_storage.c ) -if(WITH_MBEDTLS) -set (lib_common_SRCS ${lib_common_SRCS} -./tls/mbedtls/tls_mbedtls.c -) - -list (APPEND lib_common_SRCS ${tls_SRCS}) - -endif(WITH_MBEDTLS) - set (lib_asn1c_SRCS ./mms/iso_mms/asn1c/DataAccessError.c ./mms/iso_mms/asn1c/DeleteNamedVariableListRequest.c @@ -194,27 +184,12 @@ set (lib_sv_SRCS ) set (lib_linux_SRCS -./hal/socket/linux/socket_linux.c -./hal/ethernet/linux/ethernet_linux.c -./hal/thread/linux/thread_linux.c -./hal/filesystem/linux/file_provider_linux.c -./hal/time/unix/time.c ) set (lib_windows_SRCS -./hal/socket/win32/socket_win32.c -./hal/ethernet/win32/ethernet_win32.c -./hal/thread/win32/thread_win32.c -./hal/filesystem/win32/file_provider_win32.c -./hal/time/win32/time.c ) set (lib_bsd_SRCS -./hal/socket/bsd/socket_bsd.c -./hal/ethernet/bsd/ethernet_bsd.c -./hal/thread/bsd/thread_bsd.c -./hal/filesystem/linux/file_provider_linux.c -./hal/time/unix/time.c ) IF(WIN32) @@ -323,6 +298,9 @@ set_target_properties(iec61850-shared PROPERTIES SOVERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}" ) +target_link_libraries(iec61850-shared + hal-shared +) GENERATE_EXPORT_HEADER(iec61850-shared BASE_NAME iec61850-shared @@ -333,6 +311,10 @@ GENERATE_EXPORT_HEADER(iec61850-shared add_library (iec61850 STATIC ${library_SRCS}) +target_link_libraries(iec61850 + hal +) + IF(UNIX) IF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME) target_link_libraries (iec61850 @@ -376,7 +358,14 @@ if(MSVC) endif() ENDIF(WITH_WPCAP) +set(BINDIR "bin") +set(LIBDIR "lib") if(UNIX) + # GNUInstallDirs is required for Debian multiarch + include(GNUInstallDirs) + set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) + set(BINDIR ${CMAKE_INSTALL_BINDIR}) + configure_file( ${CMAKE_CURRENT_LIST_DIR}/libiec61850.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc @ONLY @@ -391,7 +380,7 @@ if(DOXYGEN_FOUND) endif(DOXYGEN_FOUND) install (TARGETS iec61850 iec61850-shared - RUNTIME DESTINATION bin COMPONENT Applications - ARCHIVE DESTINATION lib COMPONENT Libraries - LIBRARY DESTINATION lib COMPONENT Libraries + RUNTIME DESTINATION ${BINDIR} COMPONENT Applications + ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries + LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries ) diff --git a/src/Doxyfile.in b/src/Doxyfile.in index 645161fce..bc9fe0734 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -794,7 +794,6 @@ INPUT = "iec61850/inc/iec61850_client.h" \ "goose/goose_receiver.h" \ "sampled_values/sv_subscriber.h" \ "sampled_values/sv_publisher.h" \ - "sampled_values/sv_publisher_deprecated.h" \ "mms/inc/mms_device_model.h" \ "mms/inc/mms_types.h" \ "mms/inc/mms_common.h" \ diff --git a/src/common/array_list.c b/src/common/array_list.c deleted file mode 100644 index ba9eb963e..000000000 --- a/src/common/array_list.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * array_list.c - * - * Copyright 2013 Michael Zillgith - * - * This file is part of libIEC61850. - * - * libIEC61850 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libIEC61850 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with libIEC61850. If not, see . - * - * See COPYING file for the complete license text. - */ - -#include "libiec61850_platform_includes.h" -#include "array_list.h" - -int -ArrayList_listSize(void** list) -{ - int size = 0; - - while (list[size] != NULL) - size++; - return size; -} diff --git a/src/common/inc/array_list.h b/src/common/inc/array_list.h deleted file mode 100644 index 50245c950..000000000 --- a/src/common/inc/array_list.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * array_list.h - * - * Copyright 2013 Michael Zillgith - * - * This file is part of libIEC61850. - * - * libIEC61850 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libIEC61850 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with libIEC61850. If not, see . - * - * See COPYING file for the complete license text. - */ - -#ifndef ARRAY_LIST_H_ -#define ARRAY_LIST_H_ - -int -ArrayList_listSize(void** list); - - -#endif /* ARRAY_LIST_H_ */ diff --git a/src/common/inc/libiec61850_platform_includes.h b/src/common/inc/libiec61850_platform_includes.h index d8e8611db..f2770139a 100644 --- a/src/common/inc/libiec61850_platform_includes.h +++ b/src/common/inc/libiec61850_platform_includes.h @@ -5,6 +5,8 @@ #ifndef LIBIEC61850_PLATFORM_INCLUDES_H_ #define LIBIEC61850_PLATFORM_INCLUDES_H_ +#include "stack_config.h" + #include "libiec61850_common_api.h" #include "string_utilities.h" @@ -15,7 +17,7 @@ #include "platform_endian.h" -#define LIBIEC61850_VERSION "1.2.0" +#define LIBIEC61850_VERSION "1.3.0" #ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME #define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com" diff --git a/src/common/inc/linked_list.h b/src/common/inc/linked_list.h index 1c8a6848e..4cb6b4519 100644 --- a/src/common/inc/linked_list.h +++ b/src/common/inc/linked_list.h @@ -1,7 +1,7 @@ /* * linked_list.h * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,7 +24,8 @@ #ifndef LINKED_LIST_H_ #define LINKED_LIST_H_ -#include "libiec61850_common_api.h" +#include +#include #ifdef __cplusplus extern "C" { diff --git a/src/common/inc/simple_allocator.h b/src/common/inc/simple_allocator.h index 7158ac7b4..59da7ce45 100644 --- a/src/common/inc/simple_allocator.h +++ b/src/common/inc/simple_allocator.h @@ -33,6 +33,9 @@ typedef struct { void MemoryAllocator_init(MemoryAllocator* self, char* memoryBlock, int size); +int +MemoryAllocator_getAlignedSize(int size); + char* MemoryAllocator_allocate(MemoryAllocator* self, int size); diff --git a/src/common/simple_allocator.c b/src/common/simple_allocator.c index 89c994033..e93398fc4 100644 --- a/src/common/simple_allocator.c +++ b/src/common/simple_allocator.c @@ -23,6 +23,7 @@ #include "libiec61850_platform_includes.h" #include "simple_allocator.h" +#include "stack_config.h" void MemoryAllocator_init(MemoryAllocator* self, char* memoryBlock, int size) @@ -32,19 +33,23 @@ MemoryAllocator_init(MemoryAllocator* self, char* memoryBlock, int size) self->size = size; } -static int -getAlignedSize(int size) +int +MemoryAllocator_getAlignedSize(int size) { +#if (CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT == 1) if ((size % sizeof(void*)) > 0) return sizeof(void*) * ((size + sizeof(void*) - 1) / sizeof(void*)); else return size; +#else + return size; +#endif } char* MemoryAllocator_allocate(MemoryAllocator* self, int size) { - size = getAlignedSize(size); + size = MemoryAllocator_getAlignedSize(size); if (((self->currentPtr - self->memoryBlock) + size) <= self->size) { char* ptr = self->currentPtr; diff --git a/src/goose/goose_receiver.c b/src/goose/goose_receiver.c index d18dccd82..2a7280938 100644 --- a/src/goose/goose_receiver.c +++ b/src/goose/goose_receiver.c @@ -45,7 +45,8 @@ #define ETH_P_GOOSE 0x88b8 -struct sGooseReceiver { +struct sGooseReceiver +{ bool running; bool stopped; char* interfaceId; @@ -57,7 +58,6 @@ struct sGooseReceiver { #endif }; - GooseReceiver GooseReceiver_create() { @@ -89,7 +89,6 @@ GooseReceiver_removeSubscriber(GooseReceiver self, GooseSubscriber subscriber) LinkedList_remove(self->subscriberList, (void*) subscriber); } - void GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId) { @@ -122,7 +121,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) uint8_t tag = buffer[bufPos++]; if (elementIndex > maxIndex) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: too much elements!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: too much elements!\n"); return 0; } @@ -130,41 +130,51 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); if (bufPos < 0) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; } if (bufPos + elementLength > allDataLength) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n"); return 0; } - switch (tag) { + switch (tag) + { case 0x80: /* reserved for access result */ printf("GOOSE_SUBSCRIBER: found reserved value (tag 0x80)!\n"); break; + case 0xa1: /* array */ - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: found array\n"); if (MmsValue_getType(value) == MMS_ARRAY) { if (!parseAllData(buffer + bufPos, elementLength, value)) return -1; } break; + case 0xa2: /* structure */ - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: found structure\n"); if (MmsValue_getType(value) == MMS_STRUCTURE) { if (!parseAllData(buffer + bufPos, elementLength, value)) return -1; } break; + case 0x83: /* boolean */ - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: found boolean\n"); if (MmsValue_getType(value) == MMS_BOOLEAN) { MmsValue_setBoolean(value, BerDecoder_decodeBoolean(buffer, bufPos)); } else - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n"); break; @@ -177,10 +187,11 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) elementLength - 1); } else - if (DEBUG_GOOSE_SUBSCRIBER) - printf("bit-string is of wrong size"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("bit-string is of wrong size"); } break; + case 0x85: /* integer */ if (MmsValue_getType(value) == MMS_INTEGER) { if (elementLength <= value->value.integer->maxSize) { @@ -189,6 +200,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } } break; + case 0x86: /* unsigned integer */ if (MmsValue_getType(value) == MMS_UNSIGNED) { if (elementLength <= value->value.integer->maxSize) { @@ -197,6 +209,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } } break; + case 0x87: /* Float */ if (MmsValue_getType(value) == MMS_FLOAT) { if (elementLength == 9) { @@ -216,6 +229,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } } break; + case 0x8a: /* visible string */ if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { @@ -235,6 +249,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } break; + case 0x8c: /* binary time */ if (MmsValue_getType(value) == MMS_BINARY_TIME) { if ((elementLength == 4) || (elementLength == 6)) { @@ -242,17 +257,21 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } } break; + case 0x91: /* Utctime */ if (elementLength == 8) { if (MmsValue_getType(value) == MMS_UTC_TIME) { MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); } else - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n"); } else - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); break; + default: if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag); @@ -282,16 +301,19 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); if (bufPos < 0) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; } if (bufPos + elementLength > allDataLength) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n"); goto exit_with_error; } - switch (tag) { + switch (tag) + { case 0x80: /* reserved for access result */ break; case 0xa1: /* array */ @@ -340,20 +362,24 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); if (bufPos < 0) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; } if (bufPos + elementLength > allDataLength) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n"); goto exit_with_error; } MmsValue* value = NULL; - switch (tag) { + switch (tag) + { case 0xa1: /* array */ - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: found array\n"); value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, false); @@ -361,8 +387,10 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt goto exit_with_error; break; + case 0xa2: /* structure */ - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: found structure\n"); value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, true); @@ -370,8 +398,10 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt goto exit_with_error; break; + case 0x83: /* boolean */ - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: found boolean\n"); value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos)); break; @@ -385,30 +415,35 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt } break; + case 0x85: /* integer */ value = MmsValue_newInteger(elementLength * 8); memcpy(value->value.integer->octets, buffer + bufPos, elementLength); value->value.integer->size = elementLength; break; + case 0x86: /* unsigned integer */ value = MmsValue_newUnsigned(elementLength * 8); memcpy(value->value.integer->octets, buffer + bufPos, elementLength); value->value.integer->size = elementLength; break; + case 0x87: /* Float */ - if (elementLength == 9) - value = MmsValue_newDouble(BerDecoder_decodeDouble(buffer, bufPos)); - else if (elementLength == 5) - value = MmsValue_newFloat(BerDecoder_decodeFloat(buffer, bufPos)); + if (elementLength == 9) + value = MmsValue_newDouble(BerDecoder_decodeDouble(buffer, bufPos)); + else if (elementLength == 5) + value = MmsValue_newFloat(BerDecoder_decodeFloat(buffer, bufPos)); break; case 0x89: /* octet string */ value = MmsValue_newOctetString(elementLength, elementLength); memcpy(value->value.octetString.buf, buffer + bufPos, elementLength); break; + case 0x8a: /* visible string */ value = MmsValue_newVisibleStringFromByteArray(buffer + bufPos, elementLength); break; + case 0x8c: /* binary time */ if (elementLength == 4) value = MmsValue_newBinaryTime(true); @@ -419,16 +454,20 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength); break; + case 0x91: /* Utctime */ if (elementLength == 8) { value = MmsValue_newUtcTime(0); MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); } else - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); break; + default: - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag); goto exit_with_error; } @@ -444,7 +483,7 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt return dataSetValues; -exit_with_error: + exit_with_error: if (dataSetValues != NULL) MmsValue_delete(dataSetValues); @@ -452,7 +491,6 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt return NULL; } - static int parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) { @@ -474,7 +512,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) int gooseLength; bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength); if (bufPos < 0) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; } @@ -486,7 +525,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength); if (bufPos < 0) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; } @@ -500,9 +540,11 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (bufPos == -1) goto exit_with_fault; - switch(tag) { + switch (tag) + { case 0x80: /* gocbRef */ - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found gocbRef\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found gocbRef\n"); { LinkedList element = LinkedList_getNext(self->subscriberList); @@ -512,7 +554,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (subscriber->goCBRefLen == elementLength) { if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) { - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n"); matchingSubscriber = subscriber; break; } @@ -531,61 +574,73 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) timeAllowedToLive = BerDecoder_decodeUint32(buffer, elementLength, bufPos); - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found timeAllowedToLive %u\n", timeAllowedToLive); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found timeAllowedToLive %u\n", timeAllowedToLive); break; case 0x82: - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found dataSet\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found dataSet\n"); break; case 0x83: - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found goId\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found goId\n"); break; case 0x84: timestampBufPos = buffer + bufPos; - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found timestamp\n"); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found timestamp\n"); break; case 0x85: stNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos); - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found stNum: %u\n", stNum); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found stNum: %u\n", stNum); break; case 0x86: sqNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos); - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found sqNum: %u\n", sqNum); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found sqNum: %u\n", sqNum); break; case 0x87: simulation = BerDecoder_decodeBoolean(buffer, bufPos); - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found simulation: %i\n", simulation); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found simulation: %i\n", simulation); break; case 0x88: confRev = BerDecoder_decodeUint32(buffer, elementLength, bufPos); - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found confRev: %u\n", confRev); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found confRev: %u\n", confRev); break; case 0x89: ndsCom = BerDecoder_decodeBoolean(buffer, bufPos); - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found ndsCom: %i\n", ndsCom); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found ndsCom: %i\n", ndsCom); break; case 0x8a: numberOfDatSetEntries = BerDecoder_decodeUint32(buffer, elementLength, bufPos); - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found number of entries: %u\n", numberOfDatSetEntries); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found number of entries: %u\n", numberOfDatSetEntries); break; case 0xab: - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found all data with length: %i\n", elementLength); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Found all data with length: %i\n", elementLength); dataSetBufferAddress = buffer + bufPos; dataSetBufferLength = elementLength; break; default: - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Unknown tag %02x\n", tag); + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Unknown tag %02x\n", tag); break; } @@ -629,12 +684,12 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) return 0; } -exit_with_fault: - if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Invalid goose payload\n"); + exit_with_fault: + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Invalid goose payload\n"); return -1; } - static void parseGooseMessage(GooseReceiver self, int numbytes) { @@ -642,7 +697,8 @@ parseGooseMessage(GooseReceiver self, int numbytes) bool subscriberFound = false; uint8_t* buffer = self->buffer; - if (numbytes < 22) return; + if (numbytes < 22) + return; /* skip ethernet addresses */ bufPos = 12; @@ -688,7 +744,6 @@ parseGooseMessage(GooseReceiver self, int numbytes) printf("GOOSE_SUBSCRIBER: APDU length: %i\n", apduLength); } - // check if there is an interested subscriber LinkedList element = LinkedList_getNext(self->subscriberList); @@ -711,7 +766,7 @@ parseGooseMessage(GooseReceiver self, int numbytes) } } - +#if (CONFIG_MMS_THREADLESS_STACK == 0) static void gooseReceiverLoop(void* threadParameter) { @@ -722,17 +777,20 @@ gooseReceiverLoop(void* threadParameter) GooseReceiver_startThreadless(self); - while (self->running) { + if (self->running) { - if (GooseReceiver_tick(self) == false) - Thread_sleep(1); - } + while (self->running) { - GooseReceiver_stopThreadless(self); + if (GooseReceiver_tick(self) == false) + Thread_sleep(1); + } - self->stopped = true; -} + GooseReceiver_stopThreadless(self); + } + self->stopped = true; +} +#endif // start GOOSE receiver in a separate thread void @@ -808,14 +866,15 @@ GooseReceiver_startThreadless(GooseReceiver self) } else self->running = false; - + return self->ethSocket; } void GooseReceiver_stopThreadless(GooseReceiver self) { - Ethernet_destroySocket(self->ethSocket); + if (self->ethSocket) + Ethernet_destroySocket(self->ethSocket); self->running = false; } diff --git a/src/goose/goose_receiver.h b/src/goose/goose_receiver.h index 4995daeab..4638adbe3 100644 --- a/src/goose/goose_receiver.h +++ b/src/goose/goose_receiver.h @@ -30,15 +30,14 @@ extern "C" { #include -typedef struct sEthernetSocket* EthernetSocket; +#include "hal_ethernet.h" +#include "goose_subscriber.h" /** * \addtogroup goose_api_group */ /**@{*/ -typedef struct sGooseSubscriber* GooseSubscriber; - typedef struct sGooseReceiver* GooseReceiver; /** @@ -103,6 +102,15 @@ GooseReceiver_start(GooseReceiver self); void GooseReceiver_stop(GooseReceiver self); +/** + * \brief Check if GOOSE receiver is running + * + * Can be used to check if \ref GooseReceiver_start has been successful. + * + * \param self the GooseReceiver instance + * + * \return true if GOOSE receiver is running, false otherwise + */ bool GooseReceiver_isRunning(GooseReceiver self); diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index 6358f6141..173a9be4e 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -105,7 +105,7 @@ static void resetLastApplError(ControlObjectClient self) { self->lastApplError.error = 0; - self->lastApplError.addCause = ADD_CAUSE_UNKNOWN; + self->lastApplError.addCause = ADD_CAUSE_UNKNOWN; self->lastApplError.ctlNum = 0; } @@ -118,11 +118,11 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection char reference[129]; if (strlen(objectReference) < 121) { - strcpy(reference, objectReference); - strcat(reference, ".ctlModel"); + strcpy(reference, objectReference); + strcat(reference, ".ctlModel"); } else - goto exit_function; + goto exit_function; IedClientError error; @@ -136,7 +136,7 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection } MmsVariableSpecification* ctlVarSpec = - IedConnection_getVariableSpecification(connection, &error, objectReference, IEC61850_FC_CO); + IedConnection_getVariableSpecification(connection, &error, objectReference, IEC61850_FC_CO); if (error != IED_ERROR_OK) { if (DEBUG_IED_CLIENT) @@ -154,10 +154,10 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection MmsVariableSpecification* t = NULL; if (MmsVariableSpecification_getType(ctlVarSpec) == MMS_STRUCTURE) { - MmsVariableSpecification* oper = MmsVariableSpecification_getNamedVariableRecursive(ctlVarSpec, "Oper"); + MmsVariableSpecification* oper = MmsVariableSpecification_getNamedVariableRecursive(ctlVarSpec, "Oper"); - if (oper) - { + if (oper) + { hasOper = true; ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlVal"); @@ -165,18 +165,18 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection if (MmsVariableSpecification_getType(ctlVal) == MMS_STRUCTURE) isAPC = true; - MmsVariableSpecification* operTm = MmsVariableSpecification_getNamedVariableRecursive(oper, "operTm"); + MmsVariableSpecification* operTm = MmsVariableSpecification_getNamedVariableRecursive(oper, "operTm"); - if (operTm) - hasTimeActivatedControl = true; + if (operTm) + hasTimeActivatedControl = true; - MmsVariableSpecification* ctlNum = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlNum"); + MmsVariableSpecification* ctlNum = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlNum"); - if (ctlNum) - hasCtlNum = true; + if (ctlNum) + hasCtlNum = true; - t = MmsVariableSpecification_getNamedVariableRecursive(oper, "T"); - } + t = MmsVariableSpecification_getNamedVariableRecursive(oper, "T"); + } } if (hasOper == false) { @@ -187,10 +187,10 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection } if ((ctlVal == NULL) || (t == NULL)) { - if (DEBUG_IED_CLIENT) - printf("IED_CLIENT: \"Oper\" is missing required element\n"); + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: \"Oper\" is missing required element\n"); - goto free_varspec; + goto free_varspec; } self = (ControlObjectClient) GLOBAL_CALLOC(1, sizeof(struct sControlObjectClient)); @@ -221,10 +221,10 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection private_IedConnection_addControlClient(connection, self); -free_varspec: + free_varspec: MmsVariableSpecification_destroy(ctlVarSpec); -exit_function: + exit_function: return self; } @@ -241,7 +241,7 @@ ControlObjectClient_destroy(ControlObjectClient self) MmsValue_delete(self->ctlVal); if (self->analogValue != NULL) - MmsValue_delete(self->analogValue); + MmsValue_delete(self->analogValue); if (self->orIdent != NULL) GLOBAL_FREEMEM(self->orIdent); @@ -270,6 +270,15 @@ ControlObjectClient_getControlModel(ControlObjectClient self) return self->ctlModel; } +MmsType +ControlObjectClient_getCtlValType(ControlObjectClient self) +{ + if (self->analogValue != NULL) + return MmsValue_getType(self->analogValue); + else + return MmsValue_getType(self->ctlVal); +} + void ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat) { @@ -320,11 +329,11 @@ createOriginValue(ControlObjectClient self) goto exit_function; -cleanup_on_error: + cleanup_on_error: MmsValue_delete(origin); origin = NULL; -exit_function: + exit_function: return origin; } @@ -347,20 +356,20 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t int operElementCount = 5; if (self->hasTimeActivatedMode) - operElementCount++; + operElementCount++; if (self->hasCtlNum) - operElementCount++; + operElementCount++; - operParameters = MmsValue_createEmptyStructure(operElementCount); + operParameters = MmsValue_createEmptyStructure(operElementCount); - /* support simplified usage of APC controls - user doesn't need to create the structure */ - if (self->analogValue != NULL) { - if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) { - MmsValue_setElement(self->analogValue, 0, ctlVal); - ctlVal = self->analogValue; - } - } + /* support simplified usage of APC controls - user doesn't need to create the structure */ + if (self->analogValue != NULL) { + if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) { + MmsValue_setElement(self->analogValue, 0, ctlVal); + ctlVal = self->analogValue; + } + } MmsValue_setElement(operParameters, 0, ctlVal); @@ -381,19 +390,19 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t } if (self->hasCtlNum) { - MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); - MmsValue_setElement(operParameters, index++, ctlNum); + MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); + MmsValue_setElement(operParameters, index++, ctlNum); } uint64_t timestamp; if ((self->ctlModel == CONTROL_MODEL_SBO_ENHANCED) && (self->useConstantT)) - timestamp = self->constantT; + timestamp = self->constantT; else - timestamp = Hal_getTimeInMs(); + timestamp = Hal_getTimeInMs(); if (self->useConstantT) - self->constantT = timestamp; + self->constantT = timestamp; MmsValue* ctlTime; @@ -446,13 +455,13 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t MmsValue_update(self->ctlVal, ctlVal); if (self->analogValue) - MmsValue_setElement(self->analogValue, 0, NULL); + MmsValue_setElement(self->analogValue, 0, NULL); self->opertime = operTime; success = true; -exit_function: + exit_function: return success; } @@ -478,20 +487,20 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) int selValElementCount = 5; if (self->hasTimeActivatedMode) - selValElementCount++; + selValElementCount++; if (self->hasCtlNum) - selValElementCount++; + selValElementCount++; MmsValue* selValParameters = MmsValue_createEmptyStructure(selValElementCount); - /* support simplified usage of APC controls - user doesn't need to create the structure */ - if (self->analogValue != NULL) { - if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) { - MmsValue_setElement(self->analogValue, 0, ctlVal); - ctlVal = self->analogValue; - } - } + /* support simplified usage of APC controls - user doesn't need to create the structure */ + if (self->analogValue != NULL) { + if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) { + MmsValue_setElement(self->analogValue, 0, ctlVal); + ctlVal = self->analogValue; + } + } MmsValue_setElement(selValParameters, 0, ctlVal); @@ -508,15 +517,15 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) self->ctlNum++; if (self->hasCtlNum) { - MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); - MmsValue_setElement(selValParameters, index++, ctlNum); + MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); + MmsValue_setElement(selValParameters, index++, ctlNum); } uint64_t timestamp = Hal_getTimeInMs(); MmsValue* ctlTime; if (self->useConstantT) - self->constantT = timestamp; + self->constantT = timestamp; if (self->edition == 2) ctlTime = MmsValue_newUtcTimeByMsTime(timestamp); @@ -550,7 +559,7 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) MmsValue_update(self->ctlVal, ctlVal); if (self->analogValue) - MmsValue_setElement(self->analogValue, 0, NULL); + MmsValue_setElement(self->analogValue, 0, NULL); return true; } @@ -592,11 +601,11 @@ ControlObjectClient_select(ControlObjectClient self) snprintf(sboReference, 129, "%s/%s", domainId, itemId); if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { - if (strcmp(MmsValue_toString(value), "") == 0) { + if (strcmp(MmsValue_toString(value), "") == 0) { if (DEBUG_IED_CLIENT) printf("select-response-\n"); } - else if (strcmp(MmsValue_toString(value), sboReference)) { + else if (strcmp(MmsValue_toString(value), sboReference) == 0) { if (DEBUG_IED_CLIENT) printf("select-response+: (%s)\n", MmsValue_toString(value)); selected = true; @@ -613,7 +622,7 @@ ControlObjectClient_select(ControlObjectClient self) MmsValue_delete(value); -exit_function: + exit_function: return selected; } @@ -648,9 +657,9 @@ ControlObjectClient_cancel(ControlObjectClient self) uint64_t timestamp; if (self->useConstantT) - timestamp = self->constantT; + timestamp = self->constantT; else - timestamp = Hal_getTimeInMs(); + timestamp = Hal_getTimeInMs(); MmsValue* ctlTime; @@ -697,7 +706,7 @@ ControlObjectClient_cancel(ControlObjectClient self) void ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT) { - self->useConstantT = useConstantT; + self->useConstantT = useConstantT; } void @@ -712,7 +721,6 @@ ControlObjectClient_setInterlockCheck(ControlObjectClient self, bool value) self->interlockCheck = value; } - void ControlObjectClient_enableSynchroCheck(ControlObjectClient self) { diff --git a/src/iec61850/client/client_goose_control.c b/src/iec61850/client/client_goose_control.c index 89c1a8a9d..c2a5d05d7 100644 --- a/src/iec61850/client/client_goose_control.c +++ b/src/iec61850/client/client_goose_control.c @@ -183,6 +183,97 @@ newEmptyPhyCommAddress(void) { return self; } +PhyComAddress +ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self) +{ + PhyComAddress retVal; + memset(&retVal, 0, sizeof(retVal)); + + if (self->dstAddress == NULL) goto exit_error; + + if (MmsValue_getType(self->dstAddress) != MMS_STRUCTURE) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n"); + goto exit_error; + } + + if (MmsValue_getArraySize(self->dstAddress) != 4) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n"); + goto exit_error; + } + + MmsValue* addr = MmsValue_getElement(self->dstAddress, 0); + + if (MmsValue_getType(addr) != MMS_OCTET_STRING) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n"); + goto exit_error; + } + + if (MmsValue_getOctetStringSize(addr) != 6) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong size\n"); + goto exit_error; + } + + uint8_t* addrBuf = MmsValue_getOctetStringBuffer(addr); + + memcpy(&(retVal.dstAddress), addrBuf, 6); + + MmsValue* prio = MmsValue_getElement(self->dstAddress, 1); + + if (MmsValue_getType(prio) != MMS_UNSIGNED) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - prio has wrong type\n"); + goto exit_error; + } + + retVal.vlanPriority = MmsValue_toUint32(prio); + + MmsValue* vid = MmsValue_getElement(self->dstAddress, 2); + + if (MmsValue_getType(vid) != MMS_UNSIGNED) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - vid has wrong type\n"); + goto exit_error; + } + + retVal.vlanId = MmsValue_toUint32(vid); + + MmsValue* appID = MmsValue_getElement(self->dstAddress, 3); + + if (MmsValue_getType(appID) != MMS_UNSIGNED) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - appID has wrong type\n"); + goto exit_error; + } + + retVal.appId = MmsValue_toUint32(appID); + +exit_error: + return retVal; +} + +void +ClientGooseControlBlock_setDstAddress(ClientGooseControlBlock self, PhyComAddress value) +{ + if (self->dstAddress == NULL) + self->dstAddress = newEmptyPhyCommAddress(); + + if (self->dstAddress) { + + MmsValue* addr = MmsValue_getElement(self->dstAddress, 0); + + MmsValue_setOctetString(addr, value.dstAddress, 6); + + MmsValue* prio = MmsValue_getElement(self->dstAddress, 1); + + MmsValue_setUint8(prio, value.vlanPriority); + + MmsValue* vid = MmsValue_getElement(self->dstAddress, 2); + + MmsValue_setUint16(vid, value.vlanId); + + MmsValue* appID = MmsValue_getElement(self->dstAddress, 3); + + MmsValue_setUint16(appID, value.appId); + } +} + MmsValue* ClientGooseControlBlock_getDstAddress_addr(ClientGooseControlBlock self) { diff --git a/src/iec61850/client/client_sv_control.c b/src/iec61850/client/client_sv_control.c index 7c3cd3c94..55e8ae5fc 100644 --- a/src/iec61850/client/client_sv_control.c +++ b/src/iec61850/client/client_sv_control.c @@ -115,16 +115,16 @@ setBooleanVariable(ClientSVControlBlock self, const char* varName, bool value) } bool -ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool svEna) +ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool value) { - return setBooleanVariable(self, "SvEna", svEna); + return setBooleanVariable(self, "SvEna", value); } bool -ClientSVControlBlock_setResv(ClientSVControlBlock self, bool svEna) +ClientSVControlBlock_setResv(ClientSVControlBlock self, bool value) { if (self->isMulticast == false) - return setBooleanVariable(self, "SvEna", svEna); + return setBooleanVariable(self, "SvEna", value); else return false; } diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index a787ca543..d2015f411 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -21,12 +21,11 @@ * See COPYING file for the complete license text. */ +#include "stack_config.h" #include "libiec61850_platform_includes.h" #include "iec61850_client.h" -#include "stack_config.h" - #include "mms_client_connection.h" #include "ied_connection_private.h" @@ -695,7 +694,35 @@ IedConnection_readObject(IedConnection self, IedClientError* error, const char* MmsError mmsError; - value = MmsConnection_readVariable(self->connection, &mmsError, domainId, itemId); + /* check if item ID contains an array "(..)" */ + char* brace = strchr(itemId, '('); + + if (brace) { + char* secondBrace = strchr(brace, ')'); + + if (secondBrace) { + char* endPtr; + + int index = (int) strtol(brace + 1, &endPtr, 10); + + if (endPtr == secondBrace) { + char* component = NULL; + + if (strlen(secondBrace + 1) > 1) + component = secondBrace + 2; /* skip "." after array element specifier */ + + *brace = 0; + + value = MmsConnection_readSingleArrayElementWithComponent(self->connection, &mmsError, domainId, itemId, index, component); + } + else + *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; + } + else + *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; + } + else + value = MmsConnection_readVariable(self->connection, &mmsError, domainId, itemId); if (value != NULL) *error = IED_ERROR_OK; @@ -2566,7 +2593,7 @@ FileDirectoryEntry_destroy(FileDirectoryEntry self) GLOBAL_FREEMEM(self); } -char* +const char* FileDirectoryEntry_getFileName(FileDirectoryEntry self) { return self->fileName; diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index 0cd856cc4..e16047748 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -244,6 +244,21 @@ Timestamp_create() return self; } +Timestamp* +Timestamp_createFromByteArray(uint8_t* byteArray) +{ + Timestamp* self = Timestamp_create(); + + if (self) { + int i; + + for (i = 0; i < 8; i++) + self->val[i] = byteArray[i]; + } + + return self; +} + void Timestamp_destroy(Timestamp* self) { diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 0d9519d8a..e3b5e9394 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1,7 +1,7 @@ /* * iec61850_client.h * - * Copyright 2013, 2014, 2015 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -34,6 +34,14 @@ extern "C" { #include "mms_client_connection.h" #include "linked_list.h" +#ifndef DEPRECATED +#if defined(__GNUC__) || defined(__clang__) + #define DEPRECATED __attribute__((deprecated)) +#else + #define DEPRECATED +#endif +#endif + /** * * \defgroup iec61850_client_api_group IEC 61850/MMS client API */ @@ -344,6 +352,12 @@ IedConnection_getMmsConnection(IedConnection self); /** SV ASDU contains attribute Security */ #define IEC61850_SV_OPT_SECURITY 16 +#define IEC61850_SV_SMPMOD_SAMPLES_PER_PERIOD 0 + +#define IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND 1 + +#define IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE 2 + /** an opaque handle to the instance data of a ClientSVControlBlock object */ typedef struct sClientSVControlBlock* ClientSVControlBlock; @@ -385,13 +399,13 @@ ClientSVControlBlock_getLastComError(ClientSVControlBlock self); bool -ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool svEna); +ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool value); bool ClientSVControlBlock_getSvEna(ClientSVControlBlock self); bool -ClientSVControlBlock_setResv(ClientSVControlBlock self, bool svEna); +ClientSVControlBlock_setResv(ClientSVControlBlock self, bool value); bool ClientSVControlBlock_getResv(ClientSVControlBlock self); @@ -539,28 +553,34 @@ ClientGooseControlBlock_getMaxTime(ClientGooseControlBlock self); bool ClientGooseControlBlock_getFixedOffs(ClientGooseControlBlock self); -MmsValue* /* MMS_OCTET_STRING */ -ClientGooseControlBlock_getDstAddress_addr(ClientGooseControlBlock self); +PhyComAddress +ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self); void +ClientGooseControlBlock_setDstAddress(ClientGooseControlBlock self, PhyComAddress value); + +DEPRECATED MmsValue* /* MMS_OCTET_STRING */ +ClientGooseControlBlock_getDstAddress_addr(ClientGooseControlBlock self); + +DEPRECATED void ClientGooseControlBlock_setDstAddress_addr(ClientGooseControlBlock self, MmsValue* macAddr); -uint8_t +DEPRECATED uint8_t ClientGooseControlBlock_getDstAddress_priority(ClientGooseControlBlock self); -void +DEPRECATED void ClientGooseControlBlock_setDstAddress_priority(ClientGooseControlBlock self, uint8_t priorityValue); -uint16_t +DEPRECATED uint16_t ClientGooseControlBlock_getDstAddress_vid(ClientGooseControlBlock self); -void +DEPRECATED void ClientGooseControlBlock_setDstAddress_vid(ClientGooseControlBlock self, uint16_t vidValue); -uint16_t +DEPRECATED uint16_t ClientGooseControlBlock_getDstAddress_appid(ClientGooseControlBlock self); -void +DEPRECATED void ClientGooseControlBlock_setDstAddress_appid(ClientGooseControlBlock self, uint16_t appidValue); @@ -821,7 +841,7 @@ void IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReference); /** - * \brief Trigger a general interrogation (GI) report for the specified report control block (RCB) + * \brief trigger a general interrogation (GI) report for the specified report control block (RCB) * * The RCB must have been enabled and GI set as trigger option before this command can be performed. * @@ -839,7 +859,7 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c ****************************************/ /** - * \brief Get the name of the report data set + * \brief get the name of the report data set * * NOTE: the returned string is only valid as long as the ClientReport instance exists! * @@ -852,6 +872,10 @@ ClientReport_getDataSetName(ClientReport self); /** * \brief return the received data set values of the report * + * NOTE: The returned MmsValue instance is handled by the library and only valid as long as the + * ClientReport instance exists! It should not be used outside the report callback handler to + * avoid concurrency issues. + * * \param self the ClientReport instance * \return an MmsValue array instance containing the data set values */ @@ -909,26 +933,72 @@ ClientReport_getEntryId(ClientReport self); bool ClientReport_hasTimestamp(ClientReport self); +/** + * \brief determine if the last received report contains a sequence number + * + * \param self the ClientReport instance + * + * \return true if the report contains a sequence number, false otherwise + */ bool ClientReport_hasSeqNum(ClientReport self); +/** + * \brief get the value of the sequence number + * + * NOTE: The returned value is undefined if the sequence number is not present in report + * + * \param self the ClientReport instance + * + * \returns the number of the sequence number when present + */ uint16_t ClientReport_getSeqNum(ClientReport self); +/** + * \brief determine if the last received report contains the data set name + * + * \param self the ClientReport instance + * + * \return true if the report contains the data set name, false otherwise + */ bool ClientReport_hasDataSetName(ClientReport self); +/** + * \brief determine if the last received report contains reason-for-inclusion information + * + * \param self the ClientReport instance + * + * \return true if the report contains reason-for-inclusion information, false otherwise + */ bool ClientReport_hasReasonForInclusion(ClientReport self); +/** + * \brief determine if the last received report contains the configuration revision + * + * \param self the ClientReport instance + * + * \return true if the report contains the configuration revision, false otherwise + */ bool ClientReport_hasConfRev(ClientReport self); +/** + * \brief get the value of the configuration revision + * + * NOTE: The returned value is undefined if configuration revision is not present in report + * + * \param self the ClientReport instance + * + * \returns the number of the configuration revision + */ uint32_t ClientReport_getConfRev(ClientReport self); /** - * \brief Indicates if the report contains the bufOvfl (buffer overflow) flag + * \brief indicates if the report contains the bufOvfl (buffer overflow) flag * * \param self the ClientReport instance * @@ -938,7 +1008,7 @@ bool ClientReport_hasBufOvfl(ClientReport self); /** - * \brief Get the value of the bufOvfl flag + * \brief get the value of the bufOvfl flag * * \param self the ClientReport instance * @@ -948,7 +1018,7 @@ bool ClientReport_getBufOvfl(ClientReport self); /** - * \brief Indicates if the report contains data references for the reported data set members + * \brief indicates if the report contains data references for the reported data set members * * \param self the ClientReport instance * @@ -1525,6 +1595,19 @@ ControlObjectClient_getObjectReference(ControlObjectClient self); ControlModel ControlObjectClient_getControlModel(ControlObjectClient self); +/** + * \brief Get the type of ctlVal. + * + * This type is required for the ctlVal parameter of the \ref ControlObjectClient_operate + * and \ref ControlObjectClient_selectWithValue functions. + * + * \param self the control object instance to use + * + * \return MmsType required for the ctlVal value. + */ +MmsType +ControlObjectClient_getCtlValType(ControlObjectClient self); + /** * \brief Send an operate command to the server * @@ -1537,12 +1620,25 @@ ControlObjectClient_getControlModel(ControlObjectClient self); bool ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime); +/** + * \brief Send a select command to the server + * + * The select command is only used for the control model "select-before-operate with normal security" + * (CONTROL_MODEL_SBO_NORMAL). The select command has to be sent before the operate command can be used. + * + * \param self the control object instance to use + * + * \return true if operation has been successful, false otherwise. + */ bool ControlObjectClient_select(ControlObjectClient self); /** * \brief Send an select with value command to the server * + * The select-with-value command is only used for the control model "select-before-operate with enhanced security" + * (CONTROL_MODEL_SBO_ENHANCED). The select-with-value command has to be sent before the operate command can be used. + * * \param self the control object instance to use * \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER * @@ -1551,6 +1647,16 @@ ControlObjectClient_select(ControlObjectClient self); bool ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal); +/** + * \brief Send a cancel command to the server + * + * The cancel command can be used to stop an ongoing operation (when the server and application + * support this) and to cancel a former select command. + * + * \param self the control object instance to use + * + * \return true if operation has been successful, false otherwise. + */ bool ControlObjectClient_cancel(ControlObjectClient self); @@ -1560,9 +1666,25 @@ ControlObjectClient_setLastApplError(ControlObjectClient self, LastApplError las LastApplError ControlObjectClient_getLastApplError(ControlObjectClient self); +/** + * \brief Send commands in test mode. + * + * When the server supports test mode the commands that are sent with the test flag set + * are not executed (will have no effect on the attached physical process). + * + * \param self the control object instance to use + * \param value value if the test flag (true = test mode). + */ void ControlObjectClient_setTestMode(ControlObjectClient self, bool value); +/** + * \brief Set the origin parameter for control commands + * + * The origin parameter is used to identify the client/application that sent a control + * command. It is intended for later analysis. + * + */ void ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat); @@ -1804,6 +1926,8 @@ IedConnection_getDataDirectoryByFC(IedConnection self, IedClientError* error, co * This function can be used to get the MMS variable type specification for an IEC 61850 data attribute. It is an extension * of the ACSI that may be required by generic client applications. * + * NOTE: API user is responsible to free the resources (see \ref MmsVariableSpecification_destroy) + * * \param self the connection object * \param error the error code if an error occurs * \param dataAttributeReference string that represents the DA reference @@ -1878,18 +2002,49 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha typedef struct sFileDirectoryEntry* FileDirectoryEntry; +/** + * @deprecated Will be removed from API + */ FileDirectoryEntry FileDirectoryEntry_create(const char* fileName, uint32_t fileSize, uint64_t lastModified); +/** + * \brief Destroy a FileDirectoryEntry object (free all resources) + * + * NOTE: Usually is called as a parameter of the \ref LinkedList_destroyDeep function. + * + * \param self the FileDirectoryEntry object + */ void FileDirectoryEntry_destroy(FileDirectoryEntry self); -char* +/** + * \brief Get the name of the file + * + * \param self the FileDirectoryEntry object + * + * \return name of the file as null terminated string + */ +const char* FileDirectoryEntry_getFileName(FileDirectoryEntry self); +/** + * \brief Get the file size in bytes + * + * \param self the FileDirectoryEntry object + * + * \return size of the file in bytes, or 0 if file size is unknown + */ uint32_t FileDirectoryEntry_getFileSize(FileDirectoryEntry self); +/** + * \brief Get the timestamp of last modification of the file + * + * \param self the FileDirectoryEntry object + * + * \return UTC timestamp in milliseconds + */ uint64_t FileDirectoryEntry_getLastModified(FileDirectoryEntry self); diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index 08a15e13e..67d1abff3 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -37,6 +37,15 @@ extern "C" { */ /**@{*/ +/** IEC 61850 edition 1 */ +#define IEC_61850_EDITION_1 0 + +/** IEC 61850 edition 2 */ +#define IEC_61850_EDITION_2 1 + +/** IEC 61850 edition 2.1 */ +#define IEC_61850_EDITION_2_1 2 + /** PhyComAddress type contains Ethernet address and VLAN attributes */ typedef struct { uint8_t vlanPriority; @@ -290,6 +299,8 @@ typedef uint16_t Validity; #define QUALITY_OPERATOR_BLOCKED 4096 +#define QUALITY_DERIVED 8192 + Validity Quality_getValidity(Quality* self); @@ -360,6 +371,9 @@ typedef union { Timestamp* Timestamp_create(void); +Timestamp* +Timestamp_createFromByteArray(uint8_t* byteArray); + void Timestamp_destroy(Timestamp* self); @@ -422,12 +436,11 @@ Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue); /** * \brief Get the version of the library as string * - * \return the version of the library (e.g. "0.8.3") + * \return the version of the library (e.g. "1.2.2") */ char* LibIEC61850_getVersionString(void); - /** @} */ /**@}*/ diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index 7ade4d7df..c7dc0e5ec 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -314,6 +314,9 @@ DataSetEntry_getNext(DataSetEntry* self); * that have to contain the LN name, the FC and subsequent path elements separated by "$" instead of ".". * This is due to efficiency reasons to avoid the creation of additional strings. * + * If the variable parameter does not contain a logical device name (separated from the remaining variable + * name by the "/" character) the logical device where the data set resides is used automatically. + * * \param dataSet the data set to which the new entry will be added * \param variable the name of the variable as MMS variable name including FC ("$" used as separator!) * \param index the index if the FCDA is an array element, otherwise -1 diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h index d4fbcb76e..86e640217 100644 --- a/src/iec61850/inc/iec61850_model.h +++ b/src/iec61850/inc/iec61850_model.h @@ -117,7 +117,9 @@ typedef enum { IEC61850_CONSTRUCTED = 27, IEC61850_ENTRY_TIME = 28, IEC61850_PHYCOMADDR = 29, - IEC61850_CURRENCY = 30 + IEC61850_CURRENCY = 30, + IEC61850_OPTFLDS = 31, /* bit-string(10) */ + IEC61850_TRGOPS = 32 /* bit-string(6) */ #if (CONFIG_IEC61850_USE_COMPAT_TYPE_DECLARATIONS == 1) @@ -153,6 +155,8 @@ typedef enum { ENTRY_TIME = 28, PHYCOMADDR = 29, CURRENCY = 30 + OPTFLDS = 31, + TRGOPS = 32 #endif } DataAttributeType; @@ -468,6 +472,17 @@ IedModel_getModelNodeByShortAddress(IedModel* self, uint32_t shortAddress); LogicalDevice* IedModel_getDeviceByInst(IedModel* self, const char* ldInst); +/** + * \brief Lookup logical device (LD) instance by index + * + * \param self IedModel instance + * \param index the index of the LD in the range (0 .. number of LDs - 1) + * + * \return the corresponding LogicalDevice* object or NULL if the index is out of range + */ +LogicalDevice* +IedModel_getDeviceByIndex(IedModel* self, int index); + /** * \brief Lookup a logical node by name that is part of the given logical device diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index c6ae28f6e..b29362235 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -3,7 +3,7 @@ * * IEC 61850 server API for libiec61850. * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -41,6 +41,228 @@ extern "C" { #include "hal_filesystem.h" #include "iec61850_config_file_parser.h" +/** + * \brief Configuration object to configure IEC 61850 stack features + */ +typedef struct sIedServerConfig* IedServerConfig; + +struct sIedServerConfig +{ + /** size of the report buffer associated with a buffered report control block */ + int reportBufferSize; + + /** Base path (directory where the file service serves files */ + char* fileServiceBasepath; + + /** when true (default) enable MMS file service */ + bool enableFileService; + + /** when true (default) enable dynamic data set services for MMS */ + bool enableDynamicDataSetService; + + /** the maximum number of allowed association specific data sets */ + int maxAssociationSpecificDataSets; + + /** the maximum number of allowed domain specific data sets */ + int maxDomainSpecificDataSets; + + /** maximum number of data set entries of dynamic data sets */ + int maxDataSetEntries; + + /** when true (default) enable log service */ + bool enableLogService; + + /** IEC 61850 edition (0 = edition 1, 1 = edition 2, 2 = edition 2.1, ...) */ + uint8_t edition; + + /** maximum number of MMS (TCP) connections */ + int maxMmsConnections; +}; + +/** + * \brief Create a new configuration object + * + * \return a new configuration object with default configuration values + */ +IedServerConfig +IedServerConfig_create(void); + +/** + * \brief Destroy the configuration object + */ +void +IedServerConfig_destroy(IedServerConfig self); + +/** + * \brief Set the IEC 61850 standard edition to use (default is edition 2) + * + * \param edition IEC_61850_EDITION_1, IEC_61850_EDITION_2, or IEC_61850_EDITION_2_1 + */ +void +IedServerConfig_setEdition(IedServerConfig self, uint8_t edition); + +/** + * \brief Get the configued IEC 61850 standard edition + * + * \returns IEC_61850_EDITION_1, IEC_61850_EDITION_2, or IEC_61850_EDITION_2_1 + */ +uint8_t +IedServerConfig_getEdition(IedServerConfig self); + +/** + * \brief Set the report buffer size for buffered reporting + * + * \param reportBufferSize the buffer size for each buffered report control block + */ +void +IedServerConfig_setReportBufferSize(IedServerConfig self, int reportBufferSize); + +/** + * \brief Gets the report buffer size for buffered reporting + * + * \return the buffer size for each buffered report control block + */ +int +IedServerConfig_getReportBufferSize(IedServerConfig self); + +/** + * \brief Set the maximum number of MMS (TCP) connections the server accepts + * + * NOTE: Parameter has to be smaller than CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS if + * CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS != -1 + * + * \param maxConnection maximum number of TCP connections + */ +void +IedServerConfig_setMaxMmsConnections(IedServerConfig self, int maxConnections); + +/** + * \brief Get the maximum number of MMS (TCP) connections the server accepts + * + * \return maximum number of TCP connections + */ +int +IedServerConfig_getMaxMmsConnections(IedServerConfig self); + +/** + * \brief Set the basepath of the file services + * + * NOTE: the basepath specifies the local directory that is served by MMS file services + * + * \param basepath new file service base path + */ +void +IedServerConfig_setFileServiceBasePath(IedServerConfig self, const char* basepath); + +/** + * \brief Get the basepath of the file services + */ +const char* +IedServerConfig_getFileServiceBasePath(IedServerConfig self); + +/** + * \brief Enable/disable the MMS file service support + * + * \param[in] enable set true to enable the file services, otherwise false + */ +void +IedServerConfig_enableFileService(IedServerConfig self, bool enable); + +/** + * \brief Is the MMS file service enabled or disabled + * + * \return true if enabled, false otherwise + */ +bool +IedServerConfig_isFileServiceEnabled(IedServerConfig self); + +/** + * \brief Enable/disable the dynamic data set service for MMS + * + * \param[in] enable set true to enable dynamic data set service, otherwise false + */ +void +IedServerConfig_enableDynamicDataSetService(IedServerConfig self, bool enable); + +/** + * \brief Is the dynamic data set service for MMS enabled or disabled + * + * \return true if enabled, false otherwise + */ +bool +IedServerConfig_isDynamicDataSetServiceEnabled(IedServerConfig self); + +/** + * \brief Set the maximum allowed number of association specific (non-permanent) data sets + * + * NOTE: This specifies the maximum number of non-permanent data sets per connection. When + * the connection is closed these data sets are deleted automatically. + * + * \param maxDataSets maximum number of allowed data sets. + */ +void +IedServerConfig_setMaxAssociationSpecificDataSets(IedServerConfig self, int maxDataSets); + +/** + * \brief Get the maximum allowed number of association specific (non-permanent) data sets + * + * \return maximum number of allowed data sets. + */ +int +IedServerConfig_getMaxAssociationSpecificDataSets(IedServerConfig self); + +/** + * \brief Set the maximum allowed number of domain specific (permanent) data sets + * + * \param maxDataSets maximum number of allowed data sets. + */ +void +IedServerConfig_setMaxDomainSpecificDataSets(IedServerConfig self, int maxDataSets); + +/** + * \brief Get the maximum allowed number of domain specific (permanent) data sets + * + * \return maximum number of allowed data sets. + */ +int +IedServerConfig_getMaxDomainSpecificDataSets(IedServerConfig self); + +/** + * \brief Set the maximum number of entries in dynamic data sets + * + * NOTE: this comprises the base data set entries (can be simple or complex variables). + * When the client tries to create a data set with more member the request will be + * rejected and the data set will not be created. + * + * \param maxDataSetEntries the maximum number of entries allowed in a data set + */ +void +IedServerConfig_setMaxDataSetEntries(IedServerConfig self, int maxDataSetEntries); + +/** + * \brief Get the maximum number of entries in dynamic data sets + * + * \return the maximum number of entries allowed in a data sets + */ +int +IedServerConfig_getMaxDatasSetEntries(IedServerConfig self); + +/** + * \brief Enable/disable the log service for MMS + * + * \param[in] enable set true to enable dynamic data set service, otherwise false + */ +void +IedServerConfig_enableLogService(IedServerConfig self, bool enable); + +/** + * \brief Is the log service for MMS enabled or disabled + * + * \return true if enabled, false otherwise + */ +bool +IedServerConfig_isLogServiceEnabled(IedServerConfig self); + /** * An opaque handle for an IED server instance */ @@ -79,6 +301,16 @@ IedServer_create(IedModel* dataModel); IedServer IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration); +/** + * \brief Create new new IedServer with extended configurations parameters + * + * \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device + * \param tlsConfiguration TLS configuration object, or NULL to not use TLS + * \param serverConfiguration IED server configuration object for advanced server configuration + */ +IedServer +IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguration, IedServerConfig serverConfiguration); + /** * \brief Destroy an IedServer instance and release all resources (memory, TCP sockets) * @@ -338,8 +570,10 @@ IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndication /** - * \brief Lock the MMS server data model. + * \brief Lock the data model for data update. * + * This function should be called before the data model is updated. + * After updating the data model the function \ref IedServer_unlockDataModel should be called. * Client requests will be postponed until the lock is removed. * * NOTE: This method should never be called inside of a library callback function. In the context of @@ -352,7 +586,7 @@ void IedServer_lockDataModel(IedServer self); /** - * \brief Unlock the MMS server data model and process pending client requests. + * \brief Unlock the data model and process pending client requests. * * NOTE: This method should never be called inside of a library callback function. In the context of * a library callback the data model is always already locked! @@ -856,6 +1090,8 @@ typedef enum { * a control operation has been invoked by the client. This callback function is * intended to perform the static tests. It should check if the interlock conditions * are met if the interlockCheck parameter is true. + * This handler can also be check if the client has the required permissions to execute the + * operation and allow or deny the operation accordingly. * * \param parameter the parameter that was specified when setting the control handler * \param ctlVal the control value of the control operation. @@ -1010,7 +1246,7 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler **************************************************************************/ /** - * \brief callback handler to intercept/control client access to data attributes + * \brief callback handler to intercept/control client write access to data attributes * * User provided callback function to intercept/control MMS client access to * IEC 61850 data attributes. The application can install the same handler @@ -1025,7 +1261,7 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler * \param connection the connection object of the client connection that invoked the write operation * \param parameter the user provided parameter * - * \return true if access is accepted, false if access is denied. + * \return DATA_ACCESS_ERROR_SUCCESS if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied. */ typedef MmsDataAccessError (*WriteAccessHandler) (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter); @@ -1065,6 +1301,37 @@ typedef enum { void IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPolicy policy); +/** + * \brief callback handler to control client read access to data attributes + * + * User provided callback function to control MMS client read access to IEC 61850 + * data objects. The application is to allow read access to data objects for specific clients only. + * It can be used to implement a role based access control (RBAC). + * + * \param ld the logical device the client wants to access + * \param ln the logical node the client wants to access + * \param dataObject the data object the client wants to access + * \param fc the functional constraint of the access + * \param connection the client connection that causes the access + * \param parameter the user provided parameter + * + * \return DATA_ACCESS_ERROR_SUCCESS if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied. + */ +typedef MmsDataAccessError +(*ReadAccessHandler) (LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter); + +/** + * \brief Install the global read access handler + * + * The read access handler will be called for every read access before the server grants access to the client. + * + * \param self the instance of IedServer to operate on. + * \param handler the callback function that is invoked if a client tries to read a data object. + * \param parameter a user provided parameter that is passed to the callback function. + */ +void +IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); + /**@}*/ /**@}*/ diff --git a/src/iec61850/inc_private/control.h b/src/iec61850/inc_private/control.h index cd127b028..844c74b6b 100644 --- a/src/iec61850/inc_private/control.h +++ b/src/iec61850/inc_private/control.h @@ -25,29 +25,92 @@ #define CONTROL_H_ #include "iec61850_model.h" +#include "iec61850_server.h" #include "mms_server_connection.h" #include "mms_device_model.h" -#include "iec61850_server.h" + +#include "mms_mapping_internal.h" +#include "mms_client_connection.h" + +#include "libiec61850_platform_includes.h" typedef struct sControlObject ControlObject; -ControlObject* -ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* name); +struct sControlObject +{ + MmsDomain* mmsDomain; + IedServer iedServer; + char* lnName; + char* name; -void -ControlObject_destroy(ControlObject* self); + int state; -void -ControlObject_setOper(ControlObject* self, MmsValue* oper); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore stateLock; +#endif -void -ControlObject_setCancel(ControlObject* self, MmsValue* cancel); + MmsValue* mmsValue; + MmsVariableSpecification* typeSpec; + + MmsValue* oper; + MmsValue* sbo; + MmsValue* sbow; + MmsValue* cancel; + + MmsValue* ctlVal; + MmsValue* ctlNum; + MmsValue* origin; + MmsValue* timestamp; + + MmsValue* ctlNumSt; + MmsValue* originSt; + + char ctlObjectName[130]; + + /* for LastAppIError */ + MmsValue* error; + MmsValue* addCause; + + bool selected; + uint64_t selectTime; + uint32_t selectTimeout; + MmsValue* sboClass; + MmsValue* sboTimeout; + + bool timeActivatedOperate; + uint64_t operateTime; + + bool operateOnce; + MmsServerConnection mmsConnection; + + MmsValue* emptyString; + + uint32_t ctlModel; + + bool testMode; + bool interlockCheck; + bool synchroCheck; + + uint32_t operateInvokeId; + + ControlHandler operateHandler; + void* operateHandlerParameter; + + ControlPerformCheckHandler checkHandler; + void* checkHandlerParameter; + + ControlWaitForExecutionHandler waitForExecutionHandler; + void* waitForExecutionHandlerParameter; +}; + +ControlObject* +ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* name, MmsVariableSpecification* operSpec); void -ControlObject_setSBO(ControlObject* self, MmsValue* sbo); +ControlObject_initialize(ControlObject* self); void -ControlObject_setSBOw(ControlObject* self, MmsValue* sbow); +ControlObject_destroy(ControlObject* self); void ControlObject_setMmsValue(ControlObject* self, MmsValue* value); @@ -61,21 +124,6 @@ ControlObject_setTypeSpec(ControlObject* self, MmsVariableSpecification* typeSpe MmsVariableSpecification* ControlObject_getTypeSpec(ControlObject* self); -MmsValue* -ControlObject_getOper(ControlObject* self); - -MmsValue* -ControlObject_getSBOw(ControlObject* self); - -MmsValue* -ControlObject_getSBO(ControlObject* self); - -MmsValue* -ControlObject_getCancel(ControlObject* self); - -void -ControlObject_setCtlVal(ControlObject* self, MmsValue* ctlVal); - char* ControlObject_getName(ControlObject* self); diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index 32bf68d2e..003c1613d 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -3,7 +3,7 @@ * * Library private function definitions for IedServer. * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -43,10 +43,20 @@ struct sIedServer LinkedList clientConnections; uint8_t writeAccessPolicies; +#if (CONFIG_IEC61850_REPORT_SERVICE == 1) + int reportBufferSize; +#endif + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore dataModelLock; #endif +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + bool logServiceEnabled; +#endif + + uint8_t edition; + bool running; }; diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index cff0969ff..56935060c 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -1,7 +1,7 @@ /* * mms_mapping.h * - * Copyright 2013-2016 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -28,12 +28,11 @@ #include "mms_device_model.h" #include "control.h" -typedef enum { - REPORT_CONTROL_NONE, - REPORT_CONTROL_VALUE_UPDATE, - REPORT_CONTROL_VALUE_CHANGED, - REPORT_CONTROL_QUALITY_CHANGED -} ReportInclusionFlag; +#define REPORT_CONTROL_NONE 0U +#define REPORT_CONTROL_VALUE_UPDATE 1U +#define REPORT_CONTROL_VALUE_CHANGED 2U +#define REPORT_CONTROL_QUALITY_CHANGED 4U +#define REPORT_CONTROL_NOT_UPDATED 8U typedef enum { LOG_CONTROL_NONE, @@ -45,11 +44,14 @@ typedef enum { typedef struct sMmsMapping MmsMapping; MmsMapping* -MmsMapping_create(IedModel* model); +MmsMapping_create(IedModel* model, IedServer iedServer); MmsDevice* MmsMapping_getMmsDeviceModel(MmsMapping* mapping); +void +MmsMapping_initializeControlObjects(MmsMapping* self); + void MmsMapping_configureSettingGroups(MmsMapping* self); @@ -90,7 +92,7 @@ DataSet* MmsMapping_createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableList variableList); void -MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, ReportInclusionFlag flag); +MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag); void MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag flag); @@ -137,9 +139,6 @@ MmsMapping_ObjectReferenceToVariableAccessSpec(char* objectReference); char* MmsMapping_varAccessSpecToObjectReference(MmsVariableAccessSpecification* varAccessSpec); -void -MmsMapping_setIedServer(MmsMapping* self, IedServer iedServer); - void MmsMapping_setConnectionIndicationHandler(MmsMapping* self, IedConnectionIndicationHandler handler, void* parameter); @@ -149,6 +148,9 @@ MmsMapping_setLogStorage(MmsMapping* self, const char* logRef, LogStorage logSto void MmsMapping_installWriteAccessHandler(MmsMapping* self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter); +void +MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, void* paramter); + MmsDataAccessError Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsValue* value, MmsServerConnection connection); diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 52b7cf2df..24cc22c1d 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -1,7 +1,7 @@ /* * mms_mapping_internal.h * - * Copyright 2013-2016 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -54,6 +54,11 @@ struct sMmsMapping { LinkedList attributeAccessHandlers; +#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) + ReadAccessHandler readAccessHandler; + void* readAccessHandlerParameter; +#endif + #if (CONFIG_IEC61850_SETTING_GROUPS == 1) LinkedList settingGroups; #endif @@ -64,6 +69,9 @@ struct sMmsMapping { Thread reportWorkerThread; #endif + /* flag indicates if data model is locked --> prevents reports to be sent */ + bool isModelLocked; + IedServer iedServer; IedConnectionIndicationHandler connectionIndicationHandler; diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index e484ad1ea..540078b19 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -80,7 +80,7 @@ typedef struct { Semaphore createNotificationsMutex; /* { covered by mutex } */ #endif - ReportInclusionFlag* inclusionFlags; /* { covered by mutex } */ + uint8_t* inclusionFlags; /* { covered by mutex } */ bool triggered; /* { covered by mutex } */ uint64_t reportTime; /* { covered by mutex } */ @@ -94,16 +94,18 @@ typedef struct { ReportBuffer* reportBuffer; MmsValue* timeOfEntry; + + IedServer server; } ReportControl; ReportControl* -ReportControl_create(bool buffered, LogicalNode* parentLN); +ReportControl_create(bool buffered, LogicalNode* parentLN, int reportBufferSize, IedServer server); void ReportControl_destroy(ReportControl* self); void -ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag); +ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, bool modelLocked); MmsValue* ReportControl_getRCBValue(ReportControl* rc, char* elementName); @@ -123,9 +125,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme void Reporting_activateBufferedReports(MmsMapping* self); +/* periodic check if reports have to be sent */ void Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs); +/* check if report have to be sent after data model update */ +void +Reporting_processReportEventsAfterUnlock(MmsMapping* self); + void Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection); diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index f56bfcc08..26a373171 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1,7 +1,7 @@ /* * ied_server.c * - * Copyright 2013-2016 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -59,7 +59,6 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl strcat(objectName, "$"); } - bool isControlObject = false; bool hasCancel = false; int cancelIndex = 0; bool hasSBOw = false; @@ -72,12 +71,14 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl int coElementCount = coSpec->typeSpec.structure.elementCount; + MmsVariableSpecification* operSpec = NULL; + int j; for (j = 0; j < coElementCount; j++) { MmsVariableSpecification* coElementSpec = coSpec->typeSpec.structure.elements[j]; if (strcmp(coElementSpec->name, "Oper") == 0) { - isControlObject = true; + operSpec = coElementSpec; operIndex = j; } else if (strcmp(coElementSpec->name, "Cancel") == 0) { @@ -96,14 +97,14 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl } } - if (isControlObject) { + if (operSpec) { strcat(objectName, coSpec->name); if (DEBUG_IED_SERVER) printf("IED_SERVER: create control object LN:%s DO:%s\n", lnName, objectName); - ControlObject* controlObject = ControlObject_create(self, domain, lnName, objectName); + ControlObject* controlObject = ControlObject_create(self, domain, lnName, objectName, operSpec); if (controlObject == NULL) goto exit_function; @@ -119,18 +120,13 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl ControlObject_setTypeSpec(controlObject, coSpec); - MmsValue* operVal = MmsValue_getElement(structure, operIndex); - ControlObject_setOper(controlObject, operVal); + controlObject->oper = MmsValue_getElement(structure, operIndex); - if (hasCancel) { - MmsValue* cancelVal = MmsValue_getElement(structure, cancelIndex); - ControlObject_setCancel(controlObject, cancelVal); - } + if (hasCancel) + controlObject->cancel = MmsValue_getElement(structure, cancelIndex); - if (hasSBOw) { - MmsValue* sbowVal = MmsValue_getElement(structure, sBOwIndex); - ControlObject_setSBOw(controlObject, sbowVal); - } + if (hasSBOw) + controlObject->sbow = MmsValue_getElement(structure, sBOwIndex); MmsMapping_addControlObject(mapping, controlObject); } @@ -204,23 +200,19 @@ createMmsServerCache(IedServer self) ) { - char* variableName = StringUtils_createString(3, lnName, "$", fcName); + char variableName[65]; - if (variableName == NULL) goto exit_function; + StringUtils_createStringInBuffer(variableName, 3, lnName, "$", fcName); MmsValue* defaultValue = MmsValue_newDefaultValue(fcSpec); - if (defaultValue == NULL) { - GLOBAL_FREEMEM(variableName); + if (defaultValue == NULL) goto exit_function; - } if (DEBUG_IED_SERVER) printf("ied_server.c: Insert into cache %s - %s\n", logicalDevice->domainName, variableName); MmsServer_insertIntoCache(self->mmsServer, logicalDevice, variableName, defaultValue); - - GLOBAL_FREEMEM(variableName); } } } @@ -395,8 +387,9 @@ updateDataSetsWithCachedValues(IedServer self) } } + IedServer -IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration) +IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguration, IedServerConfig serverConfiguration) { IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer)); @@ -407,22 +400,56 @@ IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfigur self->running = false; self->localIpAddress = NULL; +#if (CONFIG_IEC61850_EDITION_1 == 1) + self->edition = IEC_61850_EDITION_1; +#else + self->edition = IEC_61850_EDITION_2; +#endif + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + self->logServiceEnabled = true; + + if (serverConfiguration) { + self->logServiceEnabled = serverConfiguration->enableLogService; + self->edition = serverConfiguration->edition; + } + +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + #if (CONFIG_MMS_THREADLESS_STACK != 1) self->dataModelLock = Semaphore_create(1); +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + +#if (CONFIG_IEC61850_REPORT_SERVICE == 1) + if (serverConfiguration) + self->reportBufferSize = serverConfiguration->reportBufferSize; + else + self->reportBufferSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; #endif - self->mmsMapping = MmsMapping_create(dataModel); + self->mmsMapping = MmsMapping_create(dataModel, self); self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping); self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration); +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if (serverConfiguration) { + MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService); + MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService); + MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets); + MmsServer_setMaxDomainSpecificDataSets(self->mmsServer, serverConfiguration->maxDomainSpecificDataSets); + MmsServer_setMaxDataSetEntries(self->mmsServer, serverConfiguration->maxDataSetEntries); + MmsServer_enableJournalService(self->mmsServer, serverConfiguration->enableLogService); + MmsServer_setFilestoreBasepath(self->mmsServer, serverConfiguration->fileServiceBasepath); + MmsServer_setMaxConnections(self->mmsServer, serverConfiguration->maxMmsConnections); + } +#endif + MmsMapping_setMmsServer(self->mmsMapping, self->mmsServer); MmsMapping_installHandlers(self->mmsMapping); - MmsMapping_setIedServer(self->mmsMapping, self); - createMmsServerCache(self); dataModel->initializer(); @@ -436,6 +463,8 @@ IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfigur /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */ self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE; + MmsMapping_initializeControlObjects(self->mmsMapping); + #if (CONFIG_IEC61850_REPORT_SERVICE == 1) Reporting_activateBufferedReports(self->mmsMapping); #endif @@ -451,7 +480,13 @@ IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfigur IedServer IedServer_create(IedModel* dataModel) { - return IedServer_createWithTlsSupport(dataModel, NULL); + return IedServer_createWithConfig(dataModel, NULL, NULL); +} + +IedServer +IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration) +{ + return IedServer_createWithConfig(dataModel, tlsConfiguration, NULL); } void @@ -653,11 +688,18 @@ void IedServer_lockDataModel(IedServer self) { MmsServer_lockModel(self->mmsServer); + + self->mmsMapping->isModelLocked = true; } void IedServer_unlockDataModel(IedServer self) { + /* check if reports have to be sent! */ + Reporting_processReportEventsAfterUnlock(self->mmsMapping); + + self->mmsMapping->isModelLocked = false; + MmsServer_unlockModel(self->mmsServer); } @@ -1305,6 +1347,12 @@ IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute, WriteA MmsMapping_installWriteAccessHandler(self->mmsMapping, dataAttribute, handler, parameter); } +void +IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter) +{ + MmsMapping_installReadAccessHandler(self->mmsMapping, handler, parameter); +} + void IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter) { diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c new file mode 100644 index 000000000..b9d1dd037 --- /dev/null +++ b/src/iec61850/server/impl/ied_server_config.c @@ -0,0 +1,190 @@ +/* + * ied_server_config.c + * + * Copyright 2018 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +#include "iec61850_server.h" +#include "libiec61850_platform_includes.h" + +#ifndef CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS +#define CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 100 +#endif + +#ifndef CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS +#define CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS 10 +#endif + +#ifndef CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS +#define CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS 10 +#endif + +IedServerConfig +IedServerConfig_create() +{ + IedServerConfig self = (IedServerConfig) GLOBAL_MALLOC(sizeof(struct sIedServerConfig)); + + if (self) { + self->reportBufferSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; + self->fileServiceBasepath = StringUtils_copyString(CONFIG_VIRTUAL_FILESTORE_BASEPATH); + self->enableFileService = true; + self->enableDynamicDataSetService = true; + self->maxAssociationSpecificDataSets = CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS; + self->maxDomainSpecificDataSets = CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS; + self->maxDataSetEntries = CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS; + self->enableLogService = true; + self->edition = IEC_61850_EDITION_2; + } + + return self; +} + +void +IedServerConfig_destroy(IedServerConfig self) +{ + GLOBAL_FREEMEM(self->fileServiceBasepath); + GLOBAL_FREEMEM(self); +} + +void +IedServerConfig_setEdition(IedServerConfig self, uint8_t edition) +{ + self->edition = edition; +} + +uint8_t +IedServerConfig_getEdition(IedServerConfig self) +{ + return self->edition; +} + +void +IedServerConfig_setReportBufferSize(IedServerConfig self, int reportBufferSize) +{ + self->reportBufferSize = reportBufferSize; +} + +int +IedServerConfig_getReportBufferSize(IedServerConfig self) +{ + return self->reportBufferSize; +} + +void +IedServerConfig_setFileServiceBasePath(IedServerConfig self, const char* basepath) +{ +#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) + GLOBAL_FREEMEM(self->fileServiceBasepath); + self->fileServiceBasepath = StringUtils_copyString(basepath); +#else + if (DEBUG_IED_SERVER) + printf("IED_SERVER_CONFIG: Cannot set file service basepath (enable CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME)!\n"); +#endif +} + +const char* +IedServerConfig_getFileServiceBasePath(IedServerConfig self) +{ + return self->fileServiceBasepath; +} + +void +IedServerConfig_enableFileService(IedServerConfig self, bool enable) +{ + self->enableFileService = enable; +} + +bool +IedServerConfig_isFileServiceEnabled(IedServerConfig self) +{ + return self->enableFileService; +} + +void +IedServerConfig_enableDynamicDataSetService(IedServerConfig self, bool enable) +{ + self->enableDynamicDataSetService = enable; +} + +bool +IedServerConfig_isDynamicDataSetServiceEnabled(IedServerConfig self) +{ + return self->enableDynamicDataSetService; +} + +void +IedServerConfig_setMaxAssociationSpecificDataSets(IedServerConfig self, int maxDataSets) +{ + self->maxAssociationSpecificDataSets = maxDataSets; +} + +int +IedServerConfig_getMaxAssociationSpecificDataSets(IedServerConfig self) +{ + return self->maxAssociationSpecificDataSets; +} + +void +IedServerConfig_setMaxDomainSpecificDataSets(IedServerConfig self, int maxDataSets) +{ + self->maxDomainSpecificDataSets = maxDataSets; +} + +int +IedServerConfig_getMaxDomainSpecificDataSets(IedServerConfig self) +{ + return self->maxDomainSpecificDataSets; +} + +void +IedServerConfig_setMaxDataSetEntries(IedServerConfig self, int maxDataSetEntries) +{ + self->maxDataSetEntries = maxDataSetEntries; +} + +int +IedServerConfig_getMaxDatasSetEntries(IedServerConfig self) +{ + return self->maxDataSetEntries; +} + +void +IedServerConfig_enableLogService(IedServerConfig self, bool enable) +{ + self->enableLogService = enable; +} + +bool +IedServerConfig_isLogServiceEnabled(IedServerConfig self) +{ + return self->enableLogService; +} + +void +IedServerConfig_setMaxMmsConnections(IedServerConfig self, int maxConnections) +{ + self->maxMmsConnections = maxConnections; +} + +int +IedServerConfig_getMaxMmsConnections(IedServerConfig self) +{ + return self->maxMmsConnections; +} diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index 71b3e5ace..2febf4847 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -1,7 +1,7 @@ /* * control.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -22,17 +22,12 @@ */ #include "control.h" -#include "mms_mapping.h" -#include "mms_mapping_internal.h" - -#include "mms_client_connection.h" +#include "mms_mapping.h" +#include "iec61850_server.h" #include "ied_server_private.h" - #include "mms_value_internal.h" -#include "libiec61850_platform_includes.h" - #if (CONFIG_IEC61850_CONTROL_SERVICE == 1) #ifndef DEBUG_IED_SERVER @@ -51,71 +46,6 @@ #define STATE_WAIT_FOR_EXECUTION 4 #define STATE_OPERATE 5 -struct sControlObject -{ - MmsDomain* mmsDomain; - IedServer iedServer; - char* lnName; - char* name; - - int state; - -#if (CONFIG_MMS_THREADLESS_STACK != 1) - Semaphore stateLock; -#endif - - MmsValue* mmsValue; - MmsVariableSpecification* typeSpec; - - MmsValue* oper; - MmsValue* sbo; - MmsValue* sbow; - MmsValue* cancel; - - MmsValue* ctlVal; - MmsValue* ctlNum; - MmsValue* origin; - MmsValue* timestamp; - - char* ctlObjectName; - - /* for LastAppIError */ - MmsValue* error; - MmsValue* addCause; - - bool selected; - uint64_t selectTime; - uint32_t selectTimeout; - MmsValue* sboClass; - MmsValue* sboTimeout; - - bool timeActivatedOperate; - uint64_t operateTime; - - bool operateOnce; - MmsServerConnection mmsConnection; - - MmsValue* emptyString; - - bool initialized; - uint32_t ctlModel; - - bool testMode; - bool interlockCheck; - bool synchroCheck; - - uint32_t operateInvokeId; - - ControlHandler operateHandler; - void* operateHandlerParameter; - - ControlPerformCheckHandler checkHandler; - void* checkHandlerParameter; - - ControlWaitForExecutionHandler waitForExecutionHandler; - void* waitForExecutionHandlerParameter; -}; - void ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connection, char* ctlVariable, int error, ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode); @@ -180,84 +110,6 @@ updateSboTimeoutValue(ControlObject* self) self->selectTimeout = CONFIG_CONTROL_DEFAULT_SBO_TIMEOUT; } -static void -initialize(ControlObject* self) -{ - if (!(self->initialized)) { - - MmsServer mmsServer = IedServer_getMmsServer(self->iedServer); - - self->emptyString = MmsValue_newVisibleString(NULL); - - char* ctlModelName = StringUtils_createString(4, self->lnName, "$CF$", self->name, "$ctlModel"); - - if (DEBUG_IED_SERVER) - printf("initialize control for %s\n", ctlModelName); - - MmsValue* ctlModel = MmsServer_getValueFromCache(mmsServer, - self->mmsDomain, ctlModelName); - - if (ctlModel == NULL) { - if (DEBUG_IED_SERVER) - printf("No control model found for variable %s\n", ctlModelName); - } - - GLOBAL_FREEMEM(ctlModelName); - - char* sboClassName = StringUtils_createString(4, self->lnName, "$CF$", self->name, "$sboClass"); - - self->sboClass = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, sboClassName); - - GLOBAL_FREEMEM(sboClassName); - - self->ctlObjectName = (char*) GLOBAL_MALLOC(130); - - StringUtils_createStringInBuffer(self->ctlObjectName, 5, MmsDomain_getName(self->mmsDomain), "/", - self->lnName, "$CO$", self->name); - - self->error = MmsValue_newIntegerFromInt32(0); - self->addCause = MmsValue_newIntegerFromInt32(0); - - if (ctlModel != NULL) { - uint32_t ctlModelVal = MmsValue_toInt32(ctlModel); - - self->ctlModel = ctlModelVal; - - if (DEBUG_IED_SERVER) - printf(" ctlModel: %i\n", ctlModelVal); - - if ((ctlModelVal == 2) || (ctlModelVal == 4)) { /* SBO */ - char* sboTimeoutName = StringUtils_createString(4, self->lnName, "$CF$", self->name, "$sboTimeout"); - - char* controlObjectReference = StringUtils_createString(6, self->mmsDomain->domainName, "/", self->lnName, "$", - self->name, "$SBO"); - - self->sbo = MmsValue_newVisibleString(controlObjectReference); - - self->sboTimeout = MmsServer_getValueFromCache(mmsServer, - self->mmsDomain, sboTimeoutName); - - updateSboTimeoutValue(self); - - setState(self, STATE_UNSELECTED); - - if (DEBUG_IED_SERVER) - printf("timeout for %s is %i\n", sboTimeoutName, self->selectTimeout); - - GLOBAL_FREEMEM(controlObjectReference); - GLOBAL_FREEMEM(sboTimeoutName); - } - else { - self->sbo = MmsValue_newVisibleString(NULL); - - setState(self, STATE_READY); - } - } - - self->initialized = true; - } -} - static bool isSboClassOperateOnce(ControlObject* self) { @@ -431,7 +283,7 @@ executeControlTask(ControlObject* self) } ControlObject* -ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* name) +ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* name, MmsVariableSpecification* operSpec) { ControlObject* self = (ControlObject*) GLOBAL_CALLOC(1, sizeof(ControlObject)); @@ -463,10 +315,94 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* self->mmsDomain = domain; self->iedServer = iedServer; + MmsVariableSpecification* ctlValSpec = MmsVariableSpecification_getChildSpecificationByName(operSpec, "ctlVal", NULL); + self->ctlVal = MmsValue_newDefaultValue(ctlValSpec); + + MmsVariableSpecification* originSpec = MmsVariableSpecification_getChildSpecificationByName(operSpec, "origin", NULL); + self->origin = MmsValue_newDefaultValue(originSpec); + + self->ctlNum = MmsValue_newUnsigned(8); + exit_function: return self; } +void +ControlObject_initialize(ControlObject* self) +{ + MmsServer mmsServer = IedServer_getMmsServer(self->iedServer); + + self->emptyString = MmsValue_newVisibleString(NULL); + + char strBuf[129]; + + char* ctlModelName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$CF$", self->name, "$ctlModel"); + + if (DEBUG_IED_SERVER) + printf("initialize control for %s\n", ctlModelName); + + MmsValue* ctlModel = MmsServer_getValueFromCache(mmsServer, + self->mmsDomain, ctlModelName); + + if (ctlModel == NULL) { + if (DEBUG_IED_SERVER) + printf("No control model found for variable %s\n", ctlModelName); + } + + char* sboClassName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$CF$", self->name, "$sboClass"); + + self->sboClass = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, sboClassName); + + StringUtils_createStringInBuffer(self->ctlObjectName, 5, MmsDomain_getName(self->mmsDomain), "/", + self->lnName, "$CO$", self->name); + + char* ctlNumName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$ST$", self->name, "$ctlNum"); + + self->ctlNumSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlNumName); + + char* originName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$ST$", self->name, "$origin"); + + self->originSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, originName); + + self->error = MmsValue_newIntegerFromInt32(0); + self->addCause = MmsValue_newIntegerFromInt32(0); + + if (ctlModel != NULL) { + uint32_t ctlModelVal = MmsValue_toInt32(ctlModel); + + self->ctlModel = ctlModelVal; + + if (DEBUG_IED_SERVER) + printf(" ctlModel: %i\n", ctlModelVal); + + if ((ctlModelVal == 2) || (ctlModelVal == 4)) { /* SBO */ + + + char* controlObjectReference = StringUtils_createStringInBuffer(strBuf, 6, self->mmsDomain->domainName, + "/", self->lnName, "$CO$", self->name, "$SBO"); + + self->sbo = MmsValue_newVisibleString(controlObjectReference); + + char* sboTimeoutName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$CF$", self->name, "$sboTimeout"); + + self->sboTimeout = MmsServer_getValueFromCache(mmsServer, + self->mmsDomain, sboTimeoutName); + + updateSboTimeoutValue(self); + + setState(self, STATE_UNSELECTED); + + if (DEBUG_IED_SERVER) + printf("timeout for %s is %i\n", sboTimeoutName, self->selectTimeout); + } + else { + self->sbo = MmsValue_newVisibleString(NULL); + + setState(self, STATE_READY); + } + } +} + void ControlObject_destroy(ControlObject* self) { @@ -479,9 +415,6 @@ ControlObject_destroy(ControlObject* self) if (self->emptyString != NULL) MmsValue_delete(self->emptyString); - if (self->ctlObjectName != NULL) - GLOBAL_FREEMEM(self->ctlObjectName); - if (self->error != NULL) MmsValue_delete(self->error); @@ -508,36 +441,6 @@ ControlObject_destroy(ControlObject* self) GLOBAL_FREEMEM(self); } -void -ControlObject_setOper(ControlObject* self, MmsValue* oper) -{ - self->oper = oper; -} - -void -ControlObject_setCancel(ControlObject* self, MmsValue* cancel) -{ - self->cancel = cancel; -} - -void -ControlObject_setSBOw(ControlObject* self, MmsValue* sbow) -{ - self->sbow = sbow; -} - -void -ControlObject_setSBO(ControlObject* self, MmsValue* sbo) -{ - self->sbo = sbo; -} - -void -ControlObject_setCtlVal(ControlObject* self, MmsValue* ctlVal) -{ - self->ctlVal = ctlVal; -} - char* ControlObject_getName(ControlObject* self) { @@ -556,30 +459,6 @@ ControlObject_getDomain(ControlObject* self) return self->mmsDomain; } -MmsValue* -ControlObject_getOper(ControlObject* self) -{ - return self->oper; -} - -MmsValue* -ControlObject_getSBOw(ControlObject* self) -{ - return self->sbow; -} - -MmsValue* -ControlObject_getSBO(ControlObject* self) -{ - return self->sbo; -} - -MmsValue* -ControlObject_getCancel(ControlObject* self) -{ - return self->cancel; -} - void ControlObject_setMmsValue(ControlObject* self, MmsValue* value) { @@ -1019,18 +898,15 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connect static void updateControlParameters(ControlObject* controlObject, MmsValue* ctlVal, MmsValue* ctlNum, MmsValue* origin) { - if (controlObject->ctlVal != NULL) - MmsValue_delete(controlObject->ctlVal); + MmsValue_update(controlObject->ctlVal, ctlVal); + MmsValue_update(controlObject->ctlNum, ctlNum); + MmsValue_update(controlObject->origin, origin); - if (controlObject->ctlNum != NULL) - MmsValue_delete(controlObject->ctlNum); + if (controlObject->ctlNumSt) + MmsValue_update(controlObject->ctlNumSt, ctlNum); - if (controlObject->origin != NULL) - MmsValue_delete(controlObject->origin); - - controlObject->ctlVal = MmsValue_clone(ctlVal); - controlObject->ctlNum = MmsValue_clone(ctlNum); - controlObject->origin = MmsValue_clone(origin); + if (controlObject->originSt) + MmsValue_update(controlObject->originSt, origin); } static bool @@ -1122,13 +998,11 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia if (controlObject != NULL) { - initialize(controlObject); - if (varName != NULL) { if (strcmp(varName, "Oper") == 0) - value = ControlObject_getOper(controlObject); + value = controlObject->oper; else if (strcmp(varName, "SBOw") == 0) - value = ControlObject_getSBOw(controlObject); + value = controlObject->sbow; else if (strcmp(varName, "SBO") == 0) { if (controlObject->ctlModel == 2) { @@ -1153,7 +1027,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia if (checkResult == CONTROL_ACCEPTED) { selectObject(controlObject, currentTime, connection); - value = ControlObject_getSBO(controlObject); + value = controlObject->sbo; } } @@ -1162,12 +1036,12 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia if (DEBUG_IED_SERVER) printf("IED_SERVER: select not applicable for control model %i\n", controlObject->ctlModel); - value = ControlObject_getSBO(controlObject); + value = controlObject->sbo; } } else if (strcmp(varName, "Cancel") == 0) - value = ControlObject_getCancel(controlObject); + value = controlObject->cancel; else { value = MmsValue_getSubElement(ControlObject_getMmsValue(controlObject), ControlObject_getTypeSpec(controlObject), varName); @@ -1289,8 +1163,6 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - initialize(controlObject); - if (strcmp(varName, "SBOw") == 0) { /* select with value */ if (controlObject->ctlModel == 4) { @@ -1494,12 +1366,6 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari setState(controlObject, STATE_WAIT_FOR_EXECUTION); initiateControlTask(controlObject); - -#if (CONFIG_MMS_THREADLESS_STACK == 1) - //TODO call this in single threaded version to increase response time!? - //executeControlTask(controlObject); -#endif - } else { indication = (MmsDataAccessError) checkResult; diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index 2a59c43f5..83f88ca21 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -26,7 +26,6 @@ #include "mms_mapping.h" #include "logging.h" #include "linked_list.h" -#include "array_list.h" #include "hal_thread.h" #include "simple_allocator.h" diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index e186a49d5..b15be8304 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -28,7 +28,6 @@ #include "libiec61850_platform_includes.h" #include "mms_mapping.h" #include "linked_list.h" -#include "array_list.h" #include "hal_thread.h" diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 9a9e11cc3..b8d7ee8e1 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1,7 +1,7 @@ /* * mms_mapping.c * - * Copyright 2013-2017 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,7 +24,6 @@ #include "libiec61850_platform_includes.h" #include "mms_mapping.h" #include "mms_mapping_internal.h" -#include "array_list.h" #include "stack_config.h" #include "mms_goose.h" @@ -267,6 +266,15 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute) case IEC61850_PHYCOMADDR: MmsMapping_createPhyComAddrStructure(namedVariable); break; + case IEC61850_OPTFLDS: + namedVariable->typeSpec.bitString = 10; + namedVariable->type = MMS_BIT_STRING; + break; + case IEC61850_TRGOPS: + namedVariable->typeSpec.bitString = 6; + namedVariable->type = MMS_BIT_STRING; + break; + default: if (DEBUG_IED_SERVER) printf("MMS-MAPPING: type cannot be mapped %i\n", attribute->type); @@ -591,6 +599,20 @@ MmsMapping_checkForSettingGroupReservationTimeouts(MmsMapping* self, uint64_t cu } } +void +MmsMapping_initializeControlObjects(MmsMapping* self) +{ + LinkedList element = LinkedList_getNext(self->controlObjects); + + while (element) { + ControlObject* controlObject = (ControlObject*) LinkedList_getData(element); + + ControlObject_initialize(controlObject); + + element = LinkedList_getNext(element); + } +} + void MmsMapping_configureSettingGroups(MmsMapping* self) { @@ -814,6 +836,8 @@ countSVControlBlocksForLogicalNode(MmsMapping* self, LogicalNode* logicalNode, b #endif /* (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) */ +#if (CONFIG_IEC61850_SETTING_GROUPS == 1) + static SettingGroupControlBlock* checkForSgcb(MmsMapping* self, LogicalNode* logicalNode) { @@ -829,6 +853,9 @@ checkForSgcb(MmsMapping* self, LogicalNode* logicalNode) return NULL; } +#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ + + static MmsVariableSpecification* createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode) @@ -842,9 +869,8 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain, int componentCount = determineLogicalNodeComponentCount(logicalNode); - SettingGroupControlBlock* sgControlBlock = NULL; - #if (CONFIG_IEC61850_SETTING_GROUPS == 1) + SettingGroupControlBlock* sgControlBlock = NULL; sgControlBlock = checkForSgcb(self, logicalNode); @@ -998,12 +1024,22 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain, #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ #if (CONFIG_IEC61850_LOG_SERVICE == 1) - if (lcbCount > 0) { - namedVariable->typeSpec.structure.elements[currentComponent] = - Logging_createLCBs(self, domain, logicalNode, lcbCount); - currentComponent++; +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if (self->iedServer->logServiceEnabled) { +#endif + + if (lcbCount > 0) { + namedVariable->typeSpec.structure.elements[currentComponent] = + Logging_createLCBs(self, domain, logicalNode, lcbCount); + + currentComponent++; + } + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) } +#endif + #endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ @@ -1106,33 +1142,42 @@ createMmsDomainFromIedDevice(MmsMapping* self, LogicalDevice* logicalDevice) goto exit_function; #if (CONFIG_IEC61850_LOG_SERVICE == 1) - /* add logs (journals) */ - Log* log = self->model->logs; - while (log != NULL) { +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if (self->iedServer->logServiceEnabled) { +#endif + /* add logs (journals) */ + Log* log = self->model->logs; - char journalName[65]; + while (log != NULL) { - int nameLength = strlen(log->parent->name) + strlen(log->name); + char journalName[65]; - if (nameLength > 63) { - if (DEBUG_IED_SERVER) - printf("IED_SERVER: Log name %s invalid! Resulting journal name too long! Skip log\n", log->name); - } - else { - strcpy(journalName, log->parent->name); - strcat(journalName, "$"); - strcat(journalName, log->name); + int nameLength = strlen(log->parent->name) + strlen(log->name); + + if (nameLength > 63) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: Log name %s invalid! Resulting journal name too long! Skip log\n", log->name); + } + else { + strcpy(journalName, log->parent->name); + strcat(journalName, "$"); + strcat(journalName, log->name); + + MmsDomain_addJournal(domain, journalName); - MmsDomain_addJournal(domain, journalName); + LogInstance* logInstance = LogInstance_create(log->parent, log->name); - LogInstance* logInstance = LogInstance_create(log->parent, log->name); + LinkedList_add(self->logInstances, (void*) logInstance); + } - LinkedList_add(self->logInstances, (void*) logInstance); + log = log->sibling; } - log = log->sibling; +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) } +#endif + #endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ int nodesCount = LogicalDevice_getLogicalNodeCount(logicalDevice); @@ -1259,11 +1304,12 @@ createMmsModelFromIedModel(MmsMapping* self, IedModel* iedModel) } MmsMapping* -MmsMapping_create(IedModel* model) +MmsMapping_create(IedModel* model, IedServer iedServer) { MmsMapping* self = (MmsMapping*) GLOBAL_CALLOC(1, sizeof(struct sMmsMapping)); self->model = model; + self->iedServer = iedServer; #if (CONFIG_IEC61850_REPORT_SERVICE == 1) self->reportControls = LinkedList_create(); @@ -1294,6 +1340,7 @@ MmsMapping_create(IedModel* model) self->attributeAccessHandlers = LinkedList_create(); + /* create data model specification */ self->mmsDevice = createMmsModelFromIedModel(self, model); return self; @@ -2047,8 +2094,12 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, MmsValue* matchingValue = checkIfValueBelongsToModelNode(dataAttribute, cachedValue, value); if (matchingValue != NULL) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + MmsDataAccessError handlerResult = - accessHandler->handler(dataAttribute, matchingValue, (ClientConnection) connection, + accessHandler->handler(dataAttribute, matchingValue, clientConnection, accessHandler->parameter); if (handlerResult == DATA_ACCESS_ERROR_SUCCESS) @@ -2060,8 +2111,12 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, } else { /* if ACCESS_POLICY_DENY only allow direct access to handled data attribute */ if (dataAttribute->mmsValue == cachedValue) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + MmsDataAccessError handlerResult = - accessHandler->handler(dataAttribute, value, (ClientConnection) connection, + accessHandler->handler(dataAttribute, value, clientConnection, accessHandler->parameter); if (handlerResult == DATA_ACCESS_ERROR_SUCCESS) { @@ -2134,6 +2189,15 @@ MmsMapping_installWriteAccessHandler(MmsMapping* self, DataAttribute* dataAttrib accessHandler->handler = handler; } +void +MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, void* parameter) +{ +#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) + self->readAccessHandler = handler; + self->readAccessHandlerParameter = parameter; +#endif +} + #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) static MmsValue* @@ -2293,6 +2357,9 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo } #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ + /* handle read access to other objects */ + + exit_function: return retValue; } @@ -2378,24 +2445,93 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS char* separator = strchr(variableId, '$'); - if (separator == NULL) - return DATA_ACCESS_ERROR_SUCCESS; - #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - if (isFunctionalConstraintSE(separator)) { - SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); + if (separator) { + if (isFunctionalConstraintSE(separator)) { + SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); - if (sg != NULL) { - if (sg->sgcb->editSG == 0) - return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + if (sg != NULL) { + if (sg->sgcb->editSG == 0) + return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + } + else + return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; } - else - return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; } #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ +#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) + if (self->readAccessHandler != NULL) + { + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + if (ld != NULL) { + + char str[65]; + + FunctionalConstraint fc; + + if (separator != NULL) { + fc = FunctionalConstraint_fromString(separator + 1); + + if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US || + fc == IEC61850_FC_MS || fc == IEC61850_FC_RP || + fc == IEC61850_FC_LG) + { + return DATA_ACCESS_ERROR_SUCCESS; + } + else { + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln != NULL) { + + + char* doStart = strchr(separator + 1, '$'); + + + if (doStart != NULL) { + + char* doEnd = strchr(doStart + 1, '$'); + + if (doEnd == NULL) { + StringUtils_copyStringToBuffer(doStart + 1, str); + } + else { + doEnd--; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); + } + + ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + + if (dobj != NULL) { + + if (dobj->modelType == DataObjectModelType) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + return self->readAccessHandler(ld, ln, (DataObject*) dobj, fc, clientConnection, + self->readAccessHandlerParameter); + } + } + } + } + } + } + } + + return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; + } +#endif /* CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL */ + return DATA_ACCESS_ERROR_SUCCESS; } @@ -2550,12 +2686,6 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installVariableListChangedHandler(self->mmsServer, variableListChangedHandler, (void*) self); } -void -MmsMapping_setIedServer(MmsMapping* self, IedServer iedServer) -{ - self->iedServer = iedServer; -} - void MmsMapping_setConnectionIndicationHandler(MmsMapping* self, IedConnectionIndicationHandler handler, void* parameter) { @@ -2714,7 +2844,7 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl #if (CONFIG_IEC61850_REPORT_SERVICE == 1) void -MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, ReportInclusionFlag flag) +MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) { LinkedList element = self->reportControls; @@ -2743,7 +2873,10 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, ReportInclu } if (DataSet_isMemberValue(rc->dataSet, value, &index)) { - ReportControl_valueUpdated(rc, index, flag); + + bool modelLocked = self->isModelLocked; + + ReportControl_valueUpdated(rc, index, flag, modelLocked); } } } diff --git a/src/iec61850/server/mms_mapping/mms_sv.c b/src/iec61850/server/mms_mapping/mms_sv.c index 67f387cbd..030874031 100644 --- a/src/iec61850/server/mms_mapping/mms_sv.c +++ b/src/iec61850/server/mms_mapping/mms_sv.c @@ -28,7 +28,6 @@ #include "libiec61850_platform_includes.h" #include "mms_mapping.h" #include "linked_list.h" -#include "array_list.h" #include "mms_sv.h" diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index d5eae3e86..1f5d8847f 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1,7 +1,7 @@ /* * reporting.c * - * Copyright 2013 - 2016 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,7 +24,6 @@ #include "libiec61850_platform_includes.h" #include "mms_mapping.h" #include "linked_list.h" -#include "array_list.h" #include "stack_config.h" #include "hal_thread.h" @@ -35,6 +34,7 @@ #include "mms_value_internal.h" #include "conversions.h" #include "reporting.h" +#include "ied_server_private.h" #include #ifndef DEBUG_IED_SERVER @@ -47,31 +47,26 @@ #define CONFIG_IEC61850_BRCB_WITH_RESVTMS 0 #endif - -#ifndef CONFIG_IEC61850_EDITION_1 -#define CONFIG_IEC61850_EDITION_1 0 -#endif - -#if (CONFIG_IEC61850_EDITION_1 == 1) -#define CONFIG_REPORTING_SUPPORTS_OWNER 0 -#endif - -#ifndef CONFIG_REPORTING_SUPPORTS_OWNER -#define CONFIG_REPORTING_SUPPORTS_OWNER 1 -#endif - - static ReportBuffer* -ReportBuffer_create(void) +ReportBuffer_create(int bufferSize) { ReportBuffer* self = (ReportBuffer*) GLOBAL_MALLOC(sizeof(ReportBuffer)); - self->lastEnqueuedReport = NULL; - self->oldestReport = NULL; - self->nextToTransmit = NULL; - self->memoryBlockSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; - self->memoryBlock = (uint8_t*) GLOBAL_MALLOC(self->memoryBlockSize); - self->reportsCount = 0; - self->isOverflow = true; + + if (self) { + self->lastEnqueuedReport = NULL; + self->oldestReport = NULL; + self->nextToTransmit = NULL; + self->reportsCount = 0; + self->isOverflow = true; + + self->memoryBlockSize = bufferSize; + self->memoryBlock = (uint8_t*) GLOBAL_MALLOC(self->memoryBlockSize); + + if (self->memoryBlock == NULL) { + GLOBAL_FREEMEM(self); + self = NULL; + } + } return self; } @@ -84,7 +79,7 @@ ReportBuffer_destroy(ReportBuffer* self) } ReportControl* -ReportControl_create(bool buffered, LogicalNode* parentLN) +ReportControl_create(bool buffered, LogicalNode* parentLN, int reportBufferSize, IedServer iedServer) { ReportControl* self = (ReportControl*) GLOBAL_MALLOC(sizeof(ReportControl)); self->name = NULL; @@ -119,8 +114,10 @@ ReportControl_create(bool buffered, LogicalNode* parentLN) self->valueReferences = NULL; self->lastEntryId = 0; + self->server = iedServer; + if (buffered) { - self->reportBuffer = ReportBuffer_create(); + self->reportBuffer = ReportBuffer_create(reportBufferSize); } return self; @@ -148,6 +145,9 @@ purgeBuf(ReportControl* rc) { if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: run purgeBuf\n"); + /* reset trigger */ + rc->triggered = false; + ReportBuffer* reportBuffer = rc->reportBuffer; reportBuffer->lastEnqueuedReport = NULL; @@ -248,15 +248,21 @@ ReportControl_getRCBValue(ReportControl* rc, char* elementName) return MmsValue_getElement(rc->rcbValues, 11); else if (strcmp(elementName, "TimeofEntry") == 0) return MmsValue_getElement(rc->rcbValues, 12); + + if (rc->server->edition >= IEC_61850_EDITION_2) { #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) - else if (strcmp(elementName, "ResvTms") == 0) - return MmsValue_getElement(rc->rcbValues, 13); - else if (strcmp(elementName, "Owner") == 0) - return MmsValue_getElement(rc->rcbValues, 14); + if (strcmp(elementName, "ResvTms") == 0) + return MmsValue_getElement(rc->rcbValues, 13); + if (strcmp(elementName, "Owner") == 0) + return MmsValue_getElement(rc->rcbValues, 14); #else - else if (strcmp(elementName, "Owner") == 0) - return MmsValue_getElement(rc->rcbValues, 13); + if (strcmp(elementName, "Owner") == 0) + return MmsValue_getElement(rc->rcbValues, 13); #endif + } + + + } else { if (strcmp(elementName, "RptID") == 0) return MmsValue_getElement(rc->rcbValues, 0); @@ -287,6 +293,14 @@ ReportControl_getRCBValue(ReportControl* rc, char* elementName) return NULL ; } +static inline void +clearInclusionFlags(ReportControl* reportControl) +{ + int i; + for (i = 0; i < reportControl->dataSet->elementCount; i++) + reportControl->inclusionFlags[i] = REPORT_CONTROL_NONE; +} + static void updateTimeOfEntry(ReportControl* self, uint64_t currentTime) { @@ -522,8 +536,52 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, else dataSetValue = ReportControl_getRCBValue(rc, "DatSet"); + + bool dataSetChanged = true; + + /* check if old and new data sets are the same */ + if (rc->dataSet && dataSetValue) { + + const char* dataSetLdName = rc->dataSet->logicalDeviceName; + const char* dataSetName = rc->dataSet->name; + const char* newDataSetName = MmsValue_toString(dataSetValue); + + if (newDataSetName[0] == '@') { + if ((dataSetLdName == NULL) && (!strcmp(dataSetName, newDataSetName + 1))) { + dataSetChanged = false; + } + } + else if (newDataSetName[0] == '/') { + if ((dataSetLdName == NULL) && (!strcmp(dataSetName, newDataSetName + 1))) { + dataSetChanged = false; + } + } + else { + if (dataSetLdName && dataSetName) { + + char externalVisibleName[256]; + + /* Construct external visible name */ + strcpy(externalVisibleName, mapping->model->name); + strcat(externalVisibleName, dataSetLdName); + strcat(externalVisibleName, "/"); + strcat(externalVisibleName, dataSetName); + + if (!(strcmp(externalVisibleName, newDataSetName))) { + dataSetChanged = false; + } + } + } + + if (rc->buffered) { + if (dataSetChanged) + purgeBuf(rc); + } + } + + if (rc->isDynamicDataSet) { - if (rc->dataSet != NULL) { + if (rc->dataSet && dataSetChanged) { deleteDataSetValuesShadowBuffer(rc); MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); rc->isDynamicDataSet = false; @@ -531,7 +589,7 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, } } - if (dataSetValue != NULL) { + if (dataSetValue && dataSetChanged) { const char* dataSetName = MmsValue_toString(dataSetValue); DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName); @@ -581,21 +639,28 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ - deleteDataSetValuesShadowBuffer(rc); + if ((dataSet == NULL) || (dataSetChanged == true)) { + + /* delete pending event and create buffer for new data set */ + deleteDataSetValuesShadowBuffer(rc); + + rc->dataSet = dataSet; - rc->dataSet = dataSet; + createDataSetValuesShadowBuffer(rc); - createDataSetValuesShadowBuffer(rc); + if (rc->inclusionField != NULL) + MmsValue_delete(rc->inclusionField); - if (rc->inclusionField != NULL) - MmsValue_delete(rc->inclusionField); + rc->inclusionField = MmsValue_newBitString(dataSet->elementCount); - rc->inclusionField = MmsValue_newBitString(dataSet->elementCount); + rc->triggered = false; - if (rc->inclusionFlags != NULL) - GLOBAL_FREEMEM(rc->inclusionFlags); + if (rc->inclusionFlags != NULL) + GLOBAL_FREEMEM(rc->inclusionFlags); - rc->inclusionFlags = (ReportInclusionFlag*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(ReportInclusionFlag)); + rc->inclusionFlags = (uint8_t*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(uint8_t)); + + } success = true; @@ -604,6 +669,9 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, goto exit_function; } + else { + success = true; + } exit_function: return success; @@ -757,11 +825,12 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock, mmsValue->deleteValue = false; mmsValue->type = MMS_STRUCTURE; -#if (CONFIG_REPORTING_SUPPORTS_OWNER == 1) - int structSize = 12; -#else - int structSize = 11; -#endif + int structSize; + + if (reportControl->server->edition >= IEC_61850_EDITION_2) + structSize = 12; + else + structSize = 11; mmsValue->value.structure.size = structSize; mmsValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(structSize, sizeof(MmsValue*)); @@ -863,14 +932,14 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock, rcb->typeSpec.structure.elements[10] = namedVariable; mmsValue->value.structure.components[10] = MmsValue_newBoolean(false); -#if (CONFIG_REPORTING_SUPPORTS_OWNER == 1) - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); - namedVariable->name = StringUtils_copyString("Owner"); - namedVariable->type = MMS_OCTET_STRING; - namedVariable->typeSpec.octetString = -64; - rcb->typeSpec.structure.elements[11] = namedVariable; - mmsValue->value.structure.components[11] = MmsValue_newOctetString(0, 128); -#endif /* (CONFIG_REPORTING_SUPPORTS_OWNER == 1) */ + if (reportControl->server->edition >= IEC_61850_EDITION_2) { + namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable->name = StringUtils_copyString("Owner"); + namedVariable->type = MMS_OCTET_STRING; + namedVariable->typeSpec.octetString = -64; + rcb->typeSpec.structure.elements[11] = namedVariable; + mmsValue->value.structure.components[11] = MmsValue_newOctetString(0, 128); + } reportControl->rcbValues = mmsValue; @@ -893,13 +962,14 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, int brcbElementCount = 13; + if (reportControl->server->edition >= IEC_61850_EDITION_2) { + #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) - brcbElementCount++; + brcbElementCount++; #endif -#if (CONFIG_REPORTING_SUPPORTS_OWNER == 1) - brcbElementCount++; -#endif + brcbElementCount++; + } MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); mmsValue->deleteValue = false; @@ -1021,28 +1091,26 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, reportControl->timeOfEntry = mmsValue->value.structure.components[12]; -#if ((CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) || (CONFIG_REPORTING_SUPPORTS_OWNER == 1)) - int currentIndex = 13; -#endif + if (reportControl->server->edition >= IEC_61850_EDITION_2) { + int currentIndex = 13; #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); - namedVariable->name = StringUtils_copyString("ResvTms"); - namedVariable->type = MMS_INTEGER; - namedVariable->typeSpec.integer = 16; - rcb->typeSpec.structure.elements[currentIndex] = namedVariable; - mmsValue->value.structure.components[currentIndex] = MmsValue_newInteger(16); - currentIndex++; + namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable->name = StringUtils_copyString("ResvTms"); + namedVariable->type = MMS_INTEGER; + namedVariable->typeSpec.integer = 16; + rcb->typeSpec.structure.elements[currentIndex] = namedVariable; + mmsValue->value.structure.components[currentIndex] = MmsValue_newInteger(16); + currentIndex++; #endif /* (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) */ -#if (CONFIG_REPORTING_SUPPORTS_OWNER == 1) - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); - namedVariable->name = StringUtils_copyString("Owner"); - namedVariable->type = MMS_OCTET_STRING; - namedVariable->typeSpec.octetString = -64; - rcb->typeSpec.structure.elements[currentIndex] = namedVariable; - mmsValue->value.structure.components[currentIndex] = MmsValue_newOctetString(0, 128); /* size 4 is enough to store client IPv4 address */ -#endif /* (CONFIG_REPORTING_SUPPORTS_OWNER == 1) */ + namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable->name = StringUtils_copyString("Owner"); + namedVariable->type = MMS_OCTET_STRING; + namedVariable->typeSpec.octetString = -64; + rcb->typeSpec.structure.elements[currentIndex] = namedVariable; + mmsValue->value.structure.components[currentIndex] = MmsValue_newOctetString(0, 128); /* size 4 is enough to store client IPv4 address */ + } reportControl->rcbValues = mmsValue; @@ -1096,7 +1164,7 @@ Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, int currentReport = 0; while (currentReport < reportsCount) { - ReportControl* rc = ReportControl_create(true, logicalNode); + ReportControl* rc = ReportControl_create(true, logicalNode, self->iedServer->reportBufferSize, self->iedServer); rc->domain = domain; @@ -1133,7 +1201,7 @@ Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain, int currentReport = 0; while (currentReport < reportsCount) { - ReportControl* rc = ReportControl_create(false, logicalNode); + ReportControl* rc = ReportControl_create(false, logicalNode, self->iedServer->reportBufferSize, self->iedServer); rc->domain = domain; @@ -1159,58 +1227,59 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) { rc->clientConnection = connection; -#if (CONFIG_REPORTING_SUPPORTS_OWNER == 1) - MmsValue* owner = ReportControl_getRCBValue(rc, "Owner"); + if (rc->server->edition >= IEC_61850_EDITION_2) { - if (owner != NULL) { + MmsValue* owner = ReportControl_getRCBValue(rc, "Owner"); - if (connection != NULL) { - char* clientAddressString = MmsServerConnection_getClientAddress(connection); + if (owner != NULL) { - if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: set owner to %s\n", clientAddressString); + if (connection != NULL) { + char* clientAddressString = MmsServerConnection_getClientAddress(connection); - if (strchr(clientAddressString, '.') != NULL) { - if (DEBUG_IED_SERVER) - printf("IED_SERVER: reporting.c: client address is IPv4 address\n"); + if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: set owner to %s\n", clientAddressString); - uint8_t ipV4Addr[4]; + if (strchr(clientAddressString, '.') != NULL) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: reporting.c: client address is IPv4 address\n"); - int addrElementCount = 0; + uint8_t ipV4Addr[4]; - char* separator = clientAddressString; + int addrElementCount = 0; - while (separator != NULL && addrElementCount < 4) { - int intVal = atoi(separator); + char* separator = clientAddressString; - ipV4Addr[addrElementCount] = intVal; + while (separator != NULL && addrElementCount < 4) { + int intVal = atoi(separator); - separator = strchr(separator, '.'); + ipV4Addr[addrElementCount] = intVal; - if (separator != NULL) - separator++; // skip '.' character + separator = strchr(separator, '.'); - addrElementCount ++; - } + if (separator != NULL) + separator++; // skip '.' character - if (addrElementCount == 4) - MmsValue_setOctetString(owner, ipV4Addr, 4); - else - MmsValue_setOctetString(owner, ipV4Addr, 0); + addrElementCount ++; + } + if (addrElementCount == 4) + MmsValue_setOctetString(owner, ipV4Addr, 4); + else + MmsValue_setOctetString(owner, ipV4Addr, 0); + + } + else { + uint8_t ipV6Addr[16]; + MmsValue_setOctetString(owner, ipV6Addr, 0); + if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: client address is IPv6 address or unknown\n"); + } } else { - uint8_t ipV6Addr[16]; - MmsValue_setOctetString(owner, ipV6Addr, 0); - if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: client address is IPv6 address or unknown\n"); + uint8_t emptyAddr[1]; + MmsValue_setOctetString(owner, emptyAddr, 0); } } - else { - uint8_t emptyAddr[1]; - MmsValue_setOctetString(owner, emptyAddr, 0); - } - } -#endif /* CONFIG_REPORTING_SUPPORTS_OWNER == 1*/ + } } @@ -1346,8 +1415,8 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->isResync = false; } else { - GLOBAL_FREEMEM(rc->inclusionFlags); - rc->inclusionFlags = NULL; + if (rc->dataSet) + clearInclusionFlags(rc); MmsValue* resv = ReportControl_getRCBValue(rc, "Resv"); MmsValue_setBoolean(resv, false); @@ -1568,11 +1637,6 @@ Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection c if (rc->buffered == false) { - if (rc->inclusionField != NULL) { - MmsValue_delete(rc->inclusionField); - rc->inclusionField = NULL; - } - MmsValue* resv = ReportControl_getRCBValue(rc, "Resv"); MmsValue_setBoolean(resv, false); @@ -1688,9 +1752,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ ReportBuffer* buffer = reportControl->reportBuffer; /* calculate size of complete buffer entry */ - int bufferEntrySize = sizeof(ReportBufferEntry); + int bufferEntrySize = MemoryAllocator_getAlignedSize(sizeof(ReportBufferEntry)); - int inclusionFieldSize = MmsValue_getBitStringByteSize(reportControl->inclusionField); + int inclusionFieldSize = MemoryAllocator_getAlignedSize(MmsValue_getBitStringByteSize(reportControl->inclusionField)); MmsValue inclusionFieldStatic; @@ -1708,7 +1772,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ for (i = 0; i < MmsValue_getBitStringSize(reportControl->inclusionField); i++) { assert(dataSetEntry != NULL); - bufferEntrySize += 1; /* reason-for-inclusion */ + bufferEntrySize += MemoryAllocator_getAlignedSize(1); /* reason-for-inclusion */ bufferEntrySize += MmsValue_getSizeInMemory(dataSetEntry->value); @@ -1723,7 +1787,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ for (i = 0; i < MmsValue_getBitStringSize(reportControl->inclusionField); i++) { if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { - bufferEntrySize += 1; /* reason-for-inclusion */ + bufferEntrySize += MemoryAllocator_getAlignedSize(1); /* reason-for-inclusion */ assert(reportControl->bufferedDataSetValues[i] != NULL); @@ -1946,12 +2010,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ else entry->flags = 0; - if ((bufferEntrySize % sizeof(void*)) > 0) - bufferEntrySize = sizeof(void*) * ((bufferEntrySize + sizeof(void*) - 1) / sizeof(void*)); - - entry->entryLength = bufferEntrySize; + entry->entryLength = MemoryAllocator_getAlignedSize(bufferEntrySize); - entryBufPos += sizeof(ReportBufferEntry); + entryBufPos += MemoryAllocator_getAlignedSize(sizeof(ReportBufferEntry)); if (isIntegrity || isGI) { DataSetEntry* dataSetEntry = reportControl->dataSet->fcdas; @@ -1963,7 +2024,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ assert(dataSetEntry != NULL); *entryBufPos = (uint8_t) reportControl->inclusionFlags[i]; - entryBufPos++; + entryBufPos += MemoryAllocator_getAlignedSize(1); entryBufPos = MmsValue_cloneToBuffer(dataSetEntry->value, entryBufPos); @@ -1985,7 +2046,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ assert(reportControl->bufferedDataSetValues[i] != NULL); *entryBufPos = (uint8_t) reportControl->inclusionFlags[i]; - entryBufPos++; + entryBufPos += MemoryAllocator_getAlignedSize(1); entryBufPos = MmsValue_cloneToBuffer(reportControl->bufferedDataSetValues[i], entryBufPos); @@ -1995,11 +2056,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ } } - /* clear inclusion flags */ - int i; - - for (i = 0; i < reportControl->dataSet->elementCount; i++) - reportControl->inclusionFlags[i] = REPORT_CONTROL_NONE; + clearInclusionFlags(reportControl); if (DEBUG_IED_SERVER) printf("IED_SERVER: enqueueReport: encoded %i bytes for report (estimated %i) at buffer offset %i\n", @@ -2078,7 +2135,7 @@ sendNextReportEntry(ReportControl* self) MmsValue* inclusionField = &inclusionFieldStack; if (report->flags == 0) - currentReportBufferPos += MmsValue_getBitStringByteSize(inclusionField); + currentReportBufferPos += MemoryAllocator_getAlignedSize(MmsValue_getBitStringByteSize(inclusionField)); else { inclusionFieldStack.value.bitString.buf = (uint8_t*) MemoryAllocator_allocate(&ma, MmsValue_getBitStringByteSize(inclusionField)); @@ -2244,7 +2301,7 @@ sendNextReportEntry(ReportControl* self) for (i = 0; i < self->dataSet->elementCount; i++) { if (report->flags > 0) { - currentReportBufferPos++; + currentReportBufferPos += MemoryAllocator_getAlignedSize(1);; if (MemAllocLinkedList_add(reportElements, currentReportBufferPos) == NULL) goto return_out_of_memory; @@ -2252,7 +2309,7 @@ sendNextReportEntry(ReportControl* self) } else { if (MmsValue_getBitStringBit(inclusionField, i)) { - currentReportBufferPos++; + currentReportBufferPos += MemoryAllocator_getAlignedSize(1);; if (MemAllocLinkedList_add(reportElements, currentReportBufferPos) == NULL) goto return_out_of_memory; currentReportBufferPos += MmsValue_getSizeInMemory((MmsValue*) currentReportBufferPos); @@ -2289,7 +2346,7 @@ sendNextReportEntry(ReportControl* self) if (MemAllocLinkedList_add(reportElements, reason) == NULL) goto return_out_of_memory; - currentReportBufferPos++; + currentReportBufferPos += MemoryAllocator_getAlignedSize(1); MmsValue* dataSetElement = (MmsValue*) currentReportBufferPos; @@ -2310,7 +2367,7 @@ sendNextReportEntry(ReportControl* self) MmsValue_deleteAllBitStringBits(reason); - switch((ReportInclusionFlag) *currentReportBufferPos) { + switch((int) *currentReportBufferPos) { case REPORT_CONTROL_QUALITY_CHANGED: MmsValue_setBitStringBit(reason, 2, true); break; @@ -2324,7 +2381,7 @@ sendNextReportEntry(ReportControl* self) break; } - currentReportBufferPos++; + currentReportBufferPos += MemoryAllocator_getAlignedSize(1); MmsValue* dataSetElement = (MmsValue*) currentReportBufferPos; @@ -2465,37 +2522,96 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) void Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) +{ + if (self->isModelLocked == false) { + + LinkedList element = self->reportControls; + + while ((element = LinkedList_getNext(element)) != NULL ) { + ReportControl* rc = (ReportControl*) element->data; + + ReportControl_lockNotify(rc); + + processEventsForReport(rc, currentTimeInMs); + + ReportControl_unlockNotify(rc); + } + } +} + +static inline void +copySingleValueToReportBuffer(ReportControl* self, int dataSetEntryIndex) +{ + if (self->bufferedDataSetValues[dataSetEntryIndex] == NULL) + self->bufferedDataSetValues[dataSetEntryIndex] = MmsValue_clone(self->valueReferences[dataSetEntryIndex]); + else + MmsValue_update(self->bufferedDataSetValues[dataSetEntryIndex], self->valueReferences[dataSetEntryIndex]); +} + +static void +copyValuesToReportBuffer(ReportControl* self) +{ + int i; + for (i = 0; i < self->dataSet->elementCount; i++) { + if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED) { + copySingleValueToReportBuffer(self, i); + + /* clear not-updated flag */ + self->inclusionFlags[i] &= (~REPORT_CONTROL_NOT_UPDATED); + } + } +} + +/* check if report have to be sent after data model update */ +void +Reporting_processReportEventsAfterUnlock(MmsMapping* self) { LinkedList element = self->reportControls; + uint64_t currentTime = Hal_getTimeInMs(); + while ((element = LinkedList_getNext(element)) != NULL ) { ReportControl* rc = (ReportControl*) element->data; ReportControl_lockNotify(rc); - processEventsForReport(rc, currentTimeInMs); + if ((rc->enabled) || (rc->isBuffering)) { + copyValuesToReportBuffer(rc); + + processEventsForReport(rc, currentTime); + } ReportControl_unlockNotify(rc); } } void -ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag) +ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, bool modelLocked) { ReportControl_lockNotify(self); if (self->inclusionFlags[dataSetEntryIndex] != 0) { /* report for this data set entry is already pending (bypass BufTm) */ self->reportTime = Hal_getTimeInMs(); + + if (modelLocked) { + /* buffer all relevant values */ + copyValuesToReportBuffer(self); + } + processEventsForReport(self, self->reportTime); } - self->inclusionFlags[dataSetEntryIndex] = flag; + if (modelLocked) { + /* set flag to update values when report is to be sent or data model unlocked */ + self->inclusionFlags[dataSetEntryIndex] = flag | REPORT_CONTROL_NOT_UPDATED; - /* buffer value for report */ - if (self->bufferedDataSetValues[dataSetEntryIndex] == NULL) - self->bufferedDataSetValues[dataSetEntryIndex] = MmsValue_clone(self->valueReferences[dataSetEntryIndex]); - else - MmsValue_update(self->bufferedDataSetValues[dataSetEntryIndex], self->valueReferences[dataSetEntryIndex]); + } + else { + self->inclusionFlags[dataSetEntryIndex] = flag; + + /* buffer value for report */ + copySingleValueToReportBuffer(self, dataSetEntryIndex); + } if (self->triggered == false) { uint64_t currentTime = Hal_getTimeInMs(); diff --git a/src/iec61850/server/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c index c63a802d3..a05d79dd8 100644 --- a/src/iec61850/server/model/config_file_parser.c +++ b/src/iec61850/server/model/config_file_parser.c @@ -392,6 +392,16 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) } break; + case IEC61850_OPTFLDS: + case IEC61850_TRGOPS: + { + int value; + if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBitString(2); + MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + break; + default: break; diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index cb749930b..558771e62 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -690,6 +690,8 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha self->sibling = NULL; + self->value = NULL; + DataSet_addEntry(dataSet, self); return self; diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c index 6c709b95b..79a8db19b 100644 --- a/src/iec61850/server/model/model.c +++ b/src/iec61850/server/model/model.c @@ -3,7 +3,7 @@ * * Copyright 2013 Michael Zillgith * - * This file is part of libIEC61850. + * This file is part of libIEC61850. * * libIEC61850 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include "iec61850_model.h" +#include "stack_config.h" #include "libiec61850_platform_includes.h" static void @@ -75,12 +76,12 @@ IedModel_setAttributeValuesToNull(IedModel* iedModel) int -IedModel_getLogicalDeviceCount(IedModel* iedModel) +IedModel_getLogicalDeviceCount(IedModel* self) { - if (iedModel->firstChild == NULL) + if (self->firstChild == NULL) return 0; - LogicalDevice* logicalDevice = iedModel->firstChild; + LogicalDevice* logicalDevice = self->firstChild; int ldCount = 1; @@ -165,6 +166,27 @@ IedModel_getDeviceByInst(IedModel* self, const char* ldInst) return NULL; } + +LogicalDevice* +IedModel_getDeviceByIndex(IedModel* self, int index) +{ + LogicalDevice* logicalDevice = self->firstChild; + + int currentIndex = 0; + + while (logicalDevice) { + + if (currentIndex == index) + return logicalDevice; + + currentIndex++; + + logicalDevice = (LogicalDevice*) logicalDevice->sibling; + } + + return NULL; +} + static DataAttribute* ModelNode_getDataAttributeByMmsValue(ModelNode* self, MmsValue* value) { @@ -280,15 +302,16 @@ IedModel_getModelNodeByObjectReference(IedModel* model, const char* objectRefere char* separator = strchr(objRef, '/'); - if (separator == NULL) - return NULL; - - *separator = 0; + if (separator != NULL) + *separator = 0; LogicalDevice* ld = IedModel_getDevice(model, objRef); if (ld == NULL) return NULL; + if ((separator == NULL) || (*(separator + 1) == 0)) + return (ModelNode*) ld; + return ModelNode_getChild((ModelNode*) ld, separator + 1); } @@ -328,10 +351,8 @@ IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectR char* separator = strchr(objRef, '/'); - if (separator == NULL) - return NULL; - - *separator = 0; + if (separator != NULL) + *separator = 0; char ldName[65]; strcpy(ldName, model->name); @@ -341,6 +362,9 @@ IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectR if (ld == NULL) return NULL; + if ((separator == NULL) || (*(separator + 1) == 0)) + return (ModelNode*) ld; + return ModelNode_getChild((ModelNode*) ld, separator + 1); } diff --git a/src/mms/inc/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h index 8d9a054a5..81d340b27 100644 --- a/src/mms/inc/iso_connection_parameters.h +++ b/src/mms/inc/iso_connection_parameters.h @@ -28,7 +28,7 @@ extern "C" { #endif -#include "tls_api.h" +#include "tls_config.h" /** * \addtogroup mms_client_api_group diff --git a/src/mms/inc/iso_server.h b/src/mms/inc/iso_server.h index a5da64954..849df2cfb 100644 --- a/src/mms/inc/iso_server.h +++ b/src/mms/inc/iso_server.h @@ -94,6 +94,9 @@ IsoServer_create(TLSConfiguration tlsConfiguration); void IsoServer_setTcpPort(IsoServer self, int port); +void +IsoServer_setMaxConnections(IsoServer self, int maxConnections); + void IsoServer_setLocalIpAddress(IsoServer self, const char* ipAddress); diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index aa33938ca..f4d631b3c 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -40,8 +40,7 @@ extern "C" { #include "mms_value.h" #include "iso_connection_parameters.h" #include "linked_list.h" - -#include "tls_api.h" +#include "tls_config.h" /** * Contains MMS layer specific parameters @@ -375,7 +374,7 @@ MmsValue* MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId); /** - * \brief Read an element of a single array variable from the server. + * \brief Read one or more elements of a single array variable from the server. * * \param self MmsConnection instance to operate on * \param mmsError user provided variable to store error code @@ -392,6 +391,23 @@ MmsValue* MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, uint32_t startIndex, uint32_t numberOfElements); + +/** + * \brief Read a single element (with optional component specification) from the server + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param domainId the domain name of the variable to be read + * \param itemId name of the variable to be read + * \param index array element index + * \param componentId array element component name + * + * \return Returns a MmsValue object or NULL if the request failed. + */ +MmsValue* +MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, uint32_t index, const char* componentId); + /** * \brief Read multiple variables of a domain from the server with one request message. * diff --git a/src/mms/inc/mms_common.h b/src/mms/inc/mms_common.h index 59d172b4c..ed5a88365 100644 --- a/src/mms/inc/mms_common.h +++ b/src/mms/inc/mms_common.h @@ -102,7 +102,7 @@ typedef enum } MmsError; -typedef enum ATTRIBUTE_PACKED +typedef enum { /*! this represents all MMS array types (arrays contain uniform elements) */ MMS_ARRAY = 0, diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index 553f33550..bf06de203 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -225,6 +225,74 @@ MmsServer_installFileAccessHandler(MmsServer self, MmsFileAccessHandler handler, void MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath); +/** + * \brief Set the maximum number of TCP client connections + * + * \param[in] maxConnections the maximum number of TCP client connections to accept + */ +void +MmsServer_setMaxConnections(MmsServer self, int maxConnections); + +/** + * \brief Enable/disable MMS file services at runtime + * + * NOTE: requires CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME = 1 in stack configuration + * + * \param[in] self the MmsServer instance + * \param[in] enable true to enable file services, false to disable + */ +void +MmsServer_enableFileService(MmsServer self, bool enable); + +/** + * \brief Enable/disable dynamic named variable list (data set) service + * + * NOTE: requires CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME = 1 in stack configuration + * + * \param[in] self the MmsServer instance + * \param[in] enable true to enable named variable list services, false to disable + */ +void +MmsServer_enableDynamicNamedVariableListService(MmsServer self, bool enable); + +/** + * \brief Set the maximum number of association specific data sets (per connection) + * + * \param[in] self the MmsServer instance + * \param[in] maxDataSets maximum number association specific data sets + */ +void +MmsServer_setMaxAssociationSpecificDataSets(MmsServer self, int maxDataSets); + +/** + * \brief Set the maximum number of domain specific data sets + * + * \param[in] self the MmsServer instance + * \param[in] maxDataSets maximum number domain specific data sets + */ +void +MmsServer_setMaxDomainSpecificDataSets(MmsServer self, int maxDataSets); + +/** + * \brief Set the maximum number of data set entries (for dynamic data sets) + * + * \param[in] self the MmsServer instance + * \param[in] maxDataSetEntries maximum number of dynamic data set entries + */ +void +MmsServer_setMaxDataSetEntries(MmsServer self, int maxDataSetEntries); + +/** + * \brief Enable/disable journal service + * + * NOTE: requires CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME = 1 in stack configuration + * + * \param[in] self the MmsServer instance + * \param[in] enable true to enable journal service, false to disable + */ +void +MmsServer_enableJournalService(MmsServer self, bool enable); + /** * \brief lock the cached server data model * diff --git a/src/mms/inc/mms_types.h b/src/mms/inc/mms_types.h index 61fdab484..b26896205 100644 --- a/src/mms/inc/mms_types.h +++ b/src/mms/inc/mms_types.h @@ -26,7 +26,7 @@ #include "libiec61850_common_api.h" -typedef enum ATTRIBUTE_PACKED { +typedef enum { MMS_VALUE_NO_RESPONSE, MMS_VALUE_OK, MMS_VALUE_ACCESS_DENIED, diff --git a/src/mms/inc_private/acse.h b/src/mms/inc_private/acse.h index 3f966d00d..6ad56342a 100644 --- a/src/mms/inc_private/acse.h +++ b/src/mms/inc_private/acse.h @@ -26,7 +26,7 @@ #include "byte_buffer.h" #include "buffer_chain.h" #include "iso_connection_parameters.h" -#include "tls_api.h" +#include "tls_socket.h" #ifndef ACSE_H_ #define ACSE_H_ diff --git a/src/mms/inc_private/cotp.h b/src/mms/inc_private/cotp.h index 4b6346b72..188d52826 100644 --- a/src/mms/inc_private/cotp.h +++ b/src/mms/inc_private/cotp.h @@ -29,7 +29,7 @@ #include "buffer_chain.h" #include "hal_socket.h" #include "iso_connection_parameters.h" -#include "tls_api.h" +#include "tls_socket.h" typedef struct { TSelector tSelSrc; diff --git a/src/mms/inc_private/iso_server_private.h b/src/mms/inc_private/iso_server_private.h index 4ae788086..e700c9ece 100644 --- a/src/mms/inc_private/iso_server_private.h +++ b/src/mms/inc_private/iso_server_private.h @@ -24,8 +24,8 @@ #ifndef ISO_SERVER_PRIVATE_H_ #define ISO_SERVER_PRIVATE_H_ +#include "tls_config.h" #include "hal_socket.h" -#include "tls_api.h" IsoConnection IsoConnection_create(Socket socket, IsoServer isoServer); diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 86253325e..9ffdb7319 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -79,8 +79,12 @@ struct sMmsConnection { uint32_t connectTimeout; IsoClientConnection isoClient; - AssociationState associationState; - ConnectionState connectionState; + + volatile AssociationState associationState; + Semaphore associationStateLock; + + volatile ConnectionState connectionState; + Semaphore connectionStateLock; MmsConnectionParameters parameters; IsoConnectionParameters isoParameters; @@ -97,7 +101,8 @@ struct sMmsConnection { #endif /* state of an active connection conclude/release process */ - int concludeState; + volatile int concludeState; + Semaphore concludeStateLock; #if (MMS_OBTAIN_FILE_SERVICE == 1) int32_t nextFrsmId; @@ -159,6 +164,10 @@ int mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, const char* domainId, const char* itemId, uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer); +int +mmsClient_createReadRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, + uint32_t index, const char* component, ByteBuffer* writeBuffer); + int mmsClient_createReadRequestMultipleValues(uint32_t invokeId, const char* domainId, LinkedList /**/ items, ByteBuffer* writeBuffer); diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index fdc05de7f..7a9155f43 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -164,6 +164,16 @@ struct sMmsServer { char* filestoreBasepath; #endif +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + int maxConnections; + bool fileServiceEnabled; + bool dynamicVariableListServiceEnabled; + int maxDataSetEntries; + bool journalServiceEnabled; + int maxAssociationSpecificDataSets; + int maxDomainSpecificDataSets; +#endif /* (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) */ + }; struct sMmsServerConnection { diff --git a/src/mms/iso_acse/acse.c b/src/mms/iso_acse/acse.c index 92ba37a16..6235f2165 100644 --- a/src/mms/iso_acse/acse.c +++ b/src/mms/iso_acse/acse.c @@ -43,7 +43,7 @@ checkAuthMechanismName(uint8_t* authMechanism, int authMechLen) { AcseAuthenticationMechanism authenticationMechanism = ACSE_AUTH_NONE; - if (authMechanism != NULL ) { + if (authMechanism != NULL) { if (authMechLen == 3) { if (memcmp(auth_mech_password_oid, authMechanism, 3) == 0) { @@ -81,7 +81,7 @@ checkAuthentication(AcseConnection* self, uint8_t* authMechanism, int authMechLe { self->securityToken = NULL; - if (self->authenticator != NULL ) { + if (self->authenticator != NULL) { AcseAuthenticationMechanism mechanism = checkAuthMechanismName(authMechanism, authMechLen); @@ -111,120 +111,139 @@ checkAuthentication(AcseConnection* self, uint8_t* authMechanism, int authMechLe return true; } - static int parseUserInformation(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos, bool* userInfoValid) { - if (DEBUG_ACSE) printf("ACSE: parseUserInformation %i %i\n", bufPos, maxBufPos); + if (DEBUG_ACSE) + printf("ACSE: parseUserInformation %i %i\n", bufPos, maxBufPos); - bool hasindirectReference = false; - bool isDataValid = false; + bool hasindirectReference = false; + bool isDataValid = false; - while (bufPos < maxBufPos) { - uint8_t tag = buffer[bufPos++]; - int len; + while (bufPos < maxBufPos) { + uint8_t tag = buffer[bufPos++]; + int len; - bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - switch (tag) { + if (bufPos < 0) { + *userInfoValid = false; + return -1; + } - case 0x02: /* indirect-reference */ - self->nextReference = BerDecoder_decodeUint32(buffer, len, bufPos); - bufPos += len; - hasindirectReference = true; - break; + switch (tag) + { - case 0xa0: /* encoding */ - isDataValid = true; + case 0x02: /* indirect-reference */ + self->nextReference = BerDecoder_decodeUint32(buffer, len, bufPos); + bufPos += len; + hasindirectReference = true; + break; - self->userDataBufferSize = len; - self->userDataBuffer = buffer + bufPos; + case 0xa0: /* encoding */ + isDataValid = true; - bufPos += len; + self->userDataBufferSize = len; + self->userDataBuffer = buffer + bufPos; - break; + bufPos += len; - default: /* ignore unknown tag */ - bufPos += len; - } - } + break; + default: /* ignore unknown tag */ + bufPos += len; + } + } - if (DEBUG_ACSE) { - if (!hasindirectReference) printf("ACSE: User data has no indirect reference!\n"); + if (DEBUG_ACSE) { + if (!hasindirectReference) + printf("ACSE: User data has no indirect reference!\n"); - if (!isDataValid) printf("ACSE: No valid user data\n"); - } + if (!isDataValid) + printf("ACSE: No valid user data\n"); + } - if (hasindirectReference && isDataValid) - *userInfoValid = true; - else - *userInfoValid = false; + if (hasindirectReference && isDataValid) + *userInfoValid = true; + else + *userInfoValid = false; - return bufPos; + return bufPos; } static AcseIndication parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) { - if (DEBUG_ACSE) printf("ACSE: parse AARE PDU\n"); + if (DEBUG_ACSE) + printf("ACSE: parse AARE PDU\n"); - bool userInfoValid = false; + bool userInfoValid = false; - uint32_t result = 99; + uint32_t result = 99; - while (bufPos < maxBufPos) { - uint8_t tag = buffer[bufPos++]; - int len; + while (bufPos < maxBufPos) { + uint8_t tag = buffer[bufPos++]; + int len; - bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + if (bufPos < 0) + return ACSE_ERROR; + + switch (tag) + { + case 0xa1: /* application context name */ + bufPos += len; + break; - switch (tag) { - case 0xa1: /* application context name */ - bufPos += len; - break; + case 0xa2: /* result */ + bufPos++; - case 0xa2: /* result */ - bufPos++; + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + if (bufPos < 0) + return ACSE_ERROR; - bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - result = BerDecoder_decodeUint32(buffer, len, bufPos); + result = BerDecoder_decodeUint32(buffer, len, bufPos); - bufPos += len; - break; + bufPos += len; + break; - case 0xa3: /* result source diagnostic */ - bufPos += len; - break; + case 0xa3: /* result source diagnostic */ + bufPos += len; + break; - case 0xbe: /* user information */ - if (buffer[bufPos] != 0x28) { - if (DEBUG_ACSE) printf("ACSE: invalid user info\n"); - bufPos += len; - } - else { - bufPos++; + case 0xbe: /* user information */ + if (buffer[bufPos] != 0x28) { + if (DEBUG_ACSE) + printf("ACSE: invalid user info\n"); + bufPos += len; + } + else { + bufPos++; - bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + if (bufPos < 0) + return ACSE_ERROR; - bufPos = parseUserInformation(self, buffer, bufPos, bufPos + len, &userInfoValid); - } - break; + bufPos = parseUserInformation(self, buffer, bufPos, bufPos + len, &userInfoValid); + if (bufPos < 0) + return ACSE_ERROR; + } + break; - default: /* ignore unknown tag */ - if (DEBUG_ACSE) - printf("ACSE: parseAarePdu: unknown tag %02x\n", tag); + default: /* ignore unknown tag */ + if (DEBUG_ACSE) + printf("ACSE: parseAarePdu: unknown tag %02x\n", tag); - bufPos += len; - break; - } - } + bufPos += len; + break; + } + } - if (!userInfoValid) - return ACSE_ERROR; + if (!userInfoValid) + return ACSE_ERROR; - if (result != 0) - return ACSE_ASSOCIATE_FAILED; + if (result != 0) + return ACSE_ASSOCIATE_FAILED; return ACSE_ASSOCIATE; } @@ -232,104 +251,126 @@ parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) static AcseIndication parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) { - if (DEBUG_ACSE) printf("ACSE: parse AARQ PDU\n"); + if (DEBUG_ACSE) + printf("ACSE: parse AARQ PDU\n"); - uint8_t* authValue = NULL; - int authValueLen = 0; - uint8_t* authMechanism = NULL; - int authMechLen = 0; - bool userInfoValid = false; + uint8_t* authValue = NULL; + int authValueLen = 0; + uint8_t* authMechanism = NULL; + int authMechLen = 0; + bool userInfoValid = false; - while (bufPos < maxBufPos) { - uint8_t tag = buffer[bufPos++]; - int len; + while (bufPos < maxBufPos) { + uint8_t tag = buffer[bufPos++]; + int len; - bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { - if (DEBUG_ACSE) - printf("ACSE: parseAarqPdu: user info invalid!\n"); - return ACSE_ASSOCIATE_FAILED; - } + if (bufPos < 0) { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); + return ACSE_ASSOCIATE_FAILED; + } - switch (tag) { - case 0xa1: /* application context name */ - bufPos += len; - break; + switch (tag) + { + case 0xa1: /* application context name */ + bufPos += len; + break; - case 0xa2: /* called AP title */ - bufPos += len; - break; - case 0xa3: /* called AE qualifier */ - bufPos += len; - break; + case 0xa2: /* called AP title */ + bufPos += len; + break; + case 0xa3: /* called AE qualifier */ + bufPos += len; + break; - case 0xa6: /* calling AP title */ - { - if (buffer[bufPos] == 0x06) { /* ap-title-form2 */ + case 0xa6: /* calling AP title */ + { + if (buffer[bufPos] == 0x06) { /* ap-title-form2 */ - int innerLength = buffer[bufPos+1]; + int innerLength = buffer[bufPos + 1]; - if (innerLength == len - 2) - BerDecoder_decodeOID(buffer, bufPos + 2, innerLength, &(self->applicationReference.apTitle)); - } - } - bufPos += len; - break; + if (innerLength == len - 2) + BerDecoder_decodeOID(buffer, bufPos + 2, innerLength, &(self->applicationReference.apTitle)); + } + } + bufPos += len; + break; - case 0xa7: /* calling AE qualifier */ - { - if (buffer[bufPos] == 0x02) { /* ae-qualifier-form2 */ + case 0xa7: /* calling AE qualifier */ + { + if (buffer[bufPos] == 0x02) { /* ae-qualifier-form2 */ - int innerLength = buffer[bufPos+1]; + int innerLength = buffer[bufPos + 1]; if (innerLength == len - 2) - self->applicationReference.aeQualifier = BerDecoder_decodeInt32(buffer + 2, buffer[bufPos+1], bufPos); - } - } - bufPos += len; - break; + self->applicationReference.aeQualifier = BerDecoder_decodeInt32(buffer + 2, buffer[bufPos + 1], bufPos); + } + } + bufPos += len; + break; + + case 0x8a: /* sender ACSE requirements */ + bufPos += len; + break; + + case 0x8b: /* (authentication) mechanism name */ + authMechLen = len; + authMechanism = buffer + bufPos; + bufPos += len; + break; + + case 0xac: /* authentication value */ + bufPos++; + + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + if (bufPos < 0) { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); + return ACSE_ASSOCIATE_FAILED; + } + + authValueLen = len; + authValue = buffer + bufPos; + bufPos += len; + break; + + case 0xbe: /* user information */ + if (buffer[bufPos] != 0x28) { + if (DEBUG_ACSE) + printf("ACSE: invalid user info\n"); + bufPos += len; + } + else { + bufPos++; + + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + + if (bufPos < 0) { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); + return ACSE_ASSOCIATE_FAILED; + } + + bufPos = parseUserInformation(self, buffer, bufPos, bufPos + len, &userInfoValid); + + if (bufPos < 0) { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); + return ACSE_ASSOCIATE_FAILED; + } + } + break; + + default: /* ignore unknown tag */ + if (DEBUG_ACSE) + printf("ACSE: parseAarqPdu: unknown tag %02x\n", tag); - case 0x8a: /* sender ACSE requirements */ bufPos += len; - break; - - case 0x8b: /* (authentication) mechanism name */ - authMechLen = len; - authMechanism = buffer + bufPos; - bufPos += len; - break; - - case 0xac: /* authentication value */ - bufPos++; - bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - authValueLen = len; - authValue = buffer + bufPos; - bufPos += len; - break; - - case 0xbe: /* user information */ - if (buffer[bufPos] != 0x28) { - if (DEBUG_ACSE) printf("ACSE: invalid user info\n"); - bufPos += len; - } - else { - bufPos++; - - bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - - bufPos = parseUserInformation(self, buffer, bufPos, bufPos + len, &userInfoValid); - } - break; - - default: /* ignore unknown tag */ - if (DEBUG_ACSE) - printf("ACSE: parseAarqPdu: unknown tag %02x\n", tag); - - bufPos += len; - break; - } - } + break; + } + } if (checkAuthentication(self, authMechanism, authMechLen, authValue, authValueLen) == false) { if (DEBUG_ACSE) @@ -342,7 +383,7 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) if (DEBUG_ACSE) printf("ACSE: parseAarqPdu: user info invalid!\n"); - return ACSE_ASSOCIATE_FAILED; + return ACSE_ASSOCIATE_FAILED; } return ACSE_ASSOCIATE; @@ -362,7 +403,8 @@ AcseConnection_init(AcseConnection* self, AcseAuthenticator authenticator, void* self->tlsSocket = tlsSocket; #endif - memset(&(self->applicationReference), 0, sizeof(self->applicationReference)); + memset(&(self->applicationReference), 0, + sizeof(self->applicationReference)); } void @@ -387,20 +429,22 @@ AcseConnection_parseMessage(AcseConnection* self, ByteBuffer* message) bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, messageSize); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_ACSE) printf("ACSE: AcseConnection_parseMessage: invalid ACSE message!\n"); return ACSE_ERROR; } - switch (messageType) { + switch (messageType) + { case 0x60: - indication = parseAarqPdu(self, buffer, bufPos, messageSize); - break; + indication = parseAarqPdu(self, buffer, bufPos, messageSize); + break; case 0x61: - indication = parseAarePdu(self, buffer, bufPos, messageSize); - break; + indication = parseAarePdu(self, buffer, bufPos, messageSize); + break; case 0x62: /* A_RELEASE.request RLRQ-apdu */ indication = ACSE_RELEASE_REQUEST; break; @@ -411,9 +455,10 @@ AcseConnection_parseMessage(AcseConnection* self, ByteBuffer* message) indication = ACSE_ABORT; break; default: - if (DEBUG_ACSE) printf("ACSE: Unknown ACSE message\n"); - indication = ACSE_ERROR; - break; + if (DEBUG_ACSE) + printf("ACSE: Unknown ACSE message\n"); + indication = ACSE_ERROR; + break; } return indication; @@ -422,7 +467,7 @@ AcseConnection_parseMessage(AcseConnection* self, ByteBuffer* message) void AcseConnection_createAssociateFailedMessage(AcseConnection* self, BufferChain writeBuffer) { - AcseConnection_createAssociateResponseMessage(self, ACSE_RESULT_REJECT_PERMANENT, writeBuffer, NULL); + AcseConnection_createAssociateResponseMessage(self, ACSE_RESULT_REJECT_PERMANENT, writeBuffer, NULL); } void @@ -440,7 +485,8 @@ AcseConnection_createAssociateResponseMessage(AcseConnection* self, int resultLength = 5; int resultDiagnosticLength = 5; - int fixedContentLength = appContextLength + resultLength + resultDiagnosticLength; + int fixedContentLength = appContextLength + resultLength + + resultDiagnosticLength; int variableContentLength = 0; @@ -525,156 +571,181 @@ AcseConnection_createAssociateRequestMessage(AcseConnection* self, assert(writeBuffer != NULL); assert(payload != NULL); - int payloadLength = payload->length; - int authValueLength; - int authValueStringLength = 0; - - int passwordLength = 0; + int payloadLength = payload->length; + int authValueLength; + int authValueStringLength = 0; - int contentLength = 0; + int passwordLength = 0; - /* application context name */ - contentLength += 9; + int contentLength = 0; - int calledAEQualifierLength = 0; + /* application context name */ + contentLength += 9; - if (isoParameters->remoteApTitleLen > 0) { + int calledAEQualifierLength = 0; + if (isoParameters->remoteApTitleLen > 0) + { /* called AP title */ contentLength += (4 + isoParameters->remoteApTitleLen); - calledAEQualifierLength = BerEncoder_UInt32determineEncodedSize(isoParameters->remoteAEQualifier); + calledAEQualifierLength = BerEncoder_UInt32determineEncodedSize( + isoParameters->remoteAEQualifier); /* called AE qualifier */ contentLength += (4 + calledAEQualifierLength); - } + } - int callingAEQualifierLength = 0; + int callingAEQualifierLength = 0; - if (isoParameters->localApTitleLen > 0) { + if (isoParameters->localApTitleLen > 0) + { /* calling AP title */ contentLength += (4 + isoParameters->localApTitleLen); - callingAEQualifierLength = BerEncoder_UInt32determineEncodedSize(isoParameters->localAEQualifier); + callingAEQualifierLength = BerEncoder_UInt32determineEncodedSize( + isoParameters->localAEQualifier); /* calling AE qualifier */ contentLength += (4 + callingAEQualifierLength); - } - - if (authParameter != NULL) { - - /* sender ACSE requirements */ - contentLength += 4; + } - /* mechanism name */ - contentLength += 5; + if (authParameter != NULL) + { + /* sender ACSE requirements */ + contentLength += 4; - /* authentication value */ - if (authParameter->mechanism == ACSE_AUTH_PASSWORD) { - contentLength += 2; + /* mechanism name */ + contentLength += 5; - //if (authParameter->value.password.passwordLength == 0) + /* authentication value */ + if (authParameter->mechanism == ACSE_AUTH_PASSWORD) + { + contentLength += 2; - passwordLength = authParameter->value.password.passwordLength; + //if (authParameter->value.password.passwordLength == 0) - authValueStringLength = BerEncoder_determineLengthSize(passwordLength); + passwordLength = authParameter->value.password.passwordLength; - contentLength += passwordLength + authValueStringLength; + authValueStringLength = BerEncoder_determineLengthSize( + passwordLength); - authValueLength = BerEncoder_determineLengthSize(passwordLength + authValueStringLength + 1); + contentLength += passwordLength + authValueStringLength; - contentLength += authValueLength; - } - else { - contentLength += 2; - } - } + authValueLength = BerEncoder_determineLengthSize( + passwordLength + authValueStringLength + 1); - /* user information */ - int userInfoLength = 0; + contentLength += authValueLength; + } + else + { + contentLength += 2; + } + } - /* single ASN1 type tag */ - userInfoLength += payloadLength; - userInfoLength += 1; - userInfoLength += BerEncoder_determineLengthSize(payloadLength); + /* user information */ + int userInfoLength = 0; - /* indirect reference */ - userInfoLength += 1; - userInfoLength += 2; + /* single ASN1 type tag */ + userInfoLength += payloadLength; + userInfoLength += 1; + userInfoLength += BerEncoder_determineLengthSize(payloadLength); - /* association data */ - int assocDataLength = userInfoLength; - userInfoLength += BerEncoder_determineLengthSize(assocDataLength); - userInfoLength += 1; + /* indirect reference */ + userInfoLength += 1; + userInfoLength += 2; - /* user information */ - int userInfoLen = userInfoLength; - userInfoLength += BerEncoder_determineLengthSize(userInfoLength); - userInfoLength += 1; + /* association data */ + int assocDataLength = userInfoLength; + userInfoLength += BerEncoder_determineLengthSize(assocDataLength); + userInfoLength += 1; - contentLength += userInfoLength; + /* user information */ + int userInfoLen = userInfoLength; + userInfoLength += BerEncoder_determineLengthSize(userInfoLength); + userInfoLength += 1; - uint8_t* buffer = writeBuffer->buffer; - int bufPos = 0; + contentLength += userInfoLength; - bufPos = BerEncoder_encodeTL(0x60, contentLength, buffer, bufPos); + uint8_t* buffer = writeBuffer->buffer; + int bufPos = 0; - /* application context name */ - bufPos = BerEncoder_encodeTL(0xa1, 7, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x06, 5, buffer, bufPos); - memcpy(buffer + bufPos, appContextNameMms, 5); - bufPos += 5; + bufPos = BerEncoder_encodeTL(0x60, contentLength, buffer, bufPos); - if (isoParameters->remoteApTitleLen > 0) { + /* application context name */ + bufPos = BerEncoder_encodeTL(0xa1, 7, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x06, 5, buffer, bufPos); + memcpy(buffer + bufPos, appContextNameMms, 5); + bufPos += 5; - /* called AP title */ - bufPos = BerEncoder_encodeTL(0xa2, isoParameters->remoteApTitleLen + 2, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x06, isoParameters->remoteApTitleLen, buffer, bufPos); + if (isoParameters->remoteApTitleLen > 0) + { + /* called AP title */ + bufPos = BerEncoder_encodeTL(0xa2, isoParameters->remoteApTitleLen + 2, + buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x06, isoParameters->remoteApTitleLen, + buffer, bufPos); - memcpy(buffer + bufPos, isoParameters->remoteApTitle, isoParameters->remoteApTitleLen); - bufPos += isoParameters->remoteApTitleLen; + memcpy(buffer + bufPos, isoParameters->remoteApTitle, + isoParameters->remoteApTitleLen); + bufPos += isoParameters->remoteApTitleLen; /* called AE qualifier */ - bufPos = BerEncoder_encodeTL(0xa3, calledAEQualifierLength + 2, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x02, calledAEQualifierLength, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(isoParameters->remoteAEQualifier, buffer, bufPos); - } + bufPos = BerEncoder_encodeTL(0xa3, calledAEQualifierLength + 2, buffer, + bufPos); + bufPos = BerEncoder_encodeTL(0x02, calledAEQualifierLength, buffer, + bufPos); + bufPos = BerEncoder_encodeUInt32(isoParameters->remoteAEQualifier, + buffer, bufPos); + } - if (isoParameters->localApTitleLen > 0) { + if (isoParameters->localApTitleLen > 0) + { /* calling AP title */ - bufPos = BerEncoder_encodeTL(0xa6, isoParameters->localApTitleLen + 2, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x06, isoParameters->localApTitleLen, buffer, bufPos); - memcpy(buffer + bufPos, isoParameters->localApTitle, isoParameters->localApTitleLen); + bufPos = BerEncoder_encodeTL(0xa6, isoParameters->localApTitleLen + 2, + buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x06, isoParameters->localApTitleLen, + buffer, bufPos); + memcpy(buffer + bufPos, isoParameters->localApTitle, + isoParameters->localApTitleLen); bufPos += isoParameters->localApTitleLen; /* calling AE qualifier */ - bufPos = BerEncoder_encodeTL(0xa7, callingAEQualifierLength + 2, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x02, callingAEQualifierLength, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(isoParameters->localAEQualifier, buffer, bufPos); - } - - if (authParameter != NULL) { - /* sender requirements */ - bufPos = BerEncoder_encodeTL(0x8a, 2, buffer, bufPos); - buffer[bufPos++] = 0x04; - - if (authParameter->mechanism == ACSE_AUTH_PASSWORD) { - buffer[bufPos++] = requirements_authentication[0]; - - bufPos = BerEncoder_encodeTL(0x8b, 3, buffer, bufPos); - memcpy(buffer + bufPos, auth_mech_password_oid, 3); - bufPos += 3; - - /* authentication value */ - bufPos = BerEncoder_encodeTL(0xac, authValueStringLength + passwordLength + 1, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x80, passwordLength, buffer, bufPos); - memcpy(buffer + bufPos, authParameter->value.password.octetString, passwordLength); - bufPos += passwordLength; - } - else { /* AUTH_NONE */ - buffer[bufPos++] = 0; - } - } + bufPos = BerEncoder_encodeTL(0xa7, callingAEQualifierLength + 2, buffer, + bufPos); + bufPos = BerEncoder_encodeTL(0x02, callingAEQualifierLength, buffer, + bufPos); + bufPos = BerEncoder_encodeUInt32(isoParameters->localAEQualifier, + buffer, bufPos); + } + + if (authParameter != NULL) + { + /* sender requirements */ + bufPos = BerEncoder_encodeTL(0x8a, 2, buffer, bufPos); + buffer[bufPos++] = 0x04; + + if (authParameter->mechanism == ACSE_AUTH_PASSWORD) + { + buffer[bufPos++] = requirements_authentication[0]; + + bufPos = BerEncoder_encodeTL(0x8b, 3, buffer, bufPos); + memcpy(buffer + bufPos, auth_mech_password_oid, 3); + bufPos += 3; + + /* authentication value */ + bufPos = BerEncoder_encodeTL(0xac, + authValueStringLength + passwordLength + 1, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x80, passwordLength, buffer, bufPos); + memcpy(buffer + bufPos, authParameter->value.password.octetString, + passwordLength); + bufPos += passwordLength; + } + else + { /* AUTH_NONE */ + buffer[bufPos++] = 0; + } + } /* user information */ bufPos = BerEncoder_encodeTL(0xbe, userInfoLen, buffer, bufPos); @@ -689,9 +760,9 @@ AcseConnection_createAssociateRequestMessage(AcseConnection* self, /* single ASN1 type */ bufPos = BerEncoder_encodeTL(0xa0, payloadLength, buffer, bufPos); - writeBuffer->partLength = bufPos; - writeBuffer->length = bufPos + payload->length; - writeBuffer->nextPart = payload; + writeBuffer->partLength = bufPos; + writeBuffer->length = bufPos + payload->length; + writeBuffer->nextPart = payload; } /** diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index 242eaa09c..4e5a315ae 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -33,8 +33,9 @@ #include "iso_session.h" #include "iso_presentation.h" #include "iso_client_connection.h" + +#include "tls_config.h" #include "acse.h" -#include "tls_api.h" #ifndef DEBUG_ISO_CLIENT @@ -57,7 +58,9 @@ struct sIsoClientConnection { IsoIndicationCallback callback; void* callbackParameter; + volatile int state; + Semaphore stateMutex; Socket socket; @@ -94,13 +97,32 @@ struct sIsoClientConnection Thread thread; }; +static void +setState(IsoClientConnection self, int newState) +{ + Semaphore_wait(self->stateMutex); + self->state = newState; + Semaphore_post(self->stateMutex); +} + +static int +getState(IsoClientConnection self) +{ + int stateVal; + + Semaphore_wait(self->stateMutex); + stateVal = self->state; + Semaphore_post(self->stateMutex); + + return stateVal; +} + static void connectionHandlingThread(IsoClientConnection self) { IsoSessionIndication sessionIndication; self->handlingThreadRunning = true; - self->stopHandlingThread = false; if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT_CONNECTION: new connection %p\n", self); @@ -165,7 +187,7 @@ connectionHandlingThread(IsoClientConnection self) self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL);; - self->state = STATE_IDLE; + setState(self, STATE_IDLE); #if (CONFIG_MMS_SUPPORT_TLS == 1) if (self->cotpConnection->tlsSocket) @@ -215,7 +237,9 @@ IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParamet self->callback = callback; self->callbackParameter = callbackParameter; + self->state = STATE_IDLE; + self->stateMutex = Semaphore_create(1); self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(ISO_CLIENT_BUFFER_SIZE); @@ -248,6 +272,12 @@ IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParamet self->cotpConnection = (CotpConnection*) GLOBAL_CALLOC(1, sizeof(CotpConnection)); + self->handlingThreadRunning = false; + + self->stopHandlingThread = false; + self->destroyHandlingThread = false; + self->startHandlingThread = false; + return self; } @@ -259,6 +289,13 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters Socket_setConnectTimeout(self->socket, connectTimeoutInMs); +#if (CONFIG_ACTIVATE_TCP_KEEPALIVE == 1) + Socket_activateTcpKeepAlive(self->socket, + CONFIG_TCP_KEEPALIVE_IDLE, + CONFIG_TCP_KEEPALIVE_INTERVAL, + CONFIG_TCP_KEEPALIVE_CNT); +#endif + if (!Socket_connect(self->socket, params->hostname, params->tcpPort)) goto returnError; @@ -395,7 +432,7 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters /* wait for upper layer to release buffer */ Semaphore_wait(self->receiveBufferMutex); - self->state = STATE_ASSOCIATED; + setState(self, STATE_ASSOCIATED); if (self->thread == NULL) { self->thread = Thread_create(connectionThreadFunction, self, false); @@ -412,7 +449,7 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters returnError: self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - self->state = STATE_ERROR; + setState(self, STATE_ERROR); Socket_destroy(self->socket); self->socket = NULL; @@ -466,7 +503,7 @@ IsoClientConnection_close(IsoClientConnection self) Thread_sleep(1); } - self->state = STATE_IDLE; + setState(self, STATE_IDLE); } @@ -476,7 +513,7 @@ IsoClientConnection_destroy(IsoClientConnection self) if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: IsoClientConnection_destroy\n"); - if (self->state == STATE_ASSOCIATED) { + if (getState(self) == STATE_ASSOCIATED) { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: call IsoClientConnection_close\n"); @@ -523,6 +560,7 @@ IsoClientConnection_destroy(IsoClientConnection self) Semaphore_destroy(self->receiveBufferMutex); Semaphore_destroy(self->transmitBufferMutex); + Semaphore_destroy(self->stateMutex); GLOBAL_FREEMEM(self->sendBuffer); GLOBAL_FREEMEM(self); diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index dbab9435d..b6a055467 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -28,19 +28,78 @@ #include "mms_client_internal.h" #include "stack_config.h" -#include "tls_api.h" - #include #include "byte_buffer.h" #include "ber_decode.h" #include +#include "tls_config.h" #define CONFIG_MMS_CONNECTION_DEFAULT_TIMEOUT 5000 #define CONFIG_MMS_CONNECTION_DEFAULT_CONNECT_TIMEOUT 10000 #define OUTSTANDING_CALLS 10 +static void +setAssociationState(MmsConnection self, AssociationState newState) +{ + Semaphore_wait(self->associationStateLock); + self->associationState = newState; + Semaphore_post(self->associationStateLock); +} + +static AssociationState +getAssociationState(MmsConnection self) +{ + AssociationState state; + + Semaphore_wait(self->associationStateLock); + state = self->associationState; + Semaphore_post(self->associationStateLock); + + return state; +} + +static void +setConnectionState(MmsConnection self, ConnectionState newState) +{ + Semaphore_wait(self->connectionStateLock); + self->connectionState = newState; + Semaphore_post(self->connectionStateLock); +} + +static ConnectionState +getConnectionState(MmsConnection self) +{ + ConnectionState state; + + Semaphore_wait(self->connectionStateLock); + state = self->connectionState; + Semaphore_post(self->connectionStateLock); + + return state; +} + +static void +setConcludeState(MmsConnection self, int newState) +{ + Semaphore_wait(self->concludeStateLock); + self->concludeState = newState; + Semaphore_post(self->concludeStateLock); +} + +static int +getConcludeState(MmsConnection self) +{ + int state; + + Semaphore_wait(self->concludeStateLock); + state = self->concludeState; + Semaphore_post(self->concludeStateLock); + + return state; +} + static void handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) { @@ -50,7 +109,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: report handler rcvd size:%i\n", ByteBuffer_getSize(message)); - asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, + asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); if (rval.code == RC_OK) { @@ -61,7 +120,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) if (mmsPdu->choice.unconfirmedPDU.unconfirmedService.present == UnconfirmedService_PR_informationReport) - { + { char* domainId = NULL; InformationReport_t* report = @@ -95,7 +154,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) } else if (report->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) - { + { int listSize = report->listOfAccessResult.list.count; int variableSpecSize = report->variableAccessSpecification.choice.listOfVariable.list.count; @@ -112,10 +171,10 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) for (i = 0; i < variableSpecSize; i++) { if (report->variableAccessSpecification.choice.listOfVariable.list.array[i]->variableSpecification.present == VariableSpecification_PR_name) - { + { if (report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) - { + { int nameSize = report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.choice.vmdspecific.size; @@ -148,24 +207,24 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) ->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) { int domainNameSize = - report->variableAccessSpecification.choice.listOfVariable.list.array[i] - ->variableSpecification.choice.name.choice.domainspecific.domainId.size; + report->variableAccessSpecification.choice.listOfVariable.list.array[i] + ->variableSpecification.choice.name.choice.domainspecific.domainId.size; int itemNameSize = - report->variableAccessSpecification.choice.listOfVariable.list.array[i] - ->variableSpecification.choice.name.choice.domainspecific.itemId.size; + report->variableAccessSpecification.choice.listOfVariable.list.array[i] + ->variableSpecification.choice.name.choice.domainspecific.itemId.size; if (domainNameSize < 65 && itemNameSize < 65) { char domainNameStr[65]; char itemNameStr[65]; uint8_t* domainNameBuffer = - report->variableAccessSpecification.choice.listOfVariable.list.array[i] - ->variableSpecification.choice.name.choice.domainspecific.domainId.buf; + report->variableAccessSpecification.choice.listOfVariable.list.array[i] + ->variableSpecification.choice.name.choice.domainspecific.domainId.buf; uint8_t* itemNamebuffer = report->variableAccessSpecification.choice.listOfVariable.list.array[i] - ->variableSpecification.choice.name.choice.domainspecific.itemId.buf; + ->variableSpecification.choice.name.choice.domainspecific.itemId.buf; memcpy(domainNameStr, domainNameBuffer, domainNameSize); domainNameStr[domainNameSize] = 0; @@ -175,7 +234,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) MmsValue* value = values; if (variableSpecSize != 1) - value = MmsValue_getElement(values, i); + value = MmsValue_getElement(values, i); self->reportHandler(self->reportHandlerParameter, domainNameStr, itemNameStr, value, false); @@ -312,7 +371,7 @@ sendRequestAndWaitForResponse(MmsConnection self, uint32_t invokeId, ByteBuffer* while (currentTime < waitUntilTime) { uint32_t receivedInvokeId; - if (self->associationState == MMS_STATE_CLOSED) { + if (getAssociationState(self) == MMS_STATE_CLOSED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto connection_lost; } @@ -350,7 +409,7 @@ sendRequestAndWaitForResponse(MmsConnection self, uint32_t invokeId, ByteBuffer* receivedMessage = NULL; } -connection_lost: + connection_lost: removeFromOutstandingCalls(self, invokeId); @@ -395,7 +454,7 @@ static MmsError convertRejectCodesToMmsError(int rejectType, int rejectReason) { if ((rejectType == 1) && (rejectReason == 1)) - return MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE; + return MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE; else if ((rejectType == 5) && (rejectReason == 0)) return MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE; else if ((rejectType == 1) && (rejectReason == 4)) @@ -411,7 +470,8 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) { MmsError mmsError; - switch (serviceError.errorClass) { + switch (serviceError.errorClass) + { case 0: /* class: vmd-state */ mmsError = MMS_ERROR_VMDSTATE_OTHER; break; @@ -421,7 +481,8 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) break; case 2: /* class: definition */ - switch (serviceError.errorCode) { + switch (serviceError.errorCode) + { case 1: mmsError = MMS_ERROR_DEFINITION_OBJECT_UNDEFINED; break; @@ -463,7 +524,8 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) break; case 7: /* class: access */ - switch (serviceError.errorCode) { + switch (serviceError.errorCode) + { case 1: mmsError = MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED; break; @@ -483,7 +545,8 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) break; case 11: /* class: file */ - switch (serviceError.errorCode) { + switch (serviceError.errorCode) + { case 1: mmsError = MMS_ERROR_FILE_FILENAME_AMBIGUOUS; break; @@ -517,7 +580,6 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) } break; - default: mmsError = MMS_ERROR_OTHER; } @@ -538,7 +600,8 @@ parseServiceError(uint8_t* buffer, int bufPos, int maxLength, MmsServiceError* e if (bufPos < 0) return -1; - switch (tag) { + switch (tag) + { case 0xa0: /* errorClass */ { uint8_t errorClassTag = buffer[bufPos++]; @@ -591,12 +654,13 @@ mmsMsg_parseConfirmedErrorPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32 while (bufPos < endPos) { tag = buffer[bufPos++]; - bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); if (bufPos < 0) goto exit_error; - switch (tag) { + switch (tag) + { case 0x80: /* invoke Id */ if (invokeId != NULL) *invokeId = BerDecoder_decodeUint32(buffer, length, bufPos); @@ -618,7 +682,7 @@ mmsMsg_parseConfirmedErrorPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32 return bufPos; -exit_error: + exit_error: if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: error parsing confirmed error PDU\n"); @@ -649,12 +713,11 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo while (bufPos < endPos) { tag = buffer[bufPos++]; - bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); if (bufPos < 0) goto exit_error; - if (tag == 0x80) { /* invoke id */ if (invokeId != NULL) *invokeId = BerDecoder_decodeUint32(buffer, length, bufPos); @@ -664,7 +727,7 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo *rejectReason = BerDecoder_decodeInt32(buffer, length, bufPos); } else { - /* unknown - ignore */ + /* unknown - ignore */ } bufPos += length; @@ -672,7 +735,7 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo return bufPos; -exit_error: + exit_error: if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: error parsing reject PDU\n"); @@ -690,8 +753,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (indication == ISO_IND_CLOSED) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: Connection lost or closed by client!\n"); - self->connectionState = MMS_CON_IDLE; - self->associationState = MMS_STATE_CLOSED; + setConnectionState(self, MMS_CON_IDLE); + setAssociationState(self, MMS_STATE_CLOSED); /* Call user provided callback function */ if (self->connectionLostHandler != NULL) @@ -703,8 +766,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (indication == ISO_IND_ASSOCIATION_FAILED) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: association failed!\n"); - self->connectionState = MMS_CON_ASSOCIATION_FAILED; - self->associationState = MMS_STATE_CLOSED; + setConnectionState(self, MMS_CON_ASSOCIATION_FAILED); + setAssociationState(self, MMS_STATE_CLOSED); return; } @@ -730,12 +793,12 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) printf("MMS_CLIENT: MMS-PDU: %02x\n", tag); if (tag == 0xa9) { /* initiate response PDU */ - if (indication == ISO_IND_ASSOCIATION_SUCCESS) { - self->connectionState = MMS_CON_ASSOCIATED; - } - else { - self->connectionState = MMS_CON_ASSOCIATION_FAILED; - } + + if (indication == ISO_IND_ASSOCIATION_SUCCESS) + setConnectionState(self, MMS_CON_ASSOCIATED); + else + setConnectionState(self, MMS_CON_ASSOCIATION_FAILED); + self->lastResponse = payload; IsoClientConnection_releaseReceiveBuffer(self->isoClient); @@ -748,7 +811,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.request\n"); - self->concludeState = CONCLUDE_STATE_REQUESTED; + setConcludeState(self, CONCLUDE_STATE_REQUESTED); /* TODO block all new user requests? */ @@ -758,7 +821,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.reponse+\n"); - self->concludeState = CONCLUDE_STATE_ACCEPTED; + setConcludeState(self, CONCLUDE_STATE_ACCEPTED); IsoClientConnection_release(self->isoClient); @@ -768,7 +831,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.reponse-\n"); - self->concludeState = CONCLUDE_STATE_REJECTED; + setConcludeState(self, CONCLUDE_STATE_REJECTED); IsoClientConnection_releaseReceiveBuffer(self->isoClient); } @@ -776,7 +839,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: Confirmed error PDU!\n"); uint32_t invokeId; - MmsServiceError serviceError = {0, 0}; + MmsServiceError serviceError = + { 0, 0 }; if (mmsMsg_parseConfirmedErrorPDU(payload->buffer, 0, payload->size, &invokeId, &serviceError) < 0) { if (DEBUG_MMS_CLIENT) @@ -889,7 +953,10 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) int bufPos = 1; int length; + bufPos = BerDecoder_decodeLength(buf, &length, bufPos, payload->size); + if (bufPos == -1) + goto exit_with_error; uint32_t invokeId; @@ -905,9 +972,12 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } bufPos = BerDecoder_decodeLength(buf, &length, bufPos, payload->size); + if (bufPos == -1) + goto exit_with_error; if (extendedTag) { - switch(nestedTag) { + switch (nestedTag) + { #if (MMS_FILE_SERVICE == 1) case 0x48: /* file-open-request */ @@ -957,7 +1027,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) #endif /* MMS_FILE_SERVICE == 1 */ default: - // mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + // mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); @@ -967,7 +1037,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } } else { - switch(nestedTag) { + switch (nestedTag) + { case 0x02: /* invoke Id */ invokeId = BerDecoder_decodeUint32(buf, length, bufPos); if (DEBUG_MMS_CLIENT) @@ -975,9 +1046,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) self->lastInvokeId = invokeId; break; - default: - // mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + // mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); @@ -992,7 +1062,6 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) bufPos += length; } - } #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ @@ -1001,14 +1070,13 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return; -exit_with_error: + exit_with_error: if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received malformed message from server!\n"); IsoClientConnection_releaseReceiveBuffer(self->isoClient); - if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: LEAVE mmsIsoCallback - NOT OK!\n"); return; @@ -1032,6 +1100,10 @@ MmsConnection_create() self->lastResponseLock = Semaphore_create(1); self->outstandingCallsLock = Semaphore_create(1); + self->connectionStateLock = Semaphore_create(1); + self->concludeStateLock = Semaphore_create(1); + self->associationStateLock = Semaphore_create(1); + self->lastResponseError = MMS_ERROR_NONE; self->outstandingCalls = (uint32_t*) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(uint32_t)); @@ -1039,8 +1111,8 @@ MmsConnection_create() self->isoParameters = IsoConnectionParameters_create(); /* Load default values for connection parameters */ - TSelector tSelector = { 2, { 0, 1 } }; - SSelector sSelector = {2, { 0, 1 } }; + TSelector tSelector = { 2, { 0, 1 } }; + SSelector sSelector = { 2, { 0, 1 } }; IsoConnectionParameters_setLocalAddresses(self->isoParameters, 1, sSelector, tSelector); IsoConnectionParameters_setLocalApTitle(self->isoParameters, "1.1.1.999", 12); @@ -1072,7 +1144,6 @@ MmsConnection_createSecure(TLSConfiguration tlsConfig) return self; } - void MmsConnection_destroy(MmsConnection self) { @@ -1086,6 +1157,10 @@ MmsConnection_destroy(MmsConnection self) Semaphore_destroy(self->lastResponseLock); Semaphore_destroy(self->outstandingCallsLock); + Semaphore_destroy(self->associationStateLock); + Semaphore_destroy(self->connectionStateLock); + Semaphore_destroy(self->concludeStateLock); + GLOBAL_FREEMEM(self->outstandingCalls); #if (MMS_OBTAIN_FILE_SERVICE == 1) @@ -1141,7 +1216,6 @@ MmsConnection_setRawMessageHandler(MmsConnection self, MmsRawMessageHandler hand #endif } - void MmsConnection_setConnectionLostHandler(MmsConnection self, MmsConnectionLostHandler handler, void* handlerParameter) { @@ -1193,7 +1267,7 @@ waitForConnectResponse(MmsConnection self) uint64_t waitUntilTime = currentTime + self->requestTimeout; while (currentTime < waitUntilTime) { - if (self->connectionState != MMS_CON_WAITING) + if (getConnectionState(self) != MMS_CON_WAITING) break; Thread_sleep(10); @@ -1232,7 +1306,7 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server } #endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ - self->connectionState = MMS_CON_WAITING; + setConnectionState(self, MMS_CON_WAITING); IsoClientConnection_associate(self->isoClient, self->isoParameters, payload, self->connectTimeout); @@ -1240,25 +1314,24 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server waitForConnectResponse(self); if (DEBUG_MMS_CLIENT) - printf("MmsConnection_connect: received response conState: %i\n", self->connectionState); + printf("MmsConnection_connect: received response conState: %i\n", getConnectionState(self)); - if (self->connectionState == MMS_CON_ASSOCIATED) { + if (getConnectionState(self) == MMS_CON_ASSOCIATED) { mmsClient_parseInitiateResponse(self); releaseResponse(self); - self->associationState = MMS_STATE_CONNECTED; - } - else { - self->associationState = MMS_STATE_CLOSED; + setAssociationState(self, MMS_STATE_CONNECTED); } + else + setAssociationState(self, MMS_STATE_CLOSED); - self->connectionState = MMS_CON_IDLE; + setConnectionState(self, MMS_CON_IDLE); if (DEBUG_MMS_CLIENT) - printf("MmsConnection_connect: states: con %i ass %i\n", self->connectionState, self->associationState); + printf("MmsConnection_connect: states: con %i ass %i\n", getConnectionState(self), getAssociationState(self)); - if (self->associationState == MMS_STATE_CONNECTED) { + if (getAssociationState(self) == MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_NONE; return true; } @@ -1273,7 +1346,7 @@ MmsConnection_close(MmsConnection self) { self->connectionLostHandler = NULL; - if (self->associationState == MMS_STATE_CONNECTED) + if (getAssociationState(self) == MMS_STATE_CONNECTED) IsoClientConnection_close(self->isoClient); } @@ -1286,7 +1359,7 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError) bool success = true; - if (self->associationState == MMS_STATE_CONNECTED) + if (getAssociationState(self) == MMS_STATE_CONNECTED) success = IsoClientConnection_abort(self->isoClient); if (success == false) { @@ -1310,16 +1383,16 @@ sendConcludeRequestAndWaitForResponse(MmsConnection self) mmsClient_createConcludeRequest(self, concludeMessage); - self->concludeState = CONCLUDE_STATE_REQUESTED; + setConcludeState(self, CONCLUDE_STATE_REQUESTED); IsoClientConnection_sendMessage(self->isoClient, concludeMessage); while (currentTime < waitUntilTime) { - if (self->associationState == MMS_STATE_CLOSED) + if (getAssociationState(self) == MMS_STATE_CLOSED) goto exit_function; - if (self->concludeState != CONCLUDE_STATE_REQUESTED) { + if (getConcludeState(self) != CONCLUDE_STATE_REQUESTED) { success = true; break; } @@ -1335,14 +1408,14 @@ sendConcludeRequestAndWaitForResponse(MmsConnection self) self->lastResponseError = MMS_ERROR_SERVICE_TIMEOUT; } -exit_function: + exit_function: return; } void MmsConnection_conclude(MmsConnection self, MmsError* mmsError) { - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1356,18 +1429,18 @@ MmsConnection_conclude(MmsConnection self, MmsError* mmsError) releaseResponse(self); - if (self->concludeState != CONCLUDE_STATE_ACCEPTED) { + if (getConcludeState(self) != CONCLUDE_STATE_ACCEPTED) { - if (self->associationState == MMS_STATE_CLOSED) + if (getAssociationState(self) == MMS_STATE_CLOSED) *mmsError = MMS_ERROR_CONNECTION_LOST; - if (self->concludeState == CONCLUDE_STATE_REJECTED) + if (getConcludeState(self) == CONCLUDE_STATE_REJECTED) *mmsError = MMS_ERROR_CONCLUDE_REJECTED; } self->connectionLostHandler = NULL; -exit_function: + exit_function: return; } @@ -1391,7 +1464,7 @@ mmsClient_getNameListSingleRequest( { bool moreFollows = false; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1420,7 +1493,7 @@ mmsClient_getNameListSingleRequest( releaseResponse(self); -exit_function: + exit_function: return moreFollows; } @@ -1494,7 +1567,7 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, { MmsValue* value = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1512,7 +1585,7 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, releaseResponse(self); -exit_function: + exit_function: return value; } @@ -1523,7 +1596,7 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, { MmsValue* value = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1546,13 +1619,42 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, return value; } +MmsValue* +MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, uint32_t index, const char* componentId) +{ + MmsValue* value = NULL; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + uint32_t invokeId = getNextInvokeId(self); + + mmsClient_createReadRequestAlternateAccessSingleIndexComponent(invokeId, domainId, itemId, index, componentId, + payload); + + ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload, mmsError); + + if (responseMessage != NULL) + value = mmsClient_parseReadResponse(self->lastResponse, NULL, false); + + releaseResponse(self); + +exit_function: + return value; +} + MmsValue* MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId, LinkedList /**/items) { MmsValue* value = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1570,7 +1672,7 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, releaseResponse(self); -exit_function: + exit_function: return value; } @@ -1581,7 +1683,7 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError { MmsValue* value = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1600,7 +1702,7 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError releaseResponse(self); -exit_function: + exit_function: return value; } @@ -1612,7 +1714,7 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific( { MmsValue* value = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1631,7 +1733,7 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific( releaseResponse(self); -exit_function: + exit_function: return value; } @@ -1641,7 +1743,7 @@ MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsEr { LinkedList attributes = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1661,7 +1763,7 @@ MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsEr releaseResponse(self); -exit_function: + exit_function: return attributes; } @@ -1671,7 +1773,7 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection se { LinkedList attributes = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1691,7 +1793,7 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection se releaseResponse(self); -exit_function: + exit_function: return attributes; } @@ -1699,7 +1801,7 @@ void MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName, LinkedList variableSpecs) { - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1719,7 +1821,7 @@ MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, releaseResponse(self); -exit_function: + exit_function: return; } @@ -1727,7 +1829,7 @@ void MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsError* mmsError, const char* listName, LinkedList variableSpecs) { - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1747,8 +1849,7 @@ MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, releaseResponse(self); - -exit_function: + exit_function: return; } @@ -1758,7 +1859,7 @@ MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, { bool isDeleted = false; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1777,7 +1878,7 @@ MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, releaseResponse(self); -exit_function: + exit_function: return isDeleted; } @@ -1787,7 +1888,7 @@ MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, { bool isDeleted = false; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1807,7 +1908,7 @@ MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, releaseResponse(self); -exit_function: + exit_function: return isDeleted; } @@ -1817,7 +1918,7 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError { MmsVariableSpecification* typeSpec = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1835,7 +1936,7 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError releaseResponse(self); -exit_function: + exit_function: return typeSpec; } @@ -1844,7 +1945,7 @@ MmsConnection_identify(MmsConnection self, MmsError* mmsError) { MmsServerIdentity* identity = NULL; - if (self->associationState != MMS_STATE_CONNECTED) { + if (getAssociationState(self) != MMS_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1862,13 +1963,13 @@ MmsConnection_identify(MmsConnection self, MmsError* mmsError) releaseResponse(self); -exit_function: + exit_function: return identity; } void MmsConnection_getServerStatus(MmsConnection self, MmsError* mmsError, int* vmdLogicalStatus, int* vmdPhysicalStatus, - bool extendedDerivation) +bool extendedDerivation) { ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); @@ -1879,15 +1980,15 @@ MmsConnection_getServerStatus(MmsConnection self, MmsError* mmsError, int* vmdLo ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload, mmsError); if (responseMessage != NULL) { - if (mmsClient_parseStatusResponse(self, vmdLogicalStatus, vmdPhysicalStatus) == false) - *mmsError = MMS_ERROR_PARSING_RESPONSE; - } + if (mmsClient_parseStatusResponse(self, vmdLogicalStatus, vmdPhysicalStatus) == false) + *mmsError = MMS_ERROR_PARSING_RESPONSE; + } - releaseResponse(self); + releaseResponse(self); } static LinkedList -readJournal(MmsConnection self, MmsError* mmsError, uint32_t invokeId, ByteBuffer* payload, bool* moreFollows) +readJournal(MmsConnection self, MmsError* mmsError, uint32_t invokeId, ByteBuffer* payload, bool* moreFollows) { ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload, mmsError); @@ -1926,7 +2027,6 @@ MmsJournalEntry_destroy(MmsJournalEntry self) } } - const MmsValue* MmsJournalEntry_getEntryID(MmsJournalEntry self) { @@ -2002,6 +2102,7 @@ int32_t MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filename, uint32_t initialPosition, uint32_t* fileSize, uint64_t* lastModified) { +#if (MMS_FILE_SERVICE == 1) ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); uint32_t invokeId = getNextInvokeId(self); @@ -2025,11 +2126,19 @@ MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filen releaseResponse(self); return frsmId; +#else + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: service not supported\n"); + + *mmsError = MMS_ERROR_OTHER; + return 0; +#endif } void MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId) { +#if (MMS_FILE_SERVICE == 1) ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); uint32_t invokeId = getNextInvokeId(self); @@ -2041,11 +2150,18 @@ MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId) /* nothing to do - response contains no data to evaluate */ releaseResponse(self); +#else + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: service not supported\n"); + + *mmsError = MMS_ERROR_OTHER; +#endif } void MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fileName) { +#if (MMS_FILE_SERVICE == 1) ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); uint32_t invokeId = getNextInvokeId(self); @@ -2057,12 +2173,19 @@ MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fil /* nothing to do - response contains no data to evaluate */ releaseResponse(self); +#else + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: service not supported\n"); + + *mmsError = MMS_ERROR_OTHER; +#endif } bool MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, MmsFileReadHandler handler, void* handlerParameter) { +#if (MMS_FILE_SERVICE == 1) ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); uint32_t invokeId = getNextInvokeId(self); @@ -2084,13 +2207,20 @@ MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, M releaseResponse(self); return moreFollows; -} +#else + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: service not supported\n"); + *mmsError = MMS_ERROR_OTHER; + return false; +#endif +} bool MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const char* fileSpecification, const char* continueAfter, MmsFileDirectoryHandler handler, void* handlerParameter) { +#if (MMS_FILE_SERVICE == 1) ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); uint32_t invokeId = getNextInvokeId(self); @@ -2109,11 +2239,19 @@ MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const cha releaseResponse(self); return moreFollows; +#else + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: service not supported\n"); + + *mmsError = MMS_ERROR_OTHER; + return false; +#endif } void MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* currentFileName, const char* newFileName) { +#if (MMS_FILE_SERVICE == 1) ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); uint32_t invokeId = getNextInvokeId(self); @@ -2125,12 +2263,18 @@ MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* cur /* nothing to do - response contains no data to evaluate */ releaseResponse(self); -} +#else + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: service not supported\n"); + *mmsError = MMS_ERROR_OTHER; +#endif +} void MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sourceFile, const char* destinationFile) { +#if ((MMS_FILE_SERVICE == 1) && (MMS_OBTAIN_FILE_SERVICE == 1)) ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); uint32_t invokeId = getNextInvokeId(self); @@ -2142,6 +2286,12 @@ MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sou /* nothing to do - response contains no data to evaluate */ releaseResponse(self); +#else + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: service not supported\n"); + + *mmsError = MMS_ERROR_OTHER; +#endif } MmsDataAccessError @@ -2287,13 +2437,13 @@ void MmsVariableAccessSpecification_destroy(MmsVariableAccessSpecification* self) { if (self->domainId != NULL) - GLOBAL_FREEMEM((void*) self->domainId); + GLOBAL_FREEMEM((void* ) self->domainId); if (self->itemId != NULL) - GLOBAL_FREEMEM((void*) self->itemId); + GLOBAL_FREEMEM((void* ) self->itemId); if (self->componentName != NULL) - GLOBAL_FREEMEM((void*) self->componentName); + GLOBAL_FREEMEM((void* ) self->componentName); GLOBAL_FREEMEM(self); } diff --git a/src/mms/iso_mms/client/mms_client_files.c b/src/mms/iso_mms/client/mms_client_files.c index 1eb79890e..4310da052 100644 --- a/src/mms/iso_mms/client/mms_client_files.c +++ b/src/mms/iso_mms/client/mms_client_files.c @@ -198,6 +198,8 @@ mmsClient_handleFileCloseRequest( #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ +#if (MMS_FILE_SERVICE == 1) + void mmsClient_createFileOpenRequest(uint32_t invokeId, ByteBuffer* request, const char* fileName, uint32_t initialPosition) { @@ -463,7 +465,13 @@ parseDirectoryEntry(uint8_t* buffer, int bufPos, int maxBufPos, MmsFileDirectory while (bufPos < maxBufPos) { uint8_t tag = buffer[bufPos++]; int length; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: message contains unknown tag!\n"); + return false; + } switch (tag) { case 0xa0: /* file-name */ @@ -471,7 +479,14 @@ parseDirectoryEntry(uint8_t* buffer, int bufPos, int maxBufPos, MmsFileDirectory filename = fileNameMemory; tag = buffer[bufPos++]; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: message contains unknown tag!\n"); + return false; + } + memcpy(filename, buffer + bufPos, length); filename[length] = 0; @@ -485,7 +500,7 @@ parseDirectoryEntry(uint8_t* buffer, int bufPos, int maxBufPos, MmsFileDirectory default: bufPos += length; if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileDirectoryResponse: message contains unknown tag!\n"); + printf("MMS_CLIENT: message contains unknown tag!\n"); return false; } @@ -511,7 +526,6 @@ parseListOfDirectoryEntries(uint8_t* buffer, int bufPos, int maxBufPos, int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) return false; int endPos = bufPos + length; @@ -524,7 +538,9 @@ parseListOfDirectoryEntries(uint8_t* buffer, int bufPos, int maxBufPos, while (bufPos < endPos) { tag = buffer[bufPos++]; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) return false; switch (tag) { case 0x30: /* Sequence */ @@ -583,7 +599,9 @@ mmsClient_parseFileDirectoryResponse(MmsConnection self, MmsFileDirectoryHandler while (bufPos < endPos) { tag = buffer[bufPos++]; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) return false; switch (tag) { case 0xa0: /* listOfDirectoryEntries */ @@ -641,7 +659,10 @@ mmsMsg_parseFileOpenResponse(uint8_t* buffer, int bufPos, int maxBufPos, int32_t while (bufPos < endPos) { tag = buffer[bufPos++]; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) + return false; switch (tag) { case 0x80: /* frsmId */ @@ -704,6 +725,8 @@ mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, int frs while (bufPos < endPos) { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) + return false; switch (tag) { case 0x80: /* fileData */ @@ -756,4 +779,4 @@ mmsClient_createFileCloseRequest(uint32_t invokeId, ByteBuffer* request, int32_t request->size = bufPos; } - +#endif /* (MMS_FILE_SERVICE == 1) */ diff --git a/src/mms/iso_mms/client/mms_client_get_var_access.c b/src/mms/iso_mms/client/mms_client_get_var_access.c index af9260223..d66f5f22f 100644 --- a/src/mms/iso_mms/client/mms_client_get_var_access.c +++ b/src/mms/iso_mms/client/mms_client_get_var_access.c @@ -31,186 +31,188 @@ #include "mms_client_internal.h" static MmsVariableSpecification* -createTypeSpecification(TypeSpecification_t* asnTypeSpec) { - MmsVariableSpecification* typeSpec = (MmsVariableSpecification*) - GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); - - switch (asnTypeSpec->present) { - case TypeSpecification_PR_structure: - { - typeSpec->type = MMS_STRUCTURE; - - int elementCount = asnTypeSpec->choice.structure.components.list.count; - typeSpec->typeSpec.structure.elementCount = elementCount; - - typeSpec->typeSpec.structure.elements = (MmsVariableSpecification**) - GLOBAL_CALLOC(elementCount, sizeof(MmsVariableSpecification*)); - - int i; - - for (i = 0; i < elementCount; i++) { - - char* name = StringUtils_createStringFromBuffer( - asnTypeSpec->choice.structure.components.list.array[i]->componentName->buf, - asnTypeSpec->choice.structure.components.list.array[i]->componentName->size); - - typeSpec->typeSpec.structure.elements[i] = - createTypeSpecification(asnTypeSpec->choice.structure.components. - list.array[i]->componentType); - - typeSpec->typeSpec.structure.elements[i]->name = name; - } - } - break; - case TypeSpecification_PR_array: - { - typeSpec->type = MMS_ARRAY; - - long elementCount; - asn_INTEGER2long(&asnTypeSpec->choice.array.numberOfElements, &elementCount); - - typeSpec->typeSpec.array.elementCount = elementCount; - - typeSpec->typeSpec.array.elementTypeSpec = - createTypeSpecification(asnTypeSpec->choice.array.elementType); - } - break; - case TypeSpecification_PR_boolean: - typeSpec->type = MMS_BOOLEAN; - break; - case TypeSpecification_PR_bitstring: - typeSpec->type = MMS_BIT_STRING; - typeSpec->typeSpec.bitString = asnTypeSpec->choice.bitstring; - break; - case TypeSpecification_PR_integer: - typeSpec->type = MMS_INTEGER; - typeSpec->typeSpec.integer = asnTypeSpec->choice.integer; - break; - case TypeSpecification_PR_unsigned: - typeSpec->type = MMS_UNSIGNED; - typeSpec->typeSpec.unsignedInteger = asnTypeSpec->choice.Unsigned; - break; - case TypeSpecification_PR_floatingpoint: - typeSpec->type = MMS_FLOAT; - typeSpec->typeSpec.floatingpoint.exponentWidth = - asnTypeSpec->choice.floatingpoint.exponentwidth; - typeSpec->typeSpec.floatingpoint.formatWidth = - asnTypeSpec->choice.floatingpoint.formatwidth; - break; - case TypeSpecification_PR_octetstring: - typeSpec->type = MMS_OCTET_STRING; - typeSpec->typeSpec.octetString = asnTypeSpec->choice.octetstring; - break; - case TypeSpecification_PR_visiblestring: - typeSpec->type = MMS_VISIBLE_STRING; - typeSpec->typeSpec.visibleString = asnTypeSpec->choice.visiblestring; - break; - case TypeSpecification_PR_mMSString: - typeSpec->type = MMS_STRING; - typeSpec->typeSpec.mmsString = asnTypeSpec->choice.mMSString; - break; - case TypeSpecification_PR_utctime: - typeSpec->type = MMS_UTC_TIME; - break; - case TypeSpecification_PR_binarytime: - typeSpec->type = MMS_BINARY_TIME; - if (asnTypeSpec->choice.binarytime == 0) - typeSpec->typeSpec.binaryTime = 4; - else - typeSpec->typeSpec.binaryTime = 6; - break; - default: - printf("ERROR: unknown type in type specification\n"); - break; - } - - return typeSpec; +createTypeSpecification(TypeSpecification_t* asnTypeSpec) +{ + MmsVariableSpecification* typeSpec = (MmsVariableSpecification*) + GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + + switch (asnTypeSpec->present) + { + case TypeSpecification_PR_structure: + { + typeSpec->type = MMS_STRUCTURE; + + int elementCount = asnTypeSpec->choice.structure.components.list.count; + typeSpec->typeSpec.structure.elementCount = elementCount; + + typeSpec->typeSpec.structure.elements = (MmsVariableSpecification**) + GLOBAL_CALLOC(elementCount, sizeof(MmsVariableSpecification*)); + + int i; + + for (i = 0; i < elementCount; i++) { + + char* name = StringUtils_createStringFromBuffer( + asnTypeSpec->choice.structure.components.list.array[i]->componentName->buf, + asnTypeSpec->choice.structure.components.list.array[i]->componentName->size); + + typeSpec->typeSpec.structure.elements[i] = + createTypeSpecification(asnTypeSpec->choice.structure.components. + list.array[i]->componentType); + + typeSpec->typeSpec.structure.elements[i]->name = name; + } + } + break; + case TypeSpecification_PR_array: + { + typeSpec->type = MMS_ARRAY; + + long elementCount; + asn_INTEGER2long(&asnTypeSpec->choice.array.numberOfElements, &elementCount); + + typeSpec->typeSpec.array.elementCount = elementCount; + + typeSpec->typeSpec.array.elementTypeSpec = + createTypeSpecification(asnTypeSpec->choice.array.elementType); + } + break; + case TypeSpecification_PR_boolean: + typeSpec->type = MMS_BOOLEAN; + break; + case TypeSpecification_PR_bitstring: + typeSpec->type = MMS_BIT_STRING; + typeSpec->typeSpec.bitString = asnTypeSpec->choice.bitstring; + break; + case TypeSpecification_PR_integer: + typeSpec->type = MMS_INTEGER; + typeSpec->typeSpec.integer = asnTypeSpec->choice.integer; + break; + case TypeSpecification_PR_unsigned: + typeSpec->type = MMS_UNSIGNED; + typeSpec->typeSpec.unsignedInteger = asnTypeSpec->choice.Unsigned; + break; + case TypeSpecification_PR_floatingpoint: + typeSpec->type = MMS_FLOAT; + typeSpec->typeSpec.floatingpoint.exponentWidth = + asnTypeSpec->choice.floatingpoint.exponentwidth; + typeSpec->typeSpec.floatingpoint.formatWidth = + asnTypeSpec->choice.floatingpoint.formatwidth; + break; + case TypeSpecification_PR_octetstring: + typeSpec->type = MMS_OCTET_STRING; + typeSpec->typeSpec.octetString = asnTypeSpec->choice.octetstring; + break; + case TypeSpecification_PR_visiblestring: + typeSpec->type = MMS_VISIBLE_STRING; + typeSpec->typeSpec.visibleString = asnTypeSpec->choice.visiblestring; + break; + case TypeSpecification_PR_mMSString: + typeSpec->type = MMS_STRING; + typeSpec->typeSpec.mmsString = asnTypeSpec->choice.mMSString; + break; + case TypeSpecification_PR_utctime: + typeSpec->type = MMS_UTC_TIME; + break; + case TypeSpecification_PR_binarytime: + typeSpec->type = MMS_BINARY_TIME; + if (asnTypeSpec->choice.binarytime == 0) + typeSpec->typeSpec.binaryTime = 4; + else + typeSpec->typeSpec.binaryTime = 6; + break; + default: + printf("ERROR: unknown type in type specification\n"); + break; + } + + return typeSpec; } MmsVariableSpecification* mmsClient_parseGetVariableAccessAttributesResponse(ByteBuffer* message, uint32_t* invokeId) { - MmsPdu_t* mmsPdu = 0; /* allow asn1c to allocate structure */ - MmsVariableSpecification* typeSpec = NULL; + MmsPdu_t* mmsPdu = 0; /* allow asn1c to allocate structure */ + MmsVariableSpecification* typeSpec = NULL; - asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, - (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); + asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, + (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); - if (rval.code != RC_OK) - return NULL; + if (rval.code != RC_OK) + return NULL; - if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { + if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { - if (invokeId != NULL) - *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); + if (invokeId != NULL) + *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); - if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == - ConfirmedServiceResponse_PR_getVariableAccessAttributes) - { - GetVariableAccessAttributesResponse_t* response; + if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == + ConfirmedServiceResponse_PR_getVariableAccessAttributes) + { + GetVariableAccessAttributesResponse_t* response; - response = &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.getVariableAccessAttributes); - TypeSpecification_t* asnTypeSpec = &response->typeSpecification; + response = &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.getVariableAccessAttributes); + TypeSpecification_t* asnTypeSpec = &response->typeSpecification; - typeSpec = createTypeSpecification(asnTypeSpec); - } - } + typeSpec = createTypeSpecification(asnTypeSpec); + } + } - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return typeSpec; + return typeSpec; } int mmsClient_createGetVariableAccessAttributesRequest( uint32_t invokeId, - const char* domainId, const char* itemId, - ByteBuffer* writeBuffer) + const char* domainId, const char* itemId, + ByteBuffer* writeBuffer) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = - ConfirmedServiceRequest_PR_getVariableAccessAttributes; + mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = + ConfirmedServiceRequest_PR_getVariableAccessAttributes; - GetVariableAccessAttributesRequest_t* request; + GetVariableAccessAttributesRequest_t* request; - request = - &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.getVariableAccessAttributes); + request = + &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.getVariableAccessAttributes); - request->present = GetVariableAccessAttributesRequest_PR_name; + request->present = GetVariableAccessAttributesRequest_PR_name; - if (domainId != NULL) { + if (domainId != NULL) { request->choice.name.present = ObjectName_PR_domainspecific; request->choice.name.choice.domainspecific.domainId.buf = (uint8_t*) domainId; request->choice.name.choice.domainspecific.domainId.size = strlen(domainId); request->choice.name.choice.domainspecific.itemId.buf = (uint8_t*) itemId; request->choice.name.choice.domainspecific.itemId.size = strlen(itemId); - } - else { - request->choice.name.present = ObjectName_PR_vmdspecific; - request->choice.name.choice.vmdspecific.buf = (uint8_t*) itemId; - request->choice.name.choice.vmdspecific.size = strlen(itemId); - } + } + else { + request->choice.name.present = ObjectName_PR_vmdspecific; + request->choice.name.choice.vmdspecific.buf = (uint8_t*) itemId; + request->choice.name.choice.vmdspecific.size = strlen(itemId); + } - asn_enc_rval_t rval; + asn_enc_rval_t rval; - rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - if (domainId != NULL) { + if (domainId != NULL) { request->choice.name.choice.domainspecific.domainId.buf = 0; request->choice.name.choice.domainspecific.domainId.size = 0; request->choice.name.choice.domainspecific.itemId.buf = 0; request->choice.name.choice.domainspecific.itemId.size = 0; - } - else { - request->choice.name.choice.vmdspecific.buf = 0; - request->choice.name.choice.vmdspecific.size = 0; - } + } + else { + request->choice.name.choice.vmdspecific.buf = 0; + request->choice.name.choice.vmdspecific.size = 0; + } - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return rval.encoded; + return rval.encoded; } diff --git a/src/mms/iso_mms/client/mms_client_identify.c b/src/mms/iso_mms/client/mms_client_identify.c index ea8519f06..dade11dc9 100644 --- a/src/mms/iso_mms/client/mms_client_identify.c +++ b/src/mms/iso_mms/client/mms_client_identify.c @@ -81,6 +81,7 @@ mmsClient_parseIdentifyResponse(MmsConnection self) tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) goto exit_error; switch (tag) { case 0x80: /* vendorName */ diff --git a/src/mms/iso_mms/client/mms_client_initiate.c b/src/mms/iso_mms/client/mms_client_initiate.c index d6c61892f..10810ec50 100644 --- a/src/mms/iso_mms/client/mms_client_initiate.c +++ b/src/mms/iso_mms/client/mms_client_initiate.c @@ -169,20 +169,30 @@ mmsClient_parseInitiateResponse(MmsConnection self) self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; - int bufPos = 0; + int bufPos = 1; /* ignore tag - already checked */ + int maxBufPos = ByteBuffer_getSize(self->lastResponse); uint8_t* buffer = ByteBuffer_getBuffer(self->lastResponse); + int length; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) + return false; + + if (bufPos + length > maxBufPos) + return false; + while (bufPos < maxBufPos) { uint8_t tag = buffer[bufPos++]; - int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { - // TODO write initiate error PDU! + if (bufPos < 0) + return false; + + if (bufPos + length > maxBufPos) return false; - } switch (tag) { case 0x80: /* local-detail-calling */ diff --git a/src/mms/iso_mms/client/mms_client_journals.c b/src/mms/iso_mms/client/mms_client_journals.c index cf59685e3..89ffebba7 100644 --- a/src/mms/iso_mms/client/mms_client_journals.c +++ b/src/mms/iso_mms/client/mms_client_journals.c @@ -42,11 +42,13 @@ parseJournalVariable(uint8_t* buffer, int bufPos, int maxLength, MmsJournalVaria while (bufPos < maxBufPos) { - int length; uint8_t tag = buffer[bufPos++]; + + int length; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if ((bufPos + length) > maxBufPos) { /* check length field for validity */ + if ((bufPos < 0) || ((bufPos + length) > maxBufPos)) { /* check length field for validity */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -94,7 +96,7 @@ parseJournalVariables(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntr uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if ((bufPos + length) > maxBufPos) { /* check length field for validity */ + if ((bufPos < 0) || ((bufPos + length) > maxBufPos)) { /* check length field for validity */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -137,7 +139,7 @@ parseData(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry journalEnt uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if ((bufPos + length) > maxBufPos) { /* check length field for validity */ + if ((bufPos < 0) || ((bufPos + length) > maxBufPos)) { /* check length field for validity */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -175,7 +177,7 @@ parseEntryContent(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry jo uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if ((bufPos + length) > maxBufPos) { /* check length field for validity */ + if ((bufPos < 0) ||((bufPos + length) > maxBufPos)) { /* check length field for validity */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -276,7 +278,7 @@ parseListOfJournalEntries(uint8_t* buffer, int bufPos, int maxLength, LinkedList uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if ((bufPos + length) > maxBufPos) { /* check length field for validity */ + if ((bufPos < 0) || ((bufPos + length) > maxBufPos)) { /* check length field for validity */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -330,8 +332,7 @@ mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, Linked } bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) - return false; + if (bufPos < 0) return false; int endPos = bufPos + length; @@ -346,6 +347,7 @@ mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, Linked while (bufPos < endPos) { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) return false; switch (tag) { case 0xa0: /* listOfJournalEntry */ diff --git a/src/mms/iso_mms/client/mms_client_named_variable_list.c b/src/mms/iso_mms/client/mms_client_named_variable_list.c index 9714fd7e0..6199d49dc 100644 --- a/src/mms/iso_mms/client/mms_client_named_variable_list.c +++ b/src/mms/iso_mms/client/mms_client_named_variable_list.c @@ -81,92 +81,91 @@ mmsClient_createDeleteNamedVariableListRequest(long invokeId, ByteBuffer* writeB void mmsClient_createDeleteAssociationSpecificNamedVariableListRequest( - long invokeId, - ByteBuffer* writeBuffer, - const char* listNameId) + long invokeId, + ByteBuffer* writeBuffer, + const char* listNameId) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = - ConfirmedServiceRequest_PR_deleteNamedVariableList; + mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = + ConfirmedServiceRequest_PR_deleteNamedVariableList; - DeleteNamedVariableListRequest_t* request = - &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); + DeleteNamedVariableListRequest_t* request = + &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); - request->listOfVariableListName = (struct DeleteNamedVariableListRequest__listOfVariableListName*) GLOBAL_CALLOC(1, - sizeof(struct DeleteNamedVariableListRequest__listOfVariableListName)); + request->listOfVariableListName = (struct DeleteNamedVariableListRequest__listOfVariableListName*) GLOBAL_CALLOC(1, + sizeof(struct DeleteNamedVariableListRequest__listOfVariableListName)); - request->listOfVariableListName->list.count = 1; - request->listOfVariableListName->list.size = 1; + request->listOfVariableListName->list.count = 1; + request->listOfVariableListName->list.size = 1; - request->listOfVariableListName->list.array = (ObjectName_t**) GLOBAL_CALLOC(1, sizeof(ObjectName_t*)); - request->listOfVariableListName->list.array[0] = (ObjectName_t*) GLOBAL_CALLOC(1, sizeof(ObjectName_t)); + request->listOfVariableListName->list.array = (ObjectName_t**) GLOBAL_CALLOC(1, sizeof(ObjectName_t*)); + request->listOfVariableListName->list.array[0] = (ObjectName_t*) GLOBAL_CALLOC(1, sizeof(ObjectName_t)); - request->listOfVariableListName->list.array[0]->present = ObjectName_PR_aaspecific; + request->listOfVariableListName->list.array[0]->present = ObjectName_PR_aaspecific; - request->listOfVariableListName->list.array[0]->choice.aaspecific.size = strlen(listNameId); - request->listOfVariableListName->list.array[0]->choice.aaspecific.buf = (uint8_t*) StringUtils_copyString(listNameId); + request->listOfVariableListName->list.array[0]->choice.aaspecific.size = strlen(listNameId); + request->listOfVariableListName->list.array[0]->choice.aaspecific.buf = (uint8_t*) StringUtils_copyString(listNameId); - request->scopeOfDelete = (INTEGER_t*) GLOBAL_CALLOC(1, sizeof(INTEGER_t)); - asn_long2INTEGER(request->scopeOfDelete, DeleteNamedVariableListRequest__scopeOfDelete_specific); + request->scopeOfDelete = (INTEGER_t*) GLOBAL_CALLOC(1, sizeof(INTEGER_t)); + asn_long2INTEGER(request->scopeOfDelete, DeleteNamedVariableListRequest__scopeOfDelete_specific); - der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); } bool mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId) { - MmsPdu_t* mmsPdu = 0; + MmsPdu_t* mmsPdu = 0; - bool retVal = false; + bool retVal = false; - asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, - (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); + asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, + (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); - if (rval.code == RC_OK) { - if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { + if (rval.code == RC_OK) { + if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { - if (invokeId != NULL) - *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); + if (invokeId != NULL) + *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); - if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == - ConfirmedServiceResponse_PR_deleteNamedVariableList) - { - DeleteNamedVariableListResponse_t* response = - &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.deleteNamedVariableList); + if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == + ConfirmedServiceResponse_PR_deleteNamedVariableList) + { + DeleteNamedVariableListResponse_t* response = + &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.deleteNamedVariableList); - long numberDeleted; + long numberDeleted; - asn_INTEGER2long(&(response->numberDeleted), &numberDeleted); + asn_INTEGER2long(&(response->numberDeleted), &numberDeleted); - if (numberDeleted == 1) - retVal = true; - } - } - } + if (numberDeleted == 1) + retVal = true; + } + } + } - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return retVal; + return retVal; } - void mmsClient_createGetNamedVariableListAttributesRequest(uint32_t invokeId, ByteBuffer* writeBuffer, - const char* domainId, const char* listNameId) + const char* domainId, const char* listNameId) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = - ConfirmedServiceRequest_PR_getNamedVariableListAttributes; + mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = + ConfirmedServiceRequest_PR_getNamedVariableListAttributes; - GetNamedVariableListAttributesRequest_t* request = - &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.getNamedVariableListAttributes); + GetNamedVariableListAttributesRequest_t* request = + &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.getNamedVariableListAttributes); - if (domainId != NULL) { + if (domainId != NULL) { request->present = ObjectName_PR_domainspecific; request->choice.domainspecific.domainId.size = strlen(domainId); @@ -174,18 +173,18 @@ mmsClient_createGetNamedVariableListAttributesRequest(uint32_t invokeId, ByteBuf request->choice.domainspecific.itemId.size = strlen(listNameId); request->choice.domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(listNameId); - } - else { - request->present = ObjectName_PR_vmdspecific; + } + else { + request->present = ObjectName_PR_vmdspecific; - request->choice.vmdspecific.size = strlen(listNameId); - request->choice.vmdspecific.buf = (uint8_t*) StringUtils_copyString(listNameId); - } + request->choice.vmdspecific.size = strlen(listNameId); + request->choice.vmdspecific.buf = (uint8_t*) StringUtils_copyString(listNameId); + } - der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); } void @@ -195,7 +194,7 @@ mmsClient_createGetNamedVariableListAttributesRequestAssociationSpecific(uint32_ MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = - ConfirmedServiceRequest_PR_getNamedVariableListAttributes; + ConfirmedServiceRequest_PR_getNamedVariableListAttributes; GetNamedVariableListAttributesRequest_t* request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.getNamedVariableListAttributes); @@ -214,102 +213,98 @@ mmsClient_createGetNamedVariableListAttributesRequestAssociationSpecific(uint32_ static LinkedList /* */ parseNamedVariableAttributes(GetNamedVariableListAttributesResponse_t* response, bool* deletable) { - if (deletable != NULL) - *deletable = response->mmsDeletable; - - int attributesCount = response->listOfVariable.list.count; - int i; + if (deletable != NULL) + *deletable = response->mmsDeletable; - LinkedList attributes = LinkedList_create(); + int attributesCount = response->listOfVariable.list.count; + int i; - for (i = 0; i < attributesCount; i++) { + LinkedList attributes = LinkedList_create(); - char* domainId; - char* itemId; + for (i = 0; i < attributesCount; i++) { + char* domainId; + char* itemId; - if (response->listOfVariable.list.array[i]->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) { + if (response->listOfVariable.list.array[i]->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) { - domainId = NULL; + domainId = NULL; - itemId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]-> - variableSpecification.choice.name.choice.vmdspecific); - } - else { - domainId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]-> - variableSpecification.choice.name.choice.domainspecific.domainId); + itemId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]-> + variableSpecification.choice.name.choice.vmdspecific); + } + else { + domainId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]-> + variableSpecification.choice.name.choice.domainspecific.domainId); - itemId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]-> - variableSpecification.choice.name.choice.domainspecific.itemId); - } + itemId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]-> + variableSpecification.choice.name.choice.domainspecific.itemId); + } - MmsVariableAccessSpecification* listEntry = MmsVariableAccessSpecification_create(domainId, itemId); + MmsVariableAccessSpecification* listEntry = MmsVariableAccessSpecification_create(domainId, itemId); - LinkedList_add(attributes, listEntry); - } + LinkedList_add(attributes, listEntry); + } - return attributes; + return attributes; } LinkedList /* */ -mmsClient_parseGetNamedVariableListAttributesResponse(ByteBuffer* message, uint32_t* invokeId, - bool* /*OUT*/ deletable) +mmsClient_parseGetNamedVariableListAttributesResponse(ByteBuffer* message, uint32_t* invokeId, bool* /*OUT*/deletable) { - MmsPdu_t* mmsPdu = 0; + MmsPdu_t* mmsPdu = 0; - LinkedList attributes = NULL; + LinkedList attributes = NULL; - asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, - (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); + asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, + (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); + if (rval.code == RC_OK) { + if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { - if (rval.code == RC_OK) { - if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { + if (invokeId != NULL) + *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); - if (invokeId != NULL) - *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); - - if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == - ConfirmedServiceResponse_PR_getNamedVariableListAttributes) - { - attributes = parseNamedVariableAttributes( - &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.getNamedVariableListAttributes), - deletable); - } - } - } + if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == + ConfirmedServiceResponse_PR_getNamedVariableListAttributes) + { + attributes = parseNamedVariableAttributes( + &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.getNamedVariableListAttributes), + deletable); + } + } + } - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return attributes; + return attributes; } - void mmsClient_createDefineNamedVariableListRequest( - uint32_t invokeId, - ByteBuffer* writeBuffer, - const char* domainId, - const char* listNameId, - LinkedList /**/ listOfVariables, - bool associationSpecific) + uint32_t invokeId, + ByteBuffer* writeBuffer, + const char* domainId, + const char* listNameId, + LinkedList /**/listOfVariables, + bool associationSpecific) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = - ConfirmedServiceRequest_PR_defineNamedVariableList; + mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = + ConfirmedServiceRequest_PR_defineNamedVariableList; - DefineNamedVariableListRequest_t* request = - &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList); + DefineNamedVariableListRequest_t* request = + &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList); - if (associationSpecific) { - request->variableListName.present = ObjectName_PR_aaspecific; + if (associationSpecific) { + request->variableListName.present = ObjectName_PR_aaspecific; - request->variableListName.choice.aaspecific.size = strlen(listNameId); - request->variableListName.choice.aaspecific.buf = (uint8_t*) StringUtils_copyString(listNameId); - } - else { - if (domainId != NULL) { /* domain scope */ + request->variableListName.choice.aaspecific.size = strlen(listNameId); + request->variableListName.choice.aaspecific.buf = (uint8_t*) StringUtils_copyString(listNameId); + } + else { + if (domainId != NULL) { /* domain scope */ request->variableListName.present = ObjectName_PR_domainspecific; request->variableListName.choice.domainspecific.domainId.size = strlen(domainId); @@ -317,135 +312,132 @@ mmsClient_createDefineNamedVariableListRequest( request->variableListName.choice.domainspecific.itemId.size = strlen(listNameId); request->variableListName.choice.domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(listNameId); - } - else { /* VMD scope */ - request->variableListName.present = ObjectName_PR_vmdspecific; + } + else { /* VMD scope */ + request->variableListName.present = ObjectName_PR_vmdspecific; - request->variableListName.choice.vmdspecific.size = strlen(listNameId); + request->variableListName.choice.vmdspecific.size = strlen(listNameId); request->variableListName.choice.vmdspecific.buf = (uint8_t*) StringUtils_copyString(listNameId); - } - } + } + } - int listSize = LinkedList_size(listOfVariables); + int listSize = LinkedList_size(listOfVariables); - request->listOfVariable.list.count = listSize; - request->listOfVariable.list.size = listSize; + request->listOfVariable.list.count = listSize; + request->listOfVariable.list.size = listSize; - request->listOfVariable.list.array = - (struct DefineNamedVariableListRequest__listOfVariable__Member**) GLOBAL_CALLOC(listSize, sizeof(void*)); + request->listOfVariable.list.array = + (struct DefineNamedVariableListRequest__listOfVariable__Member**) GLOBAL_CALLOC(listSize, sizeof(void*)); - int i = 0; - LinkedList element = LinkedList_getNext(listOfVariables); - while (i < listSize) { + int i = 0; + LinkedList element = LinkedList_getNext(listOfVariables); + while (i < listSize) { - MmsVariableAccessSpecification* variableSpec = (MmsVariableAccessSpecification*) element->data; + MmsVariableAccessSpecification* variableSpec = (MmsVariableAccessSpecification*) element->data; - request->listOfVariable.list.array[i] = (struct DefineNamedVariableListRequest__listOfVariable__Member*) + request->listOfVariable.list.array[i] = (struct DefineNamedVariableListRequest__listOfVariable__Member*) GLOBAL_CALLOC(1, sizeof(struct DefineNamedVariableListRequest__listOfVariable__Member)); - request->listOfVariable.list.array[i]->variableSpecification.present = - VariableSpecification_PR_name; + request->listOfVariable.list.array[i]->variableSpecification.present = + VariableSpecification_PR_name; - request->listOfVariable.list.array[i]->variableSpecification.choice.name.present = - ObjectName_PR_domainspecific; + request->listOfVariable.list.array[i]->variableSpecification.choice.name.present = + ObjectName_PR_domainspecific; - request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. - domainspecific.domainId.size = strlen(variableSpec->domainId); + request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. + domainspecific.domainId.size = strlen(variableSpec->domainId); - request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. - domainspecific.domainId.buf = (uint8_t*) StringUtils_copyString(variableSpec->domainId); + request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. + domainspecific.domainId.buf = (uint8_t*) StringUtils_copyString(variableSpec->domainId); - request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. - domainspecific.itemId.size = strlen(variableSpec->itemId); + request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. + domainspecific.itemId.size = strlen(variableSpec->itemId); - request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. - domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(variableSpec->itemId); + request->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. + domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(variableSpec->itemId); - //TODO add alternate access - if (variableSpec->arrayIndex != -1) { + //TODO add alternate access + if (variableSpec->arrayIndex != -1) { - AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); - alternateAccess->list.count = 1; - alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); - alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); + AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); + alternateAccess->list.count = 1; + alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); + alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); - alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; - alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); + alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; + alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); - alternateAccess->list.array[0]->choice.unnamed->present = - AlternateAccessSelection_PR_selectAlternateAccess; + alternateAccess->list.array[0]->choice.unnamed->present = + AlternateAccessSelection_PR_selectAlternateAccess; - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = - AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index; + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index; - asn_long2INTEGER(&(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.index), - variableSpec->arrayIndex); + asn_long2INTEGER(&(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.index), + variableSpec->arrayIndex); - if (variableSpec->componentName != NULL) { + if (variableSpec->componentName != NULL) { - AlternateAccess_t* componentAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); + AlternateAccess_t* componentAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); - componentAccess->list.count = 1; - componentAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); - componentAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); + componentAccess->list.count = 1; + componentAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); + componentAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); - componentAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; - componentAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); + componentAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; + componentAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); + componentAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; + componentAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_component; - componentAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; - componentAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = - AlternateAccessSelection__selectAccess_PR_component; + Identifier_t* componentIdentifier = + &(componentAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component); - Identifier_t* componentIdentifier = - &(componentAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component); + componentIdentifier->size = strlen(variableSpec->componentName); + componentIdentifier->buf = (uint8_t*) StringUtils_copyString(variableSpec->componentName); - componentIdentifier->size = strlen(variableSpec->componentName); - componentIdentifier->buf = (uint8_t*) StringUtils_copyString(variableSpec->componentName); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess + = componentAccess; + } - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess - = componentAccess; - } + request->listOfVariable.list.array[i]->alternateAccess = alternateAccess; + } - request->listOfVariable.list.array[i]->alternateAccess = alternateAccess; - } + element = LinkedList_getNext(element); + i++; + } - element = LinkedList_getNext(element); - - i++; - } - - der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); } bool mmsClient_parseDefineNamedVariableResponse(ByteBuffer* message, uint32_t* invokeId) { - MmsPdu_t* mmsPdu = 0; - bool retVal = false; + MmsPdu_t* mmsPdu = 0; + bool retVal = false; - asn_dec_rval_t rval; + asn_dec_rval_t rval; - rval = ber_decode(NULL, &asn_DEF_MmsPdu, - (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); + rval = ber_decode(NULL, &asn_DEF_MmsPdu, + (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); + if (rval.code == RC_OK) { + if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { + if (invokeId != NULL) + *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); - if (rval.code == RC_OK) { - if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { - if (invokeId != NULL) - *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); - - if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == - ConfirmedServiceResponse_PR_defineNamedVariableList) - retVal = true; - } - } + if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == + ConfirmedServiceResponse_PR_defineNamedVariableList) + retVal = true; + } + } - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return retVal; + return retVal; } diff --git a/src/mms/iso_mms/client/mms_client_read.c b/src/mms/iso_mms/client/mms_client_read.c index 4297ad02a..30a211c6e 100644 --- a/src/mms/iso_mms/client/mms_client_read.c +++ b/src/mms/iso_mms/client/mms_client_read.c @@ -49,7 +49,8 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi AccessResult_PR presentType = accessResultList[i]->present; if (presentType == AccessResult_PR_failure) { - if (DEBUG_MMS_CLIENT) printf("access error!\n"); + if (DEBUG_MMS_CLIENT) + printf("access error!\n"); if (accessResultList[i]->choice.failure.size > 0) { int errorCode = (int) accessResultList[i]->choice.failure.buf[0]; @@ -103,7 +104,7 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi int size = accessResultList[i]->choice.bitstring.size; value->value.bitString.size = (size * 8) - - accessResultList[i]->choice.bitstring.bits_unused; + - accessResultList[i]->choice.bitstring.bits_unused; value->value.bitString.buf = (uint8_t*) GLOBAL_MALLOC(size); memcpy(value->value.bitString.buf, @@ -113,14 +114,14 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi else if (presentType == AccessResult_PR_integer) { Asn1PrimitiveValue* berInteger = BerInteger_createFromBuffer(accessResultList[i]->choice.integer.buf, - accessResultList[i]->choice.integer.size); + accessResultList[i]->choice.integer.size); value = MmsValue_newIntegerFromBerInteger(berInteger); } else if (presentType == AccessResult_PR_unsigned) { Asn1PrimitiveValue* berInteger = BerInteger_createFromBuffer(accessResultList[i]->choice.Unsigned.buf, - accessResultList[i]->choice.Unsigned.size); + accessResultList[i]->choice.Unsigned.size); value = MmsValue_newUnsignedFromBerInteger(berInteger); } @@ -139,9 +140,9 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi value->value.floatingPoint.buf = (uint8_t*) GLOBAL_MALLOC(4); #if (ORDER_LITTLE_ENDIAN == 1) - memcpyReverseByteOrder(value->value.floatingPoint.buf, floatBuf, 4); + memcpyReverseByteOrder(value->value.floatingPoint.buf, floatBuf, 4); #else - memcpy(value->value.floatingPoint.buf, floatBuf, 4); + memcpy(value->value.floatingPoint.buf, floatBuf, 4); #endif } @@ -179,19 +180,19 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi value->value.visibleString.buf[strSize] = 0; } else if (presentType == AccessResult_PR_mMSString) { - value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_STRING; + value->type = MMS_STRING; - int strSize = accessResultList[i]->choice.mMSString.size; + int strSize = accessResultList[i]->choice.mMSString.size; - value->value.visibleString.buf = (char*) GLOBAL_MALLOC(strSize + 1); - value->value.visibleString.size = strSize; + value->value.visibleString.buf = (char*) GLOBAL_MALLOC(strSize + 1); + value->value.visibleString.size = strSize; - memcpy(value->value.visibleString.buf, - accessResultList[i]->choice.mMSString.buf, strSize); + memcpy(value->value.visibleString.buf, + accessResultList[i]->choice.mMSString.buf, strSize); - value->value.visibleString.buf[strSize] = 0; + value->value.visibleString.buf[strSize] = 0; } else if (presentType == AccessResult_PR_utctime) { @@ -215,14 +216,14 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi } } else if (presentType == AccessResult_PR_octetstring) { - int size = accessResultList[i]->choice.octetstring.size; - - value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_OCTET_STRING; - value->value.octetString.maxSize = size; - value->value.octetString.size = size; - value->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(size); - memcpy(value->value.octetString.buf, accessResultList[i]->choice.octetstring.buf, size); + int size = accessResultList[i]->choice.octetstring.size; + + value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + value->type = MMS_OCTET_STRING; + value->value.octetString.maxSize = size; + value->value.octetString.size = size; + value->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(size); + memcpy(value->value.octetString.buf, accessResultList[i]->choice.octetstring.buf, size); } else { printf("unknown type %i\n", presentType); @@ -239,7 +240,6 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi return valueList; } - /* * \param createArray if multiple variables should be read (e.g. if a data set is read) an array should * be created that contains the access results. @@ -247,68 +247,65 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi MmsValue* mmsClient_parseReadResponse(ByteBuffer* message, uint32_t* invokeId, bool createArray) { - MmsPdu_t* mmsPdu = 0; /* allow asn1c to allocate structure */ + MmsPdu_t* mmsPdu = 0; /* allow asn1c to allocate structure */ - MmsValue* valueList = NULL; + MmsValue* valueList = NULL; - asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, - (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); + asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, + (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); - if (rval.code != RC_OK) - return NULL; + if (rval.code != RC_OK) + return NULL; - if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { + if (mmsPdu->present == MmsPdu_PR_confirmedResponsePdu) { - if (invokeId != NULL) - *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); + if (invokeId != NULL) + *invokeId = mmsClient_getInvokeId(&mmsPdu->choice.confirmedResponsePdu); - if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == ConfirmedServiceResponse_PR_read) { - ReadResponse_t* response = &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.read); + if (mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present == ConfirmedServiceResponse_PR_read) { + ReadResponse_t* response = &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.read); - int elementCount = response->listOfAccessResult.list.count; + int elementCount = response->listOfAccessResult.list.count; - valueList = mmsClient_parseListOfAccessResults(response->listOfAccessResult.list.array, - elementCount, createArray); - } - } + valueList = mmsClient_parseListOfAccessResults(response->listOfAccessResult.list.array, + elementCount, createArray); + } + } - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return valueList; + return valueList; } - static ReadRequest_t* -createReadRequest (MmsPdu_t* mmsPdu) +createReadRequest(MmsPdu_t* mmsPdu) { - mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = - ConfirmedServiceRequest_PR_read; + mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = + ConfirmedServiceRequest_PR_read; - return &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.read); + return &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.read); } - int mmsClient_createReadNamedVariableListRequest(uint32_t invokeId, const char* domainId, const char* itemId, - ByteBuffer* writeBuffer, bool specWithResult) + ByteBuffer* writeBuffer, bool specWithResult) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - - ReadRequest_t* readRequest = createReadRequest(mmsPdu); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - if (specWithResult) { - readRequest->specificationWithResult = (BOOLEAN_t*) GLOBAL_CALLOC(1, sizeof(BOOLEAN_t)); - (*(readRequest->specificationWithResult)) = true; - } - else - readRequest->specificationWithResult = NULL; + ReadRequest_t* readRequest = createReadRequest(mmsPdu); - readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_variableListName; + if (specWithResult) { + readRequest->specificationWithResult = (BOOLEAN_t*) GLOBAL_CALLOC(1, sizeof(BOOLEAN_t)); + (*(readRequest->specificationWithResult)) = true; + } + else + readRequest->specificationWithResult = NULL; - ObjectName_t* objectName = &(readRequest->variableAccessSpecification.choice.variableListName); + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_variableListName; + ObjectName_t* objectName = &(readRequest->variableAccessSpecification.choice.variableListName); - if (domainId != NULL) { + if (domainId != NULL) { objectName->present = ObjectName_PR_domainspecific; objectName->choice.domainspecific.domainId.buf = (uint8_t*) StringUtils_copyString(domainId); @@ -316,59 +313,59 @@ mmsClient_createReadNamedVariableListRequest(uint32_t invokeId, const char* doma objectName->choice.domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(itemId); objectName->choice.domainspecific.itemId.size = strlen(itemId); - } - else { + } + else { objectName->present = ObjectName_PR_vmdspecific; objectName->choice.vmdspecific.buf = (uint8_t*) StringUtils_copyString(itemId); objectName->choice.vmdspecific.size = strlen(itemId); - } + } - asn_enc_rval_t rval; + asn_enc_rval_t rval; - rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return rval.encoded; + return rval.encoded; } int mmsClient_createReadAssociationSpecificNamedVariableListRequest( - uint32_t invokeId, - const char* itemId, - ByteBuffer* writeBuffer, - bool specWithResult) + uint32_t invokeId, + const char* itemId, + ByteBuffer* writeBuffer, + bool specWithResult) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - ReadRequest_t* readRequest = createReadRequest(mmsPdu); + ReadRequest_t* readRequest = createReadRequest(mmsPdu); - if (specWithResult) { - readRequest->specificationWithResult = (BOOLEAN_t*) GLOBAL_CALLOC(1, sizeof(BOOLEAN_t)); - (*(readRequest->specificationWithResult)) = true; - } - else - readRequest->specificationWithResult = NULL; + if (specWithResult) { + readRequest->specificationWithResult = (BOOLEAN_t*) GLOBAL_CALLOC(1, sizeof(BOOLEAN_t)); + (*(readRequest->specificationWithResult)) = true; + } + else + readRequest->specificationWithResult = NULL; - readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_variableListName; + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_variableListName; - ObjectName_t* objectName = &(readRequest->variableAccessSpecification.choice.variableListName); + ObjectName_t* objectName = &(readRequest->variableAccessSpecification.choice.variableListName); - objectName->present = ObjectName_PR_aaspecific; + objectName->present = ObjectName_PR_aaspecific; - objectName->choice.aaspecific.buf = (uint8_t*) StringUtils_copyString(itemId); - objectName->choice.aaspecific.size = strlen(itemId); + objectName->choice.aaspecific.buf = (uint8_t*) StringUtils_copyString(itemId); + objectName->choice.aaspecific.size = strlen(itemId); - asn_enc_rval_t rval; + asn_enc_rval_t rval; - rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return rval.encoded; + return rval.encoded; } /** @@ -377,153 +374,270 @@ mmsClient_createReadAssociationSpecificNamedVariableListRequest( int mmsClient_createReadRequest(uint32_t invokeId, const char* domainId, const char* itemId, ByteBuffer* writeBuffer) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - ReadRequest_t* readRequest = createReadRequest(mmsPdu); + ReadRequest_t* readRequest = createReadRequest(mmsPdu); - readRequest->specificationWithResult = NULL; + readRequest->specificationWithResult = NULL; - readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; - readRequest->variableAccessSpecification.choice.listOfVariable.list.array = - (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); - readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1; + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = + (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1; - ListOfVariableSeq_t* listOfVars = (ListOfVariableSeq_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t)); + ListOfVariableSeq_t* listOfVars = (ListOfVariableSeq_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t)); - readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = listOfVars; + readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = listOfVars; - listOfVars->alternateAccess = NULL; - listOfVars->variableSpecification.present = VariableSpecification_PR_name; + listOfVars->alternateAccess = NULL; + listOfVars->variableSpecification.present = VariableSpecification_PR_name; - if (domainId != NULL) { + if (domainId != NULL) { listOfVars->variableSpecification.choice.name.present = ObjectName_PR_domainspecific; listOfVars->variableSpecification.choice.name.choice.domainspecific.domainId.buf = (uint8_t*) domainId; listOfVars->variableSpecification.choice.name.choice.domainspecific.domainId.size = strlen(domainId); listOfVars->variableSpecification.choice.name.choice.domainspecific.itemId.buf = (uint8_t*) itemId; listOfVars->variableSpecification.choice.name.choice.domainspecific.itemId.size = strlen(itemId); - } - else { - listOfVars->variableSpecification.choice.name.present = ObjectName_PR_vmdspecific; - listOfVars->variableSpecification.choice.name.choice.vmdspecific.buf = (uint8_t*) itemId; - listOfVars->variableSpecification.choice.name.choice.vmdspecific.size = strlen(itemId); - } - - asn_enc_rval_t rval; - - rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - - /* clean up data structures */ - GLOBAL_FREEMEM(listOfVars); - GLOBAL_FREEMEM(readRequest->variableAccessSpecification.choice.listOfVariable.list.array); - readRequest->variableAccessSpecification.choice.listOfVariable.list.array = NULL; - readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 0; - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - - return rval.encoded; + } + else { + listOfVars->variableSpecification.choice.name.present = ObjectName_PR_vmdspecific; + listOfVars->variableSpecification.choice.name.choice.vmdspecific.buf = (uint8_t*) itemId; + listOfVars->variableSpecification.choice.name.choice.vmdspecific.size = strlen(itemId); + } + + asn_enc_rval_t rval; + + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + + /* clean up data structures */ + GLOBAL_FREEMEM(listOfVars); + GLOBAL_FREEMEM(readRequest->variableAccessSpecification.choice.listOfVariable.list.array); + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = NULL; + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 0; + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + + return rval.encoded; } static AlternateAccess_t* createAlternateAccess(uint32_t index, uint32_t elementCount) { - AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); - alternateAccess->list.count = 1; - alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); - alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); - alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; + AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); + alternateAccess->list.count = 1; + alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); + alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); + alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; - alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); + alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); - alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; - if (elementCount > 0) { - alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = - AlternateAccessSelection__selectAccess_PR_indexRange; + if (elementCount > 0) { + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_indexRange; - INTEGER_t* asnIndex = - &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex); + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex); - asn_long2INTEGER(asnIndex, index); + asn_long2INTEGER(asnIndex, index); - asnIndex = - &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements); + asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements); - asn_long2INTEGER(asnIndex, elementCount); - } - else { - alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = - AlternateAccessSelection__selectAccess_PR_index; + asn_long2INTEGER(asnIndex, elementCount); + } + else { + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_index; - INTEGER_t* asnIndex = - &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); - asn_long2INTEGER(asnIndex, index); - } + asn_long2INTEGER(asnIndex, index); + } - return alternateAccess; + return alternateAccess; +} + +static AlternateAccess_t* +createAlternateAccessComponent(const char* componentName) +{ + AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); + alternateAccess->list.count = 1; + alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); + alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); + alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; + + alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); + + const char* separator = strchr(componentName, '$'); + + if (separator) { + int size = separator - componentName; + + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAlternateAccess; + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.buf = + (uint8_t*) StringUtils_copySubString((char*) componentName, (char*) separator); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.size = size; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(separator + 1); + } + else { + int size = strlen(componentName); + + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_component; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.buf = + (uint8_t*) StringUtils_copyString(componentName); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.size = size; + } + + return alternateAccess; +} + +static AlternateAccess_t* +createAlternateAccessIndexComponent(uint32_t index, const char* componentName) +{ + AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); + alternateAccess->list.count = 1; + alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); + alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); + alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; + + alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); + + if (componentName) { + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAlternateAccess; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index; + + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); + + asn_long2INTEGER(asnIndex, index); + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(componentName); + } + else { + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_index; + + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); + + asn_long2INTEGER(asnIndex, index); + } + + return alternateAccess; } static ListOfVariableSeq_t* createVariableIdentifier(const char* domainId, const char* itemId) { - ListOfVariableSeq_t* variableIdentifier = (ListOfVariableSeq_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t)); + ListOfVariableSeq_t* variableIdentifier = (ListOfVariableSeq_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t)); - variableIdentifier->variableSpecification.present = VariableSpecification_PR_name; - variableIdentifier->variableSpecification.choice.name.present = ObjectName_PR_domainspecific; - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.buf = (uint8_t*) domainId; - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.size = strlen(domainId); - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.buf = (uint8_t*) itemId; - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.size = strlen(itemId); + variableIdentifier->variableSpecification.present = VariableSpecification_PR_name; + variableIdentifier->variableSpecification.choice.name.present = ObjectName_PR_domainspecific; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.buf = (uint8_t*) domainId; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.size = strlen(domainId); + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.buf = (uint8_t*) itemId; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.size = strlen(itemId); - return variableIdentifier; + return variableIdentifier; } int mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, const char* domainId, const char* itemId, - uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer) + uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - ReadRequest_t* readRequest = createReadRequest(mmsPdu); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + ReadRequest_t* readRequest = createReadRequest(mmsPdu); - readRequest->specificationWithResult = NULL; + readRequest->specificationWithResult = NULL; - readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; - readRequest->variableAccessSpecification.choice.listOfVariable.list.array = (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); - readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1; + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1; - ListOfVariableSeq_t* variableIdentifier = createVariableIdentifier(domainId, itemId); + ListOfVariableSeq_t* variableIdentifier = createVariableIdentifier(domainId, itemId); - readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier; + readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier; - variableIdentifier->alternateAccess = createAlternateAccess(index, elementCount); + variableIdentifier->alternateAccess = createAlternateAccess(index, elementCount); - asn_enc_rval_t rval; + asn_enc_rval_t rval; - rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.buf = 0; - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.size = 0; - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.buf = 0; - variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.size = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.buf = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.size = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.buf = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.size = 0; - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return rval.encoded; + return rval.encoded; +} + +int +mmsClient_createReadRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, + uint32_t index, const char* component, ByteBuffer* writeBuffer) +{ + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + ReadRequest_t* readRequest = createReadRequest(mmsPdu); + + readRequest->specificationWithResult = NULL; + + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; + + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1; + + ListOfVariableSeq_t* variableIdentifier = createVariableIdentifier(domainId, itemId); + + readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier; + + variableIdentifier->alternateAccess = createAlternateAccessIndexComponent(index, component); + + asn_enc_rval_t rval; + + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.buf = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.size = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.buf = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.size = 0; + + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + + return rval.encoded; } static ListOfVariableSeq_t** -createListOfVariables(ReadRequest_t* readRequest, int valuesCount) { - readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; +createListOfVariables(ReadRequest_t* readRequest, int valuesCount) +{ + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; - readRequest->variableAccessSpecification.choice.listOfVariable.list.array = (ListOfVariableSeq_t**) - GLOBAL_CALLOC(valuesCount, sizeof(ListOfVariableSeq_t*)); - readRequest->variableAccessSpecification.choice.listOfVariable.list.count = valuesCount; - readRequest->variableAccessSpecification.choice.listOfVariable.list.size = valuesCount; + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = (ListOfVariableSeq_t**) + GLOBAL_CALLOC(valuesCount, sizeof(ListOfVariableSeq_t*)); + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = valuesCount; + readRequest->variableAccessSpecification.choice.listOfVariable.list.size = valuesCount; - return readRequest->variableAccessSpecification.choice.listOfVariable.list.array; + return readRequest->variableAccessSpecification.choice.listOfVariable.list.array; } /** @@ -531,44 +645,43 @@ createListOfVariables(ReadRequest_t* readRequest, int valuesCount) { */ int mmsClient_createReadRequestMultipleValues(uint32_t invokeId, const char* domainId, LinkedList items, - ByteBuffer* writeBuffer) + ByteBuffer* writeBuffer) { - MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); - ReadRequest_t* readRequest = createReadRequest(mmsPdu); + ReadRequest_t* readRequest = createReadRequest(mmsPdu); - readRequest->specificationWithResult = NULL; + readRequest->specificationWithResult = NULL; - int valuesCount = LinkedList_size(items); + int valuesCount = LinkedList_size(items); - ListOfVariableSeq_t** listOfVars = createListOfVariables(readRequest, valuesCount); + ListOfVariableSeq_t** listOfVars = createListOfVariables(readRequest, valuesCount); - LinkedList item = items; + LinkedList item = items; - int i = 0; - - while ((item = LinkedList_getNext(item)) != NULL) { - listOfVars[i] = createVariableIdentifier(domainId, (char*) (item->data)); - i++; - } + int i = 0; - asn_enc_rval_t rval; + while ((item = LinkedList_getNext(item)) != NULL) { + listOfVars[i] = createVariableIdentifier(domainId, (char*) (item->data)); + i++; + } - rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, - (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + asn_enc_rval_t rval; - for (i = 0; i < valuesCount; i++) { - GLOBAL_FREEMEM(listOfVars[i]); - } - GLOBAL_FREEMEM(listOfVars); + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); - readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 0; - readRequest->variableAccessSpecification.choice.listOfVariable.list.size = 0; - readRequest->variableAccessSpecification.choice.listOfVariable.list.array = NULL; + for (i = 0; i < valuesCount; i++) { + GLOBAL_FREEMEM(listOfVars[i]); + } + GLOBAL_FREEMEM(listOfVars); + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 0; + readRequest->variableAccessSpecification.choice.listOfVariable.list.size = 0; + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = NULL; - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return rval.encoded; + return rval.encoded; } diff --git a/src/mms/iso_mms/client/mms_client_status.c b/src/mms/iso_mms/client/mms_client_status.c index c693d2b49..808535874 100644 --- a/src/mms/iso_mms/client/mms_client_status.c +++ b/src/mms/iso_mms/client/mms_client_status.c @@ -82,6 +82,7 @@ mmsClient_parseStatusResponse(MmsConnection self, int* vmdLogicalStatus, int* vm while (bufPos < endPos) { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) goto exit_error; switch (tag) { case 0x80: /* vmdLogicalStatus */ diff --git a/src/mms/iso_mms/client/mms_client_write.c b/src/mms/iso_mms/client/mms_client_write.c index ed0c6665c..3a6ecbb4b 100644 --- a/src/mms/iso_mms/client/mms_client_write.c +++ b/src/mms/iso_mms/client/mms_client_write.c @@ -169,6 +169,12 @@ mmsClient_parseWriteResponse(ByteBuffer* message, int32_t bufPos, MmsError* mmsE if (tag == 0x80) { bufPos = BerDecoder_decodeLength(buf, &length, bufPos, size); + if (bufPos == -1) { + *mmsError = MMS_ERROR_PARSING_RESPONSE; + retVal = DATA_ACCESS_ERROR_UNKNOWN; + goto exit_function; + } + uint32_t dataAccessErrorCode = BerDecoder_decodeUint32(buf, length, bufPos); diff --git a/src/mms/iso_mms/common/mms_common_msg.c b/src/mms/iso_mms/common/mms_common_msg.c index 75443e726..47673a48b 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 - 2017 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -344,6 +344,8 @@ mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, i } } +#if (MMS_FILE_SERVICE == 1) + void mmsMsg_createExtendedFilename(const char* basepath, char* extendedFileName, char* fileName) { @@ -358,7 +360,6 @@ mmsMsg_createExtendedFilename(const char* basepath, char* extendedFileName, char } - FileHandle mmsMsg_openFile(const char* basepath, char* fileName, bool readWrite) { @@ -416,3 +417,6 @@ mmsMsg_parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos return true; } + +#endif /* (MMS_FILE_SERVICE == 1) */ + diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c index e0a27ee64..1d8485e2b 100644 --- a/src/mms/iso_mms/common/mms_value.c +++ b/src/mms/iso_mms/common/mms_value.c @@ -1,7 +1,7 @@ /* * mms_value.c * - * Copyright 2013-2016 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,26 +32,28 @@ #include "conversions.h" +#include "simple_allocator.h" + #include /* for ctime_r */ static inline int bitStringByteSize(const MmsValue* value) { - int bitSize = value->value.bitString.size; - return (bitSize / 8) + ((bitSize % 8) > 0); + int bitSize = value->value.bitString.size; + return (bitSize / 8) + ((bitSize % 8) > 0); } int MmsValue_getBitStringByteSize(const MmsValue* self) { - return bitStringByteSize(self); + return bitStringByteSize(self); } static bool updateStructuredComponent(MmsValue* self, const MmsValue* update) { - MmsValue** selfValues; - MmsValue** updateValues; + MmsValue** selfValues; + MmsValue** updateValues; if (self->value.structure.size != update->value.structure.size) return false; @@ -59,48 +61,49 @@ updateStructuredComponent(MmsValue* self, const MmsValue* update) selfValues = self->value.structure.components; updateValues = update->value.structure.components; - int i; - for (i = 0; i < self->value.structure.size; i++) { - if (MmsValue_update(selfValues[i], updateValues[i]) == false) - return false; - } + int i; + for (i = 0; i < self->value.structure.size; i++) { + if (MmsValue_update(selfValues[i], updateValues[i]) == false) + return false; + } - return true; + return true; } MmsValue* MmsValue_newIntegerFromBerInteger(Asn1PrimitiveValue* berInteger) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_INTEGER; - self->value.integer = berInteger; + self->type = MMS_INTEGER; + self->value.integer = berInteger; - return self; + return self; } MmsValue* MmsValue_newUnsignedFromBerInteger(Asn1PrimitiveValue* berInteger) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_UNSIGNED; - self->value.integer = berInteger; + self->type = MMS_UNSIGNED; + self->value.integer = berInteger; - return self; + return self; } bool MmsValue_equals(const MmsValue* self, const MmsValue* otherValue) { if (self->type == otherValue->type) { - switch (self->type) { + switch (self->type) + { case MMS_ARRAY: case MMS_STRUCTURE: if (self->value.structure.size == otherValue->value.structure.size) { @@ -127,7 +130,7 @@ MmsValue_equals(const MmsValue* self, const MmsValue* otherValue) return true; break; case MMS_INTEGER: - case MMS_UNSIGNED: + case MMS_UNSIGNED: return Asn1PrimitivaValue_compare(self->value.integer, otherValue->value.integer); break; case MMS_UTC_TIME: @@ -159,7 +162,7 @@ MmsValue_equals(const MmsValue* self, const MmsValue* otherValue) break; case MMS_VISIBLE_STRING: - case MMS_STRING: + case MMS_STRING: if (self->value.visibleString.buf != NULL) { if (otherValue->value.visibleString.buf != NULL) { if (strcmp(self->value.visibleString.buf, otherValue->value.visibleString.buf) == 0) @@ -172,8 +175,6 @@ MmsValue_equals(const MmsValue* self, const MmsValue* otherValue) } break; - - case MMS_DATA_ACCESS_ERROR: if (self->value.dataAccessError == otherValue->value.dataAccessError) return true; @@ -193,7 +194,8 @@ bool MmsValue_equalTypes(const MmsValue* self, const MmsValue* otherValue) { if (self->type == otherValue->type) { - switch (self->type) { + switch (self->type) + { case MMS_ARRAY: case MMS_STRUCTURE: if (self->value.structure.size == otherValue->value.structure.size) { @@ -223,67 +225,76 @@ MmsValue_equalTypes(const MmsValue* self, const MmsValue* otherValue) bool MmsValue_update(MmsValue* self, const MmsValue* update) { - if (self->type == update->type) { - switch (self->type) { - case MMS_STRUCTURE: - case MMS_ARRAY: - if (updateStructuredComponent(self, update) == false) - return false; - break; - case MMS_BOOLEAN: - self->value.boolean = update->value.boolean; - break; - case MMS_FLOAT: - if (self->value.floatingPoint.formatWidth == update->value.floatingPoint.formatWidth) { - self->value.floatingPoint.exponentWidth = update->value.floatingPoint.exponentWidth; - memcpy(self->value.floatingPoint.buf, update->value.floatingPoint.buf, - self->value.floatingPoint.formatWidth / 8); - } - else return false; - break; - case MMS_INTEGER: - case MMS_UNSIGNED: - if (BerInteger_setFromBerInteger(self->value.integer, update->value.integer)) - return true; - else - return false; - break; - case MMS_UTC_TIME: - memcpy(self->value.utcTime, update->value.utcTime, 8); - break; - case MMS_BIT_STRING: - if (self->value.bitString.size == update->value.bitString.size) - memcpy(self->value.bitString.buf, update->value.bitString.buf, bitStringByteSize(self)); - else return false; - break; - case MMS_OCTET_STRING: - if (self->value.octetString.maxSize == update->value.octetString.maxSize) { - memcpy(self->value.octetString.buf, update->value.octetString.buf, - update->value.octetString.size); - - self->value.octetString.size = update->value.octetString.size; - } - else return false; - break; - case MMS_VISIBLE_STRING: - MmsValue_setVisibleString(self, update->value.visibleString.buf); - break; - case MMS_STRING: - MmsValue_setMmsString(self, update->value.visibleString.buf); - break; - case MMS_BINARY_TIME: - self->value.binaryTime.size = update->value.binaryTime.size; - memcpy(self->value.binaryTime.buf, update->value.binaryTime.buf, - update->value.binaryTime.size); - break; - default: - return false; - break; - } - return true; - } - else - return false; + if (self->type == update->type) { + switch (self->type) + { + case MMS_STRUCTURE: + case MMS_ARRAY: + if (updateStructuredComponent(self, update) == false) + return false; + break; + case MMS_BOOLEAN: + self->value.boolean = update->value.boolean; + break; + case MMS_FLOAT: + if (self->value.floatingPoint.formatWidth == update->value.floatingPoint.formatWidth) { + self->value.floatingPoint.exponentWidth = update->value.floatingPoint.exponentWidth; + memcpy(self->value.floatingPoint.buf, update->value.floatingPoint.buf, + self->value.floatingPoint.formatWidth / 8); + } + else + return false; + break; + case MMS_INTEGER: + case MMS_UNSIGNED: + if (BerInteger_setFromBerInteger(self->value.integer, update->value.integer)) + return true; + else + return false; + break; + case MMS_UTC_TIME: + memcpy(self->value.utcTime, update->value.utcTime, 8); + break; + case MMS_BIT_STRING: + if (self->value.bitString.size == update->value.bitString.size) + memcpy(self->value.bitString.buf, update->value.bitString.buf, bitStringByteSize(self)); + else + return false; + break; + case MMS_OCTET_STRING: + { + int size = update->value.octetString.size; + + if (size > self->value.octetString.maxSize) { + GLOBAL_FREEMEM(self->value.octetString.buf); + self->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(size); + self->value.octetString.maxSize = size; + } + + memcpy(self->value.octetString.buf, update->value.octetString.buf, size); + + self->value.octetString.size = size; + } + break; + case MMS_VISIBLE_STRING: + MmsValue_setVisibleString(self, update->value.visibleString.buf); + break; + case MMS_STRING: + MmsValue_setMmsString(self, update->value.visibleString.buf); + break; + case MMS_BINARY_TIME: + self->value.binaryTime.size = update->value.binaryTime.size; + memcpy(self->value.binaryTime.buf, update->value.binaryTime.buf, + update->value.binaryTime.size); + break; + default: + return false; + break; + } + return true; + } + else + return false; } MmsValue* @@ -303,51 +314,52 @@ MmsValue_newDataAccessError(MmsDataAccessError accessError) MmsValue* MmsValue_newBitString(int bitSize) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));; + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + ; - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_BIT_STRING; - self->value.bitString.size = abs(bitSize); - self->value.bitString.buf = (uint8_t*) GLOBAL_CALLOC(bitStringByteSize(self), 1); + self->type = MMS_BIT_STRING; + self->value.bitString.size = abs(bitSize); + self->value.bitString.buf = (uint8_t*) GLOBAL_CALLOC(bitStringByteSize(self), 1); - return self; + return self; } static int getBitStringByteSize(const MmsValue* self) { - int byteSize; + int byteSize; - if (self->value.bitString.size % 8) - byteSize = (self->value.bitString.size / 8) + 1; - else - byteSize = self->value.bitString.size / 8; + if (self->value.bitString.size % 8) + byteSize = (self->value.bitString.size / 8) + 1; + else + byteSize = self->value.bitString.size / 8; - return byteSize; + return byteSize; } void MmsValue_deleteAllBitStringBits(MmsValue* self) { - int byteSize = getBitStringByteSize(self); + int byteSize = getBitStringByteSize(self); - int i; - for (i = 0; i < byteSize; i++) { - self->value.bitString.buf[i] = 0; - } + int i; + for (i = 0; i < byteSize; i++) { + self->value.bitString.buf[i] = 0; + } } void MmsValue_setAllBitStringBits(MmsValue* self) { - int byteSize = getBitStringByteSize(self); + int byteSize = getBitStringByteSize(self); - int i; - for (i = 0; i < byteSize; i++) { - self->value.bitString.buf[i] = 0xff; - } + int i; + for (i = 0; i < byteSize; i++) { + self->value.bitString.buf[i] = 0xff; + } int padding = (byteSize * 8) - self->value.bitString.size; @@ -359,13 +371,13 @@ MmsValue_setAllBitStringBits(MmsValue* self) paddingMask = ~paddingMask; - self->value.bitString.buf[byteSize - 1] = self->value.bitString.buf[byteSize - 1] & paddingMask; + self->value.bitString.buf[byteSize - 1] = self->value.bitString.buf[byteSize - 1] & paddingMask; } int MmsValue_getBitStringSize(const MmsValue* self) { - return self->value.bitString.size; + return self->value.bitString.size; } int @@ -403,42 +415,43 @@ MmsValue_getNumberOfSetBits(const MmsValue* self) } } - return setBitsCount; + return setBitsCount; } void MmsValue_setBitStringBit(MmsValue* self, int bitPos, bool value) { - if (bitPos < self->value.bitString.size) { - int bytePos = bitPos / 8; - int bitPosInByte = 7 - (bitPos % 8); + if (bitPos < self->value.bitString.size) { + int bytePos = bitPos / 8; + int bitPosInByte = 7 - (bitPos % 8); - int bitMask = (1 << bitPosInByte); + int bitMask = (1 << bitPosInByte); - if (value) - self->value.bitString.buf[bytePos] |= bitMask; - else - self->value.bitString.buf[bytePos] &= (~bitMask); - } + if (value) + self->value.bitString.buf[bytePos] |= bitMask; + else + self->value.bitString.buf[bytePos] &= (~bitMask); + } } bool MmsValue_getBitStringBit(const MmsValue* self, int bitPos) { - if (bitPos < self->value.bitString.size) { - int bytePos = bitPos / 8; + if (bitPos < self->value.bitString.size) { + int bytePos = bitPos / 8; - int bitPosInByte = 7 - (bitPos % 8); + int bitPosInByte = 7 - (bitPos % 8); - int bitMask = (1 << bitPosInByte); + int bitMask = (1 << bitPosInByte); - if ((self->value.bitString.buf[bytePos] & bitMask) > 0) - return true; - else - return false; + if ((self->value.bitString.buf[bytePos] & bitMask) > 0) + return true; + else + return false; - } - else return false; /* out of range bits are always zero */ + } + else + return false; /* out of range bits are always zero */ } uint32_t @@ -507,73 +520,74 @@ MmsValue_setBitStringFromIntegerBigEndian(MmsValue* self, uint32_t intValue) } } - MmsValue* MmsValue_newFloat(float variable) { - MmsValue* self = (MmsValue*) GLOBAL_MALLOC(sizeof(MmsValue));; + MmsValue* self = (MmsValue*) GLOBAL_MALLOC(sizeof(MmsValue)); + ; - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_FLOAT; - self->value.floatingPoint.formatWidth = 32; - self->value.floatingPoint.exponentWidth = 8; - self->value.floatingPoint.buf = (uint8_t*) GLOBAL_MALLOC(4); + self->type = MMS_FLOAT; + self->value.floatingPoint.formatWidth = 32; + self->value.floatingPoint.exponentWidth = 8; + self->value.floatingPoint.buf = (uint8_t*) GLOBAL_MALLOC(4); - *((float*) self->value.floatingPoint.buf) = variable; + *((float*) self->value.floatingPoint.buf) = variable; - return self; + return self; } void MmsValue_setFloat(MmsValue* value, float newFloatValue) { - if (value->type == MMS_FLOAT) { - if (value->value.floatingPoint.formatWidth == 32) { - *((float*) value->value.floatingPoint.buf) = newFloatValue; - } - else if (value->value.floatingPoint.formatWidth == 64) { - *((double*) value->value.floatingPoint.buf) = (double) newFloatValue; - } - } + if (value->type == MMS_FLOAT) { + if (value->value.floatingPoint.formatWidth == 32) { + *((float*) value->value.floatingPoint.buf) = newFloatValue; + } + else if (value->value.floatingPoint.formatWidth == 64) { + *((double*) value->value.floatingPoint.buf) = (double) newFloatValue; + } + } } void MmsValue_setDouble(MmsValue* value, double newFloatValue) { - if (value->type == MMS_FLOAT) { - if (value->value.floatingPoint.formatWidth == 32) { - *((float*) value->value.floatingPoint.buf) = (float) newFloatValue; - } - else if (value->value.floatingPoint.formatWidth == 64) { - *((double*) value->value.floatingPoint.buf) = newFloatValue; - } - } + if (value->type == MMS_FLOAT) { + if (value->value.floatingPoint.formatWidth == 32) { + *((float*) value->value.floatingPoint.buf) = (float) newFloatValue; + } + else if (value->value.floatingPoint.formatWidth == 64) { + *((double*) value->value.floatingPoint.buf) = newFloatValue; + } + } } MmsValue* MmsValue_newDouble(double variable) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_FLOAT; - self->value.floatingPoint.formatWidth = 64; - self->value.floatingPoint.exponentWidth = 11; - self->value.floatingPoint.buf = (uint8_t*) GLOBAL_MALLOC(8); + self->type = MMS_FLOAT; + self->value.floatingPoint.formatWidth = 64; + self->value.floatingPoint.exponentWidth = 11; + self->value.floatingPoint.buf = (uint8_t*) GLOBAL_MALLOC(8); - *((double*) self->value.floatingPoint.buf) = variable; + *((double*) self->value.floatingPoint.buf) = variable; - return self; + return self; } MmsValue* MmsValue_newIntegerFromInt8(int8_t integer) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));; + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + ; self->type = MMS_INTEGER; self->value.integer = BerInteger_createFromInt32((int32_t) integer); @@ -584,12 +598,13 @@ MmsValue_newIntegerFromInt8(int8_t integer) MmsValue* MmsValue_newIntegerFromInt16(int16_t integer) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));; + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + ; - self->type = MMS_INTEGER; - self->value.integer = BerInteger_createFromInt32((int32_t) integer); + self->type = MMS_INTEGER; + self->value.integer = BerInteger_createFromInt32((int32_t) integer); - return self; + return self; } void @@ -615,11 +630,11 @@ MmsValue_setInt16(MmsValue* self, int16_t integer) void MmsValue_setInt32(MmsValue* self, int32_t integer) { - if (self->type == MMS_INTEGER) { - if (Asn1PrimitiveValue_getMaxSize(self->value.integer) >= 4) { - BerInteger_setInt32(self->value.integer, integer); - } - } + if (self->type == MMS_INTEGER) { + if (Asn1PrimitiveValue_getMaxSize(self->value.integer) >= 4) { + BerInteger_setInt32(self->value.integer, integer); + } + } } void @@ -642,7 +657,6 @@ MmsValue_setUint32(MmsValue* self, uint32_t integer) } } - void MmsValue_setUint16(MmsValue* self, uint16_t integer) { @@ -653,7 +667,6 @@ MmsValue_setUint16(MmsValue* self, uint16_t integer) } } - void MmsValue_setUint8(MmsValue* self, uint8_t integer) { @@ -665,11 +678,10 @@ MmsValue_setUint8(MmsValue* self, uint8_t integer) } - void MmsValue_setBoolean(MmsValue* self, bool boolValue) { - self->value.boolean = boolValue; + self->value.boolean = boolValue; } bool @@ -678,49 +690,47 @@ MmsValue_getBoolean(const MmsValue* self) return self->value.boolean; } - MmsValue* MmsValue_setUtcTime(MmsValue* self, uint32_t timeval) { - uint8_t* timeArray = (uint8_t*) &timeval; - uint8_t* valueArray = self->value.utcTime; + uint8_t* timeArray = (uint8_t*) &timeval; + uint8_t* valueArray = self->value.utcTime; #if (ORDER_LITTLE_ENDIAN == 1) - memcpyReverseByteOrder(valueArray, timeArray, 4); + memcpyReverseByteOrder(valueArray, timeArray, 4); #else - memcpy(valueArray, timeArray, 4); + memcpy(valueArray, timeArray, 4); #endif - return self; + return self; } - MmsValue* MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) { - uint32_t timeval32 = (timeval / 1000LL); + uint32_t timeval32 = (timeval / 1000LL); uint8_t* timeArray = (uint8_t*) &timeval32; - uint8_t* valueArray = self->value.utcTime; + uint8_t* valueArray = self->value.utcTime; #if (ORDER_LITTLE_ENDIAN == 1) - memcpyReverseByteOrder(valueArray, timeArray, 4); + memcpyReverseByteOrder(valueArray, timeArray, 4); #else - memcpy(valueArray, timeArray, 4); + memcpy(valueArray, timeArray, 4); #endif - uint32_t remainder = (timeval % 1000LL); - uint32_t fractionOfSecond = (remainder) * 16777 + ((remainder * 216) / 1000); + uint32_t remainder = (timeval % 1000LL); + uint32_t fractionOfSecond = (remainder) * 16777 + ((remainder * 216) / 1000); - /* encode fraction of second */ - valueArray[4] = ((fractionOfSecond >> 16) & 0xff); - valueArray[5] = ((fractionOfSecond >> 8) & 0xff); - valueArray[6] = (fractionOfSecond & 0xff); + /* encode fraction of second */ + valueArray[4] = ((fractionOfSecond >> 16) & 0xff); + valueArray[5] = ((fractionOfSecond >> 8) & 0xff); + valueArray[6] = (fractionOfSecond & 0xff); - /* encode time quality */ - valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */ + /* encode time quality */ + valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */ - return self; + return self; } void @@ -805,47 +815,48 @@ MmsValue_getUtcTimeInMsWithUs(const MmsValue* self, uint32_t* usec) return msVal; } - MmsValue* MmsValue_newIntegerFromInt32(int32_t integer) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_INTEGER; - self->value.integer = BerInteger_createFromInt32(integer); + self->type = MMS_INTEGER; + self->value.integer = BerInteger_createFromInt32(integer); - return self; + return self; } MmsValue* MmsValue_newUnsignedFromUint32(uint32_t integer) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));; + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + ; - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_UNSIGNED; - self->value.integer = BerInteger_createFromUint32(integer); + self->type = MMS_UNSIGNED; + self->value.integer = BerInteger_createFromUint32(integer); - return self; + return self; } MmsValue* MmsValue_newIntegerFromInt64(int64_t integer) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));; + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + ; - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_INTEGER; - self->value.integer = BerInteger_createFromInt64(integer); + self->type = MMS_INTEGER; + self->value.integer = BerInteger_createFromInt64(integer); - return self; + return self; } /** @@ -854,23 +865,23 @@ MmsValue_newIntegerFromInt64(int64_t integer) int32_t MmsValue_toInt32(const MmsValue* self) { - int32_t integerValue = 0; + int32_t integerValue = 0; - if ((self->type == MMS_INTEGER) || (self->type == MMS_UNSIGNED)) - BerInteger_toInt32(self->value.integer, &integerValue); + if ((self->type == MMS_INTEGER) || (self->type == MMS_UNSIGNED)) + BerInteger_toInt32(self->value.integer, &integerValue); - return integerValue; + return integerValue; } uint32_t MmsValue_toUint32(const MmsValue* self) { - uint32_t integerValue = 0; + uint32_t integerValue = 0; - if ((self->type == MMS_INTEGER) || (self->type == MMS_UNSIGNED)) - BerInteger_toUint32(self->value.integer, &integerValue); + if ((self->type == MMS_INTEGER) || (self->type == MMS_UNSIGNED)) + BerInteger_toUint32(self->value.integer, &integerValue); - return integerValue; + return integerValue; } /** @@ -879,67 +890,65 @@ MmsValue_toUint32(const MmsValue* self) int64_t MmsValue_toInt64(const MmsValue* self) { - int64_t integerValue = 0; + int64_t integerValue = 0; - if ((self->type == MMS_INTEGER) || (self->type == MMS_UNSIGNED)) - BerInteger_toInt64(self->value.integer, &integerValue); + if ((self->type == MMS_INTEGER) || (self->type == MMS_UNSIGNED)) + BerInteger_toInt64(self->value.integer, &integerValue); - return integerValue; + return integerValue; } float MmsValue_toFloat(const MmsValue* self) { - if (self->type == MMS_FLOAT) { - if (self->value.floatingPoint.formatWidth == 32) { - float val; + if (self->type == MMS_FLOAT) { + if (self->value.floatingPoint.formatWidth == 32) { + float val; - val = *((float*) (self->value.floatingPoint.buf)); - return val; - } - else if (self->value.floatingPoint.formatWidth == 64) { - float val; - val = *((double*) (self->value.floatingPoint.buf)); - return val; - } - } - else - printf("MmsValue_toFloat: conversion error. Wrong type!\n"); + val = *((float*) (self->value.floatingPoint.buf)); + return val; + } + else if (self->value.floatingPoint.formatWidth == 64) { + float val; + val = *((double*) (self->value.floatingPoint.buf)); + return val; + } + } + else + printf("MmsValue_toFloat: conversion error. Wrong type!\n"); - return 0.f; + return 0.f; } double MmsValue_toDouble(const MmsValue* self) { - if (self->type == MMS_FLOAT) { - double val; - if (self->value.floatingPoint.formatWidth == 32) { - val = (double) *((float*) (self->value.floatingPoint.buf)); - return val; - } - if (self->value.floatingPoint.formatWidth == 64) { - val = *((double*) (self->value.floatingPoint.buf)); - return val; - } - } + if (self->type == MMS_FLOAT) { + double val; + if (self->value.floatingPoint.formatWidth == 32) { + val = (double) *((float*) (self->value.floatingPoint.buf)); + return val; + } + if (self->value.floatingPoint.formatWidth == 64) { + val = *((double*) (self->value.floatingPoint.buf)); + return val; + } + } - return 0.f; + return 0.f; } - - uint32_t MmsValue_toUnixTimestamp(const MmsValue* self) { - uint32_t timestamp; - uint8_t* timeArray = (uint8_t*) ×tamp; + uint32_t timestamp; + uint8_t* timeArray = (uint8_t*) ×tamp; #if (ORDER_LITTLE_ENDIAN == 1) - timeArray[0] = self->value.utcTime[3]; - timeArray[1] = self->value.utcTime[2]; - timeArray[2] = self->value.utcTime[1]; - timeArray[3] = self->value.utcTime[0]; + timeArray[0] = self->value.utcTime[3]; + timeArray[1] = self->value.utcTime[2]; + timeArray[2] = self->value.utcTime[1]; + timeArray[3] = self->value.utcTime[0]; #else timeArray[0] = self->value.utcTime[0]; timeArray[1] = self->value.utcTime[1]; @@ -947,19 +956,20 @@ MmsValue_toUnixTimestamp(const MmsValue* self) timeArray[3] = self->value.utcTime[3]; #endif - return timestamp; + return timestamp; } int MmsValue_getSizeInMemory(const MmsValue* self) { - int memorySize = sizeof(MmsValue); + int memorySize = MemoryAllocator_getAlignedSize(sizeof(MmsValue)); - switch(self->type) { + switch (self->type) + { case MMS_ARRAY: - case MMS_STRUCTURE: + case MMS_STRUCTURE: { - memorySize += (sizeof(MmsValue*) * self->value.structure.size); + memorySize += (MemoryAllocator_getAlignedSize(sizeof(MmsValue*)) * self->value.structure.size); int i; for (i = 0; i < self->value.structure.size; i++) @@ -968,27 +978,26 @@ MmsValue_getSizeInMemory(const MmsValue* self) break; case MMS_BIT_STRING: - memorySize += bitStringByteSize(self); + memorySize += MemoryAllocator_getAlignedSize(bitStringByteSize(self)); break; case MMS_INTEGER: - case MMS_UNSIGNED: - memorySize += sizeof(Asn1PrimitiveValue); - memorySize += self->value.integer->maxSize; + case MMS_UNSIGNED: + memorySize += MemoryAllocator_getAlignedSize(sizeof(Asn1PrimitiveValue)); + memorySize += MemoryAllocator_getAlignedSize(self->value.integer->maxSize); break; case MMS_FLOAT: - memorySize += (self->value.floatingPoint.formatWidth / 8); + memorySize += MemoryAllocator_getAlignedSize(self->value.floatingPoint.formatWidth / 8); break; case MMS_OCTET_STRING: - memorySize += self->value.octetString.maxSize; + memorySize += MemoryAllocator_getAlignedSize(self->value.octetString.maxSize); break; case MMS_STRING: - case MMS_VISIBLE_STRING: - memorySize += strlen(self->value.visibleString.buf); - memorySize += 1; /* add space for 0 character */ + case MMS_VISIBLE_STRING: + memorySize += MemoryAllocator_getAlignedSize(strlen(self->value.visibleString.buf) + 1); /* add space for 0 character */ break; default: @@ -1004,18 +1013,19 @@ MmsValue_cloneToBuffer(const MmsValue* self, uint8_t* destinationAddress) MmsValue* newValue = (MmsValue*) destinationAddress; memcpy(destinationAddress, self, sizeof(MmsValue)); - destinationAddress += sizeof(MmsValue); + destinationAddress += MemoryAllocator_getAlignedSize(sizeof(MmsValue)); - switch (self->type) { + switch (self->type) + { case MMS_ARRAY: - case MMS_STRUCTURE: + case MMS_STRUCTURE: { newValue->value.structure.components = (MmsValue**) destinationAddress; destinationAddress += (sizeof(MmsValue*) * self->value.structure.size); int i; for (i = 0; i < self->value.structure.size; i++) { - memcpy(&(newValue->value.structure.components[i]), &(destinationAddress), sizeof (MmsValue*)); + memcpy(&(newValue->value.structure.components[i]), &(destinationAddress), sizeof(MmsValue*)); destinationAddress = MmsValue_cloneToBuffer(self->value.structure.components[i], destinationAddress); } } @@ -1024,19 +1034,19 @@ MmsValue_cloneToBuffer(const MmsValue* self, uint8_t* destinationAddress) case MMS_BIT_STRING: memcpy(destinationAddress, self->value.bitString.buf, bitStringByteSize(self)); newValue->value.bitString.buf = destinationAddress; - destinationAddress += bitStringByteSize(self); + destinationAddress += MemoryAllocator_getAlignedSize(bitStringByteSize(self)); break; case MMS_INTEGER: - case MMS_UNSIGNED: + case MMS_UNSIGNED: { newValue->value.integer = (Asn1PrimitiveValue*) destinationAddress; Asn1PrimitiveValue* newAsn1Value = (Asn1PrimitiveValue*) destinationAddress; memcpy(destinationAddress, self->value.integer, sizeof(Asn1PrimitiveValue)); - destinationAddress += sizeof(Asn1PrimitiveValue); + destinationAddress += MemoryAllocator_getAlignedSize(sizeof(Asn1PrimitiveValue)); newAsn1Value->octets = destinationAddress; memcpy(destinationAddress, self->value.integer->octets, self->value.integer->maxSize); - destinationAddress += self->value.integer->maxSize; + destinationAddress += MemoryAllocator_getAlignedSize(self->value.integer->maxSize); } break; @@ -1046,22 +1056,22 @@ MmsValue_cloneToBuffer(const MmsValue* self, uint8_t* destinationAddress) newValue->value.floatingPoint.buf = destinationAddress; memcpy(destinationAddress, self->value.floatingPoint.buf, floatSizeInBytes); - destinationAddress += floatSizeInBytes; + destinationAddress += MemoryAllocator_getAlignedSize(floatSizeInBytes); } break; case MMS_OCTET_STRING: newValue->value.octetString.buf = destinationAddress; memcpy(destinationAddress, self->value.octetString.buf, self->value.octetString.maxSize); - destinationAddress += self->value.octetString.maxSize; + destinationAddress += MemoryAllocator_getAlignedSize(self->value.octetString.maxSize); break; case MMS_STRING: - case MMS_VISIBLE_STRING: + case MMS_VISIBLE_STRING: newValue->value.visibleString.buf = (char*) destinationAddress; newValue->value.visibleString.size = self->value.visibleString.size; strcpy((char*) destinationAddress, self->value.visibleString.buf); - destinationAddress += (strlen(self->value.visibleString.buf) + 1); + destinationAddress += MemoryAllocator_getAlignedSize(strlen(self->value.visibleString.buf) + 1); break; default: @@ -1075,96 +1085,97 @@ MmsValue_cloneToBuffer(const MmsValue* self, uint8_t* destinationAddress) MmsValue* MmsValue_clone(const MmsValue* self) { - MmsValue* newValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - - if (newValue == NULL) - goto exit_function; - - newValue->deleteValue = self->deleteValue; - newValue->type = self->type; - int size; - - switch(self->type) { - - case MMS_ARRAY: - case MMS_STRUCTURE: - { - int componentCount = self->value.structure.size; - newValue->value.structure.size = componentCount; - newValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(componentCount, sizeof(MmsValue*)); - int i; - for (i = 0; i < componentCount; i++) { - newValue->value.structure.components[i] = - MmsValue_clone(self->value.structure.components[i]); - } - } - break; - - case MMS_INTEGER: - case MMS_UNSIGNED: - newValue->value.integer = Asn1PrimitiveValue_clone(self->value.integer); - break; - - case MMS_FLOAT: - newValue->value.floatingPoint.formatWidth = self->value.floatingPoint.formatWidth; - newValue->value.floatingPoint.exponentWidth = self->value.floatingPoint.exponentWidth; - size = self->value.floatingPoint.formatWidth / 8; - newValue->value.floatingPoint.buf = (uint8_t*) GLOBAL_MALLOC(size); - memcpy(newValue->value.floatingPoint.buf, self->value.floatingPoint.buf, size); - break; - - case MMS_BIT_STRING: - newValue->value.bitString.size = self->value.bitString.size; - size = bitStringByteSize(self); - newValue->value.bitString.buf = (uint8_t*) GLOBAL_MALLOC(size); - memcpy(newValue->value.bitString.buf, self->value.bitString.buf, size); - break; - - case MMS_BOOLEAN: - newValue->value.boolean = self->value.boolean; - break; - - case MMS_OCTET_STRING: - size = self->value.octetString.size; - newValue->value.octetString.size = size; - newValue->value.octetString.maxSize = self->value.octetString.maxSize; - newValue->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(self->value.octetString.maxSize); - memcpy(newValue->value.octetString.buf, self->value.octetString.buf, size); - break; - - case MMS_UTC_TIME: - memcpy(newValue->value.utcTime, self->value.utcTime, 8); - break; - - case MMS_BINARY_TIME: - newValue->value.binaryTime.size = self->value.binaryTime.size; - memcpy(newValue->value.binaryTime.buf, self->value.binaryTime.buf, 6); - break; + MmsValue* newValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + + if (newValue == NULL) + goto exit_function; + + newValue->deleteValue = self->deleteValue; + newValue->type = self->type; + int size; + + switch (self->type) + { + + case MMS_ARRAY: + case MMS_STRUCTURE: + { + int componentCount = self->value.structure.size; + newValue->value.structure.size = componentCount; + newValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(componentCount, sizeof(MmsValue*)); + int i; + for (i = 0; i < componentCount; i++) { + newValue->value.structure.components[i] = + MmsValue_clone(self->value.structure.components[i]); + } + } + break; + + case MMS_INTEGER: + case MMS_UNSIGNED: + newValue->value.integer = Asn1PrimitiveValue_clone(self->value.integer); + break; + + case MMS_FLOAT: + newValue->value.floatingPoint.formatWidth = self->value.floatingPoint.formatWidth; + newValue->value.floatingPoint.exponentWidth = self->value.floatingPoint.exponentWidth; + size = self->value.floatingPoint.formatWidth / 8; + newValue->value.floatingPoint.buf = (uint8_t*) GLOBAL_MALLOC(size); + memcpy(newValue->value.floatingPoint.buf, self->value.floatingPoint.buf, size); + break; + + case MMS_BIT_STRING: + newValue->value.bitString.size = self->value.bitString.size; + size = bitStringByteSize(self); + newValue->value.bitString.buf = (uint8_t*) GLOBAL_MALLOC(size); + memcpy(newValue->value.bitString.buf, self->value.bitString.buf, size); + break; + + case MMS_BOOLEAN: + newValue->value.boolean = self->value.boolean; + break; + + case MMS_OCTET_STRING: + size = self->value.octetString.size; + newValue->value.octetString.size = size; + newValue->value.octetString.maxSize = self->value.octetString.maxSize; + newValue->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(self->value.octetString.maxSize); + memcpy(newValue->value.octetString.buf, self->value.octetString.buf, size); + break; + + case MMS_UTC_TIME: + memcpy(newValue->value.utcTime, self->value.utcTime, 8); + break; + + case MMS_BINARY_TIME: + newValue->value.binaryTime.size = self->value.binaryTime.size; + memcpy(newValue->value.binaryTime.buf, self->value.binaryTime.buf, 6); + break; case MMS_VISIBLE_STRING: - case MMS_STRING: - size = self->value.visibleString.size; + case MMS_STRING: + size = self->value.visibleString.size; newValue->value.visibleString.buf = (char*) GLOBAL_MALLOC(size + 1); newValue->value.visibleString.size = size; strcpy(newValue->value.visibleString.buf, self->value.visibleString.buf); - break; + break; - case MMS_DATA_ACCESS_ERROR: - newValue->value.dataAccessError = self->value.dataAccessError; - break; + case MMS_DATA_ACCESS_ERROR: + newValue->value.dataAccessError = self->value.dataAccessError; + break; - default: - break; - } + default: + break; + } -exit_function: - return newValue; + exit_function: + return newValue; } uint32_t MmsValue_getArraySize(const MmsValue* self) { - return self->value.structure.size; + return self->value.structure.size; } void @@ -1177,9 +1188,10 @@ MmsValue_deleteIfNotNull(MmsValue* self) void MmsValue_delete(MmsValue* self) { - switch (self->type) { + switch (self->type) + { case MMS_INTEGER: - case MMS_UNSIGNED: + case MMS_UNSIGNED: Asn1PrimitiveValue_destroy(self->value.integer); break; case MMS_FLOAT: @@ -1192,19 +1204,19 @@ MmsValue_delete(MmsValue* self) GLOBAL_FREEMEM(self->value.octetString.buf); break; case MMS_VISIBLE_STRING: - case MMS_STRING: + case MMS_STRING: if (self->value.visibleString.buf != NULL) GLOBAL_FREEMEM(self->value.visibleString.buf); break; case MMS_ARRAY: - case MMS_STRUCTURE: + case MMS_STRUCTURE: { int componentCount = self->value.structure.size; int i; for (i = 0; i < componentCount; i++) { - if (self->value.structure.components[i] != NULL) - MmsValue_delete(self->value.structure.components[i]); + if (self->value.structure.components[i] != NULL) + MmsValue_delete(self->value.structure.components[i]); } } GLOBAL_FREEMEM(self->value.structure.components); @@ -1213,18 +1225,19 @@ MmsValue_delete(MmsValue* self) break; } - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); } /* delete only when deleteValue field set */ void MmsValue_deleteConditional(MmsValue* self) { - if (self->deleteValue == 1) { + if (self->deleteValue == 1) { - switch (self->type) { + switch (self->type) + { case MMS_INTEGER: - case MMS_UNSIGNED: + case MMS_UNSIGNED: Asn1PrimitiveValue_destroy(self->value.integer); break; case MMS_FLOAT: @@ -1237,12 +1250,12 @@ MmsValue_deleteConditional(MmsValue* self) GLOBAL_FREEMEM(self->value.octetString.buf); break; case MMS_VISIBLE_STRING: - case MMS_STRING: + case MMS_STRING: if (self->value.visibleString.buf != NULL) GLOBAL_FREEMEM(self->value.visibleString.buf); break; case MMS_ARRAY: - case MMS_STRUCTURE: + case MMS_STRUCTURE: { int componentCount = self->value.structure.size; int i; @@ -1265,71 +1278,71 @@ MmsValue_deleteConditional(MmsValue* self) MmsValue* MmsValue_newInteger(int size /*integer size in bits*/) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; self->type = MMS_INTEGER; - if (size <= 32) - self->value.integer = BerInteger_createInt32(); - else - self->value.integer = BerInteger_createInt64(); + if (size <= 32) + self->value.integer = BerInteger_createInt32(); + else + self->value.integer = BerInteger_createInt64(); - return self; + return self; } MmsValue* MmsValue_newUnsigned(int size /*integer size in bits*/) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_UNSIGNED; + self->type = MMS_UNSIGNED; - if (size <= 32) - self->value.integer = BerInteger_createInt32(); - else - self->value.integer = BerInteger_createInt64(); + if (size <= 32) + self->value.integer = BerInteger_createInt32(); + else + self->value.integer = BerInteger_createInt64(); - return self; + return self; } MmsValue* MmsValue_newBoolean(bool boolean) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_BOOLEAN; + self->type = MMS_BOOLEAN; - if (boolean == true) - self->value.boolean = 1; - else - self->value.boolean = 0; + if (boolean == true) + self->value.boolean = 1; + else + self->value.boolean = 0; - return self; + return self; } MmsValue* MmsValue_newOctetString(int size, int maxSize) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_OCTET_STRING; - self->value.octetString.size = size; - self->value.octetString.maxSize = maxSize; - self->value.octetString.buf = (uint8_t*) GLOBAL_CALLOC(1, maxSize); + self->type = MMS_OCTET_STRING; + self->value.octetString.size = size; + self->value.octetString.maxSize = maxSize; + self->value.octetString.buf = (uint8_t*) GLOBAL_CALLOC(1, maxSize); - return self; + return self; } void @@ -1362,157 +1375,158 @@ MmsValue_getOctetStringBuffer(MmsValue* self) MmsValue* MmsValue_newStructure(const MmsVariableSpecification* typeSpec) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_STRUCTURE; - int componentCount = typeSpec->typeSpec.structure.elementCount; - self->value.structure.size = componentCount; - self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(componentCount, sizeof(MmsValue*)); + self->type = MMS_STRUCTURE; + int componentCount = typeSpec->typeSpec.structure.elementCount; + self->value.structure.size = componentCount; + self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(componentCount, sizeof(MmsValue*)); - int i; - for (i = 0; i < componentCount; i++) { - self->value.structure.components[i] = - MmsValue_newDefaultValue(typeSpec->typeSpec.structure.elements[i]); - } + int i; + for (i = 0; i < componentCount; i++) { + self->value.structure.components[i] = + MmsValue_newDefaultValue(typeSpec->typeSpec.structure.elements[i]); + } - return self; + return self; } MmsValue* MmsValue_newDefaultValue(const MmsVariableSpecification* typeSpec) { - MmsValue* self = NULL; + MmsValue* self = NULL; - switch (typeSpec->type) { - case MMS_INTEGER: - self = MmsValue_newInteger(typeSpec->typeSpec.integer); - break; + switch (typeSpec->type) + { + case MMS_INTEGER: + self = MmsValue_newInteger(typeSpec->typeSpec.integer); + break; - case MMS_UNSIGNED: - self = MmsValue_newUnsigned(typeSpec->typeSpec.unsignedInteger); - break; + case MMS_UNSIGNED: + self = MmsValue_newUnsigned(typeSpec->typeSpec.unsignedInteger); + break; - case MMS_FLOAT: - self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + case MMS_FLOAT: + self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - goto exit_function; + if (self == NULL) + goto exit_function; - self->type = MMS_FLOAT; - self->value.floatingPoint.exponentWidth = typeSpec->typeSpec.floatingpoint.exponentWidth; - self->value.floatingPoint.formatWidth = typeSpec->typeSpec.floatingpoint.formatWidth; - self->value.floatingPoint.buf = (uint8_t*) GLOBAL_CALLOC(1, typeSpec->typeSpec.floatingpoint.formatWidth / 8); - break; + self->type = MMS_FLOAT; + self->value.floatingPoint.exponentWidth = typeSpec->typeSpec.floatingpoint.exponentWidth; + self->value.floatingPoint.formatWidth = typeSpec->typeSpec.floatingpoint.formatWidth; + self->value.floatingPoint.buf = (uint8_t*) GLOBAL_CALLOC(1, typeSpec->typeSpec.floatingpoint.formatWidth / 8); + break; - case MMS_BIT_STRING: - self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + case MMS_BIT_STRING: + self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - goto exit_function; + if (self == NULL) + goto exit_function; - self->type = MMS_BIT_STRING; - { - int bitSize = abs(typeSpec->typeSpec.bitString); - self->value.bitString.size = bitSize; - int size = (bitSize / 8) + ((bitSize % 8) > 0); - self->value.bitString.buf = (uint8_t*) GLOBAL_CALLOC(1, size); - } - break; + self->type = MMS_BIT_STRING; + { + int bitSize = abs(typeSpec->typeSpec.bitString); + self->value.bitString.size = bitSize; + int size = (bitSize / 8) + ((bitSize % 8) > 0); + self->value.bitString.buf = (uint8_t*) GLOBAL_CALLOC(1, size); + } + break; - case MMS_OCTET_STRING: - self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + case MMS_OCTET_STRING: + self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - goto exit_function; + if (self == NULL) + goto exit_function; - self->type = MMS_OCTET_STRING; + self->type = MMS_OCTET_STRING; - if (typeSpec->typeSpec.octetString < 0) - self->value.octetString.size = 0; - else - self->value.octetString.size = typeSpec->typeSpec.octetString; + if (typeSpec->typeSpec.octetString < 0) + self->value.octetString.size = 0; + else + self->value.octetString.size = typeSpec->typeSpec.octetString; - self->value.octetString.maxSize = abs(typeSpec->typeSpec.octetString); - self->value.octetString.buf = (uint8_t*) GLOBAL_CALLOC(1, abs(typeSpec->typeSpec.octetString)); - break; + self->value.octetString.maxSize = abs(typeSpec->typeSpec.octetString); + self->value.octetString.buf = (uint8_t*) GLOBAL_CALLOC(1, abs(typeSpec->typeSpec.octetString)); + break; - case MMS_VISIBLE_STRING: - self = MmsValue_newVisibleStringWithSize(abs(typeSpec->typeSpec.visibleString)); - break; + case MMS_VISIBLE_STRING: + self = MmsValue_newVisibleStringWithSize(abs(typeSpec->typeSpec.visibleString)); + break; - case MMS_BOOLEAN: - self = MmsValue_newBoolean(false); - break; + case MMS_BOOLEAN: + self = MmsValue_newBoolean(false); + break; - case MMS_UTC_TIME: - self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + case MMS_UTC_TIME: + self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - goto exit_function; + if (self == NULL) + goto exit_function; - self->type = MMS_UTC_TIME; - break; + self->type = MMS_UTC_TIME; + break; - case MMS_ARRAY: - self = MmsValue_createArray(typeSpec->typeSpec.array.elementTypeSpec, - typeSpec->typeSpec.array.elementCount); - break; + case MMS_ARRAY: + self = MmsValue_createArray(typeSpec->typeSpec.array.elementTypeSpec, + typeSpec->typeSpec.array.elementCount); + break; - case MMS_STRUCTURE: - self = MmsValue_newStructure(typeSpec); - break; + case MMS_STRUCTURE: + self = MmsValue_newStructure(typeSpec); + break; - case MMS_STRING: - self = MmsValue_newMmsStringWithSize(abs(typeSpec->typeSpec.visibleString)); - break; + case MMS_STRING: + self = MmsValue_newMmsStringWithSize(abs(typeSpec->typeSpec.visibleString)); + break; - case MMS_BINARY_TIME: - if (typeSpec->typeSpec.binaryTime == 4) - self = MmsValue_newBinaryTime(true); - else - self = MmsValue_newBinaryTime(false); - break; + case MMS_BINARY_TIME: + if (typeSpec->typeSpec.binaryTime == 4) + self = MmsValue_newBinaryTime(true); + else + self = MmsValue_newBinaryTime(false); + break; - default: - break; - } + default: + break; + } - if (self != NULL) - self->deleteValue = 0; + if (self != NULL) + self->deleteValue = 0; -exit_function: - return self; + exit_function: + return self; } static inline void setVisibleStringValue(MmsValue* self, const char* string) { - if (self->value.visibleString.buf != NULL) { - if (string != NULL) { + if (self->value.visibleString.buf != NULL) { + if (string != NULL) { - int newStringSize = strlen(string); + int newStringSize = strlen(string); - if (newStringSize > self->value.visibleString.size) { - GLOBAL_FREEMEM(self->value.visibleString.buf); - self->value.visibleString.buf = (char*) GLOBAL_MALLOC(newStringSize + 1); + if (newStringSize > self->value.visibleString.size) { + GLOBAL_FREEMEM(self->value.visibleString.buf); + self->value.visibleString.buf = (char*) GLOBAL_MALLOC(newStringSize + 1); - if (self->value.visibleString.buf == NULL) - goto exit_function; + if (self->value.visibleString.buf == NULL) + goto exit_function; - self->value.visibleString.size = newStringSize; - } + self->value.visibleString.size = newStringSize; + } - strncpy(self->value.visibleString.buf, string, self->value.visibleString.size + 1); - self->value.visibleString.buf[self->value.visibleString.size] = 0; - } - else - self->value.visibleString.buf[0] = 0; - } + strncpy(self->value.visibleString.buf, string, self->value.visibleString.size + 1); + self->value.visibleString.buf[self->value.visibleString.size] = 0; + } + else + self->value.visibleString.buf[0] = 0; + } -exit_function: + exit_function: return; } @@ -1545,14 +1559,14 @@ MmsValue_newString(const char* string, MmsType type) setVisibleStringValue(self, string); } -exit_function: + exit_function: return self; } MmsValue* MmsValue_newVisibleString(const char* string) { - return MmsValue_newString(string, MMS_VISIBLE_STRING); + return MmsValue_newString(string, MMS_VISIBLE_STRING); } static MmsValue* @@ -1576,7 +1590,7 @@ MmsValue_newStringWithSize(int size, MmsType type) self->value.visibleString.buf[0] = 0; -exit_function: + exit_function: return self; } @@ -1598,23 +1612,22 @@ MmsValue_newMmsStringWithSize(int size) return MmsValue_newStringWithSize(size, MMS_STRING); } - MmsValue* MmsValue_newBinaryTime(bool timeOfDay) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - return NULL; + if (self == NULL) + return NULL; - self->type = MMS_BINARY_TIME; + self->type = MMS_BINARY_TIME; - if (timeOfDay == true) - self->value.binaryTime.size = 4; - else - self->value.binaryTime.size = 6; + if (timeOfDay == true) + self->value.binaryTime.size = 4; + else + self->value.binaryTime.size = 6; - return self; + return self; } void @@ -1677,7 +1690,6 @@ MmsValue_getBinaryTimeAsUtcMs(const MmsValue* self) mmsTime = daysDiff * (86400000LL); - timestamp = mmsTime + (441763200000LL); } @@ -1702,11 +1714,11 @@ MmsValue_getDataAccessError(const MmsValue* self) void MmsValue_setMmsString(MmsValue* self, const char* string) { - if (self->type == MMS_STRING) { - assert(self->value.visibleString.buf != NULL); + if (self->type == MMS_STRING) { + assert(self->value.visibleString.buf != NULL); setVisibleStringValue(self, string); - } + } } static MmsValue* @@ -1728,7 +1740,7 @@ MmsValue_newStringFromByteArray(const uint8_t* byteArray, int size, MmsType type self = NULL; } -exit_function: + exit_function: return self; } @@ -1747,61 +1759,60 @@ MmsValue_newMmsStringFromByteArray(uint8_t* byteArray, int size) void MmsValue_setVisibleString(MmsValue* self, const char* string) { - if (self->type == MMS_VISIBLE_STRING) { - assert(self->value.visibleString.buf != NULL); + if (self->type == MMS_VISIBLE_STRING) { + assert(self->value.visibleString.buf != NULL); - setVisibleStringValue(self, string); - } + setVisibleStringValue(self, string); + } } const char* MmsValue_toString(MmsValue* self) { - if ((self->type == MMS_VISIBLE_STRING) || (self->type == MMS_STRING)) - return self->value.visibleString.buf; + if ((self->type == MMS_VISIBLE_STRING) || (self->type == MMS_STRING)) + return self->value.visibleString.buf; - return NULL; + return NULL; } int MmsValue_getStringSize(MmsValue* self) { - if ((self->type == MMS_VISIBLE_STRING) || (self->type == MMS_STRING)) - return self->value.visibleString.size; - else - return 0; + if ((self->type == MMS_VISIBLE_STRING) || (self->type == MMS_STRING)) + return self->value.visibleString.size; + else + return 0; } MmsValue* MmsValue_newUtcTime(uint32_t timeval) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - goto exit_function; + if (self == NULL) + goto exit_function; - self->type = MMS_UTC_TIME; + self->type = MMS_UTC_TIME; - uint8_t* timeArray = (uint8_t*) &timeval; - uint8_t* valueArray = self->value.utcTime; + uint8_t* timeArray = (uint8_t*) &timeval; + uint8_t* valueArray = self->value.utcTime; #if (ORDER_LITTLE_ENDIAN == 1) - valueArray[0] = timeArray[3]; - valueArray[1] = timeArray[2]; - valueArray[2] = timeArray[1]; - valueArray[3] = timeArray[0]; + valueArray[0] = timeArray[3]; + valueArray[1] = timeArray[2]; + valueArray[2] = timeArray[1]; + valueArray[3] = timeArray[0]; #else - valueArray[0] = timeArray[0]; - valueArray[1] = timeArray[1]; - valueArray[2] = timeArray[2]; - valueArray[3] = timeArray[3]; + valueArray[0] = timeArray[0]; + valueArray[1] = timeArray[1]; + valueArray[2] = timeArray[2]; + valueArray[3] = timeArray[3]; #endif -exit_function: - return self; + exit_function: + return self; } - MmsValue* MmsValue_newUtcTimeByMsTime(uint64_t timeval) { @@ -1819,61 +1830,61 @@ MmsValue_newUtcTimeByMsTime(uint64_t timeval) MmsValue* MmsValue_createArray(MmsVariableSpecification* elementType, int size) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - goto exit_function; + if (self == NULL) + goto exit_function; - self->type = MMS_ARRAY; - self->value.structure.size = size; - self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(size, sizeof(MmsValue*)); + self->type = MMS_ARRAY; + self->value.structure.size = size; + self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(size, sizeof(MmsValue*)); - if (self->value.structure.components == NULL) { - GLOBAL_FREEMEM(self); - self = NULL; - goto exit_function; - } + if (self->value.structure.components == NULL) { + GLOBAL_FREEMEM(self); + self = NULL; + goto exit_function; + } - int i; - for (i = 0; i < size; i++) { - self->value.structure.components[i] = MmsValue_newDefaultValue(elementType); + int i; + for (i = 0; i < size; i++) { + self->value.structure.components[i] = MmsValue_newDefaultValue(elementType); - if (self->value.structure.components[i] == NULL) { - MmsValue_delete(self); - self = NULL; - goto exit_function; - } - } + if (self->value.structure.components[i] == NULL) { + MmsValue_delete(self); + self = NULL; + goto exit_function; + } + } -exit_function: - return self; + exit_function: + return self; } MmsValue* MmsValue_createEmptyArray(int size) { - MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self == NULL) - goto exit_function; + if (self == NULL) + goto exit_function; - self->type = MMS_ARRAY; - self->value.structure.size = size; - self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(size, sizeof(MmsValue*)); + self->type = MMS_ARRAY; + self->value.structure.size = size; + self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(size, sizeof(MmsValue*)); - if (self->value.structure.components == NULL) { + if (self->value.structure.components == NULL) { GLOBAL_FREEMEM(self); self = NULL; goto exit_function; } - int i; - for (i = 0; i < size; i++) { - self->value.structure.components[i] = NULL; - } + int i; + for (i = 0; i < size; i++) { + self->value.structure.components[i] = NULL; + } -exit_function: - return self; + exit_function: + return self; } MmsValue* @@ -1895,28 +1906,28 @@ MmsValue_setElement(MmsValue* complexValue, int index, MmsValue* elementValue) if ((complexValue->type != MMS_ARRAY) && (complexValue->type != MMS_STRUCTURE)) return; - if ((index < 0) || (index >= complexValue->value.structure.size)) - return; + if ((index < 0) || (index >= complexValue->value.structure.size)) + return; - complexValue->value.structure.components[index] = elementValue; + complexValue->value.structure.components[index] = elementValue; } MmsValue* MmsValue_getElement(const MmsValue* complexValue, int index) { - if ((complexValue->type != MMS_ARRAY) && (complexValue->type != MMS_STRUCTURE)) - return NULL; + if ((complexValue->type != MMS_ARRAY) && (complexValue->type != MMS_STRUCTURE)) + return NULL; - if ((index < 0) || (index >= complexValue->value.structure.size)) - return NULL; + if ((index < 0) || (index >= complexValue->value.structure.size)) + return NULL; - return complexValue->value.structure.components[index]; + return complexValue->value.structure.components[index]; } void MmsValue_setDeletable(MmsValue* self) { - self->deleteValue = 1; + self->deleteValue = 1; } void @@ -1939,13 +1950,13 @@ MmsValue_setDeletableRecursive(MmsValue* self) int MmsValue_isDeletable(MmsValue* self) { - return self->deleteValue; + return self->deleteValue; } MmsType MmsValue_getType(const MmsValue* self) { - return self->type; + return self->type; } MmsValue* @@ -1957,7 +1968,8 @@ MmsValue_getSubElement(MmsValue* self, MmsVariableSpecification* varSpec, char* char* MmsValue_getTypeString(MmsValue* self) { - switch (MmsValue_getType(self)) { + switch (MmsValue_getType(self)) + { case MMS_ARRAY: return "array"; case MMS_BCD: @@ -1998,11 +2010,13 @@ MmsValue_getTypeString(MmsValue* self) const char* MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) { - switch (MmsValue_getType(self)) { + switch (MmsValue_getType(self)) + { case MMS_STRUCTURE: - case MMS_ARRAY: + case MMS_ARRAY: { - if (bufferSize==0) break; + if (bufferSize == 0) + break; buffer[0] = '{'; int bufPos = 1; diff --git a/src/mms/iso_mms/server/mms_access_result.c b/src/mms/iso_mms/server/mms_access_result.c index 0dafbf7d1..5ee4936c0 100644 --- a/src/mms/iso_mms/server/mms_access_result.c +++ b/src/mms/iso_mms/server/mms_access_result.c @@ -107,7 +107,7 @@ getNumberOfElements(uint8_t* buffer, int bufPos, int elementLength) bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, elementEndBufPos); - if (bufPos + elementLength > elementEndBufPos) { + if ((bufPos < 0) || (bufPos + elementLength > elementEndBufPos)) { goto exit_with_error; } diff --git a/src/mms/iso_mms/server/mms_association_service.c b/src/mms/iso_mms/server/mms_association_service.c index 7a5245c7e..25c69dde2 100644 --- a/src/mms/iso_mms/server/mms_association_service.c +++ b/src/mms/iso_mms/server/mms_association_service.c @@ -1,7 +1,7 @@ /* * mms_association_service.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -53,7 +53,7 @@ #define MMS_SERVICE_FILE_OPEN 0x80 #define MMS_SERVICE_FILE_READ 0x40 #define MMS_SERVICE_FILE_CLOSE 0x20 -#define MMS_SERVICE_FILE_RENAME 0x01 +#define MMS_SERVICE_FILE_RENAME 0x10 #define MMS_SERVICE_FILE_DELETE 0x08 #define MMS_SERVICE_FILE_DIRECTORY 0x04 #define MMS_SERVICE_UNSOLICITED_STATUS 0x02 @@ -62,58 +62,6 @@ #define MMS_SERVICE_CONCLUDE 0x10 #define MMS_SERVICE_CANCEL 0x08 -//TODO make dependent on stack configuration! -/* servicesSupported MMS bitstring */ -static uint8_t servicesSupported[] = -{ - 0x00 -#if (MMS_STATUS_SERVICE == 1) - | MMS_SERVICE_STATUS -#endif - | MMS_SERVICE_GET_NAME_LIST -#if (MMS_IDENTIFY_SERVICE == 1) - | MMS_SERVICE_IDENTIFY -#endif - | MMS_SERVICE_READ - | MMS_SERVICE_WRITE - | MMS_SERVICE_GET_VARIABLE_ACCESS_ATTRIBUTES - , - 0x00 - | MMS_SERVICE_DEFINE_NAMED_VARIABLE_LIST - | MMS_SERVICE_DELETE_NAMED_VARIABLE_LIST - | MMS_SERVICE_GET_NAMED_VARIABLE_LIST_ATTRIBUTES - , - 0x00, - 0x00, - 0x00, - 0x00 -#if (MMS_OBTAIN_FILE_SERVICE == 1) - | MMS_SERVICE_OBTAIN_FILE -#endif - , - 0x00, - 0x00, - 0x00 -#if (MMS_JOURNAL_SERVICE == 1) - | MMS_SERVICE_READ_JOURNAL -#endif - , - 0x00 -#if (MMS_FILE_SERVICE == 1) - | MMS_SERVICE_FILE_OPEN - | MMS_SERVICE_FILE_READ - | MMS_SERVICE_FILE_CLOSE - | MMS_SERVICE_FILE_RENAME - | MMS_SERVICE_FILE_DELETE - | MMS_SERVICE_FILE_DIRECTORY -#endif - | MMS_SERVICE_INFORMATION_REPORT - , - 0x00 - | MMS_SERVICE_CONCLUDE - | MMS_SERVICE_CANCEL -}; - /* negotiated parameter CBB */ static uint8_t parameterCBB[] = { @@ -127,7 +75,7 @@ static uint8_t parameterCBB[] = *********************************************************************************************/ static int -encodeInitResponseDetail(uint8_t* buffer, int bufPos, bool encode) +encodeInitResponseDetail(MmsServerConnection self, uint8_t* buffer, int bufPos, bool encode) { int initResponseDetailSize = 14 + 5 + 3; @@ -140,6 +88,132 @@ encodeInitResponseDetail(uint8_t* buffer, int bufPos, bool encode) bufPos = BerEncoder_encodeBitString(0x81, 11, parameterCBB, buffer, bufPos); +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + + uint8_t servicesSupported[] = + { + 0x00 + #if (MMS_STATUS_SERVICE == 1) + | MMS_SERVICE_STATUS + #endif + | MMS_SERVICE_GET_NAME_LIST + #if (MMS_IDENTIFY_SERVICE == 1) + | MMS_SERVICE_IDENTIFY + #endif + | MMS_SERVICE_READ + | MMS_SERVICE_WRITE + | MMS_SERVICE_GET_VARIABLE_ACCESS_ATTRIBUTES + , + 0x00 + #if ((MMS_DATA_SET_SERVICE == 1) && (MMS_GET_DATA_SET_ATTRIBUTES == 1)) + | MMS_SERVICE_GET_NAMED_VARIABLE_LIST_ATTRIBUTES + #endif + , + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 + | MMS_SERVICE_INFORMATION_REPORT + , + 0x00 + | MMS_SERVICE_CONCLUDE + | MMS_SERVICE_CANCEL + }; + + + if (self->server->fileServiceEnabled) { + +#if (MMS_OBTAIN_FILE_SERVICE == 1) + servicesSupported[5] |= MMS_SERVICE_OBTAIN_FILE; +#endif + +#if (MMS_FILE_SERVICE == 1) + servicesSupported[9] |= MMS_SERVICE_FILE_OPEN; + servicesSupported[9] |= MMS_SERVICE_FILE_READ; + servicesSupported[9] |= MMS_SERVICE_FILE_CLOSE; + servicesSupported[9] |= MMS_SERVICE_FILE_RENAME; + servicesSupported[9] |= MMS_SERVICE_FILE_DELETE; + servicesSupported[9] |= MMS_SERVICE_FILE_DIRECTORY; +#endif /* (MMS_FILE_SERVICE == 1) */ + + } + + if (self->server->dynamicVariableListServiceEnabled) { + +#if ((MMS_DATA_SET_SERVICE == 1) && (MMS_DYNAMIC_DATA_SETS == 1)) + servicesSupported[1] |= MMS_SERVICE_DEFINE_NAMED_VARIABLE_LIST; + servicesSupported[1] |= MMS_SERVICE_DELETE_NAMED_VARIABLE_LIST; +#endif + + } + + if (self->server->journalServiceEnabled) { +#if (MMS_JOURNAL_SERVICE == 1) + servicesSupported[8] |= MMS_SERVICE_READ_JOURNAL; +#endif + } + + +#else + uint8_t servicesSupported[] = + { + 0x00 + #if (MMS_STATUS_SERVICE == 1) + | MMS_SERVICE_STATUS + #endif + | MMS_SERVICE_GET_NAME_LIST + #if (MMS_IDENTIFY_SERVICE == 1) + | MMS_SERVICE_IDENTIFY + #endif + | MMS_SERVICE_READ + | MMS_SERVICE_WRITE + | MMS_SERVICE_GET_VARIABLE_ACCESS_ATTRIBUTES + , + 0x00 + #if ((MMS_DATA_SET_SERVICE == 1) && (MMS_DYNAMIC_DATA_SETS == 1)) + | MMS_SERVICE_DEFINE_NAMED_VARIABLE_LIST + | MMS_SERVICE_DELETE_NAMED_VARIABLE_LIST + #endif + #if ((MMS_DATA_SET_SERVICE == 1) && (MMS_GET_DATA_SET_ATTRIBUTES == 1)) + | MMS_SERVICE_GET_NAMED_VARIABLE_LIST_ATTRIBUTES + #endif + , + 0x00, + 0x00, + 0x00, + 0x00 + #if (MMS_OBTAIN_FILE_SERVICE == 1) + | MMS_SERVICE_OBTAIN_FILE + #endif + , + 0x00, + 0x00, + 0x00 + #if (MMS_JOURNAL_SERVICE == 1) + | MMS_SERVICE_READ_JOURNAL + #endif + , + 0x00 + #if (MMS_FILE_SERVICE == 1) + | MMS_SERVICE_FILE_OPEN + | MMS_SERVICE_FILE_READ + | MMS_SERVICE_FILE_CLOSE + | MMS_SERVICE_FILE_RENAME + | MMS_SERVICE_FILE_DELETE + | MMS_SERVICE_FILE_DIRECTORY + #endif + | MMS_SERVICE_INFORMATION_REPORT + , + 0x00 + | MMS_SERVICE_CONCLUDE + | MMS_SERVICE_CANCEL + }; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + bufPos = BerEncoder_encodeBitString(0x82, 85, servicesSupported, buffer, bufPos); return bufPos; @@ -159,7 +233,7 @@ createInitiateResponse(MmsServerConnection self, ByteBuffer* writeBuffer) initiateResponseLength += 2 + BerEncoder_UInt32determineEncodedSize(self->maxServOutstandingCalled); initiateResponseLength += 2 + BerEncoder_UInt32determineEncodedSize(self->dataStructureNestingLevel); - initiateResponseLength += encodeInitResponseDetail(NULL, 0, false); + initiateResponseLength += encodeInitResponseDetail(self, NULL, 0, false); /* Initiate response pdu */ bufPos = BerEncoder_encodeTL(0xa9, initiateResponseLength, buffer, bufPos); @@ -172,7 +246,7 @@ createInitiateResponse(MmsServerConnection self, ByteBuffer* writeBuffer) bufPos = BerEncoder_encodeUInt32WithTL(0x83, self->dataStructureNestingLevel, buffer, bufPos); - bufPos = encodeInitResponseDetail(buffer, bufPos, true); + bufPos = encodeInitResponseDetail(self, buffer, bufPos, true); writeBuffer->size = bufPos; diff --git a/src/mms/iso_mms/server/mms_file_service.c b/src/mms/iso_mms/server/mms_file_service.c index a9f9b7c5c..1bd29ef5e 100644 --- a/src/mms/iso_mms/server/mms_file_service.c +++ b/src/mms/iso_mms/server/mms_file_service.c @@ -1,7 +1,7 @@ /* * mms_file_service.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -250,6 +250,8 @@ mmsServer_handleFileDeleteRequest( int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos == -1) + goto exit_reject_invalid_pdu; if (length > 255) { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); @@ -665,7 +667,7 @@ mmsServer_handleObtainFileRequest( mmsClient_createFileOpenRequest(task->lastRequestInvokeId, request, sourceFilename, 0); - IsoConnection_sendMessage(task->connection->isoConnection, request, false); + IsoConnection_sendMessage(task->connection->isoConnection, request, true); MmsServer_releaseTransmitBuffer(connection->server); diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 676a1964d..486383bc3 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -48,20 +48,19 @@ static LinkedList getDomainNames(MmsServerConnection connection) { - MmsDevice* device = MmsServer_getDevice(connection->server); + MmsDevice* device = MmsServer_getDevice(connection->server); - LinkedList list = LinkedList_create(); + LinkedList list = LinkedList_create(); - int i; + int i; - for (i = 0; i < device->domainCount; i++) { - LinkedList_add(list, device->domains[i]->domainName); - } + for (i = 0; i < device->domainCount; i++) { + LinkedList_add(list, device->domains[i]->domainName); + } - return list; + return list; } - #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) static LinkedList getNameListVMDSpecific(MmsServerConnection connection) @@ -80,7 +79,6 @@ getNameListVMDSpecific(MmsServerConnection connection) } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - #if (CONFIG_MMS_SORT_NAME_LIST == 1) static void sortIndex(int* index, int size, MmsVariableSpecification** namedVariables) @@ -173,9 +171,6 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ - - - static LinkedList getJournalListDomainSpecific(MmsServerConnection connection, char* domainName) { @@ -259,7 +254,6 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) return nameList; } - #if (MMS_DATA_SET_SERVICE == 1) static LinkedList @@ -315,13 +309,13 @@ getNamedVariableListsVMDSpecific(MmsServerConnection connection) static LinkedList getNamedVariableListAssociationSpecific(MmsServerConnection connection) { - LinkedList nameList = NULL; + LinkedList nameList = NULL; - LinkedList variableLists = MmsServerConnection_getNamedVariableLists(connection); + LinkedList variableLists = MmsServerConnection_getNamedVariableLists(connection); - nameList = createStringsFromNamedVariableList(nameList, variableLists); + nameList = createStringsFromNamedVariableList(nameList, variableLists); - return nameList; + return nameList; } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ @@ -389,7 +383,7 @@ createNameListResponse( uint32_t getNameListSize = listOfIdentifierSize; if (moreFollows == false) - getNameListSize += 3; + getNameListSize += 3; uint32_t confirmedServiceResponseSize = 1 + BerEncoder_determineLengthSize(getNameListSize) + getNameListSize; @@ -423,7 +417,7 @@ createNameListResponse( } if (moreFollows == false) - bufPos = BerEncoder_encodeBoolean(0x81, moreFollows, buffer, bufPos); + bufPos = BerEncoder_encodeBoolean(0x81, moreFollows, buffer, bufPos); response->size = bufPos; @@ -433,110 +427,116 @@ createNameListResponse( void mmsServer_handleGetNameListRequest( - MmsServerConnection connection, - uint8_t* buffer, int bufPos, int maxBufPos, - uint32_t invokeId, - ByteBuffer* response) + MmsServerConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) { - int objectClass = -1; - - int objectScope = -1; - - char* domainId = NULL; - int domainIdLength; - - char* continueAfter = NULL; - int continueAfterLength; - - while (bufPos < maxBufPos) { - uint8_t tag = buffer[bufPos++]; - int length; - - bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - - if (bufPos < 0) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return; - } - - switch (tag) { - - case 0xa0: /* objectClass */ - bufPos++; - length = buffer[bufPos++]; - objectClass = BerDecoder_decodeUint32(buffer, length, bufPos); - break; - - case 0xa1: /* objectScope */ - { - uint8_t objectScopeTag = buffer[bufPos++]; - bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - - switch (objectScopeTag) { - case 0x80: /* vmd-specific */ - objectScope = OBJECT_SCOPE_VMD; - break; - case 0x81: /* domain-specific */ - domainIdLength = length; - domainId = (char*) (buffer + bufPos); - objectScope = OBJECT_SCOPE_DOMAIN; - break; - case 0x82: /* association-specific */ - objectScope = OBJECT_SCOPE_ASSOCIATION; - break; - default: - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response); - return; - } - } - break; - - case 0x82: /* continueAfter */ - continueAfter = (char*) (buffer + bufPos); - continueAfterLength = length; - break; - default: - /* ignore unknown tag */ - break; - } - - bufPos += length; - } - - - char continueAfterIdMemory[130]; - char* continueAfterId = NULL; - - if (continueAfter != NULL) { - continueAfterId = continueAfterIdMemory; - memcpy(continueAfterId, continueAfter, continueAfterLength); - continueAfterId[continueAfterLength] = 0; - - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: getNameListRequest - continue after: (%s)\n", continueAfterId); - } - - if (objectScope == OBJECT_SCOPE_DOMAIN) { - char domainSpecificName[130]; - - memcpy(domainSpecificName, domainId, domainIdLength); - domainSpecificName[domainIdLength] = 0; - - if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: get namelist for (%s)\n", domainSpecificName); - - LinkedList nameList = getNameListDomainSpecific(connection, domainSpecificName); - - if (nameList == NULL) - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { - createNameListResponse(connection, invokeId, nameList, response, continueAfterId); - LinkedList_destroy(nameList); - } - } - else if (objectClass == OBJECT_CLASS_JOURNAL) { - LinkedList nameList = getJournalListDomainSpecific(connection, domainSpecificName); + int objectClass = -1; + + int objectScope = -1; + + char* domainId = NULL; + int domainIdLength; + + char* continueAfter = NULL; + int continueAfterLength; + + while (bufPos < maxBufPos) { + uint8_t tag = buffer[bufPos++]; + int length; + + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return; + } + + switch (tag) + { + + case 0xa0: /* objectClass */ + bufPos++; + length = buffer[bufPos++]; + objectClass = BerDecoder_decodeUint32(buffer, length, bufPos); + break; + + case 0xa1: /* objectScope */ + { + uint8_t objectScopeTag = buffer[bufPos++]; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return; + } + + switch (objectScopeTag) + { + case 0x80: /* vmd-specific */ + objectScope = OBJECT_SCOPE_VMD; + break; + case 0x81: /* domain-specific */ + domainIdLength = length; + domainId = (char*) (buffer + bufPos); + objectScope = OBJECT_SCOPE_DOMAIN; + break; + case 0x82: /* association-specific */ + objectScope = OBJECT_SCOPE_ASSOCIATION; + break; + default: + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response); + return; + } + } + break; + + case 0x82: /* continueAfter */ + continueAfter = (char*) (buffer + bufPos); + continueAfterLength = length; + break; + default: + /* ignore unknown tag */ + break; + } + + bufPos += length; + } + + char continueAfterIdMemory[130]; + char* continueAfterId = NULL; + + if (continueAfter != NULL) { + continueAfterId = continueAfterIdMemory; + memcpy(continueAfterId, continueAfter, continueAfterLength); + continueAfterId[continueAfterLength] = 0; + + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getNameListRequest - continue after: (%s)\n", continueAfterId); + } + + if (objectScope == OBJECT_SCOPE_DOMAIN) { + char domainSpecificName[130]; + + memcpy(domainSpecificName, domainId, domainIdLength); + domainSpecificName[domainIdLength] = 0; + + if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: get namelist for (%s)\n", domainSpecificName); + + LinkedList nameList = getNameListDomainSpecific(connection, domainSpecificName); + + if (nameList == NULL) + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + else { + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + LinkedList_destroy(nameList); + } + } + else if (objectClass == OBJECT_CLASS_JOURNAL) { + LinkedList nameList = getJournalListDomainSpecific(connection, domainSpecificName); if (nameList == NULL) mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); @@ -548,117 +548,119 @@ mmsServer_handleGetNameListRequest( createNameListResponse(connection, invokeId, nameList, response, continueAfterId); LinkedList_destroyStatic(nameList); } - } + } #if (MMS_DATA_SET_SERVICE == 1) - else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { - LinkedList nameList = getNamedVariableListsDomainSpecific(connection, domainSpecificName); + else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { + LinkedList nameList = getNamedVariableListsDomainSpecific(connection, domainSpecificName); - if (nameList == NULL) - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { + if (nameList == NULL) + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + else { #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif createNameListResponse(connection, invokeId, nameList, response, continueAfter); LinkedList_destroy(nameList); - } - } + } + } #endif /* (MMS_DATA_SET_SERVICE == 1) */ - else { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameList domain specific objectClass %i not supported!\n", objectClass); + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getNameList domain specific objectClass %i not supported!\n", objectClass); - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); - } - } + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); + } + } - else if (objectScope == OBJECT_SCOPE_VMD) { /* vmd-specific */ + else if (objectScope == OBJECT_SCOPE_VMD) { /* vmd-specific */ - if (objectClass == OBJECT_CLASS_DOMAIN) { + if (objectClass == OBJECT_CLASS_DOMAIN) { - LinkedList nameList = getDomainNames(connection); + LinkedList nameList = getDomainNames(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif - createNameListResponse(connection, invokeId, nameList, response, continueAfter); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); - LinkedList_destroyStatic(nameList); - } + LinkedList_destroyStatic(nameList); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { + else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { LinkedList nameList = getNameListVMDSpecific(connection); createNameListResponse(connection, invokeId, nameList, response, continueAfter); LinkedList_destroyStatic(nameList); - } + } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ #if (MMS_DATA_SET_SERVICE == 1) - else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { - LinkedList nameList = getNamedVariableListsVMDSpecific(connection); + else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { + LinkedList nameList = getNamedVariableListsVMDSpecific(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif - createNameListResponse(connection, invokeId, nameList, response, continueAfter); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); - LinkedList_destroy(nameList); - } + LinkedList_destroy(nameList); + } #endif /* (MMS_DATA_SET_SERVICE == 1) */ - else if (objectClass == OBJECT_CLASS_JOURNAL) { - LinkedList nameList = LinkedList_create(); + else if (objectClass == OBJECT_CLASS_JOURNAL) { -#if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); -#endif + /* response with empty list */ + + LinkedList nameList = LinkedList_create(); - createNameListResponse(connection, invokeId, nameList, response, continueAfter); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); - LinkedList_destroy(nameList); - } + LinkedList_destroy(nameList); + } else { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameList VMD specific objectClass %i not supported!\n", objectClass); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getNameList VMD specific objectClass %i not supported!\n", objectClass); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } - } + } #if (MMS_DATA_SET_SERVICE == 1) #if (MMS_DYNAMIC_DATA_SETS == 1) - else if (objectScope == OBJECT_SCOPE_ASSOCIATION) { /* association-specific */ + else if (objectScope == OBJECT_SCOPE_ASSOCIATION) { /* association-specific */ - if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { - LinkedList nameList = getNamedVariableListAssociationSpecific(connection); + if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { + LinkedList nameList = getNamedVariableListAssociationSpecific(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif - createNameListResponse(connection, invokeId, nameList, response, continueAfter); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); - LinkedList_destroy(nameList); - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); - } + LinkedList_destroy(nameList); + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); + } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ #endif /* (MMS_DATA_SET_SERVICE == 1) */ - else { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameList(%i) not supported!\n", objectScope); - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); - } + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getNameList(%i) not supported!\n", objectScope); -} + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); + } +} #endif /* (MMS_GET_NAME_LIST == 1) */ diff --git a/src/mms/iso_mms/server/mms_journal_service.c b/src/mms/iso_mms/server/mms_journal_service.c index 890adce85..6008f955a 100644 --- a/src/mms/iso_mms/server/mms_journal_service.c +++ b/src/mms/iso_mms/server/mms_journal_service.c @@ -271,6 +271,11 @@ mmsServer_handleReadJournalRequest( uint8_t objectIdTag = requestBuffer[bufPos++]; bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); + if (bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return; + } + switch (objectIdTag) { case 0xa1: /* domain-specific */ @@ -298,13 +303,18 @@ mmsServer_handleReadJournalRequest( case 0xa1: /* rangeStartSpecification */ { uint8_t subTag = requestBuffer[bufPos++]; - bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); if (subTag != 0x80) { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response); return; } + bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return; + } if ((length == 4) || (length == 6)) { @@ -328,13 +338,19 @@ mmsServer_handleReadJournalRequest( case 0xa2: /* rangeStopSpecification */ { uint8_t subTag = requestBuffer[bufPos++]; - bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); if (subTag != 0x80) { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response); return; } + bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return; + } + if ((length == 4) || (length == 6)) { rangeStop.type = MMS_BINARY_TIME; rangeStop.value.binaryTime.size = length; @@ -359,8 +375,14 @@ mmsServer_handleReadJournalRequest( while (bufPos < maxSubBufPos) { uint8_t subTag = requestBuffer[bufPos++]; + bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); + if (bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return; + } + switch (subTag) { case 0x80: /* timeSpecification */ diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c index 9192f68bf..3a0a78a79 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list_service.c +++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c @@ -130,9 +130,10 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); - long scopeOfDelete; + long scopeOfDelete = DeleteNamedVariableListRequest__scopeOfDelete_specific; - asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete); + if (request->scopeOfDelete) + asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete); MmsDevice* device = MmsServer_getDevice(connection->server); @@ -290,7 +291,7 @@ checkIfVariableExists(MmsDevice* device, MmsAccessSpecifier* accessSpecifier) static MmsNamedVariableList -createNamedVariableList(MmsDomain* domain, MmsDevice* device, +createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, DefineNamedVariableListRequest_t* request, char* variableListName, MmsError* mmsError) { @@ -298,7 +299,11 @@ createNamedVariableList(MmsDomain* domain, MmsDevice* device, int variableCount = request->listOfVariable.list.count; +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if ((variableCount == 0 ) || (variableCount > server->maxDataSetEntries)) { +#else if ((variableCount == 0 ) || (variableCount > CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS)) { +#endif *mmsError = MMS_ERROR_DEFINITION_OTHER; goto exit_function; } @@ -448,7 +453,11 @@ mmsServer_handleDefineNamedVariableListRequest( goto exit_free_struct; } +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if (LinkedList_size(domain->namedVariableLists) < connection->server->maxDomainSpecificDataSets) { +#else if (LinkedList_size(domain->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS) { +#endif char variableListName[65]; if (request->variableListName.choice.domainspecific.itemId.size > 64) { @@ -466,7 +475,7 @@ mmsServer_handleDefineNamedVariableListRequest( else { MmsError mmsError; - MmsNamedVariableList namedVariableList = createNamedVariableList(domain, device, + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, domain, device, request, variableListName, &mmsError); if (namedVariableList != NULL) { @@ -493,7 +502,11 @@ mmsServer_handleDefineNamedVariableListRequest( } else if (request->variableListName.present == ObjectName_PR_aaspecific) { +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if (LinkedList_size(connection->namedVariableLists) < connection->server->maxAssociationSpecificDataSets) { +#else if (LinkedList_size(connection->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS) { +#endif char variableListName[65]; @@ -512,7 +525,7 @@ mmsServer_handleDefineNamedVariableListRequest( else { MmsError mmsError; - MmsNamedVariableList namedVariableList = createNamedVariableList(NULL, device, + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, request, variableListName, &mmsError); if (namedVariableList != NULL) { @@ -556,7 +569,7 @@ mmsServer_handleDefineNamedVariableListRequest( else { MmsError mmsError; - MmsNamedVariableList namedVariableList = createNamedVariableList(NULL, device, + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, request, variableListName, &mmsError); if (namedVariableList != NULL) { diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 7d69193ce..5a7a62d2d 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -1,7 +1,7 @@ /* * mms_read_service.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -140,9 +140,17 @@ deleteValueList(LinkedList values) static bool isAccessToArrayComponent(AlternateAccess_t* alternateAccess) { - if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess. - alternateAccess != NULL) - return true; + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess != NULL) + { + if (alternateAccess->list.array[0]->choice.unnamed-> + choice.selectAlternateAccess.alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component) + { + return true; + } + else + return false; + } else return false; } @@ -161,19 +169,28 @@ getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecif if (component.size > 129) goto exit_function; - int elementCount = namedVariable->typeSpec.structure.elementCount; + MmsVariableSpecification* structSpec; - - MmsVariableSpecification* structSpec = namedVariable->typeSpec.array.elementTypeSpec; + if (namedVariable->type == MMS_ARRAY) + structSpec = namedVariable->typeSpec.array.elementTypeSpec; + else if (namedVariable->type == MMS_STRUCTURE) + structSpec = namedVariable; + else + goto exit_function; int i; - for (i = 0; i < elementCount; i++) { + for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) { if (strncmp (structSpec->typeSpec.structure.elements[i]->name, (char*) component.buf, component.size) == 0) { MmsValue* value = MmsValue_getElement(structuredValue, i); - retValue = value; + if (isAccessToArrayComponent(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) { + retValue = getComponentOfArrayElement(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + structSpec->typeSpec.structure.elements[i], value); + } + else + retValue = value; goto exit_function; } @@ -242,8 +259,10 @@ alternateArrayAccess(MmsServerConnection connection, } } - appendValueToResultList(value, values); - + if (value) + appendValueToResultList(value, values); + else + appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } else /* access error */ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index 5781812c8..7d5048879 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -67,6 +67,15 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration) IsoServer_setUserLock(self->isoServer, self->modelMutex); #endif +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + self->fileServiceEnabled = true; + self->dynamicVariableListServiceEnabled = true; + self->journalServiceEnabled = true; + self->maxDataSetEntries = CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS; + self->maxAssociationSpecificDataSets = CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS; + self->maxDomainSpecificDataSets = CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + return self; } @@ -99,6 +108,51 @@ MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath) #endif /* (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) */ } +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + +void +MmsServer_setMaxConnections(MmsServer self, int maxConnections) +{ + IsoServer_setMaxConnections(self->isoServer, maxConnections); +} + +void +MmsServer_enableFileService(MmsServer self, bool enable) +{ + self->fileServiceEnabled = enable; +} + +void +MmsServer_enableDynamicNamedVariableListService(MmsServer self, bool enable) +{ + self->dynamicVariableListServiceEnabled = enable; +} + +void +MmsServer_setMaxDataSetEntries(MmsServer self, int maxDataSetEntries) +{ + self->maxDataSetEntries = maxDataSetEntries; +} + +void +MmsServer_enableJournalService(MmsServer self, bool enable) +{ + self->journalServiceEnabled = enable; +} + +void +MmsServer_setMaxAssociationSpecificDataSets(MmsServer self, int maxDataSets) +{ + self->maxAssociationSpecificDataSets = maxDataSets; +} + +void +MmsServer_setMaxDomainSpecificDataSets(MmsServer self, int maxDataSets) +{ + self->maxDomainSpecificDataSets = maxDataSets; +} + +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ void MmsServer_lockModel(MmsServer self) diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c index 3ddcb47d5..c689fdb5e 100644 --- a/src/mms/iso_mms/server/mms_server_connection.c +++ b/src/mms/iso_mms/server/mms_server_connection.c @@ -1,7 +1,7 @@ /* * mms_server_connection.c * - * Copyright 2013-2016 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -83,8 +83,8 @@ mmsMsg_encodeMmsRejectPdu(uint32_t* invokeId, int rejectType, int rejectReason, if (invokeId != NULL) { /* original invokeId */ - bufPos = BerEncoder_encodeTL(0x80, invokeIdLength, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(*invokeId, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x80, invokeIdLength, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(*invokeId, buffer, bufPos); } buffer[bufPos++] = (uint8_t) (0x80 + rejectType); @@ -100,7 +100,8 @@ mmsMsg_createMmsRejectPdu(uint32_t* invokeId, int reason, ByteBuffer* response) int rejectType = 0; int rejectReason = 0; - switch (reason) { + switch (reason) + { case MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE: rejectType = MMS_REJECT_CONFIRMED_REQUEST; @@ -136,84 +137,168 @@ mmsMsg_createMmsRejectPdu(uint32_t* invokeId, int reason, ByteBuffer* response) static void handleConfirmedRequestPdu( - MmsServerConnection self, - uint8_t* buffer, int bufPos, int maxBufPos, - ByteBuffer* response) + MmsServerConnection self, + uint8_t* buffer, int bufPos, int maxBufPos, + ByteBuffer* response) { - uint32_t invokeId = 0; + uint32_t invokeId = 0; - while (bufPos < maxBufPos) { - uint8_t tag = buffer[bufPos++]; - int length; + while (bufPos < maxBufPos) { + uint8_t tag = buffer[bufPos++]; + int length; - bool extendedTag = false; + bool extendedTag = false; - if ((tag & 0x1f) == 0x1f) { - extendedTag = true; - tag = buffer[bufPos++]; - } + if ((tag & 0x1f) == 0x1f) { + extendedTag = true; + tag = buffer[bufPos++]; + } - bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return; + if (bufPos < 0) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return; } - + if (bufPos + length > maxBufPos) { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } - if (extendedTag) { - switch(tag) { + if (extendedTag) { + switch (tag) + { #if (MMS_OBTAIN_FILE_SERVICE == 1) - case 0x2e: /* obtain-file */ - mmsServer_handleObtainFileRequest(self, buffer, bufPos, bufPos + length, invokeId, response); - break; + + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x2e: /* obtain-file */ + if (self->server->fileServiceEnabled) + mmsServer_handleObtainFileRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; +#else + case 0x2e: /* obtain-file */ + mmsServer_handleObtainFileRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + + #endif /* MMS_OBTAIN_FILE_SERVICE == 1 */ #if (MMS_JOURNAL_SERVICE == 1) + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x41: /* read-journal */ + if (self->server->journalServiceEnabled) + mmsServer_handleReadJournalRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; + +#else case 0x41: /* read-journal */ mmsServer_handleReadJournalRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + #endif /* (MMS_JOURNAL_SERVICE == 1) */ #if (MMS_FILE_SERVICE == 1) - case 0x48: /* file-open-request */ - mmsServer_handleFileOpenRequest(self, buffer, bufPos, bufPos + length, invokeId, response); - break; - - case 0x49: /* file-read-request */ - mmsServer_handleFileReadRequest(self, buffer, bufPos, bufPos + length, invokeId, response); - break; - case 0x4a: /* file-close-request */ - mmsServer_handleFileCloseRequest(self, buffer, bufPos, bufPos + length, invokeId, response); - break; +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x48: /* file-open-request */ + if (self->server->fileServiceEnabled) + mmsServer_handleFileOpenRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; - case 0x4b: /* file-rename-request */ +#else + case 0x48: /* file-open-request */ + mmsServer_handleFileOpenRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x49: /* file-read-request */ + if (self->server->fileServiceEnabled) + mmsServer_handleFileReadRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; +#else + case 0x49: /* file-read-request */ + mmsServer_handleFileReadRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x4a: /* file-close-request */ + if (self->server->fileServiceEnabled) + mmsServer_handleFileCloseRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; +#else + case 0x4a: /* file-close-request */ + mmsServer_handleFileCloseRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x4b: /* file-rename-request */ + if (self->server->fileServiceEnabled) + mmsServer_handleFileRenameRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; +#else + case 0x4b: /* file-rename-request */ mmsServer_handleFileRenameRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x4c: /* file-delete-request */ + if (self->server->fileServiceEnabled) + mmsServer_handleFileDeleteRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; +#else + case 0x4c: /* file-delete-request */ + mmsServer_handleFileDeleteRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0x4d: /* file-directory-request */ + if (self->server->fileServiceEnabled) + mmsServer_handleFileDirectoryRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; +#else + case 0x4d: /* file-directory-request */ + mmsServer_handleFileDirectoryRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + break; +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ - case 0x4c: /* file-delete-request */ - mmsServer_handleFileDeleteRequest(self, buffer, bufPos, bufPos + length, invokeId, response); - break; - - case 0x4d: /* file-directory-request */ - mmsServer_handleFileDirectoryRequest(self, buffer, bufPos, bufPos + length, invokeId, response); - break; #endif /* MMS_FILE_SERVICE == 1 */ default: mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); return; break; - } - } - else { - switch(tag) { + } + } + else { + switch (tag) + { case 0x02: /* invoke Id */ invokeId = BerDecoder_decodeUint32(buffer, length, bufPos); if (DEBUG_MMS_SERVER) @@ -248,7 +333,7 @@ handleConfirmedRequestPdu( #if (MMS_WRITE_SERVICE == 1) case 0xa5: /* write-request */ mmsServer_handleWriteRequest(self, buffer, bufPos, bufPos + length, - invokeId, response); + invokeId, response); break; #endif /* (MMS_WRITE_SERVICE == 1) */ @@ -260,41 +345,62 @@ handleConfirmedRequestPdu( break; #endif /* MMS_GET_VARIABLE_ACCESS_ATTRIBUTES == 1 */ - #if (MMS_DYNAMIC_DATA_SETS == 1) + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + case 0xab: /* define-named-variable-list */ - mmsServer_handleDefineNamedVariableListRequest(self, - buffer, bufPos, bufPos + length, - invokeId, response); + if (self->server->dynamicVariableListServiceEnabled) + mmsServer_handleDefineNamedVariableListRequest(self, + buffer, bufPos, bufPos + length, + invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); break; -#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ + case 0xad: /* delete-named-variable-list-request */ + if (self->server->dynamicVariableListServiceEnabled) + mmsServer_handleDeleteNamedVariableListRequest(self, + buffer, bufPos, bufPos + length, + invokeId, response); + else + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + break; -#if (MMS_GET_DATA_SET_ATTRIBUTES == 1) - case 0xac: /* get-named-variable-list-attributes-request */ - mmsServer_handleGetNamedVariableListAttributesRequest(self, +#else + case 0xab: /* define-named-variable-list */ + mmsServer_handleDefineNamedVariableListRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; -#endif /* (MMS_GET_DATA_SET_ATTRIBUTES == 1) */ -#if (MMS_DYNAMIC_DATA_SETS == 1) case 0xad: /* delete-named-variable-list-request */ mmsServer_handleDeleteNamedVariableListRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; + +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ +#if (MMS_GET_DATA_SET_ATTRIBUTES == 1) + case 0xac: /* get-named-variable-list-attributes-request */ + mmsServer_handleGetNamedVariableListAttributesRequest(self, + buffer, bufPos, bufPos + length, + invokeId, response); + break; +#endif /* (MMS_GET_DATA_SET_ATTRIBUTES == 1) */ + default: mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); return; break; } - } + } - bufPos += length; - } + bufPos += length; + } } #if (MMS_OBTAIN_FILE_SERVICE == 1) @@ -334,7 +440,6 @@ handleConfirmedErrorPdu( } } - static MmsObtainFileTask getUploadTaskByInvokeId(MmsServer mmsServer, uint32_t invokeId) { @@ -381,13 +486,14 @@ handleConfirmedResponsePdu( bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); return; } if (extendedTag) { - switch(tag) { + switch (tag) + { #if (MMS_FILE_SERVICE == 1) case 0x48: /* file-open-response */ @@ -400,7 +506,7 @@ handleConfirmedResponsePdu( if (fileTask != NULL) { - int32_t frmsId; + int32_t frmsId; if (mmsMsg_parseFileOpenResponse(buffer, startBufPos, maxBufPos, &frmsId, NULL, NULL)) { fileTask->frmsId = frmsId; @@ -487,7 +593,8 @@ handleConfirmedResponsePdu( } } else { - switch(tag) { + switch (tag) + { case 0x02: /* invoke Id */ invokeId = BerDecoder_decodeUint32(buffer, length, bufPos); if (DEBUG_MMS_SERVER) @@ -512,58 +619,59 @@ MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, { uint8_t* buffer = message->buffer; - if (message->size < 2) - goto parsing_error; + if (message->size < 2) + goto parsing_error; - int bufPos = 0; + int bufPos = 0; - uint8_t pduType = buffer[bufPos++]; - int pduLength; + uint8_t pduType = buffer[bufPos++]; + int pduLength; - bufPos = BerDecoder_decodeLength(buffer, &pduLength, bufPos, message->size); + bufPos = BerDecoder_decodeLength(buffer, &pduLength, bufPos, message->size); - if (bufPos < 0) - goto parsing_error; + if (bufPos < 0) + goto parsing_error; - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: recvd MMS-PDU type: %02x size: %i\n", pduType, pduLength); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: recvd MMS-PDU type: %02x size: %i\n", pduType, pduLength); - switch (pduType) { - case 0xa8: /* Initiate request PDU */ - mmsServer_handleInitiateRequest(self, buffer, bufPos, bufPos + pduLength, response); - break; + switch (pduType) + { + case 0xa8: /* Initiate request PDU */ + mmsServer_handleInitiateRequest(self, buffer, bufPos, bufPos + pduLength, response); + break; - case 0xa0: /* Confirmed request PDU */ - handleConfirmedRequestPdu(self, buffer, bufPos, bufPos + pduLength, response); - break; + case 0xa0: /* Confirmed request PDU */ + handleConfirmedRequestPdu(self, buffer, bufPos, bufPos + pduLength, response); + break; #if (MMS_OBTAIN_FILE_SERVICE == 1) - case 0xa1: /* Confirmed response PDU */ - handleConfirmedResponsePdu(self, buffer, bufPos, bufPos + pduLength, response); - break; + case 0xa1: /* Confirmed response PDU */ + handleConfirmedResponsePdu(self, buffer, bufPos, bufPos + pduLength, response); + break; - case 0xa2: /* Confirmed error PDU */ - handleConfirmedErrorPdu(self, buffer, 0, bufPos + pduLength, response); - break; + case 0xa2: /* Confirmed error PDU */ + handleConfirmedErrorPdu(self, buffer, 0, bufPos + pduLength, response); + break; #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ - case 0x8b: /* Conclude request PDU */ - mmsServer_writeConcludeResponsePdu(response); - break; + case 0x8b: /* Conclude request PDU */ + mmsServer_writeConcludeResponsePdu(response); + break; - case 0xa4: /* Reject PDU - silently ignore */ - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: received reject PDU!\n"); - break; + case 0xa4: /* Reject PDU - silently ignore */ + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: received reject PDU!\n"); + break; - default: - mmsMsg_createMmsRejectPdu(NULL, MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE, response); - break; - } + default: + mmsMsg_createMmsRejectPdu(NULL, MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE, response); + break; + } - return; + return; -parsing_error: + parsing_error: if (DEBUG_MMS_SERVER) printf("MMS_SERVER: error parsing message\n"); @@ -573,9 +681,9 @@ MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, static void /* will be called by IsoConnection */ messageReceived(void* parameter, ByteBuffer* message, ByteBuffer* response) { - MmsServerConnection self = (MmsServerConnection) parameter; + MmsServerConnection self = (MmsServerConnection) parameter; - MmsServerConnection_parseMessage(self, message, response); + MmsServerConnection_parseMessage(self, message, response); } /********************************************************************************************** @@ -585,31 +693,31 @@ messageReceived(void* parameter, ByteBuffer* message, ByteBuffer* response) MmsServerConnection MmsServerConnection_init(MmsServerConnection connection, MmsServer server, IsoConnection isoCon) { - MmsServerConnection self; + MmsServerConnection self; - if (connection == NULL) - self = (MmsServerConnection) GLOBAL_CALLOC(1, sizeof(struct sMmsServerConnection)); - else - self = connection; + if (connection == NULL) + self = (MmsServerConnection) GLOBAL_CALLOC(1, sizeof(struct sMmsServerConnection)); + else + self = connection; - self->maxServOutstandingCalled = 0; - self->maxServOutstandingCalling = 0; - self->maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; - self->dataStructureNestingLevel = 0; - self->server = server; - self->isoConnection = isoCon; + self->maxServOutstandingCalled = 0; + self->maxServOutstandingCalling = 0; + self->maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; + self->dataStructureNestingLevel = 0; + self->server = server; + self->isoConnection = isoCon; #if (MMS_DYNAMIC_DATA_SETS == 1) - self->namedVariableLists = LinkedList_create(); + self->namedVariableLists = LinkedList_create(); #endif #if (MMS_OBTAIN_FILE_SERVICE == 1) - self->lastRequestInvokeId = 0; + self->lastRequestInvokeId = 0; #endif - IsoConnection_installListener(isoCon, messageReceived, (void*) self); + IsoConnection_installListener(isoCon, messageReceived, (void*) self); - return self; + return self; } void @@ -625,27 +733,27 @@ MmsServerConnection_destroy(MmsServerConnection self) #endif #if (MMS_DYNAMIC_DATA_SETS == 1) - LinkedList_destroyDeep(self->namedVariableLists, (LinkedListValueDeleteFunction) MmsNamedVariableList_destroy); + LinkedList_destroyDeep(self->namedVariableLists, (LinkedListValueDeleteFunction) MmsNamedVariableList_destroy); #endif - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); } #if (MMS_DYNAMIC_DATA_SETS == 1) bool MmsServerConnection_addNamedVariableList(MmsServerConnection self, MmsNamedVariableList variableList) { - //TODO check if operation is allowed! + //TODO check if operation is allowed! - LinkedList_add(self->namedVariableLists, variableList); + LinkedList_add(self->namedVariableLists, variableList); - return true; + return true; } void MmsServerConnection_deleteNamedVariableList(MmsServerConnection self, char* listName) { - mmsServer_deleteVariableList(self->namedVariableLists, listName); + mmsServer_deleteVariableList(self->namedVariableLists, listName); } MmsNamedVariableList @@ -655,11 +763,10 @@ MmsServerConnection_getNamedVariableList(MmsServerConnection self, const char* v } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ - char* MmsServerConnection_getClientAddress(MmsServerConnection self) { - return IsoConnection_getPeerAddress(self->isoConnection); + return IsoConnection_getPeerAddress(self->isoConnection); } IsoConnection @@ -672,7 +779,7 @@ MmsServerConnection_getIsoConnection(MmsServerConnection self) LinkedList MmsServerConnection_getNamedVariableLists(MmsServerConnection self) { - return self->namedVariableLists; + return self->namedVariableLists; } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ @@ -691,7 +798,6 @@ MmsServerConnection_getNextRequestInvokeId(MmsServerConnection self) } #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ - const char* MmsServerConnection_getFilesystemBasepath(MmsServerConnection self) { diff --git a/src/mms/iso_presentation/iso_presentation.c b/src/mms/iso_presentation/iso_presentation.c index 1aab91381..01f4bd5b6 100644 --- a/src/mms/iso_presentation/iso_presentation.c +++ b/src/mms/iso_presentation/iso_presentation.c @@ -210,14 +210,14 @@ parseFullyEncodedData(IsoPresentation* self, uint8_t* buffer, int len, int bufPo bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, endPos); - endPos = bufPos + len; - if (bufPos < 0) { if (DEBUG_PRES) printf("PRES: wrong parameter length\n"); return -1; } + endPos = bufPos + len; + while (bufPos < endPos) { uint8_t tag = buffer[bufPos++]; int length; @@ -285,6 +285,12 @@ parsePCDLEntry(IsoPresentation* self, uint8_t* buffer, int totalLength, int bufP bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, endPos); + if (bufPos < 0) { + if (DEBUG_PRES) + printf("PRES: Invalid PDU\n"); + return -1; + } + switch (tag) { case 0x02: /* presentation-context-identifier */ contextId = BerDecoder_decodeUint32(buffer, len, bufPos); @@ -357,6 +363,8 @@ parsePresentationContextDefinitionList(IsoPresentation* self, uint8_t* buffer, i int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, endPos); + if (bufPos < 0) + return -1; switch (tag) { case 0x30: @@ -452,6 +460,12 @@ IsoPresentation_parseAcceptMessage(IsoPresentation* self, ByteBuffer* byteBuffer bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + if (bufPos < 0) { + if (DEBUG_PRES) + printf("PRES: Invalid message\n"); + return 0; + } + while (bufPos < maxBufPos) { uint8_t tag = buffer[bufPos++]; @@ -572,11 +586,23 @@ IsoPresentation_parseUserData(IsoPresentation* self, ByteBuffer* readBuffer) bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, length); + if (bufPos < 0) { + if (DEBUG_PRES) + printf("PRES: invalid message!\n"); + return 0; + } + if (buffer[bufPos++] != 0x30) return 0; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, length); + if (bufPos < 0) { + if (DEBUG_PRES) + printf("PRES: invalid message!\n"); + return 0; + } + if (buffer[bufPos++] != 0x02) return 0; @@ -592,6 +618,12 @@ IsoPresentation_parseUserData(IsoPresentation* self, ByteBuffer* readBuffer) bufPos = BerDecoder_decodeLength(buffer, &userDataLength, bufPos, length); + if (bufPos < 0) { + if (DEBUG_PRES) + printf("PRES: invalid message!\n"); + return 0; + } + ByteBuffer_wrap(&(self->nextPayload), buffer + bufPos, userDataLength, userDataLength); return 1; @@ -617,6 +649,12 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + if (bufPos < 0) { + if (DEBUG_PRES) + printf("PRES: invalid message!\n"); + return 0; + } + if (DEBUG_PRES) printf("PRES: CPType with len %i\n", len); @@ -627,7 +665,7 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) if (bufPos < 0) { if (DEBUG_PRES) - printf("PRES: wrong parameter length\n"); + printf("PRES: invalid message!\n"); return 0; } @@ -639,10 +677,20 @@ IsoPresentation_parseConnect(IsoPresentation* self, ByteBuffer* byteBuffer) printf("PRES: mode-value of wrong type!\n"); return 0; } + bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + + if (bufPos < 0) { + if (DEBUG_PRES) + printf("PRES: invalid message!\n"); + return 0; + } + uint32_t modeSelector = BerDecoder_decodeUint32(buffer, len, bufPos); + if (DEBUG_PRES) printf("PRES: modesel %ui\n", modeSelector); + bufPos += len; } break; diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c index eb7fe48e4..a7705a253 100644 --- a/src/mms/iso_server/iso_connection.c +++ b/src/mms/iso_server/iso_connection.c @@ -1,7 +1,7 @@ /* * iso_connection.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -86,8 +86,6 @@ struct sIsoConnection #if (CONFIG_MMS_THREADLESS_STACK != 1) Thread thread; Semaphore conMutex; - - bool isInsideCallback; #endif }; @@ -211,17 +209,8 @@ IsoConnection_handleTcpConnection(IsoConnection self) ByteBuffer_wrap(&mmsResponseBuffer, self->sendBuffer, 0, SEND_BUF_SIZE); if (self->msgRcvdHandler != NULL) { - -#if (CONFIG_MMS_THREADLESS_STACK != 1) - self->isInsideCallback = true; -#endif - self->msgRcvdHandler(self->msgRcvdHandlerParameter, &mmsRequest, &mmsResponseBuffer); - -#if (CONFIG_MMS_THREADLESS_STACK != 1) - self->isInsideCallback = false; -#endif } struct sBufferChain mmsBufferPartStruct; @@ -306,16 +295,9 @@ IsoConnection_handleTcpConnection(IsoConnection self) ByteBuffer_wrap(&mmsResponseBuffer, self->sendBuffer, 0, SEND_BUF_SIZE); if (self->msgRcvdHandler != NULL) { -#if (CONFIG_MMS_THREADLESS_STACK != 1) - self->isInsideCallback = true; -#endif self->msgRcvdHandler(self->msgRcvdHandlerParameter, mmsRequest, &mmsResponseBuffer); - -#if (CONFIG_MMS_THREADLESS_STACK != 1) - self->isInsideCallback = false; -#endif } /* send a response if required */ @@ -365,6 +347,11 @@ IsoConnection_handleTcpConnection(IsoConnection self) if (DEBUG_ISO_SERVER) printf("ISO_SERVER: iso_connection: presentation ok\n"); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + IsoServer_userLock(self->isoServer); + Semaphore_wait(self->conMutex); +#endif + struct sBufferChain acseBufferPartStruct; BufferChain acseBufferPart = &acseBufferPartStruct; acseBufferPart->buffer = self->sendBuffer; @@ -387,6 +374,11 @@ IsoConnection_handleTcpConnection(IsoConnection self) IsoSession_createDisconnectSpdu(self->session, sessionBufferPart, presentationBufferPart); CotpConnection_sendDataMessage(self->cotpConnection, sessionBufferPart); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->conMutex); + IsoServer_userUnlock(self->isoServer); +#endif } self->state = ISO_CON_STATE_STOPPED; @@ -432,7 +424,7 @@ IsoConnection_handleTcpConnection(IsoConnection self) return; } -#if (CONFIG_MMS_SINGLE_THREADED == 0) +#if ((CONFIG_MMS_SINGLE_THREADED == 0) && (CONFIG_MMS_THREADLESS_STACK == 0)) static void handleTcpConnection(void* parameter) { @@ -476,7 +468,6 @@ IsoConnection_create(Socket socket, IsoServer isoServer) self->clientAddress = Socket_getPeerAddress(self->socket); #if (CONFIG_MMS_THREADLESS_STACK != 1) - self->isInsideCallback = false; self->conMutex = Semaphore_create(1); #endif @@ -550,9 +541,13 @@ IsoConnection_sendMessage(IsoConnection self, ByteBuffer* message, bool handlerM goto exit_error; } + bool locked = false; + #if (CONFIG_MMS_THREADLESS_STACK != 1) - if (self->isInsideCallback == false) + if (handlerMode == false) { Semaphore_wait(self->conMutex); + locked = true; + } #endif struct sBufferChain payloadBufferStruct; @@ -589,7 +584,7 @@ IsoConnection_sendMessage(IsoConnection self, ByteBuffer* message, bool handlerM } #if (CONFIG_MMS_THREADLESS_STACK != 1) - if (self->isInsideCallback == false) + if (locked) Semaphore_post(self->conMutex); #endif diff --git a/src/mms/iso_server/iso_server.c b/src/mms/iso_server/iso_server.c index 6799af1c1..ddd43d81b 100644 --- a/src/mms/iso_server/iso_server.c +++ b/src/mms/iso_server/iso_server.c @@ -1,7 +1,7 @@ /* * iso_server.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -51,6 +51,11 @@ struct sIsoServer { IsoServerState state; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore stateLock; +#endif + ConnectionIndicationHandler connectionHandler; void* connectionHandlerParameter; @@ -67,10 +72,14 @@ struct sIsoServer { TLSConfiguration tlsConfiguration; +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + int maxConnections; +#endif + #if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) LinkedList openClientConnections; #else - IsoConnection* openClientConnections; + IsoConnection openClientConnections[CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS]; #endif /* (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) */ #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -85,6 +94,34 @@ struct sIsoServer { int connectionCounter; }; +static void +setState(IsoServer self, IsoServerState newState) +{ +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->stateLock); +#endif + self->state = newState; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->stateLock); +#endif +} + +static IsoServerState +getState(IsoServer self) +{ + IsoServerState state; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->stateLock); +#endif + state = self->state; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->stateLock); +#endif + + return state; +} + #if (CONFIG_MMS_THREADLESS_STACK != 1) && (CONFIG_MMS_SINGLE_THREADED == 0) static inline void lockClientConnections(IsoServer self) @@ -303,22 +340,24 @@ setupIsoServer(IsoServer self) self->serverSocket = (Socket) TcpServerSocket_create(self->localIpAddress, self->tcpPort); if (self->serverSocket == NULL) { - self->state = ISO_SVR_STATE_ERROR; + setState(self, ISO_SVR_STATE_ERROR); success = false; goto exit_function; } +#if (CONFIG_ACTIVATE_TCP_KEEPALIVE == 1) + Socket_activateTcpKeepAlive(self->serverSocket, + CONFIG_TCP_KEEPALIVE_IDLE, + CONFIG_TCP_KEEPALIVE_INTERVAL, + CONFIG_TCP_KEEPALIVE_CNT); +#endif + ServerSocket_setBacklog((ServerSocket) self->serverSocket, BACKLOG); ServerSocket_listen((ServerSocket) self->serverSocket); - self->state = ISO_SVR_STATE_RUNNING; - -#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS != -1) - if (DEBUG_ISO_SERVER) - printf("ISO_SERVER: server is limited to %i client connections.\n", (int) CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS); -#endif + setState(self, ISO_SVR_STATE_RUNNING); exit_function: return success; @@ -334,6 +373,19 @@ handleIsoConnections(IsoServer self) if ((connectionSocket = ServerSocket_accept((ServerSocket) self->serverSocket)) != NULL) { +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if (self->maxConnections > -1) { + if (private_IsoServer_getConnectionCounter(self) >= self->maxConnections) { + if (DEBUG_ISO_SERVER) + printf("ISO_SERVER: maximum number of connections reached -> reject connection attempt.\n"); + + Socket_destroy(connectionSocket); + + return; + } + } +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + #if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS != -1) if (private_IsoServer_getConnectionCounter(self) >= CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS) { if (DEBUG_ISO_SERVER) @@ -376,6 +428,19 @@ handleIsoConnectionsThreadless(IsoServer self) if ((connectionSocket = ServerSocket_accept((ServerSocket) self->serverSocket)) != NULL) { +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + if (self->maxConnections > -1) { + if (private_IsoServer_getConnectionCounter(self) >= self->maxConnections) { + if (DEBUG_ISO_SERVER) + printf("ISO_SERVER: maximum number of connections reached -> reject connection attempt.\n"); + + Socket_destroy(connectionSocket); + + return; + } + } +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + #if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS != -1) if (private_IsoServer_getConnectionCounter(self) >= CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS) { if (DEBUG_ISO_SERVER) @@ -453,11 +518,16 @@ IsoServer_create(TLSConfiguration tlsConfiguration) self->tlsConfiguration = tlsConfiguration; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + self->stateLock = Semaphore_create(1); +#endif + #if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) self->openClientConnections = LinkedList_create(); -#else - self->openClientConnections = (IsoConnection*) - GLOBAL_CALLOC(CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS, sizeof(IsoConnection)); +#endif + +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) + self->maxConnections = CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS; #endif #if (CONFIG_MMS_THREADLESS_STACK != 1) && (CONFIG_MMS_SINGLE_THREADED == 0) @@ -470,6 +540,14 @@ IsoServer_create(TLSConfiguration tlsConfiguration) return self; } +#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) +void +IsoServer_setMaxConnections(IsoServer self, int maxConnections) +{ + self->maxConnections = maxConnections; +} +#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ + void IsoServer_setTcpPort(IsoServer self, int port) { @@ -485,7 +563,7 @@ IsoServer_setLocalIpAddress(IsoServer self, const char* ipAddress) IsoServerState IsoServer_getState(IsoServer self) { - return self->state; + return getState(self); } void @@ -552,7 +630,7 @@ IsoServer_startListeningThreadless(IsoServer self) self->serverSocket = NULL; } else { - self->state = ISO_SVR_STATE_RUNNING; + setState(self, ISO_SVR_STATE_RUNNING); if (DEBUG_ISO_SERVER) printf("ISO_SERVER: new iso server (threadless) started\n"); @@ -564,7 +642,7 @@ IsoServer_waitReady(IsoServer self, unsigned int timeoutMs) { int result; - if (self->state == ISO_SVR_STATE_RUNNING) { + if (getState(self) == ISO_SVR_STATE_RUNNING) { HandleSet handles; handles = Handleset_new(); @@ -586,7 +664,9 @@ IsoServer_waitReady(IsoServer self, unsigned int timeoutMs) IsoConnection_addHandleSet(isoConnection, handles); openConnection = LinkedList_getNext(openConnection); } else { +#if ((CONFIG_MMS_SINGLE_THREADED == 1) || (CONFIG_MMS_THREADLESS_STACK == 1)) IsoConnection_destroy(isoConnection); +#endif lastConnection->next = openConnection->next; GLOBAL_FREEMEM(openConnection); openConnection = lastConnection->next; @@ -612,11 +692,12 @@ IsoServer_waitReady(IsoServer self, unsigned int timeoutMs) } } +#endif /* (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) */ + #if (CONFIG_MMS_THREADLESS_STACK != 1) && (CONFIG_MMS_SINGLE_THREADED == 0) unlockClientConnections(self); #endif -#endif /* (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) */ Handleset_addSocket(handles, self->serverSocket); result = Handleset_waitReady(handles, timeoutMs); Handleset_destroy(handles); @@ -634,14 +715,15 @@ IsoServer_waitReady(IsoServer self, unsigned int timeoutMs) void IsoServer_processIncomingMessages(IsoServer self) { - if (self->state == ISO_SVR_STATE_RUNNING) + if (getState(self) == ISO_SVR_STATE_RUNNING) handleIsoConnectionsThreadless(self); } static void stopListening(IsoServer self) { - self->state = ISO_SVR_STATE_STOPPED; + setState(self, ISO_SVR_STATE_STOPPED); + if (self->serverSocket != NULL) { ServerSocket_destroy((ServerSocket) self->serverSocket); self->serverSocket = NULL; @@ -682,7 +764,7 @@ IsoServer_stopListening(IsoServer self) void IsoServer_closeConnection(IsoServer self, IsoConnection isoConnection) { - if (self->state != ISO_SVR_STATE_IDLE) { + if (getState(self) != ISO_SVR_STATE_IDLE) { self->connectionHandler(ISO_CONNECTION_CLOSED, self->connectionHandlerParameter, isoConnection); } @@ -721,8 +803,6 @@ IsoServer_destroy(IsoServer self) lockClientConnections(self); #endif -#else - GLOBAL_FREEMEM(self->openClientConnections); #endif /* (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) */ #if (CONFIG_MMS_THREADLESS_STACK != 1) && (CONFIG_MMS_SINGLE_THREADED == 0) @@ -730,6 +810,10 @@ IsoServer_destroy(IsoServer self) Semaphore_destroy(self->openClientConnectionsMutex); #endif +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_destroy(self->stateLock); +#endif + GLOBAL_FREEMEM(self); } diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c index 578a1826a..b703ad469 100644 --- a/src/sampled_values/sv_publisher.c +++ b/src/sampled_values/sv_publisher.c @@ -55,6 +55,7 @@ struct sSVPublisher_ASDU { uint8_t smpSynch; uint16_t smpCnt; + uint16_t smpCntLimit; uint32_t confRev; uint64_t refrTm; @@ -66,8 +67,6 @@ struct sSVPublisher_ASDU { SVPublisher_ASDU _next; }; - - struct sSVPublisher { uint8_t* buffer; uint16_t appId; @@ -79,9 +78,7 @@ struct sSVPublisher { int payloadLength; /* length of payload buffer */ int asduCount; /* number of ASDUs in the APDU */ - SVPublisher_ASDU asduLIst; - - + SVPublisher_ASDU asduList; }; @@ -288,7 +285,7 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId) { SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); - self->asduLIst = NULL; + self->asduList = NULL; preparePacketBuffer(self, parameters, interfaceId); @@ -303,14 +300,15 @@ SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint newAsdu->svID = svID; newAsdu->datset = datset; newAsdu->confRev = confRev; + newAsdu->smpCntLimit = UINT16_MAX; newAsdu->_next = NULL; /* append new ASDU to list */ - if (self->asduLIst == NULL) - self->asduLIst = newAsdu; + if (self->asduList == NULL) + self->asduList = newAsdu; else { - SVPublisher_ASDU lastAsdu = self->asduLIst; + SVPublisher_ASDU lastAsdu = self->asduList; while (lastAsdu->_next != NULL) lastAsdu = lastAsdu->_next; @@ -376,8 +374,6 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP if (self->datset != NULL) bufPos = BerEncoder_encodeStringWithTag(0x81, self->datset, buffer, bufPos); - //uint8_t octetString[4]; - /* SmpCnt */ bufPos = BerEncoder_encodeTL(0x82, 2, buffer, bufPos); self->smpCntBuf = buffer + bufPos; @@ -408,7 +404,7 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP self->_dataBuffer = buffer + bufPos; - bufPos += self->dataSize; /* data has to inserted by user before sending message */ + bufPos += self->dataSize; /* data has to be inserted by user before sending message */ /* SmpMod */ if (self->hasSmpMod) { @@ -425,7 +421,7 @@ SVPublisher_setupComplete(SVPublisher self) int numberOfAsdu = 0; /* determine number of ASDUs and length of all ASDUs */ - SVPublisher_ASDU nextAsdu = self->asduLIst; + SVPublisher_ASDU nextAsdu = self->asduList; int totalASDULength = 0; while (nextAsdu != NULL) { @@ -456,7 +452,7 @@ SVPublisher_setupComplete(SVPublisher self) /* seqASDU */ bufPos = BerEncoder_encodeTL(0xa2, totalASDULength, buffer, bufPos); - nextAsdu = self->asduLIst; + nextAsdu = self->asduList; while (nextAsdu != NULL) { bufPos = SVPublisher_ASDU_encodeToBuffer(nextAsdu, buffer, bufPos); @@ -486,7 +482,6 @@ SVPublisher_publish(SVPublisher self) printf("SV_PUBLISHER: send SV message\n"); Ethernet_sendPacket(self->ethernetSocket, self->buffer, self->payloadStart + self->payloadLength); - } @@ -607,6 +602,44 @@ SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) } } +int +SVPublisher_ASDU_addTimestamp(SVPublisher_ASDU self) +{ + int index = self->dataSize; + self->dataSize += 8; + return index; +} + +void +SVPublisher_ASDU_setTimestamp(SVPublisher_ASDU self, int index, Timestamp value) +{ + int i; + + uint8_t* buffer = self->_dataBuffer + index; + + for (i = 0; i < 8; i++) { + buffer[i] = value.val[i]; + } +} + +int +SVPublisher_ASDU_addQuality(SVPublisher_ASDU self) +{ + int index = self->dataSize; + self->dataSize += 4; + return index; +} + +void +SVPublisher_ASDU_setQuality(SVPublisher_ASDU self, int index, Quality value) +{ + uint8_t* buffer = self->_dataBuffer + index; + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = value / 0x100; + buffer[3] = value % 0x100; +} + uint16_t SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self) { @@ -621,10 +654,16 @@ SVPublisher_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value) encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0); } +void +SVPublisher_ASDU_setSmpCntWrap(SVPublisher_ASDU self, uint16_t value) +{ + self->smpCntLimit = value; +} + void SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self) { - self->smpCnt++; + self->smpCnt = ((self->smpCnt + 1) % self->smpCntLimit); encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0); } @@ -649,3 +688,140 @@ SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate) self->hasSmpRate = true; self->smpRate = smpRate; } + + +/******************************************************************* + * Wrapper functions to support old API (remove in future versions) + *******************************************************************/ + +SVPublisher +SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId) +{ + return SVPublisher_create(parameters, interfaceId); +} + +SVPublisher_ASDU +SampledValuesPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev) +{ + return SVPublisher_addASDU(self, svID, datset, confRev); +} + +void +SampledValuesPublisher_setupComplete(SVPublisher self) +{ + SVPublisher_setupComplete(self); +} + +void +SampledValuesPublisher_publish(SVPublisher self) +{ + SVPublisher_publish(self); +} + +void +SampledValuesPublisher_destroy(SVPublisher self) +{ + SVPublisher_destroy(self); +} + +void +SV_ASDU_resetBuffer(SVPublisher_ASDU self) +{ + SVPublisher_ASDU_resetBuffer(self); +} + +int +SV_ASDU_addINT8(SVPublisher_ASDU self) +{ + return SVPublisher_ASDU_addINT8(self); +} + +void +SV_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value) +{ + SVPublisher_ASDU_setINT8(self, index, value); +} + +int +SV_ASDU_addINT32(SVPublisher_ASDU self) +{ + return SVPublisher_ASDU_addINT32(self); +} + +void +SV_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value) +{ + SVPublisher_ASDU_setINT32(self, index, value); +} + +int +SV_ASDU_addINT64(SVPublisher_ASDU self) +{ + return SVPublisher_ASDU_addINT64(self); +} + +void +SV_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value) +{ + SVPublisher_ASDU_setINT64(self, index, value); +} + +int +SV_ASDU_addFLOAT(SVPublisher_ASDU self) +{ + return SVPublisher_ASDU_addFLOAT(self); +} + +void +SV_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value) +{ + SVPublisher_ASDU_setFLOAT(self, index, value); +} + +int +SV_ASDU_addFLOAT64(SVPublisher_ASDU self) +{ + return SVPublisher_ASDU_addFLOAT64(self); +} + +void +SV_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) +{ + SVPublisher_ASDU_setFLOAT64(self, index, value); +} + +void +SV_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value) +{ + SVPublisher_ASDU_setSmpCnt(self, value); +} + +uint16_t +SV_ASDU_getSmpCnt(SVPublisher_ASDU self) +{ + return SVPublisher_ASDU_getSmpCnt(self); +} + +void +SV_ASDU_increaseSmpCnt(SVPublisher_ASDU self) +{ + SVPublisher_ASDU_increaseSmpCnt(self); +} + +void +SV_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm) +{ + SVPublisher_ASDU_setRefrTm(self, refrTm); +} + +void +SV_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod) +{ + SVPublisher_ASDU_setSmpMod(self, smpMod); +} + +void +SV_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate) +{ + SVPublisher_ASDU_setSmpRate(self, smpRate); +} diff --git a/src/sampled_values/sv_publisher.h b/src/sampled_values/sv_publisher.h index 5ea8676ba..957e12a89 100644 --- a/src/sampled_values/sv_publisher.h +++ b/src/sampled_values/sv_publisher.h @@ -26,6 +26,7 @@ #define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ #include "libiec61850_platform_includes.h" +#include "iec61850_common.h" #ifdef __cplusplus extern "C" { @@ -187,7 +188,7 @@ void SVPublisher_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value); /** - * \brief Reserve memory for a single precission floating point number in the ASDU. + * \brief Reserve memory for a single precision floating point number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. * \return the offset in bytes of the new element within the ASDU data block. @@ -196,7 +197,7 @@ int SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU self); /** - * \brief Set the value of a single precission floating point number in the ASDU. + * \brief Set the value of a single precision floating point number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. * \param[in] index The offset within the data block of the ASDU in bytes. @@ -206,7 +207,7 @@ void SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value); /** - * \brief Reserve memory for a double precission floating point number in the ASDU. + * \brief Reserve memory for a double precision floating point number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. * \return the offset in bytes of the new element within the ASDU data block. @@ -215,7 +216,7 @@ int SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self); /** - * \brief Set the value of a double precission floating pointer number in the ASDU. + * \brief Set the value of a double precision floating pointer number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. * \param[in] index The offset within the data block of the ASDU in bytes. @@ -224,6 +225,46 @@ SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self); void SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value); +/** + * \brief Reserve memory for a 64 bit time stamp in the ASDU + * + * \param[in] self the Sampled Values ASDU instance. + * \return the offset in bytes of the new element within the ASDU data block. + */ +int +SVPublisher_ASDU_addTimestamp(SVPublisher_ASDU self); + +/** + * \brief Set the value of a 64 bit time stamp in the ASDU. + * + * \param[in] self the Sampled Values ASDU instance. + * \param[in] index The offset within the data block of the ASDU in bytes. + * \param[in] value The value which should be set. + */ +void +SVPublisher_ASDU_setTimestamp(SVPublisher_ASDU self, int index, Timestamp value); + +/** + * \brief Reserve memory for a quality value in the ASDU + * + * NOTE: Quality is encoded as BITSTRING (4 byte) + * + * \param[in] self the Sampled Values ASDU instance. + * \return the offset in bytes of the new element within the ASDU data block. + */ +int +SVPublisher_ASDU_addQuality(SVPublisher_ASDU self); + +/** + * \brief Set the value of a quality attribute in the ASDU. + * + * \param[in] self the Sampled Values ASDU instance. + * \param[in] index The offset within the data block of the ASDU in bytes. + * \param[in] value The value which should be set. + */ +void +SVPublisher_ASDU_setQuality(SVPublisher_ASDU self, int index, Quality value); + /** * \brief Set the sample count attribute of the ASDU. * @@ -253,6 +294,16 @@ SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self); void SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self); +/** + * \brief Set the roll-over (wrap) limit for the sample counter. When reaching the limit the + * sample counter will be reset to 0 (default is no limit) + * + * \param[in] self the Sampled Values ASDU instance. + * \param[in] value the new sample counter limit + */ +void +SVPublisher_ASDU_setSmpCntWrap(SVPublisher_ASDU self, uint16_t value); + /** * \brief Set the refresh time attribute of the ASDU. * @@ -287,10 +338,95 @@ SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate); /**@} @}*/ +#ifndef DEPRECATED +#if defined(__GNUC__) || defined(__clang__) + #define DEPRECATED __attribute__((deprecated)) +#else + #define DEPRECATED +#endif +#endif + +/** + * \addtogroup sv_publisher_deprecated_api_group Deprecated API + * \ingroup sv_publisher_api_group IEC 61850 Sampled Values (SV) publisher API + * \deprecated + * @{ + */ + +typedef struct sSVPublisher* SampledValuesPublisher; + +typedef struct sSV_ASDU* SV_ASDU; + +DEPRECATED SVPublisher +SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId); + +DEPRECATED SVPublisher_ASDU +SampledValuesPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev); + +DEPRECATED void +SampledValuesPublisher_setupComplete(SVPublisher self); + +DEPRECATED void +SampledValuesPublisher_publish(SVPublisher self); + +DEPRECATED void +SampledValuesPublisher_destroy(SVPublisher self); + +DEPRECATED void +SV_ASDU_resetBuffer(SVPublisher_ASDU self); + +DEPRECATED int +SV_ASDU_addINT8(SVPublisher_ASDU self); + +DEPRECATED void +SV_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value); + +DEPRECATED int +SV_ASDU_addINT32(SVPublisher_ASDU self); + +DEPRECATED void +SV_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value); + +DEPRECATED int +SV_ASDU_addINT64(SVPublisher_ASDU self); + +DEPRECATED void +SV_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value); + +DEPRECATED int +SV_ASDU_addFLOAT(SVPublisher_ASDU self); + +DEPRECATED void +SV_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value); + +DEPRECATED int +SV_ASDU_addFLOAT64(SVPublisher_ASDU self); + +DEPRECATED void +SV_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value); + +void DEPRECATED +SV_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value); + +DEPRECATED uint16_t +SV_ASDU_getSmpCnt(SVPublisher_ASDU self); + +DEPRECATED void +SV_ASDU_increaseSmpCnt(SVPublisher_ASDU self); + +DEPRECATED void +SV_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm); + +DEPRECATED void +SV_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod); + +DEPRECATED void +SV_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate); + +/**@}*/ + #ifdef __cplusplus } #endif -#include "sv_publisher_deprecated.h" - #endif /* LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ */ diff --git a/src/sampled_values/sv_publisher_deprecated.h b/src/sampled_values/sv_publisher_deprecated.h deleted file mode 100644 index 3fe31ede6..000000000 --- a/src/sampled_values/sv_publisher_deprecated.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * sv_publisher.h - * - * Copyright 2016 Michael Zillgith - * - * This file is part of libIEC61850. - * - * libIEC61850 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libIEC61850 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with libIEC61850. If not, see . - * - * See COPYING file for the complete license text. - */ - - -#ifndef LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_DEPRECATED_H_ -#define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_DEPRECATED_H_ - -#include "libiec61850_platform_includes.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) || defined(__clang__) - #define DEPRECATED __attribute__((deprecated)) -#else - #define DEPRECATED -#endif - -/** - * \addtogroup sv_publisher_deprecated_api_group Deprecated API - * \ingroup sv_publisher_api_group IEC 61850 Sampled Values (SV) publisher API - * \deprecated - * @{ - */ - -typedef DEPRECATED struct sSVPublisher* SampledValuesPublisher; - -typedef DEPRECATED struct sSV_ASDU* SV_ASDU; - -static DEPRECATED -SVPublisher -SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId) -{ - return SVPublisher_create(parameters, interfaceId); -} - -static DEPRECATED -SVPublisher_ASDU -SampledValuesPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev) -{ - return SVPublisher_addASDU(self, svID, datset, confRev); -} - -static DEPRECATED -void -SampledValuesPublisher_setupComplete(SVPublisher self) -{ - SVPublisher_setupComplete(self); -} - -static DEPRECATED -void -SampledValuesPublisher_publish(SVPublisher self) -{ - SVPublisher_publish(self); -} - -static DEPRECATED -void -SampledValuesPublisher_destroy(SVPublisher self) -{ - SVPublisher_destroy(self); -} - -static DEPRECATED -void -SV_ASDU_resetBuffer(SVPublisher_ASDU self) -{ - SVPublisher_ASDU_resetBuffer(self); -} - -static DEPRECATED -int -SV_ASDU_addINT8(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addINT8(self); -} - -static DEPRECATED -void -SV_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value) -{ - SVPublisher_ASDU_setINT8(self, index, value); -} - -static DEPRECATED -int -SV_ASDU_addINT32(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addINT32(self); -} - -static DEPRECATED -void -SV_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value) -{ - SVPublisher_ASDU_setINT32(self, index, value); -} - -static DEPRECATED -int -SV_ASDU_addINT64(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addINT64(self); -} - -static DEPRECATED -void -SV_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value) -{ - SVPublisher_ASDU_setINT64(self, index, value); -} - -static DEPRECATED -int -SV_ASDU_addFLOAT(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addFLOAT(self); -} - -static DEPRECATED -void -SV_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value) -{ - SVPublisher_ASDU_setFLOAT(self, index, value); -} - -static DEPRECATED -int -SV_ASDU_addFLOAT64(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addFLOAT64(self); -} - -static DEPRECATED -void -SV_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) -{ - SVPublisher_ASDU_setFLOAT64(self, index, value); -} - -static DEPRECATED -void -SV_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value) -{ - SVPublisher_ASDU_setSmpCnt(self, value); -} - -static DEPRECATED -uint16_t -SV_ASDU_getSmpCnt(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_getSmpCnt(self); -} - -static DEPRECATED -void -SV_ASDU_increaseSmpCnt(SVPublisher_ASDU self) -{ - SVPublisher_ASDU_increaseSmpCnt(self); -} - -static DEPRECATED -void -SV_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm) -{ - SVPublisher_ASDU_setRefrTm(self, refrTm); -} - -static DEPRECATED -void -SV_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod) -{ - SVPublisher_ASDU_setSmpMod(self, smpMod); -} - -static DEPRECATED -void -SV_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate) -{ - SVPublisher_ASDU_setSmpRate(self, smpRate); -} - -/**@}*/ - -#ifdef __cplusplus -} -#endif - -#endif /* LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_DEPRECATED_H_ */ diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c index 573fc39ec..9e03607c2 100644 --- a/src/sampled_values/sv_subscriber.c +++ b/src/sampled_values/sv_subscriber.c @@ -1,7 +1,7 @@ /* * sv_receiver.c * - * Copyright 2015 Michael Zillgith + * Copyright 2015-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -118,6 +118,12 @@ SVReceiver_disableDestAddrCheck(SVReceiver self) self->checkDestAddr = false; } +void +SVReceiver_enableDestAddrCheck(SVReceiver self) +{ + self->checkDestAddr = false; +} + void SVReceiver_addSubscriber(SVReceiver self, SVSubscriber subscriber) { @@ -185,6 +191,13 @@ SVReceiver_start(SVReceiver self) } } +bool +SVReceiver_isRunning(SVReceiver self) +{ + return self->running; +} + + void SVReceiver_stop(SVReceiver self) { @@ -324,8 +337,10 @@ parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length) } /* Call callback handler */ - if (subscriber->listener != NULL) - subscriber->listener(subscriber, subscriber->listenerParameter, &asdu); + if (subscriber) { + if (subscriber->listener != NULL) + subscriber->listener(subscriber, subscriber->listenerParameter, &asdu); + } } static void @@ -661,15 +676,24 @@ SVSubscriber_ASDU_getDatSet(SVSubscriber_ASDU self) return self->datSet; } +static inline void +memcpy_reverse(void* to, const void* from, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + ((uint8_t*)to)[size - 1 - i] = ((uint8_t*)from)[i]; +} + uint32_t SVSubscriber_ASDU_getConfRev(SVSubscriber_ASDU self) { - uint32_t retVal = *((uint32_t*) (self->confRev)); + uint32_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 4); + memcpy_reverse(&retVal, self->confRev, sizeof(uint32_t)); +#else + memcpy(&retVal, self->confRev, sizeof(uint32_t)); #endif return retVal; @@ -686,12 +710,12 @@ SVSubscriber_ASDU_getSmpMod(SVSubscriber_ASDU self) uint16_t SVSubscriber_ASDU_getSmpRate(SVSubscriber_ASDU self) { - uint16_t retVal = *((uint16_t*) (self->smpRate)); + uint16_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 2); + memcpy_reverse(&retVal, self->smpRate, sizeof(uint16_t)); +#else + memcpy(&retVal, self->smpRate, sizeof(uint16_t)); #endif return retVal; @@ -708,12 +732,12 @@ SVSubscriber_ASDU_getINT8(SVSubscriber_ASDU self, int index) int16_t SVSubscriber_ASDU_getINT16(SVSubscriber_ASDU self, int index) { - int16_t retVal = *((int16_t*) (self->dataBuffer + index)); + int16_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 2); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(uint16_t)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(uint16_t)); #endif return retVal; @@ -722,12 +746,12 @@ SVSubscriber_ASDU_getINT16(SVSubscriber_ASDU self, int index) int32_t SVSubscriber_ASDU_getINT32(SVSubscriber_ASDU self, int index) { - int32_t retVal = *((int32_t*) (self->dataBuffer + index)); + int32_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 4); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(int32_t)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(int32_t)); #endif return retVal; @@ -736,12 +760,12 @@ SVSubscriber_ASDU_getINT32(SVSubscriber_ASDU self, int index) int64_t SVSubscriber_ASDU_getINT64(SVSubscriber_ASDU self, int index) { - int64_t retVal = *((int64_t*) (self->dataBuffer + index)); + int64_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 8); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(int64_t)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(int64_t)); #endif return retVal; @@ -758,12 +782,12 @@ SVSubscriber_ASDU_getINT8U(SVSubscriber_ASDU self, int index) uint16_t SVSubscriber_ASDU_getINT16U(SVSubscriber_ASDU self, int index) { - uint16_t retVal = *((uint16_t*) (self->dataBuffer + index)); + uint16_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 2); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(uint16_t)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(uint16_t)); #endif return retVal; @@ -772,12 +796,12 @@ SVSubscriber_ASDU_getINT16U(SVSubscriber_ASDU self, int index) uint32_t SVSubscriber_ASDU_getINT32U(SVSubscriber_ASDU self, int index) { - uint32_t retVal = *((uint32_t*) (self->dataBuffer + index)); + uint32_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 4); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(uint32_t)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(uint32_t)); #endif return retVal; @@ -786,12 +810,12 @@ SVSubscriber_ASDU_getINT32U(SVSubscriber_ASDU self, int index) uint64_t SVSubscriber_ASDU_getINT64U(SVSubscriber_ASDU self, int index) { - uint64_t retVal = *((uint64_t*) (self->dataBuffer + index)); + uint64_t retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 8); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(uint64_t)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(uint64_t)); #endif return retVal; @@ -800,12 +824,12 @@ SVSubscriber_ASDU_getINT64U(SVSubscriber_ASDU self, int index) float SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU self, int index) { - float retVal = *((float*) (self->dataBuffer + index)); + float retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 4); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(float)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(float)); #endif return retVal; @@ -814,17 +838,39 @@ SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU self, int index) double SVSubscriber_ASDU_getFLOAT64(SVSubscriber_ASDU self, int index) { - double retVal = *((double*) (self->dataBuffer + index)); + double retVal; #if (ORDER_LITTLE_ENDIAN == 1) - uint8_t* buf = (uint8_t*) (&retVal); - - BerEncoder_revertByteOrder(buf, 8); + memcpy_reverse(&retVal, (self->dataBuffer + index), sizeof(double)); +#else + memcpy(&retVal, (self->dataBuffer + index), sizeof(double)); #endif return retVal; } +Timestamp +SVSubscriber_ASDU_getTimestamp(SVSubscriber_ASDU self, int index) +{ + Timestamp retVal; + + memcpy(retVal.val, self->dataBuffer + index, sizeof(retVal.val)); + + return retVal; +} + +Quality +SVSubscriber_ASDU_getQuality(SVSubscriber_ASDU self, int index) +{ + Quality retVal; + + uint8_t* buffer = self->dataBuffer + index; + + retVal = buffer[3]; + retVal += (buffer[2] * 0x100); + + return retVal; +} int SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self) @@ -832,3 +878,99 @@ SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self) return self->dataBufferLength; } +uint16_t +SVClientASDU_getSmpCnt(SVSubscriber_ASDU self) +{ + return SVSubscriber_ASDU_getSmpCnt(self); +} + +const char* +SVClientASDU_getSvId(SVSubscriber_ASDU self) +{ + return SVSubscriber_ASDU_getSvId(self); +} + +uint32_t +SVClientASDU_getConfRev(SVSubscriber_ASDU self) +{ + return SVSubscriber_ASDU_getConfRev(self); +} + +bool +SVClientASDU_hasRefrTm(SVSubscriber_ASDU self) +{ + return SVSubscriber_ASDU_hasRefrTm(self); +} + +uint64_t +SVClientASDU_getRefrTmAsMs(SVSubscriber_ASDU self) +{ + return SVSubscriber_ASDU_getRefrTmAsMs(self); +} + +int8_t +SVClientASDU_getINT8(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT8(self, index); +} + +int16_t +SVClientASDU_getINT16(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT16(self, index); +} + +int32_t +SVClientASDU_getINT32(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT32(self, index); +} + +int64_t +SVClientASDU_getINT64(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT64(self, index); +} + +uint8_t +SVClientASDU_getINT8U(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT8U(self, index); +} + +uint16_t +SVClientASDU_getINT16U(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT16U(self, index); +} + +uint32_t +SVClientASDU_getINT32U(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT32U(self, index); +} + +uint64_t +SVClientASDU_getINT64U(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getINT64U(self, index); +} + +float +SVClientASDU_getFLOAT32(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getFLOAT32(self, index); +} + +double +SVClientASDU_getFLOAT64(SVSubscriber_ASDU self, int index) +{ + return SVSubscriber_ASDU_getFLOAT64(self, index); +} + +int +SVClientASDU_getDataSize(SVSubscriber_ASDU self) +{ + return SVSubscriber_ASDU_getDataSize(self); +} + diff --git a/src/sampled_values/sv_subscriber.h b/src/sampled_values/sv_subscriber.h index ef5ab875f..7379ac0b7 100644 --- a/src/sampled_values/sv_subscriber.h +++ b/src/sampled_values/sv_subscriber.h @@ -1,7 +1,7 @@ /* * sv_subscriber.h * - * Copyright 2015 Michael Zillgith + * Copyright 2015-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -25,13 +25,13 @@ #define SAMPLED_VALUES_SV_SUBSCRIBER_H_ #include "libiec61850_common_api.h" +#include "iec61850_common.h" +#include "hal_ethernet.h" #ifdef __cplusplus extern "C" { #endif -typedef struct sEthernetSocket* EthernetSocket; - /** * \defgroup sv_subscriber_api_group IEC 61850 Sampled Values (SV) subscriber API * @@ -73,6 +73,7 @@ typedef struct sEthernetSocket* EthernetSocket; * | TimeStamp | 8 byte | * | EntryTime | 6 byte | * | BITSTRING | 4 byte | + * | Quality | 4 byte | * * The SV subscriber API can be used independent of the IEC 61850 client API. In order to access the SVCB via MMS you * have to use the IEC 61850 client API. Please see \ref ClientSVControlBlock object in section \ref IEC61850_CLIENT_SV. @@ -97,7 +98,7 @@ typedef struct sSVSubscriber_ASDU* SVSubscriber_ASDU; /** * \brief opaque handle to a SV subscriber instance * - * A subscriber is an instance associated with a single stream of measurement data. It is identified + * A subscriber is an instance associated with a single stream of measurement data. It is identified * by the Ethernet destination address, the appID value (both are on SV message level) and the svID value * that is part of each ASDU (SVSubscriber_ASDU object). */ @@ -134,15 +135,24 @@ SVReceiver_create(void); /** * \brief Disable check for destination address of the received SV messages * - * Per default both the appID and the destination address are checked to identify - * relevant SV messages. Destination address check can be disabled for performance - * reason when the appIDs are unique in the local system. - * * \param self the receiver instance reference */ void SVReceiver_disableDestAddrCheck(SVReceiver self); +/** + * \brief Enable check for destination address of the received SV messages + * + * Per default only the appID is checked to identify relevant SV messages and the + * destination address is ignored for performance reasons. This only works when the + * appIDs are unique in the local system. Otherwise the destination address check + * has to be enabled. + * + * \param self the receiver instance reference + */ +void +SVReceiver_enableDestAddrCheck(SVReceiver self); + /** * \brief Set the Ethernet interface ID for the receiver instance * @@ -194,6 +204,18 @@ SVReceiver_start(SVReceiver self); void SVReceiver_stop(SVReceiver self); +/** + * \brief Check if SV receiver is running + * + * Can be used to check if \ref SVReceiver_start has been successful. + * + * \param self the receiver instance reference + * + * \return true if SV receiver is running, false otherwise + */ +bool +SVReceiver_isRunning(SVReceiver self); + /** * \brief Destroy receiver instance (cleanup resources) * @@ -469,6 +491,30 @@ SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU self, int index); double SVSubscriber_ASDU_getFLOAT64(SVSubscriber_ASDU self, int index); +/** + * \brief Get a timestamp data value in the data part of the ASDU + * + * \param self ASDU object instance + * \param index the index (byte position of the start) of the data in the data part + * + * \return SV data + */ +Timestamp +SVSubscriber_ASDU_getTimestamp(SVSubscriber_ASDU self, int index); + +/** + * \brief Get a quality value in the data part of the ASDU + * + * NOTE: Quality is encoded as BITSTRING (4 byte) + * + * \param self ASDU object instance + * \param index the index (byte position of the start) of the data in the data part + * + * \return SV data + */ +Quality +SVSubscriber_ASDU_getQuality(SVSubscriber_ASDU self, int index); + /** * \brief Returns the size of the data part of the ASDU * @@ -479,12 +525,75 @@ SVSubscriber_ASDU_getFLOAT64(SVSubscriber_ASDU self, int index); int SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self); +#ifndef DEPRECATED +#if defined(__GNUC__) || defined(__clang__) + #define DEPRECATED __attribute__((deprecated)) +#else + #define DEPRECATED +#endif +#endif + +/** + * \addtogroup sv_subscriber_deprecated_api_group Deprecated API + * \ingroup sv_subscriber_api_group IEC 61850 Sampled Values (SV) publisher API + * \deprecated + * @{ + */ + +typedef struct sSVSubscriberASDU* SVClientASDU; + +DEPRECATED uint16_t +SVClientASDU_getSmpCnt(SVSubscriber_ASDU self); + +DEPRECATED const char* +SVClientASDU_getSvId(SVSubscriber_ASDU self); + +DEPRECATED uint32_t +SVClientASDU_getConfRev(SVSubscriber_ASDU self); + +DEPRECATED bool +SVClientASDU_hasRefrTm(SVSubscriber_ASDU self); + +DEPRECATED uint64_t +SVClientASDU_getRefrTmAsMs(SVSubscriber_ASDU self); + +DEPRECATED int8_t +SVClientASDU_getINT8(SVSubscriber_ASDU self, int index); + +DEPRECATED int16_t +SVClientASDU_getINT16(SVSubscriber_ASDU self, int index); + +DEPRECATED int32_t +SVClientASDU_getINT32(SVSubscriber_ASDU self, int index); + +DEPRECATED int64_t +SVClientASDU_getINT64(SVSubscriber_ASDU self, int index); + +DEPRECATED uint8_t +SVClientASDU_getINT8U(SVSubscriber_ASDU self, int index); + +DEPRECATED uint16_t +SVClientASDU_getINT16U(SVSubscriber_ASDU self, int index); + +DEPRECATED uint32_t +SVClientASDU_getINT32U(SVSubscriber_ASDU self, int index); + +DEPRECATED uint64_t +SVClientASDU_getINT64U(SVSubscriber_ASDU self, int index); + +DEPRECATED float +SVClientASDU_getFLOAT32(SVSubscriber_ASDU self, int index); + +DEPRECATED double +SVClientASDU_getFLOAT64(SVSubscriber_ASDU self, int index); + +DEPRECATED int +SVClientASDU_getDataSize(SVSubscriber_ASDU self); + /**@} @}*/ #ifdef __cplusplus } #endif -#include "sv_subscriber_deprecated.h" - #endif /* SAMPLED_VALUES_SV_SUBSCRIBER_ */ diff --git a/src/sampled_values/sv_subscriber_deprecated.h b/src/sampled_values/sv_subscriber_deprecated.h deleted file mode 100644 index 5d5741aa4..000000000 --- a/src/sampled_values/sv_subscriber_deprecated.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * sv_subscriber_deprecated.h - * - * Copyright 2015 Michael Zillgith - * - * This file is part of libIEC61850. - * - * libIEC61850 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * libIEC61850 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with libIEC61850. If not, see . - * - * See COPYING file for the complete license text. - */ - -#ifndef SAMPLED_VALUES_SV_SUBSCRIBER_DEPRECATED_H_ -#define SAMPLED_VALUES_SV_SUBSCRIBER_DEPRECATED_H_ - -#include "libiec61850_common_api.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) || defined(__clang__) - #define DEPRECATED __attribute__((deprecated)) -#else - #define DEPRECATED -#endif - -/** - * \addtogroup sv_subscriber_deprecated_api_group Deprecated API - * \ingroup sv_subscriber_api_group IEC 61850 Sampled Values (SV) publisher API - * \deprecated - * @{ - */ - -typedef struct sSVSubscriberASDU* SVClientASDU; - -static DEPRECATED -uint16_t -SVClientASDU_getSmpCnt(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getSmpCnt(self); -} - -static DEPRECATED -const char* -SVClientASDU_getSvId(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getSvId(self); -} - -static DEPRECATED -uint32_t -SVClientASDU_getConfRev(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getConfRev(self); -} - -static DEPRECATED -bool -SVClientASDU_hasRefrTm(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_hasRefrTm(self); -} - -static DEPRECATED -uint64_t -SVClientASDU_getRefrTmAsMs(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getRefrTmAsMs(self); -} - -static DEPRECATED -int8_t -SVClientASDU_getINT8(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT8(self, index); -} - -static DEPRECATED -int16_t -SVClientASDU_getINT16(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT16(self, index); -} - -static DEPRECATED -int32_t -SVClientASDU_getINT32(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT32(self, index); -} - -static DEPRECATED -int64_t -SVClientASDU_getINT64(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT64(self, index); -} - -static DEPRECATED -uint8_t -SVClientASDU_getINT8U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT8U(self, index); -} - -static DEPRECATED -uint16_t -SVClientASDU_getINT16U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT16U(self, index); -} - -static DEPRECATED -uint32_t -SVClientASDU_getINT32U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT32U(self, index); -} - -static DEPRECATED -uint64_t -SVClientASDU_getINT64U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT64U(self, index); -} - -static DEPRECATED -float -SVClientASDU_getFLOAT32(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getFLOAT32(self, index); -} - -static DEPRECATED -double -SVClientASDU_getFLOAT64(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getFLOAT64(self, index); -} - -static DEPRECATED -int -SVClientASDU_getDataSize(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getDataSize(self); -} - -/**@}*/ - -#ifdef __cplusplus -} -#endif - - -#endif /* SAMPLED_VALUES_SV_SUBSCRIBER_DEPRECATED_H_ */ diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index dc903363b..1d98f484a 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -575,27 +575,49 @@ EXPORTS MmsServer_isRunning IedServer_createWithTlsSupport IedConnection_createWithTlsSupport - ClientGooseControlBlock_create - ClientGooseControlBlock_destroy - IedConnection_getGoCBValues - IedConnection_setGoCBValues - ClientGooseControlBlock_getGoEna - ClientGooseControlBlock_setGoEna - ClientGooseControlBlock_getDatSet - ClientGooseControlBlock_setDatSet - ClientGooseControlBlock_getGoID - ClientGooseControlBlock_setGoID - ClientGooseControlBlock_getConfRev - ClientGooseControlBlock_getNdsComm - ClientGooseControlBlock_getMinTime - ClientGooseControlBlock_getMaxTime - ClientGooseControlBlock_getFixedOffs - ClientGooseControlBlock_getDstAddress_addr - ClientGooseControlBlock_setDstAddress_addr - ClientGooseControlBlock_getDstAddress_priority - ClientGooseControlBlock_setDstAddress_priority - ClientGooseControlBlock_getDstAddress_vid - ClientGooseControlBlock_setDstAddress_vid - ClientGooseControlBlock_getDstAddress_appid - ClientGooseControlBlock_setDstAddress_appid - DataAttribute_setValue \ No newline at end of file + ClientGooseControlBlock_getDstAddress + ClientGooseControlBlock_setDstAddress + CDC_VSS_create + CDC_VSG_create + Timestamp_createFromByteArray + IedModel_getDeviceByIndex + IedServerConfig_create + IedServerConfig_destroy + IedServerConfig_setReportBufferSize + IedServerConfig_getReportBufferSize + IedServer_createWithConfig + IedServerConfig_setFileServiceBasePath + IedServerConfig_getFileServiceBasePath + ClientGooseControlBlock_create + ClientGooseControlBlock_destroy + IedConnection_getGoCBValues + IedConnection_setGoCBValues + ClientGooseControlBlock_getGoEna + ClientGooseControlBlock_setGoEna + ClientGooseControlBlock_getGoID + ClientGooseControlBlock_setGoID + ClientGooseControlBlock_getDatSet + ClientGooseControlBlock_setDatSet + ClientGooseControlBlock_getConfRev + ClientGooseControlBlock_getNdsComm + ClientGooseControlBlock_getMinTime + ClientGooseControlBlock_getMaxTime + ClientGooseControlBlock_getFixedOffs + ControlObjectClient_getCtlValType + IedServerConfig_enableFileService + IedServerConfig_isFileServiceEnabled + IedServerConfig_enableDynamicDataSetService + IedServerConfig_isDynamicDataSetServiceEnabled + IedServerConfig_enableLogService + IedServerConfig_isLogServiceEnabled + IedServerConfig_setEdition + IedServerConfig_getEdition + IedServerConfig_setMaxMmsConnections + IedServerConfig_getMaxMmsConnections + IedServerConfig_setMaxDataSetEntries + IedServerConfig_getMaxDatasSetEntries + IedServerConfig_setMaxAssociationSpecificDataSets + IedServerConfig_getMaxAssociationSpecificDataSets + IedServerConfig_setMaxDomainSpecificDataSets + IedServerConfig_getMaxDomainSpecificDataSets + IedServer_setReadAccessHandler diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index 4ff9d9120..1f8a50397 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -532,22 +532,18 @@ EXPORTS SVSubscriber_create SVSubscriber_setListener SVSubscriber_destroy - SVSubscriber_ASDU_getSmpCnt - SVSubscriber_ASDU_getSvId - SVSubscriber_ASDU_getConfRev - SVSubscriber_ASDU_hasRefrTm - SVSubscriber_ASDU_getRefrTmAsMs - SVSubscriber_ASDU_getINT8 - SVSubscriber_ASDU_getINT16 - SVSubscriber_ASDU_getINT32 - SVSubscriber_ASDU_getINT64 - SVSubscriber_ASDU_getINT8U - SVSubscriber_ASDU_getINT16U - SVSubscriber_ASDU_getINT32U - SVSubscriber_ASDU_getINT64U - SVSubscriber_ASDU_getFLOAT32 - SVSubscriber_ASDU_getFLOAT64 - SVSubscriber_ASDU_getDataSize + SVClientASDU_getSmpCnt + SVClientASDU_getSvId + SVClientASDU_getConfRev + SVClientASDU_getINT8 + SVClientASDU_getINT16 + SVClientASDU_getINT32 + SVClientASDU_getINT8U + SVClientASDU_getINT16U + SVClientASDU_getINT32U + SVClientASDU_getFLOAT32 + SVClientASDU_getFLOAT64 + SVClientASDU_getDataSize ClientSVControlBlock_create ClientSVControlBlock_destroy ClientSVControlBlock_isMulticast @@ -578,28 +574,22 @@ EXPORTS GooseReceiver_startThreadless GooseReceiver_stopThreadless GooseReceiver_tick - SVPublisher_create - SVPublisher_addASDU - SVPublisher_setupComplete - SVPublisher_publish - SVPublisher_destroy - SVPublisher_ASDU_resetBuffer - SVPublisher_ASDU_addINT8 - SVPublisher_ASDU_setINT8 - SVPublisher_ASDU_addINT32 - SVPublisher_ASDU_setINT32 - SVPublisher_ASDU_addINT64 - SVPublisher_ASDU_setINT64 - SVPublisher_ASDU_addFLOAT - SVPublisher_ASDU_setFLOAT - SVPublisher_ASDU_addFLOAT64 - SVPublisher_ASDU_setFLOAT64 - SVPublisher_ASDU_setSmpCnt - SVPublisher_ASDU_getSmpCnt - SVPublisher_ASDU_increaseSmpCnt - SVPublisher_ASDU_setRefrTm - SVPublisher_ASDU_setSmpMod - SVPublisher_ASDU_setSmpRate + SampledValuesPublisher_create + SampledValuesPublisher_addASDU + SampledValuesPublisher_setupComplete + SampledValuesPublisher_publish + SampledValuesPublisher_destroy + SV_ASDU_resetBuffer + SV_ASDU_addINT8 + SV_ASDU_setINT8 + SV_ASDU_addINT32 + SV_ASDU_setINT32 + SV_ASDU_addFLOAT + SV_ASDU_setFLOAT + SV_ASDU_setSmpCnt + SV_ASDU_increaseSmpCnt + SV_ASDU_setRefrTm + SV_ASDU_setSmpMod MmsValue_printToBuffer CDC_CTE_create CDC_SPV_create @@ -631,6 +621,8 @@ EXPORTS IedConnection_queryLogByTime IedConnection_queryLogAfter CDC_DPL_create + SV_ASDU_addFLOAT64 + SV_ASDU_setFLOAT64 MmsConnection_setRawMessageHandler ModelNode_getType ControlObjectClient_setTestMode @@ -655,6 +647,8 @@ EXPORTS GooseReceiver_isRunning IedModel_getDeviceByInst MmsConnection_writeNamedVariableList + SVClientASDU_hasRefrTm + SVClientASDU_getRefrTmAsMs IedConnection_writeDataSetValues MmsVariableSpecification_isValueOfType IedServer_udpateDbposValue @@ -662,27 +656,96 @@ EXPORTS MmsServer_isRunning IedServer_createWithTlsSupport IedConnection_createWithTlsSupport - ClientGooseControlBlock_create - ClientGooseControlBlock_destroy - IedConnection_getGoCBValues - IedConnection_setGoCBValues - ClientGooseControlBlock_getGoEna - ClientGooseControlBlock_setGoEna - ClientGooseControlBlock_getDatSet - ClientGooseControlBlock_setDatSet - ClientGooseControlBlock_getGoID - ClientGooseControlBlock_setGoID - ClientGooseControlBlock_getConfRev - ClientGooseControlBlock_getNdsComm - ClientGooseControlBlock_getMinTime - ClientGooseControlBlock_getMaxTime - ClientGooseControlBlock_getFixedOffs - ClientGooseControlBlock_getDstAddress_addr - ClientGooseControlBlock_setDstAddress_addr - ClientGooseControlBlock_getDstAddress_priority - ClientGooseControlBlock_setDstAddress_priority - ClientGooseControlBlock_getDstAddress_vid - ClientGooseControlBlock_setDstAddress_vid - ClientGooseControlBlock_getDstAddress_appid - ClientGooseControlBlock_setDstAddress_appid - DataAttribute_setValue \ No newline at end of file + ClientGooseControlBlock_getDstAddress + ClientGooseControlBlock_setDstAddress + SVPublisher_create + SVPublisher_addASDU + SVPublisher_setupComplete + SVPublisher_publish + SVPublisher_destroy + SVPublisher_ASDU_resetBuffer + SVPublisher_ASDU_addINT8 + SVPublisher_ASDU_setINT8 + SVPublisher_ASDU_addINT32 + SVPublisher_ASDU_setINT32 + SVPublisher_ASDU_addINT64 + SVPublisher_ASDU_setINT64 + SVPublisher_ASDU_addFLOAT + SVPublisher_ASDU_setFLOAT + SVPublisher_ASDU_addFLOAT64 + SVPublisher_ASDU_setFLOAT64 + SVPublisher_ASDU_setSmpCnt + SVPublisher_ASDU_getSmpCnt + SVPublisher_ASDU_increaseSmpCnt + SVPublisher_ASDU_setRefrTm + SVPublisher_ASDU_setSmpMod + SVPublisher_ASDU_setSmpRate + SVSubscriber_ASDU_getSmpCnt + SVSubscriber_ASDU_getSvId + SVSubscriber_ASDU_getConfRev + SVSubscriber_ASDU_hasRefrTm + SVSubscriber_ASDU_getRefrTmAsMs + SVSubscriber_ASDU_getINT8 + SVSubscriber_ASDU_getINT16 + SVSubscriber_ASDU_getINT32 + SVSubscriber_ASDU_getINT64 + SVSubscriber_ASDU_getINT8U + SVSubscriber_ASDU_getINT16U + SVSubscriber_ASDU_getINT32U + SVSubscriber_ASDU_getINT64U + SVSubscriber_ASDU_getFLOAT32 + SVSubscriber_ASDU_getFLOAT64 + SVSubscriber_ASDU_getDataSize + CDC_VSS_create + CDC_VSG_create + SVReceiver_isRunning + SVSubscriber_ASDU_getTimestamp + SVPublisher_ASDU_addTimestamp + SVPublisher_ASDU_setTimestamp + SVSubscriber_ASDU_getQuality + SVPublisher_ASDU_addQuality + SVPublisher_ASDU_setQuality + Timestamp_createFromByteArray + IedModel_getDeviceByIndex + SVReceiver_enableDestAddrCheck + IedServerConfig_create + IedServerConfig_destroy + IedServerConfig_setReportBufferSize + IedServerConfig_getReportBufferSize + IedServer_createWithConfig + IedServerConfig_setFileServiceBasePath + IedServerConfig_getFileServiceBasePath + ClientGooseControlBlock_create + ClientGooseControlBlock_destroy + IedConnection_getGoCBValues + IedConnection_setGoCBValues + ClientGooseControlBlock_getGoEna + ClientGooseControlBlock_setGoEna + ClientGooseControlBlock_getGoID + ClientGooseControlBlock_setGoID + ClientGooseControlBlock_getDatSet + ClientGooseControlBlock_setDatSet + ClientGooseControlBlock_getConfRev + ClientGooseControlBlock_getNdsComm + ClientGooseControlBlock_getMinTime + ClientGooseControlBlock_getMaxTime + ClientGooseControlBlock_getFixedOffs + ControlObjectClient_getCtlValType + SVPublisher_ASDU_setSmpCntWrap + IedServerConfig_enableFileService + IedServerConfig_isFileServiceEnabled + IedServerConfig_enableDynamicDataSetService + IedServerConfig_isDynamicDataSetServiceEnabled + IedServerConfig_enableLogService + IedServerConfig_isLogServiceEnabled + IedServerConfig_setEdition + IedServerConfig_getEdition + IedServerConfig_setMaxMmsConnections + IedServerConfig_getMaxMmsConnections + IedServerConfig_setMaxDataSetEntries + IedServerConfig_getMaxDatasSetEntries + IedServerConfig_setMaxAssociationSpecificDataSets + IedServerConfig_getMaxAssociationSpecificDataSets + IedServerConfig_setMaxDomainSpecificDataSets + IedServerConfig_getMaxDomainSpecificDataSets + IedServer_setReadAccessHandler diff --git a/tools/model_generator/genconfig.jar b/tools/model_generator/genconfig.jar index f50b47c88..7fbf3f964 100644 Binary files a/tools/model_generator/genconfig.jar and b/tools/model_generator/genconfig.jar differ diff --git a/tools/model_generator/gendyncode.jar b/tools/model_generator/gendyncode.jar index ba2bd0faf..a0d686f45 100644 Binary files a/tools/model_generator/gendyncode.jar and b/tools/model_generator/gendyncode.jar differ diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar index 6c45543a5..5554c798f 100644 Binary files a/tools/model_generator/genmodel.jar and b/tools/model_generator/genmodel.jar differ diff --git a/tools/model_generator/modelviewer.jar b/tools/model_generator/modelviewer.jar index 4c6360d56..50a70924e 100644 Binary files a/tools/model_generator/modelviewer.jar and b/tools/model_generator/modelviewer.jar differ diff --git a/tools/model_generator/src/com/libiec61850/scl/SclParser.java b/tools/model_generator/src/com/libiec61850/scl/SclParser.java index 4ed22e511..fc73748d0 100644 --- a/tools/model_generator/src/com/libiec61850/scl/SclParser.java +++ b/tools/model_generator/src/com/libiec61850/scl/SclParser.java @@ -27,6 +27,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Stack; @@ -57,8 +58,8 @@ import com.libiec61850.scl.types.LogicalNodeType; import com.libiec61850.scl.types.TypeDeclarations; -public class SclParser { - +public class SclParser +{ private List ieds; private Communication communication; private TypeDeclarations typeDeclarations; @@ -111,6 +112,11 @@ public IED getIedByName(String iedName) { return null; } + public Collection getIeds() + { + return ieds; + } + public IED getFirstIed() { return ieds.get(0); } @@ -314,4 +320,4 @@ public ConnectedAP getConnectedAP(IED ied, String accessPointName) { return null; } -} \ No newline at end of file +} diff --git a/tools/model_generator/src/com/libiec61850/scl/model/AccessPoint.java b/tools/model_generator/src/com/libiec61850/scl/model/AccessPoint.java index 91fec8134..f506caa61 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/AccessPoint.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/AccessPoint.java @@ -43,9 +43,9 @@ public AccessPoint(Node apNode, TypeDeclarations typeDeclarations) throws SclPar Node serverNode = ParserUtils.getChildNodeWithTag(apNode, "Server"); if (serverNode == null) - throw new SclParserException(apNode, "AccessPoint has no server defined!"); - - this.server = new Server(serverNode, typeDeclarations); + this.server = null; + else + this.server = new Server(serverNode, typeDeclarations); } diff --git a/tools/model_generator/src/com/libiec61850/scl/model/AttributeType.java b/tools/model_generator/src/com/libiec61850/scl/model/AttributeType.java index 0d0e1f8f4..9bde75a58 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/AttributeType.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/AttributeType.java @@ -56,7 +56,9 @@ public enum AttributeType { CONSTRUCTED(27), ENTRY_TIME(28), PHYCOMADDR(29), - CURRENCY(30); + CURRENCY(30), + OPTFLDS(31), + TRGOPS(32); private int intValue; @@ -125,9 +127,9 @@ else if (typeString.equals("VisString255")) else if (typeString.equals("Unicode255")) return UNICODE_STRING_255; else if (typeString.equals("OptFlds")) - return GENERIC_BITSTRING; + return OPTFLDS; else if (typeString.equals("TrgOps")) - return GENERIC_BITSTRING; + return TRGOPS; else if (typeString.equals("EntryID")) return OCTET_STRING_8; else if (typeString.equals("EntryTime")) diff --git a/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java b/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java index 079804647..4efc1e38c 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java @@ -102,35 +102,39 @@ public DataModelValue(AttributeType type, SclType sclType, String value) throws this.value = new Boolean(false); break; case FLOAT32: - - trimmedValue = value.trim(); - - if (trimmedValue != value) { - System.out.println("WARNING: value initializer contains leading or trailing whitespace"); - } - - if (trimmedValue.isEmpty()) - this.value = new Float(0); - else - this.value = new Float(trimmedValue); - break; + + trimmedValue = value.trim(); + + if (trimmedValue != value) { + System.out.println("WARNING: value initializer contains leading or trailing whitespace"); + } + + trimmedValue.replace(',', '.'); + + if (trimmedValue.isEmpty()) + this.value = new Float(0); + else + this.value = new Float(trimmedValue); + break; case FLOAT64: - - trimmedValue = value.trim(); - - if (trimmedValue != value) { - System.out.println("WARNING: value initializer contains leading or trailing whitespace"); - } - - if (trimmedValue.isEmpty()) - this.value = new Double(0); - else - this.value = new Double(trimmedValue); - break; + + trimmedValue = value.trim(); + + if (trimmedValue != value) { + System.out.println("WARNING: value initializer contains leading or trailing whitespace"); + } + + trimmedValue.replace(',', '.'); + + if (trimmedValue.isEmpty()) + this.value = new Double(0); + else + this.value = new Double(trimmedValue); + break; case UNICODE_STRING_255: - this.value = value; - break; - + this.value = value; + break; + case OCTET_STRING_64: try { this.value = Base64.getDecoder().decode(value); diff --git a/tools/model_generator/src/com/libiec61850/scl/model/IED.java b/tools/model_generator/src/com/libiec61850/scl/model/IED.java index fb381aeb2..45936a049 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/IED.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/IED.java @@ -41,9 +41,6 @@ public IED(Node iedNode, TypeDeclarations typeDeclarations) List accessPointNodes = ParserUtils.getChildNodesWithTag(iedNode, "AccessPoint"); - if (accessPointNodes.size() == 0) - throw new SclParserException(iedNode, "no AccessPoint defined in IED " + name); - this.accessPoints = new LinkedList(); for (Node accessPointNode : accessPointNodes) {