- update to current development version

pull/6/head
Michael Zillgith 11 years ago
parent 48d717f12c
commit a0f67f9a5d

@ -1,3 +1,39 @@
Changes to version 0.8.2
------------------------
- Client: Added adjustable timeout to connect functions
- Client: Support for T selectors with different size than two bytes
- GOOSE publisher: added support for explicit GoID
- GOOSE subscriber: refactored subscriber
- Server: Added support for setting groups
- Server: Added support for data references in reports
- SCL parser: support for "Val" attributes in DA type definitions
- Server: Added function to disable all GOOSE publishing
- Client/Server: added helper functions for Dbpos
- C# API: more features and improved stability
Changes to version 0.8.1
------------------------
- IEC 61850/MMS client: IedConnection and MmsConnection objects can now be reused
- reorganization of library header files
- C# API provides support for bit string and octet string data type
- IEC 61850 client/server: Added support for CommandTermination-
- C# API added handler for CommandTermination messages
- IEC 61850 server: IED name for static device model can now be configured at runtime
- Client/server: Fixed problem with association specific data sets
- GOOSE: fixed triggering GOOSE reports for data set members without trigger condition set
- Linux/Windows/BSD: added support for select based socket read (reduces CPU load)
- C# API added support for file services
- a lot of small bug fixes
Changes to version 0.8
-----------------------
- HAL: socket layer. Some changes. read and accept are now non-blocking. Changes the standard implementations (Linux/POSIX, WIN32, BSD) accordingly
- server stack: added single-threaded and threadless operation modes to better fit to resource constraint devices (added new configuration option CONFIG_MMS_SINGLE_THREADED)
- some small changes in server side control model handling (ControlHandler return values and behaviour) to enable threadless and single-threaded operation.
- C# client API extended and XML documentation completed
- some smaller bug fixes and extensions
Changes to version 0.7.8
------------------------
- IED client: added client side support for Ed.1 compliant control (client side automatically detects if a control is ed1 or ed2 compliant)

@ -10,8 +10,8 @@ endif()
project(libiec61850)
set(LIB_VERSION_MAJOR "0")
set(LIB_VERSION_MINOR "7")
set(LIB_VERSION_PATCH "8")
set(LIB_VERSION_MINOR "8")
set(LIB_VERSION_PATCH "2")
# feature checks
include(CheckLibraryExists)
@ -26,6 +26,9 @@ set(CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS 5 CACHE STRING "Configure the maximum
option(BUILD_EXAMPLES "Build the examples" ON)
option(CONFIG_MMS_SINGLE_THREADED "Compile for single threaded version" OFF)
option(CONFIG_MMS_THREADLESS_STACK "Optimize stack for threadless operation (warning: single- or multi-threaded server will not work!)" OFF)
# choose the library features which shall be included
option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" ON)
@ -33,12 +36,15 @@ option(CONFIG_IEC61850_CONTROL_SERVICE "Build with support for IEC 61850 control
option(CONFIG_IEC61850_REPORT_SERVICE "Build with support for IEC 61850 reporting services" ON)
option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON)
option(CONFIG_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON)
set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "8000" CACHE STRING "Default buffer size for buffered reports in byte" )
# advanced options
option(DEBUG "Enable debugging mode (include assertions)" OFF)
option(DEBUG_SOCKET "Enable printf debugging for socket layer" OFF)
option(DEBUG_COTP "Enable COTP printf debugging" OFF)
option(DEBUG_ISO_SERVER "Enable ISO SERVER printf debugging" OFF)
option(DEBUG_ISO_CLIENT "Enable ISO CLIENT printf debugging" OFF)
@ -51,64 +57,45 @@ option(DEBUG_MMS_CLIENT "Enable MMS CLIENT printf debugging" OFF)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}/config
src/common
src/common/inc
src/goose
src/hal
src/hal/ethernet
src/hal/socket
src/hal/thread
src/hal/filesystem
src/hal/time
src/iedclient
src/iedcommon
src/iedserver
src/iedserver/impl
src/iedserver/mms_mapping
src/iedserver/model
src/mms/asn1
src/mms/iso_acse
src/mms/iso_client
src/mms/iso_cotp
src/hal/inc
src/iec61850/inc
src/iec61850/inc_private
src/mms/inc
src/mms/inc_private
src/mms/iso_mms/asn1c
src/mms/iso_mms/client
src/mms/iso_mms/common
src/mms/iso_mms/server
src/mms/iso_presentation
src/mms/iso_server
src/mms/iso_common
src/mms/iso_session
)
set(API_HEADERS
src/hal/time/time_hal.h
src/hal/ethernet/ethernet.h
src/hal/thread/thread.h
src/hal/filesystem/filesystem.h
src/common/libiec61850_common_api.h
src/common/linked_list.h
src/common/byte_buffer.h
src/iedclient/iec61850_client.h
src/iedcommon/iec61850_common.h
src/iedserver/iec61850_server.h
src/iedserver/model/model.h
src/iedserver/model/cdc.h
src/iedserver/model/dynamic_model.h
src/iedserver/model/config_file_parser.h
src/mms/iso_mms/common/mms_value.h
src/mms/iso_mms/common/mms_common.h
src/mms/iso_mms/common/mms_types.h
src/mms/iso_mms/server/mms_device_model.h
src/mms/iso_mms/server/mms_server.h
src/mms/iso_mms/server/mms_named_variable_list.h
src/mms/iso_mms/common/mms_type_spec.h
src/mms/asn1/ber_integer.h
src/mms/asn1/asn1_ber_primitive_value.h
src/mms/iso_server/iso_server.h
src/mms/iso_common/iso_connection_parameters.h
src/hal/inc/hal_time.h
src/hal/inc/hal_thread.h
src/hal/inc/hal_filesystem.h
src/common/inc/libiec61850_common_api.h
src/common/inc/linked_list.h
src/common/inc/byte_buffer.h
src/common/inc/lib_memory.h
src/iec61850/inc/iec61850_client.h
src/iec61850/inc/iec61850_common.h
src/iec61850/inc/iec61850_server.h
src/iec61850/inc/iec61850_model.h
src/iec61850/inc/iec61850_cdc.h
src/iec61850/inc/iec61850_dynamic_model.h
src/iec61850/inc/iec61850_config_file_parser.h
src/mms/inc/mms_value.h
src/mms/inc/mms_common.h
src/mms/inc/mms_types.h
src/mms/inc/mms_device_model.h
src/mms/inc/mms_server.h
src/mms/inc/mms_named_variable_list.h
src/mms/inc/mms_type_spec.h
src/mms/inc/mms_client_connection.h
src/mms/inc/iso_connection_parameters.h
src/mms/inc/iso_server.h
src/mms/inc/ber_integer.h
src/mms/inc/asn1_ber_primitive_value.h
src/goose/goose_subscriber.h
src/mms/iso_mms/client/mms_client_connection.h
src/mms/iso_client/iso_client_connection.h
src/hal/socket/socket.h
src/goose/goose_receiver.h
)
IF(WIN32)
@ -129,3 +116,30 @@ add_subdirectory(src)
INSTALL(FILES ${API_HEADERS} DESTINATION include/libiec61850)
IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
INCLUDE(InstallRequiredSystemLibraries)
SET(CPACK_SET_DESTDIR "on")
SET(CPACK_INSTALL_PREFIX "/usr")
SET(CPACK_GENERATOR "DEB")
SET(CPACK_PACKAGE_DESCRIPTION "IEC 61850 MMS/GOOSE client and server library")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IEC 61850 MMS/GOOSE client and server library")
SET(CPACK_PACKAGE_VENDOR "The libIEC61850 project")
SET(CPACK_PACKAGE_CONTACT "info@libiec61850.com")
SET(CPACK_PACKAGE_VERSION_MAJOR "${LIB_VERSION_MAJOR}")
SET(CPACK_PACKAGE_VERSION_MINOR "${LIB_VERSION_MINOR}")
SET(CPACK_PACKAGE_VERSION_PATCH "${LIB_VERSION_PATCH}")
SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CMAKE_SYSTEM_PROCESSOR}")
SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
SET(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
SET(CPACK_COMPONENTS_ALL Libraries ApplicationData)
INCLUDE(CPack)
ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")

@ -12,7 +12,7 @@ LIB_SOURCE_DIRS += src/mms/asn1
LIB_SOURCE_DIRS += src/mms/iso_cotp
LIB_SOURCE_DIRS += src/mms/iso_mms/server
LIB_SOURCE_DIRS += src/mms/iso_mms/client
LIB_SOURCE_DIRS += src/mms/iso_client/impl
LIB_SOURCE_DIRS += src/mms/iso_client
LIB_SOURCE_DIRS += src/mms/iso_common
LIB_SOURCE_DIRS += src/mms/iso_mms/common
LIB_SOURCE_DIRS += src/mms/iso_mms/asn1c
@ -20,13 +20,12 @@ LIB_SOURCE_DIRS += src/mms/iso_server
ifndef EXCLUDE_ETHERNET_WINDOWS
LIB_SOURCE_DIRS += src/goose
endif
LIB_SOURCE_DIRS += src/iedclient/impl
LIB_SOURCE_DIRS += src/iedcommon
LIB_SOURCE_DIRS += src/iedserver
LIB_SOURCE_DIRS += src/iedserver/model
LIB_SOURCE_DIRS += src/iedserver/mms_mapping
LIB_SOURCE_DIRS += src/iedserver/impl
LIB_SOURCE_DIRS += src/hal
LIB_SOURCE_DIRS += src/iec61850/client
LIB_SOURCE_DIRS += src/iec61850/common
LIB_SOURCE_DIRS += src/iec61850/server
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
@ -46,34 +45,15 @@ LIB_SOURCE_DIRS += src/hal/ethernet/bsd
LIB_SOURCE_DIRS += src/hal/filesystem/linux
LIB_SOURCE_DIRS += src/hal/time/unix
endif
LIB_INCLUDE_DIRS += src/mms/iso_presentation
LIB_INCLUDE_DIRS += src/mms/iso_session
LIB_INCLUDE_DIRS += src/mms/iso_cotp
LIB_INCLUDE_DIRS += src/mms/iso_acse
LIB_INCLUDE_DIRS += config
LIB_INCLUDE_DIRS += src/mms/asn1
LIB_INCLUDE_DIRS += src/mms/iso_client
LIB_INCLUDE_DIRS += src/mms/iso_mms/server
LIB_INCLUDE_DIRS += src/mms/iso_mms/common
LIB_INCLUDE_DIRS += src/mms/iso_mms/client
LIB_INCLUDE_DIRS += src/mms/iso_mms/asn1c
LIB_INCLUDE_DIRS += src/common
LIB_INCLUDE_DIRS += src/hal/socket
LIB_INCLUDE_DIRS += src/hal/thread
LIB_INCLUDE_DIRS += src/hal/ethernet
LIB_INCLUDE_DIRS += src/hal/filesystem
LIB_INCLUDE_DIRS += src/hal/time
LIB_INCLUDE_DIRS += src/hal
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/mms/iso_server
LIB_INCLUDE_DIRS += src/mms/iso_common
LIB_INCLUDE_DIRS += src/iedclient
LIB_INCLUDE_DIRS += src/iedcommon
LIB_INCLUDE_DIRS += src/iedserver
LIB_INCLUDE_DIRS += src/iedserver/model
LIB_INCLUDE_DIRS += src/iedserver/mms_mapping
LIB_INCLUDE_DIRS += src/iedserver/impl
LIB_INCLUDE_DIRS += src/iec61850/inc
LIB_INCLUDE_DIRS += src/iec61850/inc_private
ifeq ($(HAL_IMPL), WIN32)
LIB_INCLUDE_DIRS += third_party/winpcap/Include
endif
@ -84,35 +64,34 @@ ifndef INSTALL_PREFIX
INSTALL_PREFIX = ./.install
endif
LIB_API_HEADER_FILES = src/hal/time/time_hal.h
LIB_API_HEADER_FILES += src/hal/ethernet/ethernet.h
LIB_API_HEADER_FILES += src/hal/thread/thread.h
LIB_API_HEADER_FILES += src/hal/filesystem/filesystem.h
LIB_API_HEADER_FILES += src/common/libiec61850_common_api.h
LIB_API_HEADER_FILES += src/common/linked_list.h
LIB_API_HEADER_FILES += src/common/byte_buffer.h
LIB_API_HEADER_FILES += src/iedclient/iec61850_client.h
LIB_API_HEADER_FILES += src/iedcommon/iec61850_common.h
LIB_API_HEADER_FILES += src/iedserver/iec61850_server.h
LIB_API_HEADER_FILES += src/iedserver/model/model.h
LIB_API_HEADER_FILES += src/iedserver/model/cdc.h
LIB_API_HEADER_FILES += src/iedserver/model/dynamic_model.h
LIB_API_HEADER_FILES += src/iedserver/model/config_file_parser.h
LIB_API_HEADER_FILES += src/mms/iso_mms/common/mms_value.h
LIB_API_HEADER_FILES += src/mms/iso_mms/common/mms_common.h
LIB_API_HEADER_FILES += src/mms/iso_mms/common/mms_types.h
LIB_API_HEADER_FILES += src/mms/iso_mms/server/mms_device_model.h
LIB_API_HEADER_FILES += src/mms/iso_mms/server/mms_server.h
LIB_API_HEADER_FILES += src/mms/iso_mms/server/mms_named_variable_list.h
LIB_API_HEADER_FILES += src/mms/iso_mms/common/mms_type_spec.h
LIB_API_HEADER_FILES += src/mms/asn1/ber_integer.h
LIB_API_HEADER_FILES += src/mms/asn1/asn1_ber_primitive_value.h
LIB_API_HEADER_FILES += src/mms/iso_server/iso_server.h
LIB_API_HEADER_FILES += src/mms/iso_common/iso_connection_parameters.h
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 += 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
LIB_API_HEADER_FILES += src/common/inc/lib_memory.h
LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_client.h
LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_common.h
LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_server.h
LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_model.h
LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_cdc.h
LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_dynamic_model.h
LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_config_file_parser.h
LIB_API_HEADER_FILES += src/mms/inc/mms_value.h
LIB_API_HEADER_FILES += src/mms/inc/mms_common.h
LIB_API_HEADER_FILES += src/mms/inc/mms_types.h
LIB_API_HEADER_FILES += src/mms/inc/mms_device_model.h
LIB_API_HEADER_FILES += src/mms/inc/mms_server.h
LIB_API_HEADER_FILES += src/mms/inc/mms_named_variable_list.h
LIB_API_HEADER_FILES += src/mms/inc/mms_type_spec.h
LIB_API_HEADER_FILES += src/mms/inc/mms_client_connection.h
LIB_API_HEADER_FILES += src/mms/inc/iso_connection_parameters.h
LIB_API_HEADER_FILES += src/mms/inc/iso_server.h
LIB_API_HEADER_FILES += src/mms/inc/ber_integer.h
LIB_API_HEADER_FILES += src/mms/inc/asn1_ber_primitive_value.h
LIB_API_HEADER_FILES += src/goose/goose_subscriber.h
LIB_API_HEADER_FILES += src/mms/iso_mms/client/mms_client_connection.h
LIB_API_HEADER_FILES += src/mms/iso_client/iso_client_connection.h
LIB_API_HEADER_FILES += src/hal/socket/socket.h
LIB_API_HEADER_FILES += src/goose/goose_receiver.h
get_sources_from_directory = $(wildcard $1/*.c)
get_sources = $(foreach dir, $1, $(call get_sources_from_directory,$(dir)))

@ -10,9 +10,10 @@
#define STACK_CONFIG_H_
/* include asserts if set to 1 */
#define DEBUG 0
#define DEBUG 1
/* print debugging information with printf if set to 1 */
#define DEBUG_SOCKET 0
#define DEBUG_COTP 0
#define DEBUG_ISO_SERVER 0
#define DEBUG_ISO_CLIENT 0
@ -20,10 +21,28 @@
#define DEBUG_IED_CLIENT 0
#define DEBUG_MMS_CLIENT 0
#define DEBUG_MMS_SERVER 0
#define DEBUG_GOOSE_SUBSCRIBER 0
#define DEBUG_GOOSE_PUBLISHER 0
/* Maximum MMS PDU SIZE - default is 65000 */
#define CONFIG_MMS_MAXIMUM_PDU_SIZE 65000
/*
* Enable single threaded mode
*
* 1 ==> server runs in single threaded mode (a single thread for the server and all client connections)
* 0 ==> server runs in multi-threaded mode (one thread for each connection and
* one server background thread )
*/
#define CONFIG_MMS_SINGLE_THREADED 1
/*
* Optimize stack for threadless operation - don't use semaphores
*
* WARNING: If set to 1 normal single- and multi-threaded server are no longer working!
*/
#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
@ -48,6 +67,7 @@
/* Ethernet interface ID for GOOSE and SV */
#define CONFIG_ETHERNET_INTERFACE_ID "eth0"
//#define CONFIG_ETHERNET_INTERFACE_ID "vboxnet0"
//#define CONFIG_ETHERNET_INTERFACE_ID "eth-f"
//#define CONFIG_ETHERNET_INTERFACE_ID "en0" // OS X uses enX in place of ethX as ethernet NIC names.
/* Set to 1 to include GOOSE support in the build. Otherwise set to 0 */
@ -102,10 +122,16 @@
/* The default buffer size of buffered RCBs in bytes */
#define CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE 65536
/* include support for setting groups */
#define CONFIG_IEC61850_SETTING_GROUPS 1
/* default reservation time of a setting group control block in s */
#define CONFIG_IEC61850_SG_RESVTMS 10
/* default results for MMS identify service */
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"
#define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850"
#define CONFIG_DEFAULT_MMS_REVISION "0.7.8"
#define CONFIG_DEFAULT_MMS_REVISION "0.8.2"
/* MMS virtual file store base path - where file services are looking for files */
#define CONFIG_VIRTUAL_FILESTORE_BASEPATH "./vmd-filestore/"
@ -113,6 +139,12 @@
/* Maximum number of open file per MMS connection (for MMS file read service) */
#define CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION 5
#define CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS 10
#define CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS 10
#define CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS 10
/* Definition of supported services */
#define MMS_DEFAULT_PROFILE 1
@ -140,4 +172,6 @@
/* VMD scope named variables are not used by IEC 61850 */
#define CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES 0
#define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0
#endif /* STACK_CONFIG_H_ */

@ -19,6 +19,7 @@
#cmakedefine01 DEBUG
/* print debugging information with printf if set to 1 */
#cmakedefine01 DEBUG_SOCKET
#cmakedefine01 DEBUG_COTP
#cmakedefine01 DEBUG_ISO_SERVER
#cmakedefine01 DEBUG_ISO_CLIENT
@ -26,6 +27,17 @@
#cmakedefine01 DEBUG_IED_CLIENT
#cmakedefine01 DEBUG_MMS_CLIENT
#cmakedefine01 DEBUG_MMS_SERVER
#cmakedefine01 DEBUG_GOOSE_SUBSCRIBER
#cmakedefine01 DEBUG_GOOSE_PUBLISHER
/* 1 ==> server runs in single threaded mode (one dedicated thread for the server)
* 0 ==> server runs in multi threaded mode (one thread for each connection and
* one server background thread )
*/
#cmakedefine01 CONFIG_MMS_SINGLE_THREADED
/* Optimize stack for threadless operation - don't use semaphores */
#cmakedefine01 CONFIG_MMS_THREADLESS_STACK
/* Maximum MMS PDU SIZE - default is 65000 */
#cmakedefine CONFIG_MMS_MAXIMUM_PDU_SIZE @CONFIG_MMS_MAXIMUM_PDU_SIZE@
@ -52,9 +64,9 @@
#define CONFIG_TCP_READ_TIMEOUT_MS 1000
/* Ethernet interface ID for GOOSE and SV */
//#define CONFIG_ETHERNET_INTERFACE_ID "eth0"
#define CONFIG_ETHERNET_INTERFACE_ID "eth0"
//#define CONFIG_ETHERNET_INTERFACE_ID "vboxnet0"
#define CONFIG_ETHERNET_INTERFACE_ID "en0" // OS X uses enX in place of ethX as ethernet NIC names.
//#define CONFIG_ETHERNET_INTERFACE_ID "en0" // OS X uses enX in place of ethX as ethernet NIC names.
/* Set to 1 to include GOOSE support in the build. Otherwise set to 0 */
#cmakedefine01 CONFIG_INCLUDE_GOOSE_SUPPORT
@ -108,6 +120,12 @@
/* The default buffer size of buffered RCBs in bytes */
#cmakedefine CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE @CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE@
/* include support for setting groups */
#cmakedefine01 CONFIG_IEC61850_SETTING_GROUPS
/* default reservation time of a setting group control block in s */
#define CONFIG_IEC61850_SG_RESVTMS 100
/* default results for MMS identify service */
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"
#define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850"
@ -125,6 +143,12 @@
/* Maximum number of open file per MMS connection (for MMS file read service) */
#define CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION 5
#define CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS 10
#define CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS 10
#define CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS 10
/* Definition of supported services */
#define MMS_DEFAULT_PROFILE 1

@ -150,15 +150,12 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t;
extern DataSet ds_GenericIO_LLN0_Events;
static DataSetEntry ds_GenericIO_LLN0_Events_fcda0;
static DataSetEntry ds_GenericIO_LLN0_Events_fcda1;
static DataSetEntry ds_GenericIO_LLN0_Events_fcda2;
static DataSetEntry ds_GenericIO_LLN0_Events_fcda3;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda0;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda1;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda2;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda3;
DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
static DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
"beagleGenericIO",
"GGIO1$ST$SPCSO1$stVal",
-1,
@ -167,7 +164,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
&ds_GenericIO_LLN0_Events_fcda1
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
static DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
"beagleGenericIO",
"GGIO1$ST$SPCSO2$stVal",
-1,
@ -176,7 +173,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
&ds_GenericIO_LLN0_Events_fcda2
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
static DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
"beagleGenericIO",
"GGIO1$ST$SPCSO3$stVal",
-1,
@ -185,7 +182,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
&ds_GenericIO_LLN0_Events_fcda3
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
static DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
"beagleGenericIO",
"GGIO1$ST$DPCSO1$stVal",
-1,
@ -194,12 +191,11 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
NULL
};
DataSet ds_GenericIO_LLN0_Events = {
static DataSet ds_GenericIO_LLN0_Events = {
"beagleGenericIO",
"LLN0$Events",
4,
&ds_GenericIO_LLN0_Events_fcda0,
NULL
&ds_GenericIO_LLN0_Events_fcda0
};
LogicalDevice iedModel_GenericIO = {
@ -1923,11 +1919,22 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = {
NULL,
0};
extern ReportControlBlock iedModel_GenericIO_LLN0_report0;
extern ReportControlBlock iedModel_GenericIO_LLN0_report1;
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 8, 111, 50, 1000, &iedModel_GenericIO_LLN0_report1};
ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsRCB201", "Events2", false, "Events", 1, 8, 111, 50, 1000, NULL};
static ReportControlBlock iedModel_GenericIO_LLN0_report0;
static ReportControlBlock iedModel_GenericIO_LLN0_report1;
static ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB", "Events1", false, "Events", 1, 16, 111, 50, 1000, &iedModel_GenericIO_LLN0_report1};
static ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsRCB2", "Events2", false, "Events", 1, 16, 111, 50, 1000, NULL};

@ -29,8 +29,14 @@ using IEC61850.Common;
namespace IEC61850
{
/// <summary>
/// IEC 61850 common API parts (used by client and server API)
/// </summary>
namespace Common {
/// <summary>
/// Control model
/// </summary>
public enum ControlModel {
CONTROL_MODEL_STATUS_ONLY = 0,
CONTROL_MODEL_DIRECT_NORMAL = 1,
@ -38,39 +44,142 @@ namespace IEC61850
CONTROL_MODEL_DIRECT_ENHANCED = 3,
CONTROL_MODEL_SBO_ENHANCED = 4
}
/// <summary>
/// Originator category
/// </summary>
public enum OrCat {
/** Not supported - should not be used */
NOT_SUPPORTED = 0,
/** Control operation issued from an operator using a client located at bay level */
BAY_CONTROL = 1,
/** Control operation issued from an operator using a client located at station level */
STATION_CONTROL = 2,
/** Control operation from a remote operator outside the substation (for example network control center) */
REMOTE_CONTROL = 3,
/** Control operation issued from an automatic function at bay level */
AUTOMATIC_BAY = 4,
/** Control operation issued from an automatic function at station level */
AUTOMATIC_STATION = 5,
/** Control operation issued from a automatic function outside of the substation */
AUTOMATIC_REMOTE = 6,
/** Control operation issued from a maintenance/service tool */
MAINTENANCE = 7,
/** Status change occurred without control action (for example external trip of a circuit breaker or failure inside the breaker) */
PROCESS = 8
}
}
namespace Client {
[StructLayout(LayoutKind.Sequential)]
internal struct LastApplErrorInternal
{
public int ctlNum;
public int error;
public int addCause;
}
public class LastApplError
{
public int ctlNum;
public int error;
public ControlAddCause addCause;
internal LastApplError (LastApplErrorInternal lastApplError)
{
this.addCause = (ControlAddCause) lastApplError.addCause;
this.error = lastApplError.error;
this.ctlNum = lastApplError.ctlNum;
}
}
/// <summary>
/// Control object.
/// </summary>
public class ControlObject
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ControlObjectClient_create(string objectReference, IntPtr connection);
private static extern LastApplErrorInternal ControlObjectClient_getLastApplError(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ControlObjectClient_destroy(IntPtr self);
private static extern IntPtr ControlObjectClient_create(string objectReference, IntPtr connection);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ControlObjectClient_getControlModel(IntPtr self);
private static extern void ControlObjectClient_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ControlObjectClient_operate(IntPtr self, IntPtr ctlVal, UInt64 operTime);
private static extern int ControlObjectClient_getControlModel(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern bool ControlObjectClient_operate(IntPtr self, IntPtr ctlVal, UInt64 operTime);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern bool ControlObjectClient_select(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern bool ControlObjectClient_selectWithValue(IntPtr self, IntPtr ctlVal);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern bool ControlObjectClient_cancel(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void ControlObjectClient_setOrigin(IntPtr self, string orIdent, int orCat);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void ControlObjectClient_enableInterlockCheck(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void ControlObjectClient_enableSynchroCheck(IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalCommandTerminationHandler(IntPtr parameter,IntPtr controlClient);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void ControlObjectClient_setCommandTerminationHandler(IntPtr self,
InternalCommandTerminationHandler handler, IntPtr handlerParameter);
public delegate void CommandTerminationHandler (Object parameter, ControlObject controlObject);
private IntPtr controlObject;
private CommandTerminationHandler commandTerminationHandler = null;
private Object commandTerminationHandlerParameter = null;
private void MyCommandTerminationHandler (IntPtr paramter, IntPtr controlClient)
{
if (commandTerminationHandler != null)
commandTerminationHandler(commandTerminationHandlerParameter, this);
}
internal ControlObject (string objectReference, IntPtr connection)
{
this.controlObject = ControlObjectClient_create(objectReference, connection);
if (this.controlObject == System.IntPtr.Zero)
throw new IedConnectionException("Control object not found", 0);
InternalCommandTerminationHandler intCommandTerminationHandler = new InternalCommandTerminationHandler (MyCommandTerminationHandler);
ControlObjectClient_setCommandTerminationHandler(controlObject, intCommandTerminationHandler, controlObject);
}
~ControlObject ()
{
ControlObjectClient_destroy(controlObject);
if (this.controlObject != System.IntPtr.Zero)
ControlObjectClient_destroy(controlObject);
}
/// <summary>
/// Gets the control model.
/// </summary>
/// <returns>
/// The control model.
/// </returns>
public ControlModel GetControlModel ()
{
ControlModel controlModel = (ControlModel) ControlObjectClient_getControlModel(controlObject);
@ -78,11 +187,36 @@ namespace IEC61850
return controlModel;
}
/// <summary>
/// Sets the origin parameter used by control commands.
/// </summary>
/// <param name='originator'>
/// Originator. An arbitrary string identifying the controlling client.
/// </param>
/// <param name='originatorCategory'>
/// Originator category.
/// </param>
public void SetOrigin (string originator, OrCat originatorCategory)
{
ControlObjectClient_setOrigin(controlObject, originator, (int) originatorCategory);
}
/// <summary>
/// Operate the control with the specified control value.
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (bool ctlVal)
{
return Operate (ctlVal, 0);
}
/// <summary>
/// Operate the control with the specified control value (time activated control).
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <param name='operTime'>the time when the operation will be executed</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (bool ctlVal, UInt64 operTime)
{
MmsValue value = new MmsValue(ctlVal);
@ -90,11 +224,22 @@ namespace IEC61850
return Operate (value, operTime);
}
/// <summary>
/// Operate the control with the specified control value.
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (float ctlVal)
{
return Operate (ctlVal, 0);
}
/// <summary>
/// Operate the control with the specified control value (time activated control).
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <param name='operTime'>the time when the operation will be executed</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (float ctlVal, UInt64 operTime)
{
MmsValue value = new MmsValue(ctlVal);
@ -102,11 +247,22 @@ namespace IEC61850
return Operate (value, operTime);
}
/// <summary>
/// Operate the control with the specified control value.
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (int ctlVal)
{
return Operate (ctlVal, 0);
}
/// <summary>
/// Operate the control with the specified control value (time activated control).
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <param name='operTime'>the time when the operation will be executed</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (int ctlVal, UInt64 operTime)
{
MmsValue value = new MmsValue(ctlVal);
@ -114,15 +270,138 @@ namespace IEC61850
return Operate (value, operTime);
}
/// <summary>
/// Operate the control with the specified control value.
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (MmsValue ctlVal)
{
return Operate (ctlVal, 0);
}
/// <summary>
/// Operate the control with the specified control value (time activated control).
/// </summary>
/// <param name='ctlVal'>the new value of the control</param>
/// <param name='operTime'>the time when the operation will be executed</param>
/// <returns>true when the operation has been successful, false otherwise</returns>
public bool Operate (MmsValue ctlVal, UInt64 operTime)
{
return ControlObjectClient_operate(controlObject, ctlVal.valueReference, operTime);
}
/// <summary>
/// Select the control object.
/// </summary>
/// <returns>true when the selection has been successful, false otherwise</returns>
public bool Select ()
{
return ControlObjectClient_select(controlObject);
}
/// <summary>
/// Send a select with value command for generic MmsValue instances
/// </summary>
/// <param name='ctlVal'>
/// the value to be checked.
/// </param>
/// <returns>true when the selection has been successful, false otherwise</returns>
public bool SelectWithValue (MmsValue ctlVal)
{
return ControlObjectClient_selectWithValue(controlObject, ctlVal.valueReference);
}
/// <summary>
/// Send a select with value command for boolean controls
/// </summary>
/// <param name='ctlVal'>
/// the value to be checked.
/// </param>
/// <returns>true when the selection has been successful, false otherwise</returns>
public bool SelectWithValue (bool ctlVal)
{
return SelectWithValue(new MmsValue(ctlVal));
}
/// <summary>
/// Send a select with value command for integer controls
/// </summary>
/// <param name='ctlVal'>
/// the value to be checked.
/// </param>
/// <returns>true when the selection has been successful, false otherwise</returns>
public bool SelectWithValue (int ctlVal)
{
return SelectWithValue(new MmsValue(ctlVal));
}
/// <summary>
/// Send a select with value command for float controls
/// </summary>
/// <param name='ctlVal'>
/// the value to be checked.
/// </param>
/// <returns>true when the selection has been successful, false otherwise</returns>
public bool SelectWithValue (float ctlVal)
{
return SelectWithValue(new MmsValue(ctlVal));
}
/// <summary>
/// Cancel a selection or time activated operation
/// </summary>
/// <returns>true when the cancelation has been successful, false otherwise</returns>
public bool Cancel ()
{
return ControlObjectClient_cancel(controlObject);
}
/// <summary>
/// Enables the synchro check for operate commands
/// </summary>
public void EnableSynchroCheck ()
{
ControlObjectClient_enableSynchroCheck(controlObject);
}
/// <summary>
/// Enables the interlock check for operate and select commands
/// </summary>
public void EnableInterlockCheck ()
{
ControlObjectClient_enableInterlockCheck(controlObject);
}
/// <summary>
/// Gets the last received LastApplError (Additional Cause Diagnostics) value.
/// </summary>
/// <returns>
/// The last appl error.
/// </returns>
public LastApplError GetLastApplError ()
{
LastApplErrorInternal lastApplError = ControlObjectClient_getLastApplError(controlObject);
return new LastApplError(lastApplError);
}
/// <summary>
/// Sets the command termination handler.
/// </summary>
/// <param name='handler'>
/// the handler (delegate) that is invoked when a CommandTerminationMessage is received.
/// </param>
/// <param name='parameter'>
/// Parameter.
/// </param>
public void SetCommandTerminationHandler (CommandTerminationHandler handler, Object parameter)
{
this.commandTerminationHandler = handler;
this.commandTerminationHandlerParameter = parameter;
}
}
}

@ -24,10 +24,16 @@ using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
namespace Client
{
/// <summary>
/// This class is used to represent a data set. It is used to store the values
/// of a data set.
/// </summary>
public class DataSet
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
@ -51,6 +57,12 @@ namespace IEC61850
this.nativeObject = nativeObject;
}
/// <summary>
/// Gets the object reference of the data set
/// </summary>
/// <returns>
/// object reference.
/// </returns>
public string GetReference ()
{
if (reference == null) {
@ -62,6 +74,15 @@ namespace IEC61850
return reference;
}
/// <summary>
/// Gets the values associated with the data set object
/// </summary>
/// <description>This function will return the locally stored values associated with the data set.
/// These are the values received by the last request to the server. A call to this method doesn't
/// invoke a request to the server! </description>
/// <returns>
/// The locally stored values of the data set (as MmsValue instance of type MMS_ARRAY)
/// </returns>
public MmsValue GetValues ()
{
if (values == null) {
@ -73,6 +94,13 @@ namespace IEC61850
return values;
}
/// <summary>
/// Gets the number of elements of the data set
/// </summary>
/// <returns>
/// the number of elementes (data set members)
/// </returns>
public int GetSize ()
{
return ClientDataSet_getDataSetSize (nativeObject);

@ -28,8 +28,14 @@ using System.Collections;
using IEC61850.Common;
/// <summary>
/// IEC 61850 API for the libiec61850 .NET wrapper library
/// </summary>
namespace IEC61850
{
/// <summary>
/// IEC 61850 client API.
/// </summary>
namespace Client
{
@ -67,6 +73,12 @@ namespace IEC61850
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedConnection_create ();
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern void IedConnection_destroy (IntPtr self);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern void IedConnection_setConnectTimeout(IntPtr self, UInt32 timeoutInMs);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern void IedConnection_connect (IntPtr self, out int error, string hostname, int tcpPort);
@ -88,6 +100,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getDataDirectory (IntPtr self, out int error, string dataReference);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getDataDirectoryByFC (IntPtr self, out int error, string dataReference, FunctionalConstraint fc);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getDataDirectoryFC (IntPtr self, out int error, string dataReference);
@ -100,7 +115,10 @@ namespace IEC61850
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getLogicalDeviceDirectory (IntPtr self, out int error, string logicalDeviceName);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getVariableSpecification(IntPtr self, out int error, string objectReference, int fc);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalConnectionClosedHandler (IntPtr parameter,IntPtr Iedconnection);
public delegate void ConnectionClosedHandler (IedConnection connection);
@ -124,7 +142,19 @@ namespace IEC61850
static extern IntPtr IedConnection_getMmsConnection (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsConnection_getIsoConnectionParameters(IntPtr mmsConnection);
static extern IntPtr MmsConnection_getIsoConnectionParameters(IntPtr mmsConnection);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getFileDirectory(IntPtr self, out int error, string directoryName);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_deleteFile(IntPtr self, out int error, string fileName);
/********************
* FileDirectoryEntry
*********************/
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void FileDirectoryEntry_destroy(IntPtr self);
/****************
* LinkedList
@ -136,7 +166,16 @@ namespace IEC61850
static extern IntPtr LinkedList_getData (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_destroy (IntPtr self);
static extern void LinkedList_destroy (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_destroyStatic(IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void LinkedListValueDeleteFunction(IntPtr pointer);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_destroyDeep(IntPtr list, LinkedListValueDeleteFunction valueDeleteFunction);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr LinkedList_create ();
@ -144,7 +183,7 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_add (IntPtr self, IntPtr data);
private IntPtr connection;
private IntPtr connection = IntPtr.Zero;
private InternalConnectionClosedHandler connectionClosedHandler;
private ConnectionClosedHandler userProvidedHandler = null;
@ -153,6 +192,12 @@ namespace IEC61850
connection = IedConnection_create ();
}
~IedConnection ()
{
if (connection != IntPtr.Zero)
IedConnection_destroy(connection);
}
public IsoConnectionParameters GetConnectionParameters ()
{
IntPtr mmsConnection = IedConnection_getMmsConnection(connection);
@ -162,12 +207,32 @@ namespace IEC61850
return new IsoConnectionParameters(parameters);
}
private void FreeHGlobaleDeleteFunction (IntPtr pointer)
{
Marshal.FreeHGlobal(pointer);
}
private UInt32 connectTimeout = 10000;
public UInt32 ConnectTimeout {
get {
return connectTimeout;
}
set {
connectTimeout = value;
}
}
/// <summary>Establish an MMS connection to a server</summary>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void Connect (string hostname, int tcpPort)
{
int error;
IedConnection_setConnectTimeout(connection, connectTimeout);
IedConnection_connect (connection, out error, hostname, tcpPort);
if (error != 0)
@ -199,19 +264,41 @@ namespace IEC61850
if (error != 0)
throw new IedConnectionException ("GetDeviceDirectory failed", error);
IntPtr element = LinkedList_getNext (linkedList);
List<string> newList = new List<string> ();
while (element != IntPtr.Zero) {
string ld = Marshal.PtrToStringAnsi (LinkedList_getData (element));
if (fileDirectory == false) {
newList.Add (ld);
IntPtr element = LinkedList_getNext (linkedList);
element = LinkedList_getNext (element);
}
while (element != IntPtr.Zero) {
string ld = Marshal.PtrToStringAnsi (LinkedList_getData (element));
LinkedList_destroy (linkedList);
newList.Add (ld);
element = LinkedList_getNext (element);
}
LinkedList_destroy (linkedList);
}
else {
IntPtr element = LinkedList_getNext(linkedList);
while (element != IntPtr.Zero)
{
IntPtr elementData = LinkedList_getData(element);
FileDirectoryEntry entry = new FileDirectoryEntry(elementData);
newList.Add(entry.GetFileName());
FileDirectoryEntry_destroy(elementData);
element = LinkedList_getNext(element);
}
LinkedList_destroyStatic(linkedList);
}
return newList;
}
@ -243,6 +330,12 @@ namespace IEC61850
return newList;
}
/// <summary>Get the directory of a logical node (LN)</summary>
/// <description>This function returns the directory contents of a LN. Depending on the provided ACSI class
/// The function returns either data object references, or references of other objects like data sets,
/// report control blocks, ...</description>
/// <param name="logicalNodeName">The object reference of a DO, SDO, or DA.</param>
/// <param name="acsiClass">the ACSI class of the requested directory elements.</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public List<string> GetLogicalNodeDirectory (string logicalNodeName, ACSIClass acsiClass)
{
@ -270,6 +363,8 @@ namespace IEC61850
return newList;
}
/// <summary>Get a list of attributes (with functional constraints) of a DO, SDO, or DA</summary>
/// <param name="dataReference">The object reference of a DO, SDO, or DA.</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public List<string> GetDataDirectory (string dataReference)
{
@ -297,6 +392,40 @@ namespace IEC61850
return newList;
}
/// <summary>Get the list of attributes with the specified FC of a DO, SDO, or DA</summary>
/// <param name="dataReference">The object reference of a DO, SDO, or DA.</param>
/// <param name="fc">Functional constraint</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public List<string> GetDataDirectory (string dataReference, FunctionalConstraint fc)
{
int error;
IntPtr linkedList = IedConnection_getDataDirectoryByFC (connection, out error, dataReference, fc);
if (error != 0)
throw new IedConnectionException ("GetDataDirectory failed", error);
IntPtr element = LinkedList_getNext (linkedList);
List<string> newList = new List<string> ();
while (element != IntPtr.Zero) {
string dataObject = Marshal.PtrToStringAnsi (LinkedList_getData (element));
newList.Add (dataObject);
element = LinkedList_getNext (element);
}
LinkedList_destroy (linkedList);
return newList;
}
/// <summary>Get a list of attributes (with functional constraints) of a DO, SDO, or DA</summary>
/// <description>This function is similar to the GetDataDirectory except that the returned element names
/// have the functional contraint (FC) appended.</description>
/// <param name="dataReference">The object reference of a DO, SDO, or DA.</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public List<string> GetDataDirectoryFC (string dataReference)
{
@ -324,6 +453,23 @@ namespace IEC61850
return newList;
}
/// <summary>Read the variable specification (type description of a DA or FDCO</summary>
/// <param name="objectReference">The object reference of a DA or FCDO.</param>
/// <param name="fc">The functional constraint (FC) of the object</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public MmsVariableSpecification GetVariableSpecification (string objectReference, FunctionalConstraint fc)
{
int error;
IntPtr varSpecPtr = IedConnection_getVariableSpecification(connection, out error, objectReference, (int) fc);
if (error != 0)
throw new IedConnectionException ("GetVariableSpecification failed", error);
return new MmsVariableSpecification(varSpecPtr, true);
}
private IntPtr readObjectInternal (string objectReference, FunctionalConstraint fc)
{
int error;
@ -342,6 +488,7 @@ namespace IEC61850
/// <summary>Read the value of a data attribute (DA) or functional constraint data object (FCDO).</summary>
/// <param name="objectReference">The object reference of a DA or FCDO.</param>
/// <param name="fc">The functional constraint (FC) of the object</param>
/// <returns>the received value as an MmsValue instance</returns>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public MmsValue ReadValue (String objectReference, FunctionalConstraint fc)
{
@ -353,6 +500,7 @@ namespace IEC61850
/// <summary>Read the value of a basic data attribute (BDA) of type boolean.</summary>
/// <param name="objectReference">The object reference of a BDA.</param>
/// <param name="fc">The functional constraint (FC) of the object</param>
/// <returns>the received boolean value</returns>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public bool ReadBooleanValue (string objectReference, FunctionalConstraint fc)
{
@ -423,6 +571,25 @@ namespace IEC61850
}
}
/// <summary>Read the value of a basic data attribute (BDA) of type bit string.</summary>
/// <param name="objectReference">The object reference of a BDA.</param>
/// <param name="fc">The functional constraint (FC) of the object</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public int ReadBitStringValue (string objectReference, FunctionalConstraint fc)
{
IntPtr mmsValue = readObjectInternal (objectReference, fc);
if (MmsValue_getType (mmsValue) == (int)MmsType.MMS_BIT_STRING) {
int bitStringValue = (int)MmsValue_getBitStringAsInteger (mmsValue);
MmsValue_delete (mmsValue);
return bitStringValue;
} else {
MmsValue_delete (mmsValue);
throw new IedConnectionException ("Result is not of type bit string", 0);
}
}
/// <summary>Read the value of a basic data attribute (BDA) of type timestamp (MMS_UTC_TIME).</summary>
/// <param name="objectReference">The object reference of a BDA.</param>
/// <param name="fc">The functional constraint (FC) of the object</param>
@ -457,6 +624,12 @@ namespace IEC61850
throw new IedConnectionException ("Result is not of type integer (MMS_INTEGER)", 0);
}
/// <summary>Write the value of a data attribute (DA) or functional constraint data object (FCDO).</summary>
/// <description>This function can be used to write simple or complex variables (setpoints, parameters, descriptive values...)
/// of the server.</description>
/// <param name="objectReference">The object reference of a BDA.</param>
/// <param name="fc">The functional constraint (FC) of the object</param>
/// <param name="value">MmsValue object representing asimple or complex variable data</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void WriteValue (string objectReference, FunctionalConstraint fc, MmsValue value)
{
@ -466,8 +639,111 @@ namespace IEC61850
if (error != 0)
throw new IedConnectionException ("Write value failed", error);
}
}
/// <summary>Read the content of a file directory.</summary>
/// <param name="directoryName">The name of the directory.</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public List<FileDirectoryEntry> GetFileDirectory(string directoryName)
{
int error;
IntPtr fileEntryList = IedConnection_getFileDirectory(connection, out error, directoryName);
if (error != 0)
throw new IedConnectionException("Reading file directory failed", error);
List<FileDirectoryEntry> fileDirectory = new List<FileDirectoryEntry>();
IntPtr element = LinkedList_getNext(fileEntryList);
while (element != IntPtr.Zero)
{
IntPtr elementData = LinkedList_getData(element);
FileDirectoryEntry entry = new FileDirectoryEntry(elementData);
fileDirectory.Add(entry);
FileDirectoryEntry_destroy(elementData);
element = LinkedList_getNext(element);
}
LinkedList_destroyStatic(fileEntryList);
return fileDirectory;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool InternalIedClientGetFileHandler(IntPtr parameter, IntPtr buffer, UInt32 bytesRead);
private bool iedClientGetFileHandler(IntPtr parameter, IntPtr buffer, UInt32 bytesRead)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
GetFileCallback getFileCallback = (GetFileCallback) handle.Target;
byte[] bytes = new byte[bytesRead];
Marshal.Copy(buffer, bytes, 0, (int) bytesRead);
return getFileCallback.handler(getFileCallback.parameter, bytes);
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 IedConnection_getFile(IntPtr self, out int error, string fileName, InternalIedClientGetFileHandler handler,
IntPtr handlerParameter);
public delegate bool GetFileHandler(object parameter, byte[] data);
private class GetFileCallback
{
public GetFileCallback(GetFileHandler handler, object parameter)
{
this.handler = handler;
this.parameter = parameter;
}
public GetFileHandler handler;
public object parameter;
}
/// <summary>
/// Download a file from the server.
/// </summary>
/// <param name='fileName'>
/// File name of the file (full path)
/// </param>
/// <param name='handler'>
/// Callback handler that is invoked for each chunk of the file received
/// </param>
/// <param name='parameter'>
/// User provided parameter that is passed to the callback handler
/// </param>
public void GetFile(string fileName, GetFileHandler handler, object parameter)
{
int error;
GetFileCallback getFileCallback = new GetFileCallback(handler, parameter);
GCHandle handle = GCHandle.Alloc(getFileCallback);
IedConnection_getFile(connection, out error, fileName, new InternalIedClientGetFileHandler(iedClientGetFileHandler),
GCHandle.ToIntPtr(handle));
if (error != 0)
throw new IedConnectionException("Error reading file", error);
handle.Free();
}
/// <summary>
/// Abort (close) the connection.
/// </summary>
/// <description>This function will send an abort request to the server. This will immediately interrupt the
/// connection.</description>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void Abort ()
{
@ -479,6 +755,11 @@ namespace IEC61850
throw new IedConnectionException ("Abort failed", error);
}
/// <summary>
/// Release (close) the connection.
/// </summary>
/// <description>This function will send an release request to the server. The function will block until the
/// connection is released or an error occured.</description>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void Release ()
{
@ -490,6 +771,12 @@ namespace IEC61850
throw new IedConnectionException ("Release failed", error);
}
/// <summary>
/// Immediately close the connection.
/// </summary>
/// <description>This function will close the connnection to the server by closing the TCP connection.
/// The client will not send an abort or release request as required by the specification!</description>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void Close ()
{
IedConnection_close(connection);
@ -501,6 +788,14 @@ namespace IEC61850
userProvidedHandler (this);
}
/// <summary>
/// Install a callback handler that will be invoked if the connection is closed.
/// </summary>
/// <description>The handler is called when the connection is closed no matter if the connection was closed
/// by the client or by the server. Any new call to this function will replace the callback handler installed
/// by a prior function call.</description>
/// <param name="handler">The user provided callback handler</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void InstallConnectionClosedHandler (ConnectionClosedHandler handler)
{
connectionClosedHandler = new InternalConnectionClosedHandler (MyConnectionClosedHandler);
@ -510,6 +805,28 @@ namespace IEC61850
IedConnection_installConnectionClosedHandler (connection, connectionClosedHandler, connection);
}
/// <summary>
/// Read the values of a data set (GetDataSetValues service).
/// </summary>
/// <description>This function will invoke a readDataSetValues service and return a new DataSet value containing the
/// received values.</description>
/// <param name="dataSetReference">The object reference of the data set</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public DataSet GetDataSetValues (string dataSetReference)
{
return ReadDataSetValues(dataSetReference, null);
}
/// <summary>
/// Read the values of a data set (GetDataSetValues service).
/// </summary>
/// <description>This function will invoke a readDataSetValues service and return a new DataSet value containing the
/// received values. If an existing instance of DataSet is provided to the function the existing instance will be
/// updated by the new values.</description>
/// <param name="dataSetReference">The object reference of the data set</param>
/// <param name="dataSet">The object reference of an existing data set instance or null</param>
/// <returns>a DataSet instance containing the received values</returns>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public DataSet ReadDataSetValues (string dataSetReference, DataSet dataSet)
{
@ -531,6 +848,14 @@ namespace IEC61850
return dataSet;
}
/// <summary>
/// Create a new data set.
/// </summary>
/// <description>This function creates a new data set at the server. The data set consists of the members defined
/// by the list of object references provided.</description>
/// <param name="dataSetReference">The object reference of the data set</param>
/// <param name="dataSetElements">A list of object references of the data set elements</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void CreateDataSet (string dataSetReference, List<string> dataSetElements)
{
IntPtr linkedList = LinkedList_create ();
@ -545,13 +870,20 @@ namespace IEC61850
IedConnection_createDataSet (connection, out error, dataSetReference, linkedList);
LinkedList_destroy (linkedList);
LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction));
if (error != 0)
throw new IedConnectionException ("Failed to create data set", error);
}
/// <summary>
/// Delete a data set.
/// </summary>
/// <description>This function will delete a data set at the server. This function may fail if the data set is not
/// deletable.</description>
/// <param name="dataSetReference">The object reference of the data set</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void DeleteDataSet (string dataSetReference)
{
int error;
@ -615,7 +947,45 @@ namespace IEC61850
{
return (IedClientError)this.errorCode;
}
}
}
public class FileDirectoryEntry
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr FileDirectoryEntry_getFileName(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 FileDirectoryEntry_getFileSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt64 FileDirectoryEntry_getLastModified(IntPtr self);
private string fileName;
private UInt32 fileSize;
private UInt64 lastModified;
internal FileDirectoryEntry(IntPtr nativeFileDirectoryEntry)
{
fileName = Marshal.PtrToStringAnsi(FileDirectoryEntry_getFileName(nativeFileDirectoryEntry));
fileSize = FileDirectoryEntry_getFileSize(nativeFileDirectoryEntry);
lastModified = FileDirectoryEntry_getLastModified(nativeFileDirectoryEntry);
}
public string GetFileName()
{
return fileName;
}
public UInt32 GetFileSize()
{
return fileSize;
}
public UInt64 GetLastModified()
{
return lastModified;
}
}
public enum IedClientError
{
@ -680,10 +1050,15 @@ namespace IEC61850
[Flags]
public enum TriggerOptions {
NONE = 0,
/** send report when value of data changed */
DATA_CHANGED = 1,
/** send report when quality of data changed */
QUALITY_CHANGED = 2,
/** send report when data or quality is updated */
DATA_UPDATE = 4,
/** periodic transmission of all data set values */
INTEGRITY = 8,
/** general interrogation (on client request) */
GI = 16
}
@ -708,6 +1083,9 @@ namespace IEC61850
QUESTIONABLE = 3
}
/// <summary>
/// The quality of a data object.
/// </summary>
public class Quality
{
@ -740,16 +1118,27 @@ namespace IEC61850
public enum ACSIClass
{
/** data objects */
ACSI_CLASS_DATA_OBJECT,
/** data sets (container for multiple data objects) */
ACSI_CLASS_DATA_SET,
/** buffered report control blocks */
ACSI_CLASS_BRCB,
/** unbuffered report control blocks */
ACSI_CLASS_URCB,
/** log control blocks */
ACSI_CLASS_LCB,
/** logs (journals) */
ACSI_CLASS_LOG,
/** setting group control blocks */
ACSI_CLASS_SGCB,
/** GOOSE control blocks */
ACSI_CLASS_GoCB,
/** GSE control blocks */
ACSI_CLASS_GsCB,
/** multicast sampled values control blocks */
ACSI_CLASS_MSVCB,
/** unicast sampled values control blocks */
ACSI_CLASS_USVCB
}
@ -785,5 +1174,89 @@ namespace IEC61850
NONE = -1
}
public enum ControlAddCause {
ADD_CAUSE_UNKNOWN = 0,
ADD_CAUSE_NOT_SUPPORTED = 1,
ADD_CAUSE_BLOCKED_BY_SWITCHING_HIERARCHY = 2,
ADD_CAUSE_SELECT_FAILED = 3,
ADD_CAUSE_INVALID_POSITION = 4,
ADD_CAUSE_POSITION_REACHED = 5,
ADD_CAUSE_PARAMETER_CHANGE_IN_EXECUTION = 6,
ADD_CAUSE_STEP_LIMIT = 7,
ADD_CAUSE_BLOCKED_BY_MODE = 8,
ADD_CAUSE_BLOCKED_BY_PROCESS = 9,
ADD_CAUSE_BLOCKED_BY_INTERLOCKING = 10,
ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK = 11,
ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION = 12,
ADD_CAUSE_BLOCKED_BY_HEALTH = 13,
ADD_CAUSE_1_OF_N_CONTROL = 14,
ADD_CAUSE_ABORTION_BY_CANCEL = 15,
ADD_CAUSE_TIME_LIMIT_OVER = 16,
ADD_CAUSE_ABORTION_BY_TRIP = 17,
ADD_CAUSE_OBJECT_NOT_SELECTED = 18,
ADD_CAUSE_OBJECT_ALREADY_SELECTED = 19,
ADD_CAUSE_NO_ACCESS_AUTHORITY = 20,
ADD_CAUSE_ENDED_WITH_OVERSHOOT = 21,
ADD_CAUSE_ABORTION_DUE_TO_DEVIATION = 22,
ADD_CAUSE_ABORTION_BY_COMMUNICATION_LOSS = 23,
ADD_CAUSE_ABORTION_BY_COMMAND = 24,
ADD_CAUSE_NONE = 25,
ADD_CAUSE_INCONSISTENT_PARAMETERS = 26,
ADD_CAUSE_LOCKED_BY_OTHER_CLIENT = 27
}
/// <summary>
/// Object reference. Helper function to handle object reference strings.
/// </summary>
public static class ObjectReference {
/// <summary>
/// Get the name part of an object reference with appended FC
/// </summary>
/// <returns>
/// The element name.
/// </returns>
/// <param name='objectReferenceWithFc'>
/// Object reference with appended fc.
/// </param>
public static string getElementName (string objectReferenceWithFc)
{
int fcPartStartIndex = objectReferenceWithFc.IndexOf('[');
if (fcPartStartIndex == -1)
return objectReferenceWithFc;
return objectReferenceWithFc.Substring(0, fcPartStartIndex);
}
/// <summary>
/// Get the FC of an object reference with appended FC.
/// </summary>
/// <returns>
/// The FC
/// </returns>
/// <param name='objectReferenceWithFc'>
/// Object reference with FC.
/// </param>
public static FunctionalConstraint getFC (string objectReferenceWithFc)
{
int fcPartStartIndex = objectReferenceWithFc.IndexOf('[');
if (fcPartStartIndex == -1)
return FunctionalConstraint.NONE;
string fcString = objectReferenceWithFc.Substring(fcPartStartIndex + 1 , 2);
try
{
return (FunctionalConstraint) Enum.Parse(typeof(FunctionalConstraint), fcString);
}
catch(ArgumentException)
{
return FunctionalConstraint.NONE;
}
}
}
}
}

@ -40,6 +40,7 @@
<Compile Include="Reporting.cs" />
<Compile Include="Control.cs" />
<Compile Include="IsoConnectionParameters.cs" />
<Compile Include="MmsVariableSpecification.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

@ -30,12 +30,26 @@ namespace IEC61850
{
public enum AcseAuthenticationMechanism {
/** don't use authentication */
ACSE_AUTH_NONE = 0,
/** use password authentication */
ACSE_AUTH_PASSWORD = 1
}
/// <summary>
/// Connection parameters associated with the ISO protocol layers (transport, session, presentation, ACSE)
/// </summary>
public class IsoConnectionParameters
{
[StructLayout(LayoutKind.Sequential)]
private struct NativeTSelector
{
public byte size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)] public byte[] value;
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_destroy(IntPtr self);
@ -43,13 +57,13 @@ namespace IEC61850
private static extern void IsoConnectionParameters_setRemoteApTitle(IntPtr self, string apTitle, int aeQualifier);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, UInt32 pSelector, UInt16 sSelector, UInt16 tSelector);
private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, UInt32 pSelector, UInt16 sSelector, NativeTSelector tSelector);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setLocalApTitle(IntPtr self, string apTitle, int aeQualifier);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, UInt32 pSelector, UInt16 sSelector, UInt16 tSelector);
private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, UInt32 pSelector, UInt16 sSelector, NativeTSelector tSelector);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setAcseAuthenticationParameter(IntPtr self, IntPtr acseAuthParameter);
@ -80,29 +94,97 @@ namespace IEC61850
if (authParameter != IntPtr.Zero)
AcseAuthenticationParameter_destroy(authParameter);
IsoConnectionParameters_destroy(self);
//IsoConnectionParameters_destroy(self);
}
/// <summary>
/// Sets the remote ap title related parameters
/// </summary>
/// <param name='apTitle'>
/// remote AP title.
/// </param>
/// <param name='aeQualifier'>
/// remote AE qualifier.
/// </param>
public void SetRemoteApTitle(string apTitle, int aeQualifier)
{
IsoConnectionParameters_setRemoteApTitle(self, apTitle, aeQualifier);
}
public void SetRemoteAddresses (UInt32 pSelector, UInt16 sSelector, UInt16 tSelector)
/// <summary>
/// Sets the remote addresses for ISO layers (transport, session, presentation)
/// </summary>
/// <param name='pSelector'>
/// presentation layer address
/// </param>
/// <param name='sSelector'>
/// session layer address
/// </param>
/// <param name='tSelector'>
/// ISO COTP transport layer address
/// </param>
public void SetRemoteAddresses (UInt32 pSelector, UInt16 sSelector, byte[] tSelector)
{
IsoConnectionParameters_setRemoteAddresses(self, pSelector, sSelector, tSelector);
if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
NativeTSelector nativeTSelector;
nativeTSelector.size = (byte) tSelector.Length;
nativeTSelector.value = new byte[4];
for (int i = 0; i < tSelector.Length; i++)
nativeTSelector.value[i] = tSelector[i];
IsoConnectionParameters_setRemoteAddresses(self, pSelector, sSelector, nativeTSelector);
}
/// <summary>
/// Sets the local ap title related parameters
/// </summary>
/// <param name='apTitle'>
/// local AP title.
/// </param>
/// <param name='aeQualifier'>
/// local AE qualifier.
/// </param>
public void SetLocalApTitle (string apTitle, int aeQualifier)
{
IsoConnectionParameters_setLocalApTitle(self, apTitle, aeQualifier);
}
public void SetLocalAddresses (UInt32 pSelector, UInt16 sSelector, UInt16 tSelector)
/// <summary>
/// Sets the local addresses for ISO layers (transport, session, presentation)
/// </summary>
/// <param name='pSelector'>
/// presentation layer address
/// </param>
/// <param name='sSelector'>
/// session layer address
/// </param>
/// <param name='tSelector'>
/// ISO COTP transport layer address
/// </param>
public void SetLocalAddresses (UInt32 pSelector, UInt16 sSelector, byte[] tSelector)
{
IsoConnectionParameters_setLocalAddresses(self, pSelector, sSelector, tSelector);
if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
NativeTSelector nativeTSelector;
nativeTSelector.size = (byte) tSelector.Length;
nativeTSelector.value = new byte[4];
for (int i = 0; i < tSelector.Length; i++)
nativeTSelector.value[i] = tSelector[i];
IsoConnectionParameters_setLocalAddresses(self, pSelector, sSelector, nativeTSelector);
}
/// <summary>
/// Instruct ACSE layer to use password authentication.
/// </summary>
/// <param name='password'>
/// Password that will be used to authenticate the client
/// </param>
public void UsePasswordAuthentication (string password)
{
if (authParameter == IntPtr.Zero) {

@ -25,12 +25,16 @@ using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
using System.Collections;
using System.Text;
namespace IEC61850
{
namespace Client
namespace Common
{
/// <summary>
/// This class is used to hold MMS data values of different types.
/// </summary>
public class MmsValue : IEnumerable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
@ -48,6 +52,18 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 MmsValue_getBitStringAsInteger (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsValue_setBitStringFromInteger(IntPtr self, UInt32 intValue);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int MmsValue_getBitStringSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsValue_setBitStringBit(IntPtr self, int bitPos, bool value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool MmsValue_getBitStringBit(IntPtr self, int bitPos);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern Int32 MmsValue_toInt32 (IntPtr self);
@ -87,9 +103,37 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newIntegerFromInt32 (Int32 integer);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newUnsignedFromUint32(UInt32 integer);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newIntegerFromInt64 (Int64 integer);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newBitString(int bitSize);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newVisibleString(string value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newOctetString(int size, int maxSize);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsValue_setOctetString(IntPtr self, [Out] byte[] buf, int size);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 MmsValue_getOctetStringSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 MmsValue_getOctetStringMaxSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_getOctetStringBuffer(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool MmsValue_equals(IntPtr self, IntPtr otherValue);
internal MmsValue (IntPtr value)
{
valueReference = value;
@ -122,33 +166,165 @@ namespace IEC61850
valueReference = MmsValue_newIntegerFromInt32 (value);
}
public MmsValue (UInt32 value)
{
valueReference = MmsValue_newUnsignedFromUint32(value);
}
public MmsValue (long value)
{
valueReference = MmsValue_newIntegerFromInt64 (value);
}
public MmsValue (string value)
{
valueReference = MmsValue_newVisibleString(value);
}
~MmsValue ()
{
if (responsableForDeletion)
MmsValue_delete (valueReference);
}
/// <summary>
/// Create a new MmsValue instance of type MMS_BIT_STRING.
/// </summary>
/// <returns>
/// the new MmsValue instance
/// </returns>
/// <param name='bitSize'>
/// the size of the bit string in bits.
/// </param>
public static MmsValue NewBitString(int bitSize)
{
IntPtr newValue = MmsValue_newBitString(bitSize);
return new MmsValue(newValue, true);
}
/// <summary>
/// Create a new MmsValue instance of type MMS_OCTET_STRING.
/// </summary>
/// <returns>
/// the new MmsValue instance
/// </returns>
/// <param name='maxSize'>
/// the maximum size of the octet string in bytes
/// </param>
/// <param name='size'>
/// the current size of the octet string in bytes (defaults to 0)
/// </param>
public static MmsValue NewOctetString (int maxSize, int size = 0)
{
IntPtr newValue = MmsValue_newOctetString(size, maxSize);
return new MmsValue(newValue, true);
}
internal IntPtr valueReference;
private bool responsableForDeletion;
/// <summary>
/// Gets the type of the value
/// </summary>
/// <returns>
/// The type.
/// </returns>
public new MmsType GetType ()
{
return (MmsType)MmsValue_getType (valueReference);
}
/// <summary>
/// Gets the size of an array, structure, or bit string
/// </summary>
/// <returns>
/// <description>
/// Return the size of an array of structure (number of elements)
/// The value has to be of type MMS_ARRAY, MMS_STRUCTURE, MMS_BIT_STRING ...
/// </description>
/// the number of elements (array/structure elements, octets, bits depending on type)
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public int Size ()
{
if ((GetType () == MmsType.MMS_ARRAY) || (GetType () == MmsType.MMS_STRUCTURE)) {
return MmsValue_getArraySize (valueReference);
} else
throw new MmsValueException ("Value is not a complex type");
} else if (GetType () == MmsType.MMS_BIT_STRING) {
return MmsValue_getBitStringSize(valueReference);
}
else if (GetType () == MmsType.MMS_OCTET_STRING) {
return MmsValue_getOctetStringSize(valueReference);
}
else
throw new MmsValueException ("Operation not supported for this type");
}
/// <summary>
/// Gets the maximum size of an octet string
/// </summary>
/// <returns>
/// The maximum size (in bytes) of the octet string
/// </returns>
public int MaxSize ()
{
if (GetType () == MmsType.MMS_OCTET_STRING) {
return MmsValue_getOctetStringMaxSize(valueReference);
}
else
throw new MmsValueException ("Operation not supported for this type");
}
/// <summary>
/// Gets the octet string as byte array
/// </summary>
/// <description>Instance has to be of type MMS_OCTET_STRING.
/// </description>
/// <returns>
/// Byte array containing the bytes of the octet string.
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public byte[] getOctetString ()
{
if (GetType () == MmsType.MMS_OCTET_STRING) {
IntPtr buffer = MmsValue_getOctetStringBuffer(valueReference);
int bufferSize = this.Size();
byte[] octetString = new byte[bufferSize];
Marshal.Copy(buffer, octetString, 0, bufferSize);
return octetString;
}
else
throw new MmsValueException ("Operation not supported for this type");
}
public void setOctetString (byte[] octetString)
{
if (GetType () == MmsType.MMS_OCTET_STRING) {
if (this.MaxSize() < octetString.Length)
throw new MmsValueException("octet string is to large");
MmsValue_setOctetString(valueReference, octetString, octetString.Length);
}
else
throw new MmsValueException ("Operation not supported for this type");
}
/// <summary>
/// Get an element of an array or structure
/// </summary>
/// <returns>
/// the MmsValue element.
/// </returns>
/// <param name='index'>
/// index of the element starting with 0
/// </param>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
/// <exception cref="MmsValueException">This exception is thrown if the index is out of range.</exception>
public MmsValue GetElement (int index)
{
MmsType type = GetType ();
@ -163,6 +339,17 @@ namespace IEC61850
throw new MmsValueException ("Value is of wrong type");
}
/// <summary>
/// Gets the timestamp value as UTC time in s (UNIX time stamp).
/// </summary>
/// <description>
/// Return the value as seconds since epoch (1.1.1970 UTC).
/// The value has to be of type MMS_UTC_TIME.
/// </description>
/// <returns>
/// The UTC time in seconds (UNIX time stamp).
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public UInt32 ToUnixTimestamp ()
{
if (GetType () == MmsType.MMS_UTC_TIME)
@ -171,6 +358,17 @@ namespace IEC61850
throw new MmsValueException ("Value is not a time type");
}
/// <summary>
/// Gets the timestamp value as UTC time in ms.
/// </summary>
/// <description>
/// Return the value as milliseconds since epoch (1.1.1970 UTC).
/// The value has to be of type MMS_UTC_TIME.
/// </description>
/// <returns>
/// The UTC time in ms.
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public ulong GetUtcTimeInMs ()
{
if (GetType () == MmsType.MMS_UTC_TIME) {
@ -179,13 +377,33 @@ namespace IEC61850
throw new MmsValueException ("Value is not a time type");
}
/// <summary>
/// Convert a millisecond time (milliseconds since epoch) to DataTimeOffset
/// </summary>
/// <returns>
/// The time as DataTimeOffset
/// </returns>
/// <param name='msTime'>
/// the millisecond time
/// </param>
public static DateTimeOffset MsTimeToDateTimeOffset (UInt64 msTime)
{
DateTimeOffset retVal = new DateTimeOffset (1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
return retVal.AddMilliseconds (msTime);
return retVal.AddMilliseconds ((double) msTime);
}
/// <summary>
/// Convert MMS_UTC_TIME to DateTimeOffset instance
/// </summary>
/// <description>
/// Return the value as DateTimeOffset instance.
/// The value has to be of type MMS_UTC_TIME.
/// </description>
/// <returns>
/// the value as DataTimeOffset instance
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public DateTimeOffset GetUtcTimeAsDateTimeOffset ()
{
if (GetType () == MmsType.MMS_UTC_TIME)
@ -194,36 +412,123 @@ namespace IEC61850
throw new MmsValueException ("Value is not a time type");
}
/// <summary>
/// Return the value as 32 bit signed integer.
/// </summary>
/// <description>
/// Return the value as 32 bit signed integer (Int32).
/// The value has to be of type MMS_INTEGER.
/// </description>
/// <returns>
/// the value if the object as 32 bit signed integer
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public Int32 ToInt32 ()
{
if (GetType () != MmsType.MMS_INTEGER)
throw new MmsValueException ("Value type is not integer");
Int32 retVal = MmsValue_toInt32 (valueReference);
return retVal;
return MmsValue_toInt32 (valueReference);
}
/// <summary>
/// Return the value as 64 bit signed integer.
/// </summary>
/// <description>
/// Return the value as 64 bit signed integer (Int64).
/// The value has to be of type MMS_INTEGER.
/// </description>
/// <returns>
/// the value if the object as 64 bit signed integer
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public Int64 ToInt64 ()
{
if (GetType () != MmsType.MMS_INTEGER)
throw new MmsValueException ("Value type is not integer");
Int64 retVal = MmsValue_toInt64 (valueReference);
return retVal;
return MmsValue_toInt64 (valueReference);
}
/// <summary>
/// Return the value as 32 bit unsigned integer.
/// </summary>
/// <description>
/// Return the value as 32 bit unsigned integer (Int32).
/// The value has to be of type MMS_INTEGER.
/// </description>
/// <returns>
/// the value if the object as 32 bit unsigned integer
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public UInt32 ToUint32 ()
{
if (GetType () != MmsType.MMS_UNSIGNED)
throw new MmsValueException ("Value type is not unsigned");
UInt32 retVal = MmsValue_toUint32 (valueReference);
return MmsValue_toUint32 (valueReference);
}
public UInt32 BitStringToUInt32 ()
{
if (GetType () != MmsType.MMS_BIT_STRING)
throw new MmsValueException("Value type is not bit string");
return MmsValue_getBitStringAsInteger(valueReference);
}
return retVal;
public void BitStringFromUInt32 (UInt32 intValue)
{
if (GetType () != MmsType.MMS_BIT_STRING)
throw new MmsValueException("Value type is not bit string");
MmsValue_setBitStringFromInteger(valueReference, intValue);
}
public void SetBit (int bitPos, bool bitValue)
{
if (GetType () != MmsType.MMS_BIT_STRING)
throw new MmsValueException("Value type is not bit string");
MmsValue_setBitStringBit(valueReference, bitPos, bitValue);
}
public bool GetBit (int bitPos)
{
if (GetType () != MmsType.MMS_BIT_STRING)
throw new MmsValueException("Value type is not bit string");
return MmsValue_getBitStringBit(valueReference, bitPos);
}
private string GetBitStringAsString()
{
if (GetType() != MmsType.MMS_BIT_STRING)
throw new MmsValueException("Value type is not bit string");
int size = Size();
StringBuilder builder = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
if (MmsValue_getBitStringBit(valueReference, i))
builder.Append('1');
else
builder.Append('0');
}
return builder.ToString();
}
/// <summary>
/// Gets the boolean value
/// </summary>
/// <returns>
/// The boolean value
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public bool GetBoolean ()
{
if (GetType () == MmsType.MMS_BOOLEAN)
@ -232,6 +537,13 @@ namespace IEC61850
throw new MmsValueException ("Value type is not boolean");
}
/// <summary>
/// Gets the float value of an MMS_FLOAT instance
/// </summary>
/// <returns>
/// The float value
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public float ToFloat ()
{
if (GetType () == MmsType.MMS_FLOAT)
@ -240,6 +552,13 @@ namespace IEC61850
throw new MmsValueException ("Value type is not float");
}
/// <summary>
/// Gets the double value of an MMS_FLOAT instance
/// </summary>
/// <returns>
/// The float value
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public double ToDouble ()
{
if (GetType () == MmsType.MMS_FLOAT)
@ -248,6 +567,13 @@ namespace IEC61850
throw new MmsValueException ("Value type is not float");
}
public override bool Equals (object obj)
{
MmsValue otherValue = (MmsValue) obj;
return MmsValue_equals(this.valueReference, otherValue.valueReference);
}
// override standard ToString() method
public override string ToString ()
{
@ -265,6 +591,8 @@ namespace IEC61850
return ToDouble ().ToString ();
case MmsType.MMS_UTC_TIME:
return GetUtcTimeAsDateTimeOffset ().ToString ();
case MmsType.MMS_BIT_STRING:
return GetBitStringAsString();
default:
return "unknown";
}
@ -321,21 +649,37 @@ namespace IEC61850
public enum MmsType
{
/** array type (multiple elements of the same type) */
MMS_ARRAY = 0,
/** structure type (multiple elements of different types) */
MMS_STRUCTURE = 1,
/** boolean */
MMS_BOOLEAN = 2,
/** bit string */
MMS_BIT_STRING = 3,
/** signed integer */
MMS_INTEGER = 4,
/** unsigned integer */
MMS_UNSIGNED = 5,
/** floating point value (32 or 64 bit) */
MMS_FLOAT = 6,
/** octet string */
MMS_OCTET_STRING = 7,
/** visible string - ANSI string */
MMS_VISIBLE_STRING = 8,
/** Generalized time */
MMS_GENERALIZED_TIME = 9,
/** Binary time */
MMS_BINARY_TIME = 10,
/** Binary coded decimal (BCD) - not used */
MMS_BCD = 11,
/** object ID - not used */
MMS_OBJ_ID = 12,
/** Unicode string */
MMS_STRING = 13,
/** UTC time */
MMS_UTC_TIME = 14,
/** will be returned in case of an error (contains error code) */
MMS_DATA_ACCESS_ERROR = 15
}

@ -0,0 +1,201 @@
/*
* MmsVariableSpecification.cs
*
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
namespace IEC61850
{
namespace Common
{
/// <summary>
/// MMS variable specification. This class is used to represent an MMS variable type definition.
/// </summary>
public class MmsVariableSpecification : IEnumerable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsVariableSpecification_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getChildValue(IntPtr self, IntPtr value, string childId);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getNamedVariableRecursive(IntPtr variable, string nameId);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int MmsVariableSpecification_getType(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getName(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int MmsVariableSpecification_getSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getChildSpecificationByIndex(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getArrayElementSpecification(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int MmsVariableSpecification_getExponentWidth(IntPtr self);
private IntPtr self;
private bool responsableForDeletion;
internal MmsVariableSpecification (IntPtr self)
{
this.self = self;
this.responsableForDeletion = false;
}
internal MmsVariableSpecification (IntPtr self, bool responsableForDeletion)
{
this.self = self;
this.responsableForDeletion = responsableForDeletion;
}
~MmsVariableSpecification ()
{
if (responsableForDeletion)
MmsVariableSpecification_destroy(self);
}
/// <summary>
/// Gets the MmsValue type of the variable
/// </summary>
/// <returns>
/// The MmsType of the variable
/// </returns>
public new MmsType GetType ()
{
return (MmsType) MmsVariableSpecification_getType(self);
}
/// <summary>
/// Gets the type of the array elements.
/// </summary>
/// <returns>
/// The array element type.
/// </returns>
/// <exception cref="MmsValueException">This exception is thrown if the value is not of type MMS_ARRAY</exception>
public MmsVariableSpecification getArrayElementType ()
{
if (GetType() == MmsType.MMS_ARRAY) {
IntPtr varSpecPtr = MmsVariableSpecification.MmsVariableSpecification_getArrayElementSpecification(self);
return new MmsVariableSpecification(varSpecPtr);
}
else
throw new MmsValueException ("specification is of wrong type");
}
/// <summary>
/// Gets the element specification of a structure element
/// </summary>
/// <returns>
/// The element of the structure at given index
/// </returns>
/// <param name='index'>
/// Index.
/// </param>
public MmsVariableSpecification GetElement (int index)
{
if (GetType () == MmsType.MMS_STRUCTURE) {
if ((index >= 0) && (index < Size ())) {
IntPtr varSpecPtr = MmsVariableSpecification_getChildSpecificationByIndex(self, index);
return new MmsVariableSpecification(varSpecPtr);
}
else
throw new MmsValueException ("Index out of bounds");
}
else
throw new MmsValueException ("specification is of wrong type");
}
/// <summary>
/// Gets the name of the variable
/// </summary>
/// <returns>
/// The name.
/// </returns>
public string GetName ()
{
IntPtr namePtr = MmsVariableSpecification_getName(self);
return Marshal.PtrToStringAnsi (namePtr);
}
/// <summary>
/// Get the "size" of the variable (array size, number of structure elements ...)
/// </summary>
public int Size ()
{
return MmsVariableSpecification_getSize(self);
}
IEnumerator IEnumerable.GetEnumerator ()
{
return new MmsVariableSpecificationEnumerator (this);
}
private class MmsVariableSpecificationEnumerator : IEnumerator
{
private MmsVariableSpecification value;
private int index = -1;
public MmsVariableSpecificationEnumerator (MmsVariableSpecification value)
{
this.value = value;
}
#region IEnumerator Members
public void Reset ()
{
index = -1;
}
public object Current {
get { return value.GetElement (index);}
}
public bool MoveNext ()
{
index++;
if (index >= value.Size ())
return false;
else
return true;
}
#endregion
}
}
}
}

@ -31,8 +31,19 @@ namespace IEC61850
namespace Client
{
/// <summary>
/// Report handler.
/// </summary>
public delegate void ReportHandler (Report report, object parameter);
/// <summary>
/// Report control block (RCB) representation.
/// </summary>
/// <description>
/// This class is used as a client side representation (copy) of a report control block (RCB).
/// 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.
/// </description>
public class ReportControlBlock
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
@ -133,12 +144,11 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_installReportHandler (IntPtr connection, string rcbReference, string rptId, InternalReportHandler handler,
IntPtr handlerParameter);
IntPtr handlerParameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalReportHandler (IntPtr parameter, IntPtr report);
private IntPtr self;
private IntPtr connection;
private string objectReference;
@ -183,8 +193,6 @@ namespace IEC61850
private void internalReportHandler (IntPtr parameter, IntPtr report)
{
Console.WriteLine ("internalReportHandler called " + this);
try {
if (this.report == null)
@ -207,20 +215,42 @@ namespace IEC61850
this.objectReference = objectReference;
}
/// <summary>
/// Installs the report handler.
/// </summary>
/// <description>
/// This will install a callback handler (delegate) that is invoked whenever a report
/// related this RCB is received. Any call of this method will replace an previously registered
/// handler!
/// </description>
/// <param name='reportHandler'>
/// report handler
/// </param>
/// <param name='parameter'>
/// parameter is passed to the handler when the handler is invoked.
/// </param>
public void InstallReportHandler (ReportHandler reportHandler, object parameter)
{
this.reportHandler = new ReportHandler(reportHandler);
Console.WriteLine("Installed report handler " + this.reportHandler);
this.reportHandlerParameter = parameter;
if (reportHandlerInstalled == false) {
IedConnection_installReportHandler (this.connection, objectReference, this.GetRptId (), new InternalReportHandler(internalReportHandler), IntPtr.Zero);
string reportId = this.GetRptId ();
// if ((GetRptId() == null) || (GetRptId().Length == 0))
// reportId =
IedConnection_installReportHandler (this.connection, objectReference, reportId, new InternalReportHandler(internalReportHandler), IntPtr.Zero);
reportHandlerInstalled = true;
}
}
/// <summary>
/// Read all RCB values from the server
/// </summary>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void GetRCBValues ()
{
int error;
@ -231,84 +261,142 @@ namespace IEC61850
throw new IedConnectionException ("getRCBValues service failed", error);
}
/// <summary>
/// Write changed RCB values to the server.
/// </summary>
/// <description>
/// This function will only write the RCB values that were set by one of the setter methods.
/// The RCB values are sent by a single MMS write request.
/// </description>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void SetRCBValues ()
{
SetRCBValues (true);
}
/// <summary>
/// Write changed RCB values to the server.
/// </summary>
/// <description>
/// This function will only write the RCB values that were set by one of the setter methods.
/// </description>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
/// <param name='singleRequest'>
/// If true the values are sent by single MMS write request. Otherwise the values are all sent by their own MMS write requests.
/// </param>
public void SetRCBValues (bool singleRequest)
{
UInt32 parametersMask = 0;
{
UInt32 parametersMask = 0;
if (flagRptId)
parametersMask += 1;
if (flagRptId)
parametersMask += 1;
if (flagRptEna)
parametersMask += 2;
if (flagRptEna)
parametersMask += 2;
if (flagResv)
parametersMask += 4;
if (flagResv)
parametersMask += 4;
if (flagDataSetReference)
parametersMask += 8;
if (flagDataSetReference)
parametersMask += 8;
if (flagConfRev)
parametersMask += 16;
if (flagConfRev)
parametersMask += 16;
if (flagOptFlds)
parametersMask += 32;
if (flagOptFlds)
parametersMask += 32;
if (flagBufTm)
parametersMask += 64;
if (flagBufTm)
parametersMask += 64;
if (flagSqNum)
parametersMask += 128;
if (flagSqNum)
parametersMask += 128;
if (flagTrgOps)
parametersMask += 256;
if (flagTrgOps)
parametersMask += 256;
if (flagIntgPd)
parametersMask += 512;
if (flagIntgPd)
parametersMask += 512;
if (flagGI)
parametersMask += 1024;
if (flagGI)
parametersMask += 1024;
if (flagPurgeBuf)
parametersMask += 2048;
if (flagPurgeBuf)
parametersMask += 2048;
if (flagEntryId)
parametersMask += 4096;
if (flagEntryId)
parametersMask += 4096;
if (flagResvTms)
parametersMask += 16384;
if (flagResvTms)
parametersMask += 16384;
int error;
int error;
IedConnection_setRCBValues (connection, out error, self, parametersMask, singleRequest);
IedConnection_setRCBValues (connection, out error, self, parametersMask, singleRequest);
if (error != 0)
throw new IedConnectionException ("setRCBValues service failed", error);
if (error != 0)
throw new IedConnectionException ("setRCBValues service failed", error);
if (flagRptId) {
if (reportHandlerInstalled) {
reportHandlerInstalled = false;
InstallReportHandler(this.reportHandler, this.reportHandlerParameter);
}
}
resetSendFlags();
}
/// <summary>
/// Determines whether this instance is a buffered or unbuffered RCB.
/// </summary>
/// <returns>
/// <c>true</c> if this instance is a buffered RCB; otherwise, <c>false</c>.
/// </returns>
public bool IsBuffered ()
{
return ClientReportControlBlock_isBuffered (self);
}
public UInt64 getEntryTime ()
/// <summary>
/// Gets the entry time of the RCB as ms time
/// </summary>
/// <description>
/// The entry time is the timestamp of the last report sent.
/// </description>
/// <returns>
/// The entry time as ms timestamp
/// </returns>
public UInt64 GetEntryTime ()
{
return ClientReportControlBlock_getEntryTime (self);
}
/// <summary>
/// Gets the entry time of the RCB as DateTimeOffset
/// </summary>
/// <description>
/// The entry time is the timestamp of the last report sent.
/// </description>
/// <returns>
/// The entry time as DataTimeOffset
/// </returns>
public DateTimeOffset GetEntryTimeAsDateTimeOffset ()
{
UInt64 entryTime = getEntryTime ();
UInt64 entryTime = GetEntryTime ();
DateTimeOffset retVal = new DateTimeOffset (1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
return retVal.AddMilliseconds (entryTime);
}
/// <summary>
/// Gets the data set reference of the associated data set
/// </summary>
/// <returns>
/// The data set reference.
/// </returns>
public string GetDataSetReference ()
{
IntPtr dataSetRefPtr = ClientReportControlBlock_getDataSetReference (self);
@ -316,6 +404,12 @@ namespace IEC61850
return Marshal.PtrToStringAnsi (dataSetRefPtr);
}
/// <summary>
/// Sets the data set reference. Use this method to select the associated data set for the RCB
/// </summary>
/// <returns>
/// The data set reference.
/// </returns>
public void SetDataSetReference (string dataSetReference)
{
ClientReportControlBlock_setDataSetReference (self, dataSetReference);
@ -323,6 +417,12 @@ namespace IEC61850
flagDataSetReference = true;
}
/// <summary>
/// Gets the report identifier.
/// </summary>
/// <returns>
/// The report identifier.
/// </returns>
public string GetRptId ()
{
IntPtr rptIdPtr = ClientReportControlBlock_getRptId (self);
@ -330,22 +430,58 @@ namespace IEC61850
return Marshal.PtrToStringAnsi (rptIdPtr);
}
/// <summary>
/// Sets the RptId (report ID) of the RCB
/// </summary>
/// <param name='rptId'>
/// The new RptId
/// </param>
public void SetRptId (string rptId)
{
ClientReportControlBlock_setRptId(self, rptId);
flagRptId = true;
}
/// <summary>
/// Check if reporting is currently enabled
/// </summary>
/// <returns>
/// true, if reporting is enabled, false otherwise
/// </returns>
public bool GetRptEna ()
{
return ClientReportControlBlock_getRptEna (self);
}
/// <summary>
/// Sets report enable flag. Use this to enable reporting
/// </summary>
/// <param name='rptEna'>
/// true to enable reporting, false to disable
/// </param>
public void SetRptEna (bool rptEna)
{
ClientReportControlBlock_setRptEna (self, rptEna);
flagRptEna = true;
}
/// <summary>
/// Gets the buffer time.
/// </summary>
/// <returns>
/// The buffer time in ms.
/// </returns>
public UInt32 GetBufTm()
{
return ClientReportControlBlock_getBufTm (self);
}
/// <summary>
/// Sets the buffer time.
/// </summary>
/// <param name='bufTm'>
/// Buffer time is ms.
/// </param>
public void SetBufTm (UInt32 bufTm)
{
ClientReportControlBlock_setBufTm (self, bufTm);
@ -353,33 +489,80 @@ namespace IEC61850
flagBufTm = true;
}
/// <summary>
/// Gets the GI flag
/// </summary>
/// <returns>
/// true, if GI flag is set
/// </returns>
public bool GetGI ()
{
return ClientReportControlBlock_getGI (self);
}
/// <summary>
/// Sets the GI flag. Use this to trigger a GI (general interrogation) command.
/// </summary>
/// <param name='GI'>
/// request general interrogation of true
/// </param>
public void SetGI (bool GI)
{
ClientReportControlBlock_setGI (self, GI);
flagGI = true;
}
/// <summary>
/// Check if RCB is reserved by a client
/// </summary>
/// <returns>
/// true, the RCB is reserver by a client
/// </returns>
public bool GetResv ()
{
return ClientReportControlBlock_getResv (self);
}
/// <summary>
/// Gets the configuration revision of the RCB
/// </summary>
/// <returns>
/// The conf rev.
/// </returns>
public UInt32 GetConfRev ()
{
return ClientReportControlBlock_getConfRev (self);
}
/// <summary>
/// Sets RESV flag. Use this to reserve (allocate) this RCB.
/// </summary>
/// <param name='resv'>
/// true: reserver this RCB for exclusive use
/// </param>
public void SetResv (bool resv)
{
ClientReportControlBlock_setResv (self, resv);
flagResv = true;
}
/// <summary>
/// Gets the trigger options of the RCB
/// </summary>
/// <returns>
/// trigger options
/// </returns>
public TriggerOptions GetTrgOps()
{
return (TriggerOptions) ClientReportControlBlock_getTrgOps (self);
}
/// <summary>
/// Sets the trigger options of the RCB.
/// </summary>
/// <param name='trgOps'>
/// trigger options
/// </param>
public void SetTrgOps(TriggerOptions trgOps)
{
ClientReportControlBlock_setTrgOps (self, (int) trgOps);
@ -387,22 +570,46 @@ namespace IEC61850
flagTrgOps = true;
}
/// <summary>
/// Gets the integrity period
/// </summary>
/// <returns>
/// integrity period in ms
/// </returns>
public UInt32 GetIntgPd ()
{
return ClientReportControlBlock_getIntgPd (self);
}
/// <summary>
/// Sets the integrity period
/// </summary>
/// <param name='intgPd'>
/// integrity period in ms
/// </param>
public void SetIntgPd (UInt32 intgPd)
{
ClientReportControlBlock_setIntgPd (self, intgPd);
flagIntgPd = true;
}
/// <summary>
/// Gets the option fields.
/// </summary>
/// <returns>
/// The option fields
/// </returns>
public ReportOptions GetOptFlds()
{
return (ReportOptions) ClientReportControlBlock_getOptFlds (self);
}
/// <summary>
/// Sets the option field. Used to enable or disable optional report elements
/// </summary>
/// <param name='optFlds'>
/// Option field.
/// </param>
public void SetOptFlds(ReportOptions optFlds)
{
ClientReportControlBlock_setOptFlds (self, (int)optFlds);

@ -23,13 +23,15 @@
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
namespace Client
{
public partial class IedConnection
{
public ReportControlBlock getReportControlBlock (string rcbObjectReference)
public ReportControlBlock GetReportControlBlock (string rcbObjectReference)
{
return new ReportControlBlock (rcbObjectReference, connection);
}
@ -59,6 +61,9 @@ namespace IEC61850
REASON_UNKNOWN = 6
}
/// <summary>
/// A class to hold the contents of a received report
/// </summary>
public class Report
{
@ -74,6 +79,37 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ClientReport_getReasonForInclusion(IntPtr self, int elementIndex);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_hasSeqNum(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 ClientReport_getSeqNum(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_hasDataSetName(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_hasReasonForInclusion(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_hasConfRev(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ClientReport_getConfRev(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_hasBufOvfl(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_hasDataReference(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientReport_getRcbReference(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientReport_getRptId(IntPtr self);
private IntPtr self;
private IntPtr dataSetValues = IntPtr.Zero;
@ -85,11 +121,23 @@ namespace IEC61850
this.self = self;
}
/// <summary>
/// Determines whether the report has a timestamp.
/// </summary>
/// <returns>
/// <c>true</c> if this report has a timestamp; otherwise, <c>false</c>.
/// </returns>
public bool HasTimestamp ()
{
return ClientReport_hasTimestamp (self);
}
/// <summary>
/// Gets the timestamp.
/// </summary>
/// <returns>
/// The timestamp as milliseconds since 1.1.1970 UTC 00:00 or 0 if no timestamp is present.
/// </returns>
public UInt64 GetTimestamp ()
{
if (HasTimestamp ())
@ -98,6 +146,52 @@ namespace IEC61850
return 0;
}
public bool HasDataSetName ()
{
return ClientReport_hasDataSetName(self);
}
public bool HasDataReference ()
{
return ClientReport_hasDataReference(self);
}
public bool HasConfRev ()
{
return ClientReport_hasConfRev(self);
}
public UInt32 GetConfRev ()
{
return ClientReport_getConfRev(self);
}
public bool HasBufOvfl ()
{
return ClientReport_hasBufOvfl(self);
}
public bool HasSeqNum ()
{
return ClientReport_hasSeqNum(self);
}
public UInt16 GetSeqNum ()
{
return ClientReport_getSeqNum(self);
}
public bool HasReasonForInclusion ()
{
return ClientReport_hasReasonForInclusion(self);
}
/// <summary>
/// Gets the data set values as MMS_ARRAY instance.
/// </summary>
/// <returns>
/// The data set values.
/// </returns>
public MmsValue GetDataSetValues ()
{
if (dataSetValues == IntPtr.Zero) {
@ -112,6 +206,15 @@ namespace IEC61850
return values;
}
/// <summary>
/// Gets the reason for inclusion of data set member with the given index
/// </summary>
/// <returns>
/// The reason for inclusion.
/// </returns>
/// <param name='index'>
/// index of the data set member in the data set
/// </param>
public ReasonForInclusion GetReasonForInclusion (int index)
{
if (values == null) {
@ -129,6 +232,23 @@ namespace IEC61850
return (ReasonForInclusion) ClientReport_getReasonForInclusion(self, index);
}
public string GetRcbReference ()
{
IntPtr rcbRef = ClientReport_getRcbReference(self);
return Marshal.PtrToStringAnsi (rcbRef);
}
public string GetRptId ()
{
IntPtr rptId = ClientReport_getRptId(self);
if (rptId == IntPtr.Zero)
return GetRcbReference();
else
return Marshal.PtrToStringAnsi (rptId);
}
}
}

@ -2,12 +2,19 @@ using System;
using System.Collections.Generic;
using IEC61850.Common;
using IEC61850.Client;
using System.Threading;
namespace control
{
class ControlExample
{
private static void commandTerminationHandler (Object parameter, ControlObject control)
{
LastApplError lastApplError = control.GetLastApplError();
Console.WriteLine("HANDLER CALLED! " + lastApplError.addCause);
}
public static void Main (string[] args)
{
IedConnection con = new IedConnection ();
@ -25,7 +32,8 @@ namespace control
{
con.Connect(hostname, 102);
string objectReference = "IEDM1CPUBHKW/DRCC1.DERStr";
/* direct control with normal security */
string objectReference = "simpleIOGenericIO/GGIO1.SPCSO1";
ControlObject control = con.CreateControlObject(objectReference);
@ -33,9 +41,27 @@ namespace control
Console.WriteLine(objectReference + " has control model " + controlModel.ToString());
if (controlModel != ControlModel.CONTROL_MODEL_STATUS_ONLY)
control.Operate(true);
if (!control.Operate(true))
Console.WriteLine("operate failed!");
/* direct control with enhanced security */
objectReference = "simpleIOGenericIO/GGIO1.SPCSO3";
control = con.CreateControlObject(objectReference);
controlModel = control.GetControlModel();
Console.WriteLine(objectReference + " has control model " + controlModel.ToString());
if (controlModel == ControlModel.CONTROL_MODEL_DIRECT_ENHANCED) {
control.SetCommandTerminationHandler(commandTerminationHandler, null);
control.Operate(true);
Thread.Sleep(1000);
}
con.Abort();

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using IEC61850.Client;
using IEC61850.Common;
namespace datasets
{
@ -16,7 +17,7 @@ namespace datasets
if (args.Length > 0)
hostname = args[0];
else
hostname = "10.0.2.2";
hostname = "localhost";
Console.WriteLine("Connect to " + hostname);
@ -31,7 +32,6 @@ namespace datasets
Console.WriteLine("LD: " + entry);
}
// create a new data set
List<string> dataSetElements = new List<string>();
@ -49,7 +49,6 @@ namespace datasets
Console.WriteLine("DS element: " + entry);
}
// read the values of the newly created data set
DataSet dataSet = con.ReadDataSetValues("IEDM1CPUBHKW/LLN0.ds1", null);

@ -19,6 +19,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "authenticate", "authenticat
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "files", "files\files.csproj", "{77127456-19B9-4D1A-AEF9-40F8D1C5695E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example3", "example3\example3.csproj", "{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -37,6 +41,14 @@ Global
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Release|Any CPU.Build.0 = Release|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Release|Any CPU.Build.0 = Release|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Release|Any CPU.Build.0 = Release|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU

@ -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("example3")]
[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("")]

@ -0,0 +1,48 @@
using System;
using IEC61850.Client;
using System.Collections.Generic;
namespace example3
{
class MainClass
{
public static void Main (string[] args)
{
IedConnection con = new IedConnection ();
string hostname;
if (args.Length > 0)
hostname = args[0];
else
hostname = "localhost";
Console.WriteLine("Connect to " + hostname);
try
{
IsoConnectionParameters parameters = con.GetConnectionParameters();
parameters.SetRemoteAddresses(1,1, new byte[] {0x00, 0x01, 0x02, 0x03});
con.ConnectTimeout = 10000;
con.Connect(hostname, 102);
List<string> serverDirectory = con.GetServerDirectory(false);
foreach (string entry in serverDirectory)
{
Console.WriteLine("LD: " + entry);
}
con.Release();
}
catch (IedConnectionException e)
{
Console.WriteLine(e.Message);
}
}
}
}

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>example3</RootNamespace>
<AssemblyName>example3</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -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("files")]
[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("")]

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using IEC61850.Client;
using IEC61850.Common;
using System.IO;
namespace files
{
class MainClass
{
public static void printFiles (IedConnection con, string prefix, string parent)
{
List<FileDirectoryEntry> files = con.GetFileDirectory (parent);
foreach (FileDirectoryEntry file in files) {
Console.WriteLine(prefix + file.GetFileName() + "\t" + file.GetFileSize() + "\t" +
MmsValue.MsTimeToDateTimeOffset(file.GetLastModified()));
if (file.GetFileName().EndsWith("/")) {
printFiles (con, prefix + " ", parent + file.GetFileName());
}
}
}
static bool getFileHandler (object parameter, byte[] data)
{
Console.WriteLine("received " + data.Length + " bytes");
BinaryWriter binWriter = (BinaryWriter) parameter;
binWriter.Write(data);
return true;
}
public static void Main (string[] args)
{
IedConnection con = new IedConnection ();
string hostname;
if (args.Length > 0)
hostname = args[0];
else
hostname = "10.0.2.2";
Console.WriteLine("Connect to " + hostname);
try
{
con.Connect(hostname, 102);
Console.WriteLine ("Files in server root directory:");
List<string> serverDirectory = con.GetServerDirectory(true);
foreach (string entry in serverDirectory) {
Console.WriteLine(entry);
}
Console.WriteLine();
Console.WriteLine ("File directory tree at server:");
printFiles(con, "", "");
Console.WriteLine();
string filename = "IEDSERVER.BIN";
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);
con.GetFile(filename, new IedConnection.GetFileHandler(getFileHandler), w);
fs.Close();
con.Abort();
}
catch (IedConnectionException e)
{
Console.WriteLine(e.Message);
}
}
}
}

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{77127456-19B9-4D1A-AEF9-40F8D1C5695E}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>files</RootNamespace>
<AssemblyName>files</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="FileServicesExample.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -51,6 +51,27 @@ namespace model_browsing
foreach (string dataObject in dataObjects) {
Console.WriteLine(" DO: " + dataObject);
List<string> dataDirectory = con.GetDataDirectoryFC(logicalNodeReference + "." + dataObject);
foreach (string dataDirectoryElement in dataDirectory) {
string daReference = logicalNodeReference + "." + dataObject + "." + ObjectReference.getElementName(dataDirectoryElement);
// get the type specification of a variable
MmsVariableSpecification specification = con.GetVariableSpecification(daReference, ObjectReference.getFC(dataDirectoryElement));
Console.WriteLine (" DA/SDO: [" + ObjectReference.getFC(dataDirectoryElement) + "] " +
ObjectReference.getElementName(dataDirectoryElement) + " : " + specification.GetType()
+ "(" + specification.Size() + ")");
if (specification.GetType() == MmsType.MMS_STRUCTURE) {
foreach (MmsVariableSpecification elementSpec in specification) {
Console.WriteLine(" " + elementSpec.GetName() + " : " + elementSpec.GetType());
}
}
}
}
// discover data sets

@ -27,6 +27,10 @@ namespace reporting
}
}
ReportControlBlock rcb = (ReportControlBlock) parameter;
Console.WriteLine("Buffered: " + rcb.IsBuffered());
}
@ -48,13 +52,14 @@ namespace reporting
try {
con.Connect (hostname, 102);
string rcbReference = "simpleIOGenericIO/LLN0.RP.EventsRCB";
string rcbReference = "simpleIOGenericIO/LLN0.RP.EventsRCB01";
ReportControlBlock rcb = con.getReportControlBlock(rcbReference);
ReportControlBlock rcb = con.GetReportControlBlock(rcbReference);
rcb.GetRCBValues();
rcb.InstallReportHandler(reportHandler, null);
// note: the second parameter is not required!
rcb.InstallReportHandler(reportHandler, rcb);
if (rcb.IsBuffered())
Console.WriteLine ("RCB: " + rcbReference + " is buffered");

@ -15,6 +15,65 @@ namespace tests
Assert.AreEqual (10.0f, val.ToFloat ());
}
[Test ()]
public void MmsValueBitString ()
{
var val = MmsValue.NewBitString(10);
Assert.AreEqual (MmsType.MMS_BIT_STRING, val.GetType());
Assert.AreEqual (10, val.Size());
val.BitStringFromUInt32(7);
Assert.AreEqual(7, val.BitStringToUInt32());
Assert.AreEqual(true, val.GetBit(0));
Assert.AreEqual(true, val.GetBit(1));
Assert.AreEqual(true, val.GetBit(2));
Assert.AreEqual(false, val.GetBit(3));
Assert.AreEqual(false, val.GetBit(9));
Assert.AreEqual(false, val.GetBit(10));
val.SetBit(3, true);
Assert.AreEqual(true, val.GetBit(3));
Assert.AreEqual(15, val.BitStringToUInt32());
val.SetBit(3, false);
Assert.AreEqual(7, val.BitStringToUInt32());
}
[Test()]
public void MmsValueOctetString ()
{
var val = MmsValue.NewOctetString(20);
Assert.AreEqual (0, val.Size());
Assert.AreEqual (20, val.MaxSize());
byte[] octetString = val.getOctetString();
Assert.AreEqual (0, octetString.Length);
octetString = new byte[5];
octetString[0] = 0x11;
octetString[1] = 0x12;
octetString[2] = 0x13;
octetString[3] = 0x14;
octetString[4] = 0x15;
val.setOctetString(octetString);
Assert.AreEqual(5, val.Size());
byte[] secondOctetString = val.getOctetString();
Assert.AreEqual(octetString, secondOctetString);
}
}
}

@ -21,7 +21,7 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
@ -30,7 +30,9 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="nunit.framework" />
<Reference Include="nunit.framework">
<Package>nunit</Package>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Test.cs" />

@ -8,7 +8,9 @@ add_subdirectory(server_example_control)
add_subdirectory(server_example_dynamic)
add_subdirectory(server_example_config_file)
add_subdirectory(server_example_complex_array)
add_subdirectory(server_example_threadless)
add_subdirectory(server_example_61400_25)
add_subdirectory(server_example_setting_groups)
add_subdirectory(iec61850_client_example1)
add_subdirectory(iec61850_client_example2)
add_subdirectory(iec61850_client_example3)

@ -21,6 +21,8 @@ EXAMPLE_DIRS += server_example_config_file
EXAMPLE_DIRS += server_example_dynamic
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 += goose_subscriber
EXAMPLE_DIRS += goose_publisher
EXAMPLE_DIRS += mms_utility

@ -23,11 +23,23 @@ main(int argc, char** argv)
LinkedList_add(dataSetValues, MmsValue_newBinaryTime(false));
LinkedList_add(dataSetValues, MmsValue_newIntegerFromInt32(5678));
GoosePublisher publisher = GoosePublisher_create(NULL, "eth0");
CommParameters gooseCommParameters;
GoosePublisher_setGoCbRef(publisher, "Test1/LLN0$GO$gocb1");
gooseCommParameters.appId = 1000;
gooseCommParameters.dstAddress[0] = 0x01;
gooseCommParameters.dstAddress[1] = 0x0c;
gooseCommParameters.dstAddress[2] = 0xcd;
gooseCommParameters.dstAddress[3] = 0x01;
gooseCommParameters.dstAddress[4] = 0x00;
gooseCommParameters.dstAddress[5] = 0x01;
gooseCommParameters.vlanId = 0;
gooseCommParameters.vlanPriority = 4;
GoosePublisher publisher = GoosePublisher_create(&gooseCommParameters, "eth0");
GoosePublisher_setGoCbRef(publisher, "simpleIOGenericIO/LLN0$GO$gcbAnalogValues");
GoosePublisher_setConfRev(publisher, 1);
GoosePublisher_setDataSetRef(publisher, "Test1/LLN0$dataset1");
GoosePublisher_setDataSetRef(publisher, "simpleIOGenericIO/LLN0$AnalogValues");
int i = 0;

@ -6,8 +6,8 @@
* Has to be started as root in Linux.
*/
#include "goose_subscriber.h"
#include "thread.h"
#include "goose_receiver.h"
#include "hal_thread.h"
#include <stdlib.h>
#include <stdio.h>
@ -28,7 +28,7 @@ gooseListener(GooseSubscriber subscriber, void* parameter)
printf(" stNum: %u sqNum: %u\n", GooseSubscriber_getStNum(subscriber),
GooseSubscriber_getSqNum(subscriber));
printf(" timeToLive: %u\n", GooseSubscriber_getTimeAllowedToLive(subscriber));
printf(" timestamp: %"PRIu64"\n", GooseSubscriber_getTimestamp(subscriber));
printf(" timestamp: %llu\n", GooseSubscriber_getTimestamp(subscriber));
MmsValue* values = GooseSubscriber_getDataSetValues(subscriber);
@ -50,20 +50,26 @@ main(int argc, char** argv)
MmsValue_setElement(dataSetValues, i, dataSetEntry);
}
// GooseSubscriber subscriber = GooseSubscriber_create("simpleIOGenericIO/LLN0$GO$gcbEvents", dataSetValues);
GooseReceiver receiver = GooseReceiver_create();
GooseSubscriber subscriber = GooseSubscriber_create("simpleIOGenericIO/LLN0$GO$gcbAnalogValues", NULL);
if (argc > 1) {
printf("Set interface id: %s\n", argv[1]);
GooseReceiver_setInterfaceId(receiver, argv[1]);
}
else {
printf("Using interface eth0\n");
GooseReceiver_setInterfaceId(receiver, "eth0");
}
if (argc > 1) {
printf("Set interface id: %s\n", argv[1]);
GooseSubscriber_setInterfaceId(subscriber, argv[1]);
}
GooseSubscriber subscriber = GooseSubscriber_create("simpleIOGenericIO/LLN0$GO$gcbAnalogValues", NULL);
GooseSubscriber_setAppId(subscriber, 1000);
GooseSubscriber_setListener(subscriber, gooseListener, NULL);
GooseSubscriber_subscribe(subscriber);
GooseReceiver_addSubscriber(receiver, subscriber);
GooseReceiver_start(receiver);
signal(SIGINT, sigint_handler);

@ -9,7 +9,7 @@
#include <stdlib.h>
#include <stdio.h>
#include "thread.h"
#include "hal_thread.h"
void
reportCallbackFunction(void* parameter, ClientReport report)

@ -25,6 +25,8 @@ printDataDirectory(char* doRef, IedConnection con, int spaces)
LinkedList dataAttributes = IedConnection_getDataDirectory(con, &error, doRef);
//LinkedList dataAttributes = IedConnection_getDataDirectoryByFC(con, &error, doRef, MX);
if (dataAttributes != NULL) {
LinkedList dataAttribute = LinkedList_getNext(dataAttributes);
@ -70,7 +72,10 @@ main(int argc, char** argv)
printf("Get logical device list...\n");
LinkedList deviceList = IedConnection_getLogicalDeviceList(con, &error);
printf("error: %i\n", error);
if (error != IED_ERROR_OK) {
printf("Failed to read device list (error code: %i)\n", error);
goto cleanup_and_exit;
}
LinkedList device = LinkedList_getNext(deviceList);
@ -191,6 +196,7 @@ main(int argc, char** argv)
printf("Connection failed!\n");
}
cleanup_and_exit:
IedConnection_destroy(con);
}

@ -5,10 +5,27 @@
*/
#include "iec61850_client.h"
#include "hal_thread.h"
#include <stdlib.h>
#include <stdio.h>
static void commandTerminationHandler(void *parameter, ControlObjectClient connection)
{
LastApplError lastApplError = ControlObjectClient_getLastApplError(connection);
// if lastApplError.error != 0 this indicates a CommandTermination-
if (lastApplError.error != 0) {
printf("Received CommandTermination-.\n");
printf(" LastApplError: %i\n", lastApplError.error);
printf(" addCause: %i\n", lastApplError.addCause);
}
else
printf("Received CommandTermination+.\n");
}
int main(int argc, char** argv) {
char* hostname;
@ -57,6 +74,7 @@ int main(int argc, char** argv) {
if (error == IED_ERROR_OK) {
bool state = MmsValue_getBoolean(stVal);
MmsValue_delete(stVal);
printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
}
@ -91,6 +109,101 @@ int main(int argc, char** argv) {
ControlObjectClient_destroy(control);
/****************************************
* Direct control with enhanced security
****************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO3", con);
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
ctlVal = MmsValue_newBoolean(true);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
}
MmsValue_delete(ctlVal);
/* Wait for command termination message */
Thread_sleep(1000);
ControlObjectClient_destroy(control);
/* Check if status value has changed */
stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", ST);
if (error == IED_ERROR_OK) {
bool state = MmsValue_getBoolean(stVal);
printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state);
MmsValue_delete(stVal);
}
else {
printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n");
}
/***********************************************
* Select before operate with enhanced security
***********************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO4", con);
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
ctlVal = MmsValue_newBoolean(true);
if (ControlObjectClient_selectWithValue(control, ctlVal)) {
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n");
}
}
else {
printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n");
}
MmsValue_delete(ctlVal);
/* Wait for command termination message */
Thread_sleep(1000);
ControlObjectClient_destroy(control);
/*********************************************************************
* Direct control with enhanced security (expect CommandTermination-)
*********************************************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con);
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
ctlVal = MmsValue_newBoolean(true);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
}
MmsValue_delete(ctlVal);
/* Wait for command termination message */
Thread_sleep(1000);
ControlObjectClient_destroy(control);
IedConnection_close(con);
}

@ -11,7 +11,7 @@
#include <stdlib.h>
#include <stdio.h>
#include "thread.h"
#include "hal_thread.h"
static void
printDataSetValues(MmsValue* dataSet)

@ -11,7 +11,7 @@
#include <stdlib.h>
#include <stdio.h>
#include "thread.h"
#include "hal_thread.h"
int main(int argc, char** argv) {
@ -47,9 +47,12 @@ int main(int argc, char** argv) {
// IsoConnectionParameters_setRemoteApTitle(parameters, NULL, 0);
// IsoConnectionParameters_setLocalApTitle(parameters, NULL, 0);
TSelector localTSelector = { 3, { 0x00, 0x01, 0x02 } };
TSelector remoteTSelector = { 2, { 0x00, 0x01 } };
/* change parameters for presentation, session and transport layers */
IsoConnectionParameters_setRemoteAddresses(parameters, 0x12345678, 12 , 13);
IsoConnectionParameters_setLocalAddresses(parameters, 0x87654321, 1234 , 2345);
IsoConnectionParameters_setRemoteAddresses(parameters, 0x12345678, 12, localTSelector);
IsoConnectionParameters_setLocalAddresses(parameters, 0x87654321, 1234 , remoteTSelector);
char* password = "top secret";
@ -61,6 +64,8 @@ int main(int argc, char** argv) {
IsoConnectionParameters_setAcseAuthenticationParameter(parameters, auth);
IedConnection_setConnectTimeout(con, 10000);
/* call connect when all parameters are set */
IedConnection_connect(con, &error, hostname, tcpPort);

@ -15,7 +15,7 @@
#include <stdlib.h>
#include <stdio.h>
#include "thread.h"
#include "hal_thread.h"
#define MAX_BUFFER_SIZE 2000000

@ -11,7 +11,7 @@
#include <signal.h>
#include <time.h>
#include "thread.h"
#include "hal_thread.h"
static int running = 0;
@ -105,7 +105,7 @@ int main(int argc, char** argv) {
/* Read RCB values */
ClientReportControlBlock rcb =
IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB", NULL);
IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL);
if (error != IED_ERROR_OK) {
printf("getRCBValues service error!\n");
@ -131,7 +131,7 @@ int main(int argc, char** argv) {
Thread_sleep(1000);
IedConnection_triggerGIReport(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB");
IedConnection_triggerGIReport(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01");
if (error != IED_ERROR_OK) {
printf("Error triggering a GI report (code: %i)\n", error);

@ -27,7 +27,7 @@
#include <stdlib.h>
#include <stdio.h>
#include "mms_client_connection.h"
#include "thread.h"
#include "hal_thread.h"
int main(int argc, char** argv) {

@ -22,7 +22,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

@ -4,7 +4,7 @@
* automatically generated from sampleModel_with_dataset.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -148,7 +148,7 @@ extern DataSetEntry ds_Device1_LLN0_dataset1_fcda1;
extern DataSetEntry ds_Device1_LLN0_dataset1_fcda2;
DataSetEntry ds_Device1_LLN0_dataset1_fcda0 = {
"SampleIEDDevice1",
"Device1",
"LLN0$ST$Mod$q",
-1,
NULL,
@ -157,7 +157,7 @@ DataSetEntry ds_Device1_LLN0_dataset1_fcda0 = {
};
DataSetEntry ds_Device1_LLN0_dataset1_fcda1 = {
"SampleIEDDevice1",
"Device1",
"MMXU1$ST$Mod$q",
-1,
NULL,
@ -166,7 +166,7 @@ DataSetEntry ds_Device1_LLN0_dataset1_fcda1 = {
};
DataSetEntry ds_Device1_LLN0_dataset1_fcda2 = {
"SampleIEDDevice1",
"Device1",
"MMXU1$CF$Mod$ctlModel",
-1,
NULL,
@ -175,7 +175,7 @@ DataSetEntry ds_Device1_LLN0_dataset1_fcda2 = {
};
DataSet ds_Device1_LLN0_dataset1 = {
"SampleIEDDevice1",
"Device1",
"LLN0$dataset1",
3,
&ds_Device1_LLN0_dataset1_fcda0,
@ -184,7 +184,7 @@ DataSet ds_Device1_LLN0_dataset1 = {
LogicalDevice iedModel_Device1 = {
LogicalDeviceModelType,
"SampleIEDDevice1",
"Device1",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_Device1_LLN0
@ -1718,31 +1718,9 @@ DataAttribute iedModel_Device1_MMXU2_TotW_t = {
NULL,
0};
extern ReportControlBlock iedModel_Device1_LLN0_report0;
ReportControlBlock iedModel_Device1_LLN0_report0 = {&iedModel_Device1_LLN0, "LLN0_Events_BuffRep", "LLN0$RP$brcbEV1", true, "dataset1", 1, 9, 239, 50, 900000, NULL};
ReportControlBlock iedModel_Device1_LLN0_report0 = {&iedModel_Device1_LLN0, "LLN0_Events_BuffRep01", "LLN0$RP$brcbEV1", true, "dataset1", 1, 9, 239, 50, 900000, NULL};
@ -1753,6 +1731,7 @@ IedModel iedModel = {
&ds_Device1_LLN0_dataset1,
&iedModel_Device1_LLN0_report0,
NULL,
NULL,
initializeValues
};

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_Device1;

@ -22,7 +22,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

@ -1,10 +1,10 @@
/*
* static_model.c
*
* automatically generated from complexModel.scd
* automatically generated from complexModel.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -314,7 +314,7 @@ extern DataSetEntry ds_Inverter_LLN0_dataset1_fcda3;
extern DataSetEntry ds_Inverter_LLN0_dataset1_fcda4;
DataSetEntry ds_Inverter_LLN0_dataset1_fcda0 = {
"ied1Inverter",
"Inverter",
"LLN0$ST$Mod$q",
-1,
NULL,
@ -323,7 +323,7 @@ DataSetEntry ds_Inverter_LLN0_dataset1_fcda0 = {
};
DataSetEntry ds_Inverter_LLN0_dataset1_fcda1 = {
"ied1Battery",
"Battery",
"LLN0$ST$Mod$q",
-1,
NULL,
@ -332,7 +332,7 @@ DataSetEntry ds_Inverter_LLN0_dataset1_fcda1 = {
};
DataSetEntry ds_Inverter_LLN0_dataset1_fcda2 = {
"ied1Inverter",
"Inverter",
"MMXU1$ST$Mod$q",
-1,
NULL,
@ -341,7 +341,7 @@ DataSetEntry ds_Inverter_LLN0_dataset1_fcda2 = {
};
DataSetEntry ds_Inverter_LLN0_dataset1_fcda3 = {
"ied1Inverter",
"Inverter",
"MMXU1$CF$Mod$ctlModel",
-1,
NULL,
@ -350,7 +350,7 @@ DataSetEntry ds_Inverter_LLN0_dataset1_fcda3 = {
};
DataSetEntry ds_Inverter_LLN0_dataset1_fcda4 = {
"ied1Inverter",
"Inverter",
"MMXU1$MX$TotW$mag",
-1,
NULL,
@ -359,7 +359,7 @@ DataSetEntry ds_Inverter_LLN0_dataset1_fcda4 = {
};
DataSet ds_Inverter_LLN0_dataset1 = {
"ied1Inverter",
"Inverter",
"LLN0$dataset1",
5,
&ds_Inverter_LLN0_dataset1_fcda0,
@ -368,7 +368,7 @@ DataSet ds_Inverter_LLN0_dataset1 = {
LogicalDevice iedModel_Inverter = {
LogicalDeviceModelType,
"ied1Inverter",
"Inverter",
(ModelNode*) &iedModel,
(ModelNode*) &iedModel_Battery,
(ModelNode*) &iedModel_Inverter_LLN0
@ -2480,7 +2480,7 @@ DataAttribute iedModel_Inverter_MMXU1_W_phsC_t = {
LogicalDevice iedModel_Battery = {
LogicalDeviceModelType,
"ied1Battery",
"Battery",
(ModelNode*) &iedModel,
(ModelNode*) &iedModel_Physical_Measurements,
(ModelNode*) &iedModel_Battery_LLN0
@ -3512,7 +3512,7 @@ DataAttribute iedModel_Battery_ZBTC1_ChaA_t = {
LogicalDevice iedModel_Physical_Measurements = {
LogicalDeviceModelType,
"ied1Physical_Measurements",
"Physical_Measurements",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_Physical_Measurements_LLN0
@ -3870,47 +3870,9 @@ DataAttribute iedModel_Physical_Measurements_LPHD1_Proxy_t = {
NULL,
0};
extern ReportControlBlock iedModel_Inverter_LLN0_report0;
ReportControlBlock iedModel_Inverter_LLN0_report0 = {&iedModel_Inverter_LLN0, "rcb1", "ID", false, "dataset1", 0, 3, 32, 0, 0, NULL};
ReportControlBlock iedModel_Inverter_LLN0_report0 = {&iedModel_Inverter_LLN0, "rcb101", "ID", false, "dataset1", 0, 3, 32, 0, 0, NULL};
@ -3921,6 +3883,7 @@ IedModel iedModel = {
&ds_Inverter_LLN0_dataset1,
&iedModel_Inverter_LLN0_report0,
NULL,
NULL,
initializeValues
};

@ -1,14 +1,14 @@
/*
* static_model.h
*
* automatically generated from complexModel.scd
* automatically generated from complexModel.icd
*/
#ifndef STATIC_MODEL_H_
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_Inverter;

@ -6,7 +6,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
@ -66,6 +66,16 @@ controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test)
return true;
}
static void
connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter)
{
if (connected)
printf("Connection opened\n");
else
printf("Connection closed\n");
}
int
main(int argc, char** argv)
{
@ -89,6 +99,8 @@ main(int argc, char** argv)
(ControlHandler) controlHandlerForBinaryOutput,
IEDMODEL_GenericIO_GGIO1_SPCSO4);
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102);
@ -135,4 +147,5 @@ main(int argc, char** argv)
/* Cleanup - free all resources */
IedServer_destroy(iedServer);
} /* main() */

@ -4,7 +4,7 @@
* automatically generated from simpleIO_direct_control.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -160,7 +160,7 @@ extern DataSetEntry ds_GenericIO_LLN0_Events_fcda2;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda3;
DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO1$stVal",
-1,
NULL,
@ -169,7 +169,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO2$stVal",
-1,
NULL,
@ -178,7 +178,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO3$stVal",
-1,
NULL,
@ -187,7 +187,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO4$stVal",
-1,
NULL,
@ -196,7 +196,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
};
DataSet ds_GenericIO_LLN0_Events = {
"simpleIOGenericIO",
"GenericIO",
"LLN0$Events",
4,
&ds_GenericIO_LLN0_Events_fcda0,
@ -209,7 +209,7 @@ extern DataSetEntry ds_GenericIO_LLN0_Events2_fcda2;
extern DataSetEntry ds_GenericIO_LLN0_Events2_fcda3;
DataSetEntry ds_GenericIO_LLN0_Events2_fcda0 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO1",
-1,
NULL,
@ -218,7 +218,7 @@ DataSetEntry ds_GenericIO_LLN0_Events2_fcda0 = {
};
DataSetEntry ds_GenericIO_LLN0_Events2_fcda1 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO2",
-1,
NULL,
@ -227,7 +227,7 @@ DataSetEntry ds_GenericIO_LLN0_Events2_fcda1 = {
};
DataSetEntry ds_GenericIO_LLN0_Events2_fcda2 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO3",
-1,
NULL,
@ -236,7 +236,7 @@ DataSetEntry ds_GenericIO_LLN0_Events2_fcda2 = {
};
DataSetEntry ds_GenericIO_LLN0_Events2_fcda3 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO4",
-1,
NULL,
@ -245,7 +245,7 @@ DataSetEntry ds_GenericIO_LLN0_Events2_fcda3 = {
};
DataSet ds_GenericIO_LLN0_Events2 = {
"simpleIOGenericIO",
"GenericIO",
"LLN0$Events2",
4,
&ds_GenericIO_LLN0_Events2_fcda0,
@ -254,7 +254,7 @@ DataSet ds_GenericIO_LLN0_Events2 = {
LogicalDevice iedModel_GenericIO = {
LogicalDeviceModelType,
"simpleIOGenericIO",
"GenericIO",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_GenericIO_LLN0
@ -1973,13 +1973,12 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = {
NULL,
0};
extern ReportControlBlock iedModel_GenericIO_LLN0_report0;
extern ReportControlBlock iedModel_GenericIO_LLN0_report1;
extern ReportControlBlock iedModel_GenericIO_LLN0_report2;
extern ReportControlBlock iedModel_GenericIO_LLN0_report3;
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB", "Events1", false, "Events", 1, 8, 111, 50, 1000, &iedModel_GenericIO_LLN0_report1};
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 8, 111, 50, 1000, &iedModel_GenericIO_LLN0_report1};
ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 8, 111, 50, 1000, &iedModel_GenericIO_LLN0_report2};
ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 8, 111, 50, 1000, &iedModel_GenericIO_LLN0_report3};
ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 8, 111, 50, 1000, NULL};
@ -1987,21 +1986,13 @@ ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0,
IedModel iedModel = {
"simpleIO",
&iedModel_GenericIO,
&ds_GenericIO_LLN0_Events,
&iedModel_GenericIO_LLN0_report0,
NULL,
NULL,
initializeValues
};

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_GenericIO;

@ -16,7 +16,7 @@
#include "iec61850_server.h"
#include "static_model.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

@ -4,7 +4,7 @@
* automatically generated from simpleIO_direct_control.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -158,7 +158,7 @@ extern DataSetEntry ds_GenericIO_LLN0_Events_fcda2;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda3;
DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO1$stVal",
-1,
NULL,
@ -167,7 +167,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO2$stVal",
-1,
NULL,
@ -176,7 +176,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO3$stVal",
-1,
NULL,
@ -185,7 +185,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO4$stVal",
-1,
NULL,
@ -194,7 +194,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
};
DataSet ds_GenericIO_LLN0_Events = {
"simpleIOGenericIO",
"GenericIO",
"LLN0$Events",
4,
&ds_GenericIO_LLN0_Events_fcda0,
@ -203,7 +203,7 @@ DataSet ds_GenericIO_LLN0_Events = {
LogicalDevice iedModel_GenericIO = {
LogicalDeviceModelType,
"simpleIOGenericIO",
"GenericIO",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_GenericIO_LLN0
@ -1909,19 +1909,9 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = {
NULL,
0};
extern ReportControlBlock iedModel_GenericIO_LLN0_report0;
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB", "Events", false, "Events", 1, 8, 111, 50, 1000, NULL};
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events", false, "Events", 1, 8, 111, 50, 1000, NULL};
@ -1932,6 +1922,7 @@ IedModel iedModel = {
&ds_GenericIO_LLN0_Events,
&iedModel_GenericIO_LLN0_report0,
NULL,
NULL,
initializeValues
};

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_GenericIO;

@ -22,7 +22,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

@ -4,7 +4,7 @@
* automatically generated from simpleIO_direct_control.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -158,7 +158,7 @@ extern DataSetEntry ds_GenericIO_LLN0_Events_fcda2;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda3;
DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO1$stVal",
-1,
NULL,
@ -167,7 +167,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO2$stVal",
-1,
NULL,
@ -176,7 +176,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO3$stVal",
-1,
NULL,
@ -185,7 +185,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO4$stVal",
-1,
NULL,
@ -194,7 +194,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
};
DataSet ds_GenericIO_LLN0_Events = {
"simpleIOGenericIO",
"GenericIO",
"LLN0$Events",
4,
&ds_GenericIO_LLN0_Events_fcda0,
@ -203,7 +203,7 @@ DataSet ds_GenericIO_LLN0_Events = {
LogicalDevice iedModel_GenericIO = {
LogicalDeviceModelType,
"simpleIOGenericIO",
"GenericIO",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_GenericIO_LLN0
@ -1909,19 +1909,9 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = {
NULL,
0};
extern ReportControlBlock iedModel_GenericIO_LLN0_report0;
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB", "Events", false, "Events", 1, 8, 111, 50, 1000, NULL};
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events", false, "Events", 1, 8, 111, 50, 1000, NULL};
@ -1932,6 +1922,7 @@ IedModel iedModel = {
&ds_GenericIO_LLN0_Events,
&iedModel_GenericIO_LLN0_report0,
NULL,
NULL,
initializeValues
};

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_GenericIO;

@ -22,7 +22,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

@ -4,7 +4,7 @@
* automatically generated from wtur.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -344,7 +344,7 @@ extern DataAttribute iedModel_WTG_WTUR1_SetTurOp_cmAcs;
LogicalDevice iedModel_WTG = {
LogicalDeviceModelType,
"WINDWTG",
"WTG",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_WTG_LLN0
@ -4466,22 +4466,13 @@ DataAttribute iedModel_WTG_WTUR1_SetTurOp_cmAcs = {
IedModel iedModel = {
"WIND",
&iedModel_WTG,
NULL,
NULL,
NULL,
NULL,
initializeValues
};

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_WTG;

@ -5,7 +5,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

@ -4,7 +4,7 @@
* automatically generated from mhai_array.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -58,7 +58,7 @@ extern DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency;
LogicalDevice iedModel_ComplexArray = {
LogicalDeviceModelType,
"testComplexArray",
"ComplexArray",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_ComplexArray_LLN0
@ -589,12 +589,14 @@ DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency = {
IedModel iedModel = {
"test",
&iedModel_ComplexArray,
NULL,
NULL,
NULL,
NULL,
initializeValues
};

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_ComplexArray;

@ -15,13 +15,13 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include "filesystem.h"
#include "config_file_parser.h"
#include "hal_filesystem.h"
#include "iec61850_config_file_parser.h"
static int running = 0;

@ -1,6 +1,7 @@
MODEL{
LD(simpleIOGenericIO){
MODEL(simpleIO){
LD(GenericIO){
LN(LLN0){
SG(1 2)
DO(Mod 0){
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
@ -35,8 +36,8 @@ DE(GGIO1$MX$AnIn2);
DE(GGIO1$MX$AnIn3);
DE(GGIO1$MX$AnIn4);
}
RC(EventsRCB Events 0 Events 1 8 111 50 1000);
RC(AnalogValuesRCB AnalogValues 0 AnalogValues 1 8 111 50 1000);
RC(EventsRCB01 - 0 Events 1 8 111 50 1000);
RC(AnalogValuesRCB01 AnalogValues 0 AnalogValues 1 8 111 50 1000);
GC(gcbEvents events Events 2 0){
PA(4 111 1000 010ccd010001);
}

@ -5,7 +5,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
@ -44,23 +44,31 @@ checkHandler(void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck,
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4)
return CONTROL_ACCEPTED;
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO9)
return CONTROL_ACCEPTED;
return CONTROL_OBJECT_UNDEFINED;
}
void
static ControlHandlerResult
controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test)
{
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1)
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value);
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2)
else if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2)
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal, value);
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3)
else if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3)
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal, value);
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4)
else if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4)
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, value);
else
return CONTROL_RESULT_FAILED;
return CONTROL_RESULT_OK;
}
int
@ -91,7 +99,16 @@ main(int argc, char** argv)
/* this is optional - performs operative checks */
IedServer_setPerformCheckHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4, checkHandler,
IEDMODEL_GenericIO_GGIO1_SPCSO4);
IEDMODEL_GenericIO_GGIO1_SPCSO4);
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO9,
(ControlHandler) controlHandlerForBinaryOutput,
IEDMODEL_GenericIO_GGIO1_SPCSO9);
/* this is optional - performs operative checks */
IedServer_setPerformCheckHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO9, checkHandler,
IEDMODEL_GenericIO_GGIO1_SPCSO9);
/* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102);

@ -93,6 +93,11 @@
<Val>sbo-with-enhanced-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO9">
<DAI name="ctlModel">
<Val>direct-with-enhanced-security</Val>
</DAI>
</DOI>
</LN>
</LDevice>
</Server>
@ -127,6 +132,7 @@
<DO name="SPCSO6" type="SPC_1_SPCSO6" />
<DO name="SPCSO7" type="SPC_1_SPCSO7" />
<DO name="SPCSO8" type="SPC_1_SPCSO8" />
<DO name="SPCSO9" type="SPC_1_SPCSO3" />
<DO name="Ind1" type="SPS_1_Proxy" />
<DO name="Ind2" type="SPS_1_Proxy" />
<DO name="Ind3" type="SPS_1_Proxy" />

@ -4,7 +4,7 @@
* automatically generated from simpleIO_control_tests.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -277,6 +277,28 @@ extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_t;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_ctlModel;
extern DataObject iedModel_GenericIO_GGIO1_SPCSO9;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_Check;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_t;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_ctlModel;
extern DataObject iedModel_GenericIO_GGIO1_Ind1;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q;
@ -298,7 +320,7 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t;
LogicalDevice iedModel_GenericIO = {
LogicalDeviceModelType,
"simpleIOGenericIO",
"GenericIO",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_GenericIO_LLN0
@ -3195,7 +3217,7 @@ DataObject iedModel_GenericIO_GGIO1_SPCSO8 = {
DataObjectModelType,
"SPCSO8",
(ModelNode*) &iedModel_GenericIO_GGIO1,
(ModelNode*) &iedModel_GenericIO_GGIO1_Ind1,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO8_SBOw,
0
};
@ -3681,6 +3703,288 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_ctlModel = {
NULL,
0};
DataObject iedModel_GenericIO_GGIO1_SPCSO9 = {
DataObjectModelType,
"SPCSO9",
(ModelNode*) &iedModel_GenericIO_GGIO1,
(ModelNode*) &iedModel_GenericIO_GGIO1_Ind1,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper,
0
};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper = {
DataAttributeModelType,
"Oper",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlVal,
0,
CO,
CONSTRUCTED,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlVal = {
DataAttributeModelType,
"ctlVal",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin,
NULL,
0,
CO,
BOOLEAN,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin = {
DataAttributeModelType,
"origin",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlNum,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orCat,
0,
CO,
CONSTRUCTED,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orCat = {
DataAttributeModelType,
"orCat",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orIdent,
NULL,
0,
CO,
ENUMERATED,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orIdent = {
DataAttributeModelType,
"orIdent",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin,
NULL,
NULL,
0,
CO,
OCTET_STRING_64,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlNum = {
DataAttributeModelType,
"ctlNum",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_T,
NULL,
0,
CO,
INT8U,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_T = {
DataAttributeModelType,
"T",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_Test,
NULL,
0,
CO,
TIMESTAMP,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_Test = {
DataAttributeModelType,
"Test",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper_Check,
NULL,
0,
CO,
BOOLEAN,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_Check = {
DataAttributeModelType,
"Check",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Oper,
NULL,
NULL,
0,
CO,
CHECK,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel = {
DataAttributeModelType,
"Cancel",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_stVal,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlVal,
0,
CO,
CONSTRUCTED,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlVal = {
DataAttributeModelType,
"ctlVal",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin,
NULL,
0,
CO,
BOOLEAN,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin = {
DataAttributeModelType,
"origin",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlNum,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orCat,
0,
CO,
CONSTRUCTED,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orCat = {
DataAttributeModelType,
"orCat",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orIdent,
NULL,
0,
CO,
ENUMERATED,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orIdent = {
DataAttributeModelType,
"orIdent",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin,
NULL,
NULL,
0,
CO,
OCTET_STRING_64,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlNum = {
DataAttributeModelType,
"ctlNum",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_T,
NULL,
0,
CO,
INT8U,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_T = {
DataAttributeModelType,
"T",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel_Test,
NULL,
0,
CO,
TIMESTAMP,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_Test = {
DataAttributeModelType,
"Test",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_Cancel,
NULL,
NULL,
0,
CO,
BOOLEAN,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_stVal = {
DataAttributeModelType,
"stVal",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_q,
NULL,
0,
ST,
BOOLEAN,
0 + TRG_OPT_DATA_CHANGED,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_q = {
DataAttributeModelType,
"q",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_t,
NULL,
0,
ST,
QUALITY,
0 + TRG_OPT_QUALITY_CHANGED,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_t = {
DataAttributeModelType,
"t",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9,
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9_ctlModel,
NULL,
0,
ST,
TIMESTAMP,
0,
NULL,
0};
DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_ctlModel = {
DataAttributeModelType,
"ctlModel",
(ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO9,
NULL,
NULL,
0,
CF,
ENUMERATED,
0,
NULL,
0};
DataObject iedModel_GenericIO_GGIO1_Ind1 = {
DataObjectModelType,
"Ind1",
@ -3878,22 +4182,13 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = {
IedModel iedModel = {
"simpleIO",
&iedModel_GenericIO,
NULL,
NULL,
NULL,
NULL,
initializeValues
};
@ -3920,4 +4215,6 @@ iedModel_GenericIO_GGIO1_SPCSO6_ctlModel.mmsValue = MmsValue_newIntegerFromInt32
iedModel_GenericIO_GGIO1_SPCSO7_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(3);
iedModel_GenericIO_GGIO1_SPCSO8_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(4);
iedModel_GenericIO_GGIO1_SPCSO9_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(3);
}

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_GenericIO;
@ -280,6 +280,28 @@ extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_t;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO8_ctlModel;
extern DataObject iedModel_GenericIO_GGIO1_SPCSO9;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Oper_Check;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_Cancel_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_t;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO9_ctlModel;
extern DataObject iedModel_GenericIO_GGIO1_Ind1;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q;
@ -568,6 +590,28 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t;
#define IEDMODEL_GenericIO_GGIO1_SPCSO8_q (&iedModel_GenericIO_GGIO1_SPCSO8_q)
#define IEDMODEL_GenericIO_GGIO1_SPCSO8_t (&iedModel_GenericIO_GGIO1_SPCSO8_t)
#define IEDMODEL_GenericIO_GGIO1_SPCSO8_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO8_ctlModel)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9 (&iedModel_GenericIO_GGIO1_SPCSO9)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper (&iedModel_GenericIO_GGIO1_SPCSO9_Oper)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orCat)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_origin_orIdent)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_ctlNum)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_T)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_Test)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO9_Oper_Check)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel_origin (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orCat)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel_origin_orIdent)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel_ctlNum)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel_T (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel_T)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_Cancel_Test (&iedModel_GenericIO_GGIO1_SPCSO9_Cancel_Test)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_stVal (&iedModel_GenericIO_GGIO1_SPCSO9_stVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_q (&iedModel_GenericIO_GGIO1_SPCSO9_q)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_t (&iedModel_GenericIO_GGIO1_SPCSO9_t)
#define IEDMODEL_GenericIO_GGIO1_SPCSO9_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO9_ctlModel)
#define IEDMODEL_GenericIO_GGIO1_Ind1 (&iedModel_GenericIO_GGIO1_Ind1)
#define IEDMODEL_GenericIO_GGIO1_Ind1_stVal (&iedModel_GenericIO_GGIO1_Ind1_stVal)
#define IEDMODEL_GenericIO_GGIO1_Ind1_q (&iedModel_GenericIO_GGIO1_Ind1_q)

@ -6,7 +6,7 @@
*/
#include "iec61850_server.h"
#include "thread.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
@ -40,6 +40,8 @@ int main(int argc, char** argv) {
DataObject* lln0_mod = CDC_ENS_create("Mod", (ModelNode*) lln0, 0);
DataObject* lln0_health = CDC_ENS_create("Health", (ModelNode*) lln0, 0);
SettingGroupControlBlock_create(lln0, 1, 1);
/* Add a temperature sensor LN */
LogicalNode* ttmp1 = LogicalNode_create("TTMP1", lDevice1);
DataObject* ttmp1_tmpsv = CDC_SAV_create("TmpSv", (ModelNode*) ttmp1, 0, false);

@ -7,7 +7,7 @@
*/
#include "iec61850_server.h"
#include "thread.h" /* for Thread_sleep() */
#include "hal_thread.h" /* for Thread_sleep() */
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

@ -17,7 +17,7 @@
</Address>
<GSE ldInst="GenericIO" cbName="gcbEvents">
<Address>
<P type="VLAN-ID">111</P>
<P type="VLAN-ID">1</P>
<P type="VLAN-PRIORITY">4</P>
<P type="MAC-Address">01-0c-cd-01-00-01</P>
<P type="APPID">1000</P>
@ -25,7 +25,7 @@
</GSE>
<GSE ldInst="GenericIO" cbName="gcbAnalogValues">
<Address>
<P type="VLAN-ID">111</P>
<P type="VLAN-ID">1</P>
<P type="VLAN-PRIORITY">4</P>
<P type="MAC-Address">01-0c-cd-01-00-01</P>
<P type="APPID">1000</P>

@ -4,7 +4,7 @@
* automatically generated from simpleIO_direct_control_goose.icd
*/
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
static void initializeValues();
@ -159,7 +159,7 @@ extern DataSetEntry ds_GenericIO_LLN0_Events_fcda2;
extern DataSetEntry ds_GenericIO_LLN0_Events_fcda3;
DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO1$stVal",
-1,
NULL,
@ -168,7 +168,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda0 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO2$stVal",
-1,
NULL,
@ -177,7 +177,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda1 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO3$stVal",
-1,
NULL,
@ -186,7 +186,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda2 = {
};
DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$ST$SPCSO4$stVal",
-1,
NULL,
@ -195,7 +195,7 @@ DataSetEntry ds_GenericIO_LLN0_Events_fcda3 = {
};
DataSet ds_GenericIO_LLN0_Events = {
"simpleIOGenericIO",
"GenericIO",
"LLN0$Events",
4,
&ds_GenericIO_LLN0_Events_fcda0,
@ -208,7 +208,7 @@ extern DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda2;
extern DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda3;
DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda0 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$MX$AnIn1",
-1,
NULL,
@ -217,7 +217,7 @@ DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda0 = {
};
DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda1 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$MX$AnIn2",
-1,
NULL,
@ -226,7 +226,7 @@ DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda1 = {
};
DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda2 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$MX$AnIn3",
-1,
NULL,
@ -235,7 +235,7 @@ DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda2 = {
};
DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda3 = {
"simpleIOGenericIO",
"GenericIO",
"GGIO1$MX$AnIn4",
-1,
NULL,
@ -244,7 +244,7 @@ DataSetEntry ds_GenericIO_LLN0_AnalogValues_fcda3 = {
};
DataSet ds_GenericIO_LLN0_AnalogValues = {
"simpleIOGenericIO",
"GenericIO",
"LLN0$AnalogValues",
4,
&ds_GenericIO_LLN0_AnalogValues_fcda0,
@ -253,7 +253,7 @@ DataSet ds_GenericIO_LLN0_AnalogValues = {
LogicalDevice iedModel_GenericIO = {
LogicalDeviceModelType,
"simpleIOGenericIO",
"GenericIO",
(ModelNode*) &iedModel,
NULL,
(ModelNode*) &iedModel_GenericIO_LLN0
@ -1970,7 +1970,7 @@ extern GSEControlBlock iedModel_GenericIO_LLN0_gse1;
static PhyComAddress iedModel_GenericIO_LLN0_gse0_address = {
4,
111,
1,
1000,
{0x1, 0xc, 0xcd, 0x1, 0x0, 0x1}
};
@ -1979,7 +1979,7 @@ GSEControlBlock iedModel_GenericIO_LLN0_gse0 = {&iedModel_GenericIO_LLN0, "gcbEv
static PhyComAddress iedModel_GenericIO_LLN0_gse1_address = {
4,
111,
1,
1000,
{0x1, 0xc, 0xcd, 0x1, 0x0, 0x1}
};
@ -1987,12 +1987,14 @@ static PhyComAddress iedModel_GenericIO_LLN0_gse1_address = {
GSEControlBlock iedModel_GenericIO_LLN0_gse1 = {&iedModel_GenericIO_LLN0, "gcbAnalogValues", "analog", "AnalogValues", 2, false,&iedModel_GenericIO_LLN0_gse1_address, NULL};
IedModel iedModel = {
"simpleIO",
&iedModel_GenericIO,
&ds_GenericIO_LLN0_Events,
&iedModel_GenericIO_LLN0_report0,
&iedModel_GenericIO_LLN0_gse0,
NULL,
initializeValues
};

@ -8,7 +8,7 @@
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "model.h"
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_GenericIO;

@ -1,24 +1,9 @@
INCLUDES = -I$(LIBIEC_HOME)/config
INCLUDES += -I$(LIBIEC_HOME)/src/common
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_presentation
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_session
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_cotp
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_acse
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_mms/common
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_mms/client
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_mms/server
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_client
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_common
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_server
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/iedcommon
INCLUDES += -I$(LIBIEC_HOME)/src/iedserver/mms_mapping
INCLUDES += -I$(LIBIEC_HOME)/src/iedserver/model
INCLUDES += -I$(LIBIEC_HOME)/src/iedserver
INCLUDES += -I$(LIBIEC_HOME)/src/iedclient
INCLUDES += -I$(LIBIEC_HOME)/src/hal
INCLUDES += -I$(LIBIEC_HOME)/src/hal/thread
INCLUDES += -I$(LIBIEC_HOME)/src/hal/socket
INCLUDES += -I$(LIBIEC_HOME)/src/hal/filesystem
INCLUDES += -I$(LIBIEC_HOME)/src/hal/time
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

@ -4,7 +4,7 @@ MIPSEL_TOOLCHAIN_PREFIX=mipsel-openwrt-linux-
# ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-
#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
#ARM_TOOLCHAIN_PREFIX=arm-poky-linux-gnueabi-
ARM_TOOLCHAIN_PREFIX=arm-linux-
ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
UCLINUX_ARM_TOOLCHAIN_PREFIX=arm-uclinux-elf-
MINGW_TOOLCHAIN_PREFIX=i586-mingw32msvc-
#MINGW_TOOLCHAIN_PREFIX=x86_64-w64-mingw32-
@ -126,7 +126,7 @@ LIB_OBJS_DIR = $(LIBIEC_HOME)/build
endif
CFLAGS += -g
CFLAGS += -Os
#CFLAGS += -Os
DYNLIB_LDFLAGS=-lpthread
endif

@ -5,12 +5,12 @@ set (lib_common_SRCS
./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
./common/mem_alloc_linked_list.c
./common/simple_allocator.c
./common/byte_stream.c
./mms/iso_server/iso_connection.c
./mms/iso_server/iso_server.c
./mms/iso_acse/acse.c
@ -52,24 +52,25 @@ set (lib_common_SRCS
./mms/asn1/asn1_ber_primitive_value.c
./mms/asn1/ber_encoder.c
./mms/asn1/ber_integer.c
./mms/iso_client/impl/iso_client_connection.c
./mms/iso_client/iso_client_connection.c
./mms/iso_common/iso_connection_parameters.c
./mms/iso_session/iso_session.c
./iedclient/impl/client_control.c
./iedclient/impl/client_report_control.c
./iedclient/impl/client_report.c
./iedclient/impl/ied_connection.c
./iedcommon/iec61850_common.c
./iedserver/impl/ied_server.c
./iedserver/impl/client_connection.c
./iedserver/model/model.c
./iedserver/model/dynamic_model.c
./iedserver/model/cdc.c
./iedserver/model/config_file_parser.c
./iedserver/mms_mapping/control.c
./iedserver/mms_mapping/mms_mapping.c
./iedserver/mms_mapping/reporting.c
./iedserver/mms_mapping/mms_goose.c
./iec61850/client/client_control.c
./iec61850/client/client_report_control.c
./iec61850/client/client_goose_control.c
./iec61850/client/client_report.c
./iec61850/client/ied_connection.c
./iec61850/common/iec61850_common.c
./iec61850/server/impl/ied_server.c
./iec61850/server/impl/client_connection.c
./iec61850/server/model/model.c
./iec61850/server/model/dynamic_model.c
./iec61850/server/model/cdc.c
./iec61850/server/model/config_file_parser.c
./iec61850/server/mms_mapping/control.c
./iec61850/server/mms_mapping/mms_mapping.c
./iec61850/server/mms_mapping/reporting.c
./iec61850/server/mms_mapping/mms_goose.c
)
@ -168,6 +169,7 @@ set (lib_asn1c_SRCS
set (lib_goose_SRCS
./goose/goose_subscriber.c
./goose/goose_receiver.c
./goose/goose_publisher.c
)
@ -274,8 +276,6 @@ set_target_properties(iec61850-shared PROPERTIES
)
GENERATE_EXPORT_HEADER(iec61850-shared
BASE_NAME iec61850-shared
EXPORT_MACRO_NAME iec61850-shared_EXPORT
@ -286,12 +286,21 @@ GENERATE_EXPORT_HEADER(iec61850-shared
add_library (iec61850 STATIC ${library_SRCS})
IF(UNIX)
target_link_libraries (iec61850
-lpthread
-lm
)
IF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
target_link_libraries (iec61850
-lpthread
-lm
-lrt
)
ELSE ()
target_link_libraries (iec61850
-lpthread
-lm
)
ENDIF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
ENDIF(UNIX)
iF(WITH_WPCAP)
target_link_libraries(iec61850
${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/lib/wpcap.lib
@ -318,6 +327,8 @@ if(MSVC)
endif()
ENDIF(WITH_WPCAP)
install (TARGETS iec61850 iec61850-shared
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib

@ -42,7 +42,7 @@ BufferChain_destroy(BufferChain self)
while (currentChainElement != NULL) {
BufferChain nextChainElement = currentChainElement->nextPart;
free(currentChainElement);
GLOBAL_FREEMEM(currentChainElement);
currentChainElement = nextChainElement;
}
}

@ -28,10 +28,10 @@ ByteBuffer*
ByteBuffer_create(ByteBuffer* self, int maxSize)
{
if (self == NULL) {
self = (ByteBuffer*) calloc(1, sizeof(ByteBuffer));
self = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer));
}
self->buffer = (uint8_t*) calloc(maxSize, sizeof(uint8_t));
self->buffer = (uint8_t*) GLOBAL_CALLOC(maxSize, sizeof(uint8_t));
self->maxSize = maxSize;
self->size = 0;
@ -41,8 +41,8 @@ ByteBuffer_create(ByteBuffer* self, int maxSize)
void
ByteBuffer_destroy(ByteBuffer* self)
{
free(self->buffer);
free(self);
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self);
}
void
@ -105,6 +105,7 @@ ByteBuffer_setSize(ByteBuffer* self, int size)
return self->size;
}
#if 0
void
ByteBuffer_print(ByteBuffer* self, char* message)
{
@ -119,3 +120,4 @@ ByteBuffer_print(ByteBuffer* self, char* message)
}
printf("\n");
}
#endif

@ -0,0 +1,83 @@
/*
* lib_memory.c
*
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "lib_memory.h"
static MemoryExceptionHandler exceptionHandler = NULL;
static void* exceptionHandlerParameter = NULL;
static void
noMemoryAvailableHandler(void)
{
if (exceptionHandler != NULL)
exceptionHandler(exceptionHandlerParameter);
}
void
Memory_installExceptionHandler(MemoryExceptionHandler handler, void* parameter)
{
exceptionHandler = handler;
exceptionHandlerParameter = parameter;
}
void*
Memory_malloc(size_t size)
{
void* memory = malloc(size);
if (memory == NULL)
noMemoryAvailableHandler();
return memory;
}
void*
Memory_calloc(size_t nmemb, size_t size)
{
void* memory = calloc(nmemb, size);
if (memory == NULL)
noMemoryAvailableHandler();
return memory;
}
void *
Memory_realloc(void *ptr, size_t size)
{
void* memory = realloc(ptr, size);
if (memory == NULL)
noMemoryAvailableHandler();
return memory;
}
void
Memory_free(void* memb)
{
free(memb);
}

@ -1,7 +1,7 @@
/*
* linked_list.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -38,7 +38,7 @@ LinkedList_create()
{
LinkedList newList;
newList = (LinkedList) malloc(sizeof(struct sLinkedList));
newList = (LinkedList) GLOBAL_MALLOC(sizeof(struct sLinkedList));
newList->data = NULL;
newList->next = NULL;
@ -57,9 +57,11 @@ LinkedList_destroyDeep(LinkedList list, LinkedListValueDeleteFunction valueDelet
do {
currentElement = nextElement;
nextElement = currentElement->next;
if (currentElement->data != NULL)
valueDeleteFunction(currentElement->data);
free(currentElement);
GLOBAL_FREEMEM(currentElement);
}
while (nextElement != NULL);
}
@ -67,7 +69,7 @@ LinkedList_destroyDeep(LinkedList list, LinkedListValueDeleteFunction valueDelet
void
LinkedList_destroy(LinkedList list)
{
LinkedList_destroyDeep(list, free);
LinkedList_destroyDeep(list, Memory_free);
}
/**
@ -82,7 +84,7 @@ LinkedList_destroyStatic(LinkedList list)
do {
currentElement = nextElement;
nextElement = currentElement->next;
free(currentElement);
GLOBAL_FREEMEM(currentElement);
}
while (nextElement != NULL);
}
@ -123,7 +125,7 @@ LinkedList_remove(LinkedList list, void* data)
while (currentElement != NULL) {
if (currentElement->data == data) {
lastElement->next = currentElement->next;
free(currentElement);
GLOBAL_FREEMEM(currentElement);
return true;
}

@ -42,7 +42,7 @@ comparePointerKeys(void* key1, void* key2)
Map
Map_create()
{
Map map = (Map) calloc(1, sizeof(struct sMap));
Map map = (Map) GLOBAL_CALLOC(1, sizeof(struct sMap));
map->entries = LinkedList_create();
map->compareKeys = comparePointerKeys;
return map;
@ -57,7 +57,7 @@ Map_size(Map map)
void*
Map_addEntry(Map map, void* key, void* value)
{
MapEntry* entry = (MapEntry*) malloc(sizeof(MapEntry));
MapEntry* entry = (MapEntry*) GLOBAL_MALLOC(sizeof(MapEntry));
entry->key = key;
entry->value = value;
LinkedList_add(map->entries, entry);
@ -81,9 +81,9 @@ Map_removeEntry(Map map, void* key, bool deleteKey)
value = entry->value;
if (deleteKey == true)
free(entry->key);
free(entry);
free(element);
GLOBAL_FREEMEM(entry->key);
GLOBAL_FREEMEM(entry);
GLOBAL_FREEMEM(element);
break;
}
@ -117,12 +117,12 @@ Map_delete(Map map, bool deleteKey)
while ((element = LinkedList_getNext(element)) != NULL) {
MapEntry* entry = (MapEntry*) element->data;
if (deleteKey == true)
free(entry->key);
free(entry->value);
GLOBAL_FREEMEM(entry->key);
GLOBAL_FREEMEM(entry->value);
}
LinkedList_destroy(map->entries);
free(map);
GLOBAL_FREEMEM(map);
}
void
@ -133,12 +133,12 @@ Map_deleteStatic(Map map, bool deleteKey)
if (deleteKey == true) {
while ((element = LinkedList_getNext(element)) != NULL) {
MapEntry* entry = (MapEntry*) element->data;
free(entry->key);
GLOBAL_FREEMEM(entry->key);
}
}
LinkedList_destroy(map->entries);
free(map);
GLOBAL_FREEMEM(map);
}
void
@ -150,10 +150,10 @@ Map_deleteDeep(Map map, bool deleteKey, void
while ((element = LinkedList_getNext(element)) != NULL) {
MapEntry* entry = (MapEntry*) element->data;
if (deleteKey == true)
free(entry->key);
GLOBAL_FREEMEM(entry->key);
valueDeleteFunction(entry->value);
}
LinkedList_destroy(map->entries);
free(map);
GLOBAL_FREEMEM(map);
}

@ -1,7 +1,7 @@
/*
* simple_allocator.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -33,9 +33,20 @@ MemoryAllocator_init(MemoryAllocator* self, char* memoryBlock, int size)
self->size = size;
}
static int
getAlignedSize(int size)
{
if ((size % sizeof(void*)) > 0)
return sizeof(void*) * ((size + sizeof(void*)) / sizeof(void*));
else
return size;
}
char*
MemoryAllocator_allocate(MemoryAllocator* self, int size)
{
size = getAlignedSize(size);
if (((self->currentPtr - self->memoryBlock) + size) <= self->size) {
char* ptr = self->currentPtr;
self->currentPtr += size;

@ -28,7 +28,7 @@ copySubString(char* startPos, char* endPos)
{
int newStringLength = endPos - startPos;
char* newString = (char*) malloc(newStringLength) + 1;
char* newString = (char*) GLOBAL_MALLOC(newStringLength) + 1;
memcpy(newString, startPos, newStringLength);
@ -42,7 +42,7 @@ copyString(const char* string)
{
int newStringLength = strlen(string) + 1;
char* newString = (char*) malloc(newStringLength);
char* newString = (char*) GLOBAL_MALLOC(newStringLength);
memcpy(newString, string, newStringLength);
@ -50,7 +50,7 @@ copyString(const char* string)
}
char*
copyStringToBuffer(char* string, char* buffer)
copyStringToBuffer(const char* string, char* buffer)
{
int newStringLength = strlen(string) + 1;
@ -63,7 +63,7 @@ copyStringToBuffer(char* string, char* buffer)
char*
createStringFromBuffer(uint8_t* buf, int size)
{
char* newStr = (char*) malloc(size + 1);
char* newStr = (char*) GLOBAL_MALLOC(size + 1);
memcpy(newStr, buf, size);
newStr[size] = 0;
@ -110,7 +110,7 @@ createString(int count, ...)
}
va_end(ap);
newStr = (char*) malloc(newStringLength + 1);
newStr = (char*) GLOBAL_MALLOC(newStringLength + 1);
currentPos = newStr;

@ -16,9 +16,9 @@
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "libIEC61850 0.7.8"
PROJECT_NAME = "libIEC61850"
PROJECT_NUMBER =
PROJECT_NUMBER = 0.8.2
PROJECT_BRIEF = "Open-source IEC 61850 MMS/GOOSE server and client library"
@ -207,29 +207,31 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = "iedclient/iec61850_client.h"
INPUT += "mms/iso_mms/common/mms_value.h"
INPUT += "common/linked_list.h"
INPUT = "iec61850/inc/iec61850_client.h"
INPUT += "mms/inc/mms_value.h"
INPUT += "common/inc/linked_list.h"
INPUT += "doxygen/mainpage.doxygen"
INPUT += "iedserver/iec61850_server.h"
INPUT += "iedcommon/iec61850_common.h"
INPUT += "iedserver/model/model.h"
INPUT += "iedserver/model/dynamic_model.h"
INPUT += "iedserver/model/config_file_parser.h"
INPUT += "iedserver/model/cdc.h"
INPUT += "iec61850/inc/iec61850_server.h"
INPUT += "iec61850/inc/iec61850_common.h"
INPUT += "iec61850/inc/iec61850_model.h"
INPUT += "iec61850/inc/iec61850_dynamic_model.h"
INPUT += "iec61850/inc/iec61850_config_file_parser.h"
INPUT += "iec61850/inc/iec61850_cdc.h"
INPUT += "goose/goose_subscriber.h"
INPUT += "mms/iso_mms/server/mms_device_model.h"
INPUT += "mms/iso_mms/common/mms_types.h"
INPUT += "mms/iso_mms/server/mms_server.h"
INPUT += "mms/iso_server/iso_server.h"
INPUT += "mms/iso_mms/server/mms_named_variable_list.h"
INPUT += "mms/iso_mms/client/mms_client_connection.h"
INPUT += "mms/iso_client/iso_connection_parameters.h"
INPUT += "hal/socket/socket.h"
INPUT += "hal/thread/thread.h"
INPUT += "hal/ethernet/ethernet.h"
INPUT += "hal/filesystem/filesystem.h"
INPUT += "hal/hal.h"
INPUT += "mms/inc/mms_device_model.h"
INPUT += "mms/inc/mms_types.h"
INPUT += "mms/inc/mms_server.h"
INPUT += "mms/inc/iso_server.h"
INPUT += "mms/inc/mms_named_variable_list.h"
INPUT += "mms/inc/mms_type_spec.h"
INPUT += "mms/inc/mms_types.h"
INPUT += "mms/inc/mms_client_connection.h"
INPUT += "mms/inc/iso_connection_parameters.h"
INPUT += "hal/inc/hal_socket.h"
INPUT += "hal/inc/hal_thread.h"
INPUT += "hal/inc/hal_ethernet.h"
INPUT += "hal/inc/hal_filesystem.h"
INPUT += "hal/inc/hal_time.h"
INPUT_ENCODING = UTF-8

@ -24,11 +24,15 @@
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include "goose_publisher.h"
#include "ethernet.h"
#include "hal_ethernet.h"
#include "ber_encoder.h"
#include "mms_server_internal.h"
#include "mms_value_internal.h"
#ifndef DEBUG_GOOSE_PUBLISHER
#define DEBUG_GOOSE_PUBLISHER 0
#endif
#define GOOSE_MAX_MESSAGE_SIZE 1518
static void
@ -36,7 +40,7 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, char* interf
struct sGoosePublisher {
uint8_t* buffer;
uint16_t appId;
//uint16_t appId;
EthernetSocket ethernetSocket;
int lengthField;
int payloadStart;
@ -45,9 +49,9 @@ struct sGoosePublisher {
char* goCBRef;
char* dataSetRef;
uint16_t minTime;
uint16_t maxTime;
bool fixedOffs;
//uint16_t minTime;
//uint16_t maxTime;
//bool fixedOffs;
uint32_t confRev;
uint32_t stNum;
@ -63,7 +67,7 @@ struct sGoosePublisher {
GoosePublisher
GoosePublisher_create(CommParameters* parameters, char* interfaceID)
{
GoosePublisher self = (GoosePublisher) calloc(1, sizeof(struct sGoosePublisher));
GoosePublisher self = (GoosePublisher) GLOBAL_CALLOC(1, sizeof(struct sGoosePublisher));
prepareGooseBuffer(self, parameters, interfaceID);
@ -82,16 +86,16 @@ GoosePublisher_destroy(GoosePublisher self)
MmsValue_delete(self->timestamp);
if (self->goID != NULL)
free(self->goID);
GLOBAL_FREEMEM(self->goID);
if (self->goCBRef != NULL)
free(self->goCBRef);
GLOBAL_FREEMEM(self->goCBRef);
if (self->dataSetRef != NULL)
free(self->dataSetRef);
GLOBAL_FREEMEM(self->dataSetRef);
free(self->buffer);
free(self);
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self);
}
void
@ -190,7 +194,7 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, char* interf
else
self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr);
self->buffer = (uint8_t*) malloc(GOOSE_MAX_MESSAGE_SIZE);
self->buffer = (uint8_t*) GLOBAL_MALLOC(GOOSE_MAX_MESSAGE_SIZE);
memcpy(self->buffer, dstAddr, 6);
memcpy(self->buffer + 6, srcAddr, 6);
@ -246,6 +250,8 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
if (self->goID != NULL)
goosePduLength += BerEncoder_determineEncodedStringSize(self->goID);
else
goosePduLength += BerEncoder_determineEncodedStringSize(self->goCBRef);
uint32_t timeAllowedToLive = self->timeAllowedToLive;
@ -303,6 +309,8 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
/* Encode goID */
if (self->goID != NULL)
bufPos = BerEncoder_encodeStringWithTag(0x83, self->goID, buffer, bufPos);
else
bufPos = BerEncoder_encodeStringWithTag(0x83, self->goCBRef, buffer, bufPos);
/* Encode t */
bufPos = BerEncoder_encodeOctetString(0x84, self->timestamp->value.utcTime, 8, buffer, bufPos);
@ -363,6 +371,9 @@ GoosePublisher_publish(GoosePublisher self, LinkedList dataSet)
self->buffer[lengthIndex] = gooseLength / 256;
self->buffer[lengthIndex + 1] = gooseLength & 0xff;
if (DEBUG_GOOSE_PUBLISHER)
printf("GOOSE_PUBLISHER: send GOOSE message\n");
Ethernet_sendPacket(self->ethernetSocket, self->buffer, self->payloadStart + payloadLength);
return 0;

@ -0,0 +1,778 @@
/*
* goose_receiver.c
*
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include "goose_subscriber.h"
#include "hal_ethernet.h"
#include "hal_thread.h"
#include "ber_decode.h"
#include "mms_value.h"
#include "mms_value_internal.h"
#include "linked_list.h"
#include "goose_receiver.h"
#include "goose_receiver_internal.h"
#ifndef DEBUG_GOOSE_SUBSCRIBER
#define DEBUG_GOOSE_SUBSCRIBER 0
#endif
#define ETH_BUFFER_LENGTH 1518
#define ETH_P_GOOSE 0x88b8
struct sGooseReceiver {
bool running;
char* interfaceId;
uint8_t* buffer;
EthernetSocket ethSocket;
LinkedList subscriberList;
};
GooseReceiver
GooseReceiver_create()
{
GooseReceiver self = (GooseReceiver) GLOBAL_MALLOC(sizeof(struct sGooseReceiver));
if (self != NULL) {
self->running = false;
self->interfaceId = NULL;
self->buffer = (uint8_t*) GLOBAL_MALLOC(ETH_BUFFER_LENGTH);
self->ethSocket = NULL;
self->subscriberList = LinkedList_create();
}
return self;
}
void
GooseReceiver_addSubscriber(GooseReceiver self, GooseSubscriber subscriber)
{
LinkedList_add(self->subscriberList, (void*) subscriber);
}
void
GooseReceiver_removeSubscriber(GooseReceiver self, GooseSubscriber subscriber)
{
LinkedList_remove(self->subscriberList, (void*) subscriber);
}
void
GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId)
{
if (self->interfaceId != NULL)
GLOBAL_FREEMEM(self->interfaceId);
self->interfaceId = copyString(interfaceId);
}
void
GooseReceiver_setBackupListener(GooseReceiver self)
{
}
static void
createNewStringFromBufferElement(MmsValue* value, uint8_t* bufferSrc, int elementLength)
{
value->value.visibleString.buf = (char*) GLOBAL_MALLOC(elementLength + 1);
memcpy(value->value.visibleString.buf, bufferSrc, elementLength);
value->value.visibleString.buf[elementLength] = 0;
value->value.visibleString.size = elementLength;
}
static int
parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
{
int bufPos = 0;
int elementLength = 0;
int elementIndex = 0;
int maxIndex = MmsValue_getArraySize(dataSetValues) - 1;
while (bufPos < allDataLength) {
uint8_t tag = buffer[bufPos++];
if (elementIndex > maxIndex) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: too much elements!\n");
return 0;
}
MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex);
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos + elementLength > allDataLength) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
return 0;
}
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 (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 (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 (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");
break;
case 0x84: /* BIT STRING */
if (MmsValue_getType(value) == MMS_BIT_STRING) {
int padding = buffer[bufPos];
int bitStringLength = (8 * (elementLength - 1)) - padding;
if (bitStringLength == value->value.bitString.size) {
memcpy(value->value.bitString.buf, buffer + bufPos + 1,
elementLength - 1);
}
else
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) {
value->value.integer->size = elementLength;
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
}
}
break;
case 0x86: /* unsigned integer */
if (MmsValue_getType(value) == MMS_UNSIGNED) {
if (elementLength <= value->value.integer->maxSize) {
value->value.integer->size = elementLength;
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
}
}
break;
case 0x87: /* Float */
if (MmsValue_getType(value) == MMS_FLOAT) {
if (elementLength == 9) {
MmsValue_setDouble(value, BerDecoder_decodeDouble(buffer, bufPos));
}
else if (elementLength == 5) {
MmsValue_setFloat(value, BerDecoder_decodeFloat(buffer, bufPos));
}
}
break;
case 0x89: /* octet string */
if (MmsValue_getType(value) == MMS_OCTET_STRING) {
if (elementLength <= value->value.octetString.maxSize) {
value->value.octetString.size = elementLength;
memcpy(value->value.octetString.buf, buffer + bufPos, elementLength);
}
}
break;
case 0x8a: /* visible string */
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
if (value->value.visibleString.buf != NULL) {
if ((int32_t) value->value.visibleString.size >= elementLength) {
memcpy(value->value.visibleString.buf, buffer + bufPos, elementLength);
value->value.visibleString.buf[elementLength] = 0;
}
else {
GLOBAL_FREEMEM(value->value.visibleString.buf);
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
}
}
else
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
}
break;
case 0x8c: /* binary time */
if (MmsValue_getType(value) == MMS_BINARY_TIME) {
if ((elementLength == 4) || (elementLength == 6)) {
memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength);
}
}
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");
}
else
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);
break;
}
bufPos += elementLength;
elementIndex++;
}
return 1;
}
static MmsValue*
parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLength, bool isStructure)
{
int bufPos = 0;
int elementLength = 0;
int elementIndex = 0;
MmsValue* dataSetValues = NULL;
while (bufPos < allDataLength) {
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos + elementLength > allDataLength) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
goto exit_with_error;
}
switch (tag) {
case 0x80: /* reserved for access result */
break;
case 0xa1: /* array */
break;
case 0xa2: /* structure */
break;
case 0x83: /* boolean */
break;
case 0x84: /* BIT STRING */
break;
case 0x85: /* integer */
break;
case 0x86: /* unsigned integer */
break;
case 0x87: /* Float */
break;
case 0x89: /* octet string */
break;
case 0x8a: /* visible string */
break;
case 0x8c: /* binary time */
break;
case 0x91: /* Utctime */
break;
default:
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
goto exit_with_error;
}
bufPos += elementLength;
elementIndex++;
}
if (isStructure)
dataSetValues = MmsValue_createEmptyStructure(elementIndex);
else
dataSetValues = MmsValue_createEmtpyArray(elementIndex);
elementIndex = 0;
bufPos = 0;
while (bufPos < allDataLength) {
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos + elementLength > allDataLength) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
goto exit_with_error;
}
MmsValue* value = NULL;
switch (tag) {
case 0xa1: /* array */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n");
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, false);
if (value == NULL)
goto exit_with_error;
break;
case 0xa2: /* structure */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n");
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, true);
if (value == NULL)
goto exit_with_error;
break;
case 0x83: /* boolean */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n");
value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
break;
case 0x84: /* BIT STRING */
{
int padding = buffer[bufPos];
int bitStringLength = (8 * (elementLength - 1)) - padding;
value = MmsValue_newBitString(bitStringLength);
memcpy(value->value.bitString.buf, buffer + bufPos + 1, elementLength - 1);
}
break;
case 0x85: /* integer */
value = MmsValue_newInteger(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
break;
case 0x86: /* unsigned integer */
value = MmsValue_newUnsigned(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, 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));
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);
else if (elementLength == 6)
value = MmsValue_newBinaryTime(false);
if ((elementLength == 4) || (elementLength == 6))
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");
break;
default:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
goto exit_with_error;
}
bufPos += elementLength;
if (value != NULL) {
MmsValue_setElement(dataSetValues, elementIndex, value);
elementIndex++;
}
}
self->dataSetValuesSelfAllocated = true;
return dataSetValues;
exit_with_error:
if (dataSetValues != NULL)
MmsValue_delete(dataSetValues);
return NULL;
}
static int
parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
{
int bufPos = 0;
uint32_t timeAllowedToLive = 0;
uint32_t stNum = 0;
uint32_t sqNum = 0;
uint32_t confRev;
bool simulation = false;
bool ndsCom = false;
GooseSubscriber matchingSubscriber = NULL;
uint8_t* timestampBufPos = NULL;
uint8_t* dataSetBufferAddress = NULL;
int dataSetBufferLength = 0;
uint32_t numberOfDatSetEntries = 0;
if (buffer[bufPos++] == 0x61) {
int gooseLength;
bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength);
int gooseEnd = bufPos + gooseLength;
while (bufPos < gooseEnd) {
int elementLength;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength);
if (bufPos + elementLength > apduLength) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
goto exit_with_fault;
}
if (bufPos == -1)
goto exit_with_fault;
switch(tag) {
case 0x80: /* gocbRef */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found gocbRef\n");
{
LinkedList element = LinkedList_getNext(self->subscriberList);
while (element != NULL) {
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
if (subscriber->goCBRefLen == elementLength) {
if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n");
matchingSubscriber = subscriber;
break;
}
}
element = LinkedList_getNext(element);
}
if (matchingSubscriber == NULL)
return 0;
}
break;
case 0x81: /* timeAllowedToLive */
timeAllowedToLive = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
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");
break;
case 0x83:
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");
break;
case 0x85:
stNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
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);
break;
case 0x87:
simulation = BerDecoder_decodeBoolean(buffer, bufPos);
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);
break;
case 0x89:
ndsCom = BerDecoder_decodeBoolean(buffer, bufPos);
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);
break;
case 0xab:
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);
break;
}
bufPos += elementLength;
}
if (matchingSubscriber != NULL) {
matchingSubscriber->timeAllowedToLive = timeAllowedToLive;
matchingSubscriber->confRev = confRev;
matchingSubscriber->ndsCom = ndsCom;
matchingSubscriber->simulation = simulation;
MmsValue_setUtcTimeByBuffer(matchingSubscriber->timestamp, timestampBufPos);
if (matchingSubscriber->dataSetValues == NULL)
matchingSubscriber->dataSetValues = parseAllDataUnknownValue(matchingSubscriber, dataSetBufferAddress, dataSetBufferLength, false);
else
parseAllData(dataSetBufferAddress, dataSetBufferLength, matchingSubscriber->dataSetValues);
bool isValid = true;
if (matchingSubscriber->stNum == stNum) {
if (matchingSubscriber->sqNum >= sqNum) {
isValid = false;
}
}
matchingSubscriber->stateValid = isValid;
matchingSubscriber->stNum = stNum;
matchingSubscriber->sqNum = sqNum;
matchingSubscriber->invalidityTime = Hal_getTimeInMs() + timeAllowedToLive;
if (matchingSubscriber->listener != NULL)
matchingSubscriber->listener(matchingSubscriber, matchingSubscriber->listenerParameter);
return 1;
}
return 0;
}
exit_with_fault:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Invalid goose payload\n");
return -1;
}
static void
parseGooseMessage(GooseReceiver self, int numbytes)
{
int bufPos;
bool subscriberFound = false;
uint8_t* buffer = self->buffer;
if (numbytes < 22) return;
/* skip ethernet addresses */
bufPos = 12;
int headerLength = 14;
/* check for VLAN tag */
if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) {
bufPos += 4; /* skip VLAN tag */
headerLength += 4;
}
/* check for GOOSE Ethertype */
if (buffer[bufPos++] != 0x88)
return;
if (buffer[bufPos++] != 0xb8)
return;
uint16_t appId;
appId = buffer[bufPos++] * 0x100;
appId += buffer[bufPos++];
uint16_t length;
length = buffer[bufPos++] * 0x100;
length += buffer[bufPos++];
/* skip reserved fields */
bufPos += 4;
int apduLength = length - 8;
if (numbytes != length + headerLength) {
if (DEBUG)
printf("GOOSE_SUBSCRIBER: Invalid PDU size\n");
return;
}
if (DEBUG_GOOSE_SUBSCRIBER) {
printf("GOOSE_SUBSCRIBER: GOOSE message:\nGOOSE_SUBSCRIBER: ----------------\n");
printf("GOOSE_SUBSCRIBER: APPID: %u\n", appId);
printf("GOOSE_SUBSCRIBER: LENGTH: %u\n", length);
printf("GOOSE_SUBSCRIBER: APDU length: %i\n", apduLength);
}
// check if there is an interested subscriber
LinkedList element = LinkedList_getNext(self->subscriberList);
while (element != NULL) {
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
if (subscriber->appId == appId) {
subscriberFound = true;
break;
}
element = LinkedList_getNext(element);
}
if (subscriberFound)
parseGoosePayload(self, buffer + bufPos, apduLength);
else {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: GOOSE message ignored due to unknown APPID value\n");
}
}
static void
gooseReceiverLoop(void* threadParameter)
{
GooseReceiver self = (GooseReceiver) threadParameter;
GooseReceiver_startThreadless(self);
while (self->running) {
GooseReceiver_tick(self);
Thread_sleep(1);
}
GooseReceiver_stopThreadless(self);
}
// start GOOSE receiver in a separate thread
void
GooseReceiver_start(GooseReceiver self)
{
Thread thread = Thread_create((ThreadExecutionFunction) gooseReceiverLoop, (void*) self, true);
if (thread != NULL) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: GOOSE receiver started for interface %s\n", self->interfaceId);
Thread_start(thread);
}
else {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Starting GOOSE receiver failed for interface %s\n", self->interfaceId);
}
}
void
GooseReceiver_stop(GooseReceiver self)
{
self->running = false;
}
void
GooseReceiver_destroy(GooseReceiver self)
{
LinkedList_destroyDeep(self->subscriberList,
(LinkedListValueDeleteFunction) GooseSubscriber_destroy);
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self);
}
/***************************************
* Functions for non-threaded operation
***************************************/
void
GooseReceiver_startThreadless(GooseReceiver self)
{
if (self->interfaceId == NULL)
self->ethSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, NULL);
else
self->ethSocket = Ethernet_createSocket(self->interfaceId, NULL);
Ethernet_setProtocolFilter(self->ethSocket, ETH_P_GOOSE);
self->running = true;
}
void
GooseReceiver_stopThreadless(GooseReceiver self)
{
Ethernet_destroySocket(self->ethSocket);
self->running = false;
}
// call after reception of ethernet frame and periodically to to house keeping tasks
void
GooseReceiver_tick(GooseReceiver self)
{
int packetSize = Ethernet_receivePacket(self->ethSocket, self->buffer, ETH_BUFFER_LENGTH);
if (packetSize > 0)
parseGooseMessage(self, packetSize);
}

@ -0,0 +1,70 @@
/*
* goose_receiver.h
*
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef GOOSE_RECEIVER_H_
#define GOOSE_RECEIVER_H_
#include <goose_subscriber.h>
typedef struct sGooseReceiver* GooseReceiver;
GooseReceiver
GooseReceiver_create(void);
void
GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId);
void
GooseReceiver_addSubscriber(GooseReceiver self, GooseSubscriber subscriber);
void
GooseReceiver_removeSubscriber(GooseReceiver self, GooseSubscriber subscriber);
// call backup listener if message is not handled by a subscriber
void
GooseReceiver_setBackupListener(GooseReceiver self);
// start GOOSE receiver in a separate thread
void
GooseReceiver_start(GooseReceiver self);
void
GooseReceiver_stop(GooseReceiver self);
void
GooseReceiver_destroy(GooseReceiver self);
/***************************************
* Functions for non-threaded operation
***************************************/
void
GooseReceiver_startThreadless(GooseReceiver self);
void
GooseReceiver_stopThreadless(GooseReceiver self);
// call after reception of ethernet frame and periodically to to house keeping tasks
void
GooseReceiver_tick(GooseReceiver self);
#endif /* GOOSE_RECEIVER_H_ */

@ -0,0 +1,62 @@
/*
* goose_receiver_internal.h
*
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef GOOSE_RECEIVER_INTERNAL_H_
#define GOOSE_RECEIVER_INTERNAL_H_
#define ETH_BUFFER_LENGTH 1518
#define ETH_P_GOOSE 0x88b8
#ifndef DEBUG_GOOSE_SUBSCRIBER
#define DEBUG_GOOSE_SUBSCRIBER 0
#endif
struct sGooseSubscriber {
char* goCBRef;
int goCBRefLen;
uint32_t timeAllowedToLive;
uint32_t stNum;
uint32_t sqNum;
uint32_t confRev;
MmsValue* timestamp;
bool simulation;
bool ndsCom;
uint64_t invalidityTime;
bool stateValid;
int32_t appId; /* APPID or -1 if APPID should be ignored */
MmsValue* dataSetValues;
bool dataSetValuesSelfAllocated;
GooseListener listener;
void* listenerParameter;
};
#endif /* GOOSE_RECEIVER_INTERNAL_H_ */

@ -25,622 +25,20 @@
#include "stack_config.h"
#include "goose_subscriber.h"
#include "ethernet.h"
#include "thread.h"
#include "hal_ethernet.h"
#include "hal_thread.h"
#include "ber_decode.h"
#include "mms_value.h"
#include "mms_value_internal.h"
#define ETH_BUFFER_LENGTH 1518
#define ETH_P_GOOSE 0x88b8
struct sGooseSubscriber {
char* goCBRef;
int goCBRefLen;
uint32_t timeAllowedToLive;
uint32_t stNum;
uint32_t sqNum;
uint32_t confRev;
MmsValue* timestamp;
bool simulation;
bool ndsCom;
int32_t appId; /* APPID or -1 if APPID should be ignored */
MmsValue* dataSetValues;
bool dataSetValuesSelfAllocated;
GooseListener listener;
void* listenerParameter;
bool running;
Thread receiver;
char* interfaceId;
};
static void
createNewStringFromBufferElement(MmsValue* value, uint8_t* bufferSrc, int elementLength)
{
value->value.visibleString.buf = (char*) malloc(elementLength + 1);
memcpy(value->value.visibleString.buf, bufferSrc, elementLength);
value->value.visibleString.buf[elementLength] = 0;
value->value.visibleString.size = elementLength;
}
static int
parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
{
int bufPos = 0;
int elementLength = 0;
int elementIndex = 0;
int maxIndex = MmsValue_getArraySize(dataSetValues) - 1;
while (bufPos < allDataLength) {
uint8_t tag = buffer[bufPos++];
if (elementIndex > maxIndex) {
if (DEBUG) printf("Malformed message: too much elements!\n");
return 0;
}
MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex);
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos + elementLength > allDataLength) {
if (DEBUG) printf("Malformed message: sub element is to large!\n");
return 0;
}
switch (tag) {
case 0x80: /* reserved for access result */
printf(" found reserved value (tag 0x80)!\n");
break;
case 0xa1: /* array */
if (DEBUG) printf(" found array\n");
if (MmsValue_getType(value) == MMS_ARRAY) {
if (!parseAllData(buffer + bufPos, elementLength, value))
return -1;
}
break;
case 0xa2: /* structure */
if (DEBUG) printf(" found structure\n");
if (MmsValue_getType(value) == MMS_STRUCTURE) {
if (!parseAllData(buffer + bufPos, elementLength, value))
return -1;
}
break;
case 0x83: /* boolean */
if (DEBUG) printf(" found boolean\n");
if (MmsValue_getType(value) == MMS_BOOLEAN) {
MmsValue_setBoolean(value, BerDecoder_decodeBoolean(buffer, bufPos));
}
else
if (DEBUG) printf(" message contains value of wrong type!\n");
break;
case 0x84: /* BIT STRING */
if (MmsValue_getType(value) == MMS_BIT_STRING) {
int padding = buffer[bufPos];
int bitStringLength = (8 * (elementLength - 1)) - padding;
if (bitStringLength == value->value.bitString.size) {
memcpy(value->value.bitString.buf, buffer + bufPos + 1,
elementLength - 1);
}
else
printf("bit-string is of wrong size");
}
break;
case 0x85: /* integer */
if (MmsValue_getType(value) == MMS_INTEGER) {
if (elementLength <= value->value.integer->maxSize) {
value->value.integer->size = elementLength;
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
}
}
break;
case 0x86: /* unsigned integer */
if (MmsValue_getType(value) == MMS_UNSIGNED) {
if (elementLength <= value->value.integer->maxSize) {
value->value.integer->size = elementLength;
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
}
}
break;
case 0x87: /* Float */
if (MmsValue_getType(value) == MMS_FLOAT) {
if (elementLength == 9) {
MmsValue_setDouble(value, BerDecoder_decodeDouble(buffer, bufPos));
}
else if (elementLength == 5) {
MmsValue_setFloat(value, BerDecoder_decodeFloat(buffer, bufPos));
}
}
break;
case 0x89: /* octet string */
if (MmsValue_getType(value) == MMS_OCTET_STRING) {
if (elementLength <= value->value.octetString.maxSize) {
value->value.octetString.size = elementLength;
memcpy(value->value.octetString.buf, buffer + bufPos, elementLength);
}
}
break;
case 0x8a: /* visible string */
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
if (value->value.visibleString.buf != NULL) {
if ((int32_t) value->value.visibleString.size >= elementLength) {
memcpy(value->value.visibleString.buf, buffer + bufPos, elementLength);
value->value.visibleString.buf[elementLength] = 0;
}
else {
free(value->value.visibleString.buf);
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
}
}
else
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
}
break;
case 0x8c: /* binary time */
if (MmsValue_getType(value) == MMS_BINARY_TIME) {
if ((elementLength == 4) || (elementLength == 6)) {
memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength);
}
}
break;
case 0x91: /* Utctime */
if (elementLength == 8) {
if (MmsValue_getType(value) == MMS_UTC_TIME) {
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
}
else
if (DEBUG) printf(" message contains value of wrong type!\n");
}
else
if (DEBUG) printf(" UTCTime element is of wrong size!\n");
break;
default:
printf(" found unkown tag %02x\n", tag);
break;
}
bufPos += elementLength;
elementIndex++;
}
return 1;
}
static MmsValue*
parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLength, bool isStructure)
{
int bufPos = 0;
int elementLength = 0;
int elementIndex = 0;
MmsValue* dataSetValues = NULL;
while (bufPos < allDataLength) {
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos + elementLength > allDataLength) {
if (DEBUG) printf("Malformed message: sub element is to large!\n");
goto exit_with_error;
}
switch (tag) {
case 0x80: /* reserved for access result */
break;
case 0xa1: /* array */
break;
case 0xa2: /* structure */
break;
case 0x83: /* boolean */
break;
case 0x84: /* BIT STRING */
break;
case 0x85: /* integer */
break;
case 0x86: /* unsigned integer */
break;
case 0x87: /* Float */
break;
case 0x89: /* octet string */
break;
case 0x8a: /* visible string */
break;
case 0x8c: /* binary time */
break;
case 0x91: /* Utctime */
break;
default:
printf(" found unkown tag %02x\n", tag);
goto exit_with_error;
}
bufPos += elementLength;
elementIndex++;
}
if (isStructure)
dataSetValues = MmsValue_createEmptyStructure(elementIndex);
else
dataSetValues = MmsValue_createEmtpyArray(elementIndex);
elementIndex = 0;
bufPos = 0;
while (bufPos < allDataLength) {
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos + elementLength > allDataLength) {
if (DEBUG) printf("Malformed message: sub element is too large!\n");
goto exit_with_error;
}
MmsValue* value = NULL;
switch (tag) {
case 0xa1: /* array */
if (DEBUG) printf(" found array\n");
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, false);
if (value == NULL)
goto exit_with_error;
break;
case 0xa2: /* structure */
if (DEBUG) printf(" found structure\n");
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, true);
if (value == NULL)
goto exit_with_error;
break;
case 0x83: /* boolean */
if (DEBUG) printf(" found boolean\n");
value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
break;
case 0x84: /* BIT STRING */
{
int padding = buffer[bufPos];
int bitStringLength = (8 * (elementLength - 1)) - padding;
value = MmsValue_newBitString(bitStringLength);
memcpy(value->value.bitString.buf, buffer + bufPos + 1, elementLength - 1);
}
break;
case 0x85: /* integer */
value = MmsValue_newInteger(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
break;
case 0x86: /* unsigned integer */
value = MmsValue_newUnsigned(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, 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));
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);
else if (elementLength == 6)
value = MmsValue_newBinaryTime(false);
if ((elementLength == 4) || (elementLength == 6))
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) printf(" UTCTime element is of wrong size!\n");
break;
default:
if (DEBUG) printf(" found unkown tag %02x\n", tag);
goto exit_with_error;
}
bufPos += elementLength;
if (value != NULL) {
MmsValue_setElement(dataSetValues, elementIndex, value);
elementIndex++;
}
}
self->dataSetValuesSelfAllocated = true;
return dataSetValues;
exit_with_error:
if (dataSetValues != NULL)
MmsValue_delete(dataSetValues);
return NULL;
}
static int
parseGoosePayload(uint8_t* buffer, int apduLength, GooseSubscriber self)
{
int bufPos = 0;
uint32_t timeAllowedToLive = 0;
uint32_t stNum = 0;
uint32_t sqNum = 0;
uint32_t confRev;
bool simulation = false;
bool ndsCom = false;
bool isMatching = false;
uint32_t numberOfDatSetEntries = 0;
if (buffer[bufPos++] == 0x61) {
int gooseLength;
bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength);
int gooseEnd = bufPos + gooseLength;
while (bufPos < gooseEnd) {
int elementLength;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength);
if (bufPos + elementLength > apduLength) {
if (DEBUG) printf("Malformed message: sub element is to large!\n");
goto exit_with_fault;
}
if (bufPos == -1)
goto exit_with_fault;
switch(tag) {
case 0x80: /* gocbRef */
if (DEBUG) printf(" Found gocbRef\n");
if (self->goCBRefLen == elementLength) {
if (memcmp(self->goCBRef, buffer + bufPos, elementLength) == 0) {
if (DEBUG) printf(" gocbRef is matching!\n");
isMatching = true;
}
else return 0;
}
else
return 0;
break;
case 0x81: /* timeAllowedToLive */
timeAllowedToLive = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG) printf(" Found timeAllowedToLive %u\n", timeAllowedToLive);
break;
case 0x82:
if (DEBUG) printf(" Found dataSet\n");
break;
case 0x83:
if (DEBUG) printf(" Found goId\n");
break;
case 0x84:
MmsValue_setUtcTimeByBuffer(self->timestamp, buffer + bufPos);
if (DEBUG) printf(" Found timestamp t: %" PRIu64 "\n", MmsValue_getUtcTimeInMs(self->timestamp));
break;
case 0x85:
stNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG) printf(" Found stNum: %u\n", stNum);
break;
case 0x86:
sqNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG) printf(" Found sqNum: %u\n", sqNum);
break;
case 0x87:
simulation = BerDecoder_decodeBoolean(buffer, bufPos);
if (DEBUG) printf(" Found simulation: %i\n", simulation);
break;
case 0x88:
confRev = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG) printf(" Found confRev: %u\n", confRev);
break;
case 0x89:
ndsCom = BerDecoder_decodeBoolean(buffer, bufPos);
if (DEBUG) printf(" Found ndsCom: %i\n", ndsCom);
break;
case 0x8a:
numberOfDatSetEntries = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG) printf(" Found number of entries: %u\n", numberOfDatSetEntries);
break;
case 0xab:
if (DEBUG) printf(" Found all data with length: %i\n", elementLength);
if (self->dataSetValues == NULL)
self->dataSetValues = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, false);
else
parseAllData(buffer + bufPos, elementLength, self->dataSetValues);
break;
default:
if (DEBUG) printf(" Unknown tag %02x\n", tag);
break;
}
bufPos += elementLength;
}
if (isMatching) {
self->stNum = stNum;
self->sqNum = sqNum;
self->timeAllowedToLive = timeAllowedToLive;
self->confRev = confRev;
self->ndsCom = ndsCom;
self->simulation = simulation;
return 1;
}
return 0;
}
exit_with_fault:
if (DEBUG) printf("Invalid goose payload\n");
return -1;
}
static int
parseGooseMessage(uint8_t* buffer, int numbytes, GooseSubscriber subscriber)
{
int bufPos;
if (numbytes < 22) return -1;
/* skip ethernet addresses */
bufPos = 12;
int headerLength = 14;
/* check for VLAN tag */
if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) {
bufPos += 4; /* skip VLAN tag */
headerLength += 4;
}
/* check for GOOSE Ethertype */
if (buffer[bufPos++] != 0x88)
return -1;
if (buffer[bufPos++] != 0xb8)
return -1;
uint16_t appId;
appId = buffer[bufPos++] * 0x100;
appId += buffer[bufPos++];
uint16_t length;
length = buffer[bufPos++] * 0x100;
length += buffer[bufPos++];
/* skip reserved fields */
bufPos += 4;
int apduLength = length - 8;
if (numbytes != length + headerLength) {
if (DEBUG)
printf("Invalid PDU size\n");
return -1;
}
if (DEBUG) {
printf("GOOSE message:\n----------------\n");
printf(" APPID: %u\n", appId);
printf(" LENGTH: %u\n", length);
printf(" APDU length: %i\n", apduLength);
}
if (subscriber->appId >= 0) {
if (appId != (uint16_t) subscriber->appId) {
if (DEBUG)
printf("GOOSE message ignored due to wrong APPID value\n");
return 0;
}
}
return parseGoosePayload(buffer + bufPos, apduLength, subscriber);
}
static void
gooseSubscriberLoop(void* threadParameter)
{
GooseSubscriber self = (GooseSubscriber) threadParameter;
uint8_t* buffer = (uint8_t*) malloc(ETH_BUFFER_LENGTH);
EthernetSocket socket;
if (self->interfaceId == NULL)
socket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, NULL);
else
socket = Ethernet_createSocket(self->interfaceId, NULL);
Ethernet_setProtocolFilter(socket, ETH_P_GOOSE);
int running = 1;
while (running) {
int packetSize = Ethernet_receivePacket(socket, buffer, ETH_BUFFER_LENGTH);
if (packetSize > 0) {
if (parseGooseMessage(buffer, packetSize, self) == 1) {
if (self->listener != NULL) {
self->listener(self, self->listenerParameter);
}
}
}
Thread_sleep(1);
running = self->running;
}
free(buffer);
Ethernet_destroySocket(socket);
}
#include "goose_receiver_internal.h"
GooseSubscriber
GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues)
{
GooseSubscriber self = (GooseSubscriber) calloc(1, sizeof(struct sGooseSubscriber));
GooseSubscriber self = (GooseSubscriber) GLOBAL_CALLOC(1, sizeof(struct sGooseSubscriber));
self->goCBRef = copyString(goCbRef);
self->goCBRefLen = strlen(goCbRef);
@ -650,59 +48,40 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues)
if (dataSetValues != NULL)
self->dataSetValuesSelfAllocated = false;
self->running = false;
self->appId = -1;
return self;
}
void
GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId)
bool
GooseSubscriber_isValid(GooseSubscriber self)
{
self->appId = (int32_t) appId;
}
if (self->stateValid == false)
return false;
void
GooseSubscriber_setInterfaceId(GooseSubscriber self, char* interfaceId)
{
self->interfaceId = copyString(interfaceId);
}
if (Hal_getTimeInMs() > self->invalidityTime)
return false;
void
GooseSubscriber_subscribe(GooseSubscriber self)
{
Thread thread = Thread_create((ThreadExecutionFunction) gooseSubscriberLoop, self, false);
self->receiver = thread;
self->running = true;
Thread_start(thread);
return true;
}
void
GooseSubscriber_unsubscribe(GooseSubscriber self)
GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId)
{
if (self->running) {
self->running = false;
Thread_destroy(self->receiver);
}
self->appId = (int32_t) appId;
}
void
GooseSubscriber_destroy(GooseSubscriber self)
{
GooseSubscriber_unsubscribe(self);
free(self->goCBRef);
GLOBAL_FREEMEM(self->goCBRef);
MmsValue_delete(self->timestamp);
if (self->dataSetValuesSelfAllocated)
MmsValue_delete(self->dataSetValues);
if (self->interfaceId != NULL)
free(self->interfaceId);
free(self);
GLOBAL_FREEMEM(self);
}
void

@ -1,7 +1,7 @@
/*
* goose_subscriber.h
*
* Copyright 2013 Michael Zillgith
* Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -70,6 +70,9 @@ typedef void (*GooseListener)(GooseSubscriber subscriber, void* parameter);
GooseSubscriber
GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues);
//char*
//GooseSubscriber_getGoCbRef(GooseSubscriber self);
/**
* \brief set the APPID used by the subscriber to filter relevant messages.
*
@ -82,30 +85,23 @@ void
GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId);
/**
* \brief set the ethernet interface that should be used.
* \brief Check if subscriber state is valid
*
* \param self GooseSubscriber instance to operate on.
* \param interfaceId the id of the interface (e.g. a network device name like eth0
* for linux or a numerical index for windows)
*/
void
GooseSubscriber_setInterfaceId(GooseSubscriber self, char* interfaceId);
/**
* \brief Start listening to GOOSE messages
* A GOOSE subscriber is valid if TimeAllowedToLive timeout is not elapsed and GOOSE
* message were received with correct state and sequence ID.
*
* \param self GooseSubscriber instance to operate on.
*/
void
GooseSubscriber_subscribe(GooseSubscriber self);
bool
GooseSubscriber_isValid(GooseSubscriber self);
//uint16_t
//GooseSubscriber_getAppId(GooseSubscriber self);
/**
* \brief Stop listening to GOOSE messages
*
* \param self GooseSubscriber instance to operate on.
*/
void
GooseSubscriber_unsubscribe(GooseSubscriber self);
GooseSubscriber_setGoId(GooseSubscriber self, const char* goId);
//char*
//GooseSubscriber_getGoId(GooseSubscriber self);
void
GooseSubscriber_destroy(GooseSubscriber self);
@ -138,6 +134,17 @@ GooseSubscriber_getTimeAllowedToLive(GooseSubscriber self);
uint64_t
GooseSubscriber_getTimestamp(GooseSubscriber self);
/**
* \brief get the data set values received with the last report
*
* Note: To prevent data corruption. The MmsValue instance received should
* only be used inside of the callback function, when the GOOSE receiver is
* running in a separate thread.
*
* \param self GooseSubscriber instance to operate on.
*
* \return MmsValue instance of the report data set
*/
MmsValue*
GooseSubscriber_getDataSetValues(GooseSubscriber self);

@ -36,7 +36,8 @@
#include <stdio.h>
#include <fcntl.h>
#include "ethernet.h"
#include "libiec61850_platform_includes.h"
#include "hal_ethernet.h"
struct sEthernetSocket {
int bpf; // BPF device handle.
@ -181,7 +182,7 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
*/
};
EthernetSocket self = calloc(1, sizeof(struct sEthernetSocket));
EthernetSocket self = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket));
if (!self)
{
printf("Could not allocate socket descriptor!\n");
@ -189,7 +190,7 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
}
// Copy default BPF filter program into descriptor.
self->bpfProgram.bf_insns = calloc(1, sizeof(destAddrFiltCode));
self->bpfProgram.bf_insns = GLOBAL_CALLOC(1, sizeof(destAddrFiltCode));
if (!self->bpfProgram.bf_insns)
{
printf("Could not allocate memory for BPF filter program!\n");
@ -212,8 +213,8 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
if (self->bpf == -1)
{
printf("Error opening BPF file handle!\n");
free(self->bpfProgram.bf_insns);
free(self);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self);
return NULL;
}
@ -223,8 +224,8 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
if (fcntl(self->bpf, F_SETFL, &optval) == -1)
{
printf("Unable to change to non-blocking mode!\n");
free(self->bpfProgram.bf_insns);
free(self);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self);
return NULL;
}
@ -233,8 +234,8 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
if (ioctl(self->bpf, BIOCSETIF, &ifr))
{
printf("Unable to select interface %s!\n", interfaceId);
free(self->bpfProgram.bf_insns);
free(self);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self);
return NULL;
}
@ -242,8 +243,8 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
if (ioctl(self->bpf, BIOCIMMEDIATE, &self->bpfBufferSize) == -1)
{
printf("Unable to activate immediate mode!\n");
free(self->bpfProgram.bf_insns);
free(self);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self);
return NULL;
}
@ -251,18 +252,18 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
if (ioctl(self->bpf, BIOCGBLEN, &self->bpfBufferSize) == -1)
{
printf("Unable to get BPF buffer lenght!\n");
free(self->bpfProgram.bf_insns);
free(self);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self);
return NULL;
}
// Allocate a buffer for the message reception.
self->bpfBuffer = calloc(1, self->bpfBufferSize);
self->bpfBuffer = GLOBAL_CALLOC(1, self->bpfBufferSize);
if (!self->bpfBuffer)
{
printf("Unable to allocate BPF RX buffer!\n");
free(self->bpfProgram.bf_insns);
free(self);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self);
return NULL;
}
self->bpfPositon = self->bpfBuffer;
@ -273,9 +274,9 @@ EthernetSocket Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
if (ioctl(self->bpf, BIOCPROMISC, &optval) == -1)
{
printf("Unable to activate promiscous mode!\n");
free(self->bpfProgram.bf_insns);
free(self->bpfBuffer);
free(self);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self->bpfBuffer);
GLOBAL_FREEMEM(self);
return NULL;
}
@ -313,7 +314,7 @@ int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
struct bpf_hdr *header = (struct bpf_hdr *)(self->bpfPositon);
// Check if the target buffer is big enough to hold the received ethernet frame.
if (bufferSize >= header->bh_caplen)
if ((unsigned int) bufferSize >= header->bh_caplen)
{
// Copy the frame to the target buffer.
memcpy(buffer, self->bpfPositon + header->bh_hdrlen, header->bh_caplen);
@ -346,7 +347,14 @@ void Ethernet_destroySocket(EthernetSocket self)
close(self->bpf);
// Free all dynamic resources used by the ethernet socket.
free(self->bpfBuffer);
free(self->bpfProgram.bf_insns);
free(self);
GLOBAL_FREEMEM(self->bpfBuffer);
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);
GLOBAL_FREEMEM(self);
}
bool
Ethernet_isSupported()
{
return true;
}

@ -35,7 +35,8 @@
#include <stdbool.h>
#include <stdio.h>
#include "ethernet.h"
#include "libiec61850_platform_includes.h"
#include "hal_ethernet.h"
struct sEthernetSocket {
int rawSocket;
@ -101,13 +102,13 @@ Ethernet_getInterfaceMACAddress(char* interfaceId, uint8_t* addr)
EthernetSocket
Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
{
EthernetSocket ethernetSocket = calloc(1, sizeof(struct sEthernetSocket));
EthernetSocket ethernetSocket = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket));
ethernetSocket->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (ethernetSocket->rawSocket == -1) {
printf("Error creating raw socket!\n");
free(ethernetSocket);
GLOBAL_FREEMEM(ethernetSocket);
return NULL;
}
@ -163,6 +164,12 @@ void
Ethernet_destroySocket(EthernetSocket ethSocket)
{
close(ethSocket->rawSocket);
free(ethSocket);
GLOBAL_FREEMEM(ethSocket);
}
bool
Ethernet_isSupported()
{
return true;
}

@ -23,12 +23,16 @@
#include "stack_config.h"
#if CONFIG_INCLUDE_ETHERNET_WINDOWS == 1
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "hal_ethernet.h"
#if (CONFIG_INCLUDE_ETHERNET_WINDOWS == 1)
#include <malloc.h>
#include <winsock2.h>
@ -38,8 +42,6 @@
#pragma comment (lib, "IPHLPAPI.lib")
#endif
#include "ethernet.h"
#define HAVE_REMOTE
#include "pcap.h"
@ -89,7 +91,7 @@ static pgetadaptersaddresses GetAdaptersAddresses;
static bool dllLoaded = false;
static void
loadDLLs()
loadDLLs(void)
{
HINSTANCE hDll = LoadLibrary("iphlpapi.dll");
@ -103,7 +105,7 @@ loadDLLs()
"GetAdaptersAddresses");
if (GetAdaptersAddresses == NULL)
printf("Error loading GetAdaptersAddresses from iphlpapi.dll (%d)\n", GetLastError());
printf("Error loading GetAdaptersAddresses from iphlpapi.dll (%d)\n", (int) GetLastError());
}
#endif /* __MINGW64_VERSION_MAJOR */
@ -155,7 +157,7 @@ getInterfaceName(int interfaceIndex)
return interfaceName;
}
void
static void
getAdapterMacAddress(char* pcapAdapterName, uint8_t* macAddress)
{
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
@ -224,7 +226,7 @@ Ethernet_getInterfaceMACAddress(char* interfaceId, uint8_t* addr)
long interfaceIndex = strtol(interfaceId, &endPtr, 10);
if (*endPtr != NULL) {
if (endPtr != NULL) {
printf("Ethernet_getInterfaceMACAddress: invalid interface number %s\n", interfaceId);
return;
}
@ -315,4 +317,50 @@ Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
}
}
#endif
bool
Ethernet_isSupported()
{
return true;
}
#else
bool
Ethernet_isSupported()
{
return false;
}
void
Ethernet_getInterfaceMACAddress(char* interfaceId, uint8_t* addr)
{
}
EthernetSocket
Ethernet_createSocket(char* interfaceId, uint8_t* destAddress)
{
return NULL;
}
void
Ethernet_destroySocket(EthernetSocket ethSocket)
{
}
void
Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize)
{
}
void
Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType)
{
}
int
Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
{
return 0;
}
#endif /* (CONFIG_INCLUDE_ETHERNET_WINDOWS == 1) */

@ -32,7 +32,9 @@
#include <sys/stat.h>
#include <unistd.h>
#include "filesystem.h"
#include "libiec61850_platform_includes.h"
#include "hal_filesystem.h"
#include "stack_config.h"
@ -154,7 +156,7 @@ FileSystem_openDirectory(char* directoryName)
DirectoryHandle handle = NULL;
if (dirHandle != NULL) {
handle = malloc(sizeof(struct sDirectoryHandle));
handle = GLOBAL_MALLOC(sizeof(struct sDirectoryHandle));
handle->handle = dirHandle;
}
@ -190,7 +192,7 @@ void
FileSystem_closeDirectory(DirectoryHandle directory)
{
closedir(directory->handle);
free(directory);
GLOBAL_FREEMEM(directory);
}
#if 0

@ -30,8 +30,9 @@
#include <sys/types.h>
#include <sys/stat.h>
#include "filesystem.h"
#include "hal_filesystem.h"
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include <malloc.h>
@ -131,7 +132,7 @@ FileSystem_openDirectory(char* directoryName)
createFullPathFromFileName(fullPath, directoryName);
DirectoryHandle dirHandle = (DirectoryHandle) calloc(1, sizeof(struct sDirectoryHandle));
DirectoryHandle dirHandle = (DirectoryHandle) GLOBAL_CALLOC(1, sizeof(struct sDirectoryHandle));
strcat(fullPath, "\\*");
@ -141,7 +142,7 @@ FileSystem_openDirectory(char* directoryName)
dirHandle->available = true;
if (dirHandle->handle == INVALID_HANDLE_VALUE) {
free(dirHandle);
GLOBAL_FREEMEM(dirHandle);
return NULL;
}
else
@ -226,27 +227,6 @@ void
FileSystem_closeDirectory(DirectoryHandle directory)
{
FindClose(directory->handle);
free(directory);
GLOBAL_FREEMEM(directory);
}
#if 0
int
main(int argc, char** argv)
{
DirectoryHandle directory = FileSystem_openDirectory("/");
if (directory != NULL) {
char* fileName = FileSystem_readDirectory(directory);
while (fileName != NULL) {
printf("FILE: (%s)\n", fileName);
fileName = FileSystem_readDirectory(directory);
}
FileSystem_closeDirectory(directory);
}
else
printf("Error opening directory!\n");
}
#endif

@ -1,7 +1,7 @@
/*
* socket_bsd.c
*
* Copyright 2013 Michael Zillgith, contributed to the project by Michael Clausen (School of engineering Valais).
* Copyright 2013, 2014 Michael Zillgith, contributions by Michael Clausen (School of engineering Valais).
*
* This file is part of libIEC61850.
*
@ -21,7 +21,7 @@
* See COPYING file for the complete license text.
*/
#include "socket.h"
#include "hal_socket.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
@ -38,7 +38,7 @@
#include <netinet/tcp.h> // required for TCP keepalive
#include "thread.h"
#include "hal_thread.h"
#include "libiec61850_platform_includes.h"
@ -48,6 +48,7 @@
struct sSocket {
int fd;
uint32_t connectTimeout;
};
struct sServerSocket {
@ -55,6 +56,58 @@ struct sServerSocket {
int backLog;
};
struct sHandleSet {
fd_set handles;
int maxHandle;
};
HandleSet
Handleset_new(void)
{
HandleSet result = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet));
if (result != NULL) {
FD_ZERO(&result->handles);
result->maxHandle = -1;
}
return result;
}
void
Handleset_addSocket(HandleSet self, const Socket sock)
{
if (self != NULL && sock != NULL && sock->fd != -1) {
FD_SET(sock->fd, &self->handles);
if (sock->fd > self->maxHandle) {
self->maxHandle = sock->fd;
}
}
}
int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
{
int result;
if (self != NULL && self->maxHandle >= 0) {
struct timeval timeout;
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = (timeoutMs % 1000) * 1000;
result = select(self->maxHandle + 1, &self->handles, NULL, NULL, &timeout);
} else {
result = -1;
}
return result;
}
void
Handleset_destroy(HandleSet self)
{
GLOBAL_FREEMEM(self);
}
static void
activateKeepAlive(int sd)
{
@ -79,7 +132,7 @@ activateKeepAlive(int sd)
}
static bool
prepareServerAddress(char* address, int port, struct sockaddr_in* sockaddr)
prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr)
{
memset((char *) sockaddr , 0, sizeof(struct sockaddr_in));
@ -101,17 +154,15 @@ prepareServerAddress(char* address, int port, struct sockaddr_in* sockaddr)
return true;
}
#if 0
static void
setSocketNonBlocking(Socket self)
{
int flags = fcntl(self->fd, F_GETFL, 0);
fcntl(self->fd, F_SETFL, flags | O_NONBLOCK);
}
#endif
ServerSocket
TcpServerSocket_create(char* address, int port)
TcpServerSocket_create(const char* address, int port)
{
ServerSocket serverSocket = NULL;
@ -129,7 +180,7 @@ TcpServerSocket_create(char* address, int port)
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int));
if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) {
serverSocket = malloc(sizeof(struct sServerSocket));
serverSocket = GLOBAL_MALLOC(sizeof(struct sServerSocket));
serverSocket->fd = fd;
serverSocket->backLog = 0;
}
@ -141,6 +192,8 @@ TcpServerSocket_create(char* address, int port)
#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1
activateKeepAlive(fd);
#endif
setSocketNonBlocking((Socket) serverSocket);
}
return serverSocket;
@ -201,21 +254,28 @@ ServerSocket_destroy(ServerSocket self)
Thread_sleep(10);
free(self);
GLOBAL_FREEMEM(self);
}
Socket
TcpSocket_create()
{
Socket self = malloc(sizeof(struct sSocket));
Socket self = GLOBAL_MALLOC(sizeof(struct sSocket));
self->fd = -1;
return self;
}
int
Socket_connect(Socket self, char* address, int port)
void
Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
{
self->connectTimeout = timeoutInMs;
}
bool
Socket_connect(Socket self, const char* address, int port)
{
struct sockaddr_in serverAddress;
@ -223,7 +283,7 @@ Socket_connect(Socket self, char* address, int port)
printf("Socket_connect: %s:%i\n", address, port);
if (!prepareServerAddress(address, port, &serverAddress))
return 0;
return false;
self->fd = socket(AF_INET, SOCK_STREAM, 0);
@ -231,10 +291,25 @@ Socket_connect(Socket self, char* address, int port)
activateKeepAlive(self->fd);
#endif
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0)
return 0;
fd_set fdSet;
FD_ZERO(&fdSet);
FD_SET(self->fd, &fdSet);
fcntl(self->fd, F_SETFL, O_NONBLOCK);
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {
if (errno != EINPROGRESS)
return false;
}
struct timeval timeout;
timeout.tv_sec = self->connectTimeout / 1000;
timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) < 0)
return false;
else
return 1;
return true;
}
char*
@ -265,7 +340,7 @@ Socket_getPeerAddress(Socket self)
else
return NULL ;
char* clientConnection = malloc(strlen(addrString) + 9);
char* clientConnection = GLOBAL_MALLOC(strlen(addrString) + 9);
if (isIPv6)
@ -284,7 +359,10 @@ Socket_read(Socket self, uint8_t* buf, int size)
if (self->fd == -1)
return -1;
int read_bytes = read(self->fd, buf, size);
int read_bytes = recv(self->fd, buf, size, MSG_DONTWAIT);
if (read_bytes == 0)
return -1;
if (read_bytes == -1) {
int error = errno;
@ -300,9 +378,8 @@ Socket_read(Socket self, uint8_t* buf, int size)
return -1;
}
}
else {
return read_bytes;
}
return read_bytes;
}
int
@ -326,5 +403,5 @@ Socket_destroy(Socket self)
Thread_sleep(10);
free(self);
GLOBAL_FREEMEM(self);
}

@ -1,7 +1,7 @@
/*
* socket_linux.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -21,7 +21,7 @@
* See COPYING file for the complete license text.
*/
#include "socket.h"
#include "hal_socket.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
@ -38,7 +38,7 @@
#include <netinet/tcp.h> // required for TCP keepalive
#include "thread.h"
#include "hal_thread.h"
#include "libiec61850_platform_includes.h"
@ -48,6 +48,7 @@
struct sSocket {
int fd;
uint32_t connectTimeout;
};
struct sServerSocket {
@ -55,6 +56,58 @@ struct sServerSocket {
int backLog;
};
struct sHandleSet {
fd_set handles;
int maxHandle;
};
HandleSet
Handleset_new(void)
{
HandleSet result = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet));
if (result != NULL) {
FD_ZERO(&result->handles);
result->maxHandle = -1;
}
return result;
}
void
Handleset_addSocket(HandleSet self, const Socket sock)
{
if (self != NULL && sock != NULL && sock->fd != -1) {
FD_SET(sock->fd, &self->handles);
if (sock->fd > self->maxHandle) {
self->maxHandle = sock->fd;
}
}
}
int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
{
int result;
if (self != NULL && self->maxHandle >= 0) {
struct timeval timeout;
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = (timeoutMs % 1000) * 1000;
result = select(self->maxHandle + 1, &self->handles, NULL, NULL, &timeout);
} else {
result = -1;
}
return result;
}
void
Handleset_destroy(HandleSet self)
{
GLOBAL_FREEMEM(self);
}
static void
activateKeepAlive(int sd)
{
@ -80,7 +133,7 @@ activateKeepAlive(int sd)
}
static bool
prepareServerAddress(char* address, int port, struct sockaddr_in* sockaddr)
prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr)
{
memset((char *) sockaddr , 0, sizeof(struct sockaddr_in));
@ -102,17 +155,23 @@ prepareServerAddress(char* address, int port, struct sockaddr_in* sockaddr)
return true;
}
#if 0
static void
setSocketNonBlocking(Socket self)
{
int flags = fcntl(self->fd, F_GETFL, 0);
fcntl(self->fd, F_SETFL, flags | O_NONBLOCK);
}
#endif
static void
activateTcpNoDelay(Socket self)
{
/* activate TCP_NODELAY option - packets will be sent immediately */
int flag = 1;
setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
}
ServerSocket
TcpServerSocket_create(char* address, int port)
TcpServerSocket_create(const char* address, int port)
{
ServerSocket serverSocket = NULL;
@ -130,9 +189,11 @@ TcpServerSocket_create(char* address, int port)
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int));
if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) {
serverSocket = malloc(sizeof(struct sServerSocket));
serverSocket = GLOBAL_MALLOC(sizeof(struct sServerSocket));
serverSocket->fd = fd;
serverSocket->backLog = 0;
setSocketNonBlocking((Socket) serverSocket);
}
else {
close(fd);
@ -153,6 +214,8 @@ ServerSocket_listen(ServerSocket self)
listen(self->fd, self->backLog);
}
/* CHANGED TO MAKE NON-BLOCKING --> RETURNS NULL IF NO CONNECTION IS PENDING */
Socket
ServerSocket_accept(ServerSocket self)
{
@ -165,6 +228,8 @@ ServerSocket_accept(ServerSocket self)
if (fd >= 0) {
conSocket = TcpSocket_create();
conSocket->fd = fd;
activateTcpNoDelay(conSocket);
}
return conSocket;
@ -202,21 +267,30 @@ ServerSocket_destroy(ServerSocket self)
Thread_sleep(10);
free(self);
GLOBAL_FREEMEM(self);
}
Socket
TcpSocket_create()
{
Socket self = malloc(sizeof(struct sSocket));
Socket self = GLOBAL_MALLOC(sizeof(struct sSocket));
self->fd = -1;
self->connectTimeout = 5000;
return self;
}
int
Socket_connect(Socket self, char* address, int port)
void
Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
{
self->connectTimeout = timeoutInMs;
}
bool
Socket_connect(Socket self, const char* address, int port)
{
struct sockaddr_in serverAddress;
@ -224,18 +298,36 @@ Socket_connect(Socket self, char* address, int port)
printf("Socket_connect: %s:%i\n", address, port);
if (!prepareServerAddress(address, port, &serverAddress))
return 0;
return false;
self->fd = socket(AF_INET, SOCK_STREAM, 0);
fd_set fdSet;
FD_ZERO(&fdSet);
FD_SET(self->fd, &fdSet);
activateTcpNoDelay(self);
#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1
activateKeepAlive(self->fd);
#endif
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0)
return 0;
fcntl(self->fd, F_SETFL, O_NONBLOCK);
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {
if (errno != EINPROGRESS)
return false;
}
struct timeval timeout;
timeout.tv_sec = self->connectTimeout / 1000;
timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) < 0)
return false;
else
return 1;
return true;
}
char*
@ -266,7 +358,7 @@ Socket_getPeerAddress(Socket self)
else
return NULL ;
char* clientConnection = malloc(strlen(addrString) + 9);
char* clientConnection = GLOBAL_MALLOC(strlen(addrString) + 9);
if (isIPv6)
@ -285,7 +377,10 @@ Socket_read(Socket self, uint8_t* buf, int size)
if (self->fd == -1)
return -1;
int read_bytes = read(self->fd, buf, size);
int read_bytes = recv(self->fd, buf, size, MSG_DONTWAIT);
if (read_bytes == 0)
return -1;
if (read_bytes == -1) {
int error = errno;
@ -301,9 +396,8 @@ Socket_read(Socket self, uint8_t* buf, int size)
return -1;
}
}
else {
return read_bytes;
}
return read_bytes;
}
int
@ -327,5 +421,5 @@ Socket_destroy(Socket self)
Thread_sleep(10);
free(self);
GLOBAL_FREEMEM(self);
}

@ -1,7 +1,7 @@
/*
* socket_win32.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -29,7 +29,8 @@
#pragma comment (lib, "Ws2_32.lib")
#include "socket.h"
#include "libiec61850_platform_includes.h"
#include "hal_socket.h"
#include "stack_config.h"
@ -45,6 +46,7 @@ struct tcp_keepalive {
struct sSocket {
SOCKET fd;
uint32_t connectTimeout;
};
struct sServerSocket {
@ -52,6 +54,58 @@ struct sServerSocket {
int backLog;
};
struct sHandleSet {
fd_set handles;
int maxHandle;
};
HandleSet
Handleset_new(void)
{
HandleSet result = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet));
if (result != NULL) {
FD_ZERO(&result->handles);
result->maxHandle = -1;
}
return result;
}
void
Handleset_addSocket(HandleSet self, const Socket sock)
{
if (self != NULL && sock != NULL && sock->fd != -1) {
FD_SET(sock->fd, &self->handles);
if (sock->fd > self->maxHandle) {
self->maxHandle = sock->fd;
}
}
}
int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
{
int result;
if (self != NULL && self->maxHandle >= 0) {
struct timeval timeout;
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = (timeoutMs % 1000) * 1000;
result = select(self->maxHandle + 1, &self->handles, NULL, NULL, &timeout);
} else {
result = -1;
}
return result;
}
void
Handleset_destroy(HandleSet self)
{
GLOBAL_FREEMEM(self);
}
static void
activateKeepAlive(SOCKET s)
{
@ -65,13 +119,30 @@ activateKeepAlive(SOCKET s)
if (WSAIoctl(s, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive),
NULL, 0, &retVal, NULL, NULL) == SOCKET_ERROR)
{
printf("WSAIotcl(SIO_KEEPALIVE_VALS) failed; %d\n",
WSAGetLastError());
if (DEBUG_SOCKET)
printf("WIN32_SOCKET: WSAIotcl(SIO_KEEPALIVE_VALS) failed: %d\n",
WSAGetLastError());
}
}
static void
setSocketNonBlocking(Socket self)
{
unsigned long mode = 1;
if (ioctlsocket(self->fd, FIONBIO, &mode) != 0) {
if (DEBUG_SOCKET)
printf("WIN32_SOCKET: failed to set socket non-blocking!\n");
}
/* activate TCP_NODELAY */
int tcpNoDelay = 1;
setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&tcpNoDelay, sizeof(int));
}
static bool
prepareServerAddress(char* address, int port, struct sockaddr_in* sockaddr)
prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr)
{
memset((char *) sockaddr , 0, sizeof(struct sockaddr_in));
@ -94,7 +165,7 @@ prepareServerAddress(char* address, int port, struct sockaddr_in* sockaddr)
}
ServerSocket
TcpServerSocket_create(char* address, int port)
TcpServerSocket_create(const char* address, int port)
{
ServerSocket serverSocket = NULL;
int ec;
@ -102,7 +173,8 @@ TcpServerSocket_create(char* address, int port)
SOCKET listen_socket = INVALID_SOCKET;
if ((ec = WSAStartup(MAKEWORD(2,0), &wsa)) != 0) {
printf("winsock error: code %i\n");
if (DEBUG_SOCKET)
printf("WIN32_SOCKET: winsock error: code %i\n", ec);
return NULL;
}
@ -118,7 +190,8 @@ TcpServerSocket_create(char* address, int port)
#endif
if (listen_socket == INVALID_SOCKET) {
printf("socket failed with error: %i\n", WSAGetLastError());
if (DEBUG_SOCKET)
printf("WIN32_SOCKET: socket failed with error: %i\n", WSAGetLastError());
WSACleanup();
return NULL;
}
@ -129,17 +202,20 @@ TcpServerSocket_create(char* address, int port)
ec = bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ec == SOCKET_ERROR) {
printf("bind failed with error:%i\n", WSAGetLastError());
if (DEBUG_SOCKET)
printf("WIN32_SOCKET: bind failed with error:%i\n", WSAGetLastError());
closesocket(listen_socket);
WSACleanup();
return NULL;
}
serverSocket = (ServerSocket) malloc(sizeof(struct sServerSocket));
serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket));
serverSocket->fd = listen_socket;
serverSocket->backLog = 10;
setSocketNonBlocking((Socket) serverSocket);
return serverSocket;
}
@ -161,6 +237,8 @@ ServerSocket_accept(ServerSocket self)
if (fd >= 0) {
conSocket = TcpSocket_create();
conSocket->fd = fd;
setSocketNonBlocking(conSocket);
}
return conSocket;
@ -183,27 +261,34 @@ ServerSocket_destroy(ServerSocket self)
Socket
TcpSocket_create()
{
Socket self = (Socket) malloc(sizeof(struct sSocket));
Socket self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
self->fd = -1;
self->fd = INVALID_SOCKET;
return self;
}
int
Socket_connect(Socket self, char* address, int port)
void
Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
{
self->connectTimeout = timeoutInMs;
}
bool
Socket_connect(Socket self, const char* address, int port)
{
struct hostent *server;
struct sockaddr_in serverAddress;
WSADATA wsa;
int ec;
if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
printf("winsock error: code %i\n");
return 0;
if ((ec = WSAStartup(MAKEWORD(2,0), &wsa)) != 0) {
if (DEBUG_SOCKET)
printf("WIN32_SOCKET: winsock error: code %i\n", ec);
return false;
}
if (!prepareServerAddress(address, port, &serverAddress))
return 0;
return false;
self->fd = socket(AF_INET, SOCK_STREAM, 0);
@ -211,12 +296,35 @@ Socket_connect(Socket self, char* address, int port)
activateKeepAlive(self->fd);
#endif
if (connect(self->fd, (struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) {
printf("Socket failed connecting!\n");
return 0;
}
else
return 1;
setSocketNonBlocking(self);
fd_set fdSet;
FD_ZERO(&fdSet);
FD_SET(self->fd, &fdSet);
// if (connect(self->fd, (struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) {
// if (DEBUG_SOCKET)
// printf("WIN32_SOCKET: Socket failed connecting!\n");
// return false;
// }
// else {
//
// return true;
// }
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK)
return false;
}
struct timeval timeout;
timeout.tv_sec = self->connectTimeout / 1000;
timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) == SOCKET_ERROR)
return false;
else
return true;
}
char*
@ -252,7 +360,7 @@ Socket_getPeerAddress(Socket self)
else
return NULL;
char* clientConnection = (char*) malloc(strlen(addrString) + 9);
char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9);
if (isIPv6)
sprintf(clientConnection, "[%s]:%i", addrString, port);
@ -265,7 +373,19 @@ Socket_getPeerAddress(Socket self)
int
Socket_read(Socket self, uint8_t* buf, int size)
{
return recv(self->fd, (char*) buf, size, 0);
int bytes_read = recv(self->fd, (char*) buf, size, 0);
if (bytes_read == 0) // peer has closed socket
return -1;
if (bytes_read == SOCKET_ERROR) {
if (WSAGetLastError() == WSAEWOULDBLOCK)
return 0;
else
return -1;
}
return bytes_read;
}
int
@ -277,7 +397,7 @@ Socket_write(Socket self, uint8_t* buf, int size)
void
Socket_destroy(Socket self)
{
if (self->fd != -1) {
if (self->fd != INVALID_SOCKET) {
closesocket(self->fd);
}

@ -26,7 +26,9 @@
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include "thread.h"
#include "hal_thread.h"
#include "libiec61850_platform_includes.h"
struct sThread {
ThreadExecutionFunction function;
@ -39,7 +41,7 @@ struct sThread {
Semaphore
Semaphore_create(int initialValue)
{
Semaphore self = malloc(sizeof(sem_t));
Semaphore self = GLOBAL_MALLOC(sizeof(sem_t));
sem_init((sem_t*) self, 0, initialValue);
@ -63,18 +65,20 @@ void
Semaphore_destroy(Semaphore self)
{
sem_destroy((sem_t*) self);
free(self);
GLOBAL_FREEMEM(self);
}
Thread
Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
{
Thread thread = malloc(sizeof(struct sThread));
Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));
thread->parameter = parameter;
thread->function = function;
thread->state = 0;
thread->autodestroy = autodestroy;
if (thread != NULL) {
thread->parameter = parameter;
thread->function = function;
thread->state = 0;
thread->autodestroy = autodestroy;
}
return thread;
}
@ -86,9 +90,9 @@ destroyAutomaticThread(void* parameter)
thread->function(thread->parameter);
free(thread);
GLOBAL_FREEMEM(thread);
return NULL;
pthread_exit(NULL);
}
void
@ -111,7 +115,7 @@ Thread_destroy(Thread thread)
pthread_join(thread->pthread, NULL);
}
free(thread);
GLOBAL_FREEMEM(thread);
}
void

@ -23,7 +23,8 @@
#include <windows.h>
#include <stdlib.h>
#include "thread.h"
#include "libiec61850_platform_includes.h"
#include "hal_thread.h"
struct sThread {
ThreadExecutionFunction function;
@ -59,7 +60,7 @@ Thread
Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
{
DWORD threadId;
Thread thread = (Thread) malloc(sizeof(struct sThread));
Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));
thread->parameter = parameter;
thread->function = function;
@ -89,7 +90,7 @@ Thread_destroy(Thread thread)
CloseHandle(thread->handle);
free(thread);
GLOBAL_FREEMEM(thread);
}
void

@ -28,11 +28,11 @@
Asn1PrimitiveValue*
Asn1PrimitiveValue_create(int size)
{
Asn1PrimitiveValue* self = (Asn1PrimitiveValue*) malloc(sizeof(Asn1PrimitiveValue));
Asn1PrimitiveValue* self = (Asn1PrimitiveValue*) GLOBAL_MALLOC(sizeof(Asn1PrimitiveValue));
self->size = size;
self->maxSize = size;
self->octets = (uint8_t*) calloc(1, size);
self->octets = (uint8_t*) GLOBAL_CALLOC(1, size);
return self;
}
@ -40,22 +40,22 @@ Asn1PrimitiveValue_create(int size)
//Asn1PrimitiveValue*
//Asn1PrimitiveValue_createFromBuffer(uint8_t buffer, int bufferSize)
//{
// Asn1PrimitiveValue* self = malloc(sizeof(Asn1PrimitiveValue));
// Asn1PrimitiveValue* self = GLOBAL_MALLOC(sizeof(Asn1PrimitiveValue));
// self->size = bufferSize;
// self->maxSize = bufferSize;
// self->octets = malloc(1, bufferSize);
// self->octets = GLOBAL_MALLOC(1, bufferSize);
//
//}
Asn1PrimitiveValue*
Asn1PrimitiveValue_clone(Asn1PrimitiveValue* self)
{
Asn1PrimitiveValue* clone = (Asn1PrimitiveValue*) malloc(sizeof(Asn1PrimitiveValue));
Asn1PrimitiveValue* clone = (Asn1PrimitiveValue*) GLOBAL_MALLOC(sizeof(Asn1PrimitiveValue));
clone->size = self->size;
clone->maxSize = self->maxSize;
clone->octets = (uint8_t*) malloc(self->maxSize);
clone->octets = (uint8_t*) GLOBAL_MALLOC(self->maxSize);
memcpy(clone->octets, self->octets, clone->maxSize);
@ -90,6 +90,6 @@ Asn1PrimitiveValue_getMaxSize(Asn1PrimitiveValue* self)
void
Asn1PrimitiveValue_destroy(Asn1PrimitiveValue* self)
{
free(self->octets);
free(self);
GLOBAL_FREEMEM(self->octets);
GLOBAL_FREEMEM(self);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save