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²
+ m³
+ 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
+ V²
+ As
+ A²
+ 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) {