Merge remote-tracking branch 'remotes/upstream/development'

pull/142/head
fitefis 6 years ago
commit bc34020184

@ -1,3 +1,29 @@
Changes to version 1.2.0
------------------------
- IEC 61850/MMS client/server: Added TLS support (TLS API and implementation for mbedtls)
- IEC 61850/MMS server: removed deprecated AttributeChangedHandler
- Added pkg-config file
- The Sampled Values APIs have been renamed. The old version of the API is deprecated but still supported and will be removed in the next major version of the library.
- SV Publisher/Subscriber: a lot of small fixed and improvements
- .NET API: Added support for sampled values (SV) subscriber
- .NET API: Added support for GOOSE subscriber
- SV subscriber: added function SVReceiver_enableDestAddrCheck
- IEC 61850 server: fixed bug in buffered report module - report can be lost under some circumstances when BRCB is enabled
- SV subscriber: replaced code that caused unaligned memory access
- IEC 61850 server: added memory alignement for buffered reporting
Changes to version 1.1.2
------------------------
- MMS client: fixed parsing initiate response message
- SV publisher: conditional encoding for SmpRate
- MmsValue_update function now allows adjusting octet-string size of target object
- .NET API: Added DeleteFile
- CDC helper functions: added helper functions for VSS and VSG CDC
- added additional locks in client and server
Changes to version 1.1.1
------------------------
@ -19,7 +45,6 @@ Changes to version 1.1.1
- MMS client: MmsConnection_getVariableAccessAttributes support for VMD specific variables
- Java SCL parser: added support for "Val" elements for Octet64 types
Changes to version 1.1.0
------------------------

@ -11,8 +11,9 @@ project(libiec61850)
ENABLE_TESTING()
set(LIB_VERSION_MAJOR "1")
set(LIB_VERSION_MINOR "1")
set(LIB_VERSION_PATCH "1")
set(LIB_VERSION_MINOR "2")
set(LIB_VERSION_PATCH "0")
set(LIB_VERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/")
@ -45,35 +46,56 @@ set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "8000" CACHE STRING "Default buf
# 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)
option(DEBUG_IED_SERVER "Enable IED SERVER printf debugging" OFF)
option(DEBUG_IED_CLIENT "Enable IED CLIENT printf debugging" OFF)
option(DEBUG_MMS_SERVER "Enable MMS SERVER printf debugging" OFF)
option(DEBUG_MMS_CLIENT "Enable MMS CLIENT printf debugging" OFF)
#mark_as_advanced(DEBUG DEBUG_COTP DEBUG_ISO_SERVER DEBUG_ISO_CLIENT DEBUG_IED_SERVER
# DEBUG_IED_CLIENT DEBUG_MMS_SERVER DEBUG_MMS_CLIENT)
option(DEBUG_SOCKET "Enable printf debugging for socket layer" ${DEBUG})
option(DEBUG_COTP "Enable COTP printf debugging" ${DEBUG})
option(DEBUG_ISO_SERVER "Enable ISO SERVER printf debugging" ${DEBUG})
option(DEBUG_ISO_CLIENT "Enable ISO CLIENT printf debugging" ${DEBUG})
option(DEBUG_IED_SERVER "Enable IED SERVER printf debugging" ${DEBUG})
option(DEBUG_IED_CLIENT "Enable IED CLIENT printf debugging" ${DEBUG})
option(DEBUG_MMS_SERVER "Enable MMS SERVER printf debugging" ${DEBUG})
option(DEBUG_MMS_CLIENT "Enable MMS CLIENT printf debugging" ${DEBUG})
option(DEBUG_GOOSE_SUBSCRIBER "Enable GOOSE subscriber printf debugging" ${DEBUG})
option(DEBUG_GOOSE_PUBLISHER "Enable GOOSE publisher printf debugging" ${DEBUG})
option(DEBUG_SV_SUBSCRIBER "Enable Sampled Values subscriber debugging" ${DEBUG})
option(DEBUG_SV_PUBLISHER "Enable Sampled Values publisher debugging" ${DEBUG})
option(DEBUG_HAL_ETHERNET "Enable Ethernet HAL printf debugging" ${DEBUG})
#mark_as_advanced(
# DEBUG_SOCKET
# DEBUG_COTP
# DEBUG_ISO_SERVER
# DEBUG_ISO_CLIENT
# DEBUG_IED_SERVER
# DEBUG_IED_CLIENT
# DEBUG_MMS_SERVER
# DEBUG_MMS_CLIENT
# DEBUG_GOOSE_SUBSCRIBER
# DEBUG_GOOSE_PUBLISHER
# DEBUG_SV_SUBSCRIBER
# DEBUG_SV_PUBLISHER
# DEBUG_HAL_ETHERNET
#)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}/config
src/common/inc
src/goose
src/sampled_values
src/hal/inc
src/iec61850/inc
src/iec61850/inc_private
src/mms/inc
src/mms/inc_private
src/mms/iso_mms/asn1c
src/logging
${CMAKE_CURRENT_LIST_DIR}/src/common/inc
${CMAKE_CURRENT_LIST_DIR}/src/goose
${CMAKE_CURRENT_LIST_DIR}/src/sampled_values
${CMAKE_CURRENT_LIST_DIR}/src/hal/inc
${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc
${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc_private
${CMAKE_CURRENT_LIST_DIR}/src/mms/inc
${CMAKE_CURRENT_LIST_DIR}/src/mms/inc_private
${CMAKE_CURRENT_LIST_DIR}/src/mms/iso_mms/asn1c
${CMAKE_CURRENT_LIST_DIR}/src/logging
${CMAKE_CURRENT_LIST_DIR}/src/tls
)
set(API_HEADERS
src/hal/inc/hal_time.h
src/hal/inc/hal_thread.h
src/hal/inc/hal_filesystem.h
src/hal/inc/hal_ethernet.h
src/hal/inc/platform_endian.h
src/common/inc/libiec61850_common_api.h
src/common/inc/libiec61850_platform_includes.h
@ -106,15 +128,33 @@ set(API_HEADERS
src/sampled_values/sv_subscriber.h
src/sampled_values/sv_publisher.h
src/logging/logging_api.h
src/tls/tls_api.h
${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h
)
if(MSVC)
include_directories(
src/vs
${CMAKE_CURRENT_LIST_DIR}/src/vs
)
endif(MSVC)
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0)
set(WITH_MBEDTLS 1)
endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0)
if(WITH_MBEDTLS)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/src/tls/mbedtls
${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0/include
)
file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0/library/*.c)
add_definitions(-DCONFIG_MMS_SUPPORT_TLS=1)
add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h")
endif(WITH_MBEDTLS)
# write the detected stuff to this file
configure_file(
${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake
@ -122,7 +162,7 @@ configure_file(
)
if(BUILD_EXAMPLES)
add_subdirectory(examples)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/examples)
endif(BUILD_EXAMPLES)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src)
@ -133,21 +173,21 @@ if(BUILD_PYTHON_BINDINGS)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/pyiec61850)
endif(BUILD_PYTHON_BINDINGS)
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 "MZ Automation GmbH")
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_COMPONENTS_ALL Libraries Development Applications)
#set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}")
if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
include(InstallRequiredSystemLibraries)
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 "MZ Automation GmbH")
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_COMPONENTS_ALL Libraries Development Applications)
#set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}")
include(CPack)
endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")

@ -60,10 +60,20 @@ LIB_INCLUDE_DIRS += src/sampled_values
LIB_INCLUDE_DIRS += src/iec61850/inc
LIB_INCLUDE_DIRS += src/iec61850/inc_private
LIB_INCLUDE_DIRS += src/logging
LIB_INCLUDE_DIRS += src/tls
ifeq ($(HAL_IMPL), WIN32)
LIB_INCLUDE_DIRS += third_party/winpcap/Include
endif
ifdef WITH_MBEDTLS
LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.6.0/library
LIB_SOURCE_DIRS += src/tls/mbedtls
LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.6.0/include
LIB_INCLUDE_DIRS += src/tls/mbedtls
CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"'
CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1'
endif
LIB_INCLUDES = $(addprefix -I,$(LIB_INCLUDE_DIRS))
ifndef INSTALL_PREFIX
@ -102,6 +112,7 @@ LIB_API_HEADER_FILES += src/goose/goose_publisher.h
LIB_API_HEADER_FILES += src/sampled_values/sv_subscriber.h
LIB_API_HEADER_FILES += src/sampled_values/sv_publisher.h
LIB_API_HEADER_FILES += src/logging/logging_api.h
LIB_API_HEADER_FILES += src/tls/tls_api.h
get_sources_from_directory = $(wildcard $1/*.c)
get_sources = $(foreach dir, $1, $(call get_sources_from_directory,$(dir)))

@ -25,9 +25,33 @@ libiec61850 is an open-source (GPLv3) implementation of an IEC 61850 client and
For commercial projects licenses and support is provided by MZ Automation GmbH. Please contact info@mz-automation.de for more details on licensing options.
## Features
The library support the following IEC 61850 protocol features:
* MMS client/server, GOOSE (IEC 61850-8-1)
* Sampled Values (SV - IEC 61850-9-2)
* Support for buffered and unbuffered reports
* Online report control block configuration
* Data access service (get data, set data)
* online data model discovery and browsing
* all data set services (get values, set values, browse)
* dynamic data set services (create and delete)
* log service
** flexible API to connect custom data bases
** comes with sqlite implementation
* MMS file services (browse, get file, set file, delete/rename file)
** required to download COMTRADE files
* Setting group handling
* GOOSE and SV control block handling
* TLS support
* C and C#/.NET API
## Building and running the examples with the provided makefiles
In the project root directoy type
In the project root directory type
```
make examples
@ -38,14 +62,23 @@ If the build succeeds you can find a few binary files in the projects root direc
Run the sample applications in the example folders. E.g.:
```
cd examples/server_example1
sudo ./server_example1
cd examples/server_example_basic_io
sudo ./server_example_basic_io
```
on the Linux command line.
You can test the server examples by using a generic client or the provided client example applications.
## Building the library with TLS support
Download, unpack, and copy mbedtls-2.6.0 into the third_party/mbedtls folder.
In the main libiec61850 folder run
```
make WITH_MBEDTLS=1
```
## Installing the library and the API headers

@ -157,6 +157,9 @@
/* include support for IEC 61850 log services */
#define CONFIG_IEC61850_LOG_SERVICE 1
/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1
/* overwrite default results for MMS identify service */
//#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"
//#define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850"

@ -151,6 +151,9 @@
/* include support for IEC 61850 log services */
#cmakedefine01 CONFIG_IEC61850_LOG_SERVICE
/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1
/* default results for MMS identify service */
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"
#define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850"

@ -0,0 +1,261 @@
/*
* GooseControlBlock.cs
*
* Copyright 2017 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using IEC61850.Common;
namespace IEC61850
{
namespace Client
{
public class GooseControlBlock {
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientGooseControlBlock_create (string dataAttributeReference);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientGooseControlBlock_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getGoCBValues (IntPtr connection, out int error, string rcbReference, IntPtr updateRcb);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setGoCBValues (IntPtr connection, out int error, IntPtr rcb, UInt32 parametersMask, bool singleRequest);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientGooseControlBlock_getGoEna (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientGooseControlBlock_setGoEna(IntPtr self, bool rptEna);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientGooseControlBlock_getGoID (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientGooseControlBlock_setGoID (IntPtr self, string goId);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientGooseControlBlock_getDatSet (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientGooseControlBlock_setDatSet (IntPtr self, string datSet);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ClientGooseControlBlock_getConfRev (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientGooseControlBlock_getNdsComm (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ClientGooseControlBlock_getMinTime (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ClientGooseControlBlock_getMaxTime (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientGooseControlBlock_getFixedOffs (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern PhyComAddress ClientGooseControlBlock_getDstAddress (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientGooseControlBlock_setDstAddress (IntPtr self, PhyComAddress value);
private IntPtr self;
private IntPtr connection;
private string objectReference;
private bool isDisposed = false;
private bool flagGoEna = false;
private bool flagGoID = false;
private bool flagDatSet = false;
private bool flagDstAddress = false;
internal GooseControlBlock(string objectReference, IntPtr connection)
{
self = ClientGooseControlBlock_create (objectReference);
this.connection = connection;
this.objectReference = objectReference;
}
public string GetObjectReference ()
{
return this.objectReference;
}
/// <summary>
/// Read all GoCB values from the server
/// </summary>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void GetCBValues ()
{
int error;
IedConnection_getGoCBValues (connection, out error, objectReference, self);
if (error != 0)
throw new IedConnectionException ("getGoCBValues service failed", error);
}
private void
resetSendFlags()
{
flagGoEna = false;
flagGoID = false;
flagDatSet = false;
flagDstAddress = false;
}
public void SetCBValues (bool singleRequest)
{
UInt32 parametersMask = 0;
if (flagGoEna)
parametersMask += 1;
if (flagGoID)
parametersMask += 2;
if (flagDatSet)
parametersMask += 4;
if (flagDstAddress)
parametersMask += 32;
int error;
IedConnection_setGoCBValues (connection, out error, self, parametersMask, singleRequest);
resetSendFlags ();
if (error != 0)
throw new IedConnectionException ("setGoCBValues service failed", error);
}
public void SetCBValues ()
{
SetCBValues (true);
}
public bool GetGoEna()
{
return ClientGooseControlBlock_getGoEna (self);
}
public void SetGoEna(bool value)
{
ClientGooseControlBlock_setGoEna (self, value);
flagGoEna = true;
}
public string GetGoID()
{
IntPtr goIdRef = ClientGooseControlBlock_getGoID (self);
return Marshal.PtrToStringAnsi (goIdRef);
}
public void SetGoID (string goID)
{
ClientGooseControlBlock_setGoID (self, goID);
flagGoID = true;
}
public string GetDatSet()
{
IntPtr datSetRef = ClientGooseControlBlock_getDatSet (self);
return Marshal.PtrToStringAnsi (datSetRef);
}
public void SetDataSet(string datSet)
{
ClientGooseControlBlock_setDatSet (self, datSet);
flagDatSet = true;
}
public UInt32 GetConfRev()
{
return ClientGooseControlBlock_getConfRev (self);
}
public bool GetNdsComm()
{
return ClientGooseControlBlock_getNdsComm (self);
}
public UInt32 GetMinTime()
{
return ClientGooseControlBlock_getMinTime (self);
}
public UInt32 GetMaxTime()
{
return ClientGooseControlBlock_getMaxTime (self);
}
public bool GetFixedOffs()
{
return ClientGooseControlBlock_getFixedOffs (self);
}
public PhyComAddress GetDstAddress()
{
return ClientGooseControlBlock_getDstAddress (self);
}
public void SetDstAddress(PhyComAddress value)
{
ClientGooseControlBlock_setDstAddress (self, value);
flagDstAddress = true;
}
public void Dispose()
{
if (isDisposed == false) {
isDisposed = true;
ClientGooseControlBlock_destroy (self);
self = IntPtr.Zero;
}
}
~GooseControlBlock()
{
Dispose ();
}
}
}
}

@ -0,0 +1,313 @@
/*
* GooseSubscriber.cs
*
* Copyright 2017 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
namespace GOOSE
{
namespace Subscriber
{
/// <summary>
/// GOOSE listener.
/// </summary>
public delegate void GooseListener (GooseSubscriber report, object parameter);
public class GooseReceiver : IDisposable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GooseReceiver_create ();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseReceiver_addSubscriber(IntPtr self, IntPtr subscriber);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseReceiver_removeSubscriber(IntPtr self, IntPtr subscriber);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseReceiver_start(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseReceiver_stop(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool GooseReceiver_isRunning (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseReceiver_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseReceiver_setInterfaceId(IntPtr self, string interfaceId);
private IntPtr self;
private bool isDisposed = false;
public GooseReceiver()
{
self = GooseReceiver_create ();
}
public void SetInterfaceId(string interfaceId)
{
GooseReceiver_setInterfaceId (self, interfaceId);
}
public void AddSubscriber(GooseSubscriber subscriber)
{
GooseReceiver_addSubscriber (self, subscriber.self);
}
public void RemoveSubscriber(GooseSubscriber subscriber)
{
GooseReceiver_removeSubscriber (self, subscriber.self);
}
public void Start()
{
GooseReceiver_start (self);
}
public void Stop()
{
GooseReceiver_stop (self);
}
public bool IsRunning()
{
return GooseReceiver_isRunning (self);
}
public void Dispose()
{
if (isDisposed == false) {
isDisposed = true;
GooseReceiver_destroy (self);
self = IntPtr.Zero;
}
}
~GooseReceiver()
{
Dispose ();
}
}
/// <summary>
/// Representing a GOOSE subscriber
/// </summary>
/// <description>
/// NOTE: After SetListener is called, do not call any function outside of
/// the callback handler!
/// </description>
public class GooseSubscriber : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalGooseListener (IntPtr subscriber, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GooseSubscriber_create (string goCbRef, IntPtr dataSetValue);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseSubscriber_setAppId(IntPtr self, UInt16 appId);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool GooseSubscriber_isValid (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 GooseSubscriber_getStNum (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 GooseSubscriber_getSqNum (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool GooseSubscriber_isTest (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 GooseSubscriber_getConfRev (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool GooseSubscriber_needsCommission (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 GooseSubscriber_getTimeAllowedToLive (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt64 GooseSubscriber_getTimestamp (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GooseSubscriber_getDataSetValues(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseSubscriber_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseSubscriber_setListener (IntPtr self, InternalGooseListener listener, IntPtr parameter);
internal IntPtr self;
private bool isDisposed = false;
private GooseListener listener = null;
private object listenerParameter = null;
private event InternalGooseListener internalListener = null;
private void internalGooseListener (IntPtr subscriber, IntPtr parameter)
{
try {
if (listener != null) {
listener(this, listenerParameter);
}
} catch (Exception e)
{
// older versions of mono 2.10 (for linux?) cause this exception
Console.WriteLine(e.Message);
}
}
public GooseSubscriber(string goCbRef)
{
self = GooseSubscriber_create (goCbRef, IntPtr.Zero);
}
public void SetAppId(UInt16 appId)
{
GooseSubscriber_setAppId (self, appId);
}
public bool IsValid ()
{
return GooseSubscriber_isValid (self);
}
public void SetListener(GooseListener listener, object parameter)
{
this.listener = listener;
this.listenerParameter = parameter;
if (internalListener == null) {
internalListener = new InternalGooseListener (internalGooseListener);
GooseSubscriber_setListener (self, internalListener, IntPtr.Zero);
}
}
public UInt32 GetStNum()
{
return GooseSubscriber_getStNum (self);
}
public UInt32 GetSqNum()
{
return GooseSubscriber_getSqNum (self);
}
public bool IsTest()
{
return GooseSubscriber_isTest (self);
}
public UInt32 GetConfRev()
{
return GooseSubscriber_getConfRev (self);
}
public bool NeedsCommission()
{
return GooseSubscriber_needsCommission (self);
}
public UInt32 GetTimeAllowedToLive()
{
return GooseSubscriber_getTimeAllowedToLive (self);
}
public UInt64 GetTimestamp ()
{
return GooseSubscriber_getTimestamp (self);
}
public DateTimeOffset GetTimestampsDateTimeOffset ()
{
UInt64 entryTime = GetTimestamp ();
DateTimeOffset retVal = new DateTimeOffset (1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
return retVal.AddMilliseconds (entryTime);
}
/// <summary>
/// Get the values of the GOOSE data set from the last received GOOSE message
/// </summary>
/// <remarks>
/// The MmsValue instance is only valid in the context of the GooseLister callback.
/// Do not store for outside use!
/// </remarks>
/// <returns>The data set values.</returns>
public MmsValue GetDataSetValues()
{
IntPtr mmsValueRef = GooseSubscriber_getDataSetValues (self);
return (new MmsValue (mmsValueRef));
}
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.GOOSE.Subscriber.GooseSubscriber"/> object.
/// </summary>
/// <remarks>>
/// This function has only to be called when the <see cref="IEC61850.GOOSE.Subscriber.GooseSubscriber"/>
/// has not been added to the GooseReceiver or has been removed from the GooseReceiver.
/// When the GooseReceiver holds a reference it will take care for releasing the resources.
/// In this case Dispose MUST not be called! Otherwise the natice resources will be
/// released twice.
/// </remarks>
public void Dispose()
{
if (isDisposed == false) {
isDisposed = true;
GooseSubscriber_destroy (self);
self = IntPtr.Zero;
}
}
}
}
}
}

@ -7,6 +7,8 @@
<OutputType>Library</OutputType>
<RootNamespace>iec61850dotnet</RootNamespace>
<AssemblyName>iec61850dotnet</AssemblyName>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -45,6 +47,11 @@
<Compile Include="MmsVariableSpecification.cs" />
<Compile Include="IEC61850ServerAPI.cs" />
<Compile Include="IEC61850CommonAPI.cs" />
<Compile Include="TLS.cs" />
<Compile Include="SampledValuesControlBlock.cs" />
<Compile Include="GooseControlBlock.cs" />
<Compile Include="GooseSubscriber.cs" />
<Compile Include="SampledValuesSubscriber.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

@ -27,6 +27,7 @@ using System.Collections.Generic;
using System.Collections;
using IEC61850.Common;
using IEC61850.TLS;
/// <summary>
/// IEC 61850 API for the libiec61850 .NET wrapper library
@ -276,6 +277,9 @@ namespace IEC61850
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedConnection_create ();
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedConnection_createWithTlsSupport (IntPtr tlsConfig);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern void IedConnection_destroy (IntPtr self);
@ -405,11 +409,23 @@ namespace IEC61850
private InternalConnectionClosedHandler connectionClosedHandler;
private ConnectionClosedHandler userProvidedHandler = null;
/// <summary>
/// Initializes a new instance of the <see cref="IEC61850.Client.IedConnection"/> class.
/// </summary>
public IedConnection ()
{
connection = IedConnection_create ();
}
/// <summary>
/// Initializes a new instance of the <see cref="IEC61850.Client.IedConnection"/> class.
/// </summary>
/// <param name="tlsConfig">TLS configuration to use</param>
public IedConnection (TLSConfiguration tlsConfig)
{
connection = IedConnection_createWithTlsSupport (tlsConfig.GetNativeInstance ());
}
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.Client.IedConnection"/> object.
/// </summary>
@ -504,7 +520,7 @@ namespace IEC61850
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error.</exception>
public void Connect (string hostname)
{
Connect (hostname, 102);
Connect (hostname, -1);
}
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
@ -515,10 +531,28 @@ namespace IEC61850
return controlObject;
}
/// <summary>
/// Creates a new SampledValuesControlBlock instance.
/// </summary>
/// <description>>
/// This function will also read the SVCB values from the server.
/// </description>
/// <returns>The new SVCB instance</returns>
/// <param name="svcbObjectReference">The object reference of the SVCB</param>
public SampledValuesControlBlock GetSvControlBlock (string svcbObjectReference)
{
return new SampledValuesControlBlock (connection, svcbObjectReference);
}
/// <summary>
/// Creates a new SampledValuesControlBlock instance.
/// </summary>
/// <returns>The new GoCB instance</returns>
/// <param name="gocbObjectReference">The object reference of the GoCB</param>
public GooseControlBlock GetGooseControlBlock (string gocbObjectReference)
{
return new GooseControlBlock (gocbObjectReference, connection);
}
/// <summary>
/// Updates the device model by quering the server.
@ -1037,6 +1071,18 @@ namespace IEC61850
throw new IedConnectionException ("Write value failed", error);
}
/// <summary>Delete file</summary>
/// <param name="fileName">The name of the file.</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void DeleteFile (string fileName)
{
int error;
IedConnection_deleteFile (connection, out error, fileName);
if (error != 0)
throw new IedConnectionException ("Deleting file " + fileName + " failed", error);
}
/// <summary>Delete file</summary>
/// <param name="fileName">The name of the file.</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>

@ -1,4 +1,27 @@
using System;
/*
* IEC61850CommonAPI.cs
*
* Copyright 2014-2017 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
namespace IEC61850
@ -49,6 +72,17 @@ namespace IEC61850
}
}
[StructLayout(LayoutKind.Sequential)]
public class PhyComAddress
{
public byte vlanPriority;
public UInt16 vlanId;
public UInt16 appId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=6)]
public byte[] dstAddress = new byte[6];
}
/// <summary>
/// MMS data access error for MmsValue type MMS_DATA_ACCESS_ERROR
/// </summary>
@ -85,6 +119,29 @@ namespace IEC61850
GI = 16
}
/// <summary>
/// SmpMod values
/// </summary>
public enum SmpMod {
SAMPLES_PER_PERIOD = 0,
SAMPLES_PER_SECOND = 1,
SECONDS_PER_SAMPLE = 2
}
/// <summary>
/// Values for Sampled Values (SV) OptFlds
/// </summary>
[Flags]
public enum SVOptions {
NONE = 0,
REFRESH_TIME = 1,
SAMPLE_SYNC = 2,
SAMPLE_RATE = 4,
DATA_SET = 8,
SECURITY = 16,
ALL = 31
}
[Flags]
public enum ReportOptions {
NONE = 0,
@ -96,7 +153,9 @@ namespace IEC61850
BUFFER_OVERFLOW = 32,
ENTRY_ID = 64,
CONF_REV = 128,
ALL = 255
SEGMENTATION = 256,
ALL = SEQ_NUM | TIME_STAMP | REASON_FOR_INCLUSION | DATA_SET | DATA_REFERENCE |
BUFFER_OVERFLOW | ENTRY_ID | CONF_REV | SEGMENTATION
}
public enum Validity
@ -112,9 +171,26 @@ namespace IEC61850
/// </summary>
public class Quality
{
private UInt16 value;
private const UInt16 QUALITY_DETAIL_OVERFLOW = 4;
private const UInt16 QUALITY_DETAIL_OUT_OF_RANGE = 8;
private const UInt16 QUALITY_DETAIL_BAD_REFERENCE = 16;
private const UInt16 QUALITY_DETAIL_OSCILLATORY = 32;
private const UInt16 QUALITY_DETAIL_FAILURE = 64;
private const UInt16 QUALITY_DETAIL_OLD_DATA = 128;
private const UInt16 QUALITY_DETAIL_INCONSISTENT = 256;
private const UInt16 QUALITY_DETAIL_INACCURATE = 512;
private const UInt16 QUALITY_SOURCE_SUBSTITUTED = 1024;
private const UInt16 QUALITY_TEST = 2048;
private const UInt16 QUALITY_OPERATOR_BLOCKED = 4096;
public override string ToString ()
{
return GetValidity ().ToString ();
}
public Quality (int bitStringValue)
{
value = (UInt16)bitStringValue;
@ -138,6 +214,133 @@ namespace IEC61850
value += (ushort)validity;
}
public Validity Validity
{
get {return GetValidity ();}
set { SetValidity (value); }
}
public bool Overflow
{
get { return ((this.value & QUALITY_DETAIL_OVERFLOW) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_OVERFLOW;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OVERFLOW));
}
}
public bool OutOfRange
{
get { return ((this.value & QUALITY_DETAIL_OUT_OF_RANGE) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_OUT_OF_RANGE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OUT_OF_RANGE));
}
}
public bool BadReference
{
get { return ((this.value & QUALITY_DETAIL_BAD_REFERENCE) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_BAD_REFERENCE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_BAD_REFERENCE));
}
}
public bool Oscillatory
{
get { return ((this.value & QUALITY_DETAIL_OSCILLATORY) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_OSCILLATORY;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OSCILLATORY));
}
}
public bool Failure
{
get { return ((this.value & QUALITY_DETAIL_FAILURE) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_FAILURE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_FAILURE));
}
}
public bool OldData
{
get { return ((this.value & QUALITY_DETAIL_OLD_DATA) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_OLD_DATA;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OLD_DATA));
}
}
public bool Inconsistent
{
get { return ((this.value & QUALITY_DETAIL_INCONSISTENT) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_INCONSISTENT;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_INCONSISTENT));
}
}
public bool Inaccurate
{
get { return ((this.value & QUALITY_DETAIL_INACCURATE) != 0);}
set {
if (value)
this.value |= QUALITY_DETAIL_INACCURATE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_INACCURATE));
}
}
public bool Substituted
{
get { return ((this.value & QUALITY_SOURCE_SUBSTITUTED) != 0);}
set {
if (value)
this.value |= QUALITY_SOURCE_SUBSTITUTED;
else
this.value = (ushort) ((int) this.value & (~QUALITY_SOURCE_SUBSTITUTED));
}
}
public bool Test
{
get { return ((this.value & QUALITY_TEST) != 0);}
set {
if (value)
this.value |= QUALITY_TEST;
else
this.value = (ushort) ((int) this.value & (~QUALITY_TEST));
}
}
public bool OperatorBlocked
{
get { return ((this.value & QUALITY_OPERATOR_BLOCKED) != 0);}
set {
if (value)
this.value |= QUALITY_OPERATOR_BLOCKED;
else
this.value = (ushort) ((int) this.value & (~QUALITY_OPERATOR_BLOCKED));
}
}
}
/// <summary>
@ -148,6 +351,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr Timestamp_create ();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr Timestamp_createFromByteArray(byte[] byteArry);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void Timestamp_destroy (IntPtr self);
@ -198,12 +404,12 @@ namespace IEC61850
static extern void Timestamp_setByMmsUtcTime (IntPtr self, IntPtr mmsValue);
internal IntPtr timestampRef = IntPtr.Zero;
private bool responsableForDeletion;
private bool responsibleForDeletion;
internal Timestamp(IntPtr timestampRef, bool selfAllocated)
{
this.timestampRef = timestampRef;
this.responsableForDeletion = selfAllocated;
this.responsibleForDeletion = selfAllocated;
}
public Timestamp (DateTime timestamp) : this ()
@ -220,12 +426,18 @@ namespace IEC61850
{
timestampRef = Timestamp_create ();
LeapSecondKnown = true;
responsableForDeletion = true;
responsibleForDeletion = true;
}
public Timestamp(byte[] value)
{
timestampRef = Timestamp_createFromByteArray (value);
responsibleForDeletion = true;
}
~Timestamp ()
{
if (responsableForDeletion)
if (responsibleForDeletion)
Timestamp_destroy (timestampRef);
}
@ -335,6 +547,15 @@ namespace IEC61850
Timestamp_setByMmsUtcTime (timestampRef, mmsValue.valueReference);
}
public DateTime AsDateTime()
{
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
DateTime retVal = epoch.AddMilliseconds ((double) GetTimeInMilliseconds ());
return retVal;
}
}
public enum ACSIClass

@ -474,9 +474,9 @@ namespace IEC61850
public IntPtr self = IntPtr.Zero;
public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered,
string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd)
string dataSetName, uint confRev, byte trgOps, byte options, uint bufTm, uint intgPd)
{
self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRef, trgOps, options, bufTm, intgPd);
self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd);
}
}

@ -430,9 +430,13 @@ namespace IEC61850
public MmsDataAccessError GetDataAccessError ()
{
int errorCode = MmsValue_getDataAccessError (valueReference);
if (GetType () == MmsType.MMS_DATA_ACCESS_ERROR) {
int errorCode = MmsValue_getDataAccessError (valueReference);
return (MmsDataAccessError)errorCode;
return (MmsDataAccessError)errorCode;
}
else
throw new MmsValueException ("Value is of wrong type");
}
/// <summary>

@ -0,0 +1,203 @@
/*
* SampledValuesControlBlock.cs
*
* Copyright 2017 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using IEC61850.Common;
namespace IEC61850
{
namespace Client
{
/// <summary>
/// Sampled values control bloc (SvCB) representation.
/// </summary>
/// <description>
/// This class is used as a client side representation (copy) of a sampled values control block (SvCB).
/// </description>
public class SampledValuesControlBlock : IDisposable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientSVControlBlock_create (IntPtr iedConnection, string reference);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientSVControlBlock_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ClientSVControlBlock_getLastComError (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientSVControlBlock_isMulticast (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientSVControlBlock_setSvEna (IntPtr self, bool value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientSVControlBlock_setResv (IntPtr self, bool value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientSVControlBlock_getSvEna (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ClientSVControlBlock_getResv (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientSVControlBlock_getMsvID (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientSVControlBlock_getDatSet (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ClientSVControlBlock_getConfRev (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 ClientSVControlBlock_getSmpRate (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ClientSVControlBlock_getOptFlds (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern byte ClientSVControlBlock_getSmpMod(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ClientSVControlBlock_getNoASDU (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern PhyComAddress ClientSVControlBlock_getDstAddress (IntPtr self);
private IntPtr self;
private string objectReference;
private bool isDisposed = false;
internal SampledValuesControlBlock(IntPtr iedConnection, string objectReference)
{
self = ClientSVControlBlock_create (iedConnection, objectReference);
this.objectReference = objectReference;
}
public string GetObjectReference ()
{
return this.objectReference;
}
public IedClientError GetLastComError()
{
return (IedClientError)ClientSVControlBlock_getLastComError (self);
}
public bool IsMulticast()
{
return ClientSVControlBlock_isMulticast (self);
}
public bool GetResv()
{
return ClientSVControlBlock_getResv (self);
}
public bool SetResv(bool value)
{
return ClientSVControlBlock_setResv (self, value);
}
public bool GetSvEna()
{
return ClientSVControlBlock_getSvEna (self);
}
public bool SetSvEna(bool value)
{
return ClientSVControlBlock_setSvEna (self, value);
}
public string GetMsvID ()
{
IntPtr msvIdPtr = ClientSVControlBlock_getMsvID (self);
return Marshal.PtrToStringAnsi (msvIdPtr);
}
public string GetDatSet ()
{
IntPtr datSetPtr = ClientSVControlBlock_getDatSet (self);
return Marshal.PtrToStringAnsi (datSetPtr);
}
public UInt32 GetConfRev ()
{
return ClientSVControlBlock_getConfRev (self);
}
public UInt16 GetSmpRate ()
{
return ClientSVControlBlock_getSmpRate (self);
}
public SVOptions GetOptFlds ()
{
return (SVOptions)ClientSVControlBlock_getOptFlds (self);
}
public SmpMod GetSmpMod ()
{
return (SmpMod)ClientSVControlBlock_getSmpMod (self);
}
public int GetNoASDU ()
{
return ClientSVControlBlock_getNoASDU (self);
}
public PhyComAddress GetDstAddress()
{
return ClientSVControlBlock_getDstAddress (self);
}
public void Dispose()
{
if (isDisposed == false) {
isDisposed = true;
ClientSVControlBlock_destroy (self);
self = IntPtr.Zero;
}
}
~SampledValuesControlBlock()
{
Dispose ();
}
}
}
}

@ -0,0 +1,474 @@
/*
* SampledValuedSubscriber.cs
*
* Copyright 2017 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
namespace SV
{
namespace Subscriber
{
/// <summary>
/// SV receiver.
/// </summary>
/// A receiver is responsible for processing all SV message for a single Ethernet interface.
/// In order to process messages from multiple Ethernet interfaces you have to create multiple
/// instances.
public class SVReceiver : IDisposable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVReceiver_create ();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_disableDestAddrCheck(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_enableDestAddrCheck(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_addSubscriber(IntPtr self, IntPtr subscriber);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_removeSubscriber(IntPtr self, IntPtr subscriber);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_start(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_stop(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVReceiver_isRunning (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_setInterfaceId(IntPtr self, string interfaceId);
private IntPtr self;
private bool isDisposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="IEC61850.SV.Subscriber.SVReceiver"/> class.
/// </summary>
public SVReceiver()
{
self = SVReceiver_create ();
}
public void SetInterfaceId(string interfaceId)
{
SVReceiver_setInterfaceId (self, interfaceId);
}
public void DisableDestAddrCheck()
{
SVReceiver_disableDestAddrCheck (self);
}
public void EnableDestAddrCheck()
{
SVReceiver_enableDestAddrCheck (self);
}
/// <summary>
/// Add a subscriber to handle
/// </summary>
/// <param name="subscriber">Subscriber.</param>
public void AddSubscriber(SVSubscriber subscriber)
{
SVReceiver_addSubscriber (self, subscriber.self);
}
public void RemoveSubscriber(SVSubscriber subscriber)
{
SVReceiver_removeSubscriber (self, subscriber.self);
}
/// <summary>
/// Start handling SV messages
/// </summary>
public void Start()
{
SVReceiver_start (self);
}
/// <summary>
/// Stop handling SV messges
/// </summary>
public void Stop()
{
SVReceiver_stop (self);
}
public bool IsRunning()
{
return SVReceiver_isRunning (self);
}
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.SV.Subscriber.SVReceiver"/> object.
/// </summary>
/// <remarks>Call <see cref="Dispose"/> when you are finished using the <see cref="IEC61850.SV.Subscriber.SVReceiver"/>. The
/// <see cref="Dispose"/> method leaves the <see cref="IEC61850.SV.Subscriber.SVReceiver"/> in an unusable state.
/// After calling <see cref="Dispose"/>, you must release all references to the
/// <see cref="IEC61850.SV.Subscriber.SVReceiver"/> so the garbage collector can reclaim the memory that the
/// <see cref="IEC61850.SV.Subscriber.SVReceiver"/> was occupying.</remarks>
public void Dispose()
{
if (isDisposed == false) {
isDisposed = true;
SVReceiver_destroy (self);
self = IntPtr.Zero;
}
}
~SVReceiver()
{
Dispose ();
}
}
/// <summary>
/// SV listener.
/// </summary>
public delegate void SVUpdateListener (SVSubscriber report, object parameter, SVSubscriberASDU asdu);
/// <summary>
/// Sampled Values (SV) Subscriber
///
/// A subscriber is an instance associated with a single stream of measurement data. It is identified
/// by the Ethernet destination address, the appID value (both are on SV message level) and the svID value
/// that is part of each ASDU.
/// </summary>
public class SVSubscriber : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalSVUpdateListener (IntPtr subscriber, IntPtr parameter, IntPtr asdu);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_create([Out] byte[] ethAddr, UInt16 appID);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_create(IntPtr ethAddr, UInt16 appID);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVSubscriber_setListener(IntPtr self, InternalSVUpdateListener listener, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVSubscriber_destroy(IntPtr self);
internal IntPtr self;
private bool isDisposed = false;
private SVUpdateListener listener;
private object listenerParameter = null;
private event InternalSVUpdateListener internalListener = null;
private void internalSVUpdateListener (IntPtr subscriber, IntPtr parameter, IntPtr asdu)
{
try {
if (listener != null) {
listener(this, listenerParameter, new SVSubscriberASDU(asdu));
}
}
catch (Exception e) {
// older versions of mono 2.10 (for linux?) cause this exception
Console.WriteLine(e.Message);
}
}
public SVSubscriber(byte[] ethAddr, UInt16 appID)
{
if (ethAddr == null) {
self = SVSubscriber_create (IntPtr.Zero, appID);
} else {
if (ethAddr.Length != 6)
throw new ArgumentException ("ethAddr argument has to be of 6 byte size");
self = SVSubscriber_create (ethAddr, appID);
}
}
public void SetListener(SVUpdateListener listener, object parameter)
{
this.listener = listener;
this.listenerParameter = parameter;
if (internalListener == null) {
internalListener = new InternalSVUpdateListener (internalSVUpdateListener);
SVSubscriber_setListener (self, internalListener, IntPtr.Zero);
}
}
public void Dispose()
{
if (isDisposed == false) {
isDisposed = true;
SVSubscriber_destroy (self);
self = IntPtr.Zero;
}
}
}
public class SVSubscriberASDU
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getSmpCnt(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_ASDU_getSvId(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_ASDU_getDatSet(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 SVSubscriber_ASDU_getConfRev(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern byte SVSubscriber_ASDU_getSmpMod(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getSmpRate(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasDatSet(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasRefrTm(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasSmpMod(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasSmpRate(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt64 SVSubscriber_ASDU_getRefrTmAsMs(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern sbyte SVSubscriber_ASDU_getINT8(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern Int16 SVSubscriber_ASDU_getINT16(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern Int32 SVSubscriber_ASDU_getINT32(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern Int64 SVSubscriber_ASDU_getINT64(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern byte SVSubscriber_ASDU_getINT8U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getINT16U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 SVSubscriber_ASDU_getINT32U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt64 SVSubscriber_ASDU_getINT64U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern float SVSubscriber_ASDU_getFLOAT32(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern double SVSubscriber_ASDU_getFLOAT64(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getQuality(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern int SVSubscriber_ASDU_getDataSize(IntPtr self);
private IntPtr self;
internal SVSubscriberASDU (IntPtr self)
{
this.self = self;
}
public UInt16 GetSmpCnt()
{
return SVSubscriber_ASDU_getSmpCnt (self);
}
public string GetSvId()
{
return Marshal.PtrToStringAnsi (SVSubscriber_ASDU_getSvId(self));
}
public string GetDatSet()
{
return Marshal.PtrToStringAnsi (SVSubscriber_ASDU_getDatSet(self));
}
public UInt32 GetConfRev()
{
return SVSubscriber_ASDU_getConfRev (self);
}
public SmpMod GetSmpMod()
{
return (SmpMod) SVSubscriber_ASDU_getSmpMod (self);
}
public UInt16 GetSmpRate()
{
return (UInt16)SVSubscriber_ASDU_getSmpRate (self);
}
public bool HasDatSet()
{
return SVSubscriber_ASDU_hasDatSet (self);
}
public bool HasRefrRm()
{
return SVSubscriber_ASDU_hasRefrTm (self);
}
public bool HasSmpMod()
{
return SVSubscriber_ASDU_hasSmpMod (self);
}
public bool HasSmpRate()
{
return SVSubscriber_ASDU_hasSmpRate (self);
}
public UInt64 GetRefrTmAsMs()
{
return SVSubscriber_ASDU_getRefrTmAsMs (self);
}
public sbyte GetINT8(int index)
{
return SVSubscriber_ASDU_getINT8 (self, index);
}
public Int16 GetINT16(int index)
{
return SVSubscriber_ASDU_getINT16 (self, index);
}
public Int32 GetINT32(int index)
{
return SVSubscriber_ASDU_getINT32 (self, index);
}
public Int64 GetINT64(int index)
{
return SVSubscriber_ASDU_getINT64 (self, index);
}
public byte GetINT8U(int index)
{
return SVSubscriber_ASDU_getINT8U (self, index);
}
public UInt16 GetINT16U(int index)
{
return SVSubscriber_ASDU_getINT16U (self, index);
}
public UInt32 GetINT32U(int index)
{
return SVSubscriber_ASDU_getINT32U (self, index);
}
public UInt64 GetINT64U(int index)
{
return SVSubscriber_ASDU_getINT64U (self, index);
}
public float GetFLOAT32(int index)
{
return SVSubscriber_ASDU_getFLOAT32 (self, index);
}
public double GetFLOAT64(int index)
{
return SVSubscriber_ASDU_getFLOAT64 (self, index);
}
private struct PTimestamp
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 8)]
public byte[] val;
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern PTimestamp SVSubscriber_ASDU_getTimestamp(IntPtr self, int index);
public Timestamp GetTimestamp(int index)
{
PTimestamp retVal = SVSubscriber_ASDU_getTimestamp (self, index);
return new Timestamp (retVal.val);
}
public Quality GetQuality(int index)
{
UInt16 qValue = SVSubscriber_ASDU_getQuality (self, index);
return new Quality (qValue);
}
/// <summary>
/// Gets the size of the payload data in bytes. The payload comprises the data set data.
/// </summary>
/// <returns>The payload data size in byte</returns>
public int GetDataSize()
{
return SVSubscriber_ASDU_getDataSize (self);
}
}
}
}
}

@ -0,0 +1,215 @@
/*
* TLS.cs
*
* Copyright 2017 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using IEC61850.Common;
/// <summary>
/// IEC 61850 API for the libiec61850 .NET wrapper library
/// </summary>
namespace IEC61850
{
namespace TLS
{
/// <summary>
/// A container for TLS configuration and certificates.
/// </summary>
public class TLSConfiguration : IDisposable
{
private IntPtr self = IntPtr.Zero;
private bool allowOnlyKnownCerts = false;
private bool chainValidation = true;
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr TLSConfiguration_create();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void TLSConfiguration_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, bool value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void TLSConfiguration_setChainValidation (IntPtr self, bool value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void TLSConfiguration_setClientMode(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_setOwnKeyFromFile (IntPtr self, string filename, string keyPassword);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename);
public TLSConfiguration() {
self = TLSConfiguration_create ();
}
~TLSConfiguration()
{
Dispose ();
}
internal IntPtr GetNativeInstance()
{
return self;
}
public bool AllowOnlyKnownCertificates
{
set {
TLSConfiguration_setAllowOnlyKnownCertificates (self, value);
allowOnlyKnownCerts = value;
}
get {
return allowOnlyKnownCerts;
}
}
public bool ChainValidation
{
set {
TLSConfiguration_setChainValidation (self, value);
chainValidation = value;
}
get {
return chainValidation;
}
}
public void SetClientMode()
{
TLSConfiguration_setClientMode (self);
}
public void SetOwnCertificate(string filename)
{
if (TLSConfiguration_setOwnCertificateFromFile (self, filename) == false) {
Console.WriteLine ("Failed to read certificate from file!");
throw new CryptographicException ("Failed to read certificate from file");
}
}
public void SetOwnCertificate(X509Certificate2 cert)
{
byte[] certBytes = cert.GetRawCertData ();
if (TLSConfiguration_setOwnCertificate (self, certBytes, certBytes.Length) == false) {
Console.WriteLine ("Failed to set certificate!");
throw new CryptographicException ("Failed to set certificate");
}
}
public void AddAllowedCertificate(string filename)
{
if (TLSConfiguration_addAllowedCertificateFromFile (self, filename) == false) {
Console.WriteLine ("Failed to read allowed certificate from file!");
throw new CryptographicException ("Failed to read allowed certificate from file");
}
}
public void AddAllowedCertificate(X509Certificate2 cert)
{
byte[] certBytes = cert.GetRawCertData ();
if (TLSConfiguration_addAllowedCertificate (self, certBytes, certBytes.Length) == false) {
Console.WriteLine ("Failed to add allowed certificate!");
throw new CryptographicException ("Failed to add allowed certificate");
}
}
public void AddCACertificate(string filename)
{
if (TLSConfiguration_addCACertificateFromFile (self, filename) == false) {
Console.WriteLine ("Failed to read CA certificate from file!");
throw new CryptographicException ("Failed to read CA certificate from file");
}
}
public void AddCACertificate(X509Certificate2 cert)
{
byte[] certBytes = cert.GetRawCertData ();
if (TLSConfiguration_addCACertificate (self, certBytes, certBytes.Length) == false) {
Console.WriteLine ("Failed to add CA certificate!");
throw new CryptographicException ("Failed to add CA certificate");
}
}
public void SetOwnKey (string filename, string password)
{
if (TLSConfiguration_setOwnKeyFromFile (self, filename, password) == false) {
Console.WriteLine ("Failed to read own key from file!");
throw new CryptographicException ("Failed to read own key from file");
}
}
public void SetOwnKey (X509Certificate2 key, string password)
{
byte[] certBytes = key.Export (X509ContentType.Pkcs12);
if (TLSConfiguration_setOwnKey (self, certBytes, certBytes.Length, password) == false) {
Console.WriteLine ("Failed to set own key!");
throw new CryptographicException ("Failed to set own key");
}
}
public void Dispose()
{
lock (this) {
if (self != IntPtr.Zero) {
TLSConfiguration_destroy (self);
self = IntPtr.Zero;
}
}
}
}
}
}

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEC61850forCSharp", "IEC61850forCSharp\IEC61850forCSharp.csproj", "{C35D624E-5506-4560-8074-1728F1FA1A4D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEC61850.NET", "IEC61850forCSharp\IEC61850.NET.csproj", "{C35D624E-5506-4560-8074-1728F1FA1A4D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example1", "example1\example1.csproj", "{C616A6DF-831E-443C-9310-3F343A6E3D1A}"
EndProject
@ -36,6 +36,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{0D2F61
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server1", "server1\server1.csproj", "{9286D2AB-96ED-4631-AB3C-ED20FF5D6E6C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tls_client_example", "tls_client_example\tls_client_example.csproj", "{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "goose_subscriber", "goose_subscriber\goose_subscriber.csproj", "{1285372C-2E62-494A-A661-8D5D3873318C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sv_subscriber", "sv_subscriber\sv_subscriber.csproj", "{44651D2D-3252-4FD5-8B8B-5552DBE1B499}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -46,6 +52,10 @@ Global
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.Build.0 = Release|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.Build.0 = Release|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -54,6 +64,10 @@ Global
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Release|Any CPU.Build.0 = Release|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Release|Any CPU.Build.0 = Release|Any CPU
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
@ -62,6 +76,10 @@ Global
{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
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Release|Any CPU.Build.0 = Release|Any CPU
{71485F99-2976-45E6-B73D-4946E594C15C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71485F99-2976-45E6-B73D-4946E594C15C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71485F99-2976-45E6-B73D-4946E594C15C}.Release|Any CPU.ActiveCfg = Release|Any CPU

@ -26,7 +26,6 @@ namespace example1
Console.WriteLine("Connect to " + hostname);
try
{
con.Connect(hostname, port);

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -0,0 +1,68 @@
using System;
using IEC61850.GOOSE.Subscriber;
using System.Threading;
using IEC61850.Common;
namespace goose_subscriber
{
class MainClass
{
private static void gooseListener (GooseSubscriber subscriber, object parameter)
{
Console.WriteLine ("Received GOOSE message:\n-------------------------");
Console.WriteLine (" stNum: " + subscriber.GetStNum ());
Console.WriteLine (" sqNum: " + subscriber.GetSqNum ());
MmsValue values = subscriber.GetDataSetValues ();
Console.WriteLine (" values: " +values.Size ().ToString ());
foreach (MmsValue value in values) {
Console.WriteLine (" value: " + value.ToString ());
}
}
public static void Main (string[] args)
{
Console.WriteLine ("Starting GOOSE subscriber...");
GooseReceiver receiver = new GooseReceiver ();
receiver.SetInterfaceId ("eth0");
GooseSubscriber subscriber = new GooseSubscriber ("simpleIOGenericIO/LLN0$GO$gcbAnalogValues");
subscriber.SetAppId(1000);
subscriber.SetListener (gooseListener, null);
receiver.AddSubscriber (subscriber);
receiver.Start ();
if (receiver.IsRunning ()) {
bool running = true;
/* run until Ctrl-C is pressed */
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
e.Cancel = true;
running = false;
};
while (running) {
Thread.Sleep (100);
}
receiver.Stop ();
} else
Console.WriteLine ("Failed to start GOOSE receiver. Running as root?");
receiver.Dispose ();
}
}
}

@ -0,0 +1,27 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("goose_subscriber")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("mzillgit")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

@ -0,0 +1,44 @@
<?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>
<ProjectGuid>{1285372C-2E62-494A-A661-8D5D3873318C}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>goose_subscriber</RootNamespace>
<AssemblyName>goose_subscriber</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</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>full</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="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -36,9 +36,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -35,9 +35,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -51,9 +51,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>

@ -36,9 +36,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -0,0 +1,74 @@
using System;
using IEC61850.SV.Subscriber;
using IEC61850.Common;
using System.Threading;
namespace sv_subscriber
{
class MainClass
{
private static void svUpdateListener(SVSubscriber subscriber, object parameter, SVSubscriberASDU asdu)
{
Console.WriteLine ("RECV ASDU:");
string svID = asdu.GetSvId ();
if (svID != null)
Console.WriteLine (" svID=" + svID);
Console.WriteLine (" smpCnt: " + asdu.GetSmpCnt ());
Console.WriteLine (" confRev: " + asdu.GetConfRev ());
if (asdu.GetDataSize () >= 8) {
Console.WriteLine (" DATA[0]: " + asdu.GetFLOAT32(0));
Console.WriteLine (" DATA[1]: " + asdu.GetFLOAT32 (4));
}
if (asdu.GetDataSize () >= 16) {
Console.WriteLine (" DATA[2]: " + asdu.GetTimestamp (8).AsDateTime().ToString());
}
}
public static void Main (string[] args)
{
Console.WriteLine ("Starting SV subscriber");
SVReceiver receiver = new SVReceiver ();
if (args.Length > 0) {
receiver.SetInterfaceId (args [0]);
}
SVSubscriber subscriber = new SVSubscriber (null, 0x4000);
subscriber.SetListener (svUpdateListener, null);
receiver.AddSubscriber (subscriber);
receiver.Start ();
if (receiver.IsRunning ()) {
bool running = true;
/* run until Ctrl-C is pressed */
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
e.Cancel = true;
running = false;
};
while (running) {
Thread.Sleep (100);
}
receiver.Stop ();
} else
Console.WriteLine ("Failed to start SV receiver. Running as root?");
receiver.Dispose ();
}
}
}

@ -0,0 +1,27 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("sv_subscriber")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("mzillgit")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

@ -0,0 +1,44 @@
<?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>
<ProjectGuid>{44651D2D-3252-4FD5-8B8B-5552DBE1B499}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>sv_subscriber</RootNamespace>
<AssemblyName>sv_subscriber</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</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>full</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="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -454,6 +454,33 @@ namespace tests
iedServer.Stop ();
}
[Test()]
public void Quality()
{
Quality q = new Quality ();
Assert.AreEqual (false, q.Overflow);
q.Overflow = true;
Assert.AreEqual (true, q.Overflow);
q.Overflow = false;
Assert.AreEqual (false, q.Overflow);
Assert.AreEqual (Validity.GOOD, q.Validity);
q.Substituted = true;
Assert.AreEqual (true, q.Substituted);
Assert.AreEqual (false, q.Overflow);
q.Validity = Validity.QUESTIONABLE;
Assert.AreEqual (Validity.QUESTIONABLE, q.Validity);
}
}
}

@ -31,9 +31,9 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850forCSharp</Name>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>

@ -0,0 +1,101 @@
using System;
using IEC61850.Client;
using IEC61850.Common;
using IEC61850.TLS;
using System.Threading;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
namespace tls_client_example
{
class MainClass
{
public static void Main (string[] args)
{
TLSConfiguration tlsConfig = new TLSConfiguration ();
tlsConfig.SetOwnCertificate (new X509Certificate2 ("client1.cer"));
tlsConfig.SetOwnKey ("client1-key.pem", null);
// Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false
tlsConfig.AddCACertificate (new X509Certificate2 ("root.cer"));
// Check if the certificate is signed by a provided CA
tlsConfig.ChainValidation = true;
// Check that the shown server certificate is in the list of allowed certificates
tlsConfig.AllowOnlyKnownCertificates = false;
IedConnection con = new IedConnection (tlsConfig);
string hostname;
if (args.Length > 0)
hostname = args[0];
else
hostname = "127.0.0.1";
int port = -1;
if (args.Length > 1)
port = Int32.Parse(args [1]);
Console.WriteLine("Connect to " + hostname);
try
{
con.Connect(hostname, port);
List<string> serverDirectory = con.GetServerDirectory(false);
foreach (string entry in serverDirectory)
{
Console.WriteLine("LD: " + entry);
}
List<string> lnDirectory = con.GetLogicalNodeDirectory("simpleIOGenericIO/LLN0", ACSIClass.ACSI_CLASS_DATA_SET);
foreach (string entry in lnDirectory)
{
Console.WriteLine("Dataset: " + entry);
}
string vendor = con.ReadStringValue ("simpleIOGenericIO/LLN0.NamPlt.vendor", FunctionalConstraint.DC);
Console.WriteLine ("Vendor: " + vendor);
/* read FCDO */
MmsValue value = con.ReadValue("simpleIOGenericIO/GGIO1.AnIn1", FunctionalConstraint.MX);
if (value.GetType() == MmsType.MMS_STRUCTURE)
{
Console.WriteLine("Value is of complex type");
for (int i = 0; i < value.Size(); i++)
{
Console.WriteLine(" element: " + value.GetElement(i).GetType());
if (value.GetElement(i).GetType() == MmsType.MMS_UTC_TIME)
{
Console.WriteLine(" -> " + value.GetElement(i).GetUtcTimeAsDateTimeOffset());
}
}
}
DataSet dataSet = con.ReadDataSetValues("simpleIOGenericIO/LLN0.Events", null);
Console.WriteLine("Read data set " + dataSet.GetReference());
con.Abort();
}
catch (IedConnectionException e)
{
Console.WriteLine(e.Message);
}
System.Threading.Thread.Sleep(2000);
// release all resources - do NOT use the object after this call!!
con.Dispose ();
}
}
}

@ -0,0 +1,27 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("tls_client_example")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("mzillgit")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxAMUXdnem99n5J8Z8Wa0fdPtoMCTdkQJrOO6WJ4XePrpQgHU
HDziSmdIZDDkpJ3Ey0Byy+b+iiRDmOuIZGSCsI0ehggWaia12h2osUK0BLyThuZ/
RQw54K0dy61eviNaYsftiBcxHYKyKmWch/wLLYxdd1qLzd0reAbSUaHDkDzrj9hO
8qr7DhKpqx7PoVh1gFhAKPKuY6b+4xqv5eZvt8QflQTWYGxaxmUIEinqCnzh1l5d
tp0rhnBsz9Y4y8dXjh0m7pbXmRNY6opMxXatqgYEqsntLy1N6x7DvWLBqtVvEmox
Tc5bbAoRW3eEToClDdFQBzLsMVcSEX8vwttk3QIDAQABAoIBABHr1ijeiqPlwTH9
+flAUrBOeCOCd/kQL3JHP/pqOestxbXrROFwD6CN4OiIL999LUkIE3bhH9SxjByn
LElBh1FtFaVbh/EcqPPQUmQinSLxuutSl8BQZdpM+bRtnYP054awkN8of60bDf8i
WzVzrfH0K3eGJ9Iirp7CwOgFykOdpQyxsI+HG8grcwA87x1ZsAIfHhiKmQByliNl
BkbJmYBOtfVgXje5QdxTptlTNljFSbZcaCXv1P3aOqctcgJMQjg0T+E37Y8Cav80
6SuXbpv/cdacG695MAT7Vtywue0Axh59DvxAzc+deyQT70Hzw+Mo6pgi0clFnwzU
Y5ViDWECgYEAxxhRKzpz7klnmGob5CZvrbqDfC3JUEOxKH0e342S/HmT05bTI21w
N8A0KStNjQXS1mmkAkY/OO1Zutmf6yjqsxAIEO5UMTCSEP7YLRB7qBdN7dOt3JaK
4wxErMCljdT68Vj5Qj8YzIXJkWPk871oFTvVNe2qxgrCUigE5ai2I8cCgYEA/Akv
E0L+2uXayEucEamzO3n9xVziNanjyHilnJJvduvO9gd+crBbxSKqaXSdfPnp2mSa
+e3N7elxP2b/kPrGkzZekSaMh1nPH4Upu+ISK117r1x+vmnxZHRpehrVh1QzOQ5p
Ljt+GaXa3ur3P/6uW5KMbtGGW6MEgDwLMLvpqjsCgYA5pnfyfYWOTWEbCDa1VM/n
zWc/cP6nKELHR5vF/fe+9fFxRm4zBwCElDpGZYyaNkJ75bEhG3g5Irll2phs/rcf
TJgZVvm4GKljFHhCbFByNvVQ1Ye1pT3oSugj4dDOhgp4Elxy61Rh/KeGWxez4Heg
FmhBqmVV3U2xfncUjUrYhwKBgQCKtPM3gpOIHSA/Q31tKxv9C7JiQDAuoIU/+0YJ
2X2G0VhhhtZMgErBP8bRquBRu6i8DMpN6lZ/LQ6qeiEExT8sHawF7lVA2GhpTHwf
btfZDeXYKOuIF/5F7ttt2/7QL8LRD+FLFGrd6q1+KYpRqfSDaS/ofV+YZys+98yg
0YpTqQKBgQCWJpV2ySgXcKJzAUh14VNpLTRzJOMSpsU576nwz0TLUREVjiH6rvKr
gxllDEe1bVNMEekaZ+dOqiJX+4aTnEbIrEXki5Dvz0vq8biImW9RRdPEHgYVTv/d
qBOPHiIq2JiY6abD9XNPM3VQ/z8em6/4mkC8COCJRd2mA89FOYRxOQ==
-----END RSA PRIVATE KEY-----

@ -0,0 +1,55 @@
<?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>
<ProjectGuid>{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>tls_client_example</RootNamespace>
<AssemblyName>tls_client_example</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</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>full</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="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="client1.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="client1-key.pem">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="root.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

@ -33,6 +33,11 @@ else()
set(BUILD_SV_GOOSE_EXAMPLES ON)
endif()
if(WITH_MBEDTLS)
add_subdirectory(tls_client_example)
add_subdirectory(tls_server_example)
endif(WITH_MBEDTLS)
if(${BUILD_SV_GOOSE_EXAMPLES})
add_subdirectory(server_example_goose)
add_subdirectory(goose_subscriber)

@ -16,6 +16,14 @@
int
main(int argc, char** argv)
{
char* interface;
if (argc > 1)
interface = argv[1];
else
interface = "eth0";
printf("Using interface %s\n", interface);
LinkedList dataSetValues = LinkedList_create();
@ -41,7 +49,7 @@ main(int argc, char** argv)
* is NULL the interface name as defined with CONFIG_ETHERNET_INTERFACE_ID in
* stack_config.h is used.
*/
GoosePublisher publisher = GoosePublisher_create(&gooseCommParameters, NULL);
GoosePublisher publisher = GoosePublisher_create(&gooseCommParameters, interface);
GoosePublisher_setGoCbRef(publisher, "simpleIOGenericIO/LLN0$GO$gcbAnalogValues");
GoosePublisher_setConfRev(publisher, 1);

@ -7,6 +7,7 @@
*/
#include "goose_receiver.h"
#include "goose_subscriber.h"
#include "hal_thread.h"
#include <stdlib.h>

@ -53,27 +53,27 @@ static int vol2;
static int vol3;
static int vol4;
static SampledValuesPublisher svPublisher;
static SV_ASDU asdu;
static SVPublisher svPublisher;
static SVPublisher_ASDU asdu;
static void
setupSVPublisher(const char* svInterface)
{
svPublisher = SampledValuesPublisher_create(NULL, svInterface);
svPublisher = SVPublisher_create(NULL, svInterface);
asdu = SampledValuesPublisher_addASDU(svPublisher, "xxxxMUnn01", NULL, 1);
asdu = SVPublisher_addASDU(svPublisher, "xxxxMUnn01", NULL, 1);
amp1 = SV_ASDU_addINT32(asdu);
amp2 = SV_ASDU_addINT32(asdu);
amp3 = SV_ASDU_addINT32(asdu);
amp4 = SV_ASDU_addINT32(asdu);
amp1 = SVPublisher_ASDU_addINT32(asdu);
amp2 = SVPublisher_ASDU_addINT32(asdu);
amp3 = SVPublisher_ASDU_addINT32(asdu);
amp4 = SVPublisher_ASDU_addINT32(asdu);
vol1 = SV_ASDU_addINT32(asdu);
vol2 = SV_ASDU_addINT32(asdu);
vol3 = SV_ASDU_addINT32(asdu);
vol4 = SV_ASDU_addINT32(asdu);
vol1 = SVPublisher_ASDU_addINT32(asdu);
vol2 = SVPublisher_ASDU_addINT32(asdu);
vol3 = SVPublisher_ASDU_addINT32(asdu);
vol4 = SVPublisher_ASDU_addINT32(asdu);
SampledValuesPublisher_setupComplete(svPublisher);
SVPublisher_setupComplete(svPublisher);
}
static void sVCBEventHandler (SVControlBlock* svcb, int event, void* parameter)
@ -143,19 +143,19 @@ main(int argc, char** argv)
if (svcbEnabled) {
SV_ASDU_setINT32(asdu, amp1, current);
SV_ASDU_setINT32(asdu, amp2, current);
SV_ASDU_setINT32(asdu, amp3, current);
SV_ASDU_setINT32(asdu, amp4, current);
SVPublisher_ASDU_setINT32(asdu, amp1, current);
SVPublisher_ASDU_setINT32(asdu, amp2, current);
SVPublisher_ASDU_setINT32(asdu, amp3, current);
SVPublisher_ASDU_setINT32(asdu, amp4, current);
SV_ASDU_setINT32(asdu, vol1, voltage);
SV_ASDU_setINT32(asdu, vol2, voltage);
SV_ASDU_setINT32(asdu, vol3, voltage);
SV_ASDU_setINT32(asdu, vol4, voltage);
SVPublisher_ASDU_setINT32(asdu, vol1, voltage);
SVPublisher_ASDU_setINT32(asdu, vol2, voltage);
SVPublisher_ASDU_setINT32(asdu, vol3, voltage);
SVPublisher_ASDU_setINT32(asdu, vol4, voltage);
SV_ASDU_increaseSmpCnt(asdu);
SVPublisher_ASDU_increaseSmpCnt(asdu);
SampledValuesPublisher_publish(svPublisher);
SVPublisher_publish(svPublisher);
}
voltage++;
@ -168,7 +168,7 @@ main(int argc, char** argv)
IedServer_stop(iedServer);
/* Cleanup - free all resources */
SampledValuesPublisher_destroy(svPublisher);
SVPublisher_destroy(svPublisher);
IedServer_destroy(iedServer);
return 0;

@ -1,7 +1,7 @@
/*
* client_example1.c
*
* This example is intended to be used with server_example3 or server_example_goose.
* This example is intended to be used with server_example_basic_io or server_example_goose.
*/
#include "iec61850_client.h"
@ -50,8 +50,6 @@ int main(int argc, char** argv) {
if (error == IED_ERROR_OK) {
IedConnection_getServerDirectory(con, &error, false);
/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);

@ -22,6 +22,7 @@ model: $(PROJECT_ICD_FILE)
$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS)
mkdir -p vmd-filestore
$(CP) $(PROJECT_BINARY_NAME) vmd-filestore/IEDSERVER.BIN
clean:

@ -37,7 +37,8 @@ void sigint_handler(int signalId)
static char* password1 = "user1@testpw";
static char* password2 = "user2@testpw";
static void printAppTitle(ItuObjectIdentifier* oid)
static void
printAppTitle(ItuObjectIdentifier* oid)
{
int i;

@ -29,39 +29,48 @@ main(int argc, char** argv)
printf("Using interface %s\n", interface);
SampledValuesPublisher svPublisher = SampledValuesPublisher_create(NULL, interface);
SVPublisher svPublisher = SVPublisher_create(NULL, interface);
SV_ASDU asdu1 = SampledValuesPublisher_addASDU(svPublisher, "svpub1", NULL, 1);
SVPublisher_ASDU asdu1 = SVPublisher_addASDU(svPublisher, "svpub1", NULL, 1);
int float1 = SV_ASDU_addFLOAT(asdu1);
int float2 = SV_ASDU_addFLOAT(asdu1);
int float1 = SVPublisher_ASDU_addFLOAT(asdu1);
int float2 = SVPublisher_ASDU_addFLOAT(asdu1);
int ts1 = SVPublisher_ASDU_addTimestamp(asdu1);
SV_ASDU asdu2 = SampledValuesPublisher_addASDU(svPublisher, "svpub2", NULL, 1);
SVPublisher_ASDU asdu2 = SVPublisher_addASDU(svPublisher, "svpub2", NULL, 1);
int float3 = SV_ASDU_addFLOAT(asdu2);
int float4 = SV_ASDU_addFLOAT(asdu2);
int float3 = SVPublisher_ASDU_addFLOAT(asdu2);
int float4 = SVPublisher_ASDU_addFLOAT(asdu2);
int ts2 = SVPublisher_ASDU_addTimestamp(asdu2);
SampledValuesPublisher_setupComplete(svPublisher);
SVPublisher_setupComplete(svPublisher);
float fVal1 = 1234.5678f;
float fVal2 = 0.12345f;
int i;
while (running) {
SV_ASDU_setFLOAT(asdu1, float1, fVal1);
SV_ASDU_setFLOAT(asdu1, float2, fVal2);
Timestamp ts;
Timestamp_clearFlags(&ts);
Timestamp_setTimeInMilliseconds(&ts, Hal_getTimeInMs());
SVPublisher_ASDU_setFLOAT(asdu1, float1, fVal1);
SVPublisher_ASDU_setFLOAT(asdu1, float2, fVal2);
SVPublisher_ASDU_setTimestamp(asdu1, ts1, ts);
SVPublisher_ASDU_setFLOAT(asdu2, float3, fVal1 * 2);
SVPublisher_ASDU_setFLOAT(asdu2, float4, fVal2 * 2);
SVPublisher_ASDU_setTimestamp(asdu2, ts2, ts);
SV_ASDU_increaseSmpCnt(asdu1);
SV_ASDU_increaseSmpCnt(asdu2);
SVPublisher_ASDU_increaseSmpCnt(asdu1);
SVPublisher_ASDU_increaseSmpCnt(asdu2);
fVal1 += 1.1f;
fVal2 += 0.1f;
SampledValuesPublisher_publish(svPublisher);
SVPublisher_publish(svPublisher);
Thread_sleep(50);
}
SampledValuesPublisher_destroy(svPublisher);
SVPublisher_destroy(svPublisher);
}

@ -21,17 +21,17 @@ void sigint_handler(int signalId)
/* Callback handler for received SV messages */
static void
svUpdateListener (SVSubscriber subscriber, void* parameter, SVClientASDU asdu)
svUpdateListener (SVSubscriber subscriber, void* parameter, SVSubscriber_ASDU asdu)
{
printf("svUpdateListener called\n");
const char* svID = SVClientASDU_getSvId(asdu);
const char* svID = SVSubscriber_ASDU_getSvId(asdu);
if (svID != NULL)
printf(" svID=(%s)\n", svID);
printf(" smpCnt: %i\n", SVClientASDU_getSmpCnt(asdu));
printf(" confRev: %u\n", SVClientASDU_getConfRev(asdu));
printf(" smpCnt: %i\n", SVSubscriber_ASDU_getSmpCnt(asdu));
printf(" confRev: %u\n", SVSubscriber_ASDU_getConfRev(asdu));
/*
* Access to the data requires a priori knowledge of the data set.
@ -43,9 +43,9 @@ svUpdateListener (SVSubscriber subscriber, void* parameter, SVClientASDU asdu)
* To prevent damages due configuration, please check the length of the
* data block of the SV message before accessing the data.
*/
if (SVClientASDU_getDataSize(asdu) >= 8) {
printf(" DATA[0]: %f\n", SVClientASDU_getFLOAT32(asdu, 0));
printf(" DATA[1]: %f\n", SVClientASDU_getFLOAT32(asdu, 4));
if (SVSubscriber_ASDU_getDataSize(asdu) >= 8) {
printf(" DATA[0]: %f\n", SVSubscriber_ASDU_getFLOAT32(asdu, 0));
printf(" DATA[1]: %f\n", SVSubscriber_ASDU_getFLOAT32(asdu, 4));
}
}

@ -0,0 +1,17 @@
set(example_SRCS
tls_client_example.c
)
IF(WIN32)
set_source_files_properties(${example_SRCS}
PROPERTIES LANGUAGE CXX)
ENDIF(WIN32)
add_executable(tls_client_example
${example_SRCS}
)
target_link_libraries(tls_client_example
iec61850
)

@ -0,0 +1,17 @@
LIBIEC_HOME=../..
PROJECT_BINARY_NAME = tls_client_example
PROJECT_SOURCES = tls_client_example.c
include $(LIBIEC_HOME)/make/target_system.mk
include $(LIBIEC_HOME)/make/stack_includes.mk
all: $(PROJECT_BINARY_NAME)
include $(LIBIEC_HOME)/make/common_targets.mk
$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS)
clean:
rm -f $(PROJECT_BINARY_NAME)

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxAMUXdnem99n5J8Z8Wa0fdPtoMCTdkQJrOO6WJ4XePrpQgHU
HDziSmdIZDDkpJ3Ey0Byy+b+iiRDmOuIZGSCsI0ehggWaia12h2osUK0BLyThuZ/
RQw54K0dy61eviNaYsftiBcxHYKyKmWch/wLLYxdd1qLzd0reAbSUaHDkDzrj9hO
8qr7DhKpqx7PoVh1gFhAKPKuY6b+4xqv5eZvt8QflQTWYGxaxmUIEinqCnzh1l5d
tp0rhnBsz9Y4y8dXjh0m7pbXmRNY6opMxXatqgYEqsntLy1N6x7DvWLBqtVvEmox
Tc5bbAoRW3eEToClDdFQBzLsMVcSEX8vwttk3QIDAQABAoIBABHr1ijeiqPlwTH9
+flAUrBOeCOCd/kQL3JHP/pqOestxbXrROFwD6CN4OiIL999LUkIE3bhH9SxjByn
LElBh1FtFaVbh/EcqPPQUmQinSLxuutSl8BQZdpM+bRtnYP054awkN8of60bDf8i
WzVzrfH0K3eGJ9Iirp7CwOgFykOdpQyxsI+HG8grcwA87x1ZsAIfHhiKmQByliNl
BkbJmYBOtfVgXje5QdxTptlTNljFSbZcaCXv1P3aOqctcgJMQjg0T+E37Y8Cav80
6SuXbpv/cdacG695MAT7Vtywue0Axh59DvxAzc+deyQT70Hzw+Mo6pgi0clFnwzU
Y5ViDWECgYEAxxhRKzpz7klnmGob5CZvrbqDfC3JUEOxKH0e342S/HmT05bTI21w
N8A0KStNjQXS1mmkAkY/OO1Zutmf6yjqsxAIEO5UMTCSEP7YLRB7qBdN7dOt3JaK
4wxErMCljdT68Vj5Qj8YzIXJkWPk871oFTvVNe2qxgrCUigE5ai2I8cCgYEA/Akv
E0L+2uXayEucEamzO3n9xVziNanjyHilnJJvduvO9gd+crBbxSKqaXSdfPnp2mSa
+e3N7elxP2b/kPrGkzZekSaMh1nPH4Upu+ISK117r1x+vmnxZHRpehrVh1QzOQ5p
Ljt+GaXa3ur3P/6uW5KMbtGGW6MEgDwLMLvpqjsCgYA5pnfyfYWOTWEbCDa1VM/n
zWc/cP6nKELHR5vF/fe+9fFxRm4zBwCElDpGZYyaNkJ75bEhG3g5Irll2phs/rcf
TJgZVvm4GKljFHhCbFByNvVQ1Ye1pT3oSugj4dDOhgp4Elxy61Rh/KeGWxez4Heg
FmhBqmVV3U2xfncUjUrYhwKBgQCKtPM3gpOIHSA/Q31tKxv9C7JiQDAuoIU/+0YJ
2X2G0VhhhtZMgErBP8bRquBRu6i8DMpN6lZ/LQ6qeiEExT8sHawF7lVA2GhpTHwf
btfZDeXYKOuIF/5F7ttt2/7QL8LRD+FLFGrd6q1+KYpRqfSDaS/ofV+YZys+98yg
0YpTqQKBgQCWJpV2ySgXcKJzAUh14VNpLTRzJOMSpsU576nwz0TLUREVjiH6rvKr
gxllDEe1bVNMEekaZ+dOqiJX+4aTnEbIrEXki5Dvz0vq8biImW9RRdPEHgYVTv/d
qBOPHiIq2JiY6abD9XNPM3VQ/z8em6/4mkC8COCJRd2mA89FOYRxOQ==
-----END RSA PRIVATE KEY-----

@ -0,0 +1,151 @@
/*
* tls_client_exmaple.c
*
* This example shows how to configure TLS
*/
#include "iec61850_client.h"
#include <stdlib.h>
#include <stdio.h>
#include "hal_thread.h"
void
reportCallbackFunction(void* parameter, ClientReport report)
{
MmsValue* dataSetValues = ClientReport_getDataSetValues(report);
printf("received report for %s\n", ClientReport_getRcbReference(report));
int i;
for (i = 0; i < 4; i++) {
ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i);
if (reason != IEC61850_REASON_NOT_INCLUDED) {
printf(" GGIO1.SPCSO%i.stVal: %i (included for reason %i)\n", i,
MmsValue_getBoolean(MmsValue_getElement(dataSetValues, i)), reason);
}
}
}
int main(int argc, char** argv) {
char* hostname;
if (argc > 1)
hostname = argv[1];
else
hostname = "localhost";
TLSConfiguration tlsConfig = TLSConfiguration_create();
TLSConfiguration_setChainValidation(tlsConfig, true);
TLSConfiguration_setAllowOnlyKnownCertificates(tlsConfig, false);
if (!TLSConfiguration_setOwnKeyFromFile(tlsConfig, "client1-key.pem", NULL)) {
printf("ERROR: Failed to load private key!\n");
return 0;
}
if (!TLSConfiguration_setOwnCertificateFromFile(tlsConfig, "client1.cer")) {
printf("ERROR: Failed to load own certificate!\n");
return 0;
}
if (!TLSConfiguration_addCACertificateFromFile(tlsConfig, "root.cer")) {
printf("ERROR: Failed to load root certificate\n");
return 0;
}
IedClientError error;
IedConnection con = IedConnection_createWithTlsSupport(tlsConfig);
IedConnection_connect(con, &error, hostname, -1);
if (error == IED_ERROR_OK) {
IedConnection_getServerDirectory(con, &error, false);
/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);
if (value != NULL) {
float fval = MmsValue_toFloat(value);
printf("read float value: %f\n", fval);
MmsValue_delete(value);
}
/* write a variable to the server */
value = MmsValue_newVisibleString("libiec61850.com");
IedConnection_writeObject(con, &error, "simpleIOGenericIO/GGIO1.NamPlt.vendor", IEC61850_FC_DC, value);
if (error != IED_ERROR_OK)
printf("failed to write simpleIOGenericIO/GGIO1.NamPlt.vendor!\n");
MmsValue_delete(value);
/* read data set */
ClientDataSet clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.Events", NULL);
if (clientDataSet == NULL)
printf("failed to read dataset\n");
/* Read RCB values */
ClientReportControlBlock rcb =
IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL);
bool rptEna = ClientReportControlBlock_getRptEna(rcb);
printf("RptEna = %i\n", rptEna);
/* Install handler for reports */
IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB01",
ClientReportControlBlock_getRptId(rcb), reportCallbackFunction, NULL);
/* Set trigger options and enable report */
ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY | TRG_OPT_GI);
ClientReportControlBlock_setRptEna(rcb, true);
ClientReportControlBlock_setIntgPd(rcb, 5000);
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_INTG_PD, true);
if (error != IED_ERROR_OK)
printf("report activation failed (code: %i)\n", error);
Thread_sleep(1000);
/* trigger GI report */
ClientReportControlBlock_setGI(rcb, true);
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true);
if (error != IED_ERROR_OK)
printf("Error triggering a GI report (code: %i)\n", error);
Thread_sleep(60000);
/* disable reporting */
ClientReportControlBlock_setRptEna(rcb, false);
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA, true);
if (error != IED_ERROR_OK)
printf("disable reporting failed (code: %i)\n", error);
ClientDataSet_destroy(clientDataSet);
ClientReportControlBlock_destroy(rcb);
close_connection:
IedConnection_close(con);
}
else {
printf("Failed to connect to %s\n", hostname);
}
IedConnection_destroy(con);
}

@ -0,0 +1,21 @@
include_directories(
.
)
set(example_SRCS
tls_server_example.c
static_model.c
)
IF(WIN32)
set_source_files_properties(${example_SRCS}
PROPERTIES LANGUAGE CXX)
ENDIF(WIN32)
add_executable(tls_server_example
${example_SRCS}
)
target_link_libraries(tls_server_example
iec61850
)

@ -0,0 +1,27 @@
LIBIEC_HOME=../..
PROJECT_BINARY_NAME = tls_server_example
PROJECT_SOURCES = tls_server_example.c
PROJECT_SOURCES += static_model.c
PROJECT_ICD_FILE = simpleIO_direct_control.icd
include $(LIBIEC_HOME)/make/target_system.mk
include $(LIBIEC_HOME)/make/stack_includes.mk
all: $(PROJECT_BINARY_NAME)
include $(LIBIEC_HOME)/make/common_targets.mk
LDLIBS += -lm
model: $(PROJECT_ICD_FILE)
java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE)
$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS)
clean:
rm -f $(PROJECT_BINARY_NAME)

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAu3Fjxb904UdyV/dDfvs8SFi6zJeNoMYjmpXm/LBcFSH3zGoK
wdcMovrUTED3Cc6Ww84AYpJ5MRMPTct7DfKJkWfSnkzOPmLldTSTv3RvzGVb4NzK
QqA5aSVDqAzPiP5RnFT6Q4KWRe69TMFxpw7zMXCJx9jDggqN1oojGGkmSgYGXnFd
Nc20Mujejh5pihgwnN4Y/bPFyxJwvIMj+D8qr9klhEmXKPTiX9UFd8oLkn9JCB6+
SiHhNyFIo+Llossn1Q2hxCGty36fAhEWzpfBTaY510VLjie9y4q9GPTwxITDqSQd
xcX8IuvrbxX0DyEK507SMmTJmB9448eF9ZCWFQIDAQABAoIBAC80BuQtqslwrKLq
adz4d93gOmp7X/c07pJnXZwU7ZuEylp3+e2Gsm/4qq3pTkzx8ZWtsvsf19U774av
z3VbtrkfZDLpNKcRUKeLbgmw0NawT8r4zxaoMsz/zWHsl/bv1K2B2ORXZnCGBrXl
oTFo2mWA6bGiLNn6vm1grCXhlPreywyG/kFK3pi2VvkpvG3XZSI7mmZ0Dq/MD3nO
03oOZBqwwnMObfQQdhKE7646/+KgeuF/JsXaUH4bkHmtzYWyocWYMqpC0hjpNWlQ
cKuQ7t1kfmpsGD9aNW4+ND2ok9BdxIiC+rPXS9NDqZxoWLp+a8seU++uqk1l8RPq
tPE3LqECgYEAz1NmemNLiUsKvyemUvjp8+dJupzWtdV7fsnCbYhj/5gDA2UhFKCf
dP9xiHCdNe0797oAqHY7c3JhS4ug8haDy9aDIu5GG2DNYzjX/oYm4ywbCdRx+uEN
RcTw69FjSYVGkObmxWYszwsFybRasV6PYamg65qYR3FlvW2Td4Fndy8CgYEA53L/
zHtBRQiNGJU9jfMHeX0bTtXIAt622Qn78jw0it/rhXWi2RwG2Cw5Q2aPRJ6uMt9F
yk1+GAPZcwYqwjq/nKRrl71Tn+KDWIk5rz1fNYRkaXtnMLs2MOogqoDTBshW0QBq
tnPrFNsaLKX6V92Az69wHjd2uwvLQLTvS/EuNfsCgYEAr3to/uhytAd3VirKRep3
o0E+D5zWw1upxrwhPDK4aUuSKVp8sIfvz8iyoQiomE9vdZPTIMPKOEI1BgtuM9pI
vcyYfIVvg5bg4T3o3H9SBPB9BknyG6ZHZKl4PjGht0X+X4GBDM4Z2Tj8Mijcpsph
1AkOsrzMbZQWyEoqCnnWSHMCgYAFEHUcak4BTrCXqxxPsNOnCt/AF9lqhqkFkrxa
joqvxzqGDw7jJUPZEw6ltObJn5c8Mbp7NLrfl6X4aFgjK9npeYeJKHFd/DzXgRks
BnHA4Aa6cCLP5CjJZTYVxP/ZFCUiKZosJ9kq+ahW9cLGjWg2IyaW4qvMZ/OolMzv
onVaZQKBgQCir8u1vDsyA4JQXMytPHBJe27XaLRGULvteNydVB59Vt21a99o5gt1
5B9gwWArZdZby3/KZiliNmzp8lMCrLJYjTL5WK6dbWdq92X5hCOofKPIjEcgHjhk
mvnAos3HeC83bJQtADXhw9jR7Vr6GJLM9HDcIgeIMzX7+BuqlMgaHA==
-----END RSA PRIVATE KEY-----

File diff suppressed because it is too large Load Diff

@ -0,0 +1,301 @@
/*
* static_model.h
*
* automatically generated from simpleIO_direct_control.icd
*/
#ifndef STATIC_MODEL_H_
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_GenericIO;
extern LogicalNode iedModel_GenericIO_LLN0;
extern DataObject iedModel_GenericIO_LLN0_Mod;
extern DataAttribute iedModel_GenericIO_LLN0_Mod_stVal;
extern DataAttribute iedModel_GenericIO_LLN0_Mod_q;
extern DataAttribute iedModel_GenericIO_LLN0_Mod_t;
extern DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel;
extern DataObject iedModel_GenericIO_LLN0_Beh;
extern DataAttribute iedModel_GenericIO_LLN0_Beh_stVal;
extern DataAttribute iedModel_GenericIO_LLN0_Beh_q;
extern DataAttribute iedModel_GenericIO_LLN0_Beh_t;
extern DataObject iedModel_GenericIO_LLN0_Health;
extern DataAttribute iedModel_GenericIO_LLN0_Health_stVal;
extern DataAttribute iedModel_GenericIO_LLN0_Health_q;
extern DataAttribute iedModel_GenericIO_LLN0_Health_t;
extern DataObject iedModel_GenericIO_LLN0_NamPlt;
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor;
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev;
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_d;
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev;
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs;
extern LogicalNode iedModel_GenericIO_LPHD1;
extern DataObject iedModel_GenericIO_LPHD1_PhyNam;
extern DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor;
extern DataObject iedModel_GenericIO_LPHD1_PhyHealth;
extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal;
extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q;
extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t;
extern DataObject iedModel_GenericIO_LPHD1_Proxy;
extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal;
extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_q;
extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_t;
extern LogicalNode iedModel_GenericIO_GGIO1;
extern DataObject iedModel_GenericIO_GGIO1_Mod;
extern DataAttribute iedModel_GenericIO_GGIO1_Mod_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Mod_t;
extern DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel;
extern DataObject iedModel_GenericIO_GGIO1_Beh;
extern DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Beh_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Beh_t;
extern DataObject iedModel_GenericIO_GGIO1_Health;
extern DataAttribute iedModel_GenericIO_GGIO1_Health_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Health_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Health_t;
extern DataObject iedModel_GenericIO_GGIO1_NamPlt;
extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor;
extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev;
extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d;
extern DataObject iedModel_GenericIO_GGIO1_AnIn1;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t;
extern DataObject iedModel_GenericIO_GGIO1_AnIn2;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t;
extern DataObject iedModel_GenericIO_GGIO1_AnIn3;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t;
extern DataObject iedModel_GenericIO_GGIO1_AnIn4;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q;
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t;
extern DataObject iedModel_GenericIO_GGIO1_SPCSO1;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t;
extern DataObject iedModel_GenericIO_GGIO1_SPCSO2;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t;
extern DataObject iedModel_GenericIO_GGIO1_SPCSO3;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t;
extern DataObject iedModel_GenericIO_GGIO1_SPCSO4;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel;
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t;
extern DataObject iedModel_GenericIO_GGIO1_Ind1;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_t;
extern DataObject iedModel_GenericIO_GGIO1_Ind2;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_t;
extern DataObject iedModel_GenericIO_GGIO1_Ind3;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_t;
extern DataObject iedModel_GenericIO_GGIO1_Ind4;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_q;
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t;
#define IEDMODEL_GenericIO (&iedModel_GenericIO)
#define IEDMODEL_GenericIO_LLN0 (&iedModel_GenericIO_LLN0)
#define IEDMODEL_GenericIO_LLN0_Mod (&iedModel_GenericIO_LLN0_Mod)
#define IEDMODEL_GenericIO_LLN0_Mod_stVal (&iedModel_GenericIO_LLN0_Mod_stVal)
#define IEDMODEL_GenericIO_LLN0_Mod_q (&iedModel_GenericIO_LLN0_Mod_q)
#define IEDMODEL_GenericIO_LLN0_Mod_t (&iedModel_GenericIO_LLN0_Mod_t)
#define IEDMODEL_GenericIO_LLN0_Mod_ctlModel (&iedModel_GenericIO_LLN0_Mod_ctlModel)
#define IEDMODEL_GenericIO_LLN0_Beh (&iedModel_GenericIO_LLN0_Beh)
#define IEDMODEL_GenericIO_LLN0_Beh_stVal (&iedModel_GenericIO_LLN0_Beh_stVal)
#define IEDMODEL_GenericIO_LLN0_Beh_q (&iedModel_GenericIO_LLN0_Beh_q)
#define IEDMODEL_GenericIO_LLN0_Beh_t (&iedModel_GenericIO_LLN0_Beh_t)
#define IEDMODEL_GenericIO_LLN0_Health (&iedModel_GenericIO_LLN0_Health)
#define IEDMODEL_GenericIO_LLN0_Health_stVal (&iedModel_GenericIO_LLN0_Health_stVal)
#define IEDMODEL_GenericIO_LLN0_Health_q (&iedModel_GenericIO_LLN0_Health_q)
#define IEDMODEL_GenericIO_LLN0_Health_t (&iedModel_GenericIO_LLN0_Health_t)
#define IEDMODEL_GenericIO_LLN0_NamPlt (&iedModel_GenericIO_LLN0_NamPlt)
#define IEDMODEL_GenericIO_LLN0_NamPlt_vendor (&iedModel_GenericIO_LLN0_NamPlt_vendor)
#define IEDMODEL_GenericIO_LLN0_NamPlt_swRev (&iedModel_GenericIO_LLN0_NamPlt_swRev)
#define IEDMODEL_GenericIO_LLN0_NamPlt_d (&iedModel_GenericIO_LLN0_NamPlt_d)
#define IEDMODEL_GenericIO_LLN0_NamPlt_configRev (&iedModel_GenericIO_LLN0_NamPlt_configRev)
#define IEDMODEL_GenericIO_LLN0_NamPlt_ldNs (&iedModel_GenericIO_LLN0_NamPlt_ldNs)
#define IEDMODEL_GenericIO_LPHD1 (&iedModel_GenericIO_LPHD1)
#define IEDMODEL_GenericIO_LPHD1_PhyNam (&iedModel_GenericIO_LPHD1_PhyNam)
#define IEDMODEL_GenericIO_LPHD1_PhyNam_vendor (&iedModel_GenericIO_LPHD1_PhyNam_vendor)
#define IEDMODEL_GenericIO_LPHD1_PhyHealth (&iedModel_GenericIO_LPHD1_PhyHealth)
#define IEDMODEL_GenericIO_LPHD1_PhyHealth_stVal (&iedModel_GenericIO_LPHD1_PhyHealth_stVal)
#define IEDMODEL_GenericIO_LPHD1_PhyHealth_q (&iedModel_GenericIO_LPHD1_PhyHealth_q)
#define IEDMODEL_GenericIO_LPHD1_PhyHealth_t (&iedModel_GenericIO_LPHD1_PhyHealth_t)
#define IEDMODEL_GenericIO_LPHD1_Proxy (&iedModel_GenericIO_LPHD1_Proxy)
#define IEDMODEL_GenericIO_LPHD1_Proxy_stVal (&iedModel_GenericIO_LPHD1_Proxy_stVal)
#define IEDMODEL_GenericIO_LPHD1_Proxy_q (&iedModel_GenericIO_LPHD1_Proxy_q)
#define IEDMODEL_GenericIO_LPHD1_Proxy_t (&iedModel_GenericIO_LPHD1_Proxy_t)
#define IEDMODEL_GenericIO_GGIO1 (&iedModel_GenericIO_GGIO1)
#define IEDMODEL_GenericIO_GGIO1_Mod (&iedModel_GenericIO_GGIO1_Mod)
#define IEDMODEL_GenericIO_GGIO1_Mod_q (&iedModel_GenericIO_GGIO1_Mod_q)
#define IEDMODEL_GenericIO_GGIO1_Mod_t (&iedModel_GenericIO_GGIO1_Mod_t)
#define IEDMODEL_GenericIO_GGIO1_Mod_ctlModel (&iedModel_GenericIO_GGIO1_Mod_ctlModel)
#define IEDMODEL_GenericIO_GGIO1_Beh (&iedModel_GenericIO_GGIO1_Beh)
#define IEDMODEL_GenericIO_GGIO1_Beh_stVal (&iedModel_GenericIO_GGIO1_Beh_stVal)
#define IEDMODEL_GenericIO_GGIO1_Beh_q (&iedModel_GenericIO_GGIO1_Beh_q)
#define IEDMODEL_GenericIO_GGIO1_Beh_t (&iedModel_GenericIO_GGIO1_Beh_t)
#define IEDMODEL_GenericIO_GGIO1_Health (&iedModel_GenericIO_GGIO1_Health)
#define IEDMODEL_GenericIO_GGIO1_Health_stVal (&iedModel_GenericIO_GGIO1_Health_stVal)
#define IEDMODEL_GenericIO_GGIO1_Health_q (&iedModel_GenericIO_GGIO1_Health_q)
#define IEDMODEL_GenericIO_GGIO1_Health_t (&iedModel_GenericIO_GGIO1_Health_t)
#define IEDMODEL_GenericIO_GGIO1_NamPlt (&iedModel_GenericIO_GGIO1_NamPlt)
#define IEDMODEL_GenericIO_GGIO1_NamPlt_vendor (&iedModel_GenericIO_GGIO1_NamPlt_vendor)
#define IEDMODEL_GenericIO_GGIO1_NamPlt_swRev (&iedModel_GenericIO_GGIO1_NamPlt_swRev)
#define IEDMODEL_GenericIO_GGIO1_NamPlt_d (&iedModel_GenericIO_GGIO1_NamPlt_d)
#define IEDMODEL_GenericIO_GGIO1_AnIn1 (&iedModel_GenericIO_GGIO1_AnIn1)
#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag (&iedModel_GenericIO_GGIO1_AnIn1_mag)
#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f (&iedModel_GenericIO_GGIO1_AnIn1_mag_f)
#define IEDMODEL_GenericIO_GGIO1_AnIn1_q (&iedModel_GenericIO_GGIO1_AnIn1_q)
#define IEDMODEL_GenericIO_GGIO1_AnIn1_t (&iedModel_GenericIO_GGIO1_AnIn1_t)
#define IEDMODEL_GenericIO_GGIO1_AnIn2 (&iedModel_GenericIO_GGIO1_AnIn2)
#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag (&iedModel_GenericIO_GGIO1_AnIn2_mag)
#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f (&iedModel_GenericIO_GGIO1_AnIn2_mag_f)
#define IEDMODEL_GenericIO_GGIO1_AnIn2_q (&iedModel_GenericIO_GGIO1_AnIn2_q)
#define IEDMODEL_GenericIO_GGIO1_AnIn2_t (&iedModel_GenericIO_GGIO1_AnIn2_t)
#define IEDMODEL_GenericIO_GGIO1_AnIn3 (&iedModel_GenericIO_GGIO1_AnIn3)
#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag (&iedModel_GenericIO_GGIO1_AnIn3_mag)
#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f (&iedModel_GenericIO_GGIO1_AnIn3_mag_f)
#define IEDMODEL_GenericIO_GGIO1_AnIn3_q (&iedModel_GenericIO_GGIO1_AnIn3_q)
#define IEDMODEL_GenericIO_GGIO1_AnIn3_t (&iedModel_GenericIO_GGIO1_AnIn3_t)
#define IEDMODEL_GenericIO_GGIO1_AnIn4 (&iedModel_GenericIO_GGIO1_AnIn4)
#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag (&iedModel_GenericIO_GGIO1_AnIn4_mag)
#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f (&iedModel_GenericIO_GGIO1_AnIn4_mag_f)
#define IEDMODEL_GenericIO_GGIO1_AnIn4_q (&iedModel_GenericIO_GGIO1_AnIn4_q)
#define IEDMODEL_GenericIO_GGIO1_AnIn4_t (&iedModel_GenericIO_GGIO1_AnIn4_t)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1 (&iedModel_GenericIO_GGIO1_SPCSO1)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal (&iedModel_GenericIO_GGIO1_SPCSO1_stVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_q (&iedModel_GenericIO_GGIO1_SPCSO1_q)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper (&iedModel_GenericIO_GGIO1_SPCSO1_Oper)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_T)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO1_ctlModel)
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_t (&iedModel_GenericIO_GGIO1_SPCSO1_t)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2 (&iedModel_GenericIO_GGIO1_SPCSO2)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal (&iedModel_GenericIO_GGIO1_SPCSO2_stVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_q (&iedModel_GenericIO_GGIO1_SPCSO2_q)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper (&iedModel_GenericIO_GGIO1_SPCSO2_Oper)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_T)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO2_ctlModel)
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_t (&iedModel_GenericIO_GGIO1_SPCSO2_t)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3 (&iedModel_GenericIO_GGIO1_SPCSO3)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal (&iedModel_GenericIO_GGIO1_SPCSO3_stVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_q (&iedModel_GenericIO_GGIO1_SPCSO3_q)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper (&iedModel_GenericIO_GGIO1_SPCSO3_Oper)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_T)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO3_ctlModel)
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_t (&iedModel_GenericIO_GGIO1_SPCSO3_t)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4 (&iedModel_GenericIO_GGIO1_SPCSO4)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal (&iedModel_GenericIO_GGIO1_SPCSO4_stVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_q (&iedModel_GenericIO_GGIO1_SPCSO4_q)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper (&iedModel_GenericIO_GGIO1_SPCSO4_Oper)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_T)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO4_ctlModel)
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_t (&iedModel_GenericIO_GGIO1_SPCSO4_t)
#define IEDMODEL_GenericIO_GGIO1_Ind1 (&iedModel_GenericIO_GGIO1_Ind1)
#define IEDMODEL_GenericIO_GGIO1_Ind1_stVal (&iedModel_GenericIO_GGIO1_Ind1_stVal)
#define IEDMODEL_GenericIO_GGIO1_Ind1_q (&iedModel_GenericIO_GGIO1_Ind1_q)
#define IEDMODEL_GenericIO_GGIO1_Ind1_t (&iedModel_GenericIO_GGIO1_Ind1_t)
#define IEDMODEL_GenericIO_GGIO1_Ind2 (&iedModel_GenericIO_GGIO1_Ind2)
#define IEDMODEL_GenericIO_GGIO1_Ind2_stVal (&iedModel_GenericIO_GGIO1_Ind2_stVal)
#define IEDMODEL_GenericIO_GGIO1_Ind2_q (&iedModel_GenericIO_GGIO1_Ind2_q)
#define IEDMODEL_GenericIO_GGIO1_Ind2_t (&iedModel_GenericIO_GGIO1_Ind2_t)
#define IEDMODEL_GenericIO_GGIO1_Ind3 (&iedModel_GenericIO_GGIO1_Ind3)
#define IEDMODEL_GenericIO_GGIO1_Ind3_stVal (&iedModel_GenericIO_GGIO1_Ind3_stVal)
#define IEDMODEL_GenericIO_GGIO1_Ind3_q (&iedModel_GenericIO_GGIO1_Ind3_q)
#define IEDMODEL_GenericIO_GGIO1_Ind3_t (&iedModel_GenericIO_GGIO1_Ind3_t)
#define IEDMODEL_GenericIO_GGIO1_Ind4 (&iedModel_GenericIO_GGIO1_Ind4)
#define IEDMODEL_GenericIO_GGIO1_Ind4_stVal (&iedModel_GenericIO_GGIO1_Ind4_stVal)
#define IEDMODEL_GenericIO_GGIO1_Ind4_q (&iedModel_GenericIO_GGIO1_Ind4_q)
#define IEDMODEL_GenericIO_GGIO1_Ind4_t (&iedModel_GenericIO_GGIO1_Ind4_t)
#endif /* STATIC_MODEL_H_ */

@ -0,0 +1,228 @@
/*
* tls_server_example.c
*
* How to configure a TLS server
*/
#include "iec61850_server.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "static_model.h"
/* import IEC 61850 device model created from SCL-File */
extern IedModel iedModel;
static int running = 0;
static IedServer iedServer = NULL;
void
sigint_handler(int signalId)
{
running = 0;
}
static ControlHandlerResult
controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test)
{
if (test)
return CONTROL_RESULT_FAILED;
if (MmsValue_getType(value) == MMS_BOOLEAN) {
printf("received binary control command: ");
if (MmsValue_getBoolean(value))
printf("on\n");
else
printf("off\n");
}
else
return CONTROL_RESULT_FAILED;
uint64_t timeStamp = Hal_getTimeInMs();
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) {
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp);
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value);
}
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2) {
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_t, timeStamp);
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal, value);
}
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3) {
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_t, timeStamp);
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal, value);
}
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4) {
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_t, timeStamp);
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, value);
}
return CONTROL_RESULT_OK;
}
static void
connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter)
{
if (connected)
printf("Connection opened\n");
else
printf("Connection closed\n");
}
static void
printAppTitle(ItuObjectIdentifier* oid)
{
int i;
for (i = 0; i < oid->arcCount; i++) {
printf("%i", oid->arc[i]);
if (i != (oid->arcCount - 1))
printf(".");
}
}
static bool
clientAuthenticator(void* parameter, AcseAuthenticationParameter authParameter, void** securityToken, IsoApplicationReference* appRef)
{
printf("ACSE Authenticator:\n");
printf(" client ap-title: "); printAppTitle(&(appRef->apTitle)); printf("\n");
printf(" client ae-qualifier: %i\n", appRef->aeQualifier);
printf(" auth-mechanism: %i\n", authParameter->mechanism);
return true;
}
int
main(int argc, char** argv)
{
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
TLSConfiguration tlsConfig = TLSConfiguration_create();
TLSConfiguration_setChainValidation(tlsConfig, false);
TLSConfiguration_setAllowOnlyKnownCertificates(tlsConfig, true);
if (!TLSConfiguration_setOwnKeyFromFile(tlsConfig, "server-key.pem", NULL)) {
printf("Failed to load private key!\n");
return 0;
}
if (!TLSConfiguration_setOwnCertificateFromFile(tlsConfig, "server.cer")) {
printf("ERROR: Failed to load own certificate!\n");
return 0;
}
if (!TLSConfiguration_addCACertificateFromFile(tlsConfig, "root.cer")) {
printf("ERROR: Failed to load root certificate\n");
return 0;
}
/**
* Configure two allowed clients
*/
if (!TLSConfiguration_addAllowedCertificateFromFile(tlsConfig, "client1.cer")) {
printf("ERROR: Failed to load allowed client certificate\n");
return 0;
}
if (!TLSConfiguration_addAllowedCertificateFromFile(tlsConfig, "client2.cer")) {
printf("ERROR: Failed to load allowed client certificate\n");
return 0;
}
iedServer = IedServer_createWithTlsSupport(&iedModel, tlsConfig);
IedServer_setAuthenticator(iedServer, clientAuthenticator, NULL);
/* Install handler for operate command */
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1,
(ControlHandler) controlHandlerForBinaryOutput,
IEDMODEL_GenericIO_GGIO1_SPCSO1);
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2,
(ControlHandler) controlHandlerForBinaryOutput,
IEDMODEL_GenericIO_GGIO1_SPCSO2);
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3,
(ControlHandler) controlHandlerForBinaryOutput,
IEDMODEL_GenericIO_GGIO1_SPCSO3);
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4,
(ControlHandler) controlHandlerForBinaryOutput,
IEDMODEL_GenericIO_GGIO1_SPCSO4);
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, -1);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
IedServer_destroy(iedServer);
exit(-1);
}
running = 1;
signal(SIGINT, sigint_handler);
float t = 0.f;
while (running) {
uint64_t timestamp = Hal_getTimeInMs();
t += 0.1f;
float an1 = sinf(t);
float an2 = sinf(t + 1.f);
float an3 = sinf(t + 2.f);
float an4 = sinf(t + 3.f);
IedServer_lockDataModel(iedServer);
Timestamp iecTimestamp;
Timestamp_clearFlags(&iecTimestamp);
Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
Timestamp_setLeapSecondKnown(&iecTimestamp, true);
/* toggle clock-not-synchronized flag in timestamp */
if (((int) t % 2) == 0)
Timestamp_setClockNotSynchronized(&iecTimestamp, true);
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1);
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, &iecTimestamp);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, an2);
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, &iecTimestamp);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f, an3);
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, &iecTimestamp);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f, an4);
IedServer_unlockDataModel(iedServer);
Thread_sleep(100);
}
/* stop MMS server - close TCP server socket and all client sockets */
IedServer_stop(iedServer);
/* Cleanup - free all resources */
IedServer_destroy(iedServer);
TLSConfiguration_destroy(tlsConfig);
} /* main() */

@ -9,3 +9,4 @@ INCLUDES += -I$(LIBIEC_HOME)/src/hal/inc
INCLUDES += -I$(LIBIEC_HOME)/src/goose
INCLUDES += -I$(LIBIEC_HOME)/src/sampled_values
INCLUDES += -I$(LIBIEC_HOME)/src/logging
INCLUDES += -I$(LIBIEC_HOME)/src/tls

@ -1,7 +1,7 @@
UNAME := $(shell uname)
MIPSEL_TOOLCHAIN_PREFIX=mipsel-openwrt-linux-
ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-
ARM_TOOLCHAIN_PREFIX=arm-linux-
#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
#ARM_TOOLCHAIN_PREFIX=arm-poky-linux-gnueabi-
#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
@ -57,7 +57,7 @@ endif
ifeq ($(TARGET), LINUX-ARM)
TOOLCHAIN_PREFIX=$(ARM_TOOLCHAIN_PREFIX)
CFLAGS += -mno-unaligned-access
#CFLAGS += -mno-unaligned-access
#CFLAGS += -mcpu=arm926ej-s
endif

@ -80,6 +80,15 @@ set (lib_common_SRCS
./logging/log_storage.c
)
if(WITH_MBEDTLS)
set (lib_common_SRCS ${lib_common_SRCS}
./tls/mbedtls/tls_mbedtls.c
)
list (APPEND lib_common_SRCS ${tls_SRCS})
endif(WITH_MBEDTLS)
set (lib_asn1c_SRCS
./mms/iso_mms/asn1c/DataAccessError.c
./mms/iso_mms/asn1c/DeleteNamedVariableListRequest.c
@ -367,10 +376,22 @@ if(MSVC)
endif()
ENDIF(WITH_WPCAP)
if(UNIX)
configure_file(
${CMAKE_CURRENT_LIST_DIR}/libiec61850.pc.in
${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc @ONLY
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig")
endif()
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM)
endif(DOXYGEN_FOUND)
install (TARGETS iec61850 iec61850-shared
RUNTIME DESTINATION bin COMPONENT Applications
ARCHIVE DESTINATION lib COMPONENT Libraries
LIBRARY DESTINATION lib COMPONENT Libraries
)

File diff suppressed because it is too large Load Diff

@ -15,7 +15,7 @@
#include "platform_endian.h"
#define LIBIEC61850_VERSION "1.1.1"
#define LIBIEC61850_VERSION "1.2.0"
#ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"

@ -111,11 +111,24 @@ LinkedList_destroyStatic(LinkedList self);
void
LinkedList_add(LinkedList self, void* data);
/**
* \brief Check if the specified data is contained in the list
*
* \param self the LinkedList instance
* \param data data to remove from the LinkedList instance
*
* \return true if data is part of the list, false otherwise
*/
bool
LinkedList_contains(LinkedList self, void* data);
/**
* \brief Removed the specified element from the list
*
* \param self the LinkedList instance
* \param data data to remove from the LinkedList instance
*
* \return true if data has been removed from the list, false otherwise
*/
bool
LinkedList_remove(LinkedList self, void* data);

@ -33,6 +33,9 @@ typedef struct {
void
MemoryAllocator_init(MemoryAllocator* self, char* memoryBlock, int size);
int
MemoryAllocator_getAlignedSize(int size);
char*
MemoryAllocator_allocate(MemoryAllocator* self, int size);

@ -27,6 +27,10 @@
#include "libiec61850_platform_includes.h"
#include "linked_list.h"
#ifdef __cplusplus
extern "C" {
#endif
char*
StringUtils_copyString(const char* string);
@ -77,6 +81,9 @@ StringUtils_createBufferFromHexString(char* hexString, uint8_t* buffer);
bool
StringUtils_startsWith(char* string, char* prefix);
bool
StringUtils_endsWith(const char* str, const char* suffix);
/**
* \brief Compare to characters using the collation order as defined in ISO 9506-2 7.5.2
*
@ -107,4 +114,9 @@ StringUtils_compareStrings(const char* a, const char* b);
void
StringUtils_sortList(LinkedList list);
#ifdef __cplusplus
}
#endif
#endif /* STRING_UTILITIES_H_ */

@ -115,6 +115,21 @@ LinkedList_add(LinkedList list, void* data)
listEnd->next = newElement;
}
bool
LinkedList_contains(LinkedList list, void* data)
{
LinkedList currentElement = list->next;
while (currentElement != NULL) {
if (currentElement->data == data)
return true;
currentElement = currentElement->next;
}
return false;
}
bool
LinkedList_remove(LinkedList list, void* data)
{

@ -23,6 +23,7 @@
#include "libiec61850_platform_includes.h"
#include "simple_allocator.h"
#include "stack_config.h"
void
MemoryAllocator_init(MemoryAllocator* self, char* memoryBlock, int size)
@ -32,19 +33,23 @@ MemoryAllocator_init(MemoryAllocator* self, char* memoryBlock, int size)
self->size = size;
}
static int
getAlignedSize(int size)
int inline
MemoryAllocator_getAlignedSize(int size)
{
#if (CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT == 1)
if ((size % sizeof(void*)) > 0)
return sizeof(void*) * ((size + sizeof(void*) - 1) / sizeof(void*));
else
return size;
#else
return size;
#endif
}
char*
MemoryAllocator_allocate(MemoryAllocator* self, int size)
{
size = getAlignedSize(size);
size = MemoryAllocator_getAlignedSize(size);
if (((self->currentPtr - self->memoryBlock) + size) <= self->size) {
char* ptr = self->currentPtr;

@ -240,6 +240,23 @@ StringUtils_startsWith(char* string, char* prefix)
return false;
}
bool
StringUtils_endsWith(const char* str, const char* suffix)
{
int stringLength = strlen(str);
int suffixLength = strlen(suffix);
if (stringLength >= suffixLength) {
if (!strcmp(str + (stringLength - suffixLength), suffix))
return true;
}
return false;
}
#define LT_MAX_CHARS 128
static int

File diff suppressed because it is too large Load Diff

@ -45,7 +45,8 @@
#define ETH_P_GOOSE 0x88b8
struct sGooseReceiver {
struct sGooseReceiver
{
bool running;
bool stopped;
char* interfaceId;
@ -57,7 +58,6 @@ struct sGooseReceiver {
#endif
};
GooseReceiver
GooseReceiver_create()
{
@ -89,7 +89,6 @@ GooseReceiver_removeSubscriber(GooseReceiver self, GooseSubscriber subscriber)
LinkedList_remove(self->subscriberList, (void*) subscriber);
}
void
GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId)
{
@ -122,45 +121,60 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
uint8_t tag = buffer[bufPos++];
if (elementIndex > maxIndex) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: too much elements!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: too much elements!\n");
return 0;
}
MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex);
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos < 0) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
}
if (bufPos + elementLength > allDataLength) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
return 0;
}
switch (tag) {
switch (tag)
{
case 0x80: /* reserved for access result */
printf("GOOSE_SUBSCRIBER: found reserved value (tag 0x80)!\n");
break;
case 0xa1: /* array */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found array\n");
if (MmsValue_getType(value) == MMS_ARRAY) {
if (!parseAllData(buffer + bufPos, elementLength, value))
return -1;
}
break;
case 0xa2: /* structure */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found structure\n");
if (MmsValue_getType(value) == MMS_STRUCTURE) {
if (!parseAllData(buffer + bufPos, elementLength, value))
return -1;
}
break;
case 0x83: /* boolean */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found boolean\n");
if (MmsValue_getType(value) == MMS_BOOLEAN) {
MmsValue_setBoolean(value, BerDecoder_decodeBoolean(buffer, bufPos));
}
else
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n");
break;
@ -173,10 +187,11 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
elementLength - 1);
}
else
if (DEBUG_GOOSE_SUBSCRIBER)
printf("bit-string is of wrong size");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("bit-string is of wrong size");
}
break;
case 0x85: /* integer */
if (MmsValue_getType(value) == MMS_INTEGER) {
if (elementLength <= value->value.integer->maxSize) {
@ -185,6 +200,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
}
break;
case 0x86: /* unsigned integer */
if (MmsValue_getType(value) == MMS_UNSIGNED) {
if (elementLength <= value->value.integer->maxSize) {
@ -193,6 +209,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
}
break;
case 0x87: /* Float */
if (MmsValue_getType(value) == MMS_FLOAT) {
if (elementLength == 9) {
@ -212,6 +229,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
}
break;
case 0x8a: /* visible string */
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
@ -231,6 +249,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
break;
case 0x8c: /* binary time */
if (MmsValue_getType(value) == MMS_BINARY_TIME) {
if ((elementLength == 4) || (elementLength == 6)) {
@ -238,17 +257,21 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
}
break;
case 0x91: /* Utctime */
if (elementLength == 8) {
if (MmsValue_getType(value) == MMS_UTC_TIME) {
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
}
else
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n");
}
else
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
break;
default:
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
@ -277,13 +300,20 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos < 0) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
}
if (bufPos + elementLength > allDataLength) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
goto exit_with_error;
}
switch (tag) {
switch (tag)
{
case 0x80: /* reserved for access result */
break;
case 0xa1: /* array */
@ -331,17 +361,25 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos < 0) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
}
if (bufPos + elementLength > allDataLength) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
goto exit_with_error;
}
MmsValue* value = NULL;
switch (tag) {
switch (tag)
{
case 0xa1: /* array */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found array\n");
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, false);
@ -349,8 +387,10 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
goto exit_with_error;
break;
case 0xa2: /* structure */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found structure\n");
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, true);
@ -358,8 +398,10 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
goto exit_with_error;
break;
case 0x83: /* boolean */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found boolean\n");
value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
break;
@ -373,30 +415,35 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
}
break;
case 0x85: /* integer */
value = MmsValue_newInteger(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
value->value.integer->size = elementLength;
break;
case 0x86: /* unsigned integer */
value = MmsValue_newUnsigned(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
value->value.integer->size = elementLength;
break;
case 0x87: /* Float */
if (elementLength == 9)
value = MmsValue_newDouble(BerDecoder_decodeDouble(buffer, bufPos));
else if (elementLength == 5)
value = MmsValue_newFloat(BerDecoder_decodeFloat(buffer, bufPos));
if (elementLength == 9)
value = MmsValue_newDouble(BerDecoder_decodeDouble(buffer, bufPos));
else if (elementLength == 5)
value = MmsValue_newFloat(BerDecoder_decodeFloat(buffer, bufPos));
break;
case 0x89: /* octet string */
value = MmsValue_newOctetString(elementLength, elementLength);
memcpy(value->value.octetString.buf, buffer + bufPos, elementLength);
break;
case 0x8a: /* visible string */
value = MmsValue_newVisibleStringFromByteArray(buffer + bufPos, elementLength);
break;
case 0x8c: /* binary time */
if (elementLength == 4)
value = MmsValue_newBinaryTime(true);
@ -407,16 +454,20 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength);
break;
case 0x91: /* Utctime */
if (elementLength == 8) {
value = MmsValue_newUtcTime(0);
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
}
else
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
break;
default:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
goto exit_with_error;
}
@ -432,7 +483,7 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
return dataSetValues;
exit_with_error:
exit_with_error:
if (dataSetValues != NULL)
MmsValue_delete(dataSetValues);
@ -440,7 +491,6 @@ exit_with_error:
return NULL;
}
static int
parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
{
@ -461,6 +511,11 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
if (buffer[bufPos++] == 0x61) {
int gooseLength;
bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength);
if (bufPos < 0) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
}
int gooseEnd = bufPos + gooseLength;
@ -469,6 +524,11 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength);
if (bufPos < 0) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
}
if (bufPos + elementLength > apduLength) {
if (DEBUG_GOOSE_SUBSCRIBER)
@ -480,9 +540,11 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
if (bufPos == -1)
goto exit_with_fault;
switch(tag) {
switch (tag)
{
case 0x80: /* gocbRef */
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found gocbRef\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found gocbRef\n");
{
LinkedList element = LinkedList_getNext(self->subscriberList);
@ -492,7 +554,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
if (subscriber->goCBRefLen == elementLength) {
if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) {
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n");
matchingSubscriber = subscriber;
break;
}
@ -511,61 +574,73 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
timeAllowedToLive = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found timeAllowedToLive %u\n", timeAllowedToLive);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found timeAllowedToLive %u\n", timeAllowedToLive);
break;
case 0x82:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found dataSet\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found dataSet\n");
break;
case 0x83:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found goId\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found goId\n");
break;
case 0x84:
timestampBufPos = buffer + bufPos;
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found timestamp\n");
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found timestamp\n");
break;
case 0x85:
stNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found stNum: %u\n", stNum);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found stNum: %u\n", stNum);
break;
case 0x86:
sqNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found sqNum: %u\n", sqNum);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found sqNum: %u\n", sqNum);
break;
case 0x87:
simulation = BerDecoder_decodeBoolean(buffer, bufPos);
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found simulation: %i\n", simulation);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found simulation: %i\n", simulation);
break;
case 0x88:
confRev = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found confRev: %u\n", confRev);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found confRev: %u\n", confRev);
break;
case 0x89:
ndsCom = BerDecoder_decodeBoolean(buffer, bufPos);
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found ndsCom: %i\n", ndsCom);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found ndsCom: %i\n", ndsCom);
break;
case 0x8a:
numberOfDatSetEntries = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found number of entries: %u\n", numberOfDatSetEntries);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found number of entries: %u\n", numberOfDatSetEntries);
break;
case 0xab:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found all data with length: %i\n", elementLength);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found all data with length: %i\n", elementLength);
dataSetBufferAddress = buffer + bufPos;
dataSetBufferLength = elementLength;
break;
default:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Unknown tag %02x\n", tag);
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Unknown tag %02x\n", tag);
break;
}
@ -609,12 +684,12 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
return 0;
}
exit_with_fault:
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Invalid goose payload\n");
exit_with_fault:
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Invalid goose payload\n");
return -1;
}
static void
parseGooseMessage(GooseReceiver self, int numbytes)
{
@ -622,7 +697,8 @@ parseGooseMessage(GooseReceiver self, int numbytes)
bool subscriberFound = false;
uint8_t* buffer = self->buffer;
if (numbytes < 22) return;
if (numbytes < 22)
return;
/* skip ethernet addresses */
bufPos = 12;
@ -668,7 +744,6 @@ parseGooseMessage(GooseReceiver self, int numbytes)
printf("GOOSE_SUBSCRIBER: APDU length: %i\n", apduLength);
}
// check if there is an interested subscriber
LinkedList element = LinkedList_getNext(self->subscriberList);
@ -691,7 +766,6 @@ parseGooseMessage(GooseReceiver self, int numbytes)
}
}
static void
gooseReceiverLoop(void* threadParameter)
{
@ -702,17 +776,19 @@ gooseReceiverLoop(void* threadParameter)
GooseReceiver_startThreadless(self);
while (self->running) {
if (self->running) {
if (GooseReceiver_tick(self) == false)
Thread_sleep(1);
}
while (self->running) {
GooseReceiver_stopThreadless(self);
if (GooseReceiver_tick(self) == false)
Thread_sleep(1);
}
self->stopped = true;
}
GooseReceiver_stopThreadless(self);
}
self->stopped = true;
}
// start GOOSE receiver in a separate thread
void
@ -774,7 +850,7 @@ GooseReceiver_destroy(GooseReceiver self)
/***************************************
* Functions for non-threaded operation
***************************************/
void
EthernetSocket
GooseReceiver_startThreadless(GooseReceiver self)
{
if (self->interfaceId == NULL)
@ -788,12 +864,15 @@ GooseReceiver_startThreadless(GooseReceiver self)
}
else
self->running = false;
return self->ethSocket;
}
void
GooseReceiver_stopThreadless(GooseReceiver self)
{
Ethernet_destroySocket(self->ethSocket);
if (self->ethSocket)
Ethernet_destroySocket(self->ethSocket);
self->running = false;
}

@ -24,19 +24,20 @@
#ifndef GOOSE_RECEIVER_H_
#define GOOSE_RECEIVER_H_
#include <goose_subscriber.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include "hal_ethernet.h"
#include "goose_subscriber.h"
/**
* \addtogroup goose_api_group
*/
/**@{*/
typedef struct sGooseReceiver* GooseReceiver;
/**
@ -101,6 +102,15 @@ GooseReceiver_start(GooseReceiver self);
void
GooseReceiver_stop(GooseReceiver self);
/**
* \brief Check if GOOSE receiver is running
*
* Can be used to check if \ref GooseReceiver_start has been successful.
*
* \param self the GooseReceiver instance
*
* \return true if GOOSE receiver is running, false otherwise
*/
bool
GooseReceiver_isRunning(GooseReceiver self);
@ -115,7 +125,7 @@ GooseReceiver_destroy(GooseReceiver self);
/***************************************
* Functions for non-threaded operation
***************************************/
void
EthernetSocket
GooseReceiver_startThreadless(GooseReceiver self);
void

@ -22,6 +22,7 @@
*/
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <net/if.h>
@ -46,12 +47,82 @@ struct sEthernetSocket {
struct bpf_program bpfProgram; // BPF filter machine code program.
};
int _Ethernet_activateBpdFilter(EthernetSocket self)
struct sEthernetHandleSet {
struct pollfd *handles;
int nhandles;
};
EthernetHandleSet
EthernetHandleSet_new(void)
{
EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet));
if (result != NULL) {
result->handles = NULL;
result->nhandles = 0;
}
return result;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if (self != NULL && sock != NULL) {
int i = self->nhandles++;
self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd));
self->handles[i].fd = sock->bpf;
self->handles[i].events = POLLIN;
}
}
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if ((self != NULL) && (sock != NULL)) {
unsigned i;
for (i = 0; i < self->nhandles; i++) {
if (self->handles[i].fd == sock->bpf) {
memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1));
self->nhandles--;
return;
}
}
}
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
int result;
if ((self != NULL) && (self->nhandles >= 0)) {
result = poll(self->handles, self->nhandles, timeoutMs);
}
else {
result = -1;
}
return result;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
if (self->nhandles)
free(self->handles);
GLOBAL_FREEMEM(self);
}
int
activateBpdFilter(EthernetSocket self)
{
return ioctl(self->bpf, BIOCSETF, &self->bpfProgram);
}
int _Ethernet_setBpfEthernetAddressFilter(EthernetSocket self, uint8_t *addr)
static int
setBpfEthernetAddressFilter(EthernetSocket self, uint8_t *addr)
{
if (addr)
{
@ -62,18 +133,19 @@ int _Ethernet_setBpfEthernetAddressFilter(EthernetSocket self, uint8_t *addr)
memcpy((void *)&self->bpfProgram.bf_insns[3].k, &addr[2], 4);
memcpy((void *)&self->bpfProgram.bf_insns[5].k, &addr, 2);
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
else
{
// Disable Ethernet address filter.
self->bpfProgram.bf_insns[0].k = 0;
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
}
int _Ethernet_setBpfEthertypeFilter(EthernetSocket self, uint16_t etherType)
static int
setBpfEthertypeFilter(EthernetSocket self, uint16_t etherType)
{
if (etherType)
{
@ -83,14 +155,14 @@ int _Ethernet_setBpfEthertypeFilter(EthernetSocket self, uint16_t etherType)
// Set protocol.
self->bpfProgram.bf_insns[9].k = etherType;
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
else
{
// Disable Ethertype filter.
self->bpfProgram.bf_insns[6].k = 0;
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
}
@ -283,13 +355,15 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
return self;
}
void Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType)
void
Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType)
{
if (!self || !self->bpfProgram.bf_insns || _Ethernet_setBpfEthertypeFilter(self, etherType))
if (!self || !self->bpfProgram.bf_insns || setBpfEthertypeFilter(self, etherType))
printf("Unable to set ethertype filter!\n");
}
int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
int
Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
{
// If the actual buffer is empty, make a read call to the BSP device in order to get new data.
if (self->bpfEnd - self->bpfPositon < 4)
@ -335,13 +409,15 @@ int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
return 0;
}
void Ethernet_sendPacket(EthernetSocket self, uint8_t* buffer, int packetSize)
void
Ethernet_sendPacket(EthernetSocket self, uint8_t* buffer, int packetSize)
{
// Just send the packet as it is.
write(self->bpf, buffer, packetSize);
}
void Ethernet_destroySocket(EthernetSocket self)
void
Ethernet_destroySocket(EthernetSocket self)
{
// Close the BPF device.
close(self->bpf);

@ -23,6 +23,7 @@
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
@ -40,6 +41,79 @@ struct sEthernetSocket {
struct sockaddr_ll socketAddress;
};
struct sEthernetHandleSet {
struct pollfd *handles;
int nhandles;
};
EthernetHandleSet
EthernetHandleSet_new(void)
{
EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet));
if (result != NULL) {
result->handles = NULL;
result->nhandles = 0;
}
return result;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if (self != NULL && sock != NULL) {
int i = self->nhandles++;
self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd));
self->handles[i].fd = sock->rawSocket;
self->handles[i].events = POLLIN;
}
}
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if ((self != NULL) && (sock != NULL)) {
int i;
for (i = 0; i < self->nhandles; i++) {
if (self->handles[i].fd == sock->rawSocket) {
memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1));
self->nhandles--;
return;
}
}
}
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
int result;
if ((self != NULL) && (self->nhandles >= 0)) {
result = poll(self->handles, self->nhandles, timeoutMs);
}
else {
result = -1;
}
return result;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
if (self->nhandles)
free(self->handles);
GLOBAL_FREEMEM(self);
}
static int
getInterfaceIndex(int sock, const char* deviceName)
{

@ -47,6 +47,8 @@
#define HAVE_REMOTE
// Enable WinPcap specific extension: pcap_getevent()
#define WPCAP
#include "pcap.h"
struct sEthernetSocket {
@ -54,6 +56,11 @@ struct sEthernetSocket {
struct bpf_program etherTypeFilter;
};
struct sEthernetHandleSet {
HANDLE *handles;
int nhandles;
};
#ifdef __GNUC__ /* detect MINGW */
#ifndef __MINGW64_VERSION_MAJOR
@ -90,7 +97,6 @@ typedef ULONG (WINAPI* pgetadaptersaddresses)(ULONG family, ULONG flags, PVOID r
static pgetadaptersaddresses GetAdaptersAddresses;
static bool dllLoaded = false;
static void
@ -115,6 +121,74 @@ loadDLLs(void)
#endif /* __GNUC__ */
EthernetHandleSet
EthernetHandleSet_new(void)
{
EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet));
if (result != NULL) {
result->handles = NULL;
result->nhandles = 0;
}
return result;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if (self != NULL && sock != NULL) {
int i = self->nhandles++;
self->handles = (HANDLE *) realloc(self->handles, self->nhandles * sizeof(HANDLE));
self->handles[i] = pcap_getevent(sock->rawSocket);
}
}
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if ((self != NULL) && (sock != NULL)) {
HANDLE h = pcap_getevent(sock->rawSocket);
int i;
for (i = 0; i < self->nhandles; i++) {
if (self->handles[i] == h) {
memmove(&self->handles[i], &self->handles[i+1], sizeof(HANDLE) * (self->nhandles - i - 1));
self->nhandles--;
return;
}
}
}
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
int result;
if ((self != NULL) && (self->nhandles > 0)) {
result = WaitForMultipleObjects(self->nhandles, self->handles, 0, timeoutMs);
}
else {
result = -1;
}
return result;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
if (self->handles)
free(self->handles);
GLOBAL_FREEMEM(self);
}
static char*
getInterfaceName(int interfaceIndex)
{
@ -141,7 +215,7 @@ getInterfaceName(int interfaceIndex)
interfaceName = (char*) malloc(strlen(device->name) + 1);
strcpy(interfaceName, device->name);
if (DEBUG_HAL_ETHERNET)
printf("Use interface (%s)\n", interfaceName);
printf("Use interface (%s)\n", interfaceName);
ifaceFound = true;
break;
}
@ -152,7 +226,7 @@ getInterfaceName(int interfaceIndex)
if (!ifaceFound)
{
if (DEBUG_HAL_ETHERNET)
printf("No ethernet interfaces found! Make sure WinPcap is installed.\n");
printf("No ethernet interfaces found! Make sure WinPcap is installed.\n");
return NULL;
}
@ -195,7 +269,7 @@ getAdapterMacAddress(char* pcapAdapterName, uint8_t* macAddress)
if (strstr(pcapAdapterName, pAddress->AdapterName) != 0) {
if (DEBUG_HAL_ETHERNET)
printf(" requested found!");
printf(" requested found!");
for (i = 0; i < (int) addressLength; i++) {
macAddress[i] = pAddress->PhysicalAddress[i];
@ -215,8 +289,6 @@ getAdapterMacAddress(char* pcapAdapterName, uint8_t* macAddress)
}
}
void
Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
{
@ -338,6 +410,28 @@ Ethernet_isSupported()
return false;
}
EthernetHandleSet
EthernetHandleSet_new(void)
{
return NULL;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
return 0;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
}
void
Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
{

@ -48,6 +48,58 @@ extern "C" {
*/
typedef struct sEthernetSocket* EthernetSocket;
/** Opaque reference for a set of ethernet socket handles */
typedef struct sEthernetHandleSet* EthernetHandleSet;
/**
* \brief Create a new connection handle set (EthernetHandleSet)
*
* \return new EthernetHandleSet instance
*/
EthernetHandleSet
EthernetHandleSet_new(void);
/**
* \brief add a socket to an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
*/
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock);
/**
* \brief remove a socket from an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
*/
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock);
/**
* \brief wait for a socket to become ready
*
* This function is corresponding to the BSD socket select function.
* The function will return after \p timeoutMs ms if no data is pending.
*
* \param self the HandleSet instance
* \param timeout in milliseconds (ms)
* \return It returns the number of sockets on which data is pending
* or 0 if no data is pending on any of the monitored connections.
* The function shall return -1 if a socket error occures.
*/
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs);
/**
* \brief destroy the EthernetHandleSet instance
*
* \param self the HandleSet instance to destroy
*/
void
EthernetHandleSet_destroy(EthernetHandleSet self);
/**
* \brief Return the MAC address of an Ethernet interface.
*

@ -166,17 +166,6 @@ FileSystem_readDirectory(DirectoryHandle directory, bool* isDirectory);
void
FileSystem_closeDirectory(DirectoryHandle directory);
/**
* \brief set local file system base path for the MMS VMD
*
* NOTE: the meaning of this functions is platform specific. It was introduced to
* simplify the configuration of the VMD base path at runtime. It may not be supported
* on all platform. Also it is not called by the MMS protocol stack.
*
* \param basePath the local base path of the MMS VMD
*/
void
FileSystem_setBasePath(char* basePath);
/*! @} */

@ -63,7 +63,7 @@ HandleSet
Handleset_new(void);
/**
* \brief add a soecket to an existing handle set
* \brief add a socket to an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
@ -71,18 +71,17 @@ Handleset_new(void);
void
Handleset_addSocket(HandleSet self, const Socket sock);
/**
* \brief wait for a socket to become ready
*
* This function is corresponding to the BSD socket select function.
* It returns the number of sockets on which data is pending or 0 if no data is pending
* on any of the monitored connections. The function will return after "timeout" ms if no
* data is pending.
* The function shall return -1 if a socket error occures.
* The function will return after \p timeoutMs ms if no data is pending.
*
* \param self the HandleSet instance
* \param timeout in milliseconds (ms)
* \param self the HandleSet instance
* \param timeout in milliseconds (ms)
* \return It returns the number of sockets on which data is pending
* or 0 if no data is pending on any of the monitored connections.
* The function shall return -1 if a socket error occures.
*/
int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs);

@ -105,7 +105,7 @@ static void
resetLastApplError(ControlObjectClient self)
{
self->lastApplError.error = 0;
self->lastApplError.addCause = ADD_CAUSE_UNKNOWN;
self->lastApplError.addCause = ADD_CAUSE_UNKNOWN;
self->lastApplError.ctlNum = 0;
}
@ -118,11 +118,11 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
char reference[129];
if (strlen(objectReference) < 121) {
strcpy(reference, objectReference);
strcat(reference, ".ctlModel");
strcpy(reference, objectReference);
strcat(reference, ".ctlModel");
}
else
goto exit_function;
goto exit_function;
IedClientError error;
@ -136,7 +136,7 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
}
MmsVariableSpecification* ctlVarSpec =
IedConnection_getVariableSpecification(connection, &error, objectReference, IEC61850_FC_CO);
IedConnection_getVariableSpecification(connection, &error, objectReference, IEC61850_FC_CO);
if (error != IED_ERROR_OK) {
if (DEBUG_IED_CLIENT)
@ -154,10 +154,10 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
MmsVariableSpecification* t = NULL;
if (MmsVariableSpecification_getType(ctlVarSpec) == MMS_STRUCTURE) {
MmsVariableSpecification* oper = MmsVariableSpecification_getNamedVariableRecursive(ctlVarSpec, "Oper");
MmsVariableSpecification* oper = MmsVariableSpecification_getNamedVariableRecursive(ctlVarSpec, "Oper");
if (oper)
{
if (oper)
{
hasOper = true;
ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlVal");
@ -165,18 +165,18 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
if (MmsVariableSpecification_getType(ctlVal) == MMS_STRUCTURE)
isAPC = true;
MmsVariableSpecification* operTm = MmsVariableSpecification_getNamedVariableRecursive(oper, "operTm");
MmsVariableSpecification* operTm = MmsVariableSpecification_getNamedVariableRecursive(oper, "operTm");
if (operTm)
hasTimeActivatedControl = true;
if (operTm)
hasTimeActivatedControl = true;
MmsVariableSpecification* ctlNum = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlNum");
MmsVariableSpecification* ctlNum = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlNum");
if (ctlNum)
hasCtlNum = true;
if (ctlNum)
hasCtlNum = true;
t = MmsVariableSpecification_getNamedVariableRecursive(oper, "T");
}
t = MmsVariableSpecification_getNamedVariableRecursive(oper, "T");
}
}
if (hasOper == false) {
@ -187,10 +187,10 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
}
if ((ctlVal == NULL) || (t == NULL)) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: \"Oper\" is missing required element\n");
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: \"Oper\" is missing required element\n");
goto free_varspec;
goto free_varspec;
}
self = (ControlObjectClient) GLOBAL_CALLOC(1, sizeof(struct sControlObjectClient));
@ -221,10 +221,10 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
private_IedConnection_addControlClient(connection, self);
free_varspec:
free_varspec:
MmsVariableSpecification_destroy(ctlVarSpec);
exit_function:
exit_function:
return self;
}
@ -241,7 +241,7 @@ ControlObjectClient_destroy(ControlObjectClient self)
MmsValue_delete(self->ctlVal);
if (self->analogValue != NULL)
MmsValue_delete(self->analogValue);
MmsValue_delete(self->analogValue);
if (self->orIdent != NULL)
GLOBAL_FREEMEM(self->orIdent);
@ -320,11 +320,11 @@ createOriginValue(ControlObjectClient self)
goto exit_function;
cleanup_on_error:
cleanup_on_error:
MmsValue_delete(origin);
origin = NULL;
exit_function:
exit_function:
return origin;
}
@ -347,20 +347,20 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
int operElementCount = 5;
if (self->hasTimeActivatedMode)
operElementCount++;
operElementCount++;
if (self->hasCtlNum)
operElementCount++;
operElementCount++;
operParameters = MmsValue_createEmptyStructure(operElementCount);
operParameters = MmsValue_createEmptyStructure(operElementCount);
/* support simplified usage of APC controls - user doesn't need to create the structure */
if (self->analogValue != NULL) {
if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) {
MmsValue_setElement(self->analogValue, 0, ctlVal);
ctlVal = self->analogValue;
}
}
/* support simplified usage of APC controls - user doesn't need to create the structure */
if (self->analogValue != NULL) {
if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) {
MmsValue_setElement(self->analogValue, 0, ctlVal);
ctlVal = self->analogValue;
}
}
MmsValue_setElement(operParameters, 0, ctlVal);
@ -381,19 +381,19 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
}
if (self->hasCtlNum) {
MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
MmsValue_setElement(operParameters, index++, ctlNum);
MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
MmsValue_setElement(operParameters, index++, ctlNum);
}
uint64_t timestamp;
if ((self->ctlModel == CONTROL_MODEL_SBO_ENHANCED) && (self->useConstantT))
timestamp = self->constantT;
timestamp = self->constantT;
else
timestamp = Hal_getTimeInMs();
timestamp = Hal_getTimeInMs();
if (self->useConstantT)
self->constantT = timestamp;
self->constantT = timestamp;
MmsValue* ctlTime;
@ -446,13 +446,13 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
MmsValue_update(self->ctlVal, ctlVal);
if (self->analogValue)
MmsValue_setElement(self->analogValue, 0, NULL);
MmsValue_setElement(self->analogValue, 0, NULL);
self->opertime = operTime;
success = true;
exit_function:
exit_function:
return success;
}
@ -478,20 +478,20 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
int selValElementCount = 5;
if (self->hasTimeActivatedMode)
selValElementCount++;
selValElementCount++;
if (self->hasCtlNum)
selValElementCount++;
selValElementCount++;
MmsValue* selValParameters = MmsValue_createEmptyStructure(selValElementCount);
/* support simplified usage of APC controls - user doesn't need to create the structure */
if (self->analogValue != NULL) {
if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) {
MmsValue_setElement(self->analogValue, 0, ctlVal);
ctlVal = self->analogValue;
}
}
/* support simplified usage of APC controls - user doesn't need to create the structure */
if (self->analogValue != NULL) {
if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) {
MmsValue_setElement(self->analogValue, 0, ctlVal);
ctlVal = self->analogValue;
}
}
MmsValue_setElement(selValParameters, 0, ctlVal);
@ -508,15 +508,15 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
self->ctlNum++;
if (self->hasCtlNum) {
MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
MmsValue_setElement(selValParameters, index++, ctlNum);
MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
MmsValue_setElement(selValParameters, index++, ctlNum);
}
uint64_t timestamp = Hal_getTimeInMs();
MmsValue* ctlTime;
if (self->useConstantT)
self->constantT = timestamp;
self->constantT = timestamp;
if (self->edition == 2)
ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
@ -550,7 +550,7 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
MmsValue_update(self->ctlVal, ctlVal);
if (self->analogValue)
MmsValue_setElement(self->analogValue, 0, NULL);
MmsValue_setElement(self->analogValue, 0, NULL);
return true;
}
@ -592,7 +592,7 @@ ControlObjectClient_select(ControlObjectClient self)
snprintf(sboReference, 129, "%s/%s", domainId, itemId);
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
if (strcmp(MmsValue_toString(value), "") == 0) {
if (strcmp(MmsValue_toString(value), "") == 0) {
if (DEBUG_IED_CLIENT)
printf("select-response-\n");
}
@ -613,7 +613,7 @@ ControlObjectClient_select(ControlObjectClient self)
MmsValue_delete(value);
exit_function:
exit_function:
return selected;
}
@ -648,9 +648,9 @@ ControlObjectClient_cancel(ControlObjectClient self)
uint64_t timestamp;
if (self->useConstantT)
timestamp = self->constantT;
timestamp = self->constantT;
else
timestamp = Hal_getTimeInMs();
timestamp = Hal_getTimeInMs();
MmsValue* ctlTime;
@ -697,7 +697,7 @@ ControlObjectClient_cancel(ControlObjectClient self)
void
ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT)
{
self->useConstantT = useConstantT;
self->useConstantT = useConstantT;
}
void
@ -712,7 +712,6 @@ ControlObjectClient_setInterlockCheck(ControlObjectClient self, bool value)
self->interlockCheck = value;
}
void
ControlObjectClient_enableSynchroCheck(ControlObjectClient self)
{

@ -183,6 +183,97 @@ newEmptyPhyCommAddress(void) {
return self;
}
PhyComAddress
ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self)
{
PhyComAddress retVal;
memset(&retVal, 0, sizeof(retVal));
if (self->dstAddress == NULL) goto exit_error;
if (MmsValue_getType(self->dstAddress) != MMS_STRUCTURE) {
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n");
goto exit_error;
}
if (MmsValue_getArraySize(self->dstAddress) != 4) {
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n");
goto exit_error;
}
MmsValue* addr = MmsValue_getElement(self->dstAddress, 0);
if (MmsValue_getType(addr) != MMS_OCTET_STRING) {
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n");
goto exit_error;
}
if (MmsValue_getOctetStringSize(addr) != 6) {
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong size\n");
goto exit_error;
}
uint8_t* addrBuf = MmsValue_getOctetStringBuffer(addr);
memcpy(&(retVal.dstAddress), addrBuf, 6);
MmsValue* prio = MmsValue_getElement(self->dstAddress, 1);
if (MmsValue_getType(prio) != MMS_UNSIGNED) {
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - prio has wrong type\n");
goto exit_error;
}
retVal.vlanPriority = MmsValue_toUint32(prio);
MmsValue* vid = MmsValue_getElement(self->dstAddress, 2);
if (MmsValue_getType(vid) != MMS_UNSIGNED) {
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - vid has wrong type\n");
goto exit_error;
}
retVal.vlanId = MmsValue_toUint32(vid);
MmsValue* appID = MmsValue_getElement(self->dstAddress, 3);
if (MmsValue_getType(appID) != MMS_UNSIGNED) {
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - appID has wrong type\n");
goto exit_error;
}
retVal.appId = MmsValue_toUint32(appID);
exit_error:
return retVal;
}
void
ClientGooseControlBlock_setDstAddress(ClientGooseControlBlock self, PhyComAddress value)
{
if (self->dstAddress == NULL)
self->dstAddress = newEmptyPhyCommAddress();
if (self->dstAddress) {
MmsValue* addr = MmsValue_getElement(self->dstAddress, 0);
MmsValue_setOctetString(addr, value.dstAddress, 6);
MmsValue* prio = MmsValue_getElement(self->dstAddress, 1);
MmsValue_setUint8(prio, value.vlanPriority);
MmsValue* vid = MmsValue_getElement(self->dstAddress, 2);
MmsValue_setUint16(vid, value.vlanId);
MmsValue* appID = MmsValue_getElement(self->dstAddress, 3);
MmsValue_setUint16(appID, value.appId);
}
}
MmsValue*
ClientGooseControlBlock_getDstAddress_addr(ClientGooseControlBlock self)
{

@ -115,16 +115,16 @@ setBooleanVariable(ClientSVControlBlock self, const char* varName, bool value)
}
bool
ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool svEna)
ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool value)
{
return setBooleanVariable(self, "SvEna", svEna);
return setBooleanVariable(self, "SvEna", value);
}
bool
ClientSVControlBlock_setResv(ClientSVControlBlock self, bool svEna)
ClientSVControlBlock_setResv(ClientSVControlBlock self, bool value)
{
if (self->isMulticast == false)
return setBooleanVariable(self, "SvEna", svEna);
return setBooleanVariable(self, "SvEna", value);
else
return false;
}

@ -470,27 +470,44 @@ informationReportHandler(void* parameter, char* domainName,
MmsValue_delete(value);
}
IedConnection
IedConnection_create()
static IedConnection
createNewConnectionObject(TLSConfiguration tlsConfig)
{
IedConnection self = (IedConnection) GLOBAL_CALLOC(1, sizeof(struct sIedConnection));
self->enabledReports = LinkedList_create();
self->logicalDevices = NULL;
self->clientControls = LinkedList_create();
if (self) {
self->enabledReports = LinkedList_create();
self->logicalDevices = NULL;
self->clientControls = LinkedList_create();
self->connection = MmsConnection_create();
if (tlsConfig)
self->connection = MmsConnection_createSecure(tlsConfig);
else
self->connection = MmsConnection_create();
self->state = IED_STATE_IDLE;
self->state = IED_STATE_IDLE;
self->stateMutex = Semaphore_create(1);
self->reportHandlerMutex = Semaphore_create(1);
self->stateMutex = Semaphore_create(1);
self->reportHandlerMutex = Semaphore_create(1);
self->connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
self->connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
}
return self;
}
IedConnection
IedConnection_create()
{
return createNewConnectionObject(NULL);
}
IedConnection
IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig)
{
return createNewConnectionObject(tlsConfig);
}
void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
{
@ -2513,7 +2530,7 @@ FileDirectoryEntry_destroy(FileDirectoryEntry self)
GLOBAL_FREEMEM(self);
}
char*
const char*
FileDirectoryEntry_getFileName(FileDirectoryEntry self)
{
return self->fileName;

@ -232,6 +232,21 @@ Timestamp_create()
return self;
}
Timestamp*
Timestamp_createFromByteArray(uint8_t* byteArray)
{
Timestamp* self = Timestamp_create();
if (self) {
int i;
for (i = 0; i < 8; i++)
self->val[i] = byteArray[i];
}
return self;
}
void
Timestamp_destroy(Timestamp* self)
{

@ -1,7 +1,7 @@
/*
* iec61850_client.h
*
* Copyright 2013, 2014, 2015 Michael Zillgith
* Copyright 2013-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -34,6 +34,14 @@ extern "C" {
#include "mms_client_connection.h"
#include "linked_list.h"
#ifndef DEPRECATED
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#else
#define DEPRECATED
#endif
#endif
/**
* * \defgroup iec61850_client_api_group IEC 61850/MMS client API
*/
@ -178,6 +186,21 @@ typedef enum {
IedConnection
IedConnection_create(void);
/**
* \brief create a new IedConnection instance that has support for TLS
*
* This function creates a new IedConnection instance that is used to handle a connection to an IED.
* It allocated all required resources. The new connection is in the "idle" state. Before it can be used
* the connect method has to be called. The connection will use TLS when a TLSConfiguration object is
* provided.
*
* \param tlsConfig the TLS configuration to be used
*
* \return the new IedConnection instance
*/
IedConnection
IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig);
/**
* \brief destroy an IedConnection instance.
*
@ -329,6 +352,12 @@ IedConnection_getMmsConnection(IedConnection self);
/** SV ASDU contains attribute Security */
#define IEC61850_SV_OPT_SECURITY 16
#define IEC61850_SV_SMPMOD_SAMPLES_PER_PERIOD 0
#define IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND 1
#define IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE 2
/** an opaque handle to the instance data of a ClientSVControlBlock object */
typedef struct sClientSVControlBlock* ClientSVControlBlock;
@ -370,13 +399,13 @@ ClientSVControlBlock_getLastComError(ClientSVControlBlock self);
bool
ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool svEna);
ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool value);
bool
ClientSVControlBlock_getSvEna(ClientSVControlBlock self);
bool
ClientSVControlBlock_setResv(ClientSVControlBlock self, bool svEna);
ClientSVControlBlock_setResv(ClientSVControlBlock self, bool value);
bool
ClientSVControlBlock_getResv(ClientSVControlBlock self);
@ -524,28 +553,34 @@ ClientGooseControlBlock_getMaxTime(ClientGooseControlBlock self);
bool
ClientGooseControlBlock_getFixedOffs(ClientGooseControlBlock self);
MmsValue* /* MMS_OCTET_STRING */
ClientGooseControlBlock_getDstAddress_addr(ClientGooseControlBlock self);
PhyComAddress
ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self);
void
ClientGooseControlBlock_setDstAddress(ClientGooseControlBlock self, PhyComAddress value);
DEPRECATED MmsValue* /* MMS_OCTET_STRING */
ClientGooseControlBlock_getDstAddress_addr(ClientGooseControlBlock self);
DEPRECATED void
ClientGooseControlBlock_setDstAddress_addr(ClientGooseControlBlock self, MmsValue* macAddr);
uint8_t
DEPRECATED uint8_t
ClientGooseControlBlock_getDstAddress_priority(ClientGooseControlBlock self);
void
DEPRECATED void
ClientGooseControlBlock_setDstAddress_priority(ClientGooseControlBlock self, uint8_t priorityValue);
uint16_t
DEPRECATED uint16_t
ClientGooseControlBlock_getDstAddress_vid(ClientGooseControlBlock self);
void
DEPRECATED void
ClientGooseControlBlock_setDstAddress_vid(ClientGooseControlBlock self, uint16_t vidValue);
uint16_t
DEPRECATED uint16_t
ClientGooseControlBlock_getDstAddress_appid(ClientGooseControlBlock self);
void
DEPRECATED void
ClientGooseControlBlock_setDstAddress_appid(ClientGooseControlBlock self, uint16_t appidValue);
@ -806,7 +841,7 @@ void
IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReference);
/**
* \brief Trigger a general interrogation (GI) report for the specified report control block (RCB)
* \brief trigger a general interrogation (GI) report for the specified report control block (RCB)
*
* The RCB must have been enabled and GI set as trigger option before this command can be performed.
*
@ -824,7 +859,7 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c
****************************************/
/**
* \brief Get the name of the report data set
* \brief get the name of the report data set
*
* NOTE: the returned string is only valid as long as the ClientReport instance exists!
*
@ -837,6 +872,10 @@ ClientReport_getDataSetName(ClientReport self);
/**
* \brief return the received data set values of the report
*
* NOTE: The returned MmsValue instance is handled by the library and only valid as long as the
* ClientReport instance exists! It should not be used outside the report callback handler to
* avoid concurrency issues.
*
* \param self the ClientReport instance
* \return an MmsValue array instance containing the data set values
*/
@ -894,26 +933,72 @@ ClientReport_getEntryId(ClientReport self);
bool
ClientReport_hasTimestamp(ClientReport self);
/**
* \brief determine if the last received report contains a sequence number
*
* \param self the ClientReport instance
*
* \return true if the report contains a sequence number, false otherwise
*/
bool
ClientReport_hasSeqNum(ClientReport self);
/**
* \brief get the value of the sequence number
*
* NOTE: The returned value is undefined if the sequence number is not present in report
*
* \param self the ClientReport instance
*
* \returns the number of the sequence number when present
*/
uint16_t
ClientReport_getSeqNum(ClientReport self);
/**
* \brief determine if the last received report contains the data set name
*
* \param self the ClientReport instance
*
* \return true if the report contains the data set name, false otherwise
*/
bool
ClientReport_hasDataSetName(ClientReport self);
/**
* \brief determine if the last received report contains reason-for-inclusion information
*
* \param self the ClientReport instance
*
* \return true if the report contains reason-for-inclusion information, false otherwise
*/
bool
ClientReport_hasReasonForInclusion(ClientReport self);
/**
* \brief determine if the last received report contains the configuration revision
*
* \param self the ClientReport instance
*
* \return true if the report contains the configuration revision, false otherwise
*/
bool
ClientReport_hasConfRev(ClientReport self);
/**
* \brief get the value of the configuration revision
*
* NOTE: The returned value is undefined if configuration revision is not present in report
*
* \param self the ClientReport instance
*
* \returns the number of the configuration revision
*/
uint32_t
ClientReport_getConfRev(ClientReport self);
/**
* \brief Indicates if the report contains the bufOvfl (buffer overflow) flag
* \brief indicates if the report contains the bufOvfl (buffer overflow) flag
*
* \param self the ClientReport instance
*
@ -923,7 +1008,7 @@ bool
ClientReport_hasBufOvfl(ClientReport self);
/**
* \brief Get the value of the bufOvfl flag
* \brief get the value of the bufOvfl flag
*
* \param self the ClientReport instance
*
@ -933,7 +1018,7 @@ bool
ClientReport_getBufOvfl(ClientReport self);
/**
* \brief Indicates if the report contains data references for the reported data set members
* \brief indicates if the report contains data references for the reported data set members
*
* \param self the ClientReport instance
*
@ -1788,6 +1873,8 @@ IedConnection_getDataDirectoryByFC(IedConnection self, IedClientError* error, co
* This function can be used to get the MMS variable type specification for an IEC 61850 data attribute. It is an extension
* of the ACSI that may be required by generic client applications.
*
* NOTE: API user is responsible to free the resources (see \ref MmsVariableSpecification_destroy)
*
* \param self the connection object
* \param error the error code if an error occurs
* \param dataAttributeReference string that represents the DA reference
@ -1862,18 +1949,49 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha
typedef struct sFileDirectoryEntry* FileDirectoryEntry;
/**
* @deprecated Will be removed from API
*/
FileDirectoryEntry
FileDirectoryEntry_create(const char* fileName, uint32_t fileSize, uint64_t lastModified);
/**
* \brief Destroy a FileDirectoryEntry object (free all resources)
*
* NOTE: Usually is called as a parameter of the \ref LinkedList_destroyDeep function.
*
* \param self the FileDirectoryEntry object
*/
void
FileDirectoryEntry_destroy(FileDirectoryEntry self);
char*
/**
* \brief Get the name of the file
*
* \param self the FileDirectoryEntry object
*
* \return name of the file as null terminated string
*/
const char*
FileDirectoryEntry_getFileName(FileDirectoryEntry self);
/**
* \brief Get the file size in bytes
*
* \param self the FileDirectoryEntry object
*
* \return size of the file in bytes, or 0 if file size is unknown
*/
uint32_t
FileDirectoryEntry_getFileSize(FileDirectoryEntry self);
/**
* \brief Get the timestamp of last modification of the file
*
* \param self the FileDirectoryEntry object
*
* \return UTC timestamp in milliseconds
*/
uint64_t
FileDirectoryEntry_getLastModified(FileDirectoryEntry self);

@ -356,6 +356,9 @@ typedef union {
Timestamp*
Timestamp_create(void);
Timestamp*
Timestamp_createFromByteArray(uint8_t* byteArray);
void
Timestamp_destroy(Timestamp* self);

@ -303,6 +303,9 @@ DataSetEntry_getNext(DataSetEntry* self);
* that have to contain the LN name, the FC and subsequent path elements separated by "$" instead of ".".
* This is due to efficiency reasons to avoid the creation of additional strings.
*
* If the variable parameter does not contain a logical device name (separated from the remaining variable
* name by the "/" character) the logical device where the data set resides is used automatically.
*
* \param dataSet the data set to which the new entry will be added
* \param variable the name of the variable as MMS variable name including FC ("$" used as separator!)
* \param index the index if the FCDA is an array element, otherwise -1

@ -117,7 +117,9 @@ typedef enum {
IEC61850_CONSTRUCTED = 27,
IEC61850_ENTRY_TIME = 28,
IEC61850_PHYCOMADDR = 29,
IEC61850_CURRENCY = 30
IEC61850_CURRENCY = 30,
IEC61850_OPTFLDS = 31, /* bit-string(10) */
IEC61850_TRGOPS = 32 /* bit-string(6) */
#if (CONFIG_IEC61850_USE_COMPAT_TYPE_DECLARATIONS == 1)
@ -153,6 +155,8 @@ typedef enum {
ENTRY_TIME = 28,
PHYCOMADDR = 29,
CURRENCY = 30
OPTFLDS = 31,
TRGOPS = 32
#endif
} DataAttributeType;
@ -468,6 +472,17 @@ IedModel_getModelNodeByShortAddress(IedModel* self, uint32_t shortAddress);
LogicalDevice*
IedModel_getDeviceByInst(IedModel* self, const char* ldInst);
/**
* \brief Lookup logical device (LD) instance by index
*
* \param self IedModel instance
* \param index the index of the LD in the range (0 .. number of LDs - 1)
*
* \return the corresponding LogicalDevice* object or NULL if the index is out of range
*/
LogicalDevice*
IedModel_getDeviceByIndex(IedModel* self, int index);
/**
* \brief Lookup a logical node by name that is part of the given logical device

@ -61,12 +61,23 @@ typedef struct sClientConnection* ClientConnection;
/**
* \brief Create a new IedServer instance
*
* \param iedModel reference to the IedModel data structure to be used as IEC 61850 model of the device
* \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device
*
* \return the newly generated IedServer instance
* \return the new IedServer instance
*/
IedServer
IedServer_create(IedModel* iedModel);
IedServer_create(IedModel* dataModel);
/**
* \brief Create a new IedServer with TLS support
*
* \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device
* \param tlsConfiguration TLS configuration object, or NULL to not use TLS
*
* \return the new IedServer instance
*/
IedServer
IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration);
/**
* \brief Destroy an IedServer instance and release all resources (memory, TCP sockets)
@ -102,7 +113,7 @@ IedServer_setFilestoreBasepath(IedServer self, const char* basepath);
* \brief Start handling client connections
*
* \param self the instance of IedServer to operate on.
* \param tcpPort the TCP port the server is listening
* \param tcpPort the TCP port the server is listening (-1 for using the default MMS or secure MMS port)
*/
void
IedServer_start(IedServer self, int tcpPort);
@ -123,7 +134,7 @@ IedServer_stop(IedServer self);
* be called periodically.
*
* \param self the instance of IedServer to operate on.
* \param tcpPort the TCP port the server is listening
* \param tcpPort the TCP port the server is listening (-1 for using the default MMS or secure MMS port)
*/
void
IedServer_startThreadless(IedServer self, int tcpPort);
@ -208,16 +219,6 @@ IedServer_isRunning(IedServer self);
MmsServer
IedServer_getMmsServer(IedServer self);
/**
* \brief get the IsoServer instance for this IedServer object
*
* \param self the instance of IedServer to operate on.
*
* \return the IsoServer instance of this IedServer object
*/
IsoServer
IedServer_getIsoServer(IedServer self);
/**
* \brief Enable all GOOSE control blocks.
*
@ -243,7 +244,7 @@ void
IedServer_disableGoosePublishing(IedServer self);
/**
* \brief Set the ethernet interface to be used by GOOSE publishing
* \brief Set the Ethernet interface to be used by GOOSE publishing
*
* This function can be used to set the GOOSE interface ID. If not used or set to NULL the
* default interface ID from stack_config.h is used. Note the interface ID is operating system
@ -273,7 +274,7 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId);
*
* \param self the instance of IedServer to operate on.
* \param authenticator the user provided authenticator callback
* \param authenticatorParameter user provided paremeter that is passed to the authenticator
* \param authenticatorParameter user provided parameter that is passed to the authenticator
*/
void
IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter);
@ -855,6 +856,8 @@ typedef enum {
* a control operation has been invoked by the client. This callback function is
* intended to perform the static tests. It should check if the interlock conditions
* are met if the interlockCheck parameter is true.
* This handler can also be check if the client has the required permissions to execute the
* operation and allow or deny the operation accordingly.
*
* \param parameter the parameter that was specified when setting the control handler
* \param ctlVal the control value of the control operation.
@ -1000,36 +1003,6 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler
* @{
*/
/**
* \brief callback handler to monitor client access to data attributes
*
* User provided callback function to observe (monitor) MMS client access to
* IEC 61850 data attributes. The application can install the same handler
* multiple times and distinguish data attributes by the dataAttribute parameter.
*
* \param the data attribute that has been written by an MMS client.
* \param connection the connection object of the client connection that invoked the write operation
*/
typedef void (*AttributeChangedHandler) (DataAttribute* dataAttribute, ClientConnection connection);
/**
* \deprecated Please use IedServer_handleWriteAccess instead!
* \brief Install an observer for a data attribute.
*
* This instructs the server to monitor write attempts by MMS clients to specific
* data attributes. If a successful write attempt happens the server will call
* the provided callback function to inform the application. This can be used to
* monitor important configuration values.
*
* \param self the instance of IedServer to operate on.
* \param dataAttribute the data attribute to monitor
* \param handler the callback function that is invoked if a client has written to
* the monitored data attribute.
*/
void
IedServer_observeDataAttribute(IedServer self, DataAttribute* dataAttribute,
AttributeChangedHandler handler);
/***************************************************************************
* Access control
**************************************************************************/

@ -38,12 +38,15 @@ struct sIedServer
IedModel* model;
MmsDevice* mmsDevice;
MmsServer mmsServer;
IsoServer isoServer;
char* localIpAddress;
MmsMapping* mmsMapping;
LinkedList clientConnections;
uint8_t writeAccessPolicies;
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
int reportBufferSize;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore dataModelLock;
#endif

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

Loading…
Cancel
Save