Merge branch 'v1.5_develop' into v1.6_develop

v1.6_develop_rgoose_sntp
Michael Zillgith 3 years ago
commit 7099535a39

@ -1,3 +1,60 @@
Changes to version 1.5.1
------------------------
New features and improvements:
- added server side ReportControlBlock events and value access functions
- added functions Timestamp_fromMmsValue and Quality_toMmsValue
- made server report reservation compatible with Ed. 2.1 (LIB61850-293)
- new functions MmsValue_getOctetStringOctet and MmsValue_setOctetStringOctet
- IedConnection: added function IedConnection_getDataSetDirectoryAsync
- IedConnection: added function IedConnection_createDataSetAsync
- IedConnection: added new function IedConnection_deleteDataSetAsync
- IedServer instance can be restarted
- new function IedConnection_setTimeQuality - Added support to set time quality for client generated time stamps (LIB61850-280)
- .NET API: added wrapper for IedConnection_setFile and IedConnection_setFilestoreBasepath (LIB61850-258)
- IED server: improved accuracy of integrity report intervals
- .NET API: GooseSubscriber - added GetGoId, GetGoCbRef, GetFataSet methods
- IED server: add support for SMV control blocks ("SMVC") in config file parser
- .NET API: added support for server integrated GOOSE publisher
- MacOS thread layer: replaced semaphore by mutex
Fixed bugs and vulnerabilities:
- fixed vulnerability of GOOSE subscriber to malformed messages (LIB61850-304)
- fixed - Bug in presentation layer parser can cause infinite loop (LIB61850-302)
- .NET API: fix problem with garbage collected delegates for async client functions (LIB61850-301)
- fixed compilation problem with option CONFIG_MMS_THREADLESS_STACK
- fixed - TPKT error when connection is interrupted during message reception (LIB61850-299)
- handle presentation layer data messages with transfer-syntax-name
- fixed - UBRB: library can't work at the same time with URCB with preconfigured client and URCB without preconfigured client (LIB61850-292)(#355)
- fix - server crashes when presentation message has no user data (LIB61850-291)(#368)
- MMS server: query log service returns services error instead of reject message when log does not exist (LIB61850-290)
- fixed - IED server: crash during invalid control access - FC=CO on invalid layer (LIB61850-282)
- fixed - Server: ctlNum and origin(status) are not updated automatically by the server when APC command is received (LIB61850-277)
- MMS server: fixed problem with continue-after in some get-name-list handling cases
- fixed - IedConnection: outstanding call on IEC layer is not release under some circumstances (LIB61850-270, LIB61850-251)
- fixed bug in IsoServer that caused memory violation when the server was restarted while a client was connected
- IED client: send RptEna as first element when RCB is to be disabled
- fixed problem with double free of TLS configuration structure (LIB61850-254)
- .NET API: Fixed problem with AccessViolationException in GooseControlBlock.GetDstAddress
- MMS server: fixed data race bug in transmitBuffer handling (#338)
- IED server: fixed crash when IEDName+LDInst is too long
- .NET API: fixed bug - server write access handler causes "CallbackOnCollectedDelegate" exception (LIB61850-236)
- MMS server: fixed potential crash when client connection closed during file upload (LIB61850-2)
- MMS client: fixed problem - doesn't close file when the setFile (obtainFile) service is interrupted e.g. due to connection loss (LIB61850-230)
- Ethernet Socket (Windows): fixed bug and added workaround for problem on Windows (most GOOSE/SV messages are not received when waiting with WaitForMultipleObjects - observed with winpcap 4.1.3 and Windows 10
- fixed problem in BER integer decoder (problem with GOOSE fixed length message decoding)
- .NET API: Fixed memory release problem in method ModelNode.GetObjectReference
- IED server: fixed bug in GoCBEventHandler
- fixed problem in BSD ethernet layer (#328)
- fixed bug in cmake file for BSD
- fixed compilation problem when compiling without GOOSE support (#325)
- IED server: control handling - fixed problem in test flag handling
- IED server: For SBOes check test flag match when accepting operate (sSBOes8)
- IED server: Reject Cancel/SBOw in WaitForChange state - fixed problem with test case sCtl26
Changes to version 1.5.0 Changes to version 1.5.0
------------------------ ------------------------
- added support for time with ns resolution - added support for time with ns resolution

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12) cmake_minimum_required(VERSION 3.5.1)
# automagically detect if we should cross-compile # automagically detect if we should cross-compile
if(DEFINED ENV{TOOLCHAIN}) if(DEFINED ENV{TOOLCHAIN})
@ -12,7 +12,7 @@ ENABLE_TESTING()
set(LIB_VERSION_MAJOR "1") set(LIB_VERSION_MAJOR "1")
set(LIB_VERSION_MINOR "5") set(LIB_VERSION_MINOR "5")
set(LIB_VERSION_PATCH "0") set(LIB_VERSION_PATCH "1")
set(LIB_VERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}") 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/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/")
@ -48,6 +48,7 @@ option(CONFIG_IEC61850_LOG_SERVICE "Build with support for IEC 61850 logging ser
option(CONFIG_IEC61850_SERVICE_TRACKING "Build with support for IEC 61850 service tracking" ON) option(CONFIG_IEC61850_SERVICE_TRACKING "Build with support for IEC 61850 service tracking" ON)
option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON) option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON)
option(CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL "Allow user provided callback to control read access" ON) option(CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL "Allow user provided callback to control read access" ON)
option(CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT "allow only configured clients (when pre-configured by ClientLN)" OFF)
set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "65536" CACHE STRING "Default buffer size for buffered reports in byte" ) set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "65536" CACHE STRING "Default buffer size for buffered reports in byte" )

@ -25,12 +25,9 @@ Content:
* [Experimental Python bindings](#experimental-python-bindings) * [Experimental Python bindings](#experimental-python-bindings)
* [Licensing](#commercial-licenses-and-support) * [Licensing](#commercial-licenses-and-support)
* [Contributing](#contributing) * [Contributing](#contributing)
* [Third-party contributions](#third-party-contributions)
## Overview ## Overview
libiec61850 is an open-source (GPLv3) implementation of an IEC 61850 client and server library implementing the protocols MMS, GOOSE and SV. It is implemented in C (according to the C99 standard) to provide maximum portability. It can be used to implement IEC 61850 compliant client and server applications on embedded systems and PCs running Linux, Windows, and MacOS. Included is a set of simple example applications that can be used as a starting point to implement own IEC 61850 compliant devices or to communicate with IEC 61850 devices. The library has been successfully used in many commercial software products and devices. libiec61850 is an open-source (GPLv3) implementation of an IEC 61850 client and server library implementing the protocols MMS, GOOSE and SV. It is implemented in C (according to the C99 standard) to provide maximum portability. It can be used to implement IEC 61850 compliant client and server applications on embedded systems and PCs running Linux, Windows, and MacOS. Included is a set of simple example applications that can be used as a starting point to implement own IEC 61850 compliant devices or to communicate with IEC 61850 devices. The library has been successfully used in many commercial software products and devices.
For commercial projects licenses and support is provided by MZ Automation GmbH. Please contact info@mz-automation.de for more details on licensing options. For commercial projects licenses and support is provided by MZ Automation GmbH. Please contact info@mz-automation.de for more details on licensing options.
@ -54,6 +51,7 @@ The library support the following IEC 61850 protocol features:
* MMS file services (browse, get file, set file, delete/rename file) * MMS file services (browse, get file, set file, delete/rename file)
** required to download COMTRADE files ** required to download COMTRADE files
* Setting group handling * Setting group handling
* Support for service tracking
* GOOSE and SV control block handling * GOOSE and SV control block handling
* TLS support * TLS support
* C and C#/.NET API * C and C#/.NET API
@ -189,10 +187,3 @@ Support and commercial license options are provided by MZ Automation GmbH. Pleas
If you want to contribute to the improvement and development of the library please send me comments, feature requests, bug reports, or patches. For more than trivial contributions I require you to sign a Contributor License Agreement. Please contact info@libiec61850.com. If you want to contribute to the improvement and development of the library please send me comments, feature requests, bug reports, or patches. For more than trivial contributions I require you to sign a Contributor License Agreement. Please contact info@libiec61850.com.
Please don't send pull requests before signing the Contributor License Agreement! Such pull requests may be silently ignored. Please don't send pull requests before signing the Contributor License Agreement! Such pull requests may be silently ignored.
## Third-party contributions
- The Mac OS X socket and ethernet layer has been kindly contributed by Michael Clausen, HES-SO Valais-Wallis, http://www.hevs.ch

@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
Please report security issues to `info@libiec61850.com`

@ -137,7 +137,7 @@
#define CONFIG_IEC61850_BRCB_WITH_RESVTMS 1 #define CONFIG_IEC61850_BRCB_WITH_RESVTMS 1
/* allow only configured clients (when pre-configured by ClientLN) - note behavior in PIXIT Rp13 */ /* allow only configured clients (when pre-configured by ClientLN) - note behavior in PIXIT Rp13 */
#define CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT 0 #cmakedefine01 CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT
/* The default buffer size of buffered RCBs in bytes */ /* The default buffer size of buffered RCBs in bytes */
#cmakedefine CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE @CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE@ #cmakedefine CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE @CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE@

@ -7570,11 +7570,11 @@ extern ReportControlBlock iedModel_GenericIO_LLN0_report2;
extern ReportControlBlock iedModel_GenericIO_LLN0_report3; extern ReportControlBlock iedModel_GenericIO_LLN0_report3;
extern ReportControlBlock iedModel_GenericIO_LLN0_report4; extern ReportControlBlock iedModel_GenericIO_LLN0_report4;
ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report1}; ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 88, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report1};
ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsRCB02", "Events1", false, "Events", 1, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report2}; ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsRCB02", "Events1", false, "Events", 1, 88, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report2};
ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsRCB03", "Events1", false, "Events", 1, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report3}; ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsRCB03", "Events1", false, "Events", 1, 88, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report3};
ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsRCB04", "Events1", false, "Events", 1, 24, 111, 50, 1000, &iedModel_GenericIO_LLN0_report4}; ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsRCB04", "Events1", false, "Events", 1, 88, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report4};
ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "EventsRCB05", "Events1", false, "Events", 1, 24, 111, 50, 1000, NULL}; ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "EventsRCB05", "Events1", false, "Events", 1, 88, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, NULL};

@ -355,6 +355,8 @@ namespace IEC61850
return ControlObjectClient_operate(self, ctlVal.valueReference, operTime); return ControlObjectClient_operate(self, ctlVal.valueReference, operTime);
} }
private ControlObjectClient_ControlActionHandler internalOperateHandler = null;
private void nativeOperateHandler (UInt32 invokeId, IntPtr parameter, int err, int type, bool success) private void nativeOperateHandler (UInt32 invokeId, IntPtr parameter, int err, int type, bool success)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -487,7 +489,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = ControlObjectClient_operateAsync(self, out error, ctlVal.valueReference, operTime, nativeOperateHandler, GCHandle.ToIntPtr(handle)); if (internalOperateHandler == null)
internalOperateHandler = new ControlObjectClient_ControlActionHandler(nativeOperateHandler);
UInt32 invokeId = ControlObjectClient_operateAsync(self, out error, ctlVal.valueReference, operTime, internalOperateHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -522,7 +527,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = ControlObjectClient_selectAsync(self, out error, nativeOperateHandler, GCHandle.ToIntPtr(handle)); if (internalOperateHandler == null)
internalOperateHandler = new ControlObjectClient_ControlActionHandler(nativeOperateHandler);
UInt32 invokeId = ControlObjectClient_selectAsync(self, out error, internalOperateHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -634,7 +642,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = ControlObjectClient_selectWithValueAsync(self, out error, ctlVal.valueReference, nativeOperateHandler, GCHandle.ToIntPtr(handle)); if (internalOperateHandler == null)
internalOperateHandler = new ControlObjectClient_ControlActionHandler(nativeOperateHandler);
UInt32 invokeId = ControlObjectClient_selectWithValueAsync(self, out error, ctlVal.valueReference, internalOperateHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -669,7 +680,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = ControlObjectClient_cancelAsync(self, out error, nativeOperateHandler, GCHandle.ToIntPtr(handle)); if (internalOperateHandler == null)
internalOperateHandler = new ControlObjectClient_ControlActionHandler(nativeOperateHandler);
UInt32 invokeId = ControlObjectClient_cancelAsync(self, out error, internalOperateHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {

@ -1,7 +1,7 @@
/* /*
* IEC61850ClientAPI.cs * IEC61850ClientAPI.cs
* *
* Copyright 2014-2019 Michael Zillgith * Copyright 2014-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -431,6 +431,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 IedConnection_getRequestTimeout(IntPtr self); static extern UInt32 IedConnection_getRequestTimeout(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort); static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort);
@ -622,13 +625,31 @@ namespace IEC61850
int fc, IedConnection_GetVariableSpecificationHandler handler, IntPtr parameter); int fc, IedConnection_GetVariableSpecificationHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_ReadDataSetHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr dataSet); private delegate void IedConnection_ReadDataSetHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSet);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 static extern UInt32
IedConnection_readDataSetValuesAsync(IntPtr self, out int error, string dataSetReference, IntPtr dataSet, IedConnection_readDataSetValuesAsync(IntPtr self, out int error, string dataSetReference, IntPtr dataSet,
IedConnection_ReadDataSetHandler handler, IntPtr parameter); IedConnection_ReadDataSetHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_createDataSetAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet,
IedConnection_GenericServiceHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_deleteDataSetAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference,
IedConnection_GenericServiceHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GetDataSetDirectoryHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSetDirectory, [MarshalAs(UnmanagedType.I1)] bool isDeletable);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getDataSetDirectoryAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference,
IedConnection_GetDataSetDirectoryHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GetRCBValuesHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr rcb); private delegate void IedConnection_GetRCBValuesHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr rcb);
@ -807,6 +828,18 @@ namespace IEC61850
} }
} }
/// <summary>
/// Set the time quality for all timestamps generated by this IedConnection instance
/// </summary>
/// <param name="leapSecondKnown">set or unset leap seconds known flag</param>
/// <param name="clockFailure">set or unset clock failure flag</param>
/// <param name="clockNotSynchronized">set or unset clock not synchronized flag</param>
/// <param name="subsecondPrecision">set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)</param>
public void SetTimeQuality(bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision)
{
IedConnection_setTimeQuality(connection, leapSecondKnown, clockFailure, clockNotSynchronized, subsecondPrecision);
}
/// <summary> /// <summary>
/// Gets the underlying MmsConnection instance. /// Gets the underlying MmsConnection instance.
/// </summary> /// </summary>
@ -1440,6 +1473,8 @@ namespace IEC61850
throw new IedConnectionException("Deleting file " + fileName + " failed", error); throw new IedConnectionException("Deleting file " + fileName + " failed", error);
} }
private IedConnection_GenericServiceHandler internalGenericServiceHandler = null;
private void nativeGenericServiceHandler(UInt32 invokeId, IntPtr parameter, int err) private void nativeGenericServiceHandler(UInt32 invokeId, IntPtr parameter, int err)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -1467,7 +1502,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_deleteFileAsync(connection, out error, filename, nativeGenericServiceHandler, GCHandle.ToIntPtr(handle)); if (internalGenericServiceHandler == null)
internalGenericServiceHandler = new IedConnection_GenericServiceHandler(nativeGenericServiceHandler);
UInt32 invokeId = IedConnection_deleteFileAsync(connection, out error, filename, internalGenericServiceHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -1570,6 +1608,12 @@ namespace IEC61850
static extern UInt32 IedConnection_getFile(IntPtr self, out int error, string fileName, InternalIedClientGetFileHandler handler, static extern UInt32 IedConnection_getFile(IntPtr self, out int error, string fileName, InternalIedClientGetFileHandler handler,
IntPtr handlerParameter); IntPtr handlerParameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setFile(IntPtr self, out int error, string sourceFilename, string destinationFilename);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setFilestoreBasepath(IntPtr self, string fileName);
public delegate bool GetFileHandler(object parameter,byte[] data); public delegate bool GetFileHandler(object parameter,byte[] data);
@ -1615,10 +1659,35 @@ namespace IEC61850
handle.Free(); handle.Free();
} }
/// <summary>
/// Set the virtual filestore basepath for the setFile service
/// </summary>
/// <param name="basepath">The new virtual filestore basepath</param>
public void SetFilestoreBasepath(string basepath)
{
IedConnection_setFilestoreBasepath(connection, basepath);
}
/// <summary>
/// Upload a file to the server.
/// </summary>
/// <param name="sourceFilename">The filename of the local (client side) file</param>
/// <param name="destinationFilename">The filename of the remote (service side) file</param>
public void SetFile(string sourceFilename, string destinationFilename)
{
int error;
IedConnection_setFile(connection, out error, sourceFilename, destinationFilename);
if (error != 0)
throw new IedConnectionException("Error uploading file", error);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private delegate bool IedConnection_GetFileAsyncHandler(UInt32 invokeId,IntPtr parameter,int err,UInt32 originalInvokeId, private delegate bool IedConnection_GetFileAsyncHandler(UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId,
IntPtr buffer,UInt32 bytesRead,bool moreFollows); IntPtr buffer, UInt32 bytesRead, [MarshalAs(UnmanagedType.I1)] bool moreFollows);
/// <summary> /// <summary>
/// Callback handler for the asynchronous get file service. Will be invoked for each chunk of received data /// Callback handler for the asynchronous get file service. Will be invoked for each chunk of received data
@ -1629,17 +1698,19 @@ namespace IEC61850
/// <param name="originalInvokeId">the invokeId of the first (file open) request</param> /// <param name="originalInvokeId">the invokeId of the first (file open) request</param>
/// <param name="buffer">the file data received with the last response, or null if no file data available</param> /// <param name="buffer">the file data received with the last response, or null if no file data available</param>
/// <param name="moreFollows">indicates that more file data follows</param> /// <param name="moreFollows">indicates that more file data follows</param>
public delegate bool GetFileAsyncHandler(UInt32 invokeId,object parameter,IedClientError err,UInt32 originalInvokeId,byte[] buffer,bool moreFollows); /// <returns>true, continue the file download when moreFollows is true, false, stop file download</returns>
public delegate bool GetFileAsyncHandler(UInt32 invokeId, object parameter, IedClientError err, UInt32 originalInvokeId, byte[] buffer, bool moreFollows);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 static extern UInt32
IedConnection_getFileAsync(IntPtr self, out int error, string fileName, IedConnection_GetFileAsyncHandler handler, IedConnection_getFileAsync(IntPtr self, out int error, string fileName, IedConnection_GetFileAsyncHandler handler,
IntPtr parameter); IntPtr parameter);
private IedConnection_GetFileAsyncHandler internalGetFileAsyncHandler = null;
private bool nativeGetFileAsyncHandler(UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId, private bool nativeGetFileAsyncHandler(UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId,
IntPtr buffer, UInt32 bytesRead, bool moreFollows) IntPtr buffer, UInt32 bytesRead, bool moreFollows)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple<GetFileAsyncHandler, object> callbackInfo = handle.Target as Tuple<GetFileAsyncHandler, object>; Tuple<GetFileAsyncHandler, object> callbackInfo = handle.Target as Tuple<GetFileAsyncHandler, object>;
@ -1647,8 +1718,6 @@ namespace IEC61850
GetFileAsyncHandler handler = callbackInfo.Item1; GetFileAsyncHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2; object handlerParameter = callbackInfo.Item2;
handle.Free();
IedClientError clientError = (IedClientError)err; IedClientError clientError = (IedClientError)err;
byte[] bytes = null; byte[] bytes = null;
@ -1660,7 +1729,28 @@ namespace IEC61850
Marshal.Copy(buffer, bytes, 0, (int)bytesRead); Marshal.Copy(buffer, bytes, 0, (int)bytesRead);
} }
return handler(invokeId, handlerParameter, clientError, originalInvokeId, bytes, moreFollows); bool retVal = handler(invokeId, handlerParameter, clientError, originalInvokeId, bytes, moreFollows);
if (clientError != IedClientError.IED_ERROR_OK)
{
handle.Free();
}
else
{
if (moreFollows == false)
{
handle.Free();
}
else
{
if (retVal == false)
{
handle.Free();
}
}
}
return retVal;
} }
/// <summary> /// <summary>
@ -1685,7 +1775,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_getFileAsync(connection, out error, fileName, nativeGetFileAsyncHandler, GCHandle.ToIntPtr(handle)); if (internalGetFileAsyncHandler == null)
internalGetFileAsyncHandler = new IedConnection_GetFileAsyncHandler(nativeGetFileAsyncHandler);
UInt32 invokeId = IedConnection_getFileAsync(connection, out error, fileName, internalGetFileAsyncHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -1696,9 +1789,6 @@ namespace IEC61850
return invokeId; return invokeId;
} }
/// <summary> /// <summary>
/// Abort (close) the connection. /// Abort (close) the connection.
/// </summary> /// </summary>
@ -1958,6 +2048,50 @@ namespace IEC61850
} }
/// <summary>
/// Create a new data set - asynchronous version.
/// </summary>
/// <description>This function creates a new data set at the server. The data set consists of the members defined
/// by the list of object references provided.</description>
/// <param name="dataSetReference">The object reference of the data set</param>
/// <param name="dataSetElements">A list of object references of the data set elements</param>
/// <param name="handler">Callback function to handle the received response or service timeout</param>
/// <param name="parameter">User provided callback parameter. Will be passed to the callback function</param>
/// <returns>the invoke ID of the sent request</returns>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public UInt32 CreateDataSetAsync(string dataSetReference, List<string> dataSetElements, GenericServiceHandler handler, object parameter)
{
int error = 0;
IntPtr linkedList = LinkedList_create();
foreach (string dataSetElement in dataSetElements)
{
IntPtr dataSetElementHandle = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(dataSetElement);
LinkedList_add(linkedList, dataSetElementHandle);
}
Tuple<GenericServiceHandler, object> callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGenericServiceHandler == null)
internalGenericServiceHandler = new IedConnection_GenericServiceHandler(nativeGenericServiceHandler);
UInt32 invokeId = IedConnection_createDataSetAsync(connection, out error, dataSetReference, linkedList, internalGenericServiceHandler, GCHandle.ToIntPtr(handle));
LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Create data set failed", error);
}
return invokeId;
}
/// <summary> /// <summary>
/// Delete a data set. /// Delete a data set.
/// </summary> /// </summary>
@ -1978,6 +2112,38 @@ namespace IEC61850
return isDeleted; return isDeleted;
} }
/// <summary>
/// Delete a data set - asynchronous version.
/// </summary>
/// <description>This function will delete a data set at the server. This function may fail if the data set is not
/// deletable.</description>
/// <param name="dataSetReference">The object reference of the data set</param>
/// <param name="handler">Callback function to handle the received response or service timeout</param>
/// <param name="parameter">User provided callback parameter. Will be passed to the callback function</param>
/// <returns>the invoke ID of the sent request</returns>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public UInt32 DeleteDataSetAsync(string dataSetReference, GenericServiceHandler handler, object parameter)
{
int error = 0;
Tuple<GenericServiceHandler, object> callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGenericServiceHandler == null)
internalGenericServiceHandler = new IedConnection_GenericServiceHandler(nativeGenericServiceHandler);
UInt32 invokeId = IedConnection_deleteDataSetAsync(connection, out error, dataSetReference, internalGenericServiceHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Delete data set failed", error);
}
return invokeId;
}
/// <summary> /// <summary>
/// Get the directory of the data set. /// Get the directory of the data set.
/// </summary> /// </summary>
@ -2027,6 +2193,77 @@ namespace IEC61850
return newList; return newList;
} }
/// <summary>
/// Get data set directory handler.
/// </summary>
/// <param name="invokeId">The invoke ID of the reqeust triggering this callback</param>
/// <param name="parameter">user provided callback parameter</param>
/// <param name="err">Error code of response or timeout error in case of a response timeout</param>
/// <param name="dataSetDirectory">the list of data set entry references</param>
/// <param name="isDeletable">data set can be deleted by a client (dynamic data set)</param>
public delegate void GetDataSetDirectoryHandler(UInt32 invokeId, object parameter, IedClientError err, List<string> dataSetDirectory, bool isDeletable);
private IedConnection_GetDataSetDirectoryHandler internalGetDataSetDirectoryHandler = null;
private void nativeGetDataSetDirectoryHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSetDirectory, bool isDeletable)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple<GetDataSetDirectoryHandler, object> callbackInfo = handle.Target as Tuple<GetDataSetDirectoryHandler, object>;
GetDataSetDirectoryHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
IntPtr element = LinkedList_getNext(dataSetDirectory);
handle.Free();
List<string> newList = new List<string>();
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(dataSetDirectory);
handler.Invoke(invokeId, handlerParameter, (IedClientError)err, newList, isDeletable);
}
/// <summary>
/// Read the data set directory - asynchronous version
/// </summary>
/// <returns>The data set directory async.</returns>
/// <param name="dataSetReference">Data set reference.</param>
/// <param name="handler">Callback function to handle the received response or service timeout</param>
/// <param name="parameter">User provided callback parameter. Will be passed to the callback function</param>
/// <returns>the invoke ID of the sent request</returns>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public UInt32 GetDataSetDirectoryAsync(string dataSetReference, GetDataSetDirectoryHandler handler, object parameter)
{
int error = 0;
Tuple<GetDataSetDirectoryHandler, object> callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetDataSetDirectoryHandler == null)
internalGetDataSetDirectoryHandler = new IedConnection_GetDataSetDirectoryHandler(nativeGetDataSetDirectoryHandler);
UInt32 invokeId = IedConnection_getDataSetDirectoryAsync(connection, out error, dataSetReference, internalGetDataSetDirectoryHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Get data set directory failed", error);
}
return invokeId;
}
/// <summary> /// <summary>
/// Read object handler. /// Read object handler.
@ -2037,6 +2274,8 @@ namespace IEC61850
/// <param name="value">The read result value or null in case of an error</param> /// <param name="value">The read result value or null in case of an error</param>
public delegate void ReadValueHandler(UInt32 invokeId,object parameter,IedClientError err,MmsValue value); public delegate void ReadValueHandler(UInt32 invokeId,object parameter,IedClientError err,MmsValue value);
private IedConnection_ReadObjectHandler internalReadObjectHandler = null;
private void nativeReadObjectHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr value) private void nativeReadObjectHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr value)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2075,7 +2314,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_readObjectAsync(connection, out error, objectReference, (int)fc, nativeReadObjectHandler, GCHandle.ToIntPtr(handle)); if (internalReadObjectHandler == null)
internalReadObjectHandler = new IedConnection_ReadObjectHandler(nativeReadObjectHandler);
UInt32 invokeId = IedConnection_readObjectAsync(connection, out error, objectReference, (int)fc, internalReadObjectHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2086,6 +2328,8 @@ namespace IEC61850
return invokeId; return invokeId;
} }
private IedConnection_GetVariableSpecificationHandler internalGetVariableSpecificationHandler = null;
private void nativeGetVariableSpecifcationHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr spec) private void nativeGetVariableSpecifcationHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr spec)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2124,7 +2368,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_getVariableSpecificationAsync(connection, out error, objectReference, (int)fc, nativeGetVariableSpecifcationHandler, GCHandle.ToIntPtr(handle)); if (internalGetVariableSpecificationHandler == null)
internalGetVariableSpecificationHandler = new IedConnection_GetVariableSpecificationHandler(nativeGetVariableSpecifcationHandler);
UInt32 invokeId = IedConnection_getVariableSpecificationAsync(connection, out error, objectReference, (int)fc, internalGetVariableSpecificationHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2135,6 +2382,8 @@ namespace IEC61850
return invokeId; return invokeId;
} }
private IedConnection_ReadDataSetHandler internalReadDataSetHandler = null;
private void nativeReadDataSetHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr nativeDataSet) private void nativeReadDataSetHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr nativeDataSet)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2188,7 +2437,10 @@ namespace IEC61850
if (dataSet != null) if (dataSet != null)
dataSetPtr = dataSet.getNativeInstance(); dataSetPtr = dataSet.getNativeInstance();
UInt32 invokeId = IedConnection_readDataSetValuesAsync(connection, out error, dataSetReference, dataSetPtr, nativeReadDataSetHandler, GCHandle.ToIntPtr(handle)); if (internalReadDataSetHandler == null)
internalReadDataSetHandler = new IedConnection_ReadDataSetHandler(nativeReadDataSetHandler);
UInt32 invokeId = IedConnection_readDataSetValuesAsync(connection, out error, dataSetReference, dataSetPtr, internalReadDataSetHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2207,6 +2459,8 @@ namespace IEC61850
/// <param name="err">Error code of response or timeout error in case of a response timeout</param> /// <param name="err">Error code of response or timeout error in case of a response timeout</param>
public delegate void WriteValueHandler(UInt32 invokeId,object parameter,IedClientError err); public delegate void WriteValueHandler(UInt32 invokeId,object parameter,IedClientError err);
private IedConnection_WriteObjectHandler internalWriteObjectHandler = null;
private void nativeWriteObjectHandler(UInt32 invokeId, IntPtr parameter, int err) private void nativeWriteObjectHandler(UInt32 invokeId, IntPtr parameter, int err)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2232,7 +2486,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_writeObjectAsync(connection, out error, objectReference, (int)fc, value.valueReference, nativeWriteObjectHandler, GCHandle.ToIntPtr(handle)); if (internalWriteObjectHandler == null)
internalWriteObjectHandler = new IedConnection_WriteObjectHandler(nativeWriteObjectHandler);
UInt32 invokeId = IedConnection_writeObjectAsync(connection, out error, objectReference, (int)fc, value.valueReference, internalWriteObjectHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2245,6 +2502,8 @@ namespace IEC61850
public delegate void GetNameListHandler(UInt32 invokeId,object parameter,IedClientError err,List<string> nameList,bool moreFollows); public delegate void GetNameListHandler(UInt32 invokeId,object parameter,IedClientError err,List<string> nameList,bool moreFollows);
private IedConnection_GetNameListHandler internalGetNameListHandler = null;
private void nativeGetNameListHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr nameList, [MarshalAs(UnmanagedType.I1)] bool moreFollows) private void nativeGetNameListHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr nameList, [MarshalAs(UnmanagedType.I1)] bool moreFollows)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2303,7 +2562,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_getServerDirectoryAsync(connection, out error, continueAfter, IntPtr.Zero, nativeGetNameListHandler, GCHandle.ToIntPtr(handle)); if (internalGetNameListHandler == null)
internalGetNameListHandler = new IedConnection_GetNameListHandler(nativeGetNameListHandler);
UInt32 invokeId = IedConnection_getServerDirectoryAsync(connection, out error, continueAfter, IntPtr.Zero, internalGetNameListHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2327,7 +2589,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_getLogicalDeviceVariablesAsync(connection, out error, ldName, continueAfter, IntPtr.Zero, nativeGetNameListHandler, GCHandle.ToIntPtr(handle)); if (internalGetNameListHandler == null)
internalGetNameListHandler = new IedConnection_GetNameListHandler(nativeGetNameListHandler);
UInt32 invokeId = IedConnection_getLogicalDeviceVariablesAsync(connection, out error, ldName, continueAfter, IntPtr.Zero, internalGetNameListHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2352,7 +2617,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_getLogicalDeviceDataSetsAsync(connection, out error, ldName, continueAfter, IntPtr.Zero, nativeGetNameListHandler, GCHandle.ToIntPtr(handle)); if (internalGetNameListHandler == null)
internalGetNameListHandler = new IedConnection_GetNameListHandler(nativeGetNameListHandler);
UInt32 invokeId = IedConnection_getLogicalDeviceDataSetsAsync(connection, out error, ldName, continueAfter, IntPtr.Zero, internalGetNameListHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2365,6 +2633,8 @@ namespace IEC61850
public delegate void QueryLogHandler(UInt32 invokeId,object parameter,IedClientError err,List<MmsJournalEntry> journalEntries,bool moreFollows); public delegate void QueryLogHandler(UInt32 invokeId,object parameter,IedClientError err,List<MmsJournalEntry> journalEntries,bool moreFollows);
private IedConnection_QueryLogHandler internalQueryLogHandler = null;
private void nativeQueryLogHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr journalEntries, private void nativeQueryLogHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr journalEntries,
[MarshalAs(UnmanagedType.I1)] bool moreFollows) [MarshalAs(UnmanagedType.I1)] bool moreFollows)
{ {
@ -2402,7 +2672,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_queryLogByTimeAsync(connection, out error, logRef, startTime, stopTime, nativeQueryLogHandler, GCHandle.ToIntPtr(handle)); if (internalQueryLogHandler == null)
internalQueryLogHandler = new IedConnection_QueryLogHandler(nativeQueryLogHandler);
UInt32 invokeId = IedConnection_queryLogByTimeAsync(connection, out error, logRef, startTime, stopTime, internalQueryLogHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2450,7 +2723,10 @@ namespace IEC61850
MmsValue entryIdValue = new MmsValue(entryID); MmsValue entryIdValue = new MmsValue(entryID);
UInt32 invokeId = IedConnection_queryLogAfterAsync(connection, out error, logRef, entryIdValue.valueReference, timestamp, nativeQueryLogHandler, GCHandle.ToIntPtr(handle)); if (internalQueryLogHandler == null)
internalQueryLogHandler = new IedConnection_QueryLogHandler(nativeQueryLogHandler);
UInt32 invokeId = IedConnection_queryLogAfterAsync(connection, out error, logRef, entryIdValue.valueReference, timestamp, internalQueryLogHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2501,6 +2777,7 @@ namespace IEC61850
} }
} }
private IedConnection_GetRCBValuesHandler internalGetRCBValuesHandler = null;
private void nativeGetRCBValuesHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr rcbPtr) private void nativeGetRCBValuesHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr rcbPtr)
{ {
@ -2527,7 +2804,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_getRCBValuesAsync(connection, out error, objectReference, updateRcb.self, nativeGetRCBValuesHandler, GCHandle.ToIntPtr(handle)); if (internalGetRCBValuesHandler == null)
internalGetRCBValuesHandler = new IedConnection_GetRCBValuesHandler(nativeGetRCBValuesHandler);
UInt32 invokeId = IedConnection_getRCBValuesAsync(connection, out error, objectReference, updateRcb.self, internalGetRCBValuesHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {
@ -2538,6 +2818,8 @@ namespace IEC61850
return invokeId; return invokeId;
} }
private IedConnection_GenericServiceHandler internalSetRcbValueHandler = null;
private void nativeSetRcbValuesHandler(UInt32 invokeId, IntPtr parameter, int err) private void nativeSetRcbValuesHandler(UInt32 invokeId, IntPtr parameter, int err)
{ {
GCHandle handle = GCHandle.FromIntPtr(parameter); GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2563,7 +2845,10 @@ namespace IEC61850
GCHandle handle = GCHandle.Alloc(callbackInfo); GCHandle handle = GCHandle.Alloc(callbackInfo);
UInt32 invokeId = IedConnection_setRCBValuesAsync(connection, out error, rcb.self, parametersMask, singleRequest, nativeSetRcbValuesHandler, GCHandle.ToIntPtr(handle)); if (internalSetRcbValueHandler == null)
internalSetRcbValueHandler = new IedConnection_GenericServiceHandler(nativeSetRcbValuesHandler);
UInt32 invokeId = IedConnection_setRCBValuesAsync(connection, out error, rcb.self, parametersMask, singleRequest, internalSetRcbValueHandler, GCHandle.ToIntPtr(handle));
if (error != 0) if (error != 0)
{ {

@ -1,7 +1,7 @@
/* /*
* IEC61850ServerAPI.cs * IEC61850ServerAPI.cs
* *
* Copyright 2016 Michael Zillgith * Copyright 2016-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -331,12 +331,19 @@ namespace IEC61850
this.parent = parent; this.parent = parent;
} }
internal Dictionary<IntPtr, ReportControlBlock> rcbs = new Dictionary<IntPtr, ReportControlBlock>();
public LogicalNode(string name, LogicalDevice parent) public LogicalNode(string name, LogicalDevice parent)
{ {
this.parent = parent; this.parent = parent;
base.self = LogicalNode_create(name, parent.self); base.self = LogicalNode_create(name, parent.self);
} }
internal void AddRcb(ReportControlBlock rcb)
{
rcbs.Add(rcb.self, rcb);
}
} }
public enum AccessPolicy { public enum AccessPolicy {
@ -1301,12 +1308,82 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ReportControlBlock_setPreconfiguredClient(IntPtr self, byte type, [Out] byte[] buf); static extern void ReportControlBlock_setPreconfiguredClient(IntPtr self, byte type, [Out] byte[] buf);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ReportControlBlock_getName(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ReportControlBlock_getRptEna(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ReportControlBlock_getRptID(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ReportControlBlock_getDataSet(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ReportControlBlock_getConfRev(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ReportControlBlock_getOptFlds(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ReportControlBlock_getBufTm(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 ReportControlBlock_getSqNum(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ReportControlBlock_getTrgOps(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 ReportControlBlock_getIntgPd(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ReportControlBlock_getGI(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ReportControlBlock_getPurgeBuf(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ReportControlBlock_getEntryId(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt64 ReportControlBlock_getTimeofEntry(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 ReportControlBlock_getResvTms(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ReportControlBlock_getResv(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ReportControlBlock_getOwner(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void Memory_free(IntPtr self);
public IntPtr self = IntPtr.Zero; public IntPtr self = IntPtr.Zero;
private string name = null;
private LogicalNode parent = null;
public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered, public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered,
string dataSetName, uint confRev, 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, confRev, trgOps, options, bufTm, intgPd); self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd);
parent.AddRcb(this);
this.parent = parent;
}
internal ReportControlBlock(IntPtr self, LogicalNode parent)
{
this.parent = parent;
this.self = self;
parent.AddRcb(this);
} }
public void SetPreconfiguredClient(byte[] clientAddress) public void SetPreconfiguredClient(byte[] clientAddress)
@ -1316,6 +1393,197 @@ namespace IEC61850
else if (clientAddress.Length == 6) else if (clientAddress.Length == 6)
ReportControlBlock_setPreconfiguredClient(self, 6, clientAddress); ReportControlBlock_setPreconfiguredClient(self, 6, clientAddress);
} }
public string Name
{
get
{
if (name == null)
{
name = Marshal.PtrToStringAnsi(ReportControlBlock_getName(self));
}
return name;
}
}
public LogicalNode Parent
{
get
{
return parent;
}
}
public bool RptEna
{
get
{
return ReportControlBlock_getRptEna(self);
}
}
public string RptID
{
get
{
IntPtr rptIdPtr = ReportControlBlock_getRptID(self);
string rptId = Marshal.PtrToStringAnsi(rptIdPtr);
Memory_free(rptIdPtr);
return rptId;
}
}
public string DataSet
{
get
{
IntPtr dataSetPtr = ReportControlBlock_getDataSet(self);
string dataSet = Marshal.PtrToStringAnsi(dataSetPtr);
Memory_free(dataSetPtr);
return dataSet;
}
}
public UInt32 ConfRev
{
get
{
return ReportControlBlock_getConfRev(self);
}
}
public ReportOptions OptFlds
{
get
{
return (ReportOptions)ReportControlBlock_getOptFlds(self);
}
}
public UInt32 BufTm
{
get
{
return ReportControlBlock_getBufTm(self);
}
}
public UInt16 SqNum
{
get
{
return ReportControlBlock_getSqNum(self);
}
}
public TriggerOptions TrgOps
{
get
{
return (TriggerOptions)ReportControlBlock_getTrgOps(self);
}
}
public UInt32 IntgPd
{
get
{
return ReportControlBlock_getIntgPd(self);
}
}
public bool GI
{
get
{
return ReportControlBlock_getGI(self);
}
}
public bool PurgeBuf
{
get
{
return ReportControlBlock_getPurgeBuf(self);
}
}
public byte[] EntryID
{
get
{
IntPtr entryIdPtr = ReportControlBlock_getEntryId(self);
if (entryIdPtr != IntPtr.Zero)
{
byte[] entryId = null;
MmsValue octetStringVal = new MmsValue(entryIdPtr, true);
entryId = octetStringVal.getOctetString();
octetStringVal.Dispose();
return entryId;
}
else
return null;
}
}
public UInt64 TimeofEntry
{
get
{
return ReportControlBlock_getTimeofEntry(self);
}
}
public UInt16 ResvTms
{
get
{
return ReportControlBlock_getResvTms(self);
}
}
public bool Resv
{
get
{
return ReportControlBlock_getResv(self);
}
}
public byte[] Owner
{
get
{
IntPtr mmsValuePtr = ReportControlBlock_getOwner(self);
if (mmsValuePtr != IntPtr.Zero)
{
byte[] owner = null;
MmsValue octetStringVal = new MmsValue(mmsValuePtr, true);
owner = octetStringVal.getOctetString();
return owner;
}
else
return null;
}
}
} }
/// <summary> /// <summary>
@ -1691,6 +1959,55 @@ namespace IEC61850
public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter); public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
/// <summary>
/// Report control block event types
/// </summary>
public enum RCBEventType
{
/// <summary>
/// parameter read by client (not implemented).
/// </summary>
GET_PARAMETER = 0,
/// <summary>
/// parameter set by client.
/// </summary>
SET_PARAMETER = 1,
/// <summary>
/// reservation canceled.
/// </summary>
UNRESERVED = 2,
/// <summary>
/// reservation
/// </summary>
RESERVED = 3,
/// <summary>
/// RCB enabled
/// </summary>
ENABLED = 4,
/// <summary>
/// RCB disabled
/// </summary>
DISABLED = 5,
/// <summary>
/// GI report triggered
/// </summary>
GI = 6,
/// <summary>
/// Purge buffer procedure executed
/// </summary>
PURGEBUF = 7,
/// <summary>
/// Report buffer overflow
/// </summary>
OVERFLOW = 8,
/// <summary>
/// A new report was created and inserted into the buffer
/// </summary>
REPORT_CREATED = 9
}
public delegate void RCBEventHandler(object parameter, ReportControlBlock rcb, ClientConnection con, RCBEventType eventType, string parameterName, MmsDataAccessError serviceError);
public delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value, public delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value,
ClientConnection connection, object parameter); ClientConnection connection, object parameter);
@ -1841,6 +2158,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute); static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedServer_getFunctionalConstrainedData(IntPtr self, IntPtr dataObject, int fc);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int InternalControlPerformCheckHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck); private delegate int InternalControlPerformCheckHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck);
@ -1917,6 +2237,12 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_setGoCBHandler(IntPtr self, InternalGoCBEventHandler handler, IntPtr parameter); static extern void IedServer_setGoCBHandler(IntPtr self, InternalGoCBEventHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalRCBEventHandler(IntPtr paramter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter);
private IntPtr self = IntPtr.Zero; private IntPtr self = IntPtr.Zero;
private InternalControlHandler internalControlHandlerRef = null; private InternalControlHandler internalControlHandlerRef = null;
@ -2456,6 +2782,22 @@ namespace IEC61850
return null; return null;
} }
/// <summary>
/// Get the MmsValue object related to a functional constrained data object (FCD)
/// </summary>
/// <param name="dataObject">the data object to specify the FCD</param>
/// <param name="fc">the FC to specify the FCD</param>
/// <returns>FCDO corresponding MmsValue object cached by the server</returns>
public MmsValue GetFunctionalConstrainedData(DataObject dataObject, FunctionalConstraint fc)
{
IntPtr mmsValuePtr = IedServer_getFunctionalConstrainedData(self, dataObject.self, (int)fc);
if (mmsValuePtr != IntPtr.Zero)
return new MmsValue(mmsValuePtr);
else
return null;
}
/// <summary> /// <summary>
/// Enable all GOOSE control blocks. /// Enable all GOOSE control blocks.
/// </summary> /// </summary>
@ -2561,6 +2903,69 @@ namespace IEC61850
} }
} }
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ReportControlBlock_getParent(IntPtr self);
private RCBEventHandler rcbEventHandler = null;
private object rcbEventHandlerParameter = null;
private InternalRCBEventHandler internalRCBEventHandler = null;
private void InternalRCBEventHandlerImplementation(IntPtr parameter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError)
{
if (rcbEventHandler != null)
{
ClientConnection con = null;
if (connection != IntPtr.Zero)
{
this.clientConnections.TryGetValue(connection, out con);
}
ReportControlBlock reportControlBlock = null;
if (rcb != IntPtr.Zero)
{
IntPtr lnPtr = ReportControlBlock_getParent(rcb);
if (lnPtr != IntPtr.Zero)
{
ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(lnPtr);
if (lnModelNode != null)
{
LogicalNode ln = lnModelNode as LogicalNode;
if (ln.rcbs.TryGetValue(rcb, out reportControlBlock) == false)
{
reportControlBlock = new ReportControlBlock(rcb, ln);
}
}
}
}
rcbEventHandler.Invoke(rcbEventHandlerParameter, reportControlBlock, con, (RCBEventType)eventType, parameterName, (MmsDataAccessError)serviceError);
}
}
/// <summary>
/// Set a callback handler for RCB events
/// </summary>
/// <param name="handler">the callback handler</param>
/// <param name="parameter">user provided parameter that is passed to the callback handler</param>
public void SetRCBEventHandler(RCBEventHandler handler, object parameter)
{
rcbEventHandler = handler;
rcbEventHandlerParameter = parameter;
if (internalRCBEventHandler == null)
{
internalRCBEventHandler = new InternalRCBEventHandler(InternalRCBEventHandlerImplementation);
IedServer_setRCBEventHandler(self, internalRCBEventHandler, IntPtr.Zero);
}
}
} }
} }

@ -124,6 +124,13 @@ namespace IEC61850.Server
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_useIntegratedGoosePublisher(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable); static extern void IedServerConfig_useIntegratedGoosePublisher(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool IedServerConfig_getSyncIntegrityReportTimes(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_setSyncIntegrityReportTimes(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
internal IntPtr self; internal IntPtr self;
public IedServerConfig() public IedServerConfig()
@ -351,6 +358,27 @@ namespace IEC61850.Server
} }
} }
/// <summary>
/// Enable/Disable synchoronized integrity report times (disabled by default)
/// </summary>
/// <remarks>
/// When this flag is enabled the integrity report generation times are
/// aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
/// integrity interval.
/// </remarks>
/// <value><c>true</c> if sync integrity report times; otherwise, <c>false</c>.</value>
public bool SyncIntegrityReportTimes
{
get
{
return IedServerConfig_getSyncIntegrityReportTimes(self);
}
set
{
IedServerConfig_setSyncIntegrityReportTimes(self, value);
}
}
/// <summary> /// <summary>
/// Releases all resource used by the <see cref="IEC61850.Server.IedServerConfig"/> object. /// Releases all resource used by the <see cref="IEC61850.Server.IedServerConfig"/> object.
/// </summary> /// </summary>

@ -174,6 +174,12 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 MmsValue_getOctetStringMaxSize(IntPtr self); static extern UInt16 MmsValue_getOctetStringMaxSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern byte MmsValue_getOctetStringOctet(IntPtr self, int pos);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsValue_setOctetStringOctet(IntPtr self, int pos, byte value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_getOctetStringBuffer(IntPtr self); static extern IntPtr MmsValue_getOctetStringBuffer(IntPtr self);
@ -518,6 +524,11 @@ namespace IEC61850
throw new MmsValueException ("Operation not supported for this type"); throw new MmsValueException ("Operation not supported for this type");
} }
/// <summary>
/// Sets the value of an octet string by a byte array
/// </summary>
/// <param name="octetString">Byte array containing the bytes of the octet string.</param>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type or the byte array is too large.</exception>
public void setOctetString (byte[] octetString) public void setOctetString (byte[] octetString)
{ {
if (GetType () == MmsType.MMS_OCTET_STRING) { if (GetType () == MmsType.MMS_OCTET_STRING) {
@ -531,6 +542,26 @@ namespace IEC61850
throw new MmsValueException ("Operation not supported for this type"); throw new MmsValueException ("Operation not supported for this type");
} }
/// <summary>
/// Gets the octet string octet.
/// </summary>
/// <returns>The octet string octet.</returns>
/// <param name="pos">Position of the octet in the octet string.</param>
public byte GetOctetStringOctet(int pos)
{
return MmsValue_getOctetStringOctet(valueReference, pos);
}
/// <summary>
/// Sets the octet string octet.
/// </summary>
/// <param name="pos">Position of the octet in the octet string.</param>
/// <param name="value">The octet string octet.</param>
public void SetOctetStringOctet(int pos, byte value)
{
MmsValue_setOctetStringOctet(valueReference, pos, value);
}
/// <summary> /// <summary>
/// Get an element of an array or structure /// Get an element of an array or structure
/// </summary> /// </summary>

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -7,7 +7,8 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>client_example_async</RootNamespace> <RootNamespace>client_example_async</RootNamespace>
<AssemblyName>client_example_async</AssemblyName> <AssemblyName>client_example_async</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -18,6 +19,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole> <Externalconsole>true</Externalconsole>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType> <DebugType>full</DebugType>
@ -41,4 +43,7 @@
<Name>IEC61850.NET</Name> <Name>IEC61850.NET</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
</Project> </Project>

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

@ -4,6 +4,7 @@ using System.Collections.Generic;
using IEC61850.Client; using IEC61850.Client;
using IEC61850.Common; using IEC61850.Common;
using System.IO; using System.IO;
using System.Threading;
namespace files namespace files
{ {
@ -13,18 +14,20 @@ namespace files
/// </summary> /// </summary>
class MainClass class MainClass
{ {
public static void printFiles (IedConnection con, string prefix, string parent) public static void printFiles(IedConnection con, string prefix, string parent)
{ {
bool moreFollows = false; bool moreFollows = false;
List<FileDirectoryEntry> files = con.GetFileDirectoryEx (parent, null, out moreFollows); List<FileDirectoryEntry> files = con.GetFileDirectoryEx(parent, null, out moreFollows);
foreach (FileDirectoryEntry file in files) { foreach (FileDirectoryEntry file in files)
Console.WriteLine (prefix + file.GetFileName () + "\t" + file.GetFileSize () + "\t" + {
MmsValue.MsTimeToDateTimeOffset (file.GetLastModified ())); Console.WriteLine(prefix + file.GetFileName() + "\t" + file.GetFileSize() + "\t" +
MmsValue.MsTimeToDateTimeOffset(file.GetLastModified()));
if (file.GetFileName ().EndsWith ("/")) { if (file.GetFileName().EndsWith("/"))
printFiles (con, prefix + " ", parent + file.GetFileName ()); {
printFiles(con, prefix + " ", parent + file.GetFileName());
} }
} }
@ -32,65 +35,100 @@ namespace files
Console.WriteLine("-- MORE FILES AVAILABLE --"); Console.WriteLine("-- MORE FILES AVAILABLE --");
} }
static bool getFileHandler (object parameter, byte[] data) static bool getFileHandler(object parameter, byte[] data)
{ {
Console.WriteLine ("received " + data.Length + " bytes"); Console.WriteLine("received " + data.Length + " bytes");
BinaryWriter binWriter = (BinaryWriter)parameter; BinaryWriter binWriter = (BinaryWriter)parameter;
binWriter.Write (data); binWriter.Write(data);
return true; return true;
} }
public static void Main (string[] args) public static void Main(string[] args)
{ {
IedConnection con = new IedConnection (); IedConnection con = new IedConnection();
string hostname; string hostname;
if (args.Length > 0) if (args.Length > 0)
hostname = args [0]; hostname = args[0];
else else
hostname = "127.0.0.1"; hostname = "127.0.0.1";
Console.WriteLine ("Connect to " + hostname); Console.WriteLine("Connect to " + hostname);
try { try
con.Connect (hostname, 102); {
con.Connect(hostname, 102);
Console.WriteLine ("Files in server root directory:"); Console.WriteLine("Files in server root directory:");
List<string> serverDirectory = con.GetServerDirectory (true); List<string> serverDirectory = con.GetServerDirectory(true);
foreach (string entry in serverDirectory) { foreach (string entry in serverDirectory)
Console.WriteLine (entry); {
Console.WriteLine(entry);
} }
Console.WriteLine (); Console.WriteLine();
Console.WriteLine ("File directory tree at server:"); Console.WriteLine("File directory tree at server:");
printFiles (con, "", ""); printFiles(con, "", "");
Console.WriteLine (); Console.WriteLine();
string filename = "IEDSERVER.BIN"; string filename = "IEDSERVER.BIN";
Console.WriteLine ("Download file " + filename); Console.WriteLine("Download file " + filename);
/* Download file from server and write it to a new local file */ /* Download file from server and write it to a new local file */
FileStream fs = new FileStream (filename, FileMode.Create); FileStream fs = new FileStream(filename, FileMode.Create);
BinaryWriter w = new BinaryWriter (fs); BinaryWriter w = new BinaryWriter(fs);
bool fileDownloadFinished = false;
//con.GetFile (filename, new IedConnection.GetFileHandler (getFileHandler), w);
con.GetFile (filename, new IedConnection.GetFileHandler (getFileHandler), w); /// COMMENT SECTION WHEN USING SYNC VERSION -->
fs.Close (); con.GetFileAsync(filename, delegate (UInt32 invokeId, object parameter, IedClientError err, UInt32 originalInvokeId, byte[] buffer, bool moreFollows)
{
if (err == IedClientError.IED_ERROR_OK)
{
Console.WriteLine("Received new file segment with " + buffer.Length.ToString() + " bytes, more-follows: " + moreFollows.ToString());
w.Write(buffer);
con.Abort (); if (moreFollows == false)
} catch (IedConnectionException e) { fileDownloadFinished = true;
Console.WriteLine (e.Message); }
else
{
Console.WriteLine("File download error: " + err.ToString());
fileDownloadFinished = true;
}
return true;
}, w);
while (fileDownloadFinished == false)
{
Thread.Sleep(500);
}
/// <-- COMMENT SECTION WHEN USING SYNC VERSION
fs.Close();
con.Abort();
}
catch (IedConnectionException e)
{
Console.WriteLine(e.Message);
} }
// release all resources - do NOT use the object after this call!! // release all resources - do NOT use the object after this call!!
con.Dispose (); con.Dispose();
} }
} }
} }

@ -67,6 +67,34 @@ namespace server1
}, null); }, null);
iedServer.SetRCBEventHandler(delegate (object parameter, ReportControlBlock rcb, ClientConnection con, RCBEventType eventType, string parameterName, MmsDataAccessError serviceError)
{
Console.WriteLine("RCB: " + rcb.Parent.GetObjectReference() + "." + rcb.Name + " event: " + eventType.ToString());
if (con != null)
{
Console.WriteLine(" caused by client " + con.GetPeerAddress());
}
else
{
Console.WriteLine(" client = null");
}
if (eventType == RCBEventType.ENABLED)
{
Console.WriteLine(" RptID: " + rcb.RptID);
Console.WriteLine(" DatSet: " + rcb.DataSet);
Console.WriteLine(" TrgOps: " + rcb.TrgOps.ToString());
}
if ((eventType == RCBEventType.SET_PARAMETER) || (eventType == RCBEventType.GET_PARAMETER))
{
Console.WriteLine(" param: " + parameterName);
Console.WriteLine(" result: " + serviceError.ToString());
}
}, null);
iedServer.Start(102); iedServer.Start(102);
if (iedServer.IsRunning()) if (iedServer.IsRunning())

@ -24,6 +24,7 @@ print_help()
printf("-x <filename> delete file\n"); printf("-x <filename> delete file\n");
printf("-j <domainName/journalName> read journal\n"); printf("-j <domainName/journalName> read journal\n");
printf("-v <variable list_name> read domain variable list\n"); printf("-v <variable list_name> read domain variable list\n");
printf("-z <variable list_name> get domain variable list directory\n");
printf("-y <index> array index for read access\n"); printf("-y <index> array index for read access\n");
printf("-m print raw MMS messages\n"); printf("-m print raw MMS messages\n");
} }
@ -123,10 +124,11 @@ int main(int argc, char** argv)
int printRawMmsMessages = 0; int printRawMmsMessages = 0;
int deleteFile = 0; int deleteFile = 0;
int readVariableList = 0; int readVariableList = 0;
int readDataSetDirectory = 0;
int c; int c;
while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:")) != -1) while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:z:")) != -1) {
switch (c) { switch (c) {
case 'm': case 'm':
printRawMmsMessages = 1; printRawMmsMessages = 1;
@ -167,6 +169,11 @@ int main(int argc, char** argv)
readVariableList = 1; readVariableList = 1;
variableName = StringUtils_copyString(optarg); variableName = StringUtils_copyString(optarg);
break; break;
case 'z':
readDataSetDirectory = 1;
variableName = StringUtils_copyString(optarg);
break;
case 'f': case 'f':
showFileList = 1; showFileList = 1;
break; break;
@ -192,12 +199,13 @@ int main(int argc, char** argv)
print_help(); print_help();
return 0; return 0;
} }
}
MmsConnection con = MmsConnection_create(); MmsConnection con = MmsConnection_create();
MmsError error; MmsError error;
/* Set maximum MMS PDU size (local detail) to 2000 byte */ /* Set maximum MMS PDU size (local detail) */
MmsConnection_setLocalDetail(con, maxPduSize); MmsConnection_setLocalDetail(con, maxPduSize);
if (printRawMmsMessages) if (printRawMmsMessages)
@ -404,6 +412,41 @@ int main(int argc, char** argv)
printf("Reading VMD scope variable list not yet supported!\n"); printf("Reading VMD scope variable list not yet supported!\n");
} }
if (readDataSetDirectory) {
if (readWriteHasDomain) {
bool deletable = false;
LinkedList varListDir = MmsConnection_readNamedVariableListDirectory(con, &error, domainName, variableName, &deletable);
if (error != MMS_ERROR_NONE) {
printf("Reading variable list directory failed: (ERROR %i)\n", error);
}
else {
LinkedList varListElem = LinkedList_getNext(varListDir);
int listIdx = 0;
while (varListElem) {
MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(varListElem);
if (varAccessSpec->arrayIndex)
printf("[%i] %s/%s(%i)%s\n", listIdx, varAccessSpec->domainId, varAccessSpec->itemId, varAccessSpec->arrayIndex, varAccessSpec->componentName == NULL ? "" : varAccessSpec->componentName);
else
printf("[%i] %s/%s\n", listIdx, varAccessSpec->domainId, varAccessSpec->itemId);
listIdx++;
varListElem = LinkedList_getNext(varListElem);
}
printf("Read SUCCESS\n");
}
}
else
printf("Reading VMD scope variable list not yet supported!\n");
}
if (showFileList) { if (showFileList) {
char lastName[300]; char lastName[300];
lastName[0] = 0; lastName[0] = 0;

@ -79,6 +79,27 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected,
printf("Connection closed\n"); printf("Connection closed\n");
} }
static void
rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError)
{
printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event);
if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) {
printf(" param: %s\n", parameterName);
printf(" result: %i\n", serviceError);
}
if (event == RCB_EVENT_ENABLE) {
char* rptId = ReportControlBlock_getRptID(rcb);
printf(" rptID: %s\n", rptId);
char* dataSet = ReportControlBlock_getDataSet(rcb);
printf(" datSet: %s\n", dataSet);
free(rptId);
free(dataSet);
}
}
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
@ -136,6 +157,8 @@ main(int argc, char** argv)
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL);
/* By default access to variables with FC=DC and FC=CF is not allowed. /* By default access to variables with FC=DC and FC=CF is not allowed.
* This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used
* by iec61850_client_example1. * by iec61850_client_example1.

@ -49,6 +49,9 @@ int main(int argc, char** argv) {
DataAttribute* temperatureValue = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "instMag.f"); DataAttribute* temperatureValue = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "instMag.f");
DataAttribute* temperatureTimestamp = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "t"); DataAttribute* temperatureTimestamp = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "t");
LogicalNode* ggio1 = LogicalNode_create("GGIO1", lDevice1);
DataObject* ggio1_anIn1 = CDC_APC_create("AnOut1", (ModelNode*)ggio1, 0, CDC_CTL_MODEL_HAS_CANCEL | CDC_CTL_MODEL_SBO_ENHANCED, false);
DataSet* dataSet = DataSet_create("events", lln0); DataSet* dataSet = DataSet_create("events", lln0);
DataSetEntry_create(dataSet, "TTMP1$MX$TmpSv$instMag$f", -1, NULL); DataSetEntry_create(dataSet, "TTMP1$MX$TmpSv$instMag$f", -1, NULL);

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 3.5.1)
# automagically detect if we should cross-compile # automagically detect if we should cross-compile
if(DEFINED ENV{TOOLCHAIN}) if(DEFINED ENV{TOOLCHAIN})

@ -357,6 +357,18 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
return self; return self;
} }
void
Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode)
{
/* not implemented */
}
void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
/* not implemented */
}
void void
Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType) Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType)
{ {

@ -1,7 +1,7 @@
/* /*
* ethernet_linux.c * ethernet_linux.c
* *
* Copyright 2013 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -136,22 +136,6 @@ getInterfaceIndex(int sock, const char* deviceName)
int interfaceIndex = ifr.ifr_ifindex; int interfaceIndex = ifr.ifr_ifindex;
if (ioctl (sock, SIOCGIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Problem getting device flags");
return -1;
}
/* replace IFF_ALLMULTI by IFF_PROMISC to also receive unicast messages for other nodes */
ifr.ifr_flags |= IFF_ALLMULTI;
if (ioctl (sock, SIOCSIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting device to promiscuous mode failed");
return -1;
}
return interfaceIndex; return interfaceIndex;
} }
@ -179,7 +163,6 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
} }
} }
EthernetSocket EthernetSocket
Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress) Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
{ {
@ -208,7 +191,7 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
ethernetSocket->socketAddress.sll_ifindex = ifcIdx; ethernetSocket->socketAddress.sll_ifindex = ifcIdx;
ethernetSocket->socketAddress.sll_hatype = ARPHRD_ETHER; ethernetSocket->socketAddress.sll_hatype = ARPHRD_ETHER;
ethernetSocket->socketAddress.sll_pkttype = PACKET_OTHERHOST; ethernetSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;
ethernetSocket->socketAddress.sll_halen = ETH_ALEN; ethernetSocket->socketAddress.sll_halen = ETH_ALEN;
@ -223,6 +206,82 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
return ethernetSocket; return ethernetSocket;
} }
void
Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode)
{
if (ethSocket) {
if (mode == ETHERNET_SOCKET_MODE_PROMISC) {
struct ifreq ifr;
if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Problem getting device flags");
return;
}
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting device to promiscuous mode failed");
return;
}
}
else if (mode == ETHERNET_SOCKET_MODE_ALL_MULTICAST) {
struct ifreq ifr;
if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Problem getting device flags");
return;
}
ifr.ifr_flags |= IFF_ALLMULTI;
if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting device to promiscuous mode failed");
return;
}
}
else if (mode == ETHERNET_SOCKET_MODE_HOST_ONLY) {
ethSocket->socketAddress.sll_pkttype = PACKET_HOST;
}
else if (mode == ETHERNET_SOCKET_MODE_MULTICAST) {
ethSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;
}
}
}
void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
struct packet_mreq mreq;
mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex;
mreq.mr_alen = ETH_ALEN;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_address[0] = multicastAddress[0];
mreq.mr_address[1] = multicastAddress[1];
mreq.mr_address[2] = multicastAddress[2];
mreq.mr_address[3] = multicastAddress[3];
mreq.mr_address[4] = multicastAddress[4];
mreq.mr_address[5] = multicastAddress[5];
int res = setsockopt(ethSocket->rawSocket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (res != 0) {
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting multicast address failed");
}
}
void void
Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType)
{ {

@ -378,6 +378,18 @@ Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize)
printf("Error sending the packet: %s\n", pcap_geterr(ethSocket->rawSocket)); printf("Error sending the packet: %s\n", pcap_geterr(ethSocket->rawSocket));
} }
void
Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode)
{
/* not implemented */
}
void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
/* not implemented */
}
void void
Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType)
{ {
@ -478,6 +490,16 @@ Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize)
{ {
} }
void
Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode)
{
}
void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
}
void void
Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType)
{ {

@ -114,7 +114,7 @@ FileSystem_openDirectory(char* directoryName)
DirectoryHandle handle = NULL; DirectoryHandle handle = NULL;
if (dirHandle != NULL) { if (dirHandle != NULL) {
handle = GLOBAL_MALLOC(sizeof(struct sDirectoryHandle)); handle = (DirectoryHandle) GLOBAL_MALLOC(sizeof(struct sDirectoryHandle));
handle->handle = dirHandle; handle->handle = dirHandle;
} }

@ -1,25 +1,10 @@
/* /*
* hal_base.h * hal_base.h
* *
* Copyright 2018 MZ Automation GmbH * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of Platform Abstraction Layer (libpal) * This file is part of Platform Abstraction Layer (libpal)
* for libiec61850 and lib60870. * for libiec61850, libmms, and lib60870.
*
* libpal 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.
*
* libpal 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 libpal. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef HAL_INC_HAL_BASE_H_ #ifndef HAL_INC_HAL_BASE_H_

@ -1,24 +1,10 @@
/* /*
* ethernet_hal.h * ethernet_hal.h
* *
* Copyright 2013, 2014 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef ETHERNET_HAL_H_ #ifndef ETHERNET_HAL_H_
@ -36,7 +22,7 @@ extern "C" {
*/ */
/** /**
* @defgroup HAL_ETHERNET Direct access to the ethernet layer (optional - required by GOOSE and Sampled Values) * @defgroup HAL_ETHERNET Direct access to the Ethernet layer (optional - required by GOOSE and Sampled Values)
* *
* @{ * @{
*/ */
@ -47,9 +33,16 @@ extern "C" {
*/ */
typedef struct sEthernetSocket* EthernetSocket; typedef struct sEthernetSocket* EthernetSocket;
/** Opaque reference for a set of ethernet socket handles */ /** Opaque reference for a set of Ethernet socket handles */
typedef struct sEthernetHandleSet* EthernetHandleSet; typedef struct sEthernetHandleSet* EthernetHandleSet;
typedef enum {
ETHERNET_SOCKET_MODE_PROMISC, /**<< receive all Ethernet messages */
ETHERNET_SOCKET_MODE_ALL_MULTICAST, /**<< receive all multicast messages */
ETHERNET_SOCKET_MODE_MULTICAST, /**<< receive only specific multicast messages */
ETHERNET_SOCKET_MODE_HOST_ONLY /**<< receive only messages for the host */
} EthernetSocketMode;
/** /**
* \brief Create a new connection handle set (EthernetHandleSet) * \brief Create a new connection handle set (EthernetHandleSet)
* *
@ -115,7 +108,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr);
* destination MAC address. * destination MAC address.
* *
* \param interfaceId the ID of the Ethernet interface * \param interfaceId the ID of the Ethernet interface
* \param destAddress byte array that contains the Ethernet MAC address * \param destAddress byte array that contains the Ethernet destination MAC address for sending
*/ */
PAL_API EthernetSocket PAL_API EthernetSocket
Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress); Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress);
@ -131,9 +124,35 @@ Ethernet_destroySocket(EthernetSocket ethSocket);
PAL_API void PAL_API void
Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize); Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize);
/*
* \brief set the receive mode of the Ethernet socket
*
* NOTE: When not implemented the the implementation has to be able to receive
* all messages required by GOOSE and/or SV (usually multicast addresses).
*
* \param ethSocket the ethernet socket handle
* \param mode the mode of the socket
*/
PAL_API void
Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode);
/**
* \brief Add a multicast address to be received by the Ethernet socket
*
* Used when mode is ETHERNET_SOCKET_MODE_MULTICAST
*
* \param ethSocket the ethernet socket handle
* \param multicastAddress the multicast Ethernet address (this has to be a byte buffer of at least 6 byte)
*/
PAL_API void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress);
/* /*
* \brief set a protocol filter for the specified etherType * \brief set a protocol filter for the specified etherType
* *
* NOTE: Implementation is not required but can improve the performance when the ethertype
* filtering can be done on OS/network stack layer.
*
* \param ethSocket the ethernet socket handle * \param ethSocket the ethernet socket handle
* \param etherType the ether type of messages to accept * \param etherType the ether type of messages to accept
*/ */

@ -1,24 +1,10 @@
/* /*
* filesystem_hal.h * filesystem_hal.h
* *
* Copyright 2014 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef FILESYSTEM_HAL_H_ #ifndef FILESYSTEM_HAL_H_

@ -1,24 +1,10 @@
/* /*
* hal_serial.h * hal_serial.h
* *
* Copyright 2017 MZ Automation GmbH * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of lib60870-C * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* lib60870-C is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* lib60870-C is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with lib60870-C. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_ #ifndef SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_

@ -1,24 +1,10 @@
/* /*
* socket_hal.h * socket_hal.h
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef SOCKET_HAL_H_ #ifndef SOCKET_HAL_H_
@ -242,6 +228,20 @@ TcpSocket_create(void);
PAL_API void PAL_API void
Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs); Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs);
/**
* \brief bind a socket to a particular IP address and port (for TcpSocket)
*
* NOTE: Don't use the socket when this functions returns false!
*
* \param self the client socket instance
* \param srcAddress the local IP address or hostname as C string
* \param srcPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
*
* \return true in case of success, false otherwise
*/
PAL_API bool
Socket_bind(Socket self, const char* srcAddress, int srcPort);
/** /**
* \brief connect to a server * \brief connect to a server
* *

@ -3,24 +3,10 @@
* *
* Multi-threading abstraction layer * Multi-threading abstraction layer
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef THREAD_HAL_H_ #ifndef THREAD_HAL_H_

@ -1,24 +1,10 @@
/* /*
* time.c * time.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef HAL_C_ #ifndef HAL_C_

@ -1,24 +1,10 @@
/* /*
* lib_memory.h * lib_memory.h
* *
* Copyright 2014 Michael Zillgith * Copyright 2014-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef MEMORY_H_ #ifndef MEMORY_H_
@ -40,6 +26,8 @@
extern "C" { extern "C" {
#endif #endif
#include <stdlib.h>
typedef void typedef void
(*MemoryExceptionHandler) (void* parameter); (*MemoryExceptionHandler) (void* parameter);

@ -1,24 +1,10 @@
/* /*
* platform_endian.h * platform_endian.h
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#ifndef ENDIAN_H_ #ifndef ENDIAN_H_

@ -3,7 +3,7 @@
* *
* TLS Configuration API for protocol stacks using TCP/IP * TLS Configuration API for protocol stacks using TCP/IP
* *
* Copyright 2017-2018 MZ Automation GmbH * Copyright 2017-2021 Michael Zillgith
* *
* Abstraction layer for configuration of different TLS implementations * Abstraction layer for configuration of different TLS implementations
* *
@ -39,6 +39,8 @@ typedef struct sTLSConfiguration* TLSConfiguration;
/** /**
* \brief Create a new \ref TLSConfiguration object to represent TLS configuration and certificates * \brief Create a new \ref TLSConfiguration object to represent TLS configuration and certificates
* *
* WARNING: Configuration cannot be changed after using for the first time.
*
* \return the new TLS configuration * \return the new TLS configuration
*/ */
PAL_API TLSConfiguration PAL_API TLSConfiguration
@ -88,21 +90,64 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate,
PAL_API bool PAL_API bool
TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename); TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename);
/**
* \brief Set the own private key from a byte buffer
*
* \param key the private key to use
* \param keyLen the length of the key
* \param password the password of the key or null if the key is not password protected
*
* \return true, when the key was set, false otherwise (e.g. unknown key format)
*/
PAL_API bool PAL_API bool
TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword); TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword);
/**
* \brief Set the own private key from a key file
*
* \param filename filename/path of the key file
* \param password the password of the key or null if the key is not password protected
*
* \return true, when the key was set, false otherwise (e.g. unknown key format)
*/
PAL_API bool PAL_API bool
TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword); TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword);
/**
* Add a certificate to the list of allowed peer certificates from a byte buffer
*
* \param certificate the certificate buffer
* \param certLen the length of the certificate buffer
* \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
*/
PAL_API bool PAL_API bool
TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
/**
* \brief Add a certificate to the list of allowed peer certificates
*
* \param filename filename of the certificate file
* \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
*/
PAL_API bool PAL_API bool
TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename); TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename);
/**
* \brief Add a CA certificate used to validate peer certificates from a byte buffer
*
* \param certificate the certificate buffer
* \param certLen the length of the certificate buffer
* \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
*/
PAL_API bool PAL_API bool
TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
/**
* \brief Add a CA certificate used to validate peer certificates from a file
*
* \param filename filename of the certificate file
* \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
*/
PAL_API bool PAL_API bool
TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename); TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename);
@ -116,6 +161,51 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
PAL_API void PAL_API void
TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs); TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs);
typedef enum {
TLS_VERSION_NOT_SELECTED = 0,
TLS_VERSION_SSL_3_0 = 3,
TLS_VERSION_TLS_1_0 = 4,
TLS_VERSION_TLS_1_1 = 5,
TLS_VERSION_TLS_1_2 = 6,
TLS_VERSION_TLS_1_3 = 7
} TLSConfigVersion;
/**
* \brief Set minimal allowed TLS version to use
*/
PAL_API void
TLSConfiguration_setMinTlsVersion(TLSConfiguration self, TLSConfigVersion version);
/**
* \brief Set maximal allowed TLS version to use
*/
PAL_API void
TLSConfiguration_setMaxTlsVersion(TLSConfiguration self, TLSConfigVersion version);
/**
* \brief Add a CRL (certificate revocation list) from buffer
*
* \param crl the buffer containing the CRL
* \param crlLen the length of the CRL buffer
* \return true, when the CRL was imported, false otherwise (e.g. unknown format)
*/
PAL_API bool
TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen);
/**
* \brief Add a CRL (certificate revocation list) from a file
*
* \param filename filename of the CRL file
* \return true, when the CRL was imported, false otherwise (e.g. unknown format)
*/
PAL_API bool
TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename);
/**
* Release all resource allocated by the TLSConfiguration instance
*
* NOTE: Do not use the object after calling this function!
*/
PAL_API void PAL_API void
TLSConfiguration_destroy(TLSConfiguration self); TLSConfiguration_destroy(TLSConfiguration self);

@ -3,7 +3,7 @@
* *
* TLS socket API for protocol libraries using TCP/IP * TLS socket API for protocol libraries using TCP/IP
* *
* Copyright 2017-2020 Michael Zillgith, MZ Automation GmbH * Copyright 2017-2021 Michael Zillgith, MZ Automation GmbH
* *
* Abstraction layer for different TLS implementations * Abstraction layer for different TLS implementations
* *

@ -1,24 +1,10 @@
/* /*
* lib_memory.c * lib_memory.c
* *
* Copyright 2014-2018 Michael Zillgith * Copyright 2014-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include <stdlib.h> #include <stdlib.h>

@ -1,24 +1,10 @@
/* /*
* serial_port_linux.c * serial_port_linux.c
* *
* Copyright 2017 MZ Automation GmbH * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of lib60870-C * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* lib60870-C is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* lib60870-C is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with lib60870-C. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include "lib_memory.h" #include "lib_memory.h"

@ -1,24 +1,10 @@
/* /*
* serial_port_win32.c * serial_port_win32.c
* *
* Copyright 2017 MZ Automation GmbH * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of lib60870-C * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* lib60870-C is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* lib60870-C is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with lib60870-C. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include <stdint.h> #include <stdint.h>

@ -1,25 +1,10 @@
/* /*
* socket_bsd.c * socket_bsd.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* contributions by Michael Clausen (School of engineering Valais).
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include "hal_socket.h" #include "hal_socket.h"
@ -199,7 +184,7 @@ Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count)
} }
static bool static bool
prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr) prepareAddress(const char* address, int port, struct sockaddr_in* sockaddr)
{ {
memset((char *) sockaddr , 0, sizeof(struct sockaddr_in)); memset((char *) sockaddr , 0, sizeof(struct sockaddr_in));
@ -246,7 +231,7 @@ TcpServerSocket_create(const char* address, int port)
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) {
struct sockaddr_in serverAddress; struct sockaddr_in serverAddress;
if (!prepareServerAddress(address, port, &serverAddress)) { if (!prepareAddress(address, port, &serverAddress)) {
close(fd); close(fd);
return NULL; return NULL;
} }
@ -257,7 +242,7 @@ TcpServerSocket_create(const char* address, int port)
if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) { if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) {
serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket)); serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket));
serverSocket->fd = fd; serverSocket->fd = fd;
serverSocket->backLog = 0; serverSocket->backLog = 2;
setSocketNonBlocking((Socket) serverSocket); setSocketNonBlocking((Socket) serverSocket);
} }
@ -293,6 +278,8 @@ ServerSocket_accept(ServerSocket self)
if (conSocket) { if (conSocket) {
conSocket->fd = fd; conSocket->fd = fd;
setSocketNonBlocking(conSocket);
activateTcpNoDelay(conSocket); activateTcpNoDelay(conSocket);
} }
else { else {
@ -345,7 +332,7 @@ ServerSocket_destroy(ServerSocket self)
Socket Socket
TcpSocket_create() TcpSocket_create()
{ {
Socket self = NULL; Socket self = (Socket)NULL;
int sock = socket(AF_INET, SOCK_STREAM, 0); int sock = socket(AF_INET, SOCK_STREAM, 0);
@ -368,7 +355,6 @@ TcpSocket_create()
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: out of memory\n"); printf("SOCKET: out of memory\n");
} }
} }
else { else {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
@ -385,15 +371,24 @@ Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
} }
bool bool
Socket_setLocalAddress(Socket self, const char* address, int port) Socket_bind(Socket self, const char* srcAddress, int srcPort)
{ {
struct sockaddr_in clientAddress; struct sockaddr_in localAddress;
if (!prepareServerAddress(address, port, &clientAddress)) if (!prepareAddress(srcAddress, srcPort, &localAddress))
return false; return false;
if (bind(self->fd, (struct sockaddr *) &clientAddress, sizeof(clientAddress)) != 0) int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to bind TCP socket (errno=%i)\n", errno);
close(self->fd);
self->fd = -1;
return false; return false;
}
return true; return true;
} }
@ -406,7 +401,7 @@ Socket_connectAsync(Socket self, const char* address, int port)
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: connect: %s:%i\n", address, port); printf("SOCKET: connect: %s:%i\n", address, port);
if (!prepareServerAddress(address, port, &serverAddress)) if (!prepareAddress(address, port, &serverAddress))
return false; return false;
fd_set fdSet; fd_set fdSet;

@ -1,34 +1,21 @@
/* /*
* socket_linux.c * socket_linux.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include "hal_socket.h" #include "hal_socket.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h>
#include <sys/select.h> #include <sys/select.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
@ -150,6 +137,10 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
if (self->fds && self->nfds > 0) { if (self->fds && self->nfds > 0) {
int result = poll(self->fds, self->nfds, timeoutMs); int result = poll(self->fds, self->nfds, timeoutMs);
if (result == -1 && errno == EINTR) {
result = 0;
}
if (result == -1) { if (result == -1) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: poll error (errno: %i)\n", errno); printf("SOCKET: poll error (errno: %i)\n", errno);
@ -215,7 +206,7 @@ Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count)
} }
static bool static bool
prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr) prepareAddress(const char* address, int port, struct sockaddr_in* sockaddr)
{ {
bool retVal = true; bool retVal = true;
@ -231,6 +222,10 @@ prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr
result = getaddrinfo(address, NULL, &addressHints, &lookupResult); result = getaddrinfo(address, NULL, &addressHints, &lookupResult);
if (result != 0) { if (result != 0) {
if (DEBUG_SOCKET)
printf("SOCKET: getaddrinfo failed (code=%i)\n", result);
retVal = false; retVal = false;
goto exit_function; goto exit_function;
} }
@ -242,6 +237,10 @@ prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr
sockaddr->sin_addr.s_addr = htonl(INADDR_ANY); sockaddr->sin_addr.s_addr = htonl(INADDR_ANY);
sockaddr->sin_family = AF_INET; sockaddr->sin_family = AF_INET;
if (port < 0)
port = 0;
sockaddr->sin_port = htons(port); sockaddr->sin_port = htons(port);
exit_function: exit_function:
@ -273,7 +272,7 @@ TcpServerSocket_create(const char* address, int port)
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) {
struct sockaddr_in serverAddress; struct sockaddr_in serverAddress;
if (!prepareServerAddress(address, port, &serverAddress)) { if (!prepareAddress(address, port, &serverAddress)) {
close(fd); close(fd);
return NULL; return NULL;
} }
@ -312,7 +311,10 @@ TcpServerSocket_create(const char* address, int port)
void void
ServerSocket_listen(ServerSocket self) ServerSocket_listen(ServerSocket self)
{ {
listen(self->fd, self->backLog); if (listen(self->fd, self->backLog) == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: listen failed (errno: %i)\n", errno);
}
} }
/* CHANGED TO MAKE NON-BLOCKING --> RETURNS NULL IF NO CONNECTION IS PENDING */ /* CHANGED TO MAKE NON-BLOCKING --> RETURNS NULL IF NO CONNECTION IS PENDING */
@ -327,10 +329,26 @@ ServerSocket_accept(ServerSocket self)
if (fd >= 0) { if (fd >= 0) {
conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket)); conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket));
if (conSocket) {
conSocket->fd = fd; conSocket->fd = fd;
setSocketNonBlocking(conSocket);
activateTcpNoDelay(conSocket); activateTcpNoDelay(conSocket);
} }
else {
/* out of memory */
close(fd);
if (DEBUG_SOCKET)
printf("SOCKET: out of memory\n");
}
}
else {
if (DEBUG_SOCKET)
printf("SOCKET: accept failed (errno=%i)\n", errno);
}
return conSocket; return conSocket;
} }
@ -350,9 +368,19 @@ closeAndShutdownSocket(int socketFd)
printf("SOCKET: call shutdown for %i!\n", socketFd); printf("SOCKET: call shutdown for %i!\n", socketFd);
/* shutdown is required to unblock read or accept in another thread! */ /* shutdown is required to unblock read or accept in another thread! */
shutdown(socketFd, SHUT_RDWR); int result = shutdown(socketFd, SHUT_RDWR);
if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: shutdown error: %i\n", errno);
}
close(socketFd); result = close(socketFd);
if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: close error: %i\n", errno);
}
} }
} }
@ -373,13 +401,14 @@ ServerSocket_destroy(ServerSocket self)
Socket Socket
TcpSocket_create() TcpSocket_create()
{ {
Socket self = NULL; Socket self = (Socket)NULL;
int sock = socket(AF_INET, SOCK_STREAM, 0); int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock != -1) { if (sock != -1) {
self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket)); self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
if (self) {
self->fd = sock; self->fd = sock;
self->connectTimeout = 5000; self->connectTimeout = 5000;
@ -387,12 +416,19 @@ TcpSocket_create()
int tcpUserTimeout = 10000; int tcpUserTimeout = 10000;
int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
if (result < 0) { if (result == -1) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: failed to set TCP_USER_TIMEOUT\n"); printf("SOCKET: failed to set TCP_USER_TIMEOUT (errno=%i)\n", errno);
} }
#endif #endif
}
else {
/* out of memory */
close(sock);
if (DEBUG_SOCKET)
printf("SOCKET: out of memory\n");
}
} }
else { else {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
@ -402,13 +438,35 @@ TcpSocket_create()
return self; return self;
} }
void void
Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs) Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
{ {
self->connectTimeout = timeoutInMs; self->connectTimeout = timeoutInMs;
} }
bool
Socket_bind(Socket self, const char* srcAddress, int srcPort)
{
struct sockaddr_in localAddress;
if (!prepareAddress(srcAddress, srcPort, &localAddress))
return false;
int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to bind TCP socket (errno=%i)\n", errno);
close(self->fd);
self->fd = -1;
return false;
}
return true;
}
bool bool
Socket_connectAsync(Socket self, const char* address, int port) Socket_connectAsync(Socket self, const char* address, int port)
{ {
@ -417,7 +475,7 @@ Socket_connectAsync(Socket self, const char* address, int port)
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: connect: %s:%i\n", address, port); printf("SOCKET: connect: %s:%i\n", address, port);
if (!prepareServerAddress(address, port, &serverAddress)) if (!prepareAddress(address, port, &serverAddress))
return false; return false;
fd_set fdSet; fd_set fdSet;
@ -627,6 +685,8 @@ Socket_read(Socket self, uint8_t* buf, int size)
case EAGAIN: case EAGAIN:
return 0; return 0;
case EBADF:
return -1;
default: default:
@ -647,7 +707,7 @@ Socket_write(Socket self, uint8_t* buf, int size)
return -1; return -1;
/* MSG_NOSIGNAL - prevent send to signal SIGPIPE when peer unexpectedly closed the socket */ /* MSG_NOSIGNAL - prevent send to signal SIGPIPE when peer unexpectedly closed the socket */
int retVal = send(self->fd, buf, size, MSG_NOSIGNAL); int retVal = send(self->fd, buf, size, MSG_NOSIGNAL | MSG_DONTWAIT);
if (retVal == -1) { if (retVal == -1) {
if (errno == EAGAIN) { if (errno == EAGAIN) {
@ -701,7 +761,7 @@ UdpSocket_bind(UdpSocket self, const char* address, int port)
{ {
struct sockaddr_in localAddress; struct sockaddr_in localAddress;
if (!prepareServerAddress(address, port, &localAddress)) { if (!prepareAddress(address, port, &localAddress)) {
close(self->fd); close(self->fd);
self->fd = 0; self->fd = 0;
return false; return false;
@ -727,7 +787,7 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in
{ {
struct sockaddr_in remoteAddress; struct sockaddr_in remoteAddress;
if (!prepareServerAddress(address, port, &remoteAddress)) { if (!prepareAddress(address, port, &remoteAddress)) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: failed to lookup remote address %s\n", address); printf("SOCKET: failed to lookup remote address %s\n", address);

@ -1,26 +1,14 @@
/* /*
* socket_win32.c * socket_win32.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* 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.
*/ */
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <windows.h> #include <windows.h>
@ -71,6 +59,7 @@ Handleset_new(void)
FD_ZERO(&result->handles); FD_ZERO(&result->handles);
result->maxHandle = INVALID_SOCKET; result->maxHandle = INVALID_SOCKET;
} }
return result; return result;
} }
@ -172,18 +161,18 @@ setSocketNonBlocking(Socket self)
} }
static bool static bool
prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr) prepareAddress(const char *address, int port, struct sockaddr_in *sockaddr)
{ {
memset((char *)sockaddr, 0, sizeof(struct sockaddr_in));
memset((char *) sockaddr , 0, sizeof(struct sockaddr_in));
if (address != NULL) { if (address != NULL) {
struct hostent *server; struct hostent *server;
server = gethostbyname(address); server = gethostbyname(address);
if (server == NULL) return false; if (server == NULL)
return false;
memcpy((char *) &sockaddr->sin_addr.s_addr, (char *) server->h_addr, server->h_length); memcpy((char *)&sockaddr->sin_addr.s_addr, (char *)server->h_addr, server->h_length);
} }
else else
sockaddr->sin_addr.s_addr = htonl(INADDR_ANY); sockaddr->sin_addr.s_addr = htonl(INADDR_ANY);
@ -240,7 +229,7 @@ TcpServerSocket_create(const char* address, int port)
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
if (!prepareServerAddress(address, port, &server_addr)) if (!prepareAddress(address, port, &server_addr))
return NULL; return NULL;
listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@ -269,14 +258,20 @@ TcpServerSocket_create(const char* address, int port)
return NULL; return NULL;
} }
serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket)); serverSocket = (ServerSocket)GLOBAL_MALLOC(sizeof(struct sServerSocket));
if (serverSocket) {
serverSocket->fd = listen_socket; serverSocket->fd = listen_socket;
serverSocket->backLog = 10; serverSocket->backLog = 10;
setSocketNonBlocking((Socket) serverSocket); setSocketNonBlocking((Socket)serverSocket);
socketCount++; socketCount++;
}
else {
closesocket(listen_socket);
wsaShutdown();
}
return serverSocket; return serverSocket;
} }
@ -346,6 +341,7 @@ TcpSocket_create()
if (sock != INVALID_SOCKET) { if (sock != INVALID_SOCKET) {
self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket)); self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
if (self) {
self->fd = sock; self->fd = sock;
self->connectTimeout = 5000; self->connectTimeout = 5000;
@ -353,7 +349,16 @@ TcpSocket_create()
} }
else { else {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("WIN32_SOCKET: failed to create socket (error code=%i)\n", WSAGetLastError()); printf("SOCKET: failed to create socket - cannot allocate memory\n");
closesocket(sock);
wsaShutdown();
}
}
else {
if (DEBUG_SOCKET)
printf("SOCKET: failed to create socket (error code=%i)\n", WSAGetLastError());
} }
return self; return self;
@ -365,6 +370,29 @@ Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
self->connectTimeout = timeoutInMs; self->connectTimeout = timeoutInMs;
} }
bool
Socket_bind(Socket self, const char* srcAddress, int srcPort)
{
struct sockaddr_in localAddress;
if (!prepareAddress(srcAddress, srcPort, &localAddress))
return false;
int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
if (result == SOCKET_ERROR) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to bind TCP socket (errno=%i)\n", WSAGetLastError());
closesocket(self->fd);
self->fd = -1;
return false;
}
return true;
}
bool bool
Socket_connectAsync(Socket self, const char* address, int port) Socket_connectAsync(Socket self, const char* address, int port)
{ {
@ -381,7 +409,7 @@ Socket_connectAsync(Socket self, const char* address, int port)
return false; return false;
} }
if (!prepareServerAddress(address, port, &serverAddress)) if (!prepareAddress(address, port, &serverAddress))
return false; return false;
setSocketNonBlocking(self); setSocketNonBlocking(self);
@ -417,11 +445,25 @@ Socket_checkAsyncConnectState(Socket self)
int so_error; int so_error;
int len = sizeof so_error; int len = sizeof so_error;
if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*) (&so_error), &len) >= 0) { if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*) (&so_error), &len) != SOCKET_ERROR) {
if (so_error == 0) {
int recvRes = recv(self->fd, NULL, 0, 0);
if (recvRes == SOCKET_ERROR) {
int wsaError = WSAGetLastError();
if (wsaError == WSAECONNRESET)
return SOCKET_STATE_FAILED;
if (wsaError == WSAECONNABORTED)
return SOCKET_STATE_FAILED;
}
if (so_error == 0)
return SOCKET_STATE_CONNECTED; return SOCKET_STATE_CONNECTED;
} }
}
return SOCKET_STATE_FAILED; return SOCKET_STATE_FAILED;
} }
@ -436,30 +478,35 @@ Socket_checkAsyncConnectState(Socket self)
bool bool
Socket_connect(Socket self, const char* address, int port) Socket_connect(Socket self, const char* address, int port)
{ {
struct sockaddr_in serverAddress; if (Socket_connectAsync(self, address, port) == false)
if (!prepareServerAddress(address, port, &serverAddress))
return false; return false;
setSocketNonBlocking(self); struct timeval timeout;
timeout.tv_sec = self->connectTimeout / 1000;
timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
fd_set fdSet; fd_set fdSet;
FD_ZERO(&fdSet); FD_ZERO(&fdSet);
FD_SET(self->fd, &fdSet); FD_SET(self->fd, &fdSet);
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) { if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) {
if (WSAGetLastError() != WSAEWOULDBLOCK)
return false; /* Check if connection is established */
int so_error;
socklen_t len = sizeof so_error;
if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*)&so_error, &len) >= 0) {
if (so_error == 0)
return true;
}
} }
struct timeval timeout; closesocket (self->fd);
timeout.tv_sec = self->connectTimeout / 1000; self->fd = INVALID_SOCKET;
timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) <= 0)
return false; return false;
else
return true;
} }
static char* static char*
@ -647,7 +694,7 @@ UdpSocket_bind(UdpSocket self, const char* address, int port)
{ {
struct sockaddr_in localAddress; struct sockaddr_in localAddress;
if (!prepareServerAddress(address, port, &localAddress)) { if (!prepareAddress(address, port, &localAddress)) {
closesocket(self->fd); closesocket(self->fd);
self->fd = 0; self->fd = 0;
return false; return false;
@ -673,7 +720,7 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in
{ {
struct sockaddr_in remoteAddress; struct sockaddr_in remoteAddress;
if (!prepareServerAddress(address, port, &remoteAddress)) { if (!prepareAddress(address, port, &remoteAddress)) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: failed to lookup remote address %s\n", address); printf("SOCKET: failed to lookup remote address %s\n", address);

@ -1,31 +1,16 @@
/** /**
* thread_bsd.c * thread_bsd.c
* *
* Copyright 2016 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include <pthread.h> #include <pthread.h>
#include <semaphore.h> #include <semaphore.h>
#include <unistd.h> #include <unistd.h>
#include "hal_thread.h" #include "hal_thread.h"
#include "lib_memory.h" #include "lib_memory.h"
struct sThread { struct sThread {
@ -62,7 +47,8 @@ Semaphore_post(Semaphore self)
void void
Semaphore_destroy(Semaphore self) Semaphore_destroy(Semaphore self)
{ {
sem_close(self); sem_destroy((sem_t*) self);
GLOBAL_FREEMEM(self);
} }
Thread Thread
@ -120,3 +106,4 @@ Thread_sleep(int millies)
{ {
usleep(millies * 1000); usleep(millies * 1000);
} }

@ -1,32 +1,16 @@
/* /*
* thread_linux.c * thread_linux.c
* *
* Copyright 2013 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include <pthread.h> #include <pthread.h>
#include <semaphore.h> #include <semaphore.h>
#include <unistd.h> #include <unistd.h>
#include "hal_thread.h" #include "hal_thread.h"
#include "lib_memory.h" #include "lib_memory.h"
struct sThread { struct sThread {

@ -1,7 +1,7 @@
/** /**
* thread_macos.c * thread_macos.c
* *
* Copyright 2013-2019 MZ Automation GmbH * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of Platform Abstraction Layer (libpal) * This file is part of Platform Abstraction Layer (libpal)
* for libiec61850, libmms, and lib60870. * for libiec61850, libmms, and lib60870.
@ -9,10 +9,10 @@
/* /*
* NOTE: MacOS needs own thread layer because it doesn't support unnamed semaphores! * NOTE: MacOS needs own thread layer because it doesn't support unnamed semaphores!
* NOTE: named semaphores were replaced by POSIX mutex
*/ */
#include <pthread.h> #include <pthread.h>
#include <semaphore.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
@ -32,56 +32,52 @@ typedef struct sSemaphore* mSemaphore;
struct sSemaphore struct sSemaphore
{ {
sem_t* sem; pthread_mutex_t mutex;
}; };
/*
* NOTE: initialValue is ignored because semaphore was replaced by mutex
*/
Semaphore Semaphore
Semaphore_create(int initialValue) Semaphore_create(int initialValue)
{ {
mSemaphore self = NULL; mSemaphore self = NULL;
char tmpname[] = {"/tmp/libiec61850.XXXXXX"};
char* res = mktemp(tmpname);
if (res) {
self = (mSemaphore) GLOBAL_CALLOC(1, sizeof(struct sSemaphore)); self = (mSemaphore) GLOBAL_CALLOC(1, sizeof(struct sSemaphore));
if (self) { if (self) {
self->sem = sem_open(tmpname, O_CREAT, 0666, initialValue); pthread_mutex_init(&(self->mutex), NULL);
if (self->sem == SEM_FAILED) {
printf("ERROR: Failed to create semaphore (errno = %i)\n", errno);
GLOBAL_FREEMEM(self);
self = NULL;
}
else {
int ret = sem_unlink(tmpname);
if (ret == -1)
printf("ERROR: Failed to unlink semaphore %s\n", tmpname);
}
}
} }
return (Semaphore)self; return (Semaphore)self;
} }
/* Wait until semaphore value is more than zero. Then decrease the semaphore value. */ /* lock mutex */
void void
Semaphore_wait(Semaphore self) Semaphore_wait(Semaphore self)
{ {
mSemaphore mSelf = (mSemaphore) self; mSemaphore mSelf = (mSemaphore) self;
sem_wait(mSelf->sem); int retVal = pthread_mutex_lock(&(mSelf->mutex));
if (retVal) {
printf("FATAL ERROR: pthread_mutex_lock failed (err=%i)\n", retVal);
exit(-1);
}
} }
/* unlock mutex */
void void
Semaphore_post(Semaphore self) Semaphore_post(Semaphore self)
{ {
mSemaphore mSelf = (mSemaphore) self; mSemaphore mSelf = (mSemaphore) self;
sem_post(mSelf->sem); int retVal = pthread_mutex_unlock(&(mSelf->mutex));
if (retVal) {
printf("FATAL ERROR: pthread_mutex_unlock failed (err=%i)\n", retVal);
exit(-1);
}
} }
void void
@ -90,10 +86,7 @@ Semaphore_destroy(Semaphore self)
if (self) { if (self) {
mSemaphore mSelf = (mSemaphore) self; mSemaphore mSelf = (mSemaphore) self;
int ret = sem_close(mSelf->sem); pthread_mutex_destroy(&(mSelf->mutex));
if (ret == -1)
printf("ERROR: Failed to close semaphore (errno = %i)\n", errno);
GLOBAL_FREEMEM(mSelf); GLOBAL_FREEMEM(mSelf);
} }

@ -1,24 +1,10 @@
/* /*
* thread_win32.c * thread_win32.c
* *
* Copyright 2013, 2014 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include <windows.h> #include <windows.h>

@ -3,26 +3,11 @@
* *
* Copyright 2013-2021 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include "hal_time.h" #include "hal_time.h"
#include <time.h> #include <time.h>
#ifdef CONFIG_SYSTEM_HAS_CLOCK_GETTIME #ifdef CONFIG_SYSTEM_HAS_CLOCK_GETTIME

@ -1,30 +1,14 @@
/* /*
* time.c * time.c
* *
* Copyright 2013, 2014 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of Platform Abstraction Layer (libpal)
* * for libiec61850, libmms, and lib60870.
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/ */
#include "hal_time.h" #include "hal_time.h"
#include <time.h> #include <time.h>
#include <windows.h> #include <windows.h>
uint64_t uint64_t

@ -4,6 +4,7 @@
/* System support */ /* System support */
#define MBEDTLS_HAVE_ASM #define MBEDTLS_HAVE_ASM
#define MBEDTLS_HAVE_TIME #define MBEDTLS_HAVE_TIME
#define MBEDTLS_HAVE_TIME_DATE
#define MBEDTLS_NO_UDBL_DIVISION #define MBEDTLS_NO_UDBL_DIVISION
#define MBEDTLS_PLATFORM_C #define MBEDTLS_PLATFORM_C
#define MBEDTLS_DEBUG_C #define MBEDTLS_DEBUG_C
@ -12,6 +13,7 @@
#define MBEDTLS_CIPHER_MODE_CBC #define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_PKCS1_V15 #define MBEDTLS_PKCS1_V15
#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_SSL_PROTO_TLS1_1 #define MBEDTLS_SSL_PROTO_TLS1_1
#define MBEDTLS_SSL_PROTO_TLS1 #define MBEDTLS_SSL_PROTO_TLS1
#define MBEDTLS_SSL_RENEGOTIATION #define MBEDTLS_SSL_RENEGOTIATION
@ -40,6 +42,7 @@
#define MBEDTLS_SSL_SRV_C #define MBEDTLS_SSL_SRV_C
#define MBEDTLS_SSL_TLS_C #define MBEDTLS_SSL_TLS_C
#define MBEDTLS_X509_CRT_PARSE_C #define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_CRL_PARSE_C
#define MBEDTLS_X509_USE_C #define MBEDTLS_X509_USE_C
#define MBEDTLS_SSL_CACHE_C #define MBEDTLS_SSL_CACHE_C
@ -54,6 +57,9 @@
/* For testing with compat.sh */ /* For testing with compat.sh */
#define MBEDTLS_FS_IO #define MBEDTLS_FS_IO
#define MBEDTLS_X509_CHECK_KEY_USAGE
#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
#include "mbedtls/check_config.h" #include "mbedtls/check_config.h"
#endif /* MBEDTLS_CONFIG_H */ #endif /* MBEDTLS_CONFIG_H */

@ -3,7 +3,7 @@
* *
* TLS API for TCP/IP protocol stacks * TLS API for TCP/IP protocol stacks
* *
* Copyright 2017 MZ Automation GmbH * Copyright 2017-2021 Michael Zillgith, MZ Automation GmbH
* *
* Implementation of the TLS abstraction layer for mbedtls * Implementation of the TLS abstraction layer for mbedtls
* *
@ -14,6 +14,7 @@
#include "tls_socket.h" #include "tls_socket.h"
#include "hal_thread.h" #include "hal_thread.h"
#include "lib_memory.h" #include "lib_memory.h"
#include "hal_time.h"
#include "linked_list.h" #include "linked_list.h"
#include "mbedtls/platform.h" #include "mbedtls/platform.h"
@ -27,9 +28,7 @@
#include "mbedtls/debug.h" #include "mbedtls/debug.h"
#if (CONFIG_DEBUG_TLS == 1) #if (CONFIG_DEBUG_TLS == 1)
#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, \ #define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__);
appId, \
__VA_ARGS__)
#else #else
#define DEBUG_PRINT(fmt, ...) {do {} while(0);} #define DEBUG_PRINT(fmt, ...) {do {} while(0);}
#endif #endif
@ -44,14 +43,24 @@ struct sTLSConfiguration {
mbedtls_x509_crt cacerts; mbedtls_x509_crt cacerts;
mbedtls_x509_crl crl;
mbedtls_ssl_config conf; mbedtls_ssl_config conf;
LinkedList /* <mbedtls_x509_crt*> */ allowedCertificates; LinkedList /* <mbedtls_x509_crt*> */ allowedCertificates;
bool chainValidation; bool chainValidation;
bool allowOnlyKnownCertificates; bool allowOnlyKnownCertificates;
/* TLS session renegotioation time in milliseconds */ /* TLS session renegotiation interval in milliseconds */
int renegotiationTimeInMs; int renegotiationTimeInMs;
/* TLS minimum version allowed (default: TLS_VERSION_TLS_1_0) */
TLSConfigVersion minVersion;
/* TLS minimum version allowed (default: TLS_VERSION_TLS_1_2) */
TLSConfigVersion maxVersion;
bool setupComplete;
}; };
struct sTLSSocket { struct sTLSSocket {
@ -62,6 +71,9 @@ struct sTLSSocket {
bool storePeerCert; bool storePeerCert;
uint8_t* peerCert; uint8_t* peerCert;
int peerCertLength; int peerCertLength;
/* time of last session renegotiation (used to calculate next renegotiation time) */
uint64_t lastRenegotiationTime;
}; };
static bool static bool
@ -145,7 +157,27 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth
return 0; return 0;
} }
/*
* Finish configuration when used the first time.
*/
static bool
TLSConfiguration_setupComplete(TLSConfiguration self)
{
if (self->setupComplete == false) {
mbedtls_ssl_conf_ca_chain( &(self->conf), &(self->cacerts), &(self->crl) );
int ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(self->ownCertificate), &(self->ownKey));
if (ret != 0) {
DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned %d\n", ret);
return false;
}
self->setupComplete = true;
}
return true;
}
TLSConfiguration TLSConfiguration
TLSConfiguration_create() TLSConfiguration_create()
@ -155,7 +187,8 @@ TLSConfiguration_create()
if (self != NULL) { if (self != NULL) {
mbedtls_ssl_config_init( &(self->conf) ); mbedtls_ssl_config_init( &(self->conf) );
mbedtls_x509_crt_init( &(self->ownCertificate) ); mbedtls_x509_crt_init( &(self->ownCertificate) );
mbedtls_x509_crt_init( &(self->cacerts)); mbedtls_x509_crt_init( &(self->cacerts) );
mbedtls_x509_crl_init( &(self->crl) );
mbedtls_pk_init( &(self->ownKey) ); mbedtls_pk_init( &(self->ownKey) );
mbedtls_entropy_init( &(self->entropy) ); mbedtls_entropy_init( &(self->entropy) );
mbedtls_ctr_drbg_init( &(self->ctr_drbg) ); mbedtls_ctr_drbg_init( &(self->ctr_drbg) );
@ -171,16 +204,22 @@ TLSConfiguration_create()
mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_DISABLED); mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED);
/* static int hashes[] = {3,4,5,6,7,8,0}; */ /* static int hashes[] = {3,4,5,6,7,8,0}; */
/* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */ /* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */
self->minVersion = TLS_VERSION_TLS_1_0;
self->maxVersion = TLS_VERSION_NOT_SELECTED;
self->renegotiationTimeInMs = -1; /* no automatic renegotiation */
self->allowedCertificates = LinkedList_create(); self->allowedCertificates = LinkedList_create();
/* default behavior is to allow all certificates that are signed by the CA */ /* default behavior is to allow all certificates that are signed by the CA */
self->chainValidation = true; self->chainValidation = true;
self->allowOnlyKnownCertificates = false; self->allowOnlyKnownCertificates = false;
self->setupComplete = false;
} }
return self; return self;
@ -192,6 +231,18 @@ TLSConfiguration_setClientMode(TLSConfiguration self)
self->conf.endpoint = MBEDTLS_SSL_IS_CLIENT; self->conf.endpoint = MBEDTLS_SSL_IS_CLIENT;
} }
void
TLSConfiguration_setMinTlsVersion(TLSConfiguration self, TLSConfigVersion version)
{
self->minVersion = version;
}
void
TLSConfiguration_setMaxTlsVersion(TLSConfiguration self, TLSConfigVersion version)
{
self->maxVersion = version;
}
void void
TLSConfiguration_setChainValidation(TLSConfiguration self, bool value) TLSConfiguration_setChainValidation(TLSConfiguration self, bool value)
{ {
@ -210,7 +261,7 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate,
int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen); int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen);
if (ret != 0) if (ret != 0)
DEBUG_PRINT("mbedtls_x509_crt_parse returned %d\n", ret); DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret);
return (ret == 0); return (ret == 0);
} }
@ -306,6 +357,29 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
return (ret == 0); return (ret == 0);
} }
bool
TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen)
{
int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen);
if (ret != 0) {
DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned %d\n", ret);
}
return (ret == 0);
}
bool
TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename)
{
int ret = mbedtls_x509_crl_parse_file(&(self->crl), filename);
if (ret != 0)
DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret);
return (ret == 0);
}
void void
TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs)
{ {
@ -317,6 +391,7 @@ TLSConfiguration_destroy(TLSConfiguration self)
{ {
mbedtls_x509_crt_free(&(self->ownCertificate)); mbedtls_x509_crt_free(&(self->ownCertificate));
mbedtls_x509_crt_free(&(self->cacerts)); mbedtls_x509_crt_free(&(self->cacerts));
mbedtls_x509_crl_free(&(self->crl));
mbedtls_pk_free(&(self->ownKey)); mbedtls_pk_free(&(self->ownKey));
mbedtls_ssl_config_free(&(self->conf)); mbedtls_ssl_config_free(&(self->conf));
@ -348,6 +423,50 @@ readFunction(void* ctx, unsigned char* buf, size_t len)
return ret; return ret;
} }
static int
writeFunction(void* ctx, unsigned char* buf, size_t len)
{
return Socket_write((Socket)ctx, buf, (int)len);
}
static int
getMajorVersion(TLSConfigVersion version)
{
switch(version) {
case TLS_VERSION_NOT_SELECTED:
return 0;
case TLS_VERSION_SSL_3_0:
case TLS_VERSION_TLS_1_0:
case TLS_VERSION_TLS_1_1:
case TLS_VERSION_TLS_1_2:
case TLS_VERSION_TLS_1_3:
return 3;
default:
return 0;
}
}
static int
getMinorVersion(TLSConfigVersion version)
{
switch(version) {
case TLS_VERSION_NOT_SELECTED:
return 0;
case TLS_VERSION_SSL_3_0:
return 0;
case TLS_VERSION_TLS_1_0:
return 1;
case TLS_VERSION_TLS_1_1:
return 2;
case TLS_VERSION_TLS_1_2:
return 3;
case TLS_VERSION_TLS_1_3:
return 4;
default:
return 0;
}
}
TLSSocket TLSSocket
TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert) TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert)
{ {
@ -361,13 +480,33 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient
self->peerCert = NULL; self->peerCert = NULL;
self->peerCertLength = 0; self->peerCertLength = 0;
TLSConfiguration_setupComplete(configuration);
memcpy(&(self->conf), &(configuration->conf), sizeof(mbedtls_ssl_config)); memcpy(&(self->conf), &(configuration->conf), sizeof(mbedtls_ssl_config));
mbedtls_ssl_conf_verify(&(self->conf), verifyCertificate, (void*) self); mbedtls_ssl_conf_verify(&(self->conf), verifyCertificate, (void*) self);
int ret; int ret;
mbedtls_ssl_conf_ca_chain( &(self->conf), &(configuration->cacerts), NULL ); mbedtls_ssl_conf_ca_chain( &(self->conf), &(configuration->cacerts), &(configuration->crl) );
if (configuration->minVersion != TLS_VERSION_NOT_SELECTED) {
/* set minimum TLS version */
int majorVer = getMajorVersion(configuration->minVersion);
int minorVer = getMinorVersion(configuration->minVersion);
mbedtls_ssl_conf_min_version( &(self->conf), majorVer, minorVer);
}
if (configuration->maxVersion != TLS_VERSION_NOT_SELECTED) {
/* set maximum TLS version */
int majorVer = getMajorVersion(configuration->maxVersion);
int minorVer = getMinorVersion(configuration->maxVersion);
mbedtls_ssl_conf_max_version( &(self->conf), majorVer, minorVer);
}
ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(configuration->ownCertificate), &(configuration->ownKey)); ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(configuration->ownCertificate), &(configuration->ownKey));
@ -379,23 +518,31 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient
if (ret != 0) if (ret != 0)
DEBUG_PRINT("TLS", "mbedtls_ssl_setup returned %d\n", ret); DEBUG_PRINT("TLS", "mbedtls_ssl_setup returned %d\n", ret);
mbedtls_ssl_set_bio(&(self->ssl), socket, (mbedtls_ssl_send_t*) Socket_write, mbedtls_ssl_set_bio(&(self->ssl), socket, (mbedtls_ssl_send_t*) writeFunction,
(mbedtls_ssl_recv_t*) readFunction, NULL); (mbedtls_ssl_recv_t*) readFunction, NULL);
while( (ret = mbedtls_ssl_handshake(&(self->ssl)) ) != 0 ) while( (ret = mbedtls_ssl_handshake(&(self->ssl)) ) != 0 )
{ {
if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{ {
DEBUG_PRINT("TLS", "mbedtls_ssl_handshake returned %d\n\n", ret ); DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake --> %d\n\n", ret );
mbedtls_ssl_config_free(&(self->conf));
mbedtls_ssl_free(&(self->ssl)); mbedtls_ssl_free(&(self->ssl));
if (self->peerCert) {
GLOBAL_FREEMEM(self->peerCert);
}
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
return NULL; return NULL;
} }
} }
self->lastRenegotiationTime = Hal_getTimeInMs();
/* TODO check for TLS version warning or alarm condition */
/* printf("TLS %i.%i\n", self->ssl.major_ver, self->ssl.minor_ver); */
} }
return self; return self;
@ -422,6 +569,19 @@ TLSSocket_performHandshake(TLSSocket self)
int int
TLSSocket_read(TLSSocket self, uint8_t* buf, int size) TLSSocket_read(TLSSocket self, uint8_t* buf, int size)
{ {
if (self->tlsConfig->renegotiationTimeInMs > 0) {
if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) {
if (TLSSocket_performHandshake(self) == false) {
DEBUG_PRINT("TLS", " renegotiation failed\n");
return -1;
}
else {
DEBUG_PRINT("TLS", " started renegotiation\n");
self->lastRenegotiationTime = Hal_getTimeInMs();
}
}
}
int ret = mbedtls_ssl_read(&(self->ssl), buf, size); int ret = mbedtls_ssl_read(&(self->ssl), buf, size);
if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE))
@ -492,7 +652,6 @@ TLSSocket_close(TLSSocket self)
Thread_sleep(10); Thread_sleep(10);
mbedtls_ssl_config_free(&(self->conf));
mbedtls_ssl_free(&(self->ssl)); mbedtls_ssl_free(&(self->ssl));
if (self->peerCert) if (self->peerCert)

@ -0,0 +1,407 @@
#ifndef PYIEC61850_CONTROLACTIONHANDLER_HPP
#define PYIEC61850_CONTROLACTIONHANDLER_HPP
#include "eventHandler.hpp"
/*
* Abstract class for processing the received 'Control' check events.
*/
class CheckHandlerForPython: public EventHandler {
public:
virtual ~CheckHandlerForPython() {}
virtual void setReceivedData(void *i_data_p)
{
// copy the received data
ControlAction *l_my_data_p = static_cast<ControlAction*>(i_data_p);
_libiec61850_control_action = *l_my_data_p;
}
void setMmsValue(MmsValue *i_data_value)
{
_libiec61850_mms_value = i_data_value;
}
void setIedServer(IedServer *i_ied_server)
{
// copy the pointer and get to server
IedServer *l_my_server_p = i_ied_server;
_libiec61850_ied_server = *l_my_server_p;
}
void setTest(bool i_test)
{
_libiec61850_test = i_test;
}
void setInterlockCheck(bool i_interlock_check)
{
_libiec61850_interlock_check = i_interlock_check;
}
CheckHandlerResult getCheckHandlerResult()
{
return _libiec61850_check_handler_result;
}
CheckHandlerResult _libiec61850_check_handler_result; // Should be set during "trigger" callback function
ControlAction _libiec61850_control_action;
MmsValue* _libiec61850_mms_value;
IedServer _libiec61850_ied_server;
bool _libiec61850_test;
bool _libiec61850_interlock_check;
};
/*
* Abstract class for processing the received 'Control' wait for execution events.
*/
class WaitForExecutionHandlerForPython: public EventHandler {
public:
virtual ~WaitForExecutionHandlerForPython() {}
virtual void setReceivedData(void *i_data_p)
{
// copy the received data
ControlAction *l_my_data_p = static_cast<ControlAction*>(i_data_p);
_libiec61850_control_action = *l_my_data_p;
}
void setMmsValue(MmsValue *i_data_value)
{
_libiec61850_mms_value = i_data_value;
}
void setIedServer(IedServer *i_ied_server)
{
// copy the pointer and get to server
IedServer *l_my_server_p = i_ied_server;
_libiec61850_ied_server = *l_my_server_p;
}
void setTest(bool i_test)
{
_libiec61850_test = i_test;
}
void setSynchroCheck(bool i_synchro_check)
{
_libiec61850_synchro_check = i_synchro_check;
}
ControlHandlerResult getControlHandlerResult()
{
return _libiec61850_control_handler_result;
}
ControlHandlerResult _libiec61850_control_handler_result; // Should be set during "trigger" callback function
ControlAction _libiec61850_control_action;
MmsValue* _libiec61850_mms_value;
IedServer _libiec61850_ied_server;
bool _libiec61850_test;
bool _libiec61850_synchro_check;
};
/*
* Abstract class for processing the received 'Control' events.
*/
class ControlHandlerForPython: public EventHandler {
public:
virtual ~ControlHandlerForPython() {}
virtual void setReceivedData(void *i_data_p)
{
// copy the received data
ControlAction *l_my_data_p = static_cast<ControlAction*>(i_data_p);
_libiec61850_control_action = *l_my_data_p;
}
void setMmsValue(MmsValue *i_data_value)
{
_libiec61850_mms_value = i_data_value;
}
void setIedServer(IedServer *i_ied_server)
{
// copy the pointer and get to server
IedServer *l_my_server_p = i_ied_server;
_libiec61850_ied_server = *l_my_server_p;
}
void setTest(bool i_test)
{
_libiec61850_test = i_test;
}
ControlHandlerResult getControlHandlerResult()
{
return _libiec61850_control_handler_result;
}
ControlHandlerResult _libiec61850_control_handler_result; // Should be set during "trigger" callback function
ControlAction _libiec61850_control_action;
MmsValue* _libiec61850_mms_value;
IedServer _libiec61850_ied_server;
bool _libiec61850_test;
};
/*
* Class for the subscription to the 'Control' events
*/
class ControlSubscriberForPython: public EventSubscriber {
public:
ControlSubscriberForPython(): EventSubscriber()
{
m_ied_server = nullptr;
m_control_object = nullptr;
m_perform_check_handler_p = nullptr;
m_wait_handler_p = nullptr;
m_control_handler_p = nullptr;
}
virtual ~ControlSubscriberForPython() {
if (m_perform_check_handler_p) {
delete m_perform_check_handler_p;
}
m_perform_check_handler_p = nullptr;
if (m_wait_handler_p) {
delete m_wait_handler_p;
}
m_wait_handler_p = nullptr;
if (m_control_handler_p) {
delete m_control_handler_p;
}
m_control_handler_p = nullptr;
}
// Getters
CheckHandlerForPython *getCheckHandler()
{
return m_perform_check_handler_p;
}
WaitForExecutionHandlerForPython *getWaitHandler()
{
return m_wait_handler_p;
}
ControlHandlerForPython *getControlHandler()
{
return m_control_handler_p;
}
// Subscriber: assign callbacks to IedServer
virtual bool subscribe()
{
// preconditions
if (nullptr == m_ied_server) {
fprintf(stderr, "ControlSubscriber::subscribe() failed: 'ied server' is null\n");
return false;
}
if (nullptr == m_control_object) {
fprintf(stderr, "ControlSubscriber::subscribe() failed: 'control object' is null\n");
return false;
}
// install the libiec61850 callbacks for the different control states, if handler are set:
// the 'function pointer' is the 'static' method of this class
if (m_perform_check_handler_p) {
IedServer_setPerformCheckHandler(m_ied_server,
m_control_object,
(ControlPerformCheckHandler) ControlSubscriberForPython::triggerPerformCheckHandler,
&m_ied_server);
}
if (m_wait_handler_p){
IedServer_setWaitForExecutionHandler(m_ied_server,
m_control_object,
(ControlWaitForExecutionHandler) ControlSubscriberForPython::triggerWaitForExecutionHandler,
&m_ied_server);
}
if (m_control_handler_p) {
IedServer_setControlHandler(m_ied_server,
m_control_object,
(ControlHandler) ControlSubscriberForPython::triggerControlHandler,
&m_ied_server);
}
char *l_object_ref = ModelNode_getObjectReference( (ModelNode*) m_control_object, NULL);
std::string objs(l_object_ref);
std::string dirs = std::string(ModelNode_getObjectReference( (ModelNode*) m_control_object, NULL));
return (EventSubscriber::registerNewSubscriber(this, l_object_ref));
}
// Static method: it is the control perform check 'callback' for libiec61850 in C
static CheckHandlerResult triggerPerformCheckHandler(ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck)
{
PyThreadStateLock PyThreadLock;
// Preconditions
if (nullptr == parameter) {
fprintf(stderr, "ControlSubscriber::triggerPerformCheckHandler() failed: input object is null\n");
return CONTROL_HARDWARE_FAULT; // Status may not
}
DataObject* control_object = ControlAction_getControlObject(action);
std::string l_object_ref = ModelNode_getObjectReference( (ModelNode*) control_object, NULL);
// Search the appropriate 'EventSubscriber' object
ControlSubscriberForPython *l_registered_subscriber = (ControlSubscriberForPython*) EventSubscriber::findSubscriber(l_object_ref);
if (l_registered_subscriber) {
CheckHandlerForPython *l_perform_check_handler_p = l_registered_subscriber->getCheckHandler();
if (l_perform_check_handler_p) {
l_perform_check_handler_p->setReceivedData(&action);
l_perform_check_handler_p->setMmsValue(ctlVal);
l_perform_check_handler_p->setIedServer((IedServer*) parameter);
l_perform_check_handler_p->setTest(test);
l_perform_check_handler_p->setInterlockCheck(interlockCheck);
l_perform_check_handler_p->trigger();
fprintf(stderr, "triggerPerformCheckHandler::triggerPerformCheckHandler() end\n");
return l_perform_check_handler_p->getCheckHandlerResult();
}
else {
fprintf(stderr, "ControlSubscriber::triggerPerformCheckHandler() failed: EventHandler is undefined\n");
return CONTROL_HARDWARE_FAULT;
}
}
else {
fprintf(stderr, "ControlSubscriber::triggerPerformCheckHandler() failed: subscriber is not registered\n");
return CONTROL_HARDWARE_FAULT;
}
}
// Static method: it is the control wait for execution 'callback' for libiec61850 in C
static ControlHandlerResult triggerWaitForExecutionHandler(ControlAction controlAction, void* parameter, MmsValue* value, bool test, bool synchroCheck)
{
PyThreadStateLock PyThreadLock;
// Preconditions
if (nullptr == parameter) {
fprintf(stderr, "ControlSubscriber::triggerWaitForExecutionHandler() failed: input object is null\n");
return CONTROL_RESULT_FAILED;
}
DataObject* control_object = ControlAction_getControlObject(controlAction);
std::string l_object_ref = ModelNode_getObjectReference( (ModelNode*) control_object, NULL);
// Search the appropriate 'EventSubscriber' object
ControlSubscriberForPython *l_registered_subscriber = (ControlSubscriberForPython*) EventSubscriber::findSubscriber(l_object_ref);
if (l_registered_subscriber) {
WaitForExecutionHandlerForPython *l_wait_handler_p = l_registered_subscriber->getWaitHandler();
if (l_wait_handler_p) {
l_wait_handler_p->setReceivedData(&controlAction);
l_wait_handler_p->setMmsValue(value);
l_wait_handler_p->setIedServer((IedServer*) parameter);
l_wait_handler_p->setTest(test);
l_wait_handler_p->setSynchroCheck(synchroCheck);
l_wait_handler_p->trigger();
fprintf(stderr, "triggerWaitForExecutionHandler::triggerWaitForExecutionHandler() end\n");
return l_wait_handler_p->getControlHandlerResult();
}
else {
fprintf(stderr, "ControlSubscriber::triggerWaitForExecutionHandler() failed: EventHandler is undefined\n");
return CONTROL_RESULT_FAILED;
}
}
else {
fprintf(stderr, "ControlSubscriber::triggerWaitForExecutionHandler() failed: subscriber is not registered\n");
return CONTROL_RESULT_FAILED;
}
}
// Static method: it is the control 'callback' for libiec61850 in C
static ControlHandlerResult triggerControlHandler(ControlAction controlAction, void* parameter, MmsValue* value, bool test)
{
PyThreadStateLock PyThreadLock;
// Preconditions
if (nullptr == parameter) {
fprintf(stderr, "ControlSubscriber::triggerControlHandler() failed: input object is null\n");
return CONTROL_RESULT_FAILED;
}
DataObject* control_object = ControlAction_getControlObject(controlAction);
std::string l_object_ref = ModelNode_getObjectReference( (ModelNode*) control_object, NULL);
// Search the appropriate 'EventSubscriber' object
ControlSubscriberForPython *l_registered_subscriber = (ControlSubscriberForPython*) EventSubscriber::findSubscriber(l_object_ref);
if (l_registered_subscriber) {
ControlHandlerForPython *l_control_handler_p = l_registered_subscriber->getControlHandler();
if (l_control_handler_p) {
l_control_handler_p->setReceivedData(&controlAction);
l_control_handler_p->setMmsValue(value);
l_control_handler_p->setIedServer((IedServer*) parameter);
l_control_handler_p->setTest(test);
l_control_handler_p->trigger();
fprintf(stderr, "triggerControlHandler::triggerControlHandler() end\n");
return l_control_handler_p->getControlHandlerResult();
}
else {
fprintf(stderr, "ControlSubscriber::triggerControlHandler() failed: EventHandler is undefined\n");
return CONTROL_RESULT_FAILED;
}
}
else {
fprintf(stderr, "ControlSubscriber::triggerControlHandler() failed: subscriber is not registered\n");
return CONTROL_RESULT_FAILED;
}
}
// Setters
void setIedServer(const IedServer &i_ied_server)
{
m_ied_server = i_ied_server;
}
void setControlObject(DataObject *i_control_object)
{
m_control_object = i_control_object;
}
void setCheckHandler(CheckHandlerForPython* i_check_handler_p)
{
m_perform_check_handler_p = i_check_handler_p;
}
void setWaitHandler(WaitForExecutionHandlerForPython* i_wait_handler_p)
{
m_wait_handler_p = i_wait_handler_p;
}
void setControlHandler(ControlHandlerForPython* i_control_handler_p)
{
m_control_handler_p = i_control_handler_p;
}
private:
// Parameters
IedServer m_ied_server;
DataObject* m_control_object;
CheckHandlerForPython* m_perform_check_handler_p;
WaitForExecutionHandlerForPython* m_wait_handler_p;
ControlHandlerForPython* m_control_handler_p;
};
#endif

@ -6,7 +6,6 @@
#include <string> #include <string>
#include <Python.h> #include <Python.h>
class PyThreadStateLock class PyThreadStateLock
{ {
public: public:
@ -25,7 +24,6 @@ private:
}; };
class EventHandler { class EventHandler {
public: public:
EventHandler() {} EventHandler() {}
@ -118,7 +116,7 @@ class EventSubscriber {
m_subscriber_map.erase(l_it); m_subscriber_map.erase(l_it);
} }
else { else {
fprintf(stderr, "EventSubscriber::unregisterSubscriber() failed: '%s' is not registered\n"); fprintf(stderr, "EventSubscriber::unregisterSubscriber() failed: '%s' is not registered\n", i_subscriber_id.c_str());
} }
} }

@ -26,6 +26,8 @@ DataAttribute* toDataAttribute(DataObject * DO)
{ return (DataAttribute*)DO;} { return (DataAttribute*)DO;}
DataAttribute* toDataAttribute(ModelNode * MN) DataAttribute* toDataAttribute(ModelNode * MN)
{ return (DataAttribute*)MN;} { return (DataAttribute*)MN;}
DataObject* toDataObject(ModelNode * MN)
{ return (DataObject*)MN;}
%} %}
%apply int *OUTPUT {IedClientError* error}; %apply int *OUTPUT {IedClientError* error};
@ -55,6 +57,7 @@ ModelNode* toModelNode(LogicalNode *);
ModelNode* toModelNode(DataObject *); ModelNode* toModelNode(DataObject *);
DataAttribute* toDataAttribute(DataObject *); DataAttribute* toDataAttribute(DataObject *);
DataAttribute* toDataAttribute(ModelNode *); DataAttribute* toDataAttribute(ModelNode *);
DataObject* toDataObject(ModelNode *);
char* toCharP(void *); char* toCharP(void *);
/* Goose Subscriber section */ /* Goose Subscriber section */
@ -99,17 +102,23 @@ void GooseSubscriber_setDstMac(GooseSubscriber subscriber,
%feature("director") RCBHandler; %feature("director") RCBHandler;
%feature("director") GooseHandler; %feature("director") GooseHandler;
%feature("director") CommandTermHandler; %feature("director") CommandTermHandler;
%feature("director") CheckHandlerForPython;
%feature("director") WaitForExecutionHandlerForPython;
%feature("director") ControlHandlerForPython;
%{ %{
#include "eventHandlers/eventHandler.hpp" #include "eventHandlers/eventHandler.hpp"
#include "eventHandlers/reportControlBlockHandler.hpp" #include "eventHandlers/reportControlBlockHandler.hpp"
#include "eventHandlers/gooseHandler.hpp" #include "eventHandlers/gooseHandler.hpp"
#include "eventHandlers/commandTermHandler.hpp" #include "eventHandlers/commandTermHandler.hpp"
#include "eventHandlers/controlActionHandler.hpp"
std::map< std::string, EventSubscriber*> EventSubscriber::m_subscriber_map = {}; std::map< std::string, EventSubscriber*> EventSubscriber::m_subscriber_map = {};
%} %}
%include "eventHandlers/eventHandler.hpp" %include "eventHandlers/eventHandler.hpp"
%include "eventHandlers/reportControlBlockHandler.hpp" %include "eventHandlers/reportControlBlockHandler.hpp"
%include "eventHandlers/gooseHandler.hpp" %include "eventHandlers/gooseHandler.hpp"
%include "eventHandlers/commandTermHandler.hpp" %include "eventHandlers/commandTermHandler.hpp"
%include "eventHandlers/controlActionHandler.hpp"
/* Goose Publisher section */ /* Goose Publisher section */
%{ %{
@ -145,3 +154,9 @@ void CommParameters_setDstAddress(CommParameters *gooseCommParameters,
uint8_t dst_mac_4, uint8_t dst_mac_4,
uint8_t dst_mac_5); uint8_t dst_mac_5);
/* Wrapper for synchronous functions */
%{
#include "servicePythonWrapper.hpp"
%}
%include "servicePythonWrapper.hpp"

@ -0,0 +1,63 @@
#ifndef PYIEC61850_SERVICEPYTHONWRAPPER_HPP
#define PYIEC61850_SERVICEPYTHONWRAPPER_HPP
#include "iec61850_client.h"
#include <Python.h>
class PyThreadStateSave
{
public:
PyThreadStateSave(void)
{
if (PyGILState_Check())
state = PyEval_SaveThread();
else
state = nullptr;
}
~PyThreadStateSave(void)
{
if (state)
PyEval_RestoreThread(state);
}
private:
PyThreadState* state;
};
/*
Wrapping of synchronous functions to prevent deadlocks
*/
static MmsValue*
pyWrap_IedConnection_readObject(IedConnection con, IedClientError* error, const char* objRef, FunctionalConstraint fc)
{
PyThreadStateSave gilStateSave;
return IedConnection_readObject(con, error, objRef, fc);
}
static void
pyWrap_IedConnection_writeObject(IedConnection con, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, MmsValue* value)
{
PyThreadStateSave gilStateSave;
IedConnection_writeObject(con, error, objectReference, fc, value);
}
static void
pyWrap_IedConnection_setRCBValues(IedConnection con, IedClientError* error, ClientReportControlBlock rcb, uint32_t parametersMask,
bool singleRequest)
{
PyThreadStateSave gilStateSave;
IedConnection_setRCBValues(con, error, rcb, parametersMask, singleRequest);
}
static ClientReportControlBlock
pyWrap_IedConnection_getRCBValues(IedConnection con, IedClientError* error, const char* rcbReference,
ClientReportControlBlock updateRcb)
{
PyThreadStateSave gilStateSave;
return IedConnection_getRCBValues(con, error, rcbReference, updateRcb);
}
#endif

@ -17,7 +17,7 @@
#include "platform_endian.h" #include "platform_endian.h"
#define LIBIEC61850_VERSION "1.5.0" #define LIBIEC61850_VERSION "1.5.1"
#ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME #ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com" #define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"

@ -79,7 +79,7 @@ StringUtils_createBufferFromHexString(char* hexString, uint8_t* buffer);
* \brief test if string starts with prefix * \brief test if string starts with prefix
*/ */
LIB61850_INTERNAL bool LIB61850_INTERNAL bool
StringUtils_startsWith(char* string, char* prefix); StringUtils_startsWith(const char* string, const char* prefix);
LIB61850_INTERNAL bool LIB61850_INTERNAL bool
StringUtils_endsWith(const char* str, const char* suffix); StringUtils_endsWith(const char* str, const char* suffix);

@ -204,14 +204,8 @@ LinkedList_printStringList(LinkedList list)
{ {
LinkedList element = list; LinkedList element = list;
int elementCount = 0;
while ((element = LinkedList_getNext(element)) != NULL) { while ((element = LinkedList_getNext(element)) != NULL) {
char* str = (char*) (element->data); char* str = (char*) (element->data);
printf("%s\n", str); printf("%s\n", str);
elementCount++;
} }
} }

@ -111,7 +111,6 @@ StringUtils_createString(int count, ...)
{ {
va_list ap; va_list ap;
char* newStr; char* newStr;
char* currentPos;
int newStringLength = 0; int newStringLength = 0;
int i; int i;
@ -127,7 +126,7 @@ StringUtils_createString(int count, ...)
newStr = (char*) GLOBAL_MALLOC(newStringLength + 1); newStr = (char*) GLOBAL_MALLOC(newStringLength + 1);
if (newStr) { if (newStr) {
currentPos = newStr; char* currentPos = newStr;
va_start(ap, count); va_start(ap, count);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
@ -136,8 +135,6 @@ StringUtils_createString(int count, ...)
currentPos += strlen(str); currentPos += strlen(str);
} }
va_end(ap); va_end(ap);
*currentPos = 0;
} }
return newStr; return newStr;
@ -230,7 +227,7 @@ StringUtils_createBufferFromHexString(char* hexString, uint8_t* buffer)
} }
bool bool
StringUtils_startsWith(char* string, char* prefix) StringUtils_startsWith(const char* string, const char* prefix)
{ {
int index = 0; int index = 0;
@ -271,11 +268,11 @@ getCharWeight(int c)
{ {
static bool initialized = false; static bool initialized = false;
static char lookupTable[LT_MAX_CHARS + 1]; static char lookupTable[LT_MAX_CHARS + 1];
static const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789";
if (!initialized) { if (!initialized) {
int ltIndex; int ltIndex;
int weight = 1; int weight = 1;
const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789";
for (ltIndex = 1; ltIndex < LT_MAX_CHARS; ltIndex++) { for (ltIndex = 1; ltIndex < LT_MAX_CHARS; ltIndex++) {
if (strchr(charOrder, ltIndex)) continue; if (strchr(charOrder, ltIndex)) continue;

@ -3,19 +3,45 @@
* \section intro_sec Introduction * \section intro_sec Introduction
* *
* This is the reference manual of libIEC61850. * This is the reference manual of libIEC61850.
* libIEC61850 is an open-source (GPLv3) implementation of an IEC 61850 client and server library. It is implemented
* in C to provide maximum portability. It can be used to implement IEC 61850
* compatible client and server applications on embedded systems and PCs running Linux and Windows. Included
* is a set of simple example applications that can be used as a starting point to implement your own IEC 61850
* compatible devices or to communicate with IEC 61850 devices.
* There is also an open-source implementation of IEC 61850 in Java (see http://openmuc.org).
* *
* The library implements the most important parts of IEC 61850 on top of the MMS mapping. It provides the MMS protocol stack * libiec61850 is an open-source (GPLv3) implementation of an IEC 61850 client and server library implementing
* on top of TCP/IP as well as GOOSE for real-time data transfer inside of substations. * the protocols MMS, GOOSE and SV. It is implemented in C (according to the C99 standard)
* to be portable to a large number of platforms. It can be used to implement IEC 61850 compliant client and server applications on embedded systems and PCs running Linux, Windows, and MacOS.
* Included is a set of simple example applications that can be used as a starting point to implement own IEC 61850 compliant devices or to communicate with IEC 61850 devices.
* The library has been successfully used in many commercial software products and devices. Various of these devices have been successfully certified for standard compliance.
* *
* The API of libIEC61850 can be divided into a client and a server part. Both parts also * The library implements the most important parts of IEC 61850 on top of the MMS mapping. It provides the MMS protocol stack
* share common elements. For both client and server there exist two different APIs. There is a * on top of TCP/IP as well as GOOSE and sampled measured values (SMV) for real-time data transfer inside of substations.
* "low-level" MMS API and the more high-level IEC 61850 API. *
* \section features Library features
*
* The library support the following IEC 61850 protocol features:
*
* - MMS client/server, GOOSE publisher/subscriber(IEC 61850-8-1)
* - Sampled Values publisher/subscriber (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 ready-to-use sqlite implementation
* - MMS file services (browse, get file, set file, delete/rename file)
* - required to download COMTRADE files
* - Setting group handling
* - Support for service tracking
* - GOOSE and SV control block handling
* - TLS support
* - C and C#/.NET API
*
* \section api API for application programming
*
* The API of libIEC61850 can be divided into a client, server, publisher, and subscriber parts.
* These parts also share common elements. For client and server there exist two different APIs. There is a
* "low-level" MMS API and the more high-level IEC 61850 API. It is always recommended to use the IEC 61850 API
* to be compliant with the standard. In some cases in can also be beneficial to use the lower-level MMS API.
* *
* \section iec_client_api IEC 61850 client API * \section iec_client_api IEC 61850 client API
* *
@ -49,13 +75,31 @@
* \section hal Hardware/OS abstraction layer * \section hal Hardware/OS abstraction layer
* *
* libIEC61850 provides a hardware/OS abstraction layer (HAL) to hide the dependencies to the * libIEC61850 provides a hardware/OS abstraction layer (HAL) to hide the dependencies to the
* underlying platform. Currently this layer consists of thread, socket and time abstractions. If * underlying platform. Currently this layer consists of thread, socket, filesystem, Ethernet and time abstractions. If
* you want to port the library to a new platform you need to implement the functions defined * you want to port the library to a new platform you need to implement the functions defined
* by this API. At the moment implementations for POSIX(Linux, Mac OS X ...) and Win32 exists. This API * by this API. At the moment implementations for Linux, BSD, Windows and MacOS exists. This API
* consists of the three files <code>hal/hal.h</code>, <code>hal/socket/socket.h</code> and * consists of header files starting with <code>hal_</code> in the <code>hal/inc</code> folder.
* <code>hal/thread/thread.h</code>. *
* \section tls TLS support
*
* The library can also be compiled with TLS support. The library is using an abstraction layer for
* TLS libraries. Currently the only supported implementation of this abstraction layer is for mbedtls.
*
* \section building Building the library and examples with cmake
*
* With the help of the cmake build script it is possible to create platform independent project descriptions and let cmake create specific project or build files for other tools like Make or Visual Studio.
*
* If you have cmake installed fire up a command line (cmd.exe) and create a new subdirectory in the libiec61850 folder. Change to this subdirectory. Then you can invoke cmake. As an command line argument you have to supply a "generator" that is used by cmake to create the project file for the actual build tool (in our case Visual Studio 2015).
*
* For Visual Studio 2017:
*
* <code>cmake -G "Visual Studio 15 2017 Win64" ..</code>
*
* For Visual Studio 2019 (new way to specify the x64 platform):
*
* <code>cmake -G "Visual Studio 16 2019" .. -A x64</code>
* *
* \section examples Building the library and the examples * \section examples Building the library and examples with gcc/make
* *
* To build the library you can simply execute <code>make</code> in the main folder of the * To build the library you can simply execute <code>make</code> in the main folder of the
* source distribution. It is assumed that a GCC toolchain and the Make tool is installed. * source distribution. It is assumed that a GCC toolchain and the Make tool is installed.

@ -1,6 +1,6 @@
# API Documentation # API Documentation
Copyright 2018 Michael Zillgith Copyright 2022 Michael Zillgith
This is the documentation for IEC61850.NET a .NET wrapper for the libiec61850 IEC 61850 protocol library. This is the documentation for IEC61850.NET a .NET wrapper for the libiec61850 IEC 61850 protocol library.

@ -1,7 +1,7 @@
/* /*
* goose_publisher.c * goose_publisher.c
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -91,6 +91,7 @@ GoosePublisher_create(CommParameters* parameters, const char* interfaceID)
void void
GoosePublisher_destroy(GoosePublisher self) GoosePublisher_destroy(GoosePublisher self)
{ {
if (self) {
if (self->ethernetSocket) { if (self->ethernetSocket) {
Ethernet_destroySocket(self->ethernetSocket); Ethernet_destroySocket(self->ethernetSocket);
} }
@ -110,6 +111,7 @@ GoosePublisher_destroy(GoosePublisher self)
GLOBAL_FREEMEM(self->buffer); GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
void void

@ -1,7 +1,7 @@
/* /*
* goose_receiver.c * goose_receiver.c
* *
* Copyright 2014-2017 Michael Zillgith * Copyright 2014-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -151,6 +151,12 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex); MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex);
if (value == NULL) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: type mismatch (element %i not found)\n", elementIndex);
return GOOSE_PARSE_ERROR_TYPE_MISMATCH;
}
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos < 0) { if (bufPos < 0) {
pe = GOOSE_PARSE_ERROR_TAGDECODE; pe = GOOSE_PARSE_ERROR_TAGDECODE;
@ -267,10 +273,26 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0x89: /* octet string */ case 0x89: /* octet string */
if (MmsValue_getType(value) == MMS_OCTET_STRING) { if (MmsValue_getType(value) == MMS_OCTET_STRING) {
if (elementLength <= value->value.octetString.maxSize) { if (elementLength <= abs(value->value.octetString.maxSize)) {
value->value.octetString.size = elementLength; value->value.octetString.size = elementLength;
memcpy(value->value.octetString.buf, buffer + bufPos, elementLength); memcpy(value->value.octetString.buf, buffer + bufPos, elementLength);
} }
else {
uint8_t* newBuf = (uint8_t*)GLOBAL_MALLOC(elementLength);
if (newBuf) {
memcpy(newBuf, buffer + bufPos, elementLength);
uint8_t* oldBuf = value->value.octetString.buf;
value->value.octetString.buf = newBuf;
value->value.octetString.maxSize = -elementLength;
value->value.octetString.size = elementLength;
GLOBAL_FREEMEM(oldBuf);
}
}
} }
else { else {
pe = GOOSE_PARSE_ERROR_TYPE_MISMATCH; pe = GOOSE_PARSE_ERROR_TYPE_MISMATCH;
@ -1008,6 +1030,7 @@ GooseReceiver_stop(GooseReceiver self)
void void
GooseReceiver_destroy(GooseReceiver self) GooseReceiver_destroy(GooseReceiver self)
{ {
if (self) {
#if (CONFIG_MMS_THREADLESS_STACK == 0) #if (CONFIG_MMS_THREADLESS_STACK == 0)
if ((self->thread != NULL) && (GooseReceiver_isRunning(self))) if ((self->thread != NULL) && (GooseReceiver_isRunning(self)))
GooseReceiver_stop(self); GooseReceiver_stop(self);
@ -1021,6 +1044,7 @@ GooseReceiver_destroy(GooseReceiver self)
GLOBAL_FREEMEM(self->buffer); GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
/*************************************** /***************************************
@ -1036,6 +1060,26 @@ GooseReceiver_startThreadless(GooseReceiver self)
if (self->ethSocket != NULL) { if (self->ethSocket != NULL) {
Ethernet_setProtocolFilter(self->ethSocket, ETH_P_GOOSE); Ethernet_setProtocolFilter(self->ethSocket, ETH_P_GOOSE);
/* set multicast addresses for subscribers */
Ethernet_setMode(self->ethSocket, ETHERNET_SOCKET_MODE_MULTICAST);
LinkedList element = LinkedList_getNext(self->subscriberList);
while (element != NULL) {
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
if (subscriber->dstMacSet == false) {
/* no destination MAC address defined -> we have to switch to all multicast mode */
Ethernet_setMode(self->ethSocket, ETHERNET_SOCKET_MODE_ALL_MULTICAST);
}
else {
Ethernet_addMulticastAddress(self->ethSocket, subscriber->dstMac);
}
element = LinkedList_getNext(element);
}
self->running = true; self->running = true;
} }
else else

@ -1,7 +1,7 @@
/* /*
* goose_subscriber.c * goose_subscriber.c
* *
* Copyright 2013, 2014 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -40,6 +40,7 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues)
{ {
GooseSubscriber self = (GooseSubscriber) GLOBAL_CALLOC(1, sizeof(struct sGooseSubscriber)); GooseSubscriber self = (GooseSubscriber) GLOBAL_CALLOC(1, sizeof(struct sGooseSubscriber));
if (self) {
strncpy(self->goCBRef, goCbRef, 129); strncpy(self->goCBRef, goCbRef, 129);
self->goCBRef[129] = 0; self->goCBRef[129] = 0;
@ -58,6 +59,7 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues)
self->isObserver = false; self->isObserver = false;
self->vlanSet = false; self->vlanSet = false;
self->parseError = GOOSE_PARSE_ERROR_NO_ERROR; self->parseError = GOOSE_PARSE_ERROR_NO_ERROR;
}
return self; return self;
} }
@ -96,12 +98,14 @@ GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId)
void void
GooseSubscriber_destroy(GooseSubscriber self) GooseSubscriber_destroy(GooseSubscriber self)
{ {
if (self) {
MmsValue_delete(self->timestamp); MmsValue_delete(self->timestamp);
if (self->dataSetValuesSelfAllocated) if (self->dataSetValuesSelfAllocated)
MmsValue_delete(self->dataSetValues); MmsValue_delete(self->dataSetValues);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
void void

@ -1,7 +1,7 @@
/* /*
* client_control.c * client_control.c
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -441,8 +441,12 @@ prepareOperParameters(ControlObjectClient self, MmsValue* ctlVal, uint64_t operT
MmsValue* ctlTime; MmsValue* ctlTime;
if (self->edition == 2) if (self->edition == 2) {
ctlTime = MmsValue_newUtcTimeByMsTime(timestamp); ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
if (self->connection)
MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
}
else { else {
ctlTime = MmsValue_newBinaryTime(false); ctlTime = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(ctlTime, timestamp); MmsValue_setBinaryTime(ctlTime, timestamp);
@ -566,7 +570,7 @@ internalOperateHandler(uint32_t invokeId, void* parameter, MmsError err, MmsData
} }
else { else {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call (ID: %d)!\n", invokeId); printf("IED_CLIENT: internal error - no matching outstanding call (ID: %u)!\n", invokeId);
} }
} }
@ -610,7 +614,7 @@ ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err,
MmsError mmsError; MmsError mmsError;
call->invokeId = MmsConnection_writeVariableAsync(self->connection->connection, &mmsError, domainId, itemId, operParameters, internalOperateHandler, self); MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId, operParameters, internalOperateHandler, self);
invokeId = call->invokeId; invokeId = call->invokeId;
@ -682,8 +686,12 @@ prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal)
if (self->useConstantT) if (self->useConstantT)
self->constantT = timestamp; self->constantT = timestamp;
if (self->edition == 2) if (self->edition == 2) {
ctlTime = MmsValue_newUtcTimeByMsTime(timestamp); ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
if (self->connection)
MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
}
else { else {
ctlTime = MmsValue_newBinaryTime(false); ctlTime = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(ctlTime, timestamp); MmsValue_setBinaryTime(ctlTime, timestamp);
@ -855,7 +863,7 @@ ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientErro
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId); printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
call->invokeId = MmsConnection_writeVariableAsync(self->connection->connection, &mmsError, domainId, itemId, selValParameters, internalSelWithValHandler, self); MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId, selValParameters, internalSelWithValHandler, self);
invokeId = call->invokeId; invokeId = call->invokeId;
@ -1011,6 +1019,9 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue
uint32_t uint32_t
ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter) ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter)
{ {
*err = IED_ERROR_OK;
uint32_t invokeId = 0;
resetLastApplError(self); resetLastApplError(self);
char domainId[65]; char domainId[65];
@ -1037,14 +1048,18 @@ ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, C
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: %s/%s\n", domainId, itemId); printf("IED_CLIENT: select: %s/%s\n", domainId, itemId);
call->invokeId = MmsConnection_readVariableAsync(IedConnection_getMmsConnection(self->connection), MmsConnection_readVariableAsync(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, internalSelectHandler, self); &(call->invokeId), &mmsError, domainId, itemId, internalSelectHandler, self);
invokeId = call->invokeId;
*err = iedConnection_mapMmsErrorToIedError(mmsError);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
return call->invokeId; return invokeId;
} }
static MmsValue* static MmsValue*
@ -1082,8 +1097,12 @@ createCancelParameters(ControlObjectClient self)
MmsValue* ctlTime; MmsValue* ctlTime;
if (self->edition == 2) if (self->edition == 2) {
ctlTime = MmsValue_newUtcTimeByMsTime(timestamp); ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
if (self->connection)
MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
}
else { else {
ctlTime = MmsValue_newBinaryTime(false); ctlTime = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(ctlTime, timestamp); MmsValue_setBinaryTime(ctlTime, timestamp);
@ -1220,7 +1239,7 @@ ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err, C
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId); printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
call->invokeId = MmsConnection_writeVariableAsync(self->connection->connection, &mmsError, domainId, itemId, cancelParameters, internalCancelHandler, self); MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId, cancelParameters, internalCancelHandler, self);
invokeId = call->invokeId; invokeId = call->invokeId;

@ -3,7 +3,7 @@
* *
* Client implementation for IEC 61850 reporting. * Client implementation for IEC 61850 reporting.
* *
* Copyright 2013-2019 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -383,11 +383,13 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
while (element != NULL) { while (element != NULL) {
ClientReport report = (ClientReport) element->data; ClientReport report = (ClientReport) element->data;
char defaultRptId[129]; char defaultRptId[130];
char* rptId = report->rptId; char* rptId = report->rptId;
if ((rptId == NULL) || (rptId && (strlen(rptId) == 0))) { if ((rptId == NULL) || (strlen(rptId) == 0)) {
//if ((rptId == NULL) || (rptId && (strlen(rptId) == 0))) {
strncpy(defaultRptId, report->rcbReference, 129); strncpy(defaultRptId, report->rcbReference, 129);
defaultRptId[129] = 0;
StringUtils_replace(defaultRptId, '.', '$'); StringUtils_replace(defaultRptId, '.', '$');
rptId = defaultRptId; rptId = defaultRptId;
} }

@ -624,7 +624,7 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const
MmsError err = MMS_ERROR_NONE; MmsError err = MMS_ERROR_NONE;
call->invokeId = MmsConnection_readVariableAsync(self->connection, &err, domainId, itemId, readObjectHandlerInternal, self); MmsConnection_readVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, readObjectHandlerInternal, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -758,7 +758,7 @@ writeMultipleVariablesHandler(uint32_t invokeId, void* parameter, MmsError mmsEr
} }
else { else {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n"); printf("IED_CLIENT: internal error - no matching outstanding call with invoke ID: %u!\n", invokeId);
} }
} }
@ -828,7 +828,7 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD
MmsError writeError; MmsError writeError;
call->invokeId = MmsConnection_writeVariableAsync(self->connection, &writeError, param->domainId, itemId, value, writeVariableHandler, self); MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &writeError, param->domainId, itemId, value, writeVariableHandler, self);
if (writeError != MMS_ERROR_NONE) { if (writeError != MMS_ERROR_NONE) {
handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError)); handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError));
@ -1006,7 +1006,7 @@ IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, Clien
if (singleRequest) { if (singleRequest) {
call->invokeId = MmsConnection_writeMultipleVariablesAsync(self->connection, &err, domainId, itemIds, values, writeMultipleVariablesHandler, self); MmsConnection_writeMultipleVariablesAsync(self->connection, &(call->invokeId), &err, domainId, itemIds, values, writeMultipleVariablesHandler, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -1035,7 +1035,7 @@ IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, Clien
char* variableId = (char*) LinkedList_getData(param->currentItemId); char* variableId = (char*) LinkedList_getData(param->currentItemId);
MmsValue* value = (MmsValue*) LinkedList_getData(param->currentValue); MmsValue* value = (MmsValue*) LinkedList_getData(param->currentValue);
call->invokeId = MmsConnection_writeVariableAsync(self->connection, &err, domainId, variableId, value, writeVariableHandler, self); MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &err, domainId, variableId, value, writeVariableHandler, self);
param->originalInvokeId = call->invokeId; param->originalInvokeId = call->invokeId;
@ -1094,7 +1094,17 @@ IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientRepo
LinkedList itemIds = LinkedList_create(); LinkedList itemIds = LinkedList_create();
LinkedList values = LinkedList_create(); LinkedList values = LinkedList_create();
/* add resv/resvTms as first element and rptEna as last element */ /* add rptEna = false as first element */
if (ClientReportControlBlock_getRptEna(rcb) == false) {
if (parametersMask & RCB_ELEMENT_RPT_ENA) {
strcpy(itemId + itemIdLen, "$RptEna");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->rptEna);
}
}
/* add resv/resvTms as first element and rptEna as last element when enabling a report */
if (parametersMask & RCB_ELEMENT_RESV) { if (parametersMask & RCB_ELEMENT_RESV) {
if (isBuffered) if (isBuffered)
goto error_invalid_parameter; goto error_invalid_parameter;
@ -1199,12 +1209,14 @@ IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientRepo
LinkedList_add(values, rcb->timeOfEntry); LinkedList_add(values, rcb->timeOfEntry);
} }
if (ClientReportControlBlock_getRptEna(rcb) == true) {
if (parametersMask & RCB_ELEMENT_RPT_ENA) { if (parametersMask & RCB_ELEMENT_RPT_ENA) {
strcpy(itemId + itemIdLen, "$RptEna"); strcpy(itemId + itemIdLen, "$RptEna");
LinkedList_add(itemIds, StringUtils_copyString(itemId)); LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->rptEna); LinkedList_add(values, rcb->rptEna);
} }
}
if (sendGILast) { if (sendGILast) {
strcpy(itemId + itemIdLen, "$GI"); strcpy(itemId + itemIdLen, "$GI");

@ -1,7 +1,7 @@
/* /*
* ied_connection.c * ied_connection.c
* *
* Copyright 2013-2019 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -672,6 +672,25 @@ IedConnection_getRequestTimeout(IedConnection self)
return 0; return 0;
} }
void
IedConnection_setTimeQuality(IedConnection self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision)
{
uint8_t timeQuality = 0;
if (clockNotSynchronized)
timeQuality += 0x20;
if (clockFailure)
timeQuality += 0x40;
if (leapSecondKnown)
timeQuality += 0x80;
timeQuality += (subsecondPrecision & 0x1f);
self->timeQuality = timeQuality;
}
IedConnectionState IedConnectionState
IedConnection_getState(IedConnection self) IedConnection_getState(IedConnection self)
{ {
@ -942,7 +961,7 @@ IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError*
call->callback = handler; call->callback = handler;
call->callbackParameter = parameter; call->callbackParameter = parameter;
call->invokeId = MmsConnection_getVariableAccessAttributesAsync(self->connection, &err, domainId, itemId, getAccessAttrHandler, self); MmsConnection_getVariableAccessAttributesAsync(self->connection, &(call->invokeId), &err, domainId, itemId, getAccessAttrHandler, self);
invokeId = call->invokeId; invokeId = call->invokeId;
@ -991,7 +1010,7 @@ IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error,
MmsError err = MMS_ERROR_NONE; MmsError err = MMS_ERROR_NONE;
call->invokeId = MmsConnection_getDomainNamesAsync(self->connection, &err, continueAfter, result, getNameListHandler, self); MmsConnection_getDomainNamesAsync(self->connection, &(call->invokeId), &err, continueAfter, result, getNameListHandler, self);
if (err != MMS_ERROR_NONE) { if (err != MMS_ERROR_NONE) {
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -1007,9 +1026,6 @@ IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error,
return call->invokeId; return call->invokeId;
} }
uint32_t uint32_t
IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result, IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result,
IedConnection_GetNameListHandler handler, void* parameter) IedConnection_GetNameListHandler handler, void* parameter)
@ -1026,7 +1042,7 @@ IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError*
MmsError err = MMS_ERROR_NONE; MmsError err = MMS_ERROR_NONE;
call->invokeId = MmsConnection_getDomainVariableNamesAsync(self->connection, &err, ldName, continueAfter, result, getNameListHandler, self); MmsConnection_getDomainVariableNamesAsync(self->connection, &(call->invokeId), &err, ldName, continueAfter, result, getNameListHandler, self);
if (err != MMS_ERROR_NONE) { if (err != MMS_ERROR_NONE) {
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -1058,7 +1074,7 @@ IedConnection_getLogicalDeviceDataSetsAsync(IedConnection self, IedClientError*
MmsError err = MMS_ERROR_NONE; MmsError err = MMS_ERROR_NONE;
call->invokeId = MmsConnection_getDomainVariableListNamesAsync(self->connection, &err, ldName, continueAfter, result, getNameListHandler, self); MmsConnection_getDomainVariableListNamesAsync(self->connection, &(call->invokeId), &err, ldName, continueAfter, result, getNameListHandler, self);
if (err != MMS_ERROR_NONE) { if (err != MMS_ERROR_NONE) {
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -1146,7 +1162,7 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c
*brace = 0; *brace = 0;
call->invokeId = MmsConnection_readSingleArrayElementWithComponentAsync(self->connection, &err, domainId, itemId, index, component, readObjectHandlerInternal, self); MmsConnection_readSingleArrayElementWithComponentAsync(self->connection, &(call->invokeId), &err, domainId, itemId, index, component, readObjectHandlerInternal, self);
} }
else else
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
@ -1155,7 +1171,7 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
} }
else else
call->invokeId = MmsConnection_readVariableAsync(self->connection, &err, domainId, itemId, readObjectHandlerInternal, self); MmsConnection_readVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, readObjectHandlerInternal, self);
if ((err != MMS_ERROR_NONE) || (*error != IED_ERROR_OK)) { if ((err != MMS_ERROR_NONE) || (*error != IED_ERROR_OK)) {
@ -1559,7 +1575,7 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const
*brace = 0; *brace = 0;
call->invokeId = MmsConnection_writeSingleArrayElementWithComponentAsync(self->connection, &err, domainId, itemId, index, component, value, MmsConnection_writeSingleArrayElementWithComponentAsync(self->connection, &(call->invokeId), &err, domainId, itemId, index, component, value,
writeVariableHandler, self); writeVariableHandler, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -1571,7 +1587,7 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
} }
else { else {
call->invokeId = MmsConnection_writeVariableAsync(self->connection, &err, domainId, itemId, value, writeVariableHandler, self); MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, value, writeVariableHandler, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
} }
@ -1660,7 +1676,6 @@ IedConnection_writeOctetString(IedConnection self, IedClientError* error, const
mmsValue.type = MMS_OCTET_STRING; mmsValue.type = MMS_OCTET_STRING;
mmsValue.value.octetString.buf = value; mmsValue.value.octetString.buf = value;
mmsValue.value.octetString.size = valueLength; mmsValue.value.octetString.size = valueLength;
mmsValue.value.octetString.size = valueLength;
IedConnection_writeObject(self, error, objectReference, fc, &mmsValue); IedConnection_writeObject(self, error, objectReference, fc, &mmsValue);
} }
@ -1885,7 +1900,7 @@ IedConnection_getFileDirectoryAsyncEx(IedConnection self, IedClientError* error,
call->callbackParameter = parameter; call->callbackParameter = parameter;
call->specificParameter2.getFileDirectory.cont = true; call->specificParameter2.getFileDirectory.cont = true;
call->invokeId = MmsConnection_getFileDirectoryAsync(self->connection, &err, directoryName, continueAfter, MmsConnection_getFileDirectoryAsync(self->connection, &(call->invokeId), &err, directoryName, continueAfter,
fileDirectoryHandlerEx, self); fileDirectoryHandlerEx, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -2009,7 +2024,7 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
if (mmsError != MMS_ERROR_SERVICE_TIMEOUT) { if (mmsError != MMS_ERROR_SERVICE_TIMEOUT) {
/* close file */ /* close file */
call->invokeId = MmsConnection_fileCloseAsync(self->connection, &mmsError, frsmId, mmsConnectionFileCloseHandler, self); MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self);
if (mmsError != MMS_ERROR_NONE) if (mmsError != MMS_ERROR_NONE)
iedConnection_releaseOutstandingCall(self, call); iedConnection_releaseOutstandingCall(self, call);
@ -2026,7 +2041,7 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
if ((moreFollows == false) || (cont == false)) { if ((moreFollows == false) || (cont == false)) {
/* close file */ /* close file */
call->invokeId = MmsConnection_fileCloseAsync(self->connection, &mmsError, frsmId, mmsConnectionFileCloseHandler, self); MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self);
if (mmsError != MMS_ERROR_NONE) if (mmsError != MMS_ERROR_NONE)
iedConnection_releaseOutstandingCall(self, call); iedConnection_releaseOutstandingCall(self, call);
@ -2034,7 +2049,7 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
else { else {
/* send next read request */ /* send next read request */
call->invokeId = MmsConnection_fileReadAsync(self->connection, &mmsError, frsmId, MmsConnection_fileReadAsync(self->connection, &(call->invokeId), &mmsError, frsmId,
mmsConnectionFileReadHandler, self); mmsConnectionFileReadHandler, self);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE) {
@ -2043,7 +2058,7 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false);
/* close file */ /* close file */
call->invokeId = MmsConnection_fileCloseAsync(self->connection, &mmsError, frsmId, mmsConnectionFileCloseHandler, self); MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self, call); iedConnection_releaseOutstandingCall(self, call);
@ -2085,7 +2100,7 @@ mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
} }
else { else {
call->specificParameter2.getFileInfo.originalInvokeId = invokeId; call->specificParameter2.getFileInfo.originalInvokeId = invokeId;
call->invokeId = MmsConnection_fileReadAsync(self->connection, &mmsError, frsmId, mmsConnectionFileReadHandler, self); MmsConnection_fileReadAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileReadHandler, self);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE) {
IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError);
@ -2093,7 +2108,7 @@ mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false);
/* close file */ /* close file */
call->invokeId = MmsConnection_fileCloseAsync(self->connection, &mmsError, frsmId, mmsConnectionFileCloseHandler, self); MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self);
if (mmsError != MMS_ERROR_NONE) if (mmsError != MMS_ERROR_NONE)
iedConnection_releaseOutstandingCall(self, call); iedConnection_releaseOutstandingCall(self, call);
@ -2124,7 +2139,7 @@ IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char
call->callback = handler; call->callback = handler;
call->callbackParameter = parameter; call->callbackParameter = parameter;
call->invokeId = MmsConnection_fileOpenAsync(self->connection, &err, fileName, 0, mmsConnectionFileOpenHandler, self); MmsConnection_fileOpenAsync(self->connection, &(call->invokeId), &err, fileName, 0, mmsConnectionFileOpenHandler, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -2199,7 +2214,7 @@ IedConnection_setFileAsync(IedConnection self, IedClientError* error, const char
call->callback = handler; call->callback = handler;
call->callbackParameter = parameter; call->callbackParameter = parameter;
call->invokeId = MmsConnection_obtainFileAsync(self->connection, &err, sourceFilename, destinationFilename, deleteFileAndSetFileHandler, self); MmsConnection_obtainFileAsync(self->connection, &(call->invokeId), &err, sourceFilename, destinationFilename, deleteFileAndSetFileHandler, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -2239,7 +2254,7 @@ IedConnection_deleteFileAsync(IedConnection self, IedClientError* error, const c
call->callback = handler; call->callback = handler;
call->callbackParameter = parameter; call->callbackParameter = parameter;
call->invokeId = MmsConnection_fileDeleteAsync(self->connection, &err, fileName, deleteFileAndSetFileHandler, self); MmsConnection_fileDeleteAsync(self->connection, &(call->invokeId), &err, fileName, deleteFileAndSetFileHandler, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -2993,7 +3008,6 @@ void
IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference, IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference,
LinkedList /* <char*> */dataSetElements) LinkedList /* <char*> */dataSetElements)
{ {
char domainIdBuffer[65]; char domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
@ -3137,6 +3151,250 @@ exit_function:
return isDeleted; return isDeleted;
} }
static void
deleteNamedVariableListHandler(uint32_t invokeId, void* parameter, MmsError mmsError, bool success)
{
IedConnection self = (IedConnection) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler)call->callback;
IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError);
if (err == IED_ERROR_OK) {
if (success == false)
err = IED_ERROR_ACCESS_DENIED;
}
handler(invokeId, call->callbackParameter, err);
iedConnection_releaseOutstandingCall(self, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
IedConnection_GenericServiceHandler handler, void* parameter)
{
*error = IED_ERROR_OK;
char domainIdBuf[65];
char *domainId = domainIdBuf;
char itemId[DATA_SET_MAX_NAME_LENGTH + 1];
bool isAssociationSpecific = false;
int dataSetReferenceLength = strlen(dataSetReference);
if (dataSetReference[0] != '@') {
if ((dataSetReference[0] == '/')
|| (strchr(dataSetReference, '/') == NULL)) {
domainId = NULL;
if (dataSetReference[0] == '/')
strcpy(itemId, dataSetReference + 1);
else
strcpy(itemId, dataSetReference);
}
else {
if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference,
domainId) == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return 0;
}
const char *itemIdString = dataSetReference + strlen(domainId) + 1;
if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return 0;
}
StringUtils_copyStringToBuffer(itemIdString, itemId);
StringUtils_replace(itemId, '.', '$');
}
}
else {
if (dataSetReferenceLength > 33) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return 0;
}
strcpy(itemId, dataSetReference + 1);
isAssociationSpecific = true;
}
MmsError mmsError;
if ((domainId == NULL) || (itemId == NULL)) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return 0;
}
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
if (call == NULL) {
*error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
return 0;
}
call->callback = handler;
call->callbackParameter = parameter;
call->invokeId = 0;
if (isAssociationSpecific) {
MmsConnection_deleteAssociationSpecificNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, itemId, deleteNamedVariableListHandler, self);
}
else {
MmsConnection_deleteNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, deleteNamedVariableListHandler, self);
}
if (*error != IED_ERROR_OK) {
iedConnection_releaseOutstandingCall(self, call);
return 0;
}
return call->invokeId;
}
static void
createDataSetAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, bool success)
{
IedConnection self = (IedConnection) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler)call->callback;
IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError);
if (err == IED_ERROR_OK) {
if (success == false)
err = IED_ERROR_ACCESS_DENIED;
}
handler(invokeId, call->callbackParameter, err);
iedConnection_releaseOutstandingCall(self, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements,
IedConnection_GenericServiceHandler handler, void* parameter)
{
uint32_t invokeId = 0;
char domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
const char* domainId;
const char* itemId;
bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') {
if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) {
domainId = NULL;
if (dataSetReference[0] == '/')
itemId = dataSetReference + 1;
else
itemId = dataSetReference;
}
else {
domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
if (domainId == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
goto exit_function;
}
int domainIdLength = strlen(domainId);
if ((strlen(dataSetReference) - domainIdLength - 1) > 32) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
goto exit_function;
}
char* itemIdRef = StringUtils_copyStringToBuffer(dataSetReference + domainIdLength + 1, itemIdBuffer);
StringUtils_replace(itemIdRef, '.', '$');
itemId = itemIdRef;
}
}
else {
itemId = dataSetReference + 1;
isAssociationSpecific = true;
}
MmsError mmsError;
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
if (call == NULL) {
*error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function;
}
call->callback = handler;
call->callbackParameter = parameter;
call->invokeId = 0;
LinkedList dataSetEntries = LinkedList_create();
LinkedList dataSetElement = LinkedList_getNext(dataSetElements);
while (dataSetElement != NULL) {
MmsVariableAccessSpecification* dataSetEntry =
MmsMapping_ObjectReferenceToVariableAccessSpec((char*) dataSetElement->data);
if (dataSetEntry == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
goto cleanup_list;
}
LinkedList_add(dataSetEntries, (void*) dataSetEntry);
dataSetElement = LinkedList_getNext(dataSetElement);
}
if (isAssociationSpecific) {
MmsConnection_defineNamedVariableListAssociationSpecificAsync(self->connection, &(call->invokeId),
&mmsError, itemId, dataSetEntries, createDataSetAsyncHandler, self);
}
else {
MmsConnection_defineNamedVariableListAsync(self->connection, &(call->invokeId),
&mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self);
}
invokeId = call->invokeId;
*error = iedConnection_mapMmsErrorToIedError(mmsError);
cleanup_list:
/* delete list and all elements */
LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy);
exit_function:
return invokeId;
}
LinkedList /* <char*> */ LinkedList /* <char*> */
IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable) IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable)
{ {
@ -3203,8 +3461,8 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con
dataSetMembers = LinkedList_create(); dataSetMembers = LinkedList_create();
while (entry != NULL) { while (entry) {
MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*) entry->data; MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(entry);
char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec); char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec);
@ -3225,6 +3483,120 @@ exit_function:
return dataSetMembers; return dataSetMembers;
} }
static void
getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsVariableAccessSpecification*> */ specs, bool deletable)
{
IedConnection self = (IedConnection)parameter;
IedClientError err = IED_ERROR_OK;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
LinkedList dataSetMembers = NULL;
if (mmsError != MMS_ERROR_NONE)
err = iedConnection_mapMmsErrorToIedError(mmsError);
if (specs) {
dataSetMembers = LinkedList_create();
LinkedList specElem = LinkedList_getNext(specs);
while (specElem) {
MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(specElem);
char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec);
LinkedList_add(dataSetMembers, objectReference);
specElem = LinkedList_getNext(specElem);
}
}
IedConnection_GetDataSetDirectoryHandler handler = (IedConnection_GetDataSetDirectoryHandler)call->callback;
if (handler)
handler(call->invokeId, call->callbackParameter, err, dataSetMembers, deletable);
}
if (specs)
LinkedList_destroyDeep(specs, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy);
}
uint32_t
IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
IedConnection_GetDataSetDirectoryHandler handler, void* parameter)
{
uint32_t invokeId = 0;
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
if (call == NULL) {
*error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
return 0;
}
call->callback = handler;
call->callbackParameter = parameter;
call->invokeId = 0;
char domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
const char* domainId = NULL;
const char* itemId = NULL;
bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') {
if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) {
domainId = NULL;
if (dataSetReference[0] == '/')
itemId = dataSetReference + 1;
else
itemId = dataSetReference;
}
else {
domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
if (domainId == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
goto exit_function;
}
const char* itemIdRef = dataSetReference + strlen(domainId) + 1;
if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
goto exit_function;
}
char* itemIdRefInBuffer = StringUtils_copyStringToBuffer(itemIdRef, itemIdBuffer);
StringUtils_replace(itemIdRefInBuffer, '.', '$');
itemId = itemIdRefInBuffer;
}
}
else {
itemId = dataSetReference + 1;
isAssociationSpecific = true;
}
MmsError mmsError = MMS_ERROR_NONE;
if (isAssociationSpecific)
MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self);
else
MmsConnection_readNamedVariableListDirectoryAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, getDataSetDirectoryAsyncHandler, self);
*error = iedConnection_mapMmsErrorToIedError(mmsError);
exit_function:
return invokeId;
}
ClientDataSet ClientDataSet
IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference, IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
ClientDataSet dataSet) ClientDataSet dataSet)
@ -3410,10 +3782,10 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error,
MmsError err = MMS_ERROR_NONE; MmsError err = MMS_ERROR_NONE;
if (isAssociationSpecific) if (isAssociationSpecific)
call->invokeId = MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(self->connection, MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(self->connection, &(call->invokeId),
&err, itemId, true, getDataSetHandlerInternal, self); &err, itemId, true, getDataSetHandlerInternal, self);
else else
call->invokeId = MmsConnection_readNamedVariableListValuesAsync(self->connection, &err, MmsConnection_readNamedVariableListValuesAsync(self->connection, &(call->invokeId), &err,
domainId, itemId, true, getDataSetHandlerInternal, self); domainId, itemId, true, getDataSetHandlerInternal, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -3569,7 +3941,7 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error,
MmsError err = MMS_ERROR_NONE; MmsError err = MMS_ERROR_NONE;
call->invokeId = MmsConnection_writeNamedVariableListAsync(self->connection, &err, isAssociationSpecific, domainId, itemId, values, writeDataSetHandlerInternal, self); MmsConnection_writeNamedVariableListAsync(self->connection, &(call->invokeId), &err, isAssociationSpecific, domainId, itemId, values, writeDataSetHandlerInternal, self);
*error = iedConnection_mapMmsErrorToIedError(err); *error = iedConnection_mapMmsErrorToIedError(err);
@ -3591,6 +3963,7 @@ IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const ch
char logRef[130]; char logRef[130];
strncpy(logRef, logReference, 129); strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef; char* logDomain = logRef;
char* logName = strchr(logRef, '/'); char* logName = strchr(logRef, '/');
@ -3654,6 +4027,7 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con
char logRef[130]; char logRef[130];
strncpy(logRef, logReference, 129); strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef; char* logDomain = logRef;
char* logName = strchr(logRef, '/'); char* logName = strchr(logRef, '/');
@ -3681,7 +4055,7 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con
MmsValue* endTimeMms = MmsValue_newBinaryTime(false); MmsValue* endTimeMms = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(endTimeMms, endTime); MmsValue_setBinaryTime(endTimeMms, endTime);
call->invokeId = MmsConnection_readJournalTimeRangeAsync(self->connection, &err, logDomain, logName, MmsConnection_readJournalTimeRangeAsync(self->connection, &(call->invokeId), &err, logDomain, logName,
startTimeMms, endTimeMms, readJournalHandler, self); startTimeMms, endTimeMms, readJournalHandler, self);
MmsValue_delete(startTimeMms); MmsValue_delete(startTimeMms);
@ -3710,6 +4084,7 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons
char logRef[130]; char logRef[130];
strncpy(logRef, logReference, 129); strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef; char* logDomain = logRef;
char* logName = strchr(logRef, '/'); char* logName = strchr(logRef, '/');
@ -3734,7 +4109,7 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons
MmsValue* timeStampMms = MmsValue_newBinaryTime(false); MmsValue* timeStampMms = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(timeStampMms, timeStamp); MmsValue_setBinaryTime(timeStampMms, timeStamp);
call->invokeId = MmsConnection_readJournalStartAfterAsync(self->connection, &err, logDomain, logName, MmsConnection_readJournalStartAfterAsync(self->connection, &(call->invokeId), &err, logDomain, logName,
timeStampMms, entryID, readJournalHandler, self); timeStampMms, entryID, readJournalHandler, self);
MmsValue_delete(timeStampMms); MmsValue_delete(timeStampMms);
@ -3763,6 +4138,7 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha
char logRef[130]; char logRef[130];
strncpy(logRef, logReference, 129); strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef; char* logDomain = logRef;
char* logName = strchr(logRef, '/'); char* logName = strchr(logRef, '/');
@ -3793,8 +4169,6 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha
} }
} }
MmsConnection MmsConnection
IedConnection_getMmsConnection(IedConnection self) IedConnection_getMmsConnection(IedConnection self)
{ {

@ -1,7 +1,7 @@
/* /*
* iec61850_common.c * iec61850_common.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -70,6 +70,20 @@ Quality_fromMmsValue(const MmsValue* mmsValue)
return (Quality) MmsValue_getBitStringAsInteger(mmsValue); return (Quality) MmsValue_getBitStringAsInteger(mmsValue);
} }
MmsValue*
Quality_toMmsValue(Quality* self, MmsValue* mmsValue)
{
if (mmsValue == NULL) {
mmsValue = MmsValue_newBitString(13);
}
if (mmsValue) {
MmsValue_setBitStringFromInteger(mmsValue, *self);
}
return mmsValue;
}
Dbpos Dbpos
Dbpos_fromMmsValue(const MmsValue* mmsValue) Dbpos_fromMmsValue(const MmsValue* mmsValue)
{ {
@ -242,7 +256,7 @@ Timestamp_create()
} }
Timestamp* Timestamp*
Timestamp_createFromByteArray(uint8_t* byteArray) Timestamp_createFromByteArray(const uint8_t* byteArray)
{ {
Timestamp* self = Timestamp_create(); Timestamp* self = Timestamp_create();
@ -471,7 +485,7 @@ Timestamp_getTimeInNs(Timestamp* self)
} }
void void
Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue) Timestamp_setByMmsUtcTime(Timestamp* self, const MmsValue* mmsValue)
{ {
if (MmsValue_getType(mmsValue) == MMS_UTC_TIME) if (MmsValue_getType(mmsValue) == MMS_UTC_TIME)
memcpy(self->val, mmsValue->value.utcTime, 8); memcpy(self->val, mmsValue->value.utcTime, 8);
@ -491,6 +505,25 @@ Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue)
return convertedValue; return convertedValue;
} }
Timestamp*
Timestamp_fromMmsValue(Timestamp* self, MmsValue* mmsValue)
{
if (mmsValue->type == MMS_UTC_TIME) {
if (self == NULL)
self = Timestamp_create();
if (self) {
memcpy(self->val, mmsValue->value.utcTime, 8);
}
return self;
}
else {
return NULL;
}
}
char* char*
MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer) MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer)
{ {

@ -1,7 +1,7 @@
/* /*
* iec61850_client.h * iec61850_client.h
* *
* Copyright 2013-2019 Michael Zillgith * Copyright 2013-2021 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -266,6 +266,18 @@ IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs);
LIB61850_API uint32_t LIB61850_API uint32_t
IedConnection_getRequestTimeout(IedConnection self); IedConnection_getRequestTimeout(IedConnection self);
/**
* \brief Set the time quality for all timestamps generated by this IedConnection instance
*
* \param self the connection object
* \param leapSecondKnown set/unset leap seconds known flag
* \param clockFailure set/unset clock failure flag
* \param clockNotSynchronized set/unset clock not synchronized flag
* \param subsecondPrecision set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)
*/
LIB61850_API void
IedConnection_setTimeQuality(IedConnection self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision);
/** /**
* \brief Perform MMS message handling and house-keeping tasks (for non-thread mode only) * \brief Perform MMS message handling and house-keeping tasks (for non-thread mode only)
* *
@ -1759,6 +1771,31 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error,
LIB61850_API void LIB61850_API void
IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements); IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements);
/**
* \brief create a new data set at the connected server device
*
* This function creates a new data set at the server. The parameter dataSetReference is the name of the new data set
* to create. It is either in the form LDName/LNodeName.dataSetName for permanent domain or VMD scope data sets or
* @dataSetName for an association specific data set. If the LDName part of the reference is missing the resulting
* data set will be of VMD scope.
*
* The dataSetElements parameter contains a linked list containing the object references of FCDs or FCDAs. The format of
* this object references is LDName/LNodeName.item(arrayIndex)component[FC].
*
* \param connection the connection object
* \param error the error code if an error occurs
* \param dataSetReference object reference of the data set
* \param dataSetElements a list of object references defining the members of the new data set
*
* \param handler the callback handler that is called when the response is received or timeout
* \param parameter user provided parameter that is passed to the callback handler
*
* \return the invoke ID of the request
*/
LIB61850_API uint32_t
IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements,
IedConnection_GenericServiceHandler handler, void* parameter);
/** /**
* \brief delete a deletable data set at the connected server device * \brief delete a deletable data set at the connected server device
* *
@ -1775,9 +1812,29 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha
LIB61850_API bool LIB61850_API bool
IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const char* dataSetReference); IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const char* dataSetReference);
/**
* \brief delete a deletable data set at the connected server device - asynchronous version
*
* This function deletes a data set at the server. The parameter dataSetReference is the name of the data set
* to delete. It is either in the form LDName/LNodeName.dataSetName or @dataSetName for an association specific data set.
*
* The data set was deleted successfully when the callback parameter "error" is IED_ERROR_OK. Otherwise the "error"
* parameter contains a particular error code.
*
* \param connection the connection object
* \param error the error code if an error occurs
* \param dataSetReference object reference of the data set
* \param handler the callback handler that is called when the response is received or timeout
* \param parameter user provided parameter that is passed to the callback handler
*
* \return the invoke ID of the request
*/
LIB61850_API uint32_t
IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
IedConnection_GenericServiceHandler handler, void* parameter);
/** /**
* \brief returns the object references of the elements of a data set * \brief read the data set directory
* *
* The return value contains a linked list containing the object references of FCDs or FCDAs. The format of * The return value contains a linked list containing the object references of FCDs or FCDAs. The format of
* this object references is LDName/LNodeName.item(arrayIndex)component[FC]. * this object references is LDName/LNodeName.item(arrayIndex)component[FC].
@ -1793,6 +1850,34 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha
LIB61850_API LinkedList /* <char*> */ LIB61850_API LinkedList /* <char*> */
IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable); IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable);
/**
* \brief GetDataSetDirectory response or timeout callback
*
* \param dataSetDirectory a linked list containing the object references of FCDs or FCDAs. The format of
* this object references is LDName/LNodeName.item(arrayIndex)component[FC].
* \param isDeletable this is an output parameter indicating that the requested data set is deletable by clients.
*/
typedef void
(*IedConnection_GetDataSetDirectoryHandler) (uint32_t invokeId, void* parameter, IedClientError err, LinkedList /* <char*> */ dataSetDirectory, bool isDeletable);
/**
* \brief read the data set directory - asynchronous version
*
* The result data is a linked list containing the object references of FCDs or FCDAs. The format of
* this object references is LDName/LNodeName.item(arrayIndex)component[FC].
*
* \param connection the connection object
* \param[out] error the error code if an error occurs
* \param dataSetReference object reference of the data set
* \param handler the callback handler that is called when the response is received or timeout
* \param parameter user provided parameter that is passed to the callback handler
*
* \return the invoke ID of the request
*/
LIB61850_API uint32_t
IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
IedConnection_GetDataSetDirectoryHandler handler, void* parameter);
/** /**
* \brief Write the data set values to the server * \brief Write the data set values to the server
* *
@ -2884,6 +2969,8 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil
* \param buffer the buffer that contains the received file data * \param buffer the buffer that contains the received file data
* \param bytesRead the number of bytes read into the buffer * \param bytesRead the number of bytes read into the buffer
* \param moreFollows indicates that more file data is following * \param moreFollows indicates that more file data is following
*
* \return true, continue the file download when moreFollows is true, false, stop file download
*/ */
typedef bool typedef bool
(*IedConnection_GetFileAsyncHandler) (uint32_t invokeId, void* parameter, IedClientError err, uint32_t originalInvokeId, (*IedConnection_GetFileAsyncHandler) (uint32_t invokeId, void* parameter, IedClientError err, uint32_t originalInvokeId,

@ -370,6 +370,9 @@ Quality_isFlagSet(Quality* self, int flag);
LIB61850_API Quality LIB61850_API Quality
Quality_fromMmsValue(const MmsValue* mmsValue); Quality_fromMmsValue(const MmsValue* mmsValue);
LIB61850_API MmsValue*
Quality_toMmsValue(Quality* self, MmsValue* mmsValue);
/** @} */ /** @} */
/** /**
@ -423,7 +426,7 @@ LIB61850_API Timestamp*
Timestamp_create(void); Timestamp_create(void);
LIB61850_API Timestamp* LIB61850_API Timestamp*
Timestamp_createFromByteArray(uint8_t* byteArray); Timestamp_createFromByteArray(const uint8_t* byteArray);
LIB61850_API void LIB61850_API void
Timestamp_destroy(Timestamp* self); Timestamp_destroy(Timestamp* self);
@ -504,7 +507,7 @@ LIB61850_API void
Timestamp_setTimeInNanoseconds(Timestamp* self, nsSinceEpoch nsTime); Timestamp_setTimeInNanoseconds(Timestamp* self, nsSinceEpoch nsTime);
LIB61850_API void LIB61850_API void
Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue); Timestamp_setByMmsUtcTime(Timestamp* self, const MmsValue* mmsValue);
/** /**
* \brief Set an MmsValue instance of type UTCTime to the timestamp value * \brief Set an MmsValue instance of type UTCTime to the timestamp value
@ -515,6 +518,17 @@ Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue);
LIB61850_API MmsValue* LIB61850_API MmsValue*
Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue); Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue);
/**
* \brief Get the Timestamp value from an MmsValue instance of type MMS_UTC_TIME
*
* \param self the Timestamp instance or NULL to create a new instance
* \param mmsValue the mmsValue instance of type MMS_UTC_TIME
*
* \return the updated Timestamp value or NULL in case of an error
*/
LIB61850_API Timestamp*
Timestamp_fromMmsValue(Timestamp* self, MmsValue* mmsValue);
/** /**
* \brief Get the version of the library as string * \brief Get the version of the library as string
* *

@ -46,14 +46,14 @@ extern "C" {
/** /**
* \brief create a new IedModel instance * \brief create a new IedModel instance
* *
* The IedModel object is the root node of an IEC 61850 service data model. * The IedModel object is the root node of an IEC 61850 data model.
* *
* \param name the name of the IedModel or NULL (optional - NOT YET USED) * \param name the name of the IedModel
* *
* \return * \return the new data model instance
*/ */
LIB61850_API IedModel* LIB61850_API IedModel*
IedModel_create(const char* name/*, MemoryAllocator allocator*/); IedModel_create(const char* name);
/** /**
* \brief Set the name of the IED (use only for dynamic model!) * \brief Set the name of the IED (use only for dynamic model!)
@ -210,7 +210,7 @@ DataAttribute_setValue(DataAttribute* self, MmsValue* value);
* \return the new RCB instance. * \return the new RCB instance.
*/ */
LIB61850_API ReportControlBlock* LIB61850_API ReportControlBlock*
ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bool isBuffered, char* ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rptId, bool isBuffered, const char*
dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd); dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd);
/** /**
@ -223,7 +223,156 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bo
* \param clientAddress buffer containing the client address (4 byte in case of an IPv4 address, 16 byte in case of an IPv6 address, NULL for no client) * \param clientAddress buffer containing the client address (4 byte in case of an IPv4 address, 16 byte in case of an IPv6 address, NULL for no client)
*/ */
LIB61850_API void LIB61850_API void
ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clientType, uint8_t* clientAddress); ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clientType, const uint8_t* clientAddress);
/**
* \brief Get the name of the RCB instance
*
* NOTE: the returned string is only valid during the lifetime of the ReportControlBlock instance!
*
* \param self the RCB instance
*
* \return the RCB instance name
*/
LIB61850_API const char*
ReportControlBlock_getName(ReportControlBlock* self);
/**
* \brief Is the RCB buffered or unbuffered?
*
* \param self the RCB instance
*
* \return true, in case of a buffered RCB, false otherwise
*/
LIB61850_API bool
ReportControlBlock_isBuffered(ReportControlBlock* self);
/**
* \brief Get the parent (LogicalNode) of the RCB instance
*
* \param self the RCB instance
*
* \return the parent (LogicalNode) of the RCB instance
*/
LIB61850_API LogicalNode*
ReportControlBlock_getParent(ReportControlBlock* self);
/**
* \brief Get the name of the currently set report ID
*
* \param self the RCB instance
*
* \return a null terminated string containing the current data set name (the string has to be released by the caller!)
*/
LIB61850_API char*
ReportControlBlock_getRptID(ReportControlBlock* self);
/**
* \brief Check if RCB instance is enabled
*
* \param self the RCB instance
*
* \return true when the RCB instance is enabled, false otherwise
*/
LIB61850_API bool
ReportControlBlock_getRptEna(ReportControlBlock* self);
/**
* \brief Get the name of the currenlty set data set
*
* \param self the RCB instance
*
* \return a null terminated string containing the current data set name (the string has to be released by the caller!)
*/
LIB61850_API char*
ReportControlBlock_getDataSet(ReportControlBlock* self);
/**
* \brief Get the confRev value
*
* \param self the RCB instance
*
* \return confRev value
*/
LIB61850_API uint32_t
ReportControlBlock_getConfRev(ReportControlBlock* self);
/**
* \brief Get the currently set OptFlds value
*
* The OptField (option field) value is a bit field with the following fields:
* - RPT_OPT_SEQ_NUM
* - RPT_OPT_TIME_STAMP
* - RPT_OPT_REASON_FOR_INCLUSION
* - RPT_OPT_DATA_SET
* - RPT_OPT_DATA_REFERENCE
* - RPT_OPT_BUFFER_OVERFLOW
* - RPT_OPT_ENTRY_ID
* - RPT_OPT_CONF_REV
*
* \param self the RCB instance
*
* \return OptFlds options value
*/
LIB61850_API uint32_t
ReportControlBlock_getOptFlds(ReportControlBlock* self);
/**
* \brief Get the BufTm value (buffer time)
*
* The buffer time is the maximum value between an event and
* the actual report generation.
*
* \param self the RCB instance
*
* \return bufTm value
*/
LIB61850_API uint32_t
ReportControlBlock_getBufTm(ReportControlBlock* self);
LIB61850_API uint16_t
ReportControlBlock_getSqNum(ReportControlBlock* self);
/**
* \brief Get the currently set trigger options
*
* The trigger option value is a bit field with the following fields:
* - TRG_OPT_DATA_CHANGED
* - TRG_OPT_QUALITY_CHANGED
* - TRG_OPT_DATA_UPDATE
* - TRG_OPT_INTEGRITY
* - TRG_OPT_GI
*
* \param self the RCB instance
*
* \return trigger options value
*/
LIB61850_API uint32_t
ReportControlBlock_getTrgOps(ReportControlBlock* self);
LIB61850_API uint32_t
ReportControlBlock_getIntgPd(ReportControlBlock* self);
LIB61850_API bool
ReportControlBlock_getGI(ReportControlBlock* self);
LIB61850_API bool
ReportControlBlock_getPurgeBuf(ReportControlBlock* self);
LIB61850_API MmsValue*
ReportControlBlock_getEntryId(ReportControlBlock* self);
LIB61850_API uint64_t
ReportControlBlock_getTimeofEntry(ReportControlBlock* self);
LIB61850_API int16_t
ReportControlBlock_getResvTms(ReportControlBlock* self);
LIB61850_API bool
ReportControlBlock_getResv(ReportControlBlock* self);
LIB61850_API MmsValue*
ReportControlBlock_getOwner(ReportControlBlock* self);
/** /**
* \brief create a new log control block (LCB) * \brief create a new log control block (LCB)
@ -243,7 +392,7 @@ ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clie
* \return the new LCB instance * \return the new LCB instance
*/ */
LIB61850_API LogControlBlock* LIB61850_API LogControlBlock*
LogControlBlock_create(const char* name, LogicalNode* parent, char* dataSetName, char* logRef, uint8_t trgOps, LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps,
uint32_t intgPd, bool logEna, bool reasonCode); uint32_t intgPd, bool logEna, bool reasonCode);
/** /**
@ -288,7 +437,7 @@ SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numO
* \return the new GoCB instance * \return the new GoCB instance
*/ */
LIB61850_API GSEControlBlock* LIB61850_API GSEControlBlock*
GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRev, GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, const char* dataSet, uint32_t confRev,
bool fixedOffs, int minTime, int maxTime); bool fixedOffs, int minTime, int maxTime);
/** /**
@ -308,7 +457,7 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char*
* \return the new SvCB instance * \return the new SvCB instance
*/ */
LIB61850_API SVControlBlock* LIB61850_API SVControlBlock*
SVControlBlock_create(const char* name, LogicalNode* parent, char* svID, char* dataSet, uint32_t confRev, uint8_t smpMod, SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, const char* dataSet, uint32_t confRev, uint8_t smpMod,
uint16_t smpRate, uint8_t optFlds, bool isUnicast); uint16_t smpRate, uint8_t optFlds, bool isUnicast);
LIB61850_API void LIB61850_API void

@ -272,7 +272,9 @@ struct sReportControlBlock {
type can be one of (0 - no reservation, 4 - IPv4 client, 6 - IPv6 client) */ type can be one of (0 - no reservation, 4 - IPv4 client, 6 - IPv6 client) */
uint8_t clientReservation[17]; uint8_t clientReservation[17];
ReportControlBlock* sibling; /* next control block in list or NULL if this is the last entry */ ReportControlBlock* sibling; /* next control block in list or NULL if this is the last entry
* at runtime reuse as pointer to ReportControl instance!
**/
}; };
struct sLogControlBlock { struct sLogControlBlock {

@ -3,7 +3,7 @@
* *
* IEC 61850 server API for libiec61850. * IEC 61850 server API for libiec61850.
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -96,6 +96,9 @@ struct sIedServerConfig
/** RCB has owner attribute (default: true) */ /** RCB has owner attribute (default: true) */
bool enableOwnerForRCB; bool enableOwnerForRCB;
/** integrity report start times will by synchronized with straight numbers (default: false) */
bool syncIntegrityReportTimes;
}; };
/** /**
@ -179,6 +182,30 @@ IedServerConfig_setMaxMmsConnections(IedServerConfig self, int maxConnections);
LIB61850_API int LIB61850_API int
IedServerConfig_getMaxMmsConnections(IedServerConfig self); IedServerConfig_getMaxMmsConnections(IedServerConfig self);
/**
* \brief Enable synchronized integrity report times
*
* NOTE: When this flag is enabled the integrity report generation times are
* aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
* integrity interval.
*
* \param enable when true synchronized integrity report times are enabled
*/
void
IedServerConfig_setSyncIntegrityReportTimes(IedServerConfig self, bool enable);
/**
* \brief Check if synchronized integrity report times are enabled
*
* NOTE: When this flag is enabled the integrity report generation times are
* aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
* integrity interval.
*
* \return true, when enabled, false otherwise
*/
bool
IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self);
/** /**
* \brief Set the basepath of the file services * \brief Set the basepath of the file services
* *
@ -911,9 +938,8 @@ IedServer_getBitStringAttributeValue(IedServer self, const DataAttribute* dataAt
LIB61850_API const char* LIB61850_API const char*
IedServer_getStringAttributeValue(IedServer self, const DataAttribute* dataAttribute); IedServer_getStringAttributeValue(IedServer self, const DataAttribute* dataAttribute);
/** /**
* \brief Get the MmsValue object related to a FunctionalConstrainedData object * \brief Get the MmsValue object related to a functional constrained data object (FCD)
* *
* Get the MmsValue from the server cache that is associated with the Functional Constrained Data (FCD) * Get the MmsValue from the server cache that is associated with the Functional Constrained Data (FCD)
* object that is specified by the DataObject and the given Function Constraint (FC). * object that is specified by the DataObject and the given Function Constraint (FC).
@ -1534,6 +1560,49 @@ IedServer_updateCtlModel(IedServer self, DataObject* ctlObject, ControlModel val
/**@}*/ /**@}*/
/**
* @defgroup IEC61850_SERVER_RCB Server side report control block (RCB) handling
*
* @{
*/
typedef enum {
RCB_EVENT_GET_PARAMETER, /* << parameter read by client (not implemented) */
RCB_EVENT_SET_PARAMETER, /* << parameter set by client */
RCB_EVENT_UNRESERVED, /* << RCB reservation canceled */
RCB_EVENT_RESERVED, /* << RCB reserved */
RCB_EVENT_ENABLE, /* << RCB enabled */
RCB_EVENT_DISABLE, /* << RCB disabled */
RCB_EVENT_GI, /* << GI report triggered */
RCB_EVENT_PURGEBUF, /* << Purge buffer procedure executed */
RCB_EVENT_OVERFLOW, /* << Report buffer overflow */
RCB_EVENT_REPORT_CREATED /* << A new report was created and inserted into the buffer */
} IedServer_RCBEventType;
/**
* \brief Callback that is called in case of RCB event
*
* \param parameter user provided parameter
* \param rcb affected report control block
* \param connection client connection that is involved
* \param event event type
* \param parameterName name of the parameter in case of RCB_EVENT_SET_PARAMETER
* \param serviceError service error in case of RCB_EVENT_SET_PARAMETER
*/
typedef void (*IedServer_RCBEventHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError);
/**
* \brief Set a handler for report control block (RCB) events
*
* \param self the instance of IedServer to operate on.
* \param handler the event handler to be used
* \param parameter a user provided parameter that is passed to the handler.
*/
LIB61850_API void
IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, void* parameter);
/**@}*/
/** /**
* @defgroup IEC61850_SERVER_SVCB Server side sampled values control block (SVCB) handling * @defgroup IEC61850_SERVER_SVCB Server side sampled values control block (SVCB) handling
* *

@ -173,9 +173,6 @@ ControlObject_getDomain(ControlObject* self);
LIB61850_INTERNAL bool LIB61850_INTERNAL bool
ControlObject_select(ControlObject* self, MmsServerConnection connection); ControlObject_select(ControlObject* self, MmsServerConnection connection);
LIB61850_INTERNAL bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection);
LIB61850_INTERNAL void LIB61850_INTERNAL void
ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter); ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter);

@ -75,6 +75,8 @@ struct sIedConnection
void* connectionStateChangedHandlerParameter; void* connectionStateChangedHandlerParameter;
uint32_t connectionTimeout; uint32_t connectionTimeout;
uint8_t timeQuality;
}; };
struct sClientReportControlBlock { struct sClientReportControlBlock {

@ -49,6 +49,7 @@ struct sIedServer
int reportBufferSizeURCBs; int reportBufferSizeURCBs;
bool enableBRCBResvTms; bool enableBRCBResvTms;
bool enableOwnerForRCB; bool enableOwnerForRCB;
bool syncIntegrityReportTimes;
#endif #endif
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)

@ -1,7 +1,7 @@
/* /*
* logging.h * logging.h
* *
* Copyright 2016 Michael Zillgith * Copyright 2016-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -29,7 +29,9 @@ typedef struct {
char* name; char* name;
LogicalNode* parentLN; LogicalNode* parentLN;
bool locked; #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore lock;
#endif
LogStorage logStorage; LogStorage logStorage;

@ -1,7 +1,7 @@
/* /*
* mms_mapping_internal.h * mms_mapping_internal.h
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -285,6 +285,7 @@ struct sMmsMapping {
#endif #endif
LinkedList controlObjects; LinkedList controlObjects;
uint64_t nextControlTimeout; /* next timeout in one of the control state machines */
LinkedList attributeAccessHandlers; LinkedList attributeAccessHandlers;
@ -322,12 +323,17 @@ struct sMmsMapping {
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
/* flag indicates if data model is locked --> prevents reports to be sent */ /* flag indicates if data model is locked --> prevents reports to be sent */
bool isModelLocked; bool isModelLocked;
Semaphore isModelLockedMutex;
IedServer iedServer; IedServer iedServer;
IedConnectionIndicationHandler connectionIndicationHandler; IedConnectionIndicationHandler connectionIndicationHandler;
void* connectionIndicationHandlerParameter; void* connectionIndicationHandlerParameter;
IedServer_RCBEventHandler rcbEventHandler;
void* rcbEventHandlerParameter;
}; };
#endif /* MMS_MAPPING_INTERNAL_H_ */ #endif /* MMS_MAPPING_INTERNAL_H_ */

@ -53,8 +53,12 @@ typedef struct {
LogicalNode* parentLN; LogicalNode* parentLN;
MmsValue* rcbValues; MmsValue* rcbValues;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore rcbValuesLock;
#endif
MmsValue* inclusionField; MmsValue* inclusionField;
MmsValue* confRev;
DataSet* dataSet; DataSet* dataSet;
bool isDynamicDataSet; bool isDynamicDataSet;
@ -107,6 +111,7 @@ typedef struct {
MmsValue* timeOfEntry; MmsValue* timeOfEntry;
ReportControlBlock* rcb; ReportControlBlock* rcb;
ReportControlBlock* sibling; /* backup sibling field of original ReportControlBlock */
IedServer server; IedServer server;
} ReportControl; } ReportControl;
@ -136,7 +141,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
MmsServerConnection connection); MmsServerConnection connection);
LIB61850_INTERNAL void LIB61850_INTERNAL void
ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, char* elementName); ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName);
LIB61850_INTERNAL void LIB61850_INTERNAL void
Reporting_activateBufferedReports(MmsMapping* self); Reporting_activateBufferedReports(MmsMapping* self);
@ -153,6 +158,9 @@ Reporting_processReportEventsAfterUnlock(MmsMapping* self);
LIB61850_INTERNAL void LIB61850_INTERNAL void
Reporting_sendReports(MmsMapping* self, MmsServerConnection connection); Reporting_sendReports(MmsMapping* self, MmsServerConnection connection);
LIB61850_INTERNAL void
Reporting_deactivateAllReports(MmsMapping* self);
LIB61850_INTERNAL void LIB61850_INTERNAL void
Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection); Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection);

@ -1,7 +1,7 @@
/* /*
* client_connection.c * client_connection.c
* *
* Copyright 2013, 2014, 2015 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -47,12 +47,14 @@ private_ClientConnection_create(void* serverConnectionHandle)
{ {
ClientConnection self = (ClientConnection) GLOBAL_MALLOC(sizeof(struct sClientConnection)); ClientConnection self = (ClientConnection) GLOBAL_MALLOC(sizeof(struct sClientConnection));
if (self) {
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
self->tasksCountMutex = Semaphore_create(1); self->tasksCountMutex = Semaphore_create(1);
#endif #endif
self->tasksCount = 0; self->tasksCount = 0;
self->serverConnectionHandle = serverConnectionHandle; self->serverConnectionHandle = serverConnectionHandle;
}
return self; return self;
} }
@ -60,11 +62,13 @@ private_ClientConnection_create(void* serverConnectionHandle)
void void
private_ClientConnection_destroy(ClientConnection self) private_ClientConnection_destroy(ClientConnection self)
{ {
if (self) {
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->tasksCountMutex); Semaphore_destroy(self->tasksCountMutex);
#endif #endif
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
int int

@ -1,7 +1,7 @@
/* /*
* ied_server.c * ied_server.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -438,6 +438,7 @@ updateDataSetsWithCachedValues(IedServer self)
char domainName[65]; char domainName[65];
strncpy(domainName, self->model->name, 64); strncpy(domainName, self->model->name, 64);
domainName[64] = 0;
strncat(domainName, dataSetEntry->logicalDeviceName, 64 - iedNameLength); strncat(domainName, dataSetEntry->logicalDeviceName, 64 - iedNameLength);
domain = MmsDevice_getDomain(self->mmsDevice, domainName); domain = MmsDevice_getDomain(self->mmsDevice, domainName);
@ -555,11 +556,13 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs; self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs;
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB; self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB; self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB;
self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes;
} }
else { else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false; self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
self->enableBRCBResvTms = true; self->enableBRCBResvTms = true;
#else #else
@ -656,9 +659,16 @@ IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfigur
} }
void void
IedServer_destroy(IedServer self) IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, void* parameter)
{ {
self->mmsMapping->rcbEventHandler = handler;
self->mmsMapping->rcbEventHandlerParameter = parameter;
}
void
IedServer_destroy(IedServer self)
{
if (self) {
/* Stop server if running */ /* Stop server if running */
if (self->running) { if (self->running) {
#if (CONFIG_MMS_THREADLESS_STACK == 1) #if (CONFIG_MMS_THREADLESS_STACK == 1)
@ -703,6 +713,7 @@ IedServer_destroy(IedServer self)
#endif /* (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1) */ #endif /* (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1) */
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
void void
@ -790,6 +801,8 @@ IedServer_stop(IedServer self)
MmsMapping_stopEventWorkerThread(self->mmsMapping); MmsMapping_stopEventWorkerThread(self->mmsMapping);
Reporting_deactivateAllReports(self->mmsMapping);
#if (CONFIG_MMS_SINGLE_THREADED == 1) #if (CONFIG_MMS_SINGLE_THREADED == 1)
Thread_destroy(self->serverThread); Thread_destroy(self->serverThread);
self->serverThread = NULL; self->serverThread = NULL;
@ -872,9 +885,13 @@ IedServer_stopThreadless(IedServer self)
void void
IedServer_lockDataModel(IedServer self) IedServer_lockDataModel(IedServer self)
{ {
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
MmsServer_lockModel(self->mmsServer); MmsServer_lockModel(self->mmsServer);
self->mmsMapping->isModelLocked = true; self->mmsMapping->isModelLocked = true;
Semaphore_post(self->mmsMapping->isModelLockedMutex);
} }
void void
@ -888,9 +905,13 @@ IedServer_unlockDataModel(IedServer self)
/* check if reports have to be sent! */ /* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping); Reporting_processReportEventsAfterUnlock(self->mmsMapping);
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
self->mmsMapping->isModelLocked = false; self->mmsMapping->isModelLocked = false;
MmsServer_unlockModel(self->mmsServer); MmsServer_unlockModel(self->mmsServer);
Semaphore_post(self->mmsMapping->isModelLockedMutex);
} }
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1) #if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
@ -1223,8 +1244,8 @@ IedServer_updateAttributeValue(IedServer self, DataAttribute* dataAttribute, Mms
void void
IedServer_updateFloatAttributeValue(IedServer self, DataAttribute* dataAttribute, float value) IedServer_updateFloatAttributeValue(IedServer self, DataAttribute* dataAttribute, float value)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_FLOAT);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_FLOAT);
assert(self != NULL); assert(self != NULL);
float currentValue = MmsValue_toFloat(dataAttribute->mmsValue); float currentValue = MmsValue_toFloat(dataAttribute->mmsValue);
@ -1247,8 +1268,8 @@ IedServer_updateFloatAttributeValue(IedServer self, DataAttribute* dataAttribute
void void
IedServer_updateInt32AttributeValue(IedServer self, DataAttribute* dataAttribute, int32_t value) IedServer_updateInt32AttributeValue(IedServer self, DataAttribute* dataAttribute, int32_t value)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_INTEGER);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_INTEGER);
assert(self != NULL); assert(self != NULL);
int32_t currentValue = MmsValue_toInt32(dataAttribute->mmsValue); int32_t currentValue = MmsValue_toInt32(dataAttribute->mmsValue);
@ -1293,8 +1314,8 @@ IedServer_updateDbposValue(IedServer self, DataAttribute* dataAttribute, Dbpos v
void void
IedServer_updateInt64AttributeValue(IedServer self, DataAttribute* dataAttribute, int64_t value) IedServer_updateInt64AttributeValue(IedServer self, DataAttribute* dataAttribute, int64_t value)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_INTEGER);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_INTEGER);
assert(self != NULL); assert(self != NULL);
int64_t currentValue = MmsValue_toInt64(dataAttribute->mmsValue); int64_t currentValue = MmsValue_toInt64(dataAttribute->mmsValue);
@ -1318,8 +1339,8 @@ IedServer_updateInt64AttributeValue(IedServer self, DataAttribute* dataAttribute
void void
IedServer_updateUnsignedAttributeValue(IedServer self, DataAttribute* dataAttribute, uint32_t value) IedServer_updateUnsignedAttributeValue(IedServer self, DataAttribute* dataAttribute, uint32_t value)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_UNSIGNED);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_UNSIGNED);
assert(self != NULL); assert(self != NULL);
uint32_t currentValue = MmsValue_toUint32(dataAttribute->mmsValue); uint32_t currentValue = MmsValue_toUint32(dataAttribute->mmsValue);
@ -1343,8 +1364,8 @@ IedServer_updateUnsignedAttributeValue(IedServer self, DataAttribute* dataAttrib
void void
IedServer_updateBitStringAttributeValue(IedServer self, DataAttribute* dataAttribute, uint32_t value) IedServer_updateBitStringAttributeValue(IedServer self, DataAttribute* dataAttribute, uint32_t value)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_BIT_STRING);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_BIT_STRING);
assert(self != NULL); assert(self != NULL);
uint32_t currentValue = MmsValue_getBitStringAsInteger(dataAttribute->mmsValue); uint32_t currentValue = MmsValue_getBitStringAsInteger(dataAttribute->mmsValue);
@ -1400,8 +1421,8 @@ IedServer_updateBooleanAttributeValue(IedServer self, DataAttribute* dataAttribu
void void
IedServer_updateVisibleStringAttributeValue(IedServer self, DataAttribute* dataAttribute, char *value) IedServer_updateVisibleStringAttributeValue(IedServer self, DataAttribute* dataAttribute, char *value)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_VISIBLE_STRING);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_VISIBLE_STRING);
assert(self != NULL); assert(self != NULL);
const char* currentValue = MmsValue_toString(dataAttribute->mmsValue); const char* currentValue = MmsValue_toString(dataAttribute->mmsValue);
@ -1424,8 +1445,8 @@ IedServer_updateVisibleStringAttributeValue(IedServer self, DataAttribute* dataA
void void
IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribute, uint64_t value) IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribute, uint64_t value)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_UTC_TIME);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_UTC_TIME);
assert(self != NULL); assert(self != NULL);
uint64_t currentValue = MmsValue_getUtcTimeInMs(dataAttribute->mmsValue); uint64_t currentValue = MmsValue_getUtcTimeInMs(dataAttribute->mmsValue);
@ -1449,8 +1470,8 @@ IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribu
void void
IedServer_updateTimestampAttributeValue(IedServer self, DataAttribute* dataAttribute, Timestamp* timestamp) IedServer_updateTimestampAttributeValue(IedServer self, DataAttribute* dataAttribute, Timestamp* timestamp)
{ {
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_UTC_TIME);
assert(dataAttribute != NULL); assert(dataAttribute != NULL);
assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_UTC_TIME);
assert(self != NULL); assert(self != NULL);
if (memcmp(dataAttribute->mmsValue->value.utcTime, timestamp->val, 8)) { if (memcmp(dataAttribute->mmsValue->value.utcTime, timestamp->val, 8)) {
@ -1683,6 +1704,7 @@ IedServer_getFunctionalConstrainedData(IedServer self, DataObject* dataObject, F
} }
strncpy(domainName, self->model->name, 64); strncpy(domainName, self->model->name, 64);
domainName[64] = 0;
strncat(domainName, ld->name, 64); strncat(domainName, ld->name, 64);
MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName); MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName);

@ -1,7 +1,7 @@
/* /*
* ied_server_config.c * ied_server_config.c
* *
* Copyright 2018-2020 Michael Zillgith * Copyright 2018-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -55,8 +55,10 @@ IedServerConfig_create()
self->edition = IEC_61850_EDITION_2; self->edition = IEC_61850_EDITION_2;
self->maxMmsConnections = 5; self->maxMmsConnections = 5;
self->enableEditSG = true; self->enableEditSG = true;
self->enableResvTmsForSGCB = true;
self->enableResvTmsForBRCB = true; self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false; self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
} }
return self; return self;
@ -65,8 +67,10 @@ IedServerConfig_create()
void void
IedServerConfig_destroy(IedServerConfig self) IedServerConfig_destroy(IedServerConfig self)
{ {
if (self) {
GLOBAL_FREEMEM(self->fileServiceBasepath); GLOBAL_FREEMEM(self->fileServiceBasepath);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
void void
@ -248,3 +252,15 @@ IedServerConfig_getMaxMmsConnections(IedServerConfig self)
{ {
return self->maxMmsConnections; return self->maxMmsConnections;
} }
void
IedServerConfig_setSyncIntegrityReportTimes(IedServerConfig self, bool enable)
{
self->syncIntegrityReportTimes = enable;
}
bool
IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self)
{
return self->syncIntegrityReportTimes;
}

@ -1,7 +1,7 @@
/* /*
* control.c * control.c
* *
* Copyright 2013-2021 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -68,6 +68,9 @@ ControlObject_sendCommandTerminationPositive(ControlObject* self);
void void
ControlObject_sendCommandTerminationNegative(ControlObject* self); ControlObject_sendCommandTerminationNegative(ControlObject* self);
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping);
static MmsValue* static MmsValue*
getOperParameterCtlNum(MmsValue* operParameters) getOperParameterCtlNum(MmsValue* operParameters)
{ {
@ -413,7 +416,14 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
static void static void
unselectObject(ControlObject* self, SelectStateChangedReason reason); unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* mmsMapping);
static void
updateNextControlTimeout(MmsMapping* self, uint64_t timeout)
{
if (timeout < self->nextControlTimeout)
self->nextControlTimeout = timeout;
}
static void static void
setState(ControlObject* self, int newState) setState(ControlObject* self, int newState)
@ -482,7 +492,7 @@ updateSboTimeoutValue(ControlObject* self)
} }
static void static void
selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection connection) selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection connection, MmsMapping* mmsMapping)
{ {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: control %s/%s.%s selected\n", MmsDomain_getName(self->mmsDomain), self->lnName, self->name); printf("IED_SERVER: control %s/%s.%s selected\n", MmsDomain_getName(self->mmsDomain), self->lnName, self->name);
@ -494,6 +504,8 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne
setStSeld(self, true); setStSeld(self, true);
setState(self, STATE_READY); setState(self, STATE_READY);
updateNextControlTimeout(mmsMapping, selectTime);
if (self->selectStateChangedHandler) { if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self, self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter, self->selectStateChangedHandlerParameter,
@ -503,13 +515,16 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne
} }
static void static void
unselectObject(ControlObject* self, SelectStateChangedReason reason) unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* mmsMapping)
{ {
if (getState(self) != STATE_UNSELECTED) { if (getState(self) != STATE_UNSELECTED) {
setState(self, STATE_UNSELECTED); setState(self, STATE_UNSELECTED);
setStSeld(self, false); setStSeld(self, false);
/* trigger timeout check in next cycle to update the next timeout value */
mmsMapping->nextControlTimeout = 0;
if (self->selectStateChangedHandler) { if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self, self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter, self->selectStateChangedHandlerParameter,
@ -523,7 +538,7 @@ unselectObject(ControlObject* self, SelectStateChangedReason reason)
} }
static void static void
checkSelectTimeout(ControlObject* self, uint64_t currentTime) checkSelectTimeout(ControlObject* self, uint64_t currentTime, MmsMapping* mmsMapping)
{ {
if ((self->ctlModel == 2) || (self->ctlModel == 4)) { if ((self->ctlModel == 2) || (self->ctlModel == 4)) {
@ -531,10 +546,13 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime)
if (self->selectTimeout > 0) { if (self->selectTimeout > 0) {
if (currentTime > (self->selectTime + self->selectTimeout)) { if (currentTime > (self->selectTime + self->selectTimeout)) {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: select-timeout (timeout-val = %i) for control %s/%s.%s\n", printf("IED_SERVER: select-timeout (timeout-val = %u) for control %s/%s.%s\n",
self->selectTimeout, MmsDomain_getName(self->mmsDomain), self->lnName, self->name); self->selectTimeout, MmsDomain_getName(self->mmsDomain), self->lnName, self->name);
unselectObject(self, SELECT_STATE_REASON_TIMEOUT); unselectObject(self, SELECT_STATE_REASON_TIMEOUT, mmsMapping);
}
else {
updateNextControlTimeout(mmsMapping, self->selectTime + self->selectTimeout);
} }
} }
} }
@ -641,16 +659,16 @@ exitControlTask(ControlObject* self)
} }
static void static void
abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason) abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason, MmsMapping* mmsMapping)
{ {
if ((self->ctlModel == 2) || (self->ctlModel == 4)) { if ((self->ctlModel == 2) || (self->ctlModel == 4)) {
if (unconditional) { if (unconditional) {
unselectObject(self, reason); unselectObject(self, reason, mmsMapping);
} }
else { else {
if (isSboClassOperateOnce(self)) if (isSboClassOperateOnce(self))
unselectObject(self, reason); unselectObject(self, reason, mmsMapping);
else else
setState(self, STATE_READY); setState(self, STATE_READY);
} }
@ -716,7 +734,7 @@ executeStateMachine:
if (checkHandlerResult == CONTROL_ACCEPTED) { if (checkHandlerResult == CONTROL_ACCEPTED) {
LinkedList_add(values, controlObject->sbo); LinkedList_add(values, controlObject->sbo);
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection); selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self);
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR); updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR);
@ -740,7 +758,7 @@ executeStateMachine:
else if (controlObject->ctlModel == 4) { else if (controlObject->ctlModel == 4) {
if (checkHandlerResult == CONTROL_ACCEPTED) { if (checkHandlerResult == CONTROL_ACCEPTED) {
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection); selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self);
if (controlObject->ctlNumSt) if (controlObject->ctlNumSt)
MmsValue_update(controlObject->ctlNumSt, controlObject->ctlNum); MmsValue_update(controlObject->ctlNumSt, controlObject->ctlNum);
@ -783,11 +801,13 @@ executeStateMachine:
} }
} }
else {
updateNextControlTimeout(self, Hal_getTimeInMs() + 100);
}
} }
break; break;
case STATE_WAIT_FOR_ACTIVATION_TIME: case STATE_WAIT_FOR_ACTIVATION_TIME:
case STATE_WAIT_FOR_EXECUTION: case STATE_WAIT_FOR_EXECUTION:
{ {
@ -828,7 +848,7 @@ executeStateMachine:
resetAddCause(controlObject); resetAddCause(controlObject);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
exitControlTask(controlObject); exitControlTask(controlObject);
} }
else if (dynamicCheckResult == CONTROL_RESULT_OK) { else if (dynamicCheckResult == CONTROL_RESULT_OK) {
@ -858,6 +878,9 @@ executeStateMachine:
goto executeStateMachine; goto executeStateMachine;
} }
else {
updateNextControlTimeout(self, Hal_getTimeInMs() + 10);
}
} }
break; break;
@ -879,7 +902,7 @@ executeStateMachine:
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
} }
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED, self);
} }
else { else {
@ -894,7 +917,7 @@ executeStateMachine:
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT); updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
} }
exitControlTask(controlObject); exitControlTask(controlObject);
@ -903,6 +926,9 @@ executeStateMachine:
resetAddCause(controlObject); resetAddCause(controlObject);
} }
else {
updateNextControlTimeout(self, currentTimeInMs + 10);
}
} }
break; break;
@ -1003,10 +1029,24 @@ ControlObject_initialize(ControlObject* self)
self->ctlNumSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlNumName); self->ctlNumSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlNumName);
if (self->ctlNumSt == NULL) {
/* for APC */
ctlNumName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$MX$", self->name, "$ctlNum");
self->ctlNumSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlNumName);
}
char* originName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$ST$", self->name, "$origin"); char* originName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$ST$", self->name, "$origin");
self->originSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, originName); self->originSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, originName);
if (self->originSt == NULL) {
/* for APC */
originName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$MX$", self->name, "$origin");
self->originSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, originName);
}
char* sboTimeoutName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$CF$", self->name, "$sboTimeout"); char* sboTimeoutName = StringUtils_createStringInBuffer(strBuf, 4, self->lnName, "$CF$", self->name, "$sboTimeout");
self->sboTimeout = MmsServer_getValueFromCache(mmsServer, self->sboTimeout = MmsServer_getValueFromCache(mmsServer,
@ -1067,7 +1107,6 @@ ControlObject_initialize(ControlObject* self)
mxValType = da->type; mxValType = da->type;
} }
if (stValType == IEC61850_BOOLEAN && ctlValType == IEC61850_BOOLEAN) if (stValType == IEC61850_BOOLEAN && ctlValType == IEC61850_BOOLEAN)
{ {
self->cdc = CST_SPCTRK; /* SPC */ self->cdc = CST_SPCTRK; /* SPC */
@ -1240,6 +1279,7 @@ ControlObject_handlePendingEvents(ControlObject* self)
void void
ControlObject_destroy(ControlObject* self) ControlObject_destroy(ControlObject* self)
{ {
if (self) {
if (self->mmsValue) if (self->mmsValue)
MmsValue_delete(self->mmsValue); MmsValue_delete(self->mmsValue);
@ -1270,6 +1310,7 @@ ControlObject_destroy(ControlObject* self)
#endif #endif
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
char* char*
@ -1315,10 +1356,10 @@ ControlObject_getMmsValue(ControlObject* self)
} }
bool bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection) ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping)
{ {
if (self->mmsConnection == connection) { if (self->mmsConnection == connection) {
abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED); abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED, mmsMapping);
return true; return true;
} }
else else
@ -1375,11 +1416,25 @@ ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataOb
void void
Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
{ {
if (currentTimeInMs >= self->nextControlTimeout) {
/* invalidate nextControlTimeout */
self->nextControlTimeout = (uint64_t) 0xFFFFFFFFFFFFFFFFLLU;
LinkedList element = LinkedList_getNext(self->controlObjects); LinkedList element = LinkedList_getNext(self->controlObjects);
while (element != NULL) { while (element != NULL) {
ControlObject* controlObject = (ControlObject*) element->data; ControlObject* controlObject = (ControlObject*) element->data;
if (controlObject->state != STATE_UNSELECTED) {
if ((controlObject->ctlModel == 1) || (controlObject->ctlModel == 3)) {
if (controlObject->state == STATE_READY) {
element = LinkedList_getNext(element);
continue;
}
}
if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) { if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) {
if (controlObject->operateTime <= currentTimeInMs) { if (controlObject->operateTime <= currentTimeInMs) {
@ -1428,24 +1483,29 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
/* leave state Perform Test */ /* leave state Perform Test */
setOpRcvd(controlObject, false); setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
resetAddCause(controlObject); resetAddCause(controlObject);
} }
} }
else {
updateNextControlTimeout(self, controlObject->operateTime);
}
} /* if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) */ } /* if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) */
else if (!((controlObject->state == STATE_UNSELECTED) || (controlObject->state == STATE_READY))) { else if (!((controlObject->state == STATE_UNSELECTED) || (controlObject->state == STATE_READY))) {
executeControlTask(self, controlObject, currentTimeInMs); executeControlTask(self, controlObject, currentTimeInMs);
} }
else if (controlObject->state == STATE_READY) { else if (controlObject->state == STATE_READY) {
checkSelectTimeout(controlObject, currentTimeInMs); checkSelectTimeout(controlObject, currentTimeInMs, self);
}
} }
ControlObject_handlePendingEvents(controlObject); ControlObject_handlePendingEvents(controlObject);
element = LinkedList_getNext(element); element = LinkedList_getNext(element);
} }
}
} }
ControlObject* ControlObject*
@ -1768,7 +1828,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
value = &emptyString; value = &emptyString;
if (isDirectAccess == true) { if (isDirectAccess == true) {
checkSelectTimeout(controlObject, currentTime); checkSelectTimeout(controlObject, currentTime, self);
if (getState(controlObject) == STATE_UNSELECTED) { if (getState(controlObject) == STATE_UNSELECTED) {
CheckHandlerResult checkResult = CONTROL_ACCEPTED; CheckHandlerResult checkResult = CONTROL_ACCEPTED;
@ -1789,7 +1849,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
} }
if (checkResult == CONTROL_ACCEPTED) { if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection); selectObject(controlObject, currentTime, connection, self);
value = controlObject->sbo; value = controlObject->sbo;
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
@ -1814,7 +1874,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
} }
else { else {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: select not applicable for control model %i\n", controlObject->ctlModel); printf("IED_SERVER: select not applicable for control model %u\n", controlObject->ctlModel);
value = controlObject->sbo; value = controlObject->sbo;
} }
@ -1992,7 +2052,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
uint64_t currentTime = Hal_getTimeInMs(); uint64_t currentTime = Hal_getTimeInMs();
checkSelectTimeout(controlObject, currentTime); checkSelectTimeout(controlObject, currentTime, self);
if (state != STATE_UNSELECTED) { if (state != STATE_UNSELECTED) {
@ -2049,7 +2109,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
} }
if (checkResult == CONTROL_ACCEPTED) { if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection); selectObject(controlObject, currentTime, connection, self);
indication = DATA_ACCESS_ERROR_SUCCESS; indication = DATA_ACCESS_ERROR_SUCCESS;
@ -2066,6 +2126,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setState(controlObject, STATE_WAIT_FOR_SELECT); setState(controlObject, STATE_WAIT_FOR_SELECT);
updateNextControlTimeout(self, Hal_getTimeInMs() + 100);
indication = DATA_ACCESS_ERROR_NO_RESPONSE; indication = DATA_ACCESS_ERROR_NO_RESPONSE;
} }
else { else {
@ -2115,7 +2177,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
ctlNum, origin, true); ctlNum, origin, true);
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED); unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED, self);
} }
goto free_and_return; goto free_and_return;
@ -2123,7 +2185,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
uint64_t currentTime = Hal_getTimeInMs(); uint64_t currentTime = Hal_getTimeInMs();
checkSelectTimeout(controlObject, currentTime); checkSelectTimeout(controlObject, currentTime, self);
int state = getState(controlObject); int state = getState(controlObject);
@ -2170,7 +2232,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true); ctlNum, origin, true);
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED); unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED, self);
goto free_and_return; goto free_and_return;
} }
@ -2205,6 +2267,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setState(controlObject, STATE_WAIT_FOR_ACTIVATION_TIME); setState(controlObject, STATE_WAIT_FOR_ACTIVATION_TIME);
updateNextControlTimeout(self, controlObject->operateTime);
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: Oper - activate time activated control\n"); printf("IED_SERVER: Oper - activate time activated control\n");
@ -2251,6 +2315,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setOpRcvd(controlObject, false); setOpRcvd(controlObject, false);
initiateControlTask(controlObject); initiateControlTask(controlObject);
updateNextControlTimeout(self, currentTime);
} }
else { else {
indication = (MmsDataAccessError) checkResult; indication = (MmsDataAccessError) checkResult;
@ -2258,7 +2324,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
/* leave state Perform Test */ /* leave state Perform Test */
setOpRcvd(controlObject, false); setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
if ((controlObject->ctlModel == 3) || (controlObject->ctlModel == 4)) { if ((controlObject->ctlModel == 3) || (controlObject->ctlModel == 4)) {
ControlObject_sendLastApplError(controlObject, connection, "Oper", ControlObject_sendLastApplError(controlObject, connection, "Oper",
@ -2328,7 +2394,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (state != STATE_UNSELECTED) { if (state != STATE_UNSELECTED) {
if (controlObject->mmsConnection == connection) { if (controlObject->mmsConnection == connection) {
indication = DATA_ACCESS_ERROR_SUCCESS; indication = DATA_ACCESS_ERROR_SUCCESS;
unselectObject(controlObject, SELECT_STATE_REASON_CANCELED); unselectObject(controlObject, SELECT_STATE_REASON_CANCELED, self);
goto free_and_return; goto free_and_return;
} }
else { else {
@ -2345,7 +2411,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (controlObject->timeActivatedOperate) { if (controlObject->timeActivatedOperate) {
controlObject->timeActivatedOperate = false; controlObject->timeActivatedOperate = false;
abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED); abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED, self);
indication = DATA_ACCESS_ERROR_SUCCESS; indication = DATA_ACCESS_ERROR_SUCCESS;
@ -2356,14 +2422,17 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
free_and_return: free_and_return:
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
if (controlObject) {
if (serviceError == IEC61850_SERVICE_ERROR_NO_ERROR) { if (serviceError == IEC61850_SERVICE_ERROR_NO_ERROR) {
if (indication != DATA_ACCESS_ERROR_NO_RESPONSE) { if (indication != DATA_ACCESS_ERROR_NO_RESPONSE) {
updateGenericTrackingObjectValues(self, controlObject, serviceType, updateGenericTrackingObjectValues(self, controlObject, serviceType,
private_IedServer_convertMmsDataAccessErrorToServiceError(indication)); private_IedServer_convertMmsDataAccessErrorToServiceError(indication));
} }
} }
else else {
updateGenericTrackingObjectValues(self, controlObject, serviceType, serviceError); updateGenericTrackingObjectValues(self, controlObject, serviceType, serviceError);
}
}
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
resetAddCause(controlObject); resetAddCause(controlObject);

@ -1,7 +1,7 @@
/* /*
* logging.c * logging.c
* *
* Copyright 2016 Michael Zillgith * Copyright 2016-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -48,15 +48,20 @@ LogInstance_create(LogicalNode* parentLN, const char* name)
{ {
LogInstance* self = (LogInstance*) GLOBAL_MALLOC(sizeof(LogInstance)); LogInstance* self = (LogInstance*) GLOBAL_MALLOC(sizeof(LogInstance));
if (self) {
self->name = StringUtils_copyString(name); self->name = StringUtils_copyString(name);
self->parentLN = parentLN; self->parentLN = parentLN;
self->logStorage = NULL; self->logStorage = NULL;
self->locked = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->lock = Semaphore_create(1);
#endif
self->oldEntryId = 0; self->oldEntryId = 0;
self->oldEntryTime = 0; self->oldEntryTime = 0;
self->newEntryId = 0; self->newEntryId = 0;
self->newEntryTime = 0; self->newEntryTime = 0;
}
return self; return self;
} }
@ -64,8 +69,14 @@ LogInstance_create(LogicalNode* parentLN, const char* name)
void void
LogInstance_destroy(LogInstance* self) LogInstance_destroy(LogInstance* self)
{ {
if (self) {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->lock);
#endif
GLOBAL_FREEMEM(self->name); GLOBAL_FREEMEM(self->name);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
void void
@ -75,10 +86,9 @@ LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* valu
if (logStorage != NULL) { if (logStorage != NULL) {
while (self->locked) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Thread_sleep(1); Semaphore_wait(self->lock);
#endif
self->locked = true;
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: Log value - dataRef: %s flag: %i\n", dataRef, flag); printf("IED_SERVER: Log value - dataRef: %s flag: %i\n", dataRef, flag);
@ -91,17 +101,21 @@ LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* valu
uint8_t* data = (uint8_t*) GLOBAL_MALLOC(dataSize); uint8_t* data = (uint8_t*) GLOBAL_MALLOC(dataSize);
if (data) {
MmsValue_encodeMmsData(value, data, 0, true); MmsValue_encodeMmsData(value, data, 0, true);
LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag); LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag);
self->locked = false;
GLOBAL_FREEMEM(data); GLOBAL_FREEMEM(data);
}
self->newEntryId = entryID; self->newEntryId = entryID;
self->newEntryTime = timestamp; self->newEntryTime = timestamp;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->lock);
#endif
} }
else else
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
@ -115,10 +129,9 @@ LogInstance_logEntryStart(LogInstance* self)
if (logStorage != NULL) { if (logStorage != NULL) {
while (self->locked) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Thread_sleep(1); Semaphore_wait(self->lock);
#endif
self->locked = true;
uint64_t timestamp = Hal_getTimeInMs(); uint64_t timestamp = Hal_getTimeInMs();
@ -139,20 +152,20 @@ LogInstance_logEntryData(LogInstance* self, uint64_t entryID, const char* dataRe
{ {
LogStorage logStorage = self->logStorage; LogStorage logStorage = self->logStorage;
if (logStorage != NULL) { if (logStorage) {
int dataSize = MmsValue_encodeMmsData(value, NULL, 0, false); int dataSize = MmsValue_encodeMmsData(value, NULL, 0, false);
uint8_t* data = (uint8_t*) GLOBAL_MALLOC(dataSize); uint8_t* data = (uint8_t*) GLOBAL_MALLOC(dataSize);
if (data) {
MmsValue_encodeMmsData(value, data, 0, true); MmsValue_encodeMmsData(value, data, 0, true);
LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag); LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag);
self->locked = false;
GLOBAL_FREEMEM(data); GLOBAL_FREEMEM(data);
} }
}
} }
void void
@ -160,7 +173,9 @@ LogInstance_logEntryFinished(LogInstance* self, uint64_t entryID)
{ {
(void)entryID; (void)entryID;
self->locked = false; #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->lock);
#endif
} }
void void
@ -185,6 +200,7 @@ LogControl_create(LogicalNode* parentLN, MmsMapping* mmsMapping)
{ {
LogControl* self = (LogControl*) GLOBAL_MALLOC(sizeof(LogControl)); LogControl* self = (LogControl*) GLOBAL_MALLOC(sizeof(LogControl));
if (self) {
self->enabled = false; self->enabled = false;
self->dataSet = NULL; self->dataSet = NULL;
self->isDynamicDataSet = false; self->isDynamicDataSet = false;
@ -196,6 +212,7 @@ LogControl_create(LogicalNode* parentLN, MmsMapping* mmsMapping)
self->intgPd = 0; self->intgPd = 0;
self->nextIntegrityScan = 0; self->nextIntegrityScan = 0;
self->logRef = NULL; self->logRef = NULL;
}
return self; return self;
} }
@ -315,6 +332,7 @@ getLogInstanceByLogRef(MmsMapping* self, const char* logRef)
char* logName; char* logName;
strncpy(refStr, logRef, 129); strncpy(refStr, logRef, 129);
refStr[129] = 0;
domainName = refStr; domainName = refStr;
@ -477,6 +495,7 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma
char variableId[130]; char variableId[130];
strncpy(variableId, variableIdOrig, 129); strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
char* separator = strchr(variableId, '$'); char* separator = strchr(variableId, '$');
@ -494,7 +513,9 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma
char* varName = MmsMapping_getNextNameElement(objectName); char* varName = MmsMapping_getNextNameElement(objectName);
if (varName != NULL) if (varName == NULL)
return DATA_ACCESS_ERROR_INVALID_ADDRESS;
*(varName - 1) = 0; *(varName - 1) = 0;
LogControl* logControl = lookupLogControl(self, domain, lnName, objectName); LogControl* logControl = lookupLogControl(self, domain, lnName, objectName);
@ -688,6 +709,7 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain,
char variableId[130]; char variableId[130];
strncpy(variableId, variableIdOrig, 129); strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
char* separator = strchr(variableId, '$'); char* separator = strchr(variableId, '$');
@ -978,6 +1000,8 @@ LogControl_logAllDatasetEntries(LogControl* self, const char* iedName)
uint64_t entryID = LogInstance_logEntryStart(log); uint64_t entryID = LogInstance_logEntryStart(log);
if (entryID != 0) {
DataSetEntry* dataSetEntry = self->dataSet->fcdas; DataSetEntry* dataSetEntry = self->dataSet->fcdas;
while (dataSetEntry != NULL) { while (dataSetEntry != NULL) {
@ -990,6 +1014,7 @@ LogControl_logAllDatasetEntries(LogControl* self, const char* iedName)
} }
LogInstance_logEntryFinished(log, entryID); LogInstance_logEntryFinished(log, entryID);
}
} }
} }

@ -274,6 +274,8 @@ MmsGooseControlBlock_create()
void void
MmsGooseControlBlock_destroy(MmsGooseControlBlock self) MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
{ {
if (self) {
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->publisherMutex); Semaphore_destroy(self->publisherMutex);
#endif #endif
@ -307,6 +309,7 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
MmsValue_delete(self->mmsValue); MmsValue_delete(self->mmsValue);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
void void
@ -441,12 +444,14 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
/* Calculate maximum GOOSE message size */ /* Calculate maximum GOOSE message size */
int maxGooseMessageSize = 26 + 51 + 6; int maxGooseMessageSize = 26 + 51 + 6;
maxGooseMessageSize += strlen(self->goCBRef); maxGooseMessageSize += (int)strlen(self->goCBRef);
if (self->goId) if (self->goId)
maxGooseMessageSize += strlen(self->goId); maxGooseMessageSize += (int)strlen(self->goId);
else else
maxGooseMessageSize += strlen(self->goCBRef); maxGooseMessageSize += (int)strlen(self->goCBRef);
maxGooseMessageSize += strlen(self->dataSetRef);
maxGooseMessageSize += (int)strlen(self->dataSetRef);
maxGooseMessageSize += dataSetSize; maxGooseMessageSize += dataSetSize;
@ -463,7 +468,6 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
MmsValue_setBoolean(goEna, true); MmsValue_setBoolean(goEna, true);
MmsValue* dstAddress = MmsValue_getElement(self->mmsValue, 5); MmsValue* dstAddress = MmsValue_getElement(self->mmsValue, 5);
CommParameters commParameters; CommParameters commParameters;
@ -512,7 +516,6 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
LinkedList_add(self->dataSetValues, dataSetEntry->value); LinkedList_add(self->dataSetValues, dataSetEntry->value);
dataSetEntry = dataSetEntry->sibling; dataSetEntry = dataSetEntry->sibling;
} }
} }
else { else {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)

@ -1,7 +1,7 @@
/* /*
* mms_mapping.c * mms_mapping.c
* *
* Copyright 2013-2021 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -72,6 +72,9 @@ typedef struct
MmsValue* MmsValue*
Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
MmsServerConnection connection, bool isDirectAccess); MmsServerConnection connection, bool isDirectAccess);
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping);
#endif #endif
void /* Create PHYCOMADDR ACSI type instance */ void /* Create PHYCOMADDR ACSI type instance */
@ -1344,6 +1347,8 @@ checkForServiceTrackingVariables(MmsMapping* self, LogicalNode* logicalNode)
else if (!strcmp(modelNode->name, "BacTrk")) else if (!strcmp(modelNode->name, "BacTrk"))
actInstance = &self->bacTrk; actInstance = &self->bacTrk;
if (actInstance)
{
if (*actInstance != NULL) { if (*actInstance != NULL) {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: ERROR: multiple %s instances found in server\n", modelNode->name); printf("IED_SERVER: ERROR: multiple %s instances found in server\n", modelNode->name);
@ -1358,7 +1363,7 @@ checkForServiceTrackingVariables(MmsMapping* self, LogicalNode* logicalNode)
getControlTrackingAttributes(*actInstance, actTrk); getControlTrackingAttributes(*actInstance, actTrk);
} }
} }
}
} }
else if (!strcmp(modelNode->name, "BrcbTrk")) { else if (!strcmp(modelNode->name, "BrcbTrk")) {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
@ -1805,8 +1810,8 @@ createMmsDomainFromIedDevice(MmsMapping* self, LogicalDevice* logicalDevice)
} }
strncpy(domainName, self->model->name, 64); strncpy(domainName, self->model->name, 64);
strncat(domainName, logicalDevice->name, 64 - modelNameLength);
domainName[64] = 0; domainName[64] = 0;
strncat(domainName, logicalDevice->name, 64 - modelNameLength);
} }
else { else {
if (strlen(logicalDevice->ldName) > 64) if (strlen(logicalDevice->ldName) > 64)
@ -1926,8 +1931,8 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel)
goto exit_function; goto exit_function;
} }
while (dataset != NULL) { while (dataset)
{
LogicalDevice* ld = IedModel_getDeviceByInst(iedModel, dataset->logicalDeviceName); LogicalDevice* ld = IedModel_getDeviceByInst(iedModel, dataset->logicalDeviceName);
if (ld) { if (ld) {
@ -1938,8 +1943,8 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel)
} }
else { else {
strncpy(domainName, iedModel->name, 64); strncpy(domainName, iedModel->name, 64);
strncat(domainName, dataset->logicalDeviceName, 64 - iedModelNameLength);
domainName[64] = 0; domainName[64] = 0;
strncat(domainName, dataset->logicalDeviceName, 64 - iedModelNameLength);
} }
MmsDomain* dataSetDomain = MmsDevice_getDomain(mmsDevice, domainName); MmsDomain* dataSetDomain = MmsDevice_getDomain(mmsDevice, domainName);
@ -1974,7 +1979,6 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel)
} }
accessSpecifier.domain = MmsDevice_getDomain(mmsDevice, domainName); accessSpecifier.domain = MmsDevice_getDomain(mmsDevice, domainName);
accessSpecifier.variableName = dataSetEntry->variableName; accessSpecifier.variableName = dataSetEntry->variableName;
accessSpecifier.arrayIndex = dataSetEntry->index; accessSpecifier.arrayIndex = dataSetEntry->index;
accessSpecifier.componentName = dataSetEntry->componentName; accessSpecifier.componentName = dataSetEntry->componentName;
@ -2058,12 +2062,19 @@ MmsMapping_create(IedModel* model, IedServer iedServer)
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1) #if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
self->controlObjects = LinkedList_create(); self->controlObjects = LinkedList_create();
self->nextControlTimeout = 0xffffffffffffffffLLU;
#endif #endif
#if (CONFIG_IEC61850_SETTING_GROUPS == 1) #if (CONFIG_IEC61850_SETTING_GROUPS == 1)
self->settingGroups = LinkedList_create(); self->settingGroups = LinkedList_create();
#endif #endif
self->isModelLocked = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->isModelLockedMutex = Semaphore_create(1);
#endif
self->attributeAccessHandlers = LinkedList_create(); self->attributeAccessHandlers = LinkedList_create();
/* create data model specification */ /* create data model specification */
@ -2073,6 +2084,24 @@ MmsMapping_create(IedModel* model, IedServer iedServer)
MmsMapping_destroy(self); MmsMapping_destroy(self);
self = NULL; self = NULL;
} }
else {
LinkedList rcElem = LinkedList_getNext(self->reportControls);
while (rcElem) {
ReportControl* rc = (ReportControl*)LinkedList_getData(rcElem);
/* backup original sibling of ReportControlBlock */;
rc->sibling = rc->rcb->sibling;
/* reuse ReportControlBlock.sibling as reference to runtime information (ReportControl) */
rc->rcb->sibling = (ReportControlBlock*)rc;
/* set runtime mode flag (indicate that sibling field contains now runtime information reference!) */
rc->rcb->trgOps |= 64;
rcElem = LinkedList_getNext(rcElem);
}
}
return self; return self;
} }
@ -2136,6 +2165,10 @@ MmsMapping_destroy(MmsMapping* self)
if (self->locbTrk) GLOBAL_FREEMEM(self->locbTrk); if (self->locbTrk) GLOBAL_FREEMEM(self->locbTrk);
#endif #endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->isModelLockedMutex);
#endif
LinkedList_destroy(self->attributeAccessHandlers); LinkedList_destroy(self->attributeAccessHandlers);
IedModel_setAttributeValuesToNull(self->model); IedModel_setAttributeValuesToNull(self->model);
@ -2306,6 +2339,7 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable
char variableId[130]; char variableId[130];
strncpy(variableId, variableIdOrig, 129); strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
char* separator = strchr(variableId, '$'); char* separator = strchr(variableId, '$');
@ -2691,11 +2725,16 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
} }
else else
retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
}
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
copySGCBValuesToTrackingObject(self, sg->sgcb); copySGCBValuesToTrackingObject(self, sg->sgcb);
updateGenericTrackingObjectValues(self, sg->sgcb, IEC61850_SERVICE_TYPE_SELECT_ACTIVE_SG, retVal); updateGenericTrackingObjectValues(self, sg->sgcb, IEC61850_SERVICE_TYPE_SELECT_ACTIVE_SG, retVal);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
}
else {
retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
}
return retVal; return retVal;
} }
else if (strcmp(nameId, "EditSG") == 0) { else if (strcmp(nameId, "EditSG") == 0) {
@ -2984,6 +3023,7 @@ readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableI
char variableId[130]; char variableId[130];
strncpy(variableId, variableIdOrig, 129); strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
char* separator = strchr(variableId, '$'); char* separator = strchr(variableId, '$');
@ -3114,15 +3154,28 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
char* elementName = MmsMapping_getNextNameElement(reportName); char* elementName = MmsMapping_getNextNameElement(reportName);
ReportControl_readAccess(rc, self, elementName); ReportControl_readAccess(rc, self, connection, elementName);
MmsValue* value = NULL; MmsValue* value = NULL;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
if (elementName != NULL) if (elementName != NULL)
value = ReportControl_getRCBValue(rc, elementName); value = ReportControl_getRCBValue(rc, elementName);
else else
value = rc->rcbValues; value = rc->rcbValues;
if (value) {
value = MmsValue_clone(value);
MmsValue_setDeletableRecursive(value);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock);
#endif
retValue = value; retValue = value;
goto exit_function; goto exit_function;
@ -3156,7 +3209,7 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection)
while (controlObjectElement != NULL) { while (controlObjectElement != NULL) {
ControlObject* controlObject = (ControlObject*) controlObjectElement->data; ControlObject* controlObject = (ControlObject*) controlObjectElement->data;
ControlObject_unselect(controlObject, connection); ControlObject_unselect(controlObject, connection, self);
controlObjectElement = LinkedList_getNext(controlObjectElement); controlObjectElement = LinkedList_getNext(controlObjectElement);
} }
@ -3658,6 +3711,8 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
{ {
LinkedList element = self->reportControls; LinkedList element = self->reportControls;
Semaphore_wait(self->isModelLockedMutex);
bool modelLocked = self->isModelLocked; bool modelLocked = self->isModelLocked;
while ((element = LinkedList_getNext(element)) != NULL) { while ((element = LinkedList_getNext(element)) != NULL) {
@ -3693,6 +3748,8 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
if (modelLocked == false) { if (modelLocked == false) {
Reporting_processReportEventsAfterUnlock(self); Reporting_processReportEventsAfterUnlock(self);
} }
Semaphore_post(self->isModelLockedMutex);
} }
#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */
@ -3704,8 +3761,6 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
{ {
LinkedList element = self->gseControls; LinkedList element = self->gseControls;
bool modelLocked = self->isModelLocked;
while ((element = LinkedList_getNext(element)) != NULL) { while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
@ -3715,9 +3770,13 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
if (DataSet_isMemberValue(dataSet, value, NULL)) { if (DataSet_isMemberValue(dataSet, value, NULL)) {
MmsGooseControlBlock_setStateChangePending(gcb); MmsGooseControlBlock_setStateChangePending(gcb);
if (modelLocked == false) { Semaphore_wait(self->isModelLockedMutex);
if (self->isModelLocked == false) {
MmsGooseControlBlock_publishNewState(gcb); MmsGooseControlBlock_publishNewState(gcb);
} }
Semaphore_post(self->isModelLockedMutex);
} }
} }
} }

@ -1,7 +1,7 @@
/* /*
* mms_sv.c * mms_sv.c
* *
* Copyright 2015 Michael Zillgith * Copyright 2015-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -67,9 +67,11 @@ MmsSampledValueControlBlock_create()
void void
MmsSampledValueControlBlock_destroy(MmsSampledValueControlBlock self) MmsSampledValueControlBlock_destroy(MmsSampledValueControlBlock self)
{ {
if (self) {
MmsValue_delete(self->mmsValue); MmsValue_delete(self->mmsValue);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
static MmsSampledValueControlBlock static MmsSampledValueControlBlock
@ -131,6 +133,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch
char variableId[130]; char variableId[130];
strncpy(variableId, variableIdOrig, 129); strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
char* separator = strchr(variableId, '$'); char* separator = strchr(variableId, '$');
@ -214,6 +217,7 @@ LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* d
char variableId[130]; char variableId[130];
strncpy(variableId, variableIdOrig, 129); strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
char* separator = strchr(variableId, '$'); char* separator = strchr(variableId, '$');

File diff suppressed because it is too large Load Diff

@ -190,8 +190,6 @@ CDA_Cancel(ModelNode* parent, DataAttributeType type, bool isTImeActivated)
return oper; return oper;
} }
/************************************************ /************************************************
* Common Data Classes - helper functions * Common Data Classes - helper functions
***********************************************/ ***********************************************/
@ -674,7 +672,7 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot
addCommonOperateElements(oper, isTimeActivated, true); addCommonOperateElements(oper, isTimeActivated, true);
if (controlOptions & CDC_CTL_MODEL_HAS_CANCEL) { if (controlOptions & CDC_CTL_MODEL_HAS_CANCEL) {
DataAttribute* cancel = DataAttribute_create("SBOw", (ModelNode*) parent, IEC61850_CONSTRUCTED, IEC61850_FC_CO, 0, 0, 0); DataAttribute* cancel = DataAttribute_create("Cancel", (ModelNode*) parent, IEC61850_CONSTRUCTED, IEC61850_FC_CO, 0, 0, 0);
CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat); CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat);

@ -1,7 +1,7 @@
/* /*
* config_file_parser.c * config_file_parser.c
* *
* Copyright 2014 Michael Zillgith * Copyright 2014-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -32,7 +32,6 @@
static uint8_t lineBuffer[READ_BUFFER_MAX_SIZE]; static uint8_t lineBuffer[READ_BUFFER_MAX_SIZE];
static int static int
readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize) readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize)
{ {
@ -79,7 +78,6 @@ readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize)
} }
} }
return bytesRead; return bytesRead;
} }
@ -175,7 +173,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
char ldName[65]; char ldName[65];
ldName[0] = 0; ldName[0] = 0;
if (sscanf((char*) lineBuffer, "LD(%s %s)", nameString, ldName) < 1) if (sscanf((char*) lineBuffer, "LD(%129s %64s)", nameString, ldName) < 1)
goto exit_error; goto exit_error;
terminateString(nameString, ')'); terminateString(nameString, ')');
@ -196,7 +194,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
if (StringUtils_startsWith((char*) lineBuffer, "LN")) { if (StringUtils_startsWith((char*) lineBuffer, "LN")) {
indendation = 3; indendation = 3;
if (sscanf((char*) lineBuffer, "LN(%s)", nameString) < 1) if (sscanf((char*) lineBuffer, "LN(%129s)", nameString) < 1)
goto exit_error; goto exit_error;
terminateString(nameString, ')'); terminateString(nameString, ')');
@ -212,7 +210,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int arrayElements = 0; int arrayElements = 0;
sscanf((char*) lineBuffer, "DO(%s %i)", nameString, &arrayElements); sscanf((char*) lineBuffer, "DO(%129s %i)", nameString, &arrayElements);
currentModelNode = (ModelNode*) currentModelNode = (ModelNode*)
DataObject_create(nameString, (ModelNode*) currentLN, arrayElements); DataObject_create(nameString, (ModelNode*) currentLN, arrayElements);
@ -220,7 +218,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
else if (StringUtils_startsWith((char*) lineBuffer, "DS")) { else if (StringUtils_startsWith((char*) lineBuffer, "DS")) {
indendation = 4; indendation = 4;
sscanf((char*) lineBuffer, "DS(%s)", nameString); sscanf((char*) lineBuffer, "DS(%129s)", nameString);
terminateString(nameString, ')'); terminateString(nameString, ')');
currentDataSet = DataSet_create(nameString, currentLN); currentDataSet = DataSet_create(nameString, currentLN);
@ -233,7 +231,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
uint32_t bufTm; uint32_t bufTm;
uint32_t intgPd; uint32_t intgPd;
int matchedItems = sscanf((char*) lineBuffer, "RC(%s %s %i %s %u %i %i %u %u)", int matchedItems = sscanf((char*) lineBuffer, "RC(%129s %129s %i %129s %u %i %i %u %u)",
nameString, nameString2, &isBuffered, nameString3, &confRef, nameString, nameString2, &isBuffered, nameString3, &confRef,
&trgOps, &options, &bufTm, &intgPd); &trgOps, &options, &bufTm, &intgPd);
@ -258,7 +256,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int logEna; int logEna;
int withReasonCode; int withReasonCode;
int matchedItems = sscanf((char*) lineBuffer, "LC(%s %s %s %u %u %i %i)", int matchedItems = sscanf((char*) lineBuffer, "LC(%129s %129s %129s %u %u %i %i)",
nameString, nameString2, nameString3, &trgOps, &intgPd, &logEna, &withReasonCode); nameString, nameString2, nameString3, &trgOps, &intgPd, &logEna, &withReasonCode);
if (matchedItems < 7) goto exit_error; if (matchedItems < 7) goto exit_error;
@ -274,7 +272,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
LogControlBlock_create(nameString, currentLN, dataSet, logRef, trgOps, intgPd, logEna, withReasonCode); LogControlBlock_create(nameString, currentLN, dataSet, logRef, trgOps, intgPd, logEna, withReasonCode);
} }
else if (StringUtils_startsWith((char*) lineBuffer, "LOG")) { else if (StringUtils_startsWith((char*) lineBuffer, "LOG")) {
int matchedItems = sscanf((char*) lineBuffer, "LOG(%s)", nameString); int matchedItems = sscanf((char*) lineBuffer, "LOG(%129s)", nameString);
if (matchedItems < 1) goto exit_error; if (matchedItems < 1) goto exit_error;
@ -289,7 +287,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int minTime = -1; int minTime = -1;
int maxTime = -1; int maxTime = -1;
int matchedItems = sscanf((char*) lineBuffer, "GC(%s %s %s %u %i %i %i)", int matchedItems = sscanf((char*) lineBuffer, "GC(%129s %129s %129s %u %i %i %i)",
nameString, nameString2, nameString3, &confRef, &fixedOffs, &minTime, &maxTime); nameString, nameString2, nameString3, &confRef, &fixedOffs, &minTime, &maxTime);
if (matchedItems < 5) goto exit_error; if (matchedItems < 5) goto exit_error;
@ -307,7 +305,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int optFlds; int optFlds;
int isUnicast; int isUnicast;
int matchedItems = sscanf((char*) lineBuffer, "SMVC(%s %s %s %u %i %i %i %i)", int matchedItems = sscanf((char*) lineBuffer, "SMVC(%129s %129s %129s %u %i %i %i %i)",
nameString, nameString2, nameString3, &confRev, &smpMod, &smpRate, &optFlds, &isUnicast); nameString, nameString2, nameString3, &confRev, &smpMod, &smpRate, &optFlds, &isUnicast);
if (matchedItems < 5) goto exit_error; if (matchedItems < 5) goto exit_error;
@ -353,7 +351,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int arrayElements = 0; int arrayElements = 0;
int matchedItems = sscanf((char*) lineBuffer, "DO(%s %i)", nameString, &arrayElements); int matchedItems = sscanf((char*) lineBuffer, "DO(%129s %i)", nameString, &arrayElements);
if (matchedItems != 2) goto exit_error; if (matchedItems != 2) goto exit_error;
@ -368,7 +366,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int triggerOptions = 0; int triggerOptions = 0;
uint32_t sAddr = 0; uint32_t sAddr = 0;
sscanf((char*) lineBuffer, "DA(%s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr); sscanf((char*) lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr);
DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode, DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode,
(DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr); (DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr);
@ -483,6 +481,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
if (start) { if (start) {
start++; start++;
strncpy(nameString, start, 129); strncpy(nameString, start, 129);
nameString[129] = 0;
terminateString(nameString, ')'); terminateString(nameString, ')');
int indexVal = -1; int indexVal = -1;
@ -514,7 +513,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
uint32_t vlanId; uint32_t vlanId;
uint32_t appId; uint32_t appId;
int matchedItems = sscanf((char*) lineBuffer, "PA(%u %u %u %s)", &vlanPrio, &vlanId, &appId, nameString); int matchedItems = sscanf((char*) lineBuffer, "PA(%u %u %u %129s)", &vlanPrio, &vlanId, &appId, nameString);
if ((matchedItems != 4) || ((currentGoCB == NULL) && (currentSMVCB == NULL))) goto exit_error; if ((matchedItems != 4) || ((currentGoCB == NULL) && (currentSMVCB == NULL))) goto exit_error;
@ -552,7 +551,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
indendation = 1; indendation = 1;
} }
else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) { else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) {
sscanf((char*) lineBuffer, "MODEL(%s)", nameString); sscanf((char*) lineBuffer, "MODEL(%129s)", nameString);
terminateString(nameString, ')'); terminateString(nameString, ')');
model = IedModel_create(nameString); model = IedModel_create(nameString);
stateInModel = true; stateInModel = true;

@ -1,7 +1,7 @@
/* /*
* dynamic_model.c * dynamic_model.c
* *
* Copyright 2014-2016 Michael Zillgith * Copyright 2014-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -41,10 +41,12 @@ IedModel_setIedNameForDynamicModel(IedModel* self, const char* name)
} }
IedModel* IedModel*
IedModel_create(const char* name/*, MemoryAllocator allocator*/) IedModel_create(const char* name)
{ {
IedModel* self = (IedModel*) GLOBAL_CALLOC(1, sizeof(IedModel)); IedModel* self = (IedModel*) GLOBAL_CALLOC(1, sizeof(IedModel));
if (self)
{
if (name) if (name)
self->name = StringUtils_copyString(name); self->name = StringUtils_copyString(name);
else else
@ -63,6 +65,7 @@ IedModel_create(const char* name/*, MemoryAllocator allocator*/)
self->logs = NULL; self->logs = NULL;
self->initializer = iedModel_emptyVariableInitializer; self->initializer = iedModel_emptyVariableInitializer;
}
return self; return self;
} }
@ -309,7 +312,7 @@ LogicalNode_addLogControlBlock(LogicalNode* self, LogControlBlock* lcb)
} }
LogControlBlock* LogControlBlock*
LogControlBlock_create(const char* name, LogicalNode* parent, char* dataSetName, char* logRef, uint8_t trgOps, LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps,
uint32_t intPeriod, bool logEna, bool reasonCode) uint32_t intPeriod, bool logEna, bool reasonCode)
{ {
LogControlBlock* self = (LogControlBlock*) GLOBAL_MALLOC(sizeof(LogControlBlock)); LogControlBlock* self = (LogControlBlock*) GLOBAL_MALLOC(sizeof(LogControlBlock));
@ -347,7 +350,7 @@ LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb)
} }
ReportControlBlock* ReportControlBlock*
ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bool isBuffered, char* ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rptId, bool isBuffered, const char*
dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd) dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd)
{ {
ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock)); ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock));
@ -381,7 +384,7 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bo
} }
void void
ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clientType, uint8_t* clientAddress) ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clientType, const uint8_t* clientAddress)
{ {
if (clientType == 4) { /* IPv4 address */ if (clientType == 4) { /* IPv4 address */
self->clientReservation[0] = 4; self->clientReservation[0] = 4;
@ -396,6 +399,24 @@ ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clie
} }
} }
const char*
ReportControlBlock_getName(ReportControlBlock* self)
{
return self->name;
}
LogicalNode*
ReportControlBlock_getParent(ReportControlBlock* self)
{
return self->parent;
}
bool
ReportControlBlock_isBuffered(ReportControlBlock* self)
{
return self->buffered;
}
#if (CONFIG_IEC61850_SETTING_GROUPS == 1) #if (CONFIG_IEC61850_SETTING_GROUPS == 1)
static void static void
LogicalNode_addSettingGroupControlBlock(LogicalNode* self, SettingGroupControlBlock* sgcb) LogicalNode_addSettingGroupControlBlock(LogicalNode* self, SettingGroupControlBlock* sgcb)
@ -434,7 +455,7 @@ LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb)
} }
GSEControlBlock* GSEControlBlock*
GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRef, bool fixedOffs, GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, const char* dataSet, uint32_t confRef, bool fixedOffs,
int minTime, int maxTime) int minTime, int maxTime)
{ {
GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock)); GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock));
@ -468,7 +489,7 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char*
} }
SVControlBlock* SVControlBlock*
SVControlBlock_create(const char* name, LogicalNode* parent, char* svID, char* dataSet, uint32_t confRev, uint8_t smpMod, SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, const char* dataSet, uint32_t confRev, uint8_t smpMod,
uint16_t smpRate, uint8_t optFlds, bool isUnicast) uint16_t smpRate, uint8_t optFlds, bool isUnicast)
{ {
SVControlBlock* self = (SVControlBlock*) GLOBAL_MALLOC(sizeof(SVControlBlock)); SVControlBlock* self = (SVControlBlock*) GLOBAL_MALLOC(sizeof(SVControlBlock));
@ -742,6 +763,7 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha
char variableName[130]; char variableName[130];
strncpy(variableName, variable, 129); strncpy(variableName, variable, 129);
variableName[129] = 0;
char* separator = strchr(variableName, '/'); char* separator = strchr(variableName, '/');
@ -777,6 +799,9 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha
static void static void
ModelNode_destroy(ModelNode* modelNode) ModelNode_destroy(ModelNode* modelNode)
{ {
if (modelNode) {
if (modelNode->name)
GLOBAL_FREEMEM(modelNode->name); GLOBAL_FREEMEM(modelNode->name);
ModelNode* currentChild = modelNode->firstChild; ModelNode* currentChild = modelNode->firstChild;
@ -799,11 +824,13 @@ ModelNode_destroy(ModelNode* modelNode)
} }
GLOBAL_FREEMEM(modelNode); GLOBAL_FREEMEM(modelNode);
}
} }
void void
IedModel_destroy(IedModel* model) IedModel_destroy(IedModel* model)
{ {
if (model) {
/* delete all model nodes and dynamically created strings */ /* delete all model nodes and dynamically created strings */
/* delete all logical devices */ /* delete all logical devices */
@ -811,10 +838,9 @@ IedModel_destroy(IedModel* model)
LogicalDevice* ld = model->firstChild; LogicalDevice* ld = model->firstChild;
while (ld != NULL) { while (ld != NULL) {
GLOBAL_FREEMEM (ld->name);
if (ld->ldName) if (ld->ldName)
GLOBAL_FREEMEM (ld->ldName); GLOBAL_FREEMEM (ld->name);
LogicalNode* ln = (LogicalNode*) ld->firstChild; LogicalNode* ln = (LogicalNode*) ld->firstChild;
@ -963,13 +989,12 @@ IedModel_destroy(IedModel* model)
log = nextLog; log = nextLog;
} }
/* delete generic model parts */ /* delete generic model parts */
if (model->name) if (model->name)
GLOBAL_FREEMEM(model->name); GLOBAL_FREEMEM(model->name);
GLOBAL_FREEMEM(model); GLOBAL_FREEMEM(model);
}
} }

@ -1,7 +1,7 @@
/* /*
* model.c * model.c
* *
* Copyright 2013 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -107,6 +107,9 @@ IedModel_lookupDataSet(IedModel* self, const char* dataSetReference /* e.g. ied
int modelNameLen = strlen(self->name); int modelNameLen = strlen(self->name);
if (modelNameLen > 64)
return NULL;
memcpy(domainName, self->name, modelNameLen); memcpy(domainName, self->name, modelNameLen);
while (dataSet != NULL) { while (dataSet != NULL) {
@ -147,7 +150,7 @@ IedModel_getDevice(IedModel* self, const char* deviceName)
{ {
LogicalDevice* device = self->firstChild; LogicalDevice* device = self->firstChild;
while (device != NULL) { while (device) {
if (device->ldName) { if (device->ldName) {
/* functional naming */ /* functional naming */
@ -158,8 +161,8 @@ IedModel_getDevice(IedModel* self, const char* deviceName)
char domainName[65]; char domainName[65];
strncpy(domainName, self->name, 64); strncpy(domainName, self->name, 64);
strncat(domainName, device->name, 64);
domainName[64] = 0; domainName[64] = 0;
strncat(domainName, device->name, 64);
if (strcmp(domainName, deviceName) == 0) if (strcmp(domainName, deviceName) == 0)
return device; return device;
@ -176,8 +179,8 @@ IedModel_getDeviceByInst(IedModel* self, const char* ldInst)
{ {
LogicalDevice* device = self->firstChild; LogicalDevice* device = self->firstChild;
while (device != NULL) { while (device)
{
if (strcmp(device->name, ldInst) == 0) if (strcmp(device->name, ldInst) == 0)
return device; return device;
@ -389,7 +392,6 @@ IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectR
return ModelNode_getChild((ModelNode*) ld, separator + 1); return ModelNode_getChild((ModelNode*) ld, separator + 1);
} }
bool bool
DataObject_hasFCData(DataObject* dataObject, FunctionalConstraint fc) DataObject_hasFCData(DataObject* dataObject, FunctionalConstraint fc)
{ {
@ -488,17 +490,15 @@ LogicalDevice_getLogicalNodeCount(LogicalDevice* logicalDevice)
ModelNode* ModelNode*
LogicalDevice_getChildByMmsVariableName(LogicalDevice* logicalDevice, const char* mmsVariableName) LogicalDevice_getChildByMmsVariableName(LogicalDevice* logicalDevice, const char* mmsVariableName)
{ {
char fcString[3];
char nameRef[65];
const char* separator = strchr(mmsVariableName,'$'); const char* separator = strchr(mmsVariableName,'$');
if (separator == NULL) if (separator == NULL)
return NULL; return NULL;
if (strlen(separator) > 4) { if (strlen(separator) > 4) {
char fcString[3];
char nameRef[65];
fcString[0] = separator[1]; fcString[0] = separator[1];
fcString[1] = separator[2]; fcString[1] = separator[2];
fcString[2] = 0; fcString[2] = 0;
@ -569,9 +569,8 @@ createObjectReference(ModelNode* node, char* objectReference, bool withoutIedNam
nameLength = strlen (iedModel->name) + strlen(lDevice->name); nameLength = strlen (iedModel->name) + strlen(lDevice->name);
strncpy(objectReference, iedModel->name, 64); strncpy(objectReference, iedModel->name, 64);
strncat(objectReference, lDevice->name, 64);
objectReference[64] = 0; objectReference[64] = 0;
strncat(objectReference, lDevice->name, 64);
} }
} }

@ -295,7 +295,6 @@ SqliteLogStorage_addEntry(LogStorage self, uint64_t timestamp)
sqlite3* db = instanceData->db; sqlite3* db = instanceData->db;
int rc; int rc;
char *zErrMsg = 0;
rc = sqlite3_bind_int64(instanceData->insertEntryStmt, 1, (sqlite_int64) timestamp); rc = sqlite3_bind_int64(instanceData->insertEntryStmt, 1, (sqlite_int64) timestamp);
@ -336,12 +335,8 @@ SqliteLogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dat
{ {
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
sqlite3* db = instanceData->db;
int rc; int rc;
char *zErrMsg = 0;
rc = sqlite3_bind_int64(instanceData->insertEntryDataStmt, 1, (sqlite_int64) entryID); rc = sqlite3_bind_int64(instanceData->insertEntryDataStmt, 1, (sqlite_int64) entryID);
if (rc != SQLITE_OK) if (rc != SQLITE_OK)
@ -431,8 +426,6 @@ SqliteLogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t end
{ {
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
sqlite3* db = instanceData->db;
int rc; int rc;
rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 1, startingTime); rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 1, startingTime);
@ -488,8 +481,6 @@ SqliteLogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry,
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
sqlite3* db = instanceData->db;
int rc; int rc;
/* Get oldest entry */ /* Get oldest entry */
@ -532,8 +523,6 @@ SqliteLogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_
{ {
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData); SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
sqlite3* db = instanceData->db;
int rc; int rc;
rc = sqlite3_bind_int64(instanceData->getEntriesAfter, 1, entryID); rc = sqlite3_bind_int64(instanceData->getEntriesAfter, 1, entryID);

@ -181,16 +181,14 @@ void
BerEncoder_revertByteOrder(uint8_t* octets, const int size) BerEncoder_revertByteOrder(uint8_t* octets, const int size)
{ {
int i; int i;
uint8_t temp;
for (i = 0; i < size / 2; i++) { for (i = 0; i < size / 2; i++) {
temp = octets[i]; uint8_t temp = octets[i];
octets[i] = octets[(size - 1) - i]; octets[i] = octets[(size - 1) - i];
octets[(size - 1) - i] = temp; octets[(size - 1) - i] = temp;
} }
} }
int int
BerEncoder_compressInteger(uint8_t* integer, int originalSize) BerEncoder_compressInteger(uint8_t* integer, int originalSize)
{ {
@ -431,13 +429,13 @@ BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufL
val = atoi(separator + 1); val = atoi(separator + 1);
int requiredBytes = 0;
if (val == 0) { if (val == 0) {
buffer[encodedBytes++] = 0; buffer[encodedBytes++] = 0;
} }
else { else {
int requiredBytes = 0;
int val2 = val; int val2 = val;
while (val2 > 0) { while (val2 > 0) {
requiredBytes++; requiredBytes++;
val2 = val2 >> 7; val2 = val2 >> 7;

@ -352,8 +352,8 @@ typedef void
LIB61850_API LinkedList /* <char*> */ LIB61850_API LinkedList /* <char*> */
MmsConnection_getVMDVariableNames(MmsConnection self, MmsError* mmsError); MmsConnection_getVMDVariableNames(MmsConnection self, MmsError* mmsError);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getVMDVariableNamesAsync(MmsConnection self, MmsError* mmsError, const char* continueAfter, MmsConnection_getVMDVariableNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* continueAfter,
MmsConnection_GetNameListHandler handler, void* parameter); MmsConnection_GetNameListHandler handler, void* parameter);
/** /**
@ -374,16 +374,15 @@ MmsConnection_getDomainNames(MmsConnection self, MmsError* mmsError);
* \brief Get the domain names of the server (asynchronous version). * \brief Get the domain names of the server (asynchronous version).
* *
* \param[in] self MmsConnection instance to operate on * \param[in] self MmsConnection instance to operate on
* \param[out] usedInvokeId the invoke ID of the request
* \param[out] mmsError user provided variable to store error code * \param[out] mmsError user provided variable to store error code
* \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call
* \param[in] result list to store (append) the response names, or NULL to create a new list for the response names * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names
* \param[in] handler will be called when response is received or timed out. * \param[in] handler will be called when response is received or timed out.
* \param[in] parameter * \param[in] parameter
*
* \return the invoke ID of the request
*/ */
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getDomainNamesAsync(MmsConnection self, MmsError* mmsError, const char* continueAfter, LinkedList result, MmsConnection_getDomainNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* continueAfter, LinkedList result,
MmsConnection_GetNameListHandler handler, void* parameter); MmsConnection_GetNameListHandler handler, void* parameter);
/** /**
@ -406,17 +405,16 @@ MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* mmsError, con
* This will result in a domain specific GetNameList request. * This will result in a domain specific GetNameList request.
* *
* \param[in] self MmsConnection instance to operate on * \param[in] self MmsConnection instance to operate on
* \param[out] usedInvokeId the invoke ID of the request
* \param[out] mmsError user provided variable to store error code * \param[out] mmsError user provided variable to store error code
* \param[in] domainId the domain name for the domain specific request * \param[in] domainId the domain name for the domain specific request
* \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call
* \param[in] result list to store (append) the response names, or NULL to create a new list for the response names * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names
* \param[in] handler will be called when response is received or timed out. * \param[in] handler will be called when response is received or timed out.
* \param[in] parameter * \param[in] parameter
*
* \return the invoke ID of the request
*/ */
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getDomainVariableNamesAsync(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_getDomainVariableNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter); const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter);
/** /**
@ -433,8 +431,8 @@ MmsConnection_getDomainVariableNamesAsync(MmsConnection self, MmsError* mmsError
LIB61850_API LinkedList /* <char*> */ LIB61850_API LinkedList /* <char*> */
MmsConnection_getDomainVariableListNames(MmsConnection self, MmsError* mmsError, const char* domainId); MmsConnection_getDomainVariableListNames(MmsConnection self, MmsError* mmsError, const char* domainId);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getDomainVariableListNamesAsync(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_getDomainVariableListNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter); const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter);
/** /**
@ -451,8 +449,8 @@ MmsConnection_getDomainVariableListNamesAsync(MmsConnection self, MmsError* mmsE
LIB61850_API LinkedList /* <char*> */ LIB61850_API LinkedList /* <char*> */
MmsConnection_getDomainJournals(MmsConnection self, MmsError* mmsError, const char* domainId); MmsConnection_getDomainJournals(MmsConnection self, MmsError* mmsError, const char* domainId);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getDomainJournalsAsync(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_getDomainJournalsAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter); const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter);
/** /**
@ -468,8 +466,8 @@ MmsConnection_getDomainJournalsAsync(MmsConnection self, MmsError* mmsError, con
LIB61850_API LinkedList /* <char*> */ LIB61850_API LinkedList /* <char*> */
MmsConnection_getVariableListNamesAssociationSpecific(MmsConnection self, MmsError* mmsError); MmsConnection_getVariableListNamesAssociationSpecific(MmsConnection self, MmsError* mmsError);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getVariableListNamesAssociationSpecificAsync(MmsConnection self, MmsError* mmsError, MmsConnection_getVariableListNamesAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter); const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter);
@ -495,15 +493,14 @@ typedef void
/** /**
* \brief Read a single variable from the server (asynchronous version) * \brief Read a single variable from the server (asynchronous version)
* *
* \param self MmsConnection instance to operate on * \param{in] self MmsConnection instance to operate on
* \param mmsError user provided variable to store error code * \param[out] usedInvokeId the invoke ID of the request
* \param domainId the domain name of the variable to be read or NULL to read a VMD specific named variable * \param[out] mmsError user provided variable to store error code
* \param itemId name of the variable to be read * \param[in] domainId the domain name of the variable to be read or NULL to read a VMD specific named variable
* * \param[in] itemId name of the variable to be read
* \return invoke ID of the request when the request was sent successfully
*/ */
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readVariableAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_readVariableAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
MmsConnection_ReadVariableHandler handler, void* parameter); MmsConnection_ReadVariableHandler handler, void* parameter);
/** /**
@ -526,18 +523,17 @@ MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError,
/** /**
* \brief Read a component of a single variable from the server (asynchronous version) * \brief Read a component of a single variable from the server (asynchronous version)
* *
* \param self MmsConnection instance to operate on * \param[in] self MmsConnection instance to operate on
* \param mmsError user provided variable to store error code * \param[out] usedInvokeId the invoke ID of the request
* \param domainId the domain name of the variable to be read or NULL to read a VMD specific named variable * \param[out] mmsError user provided variable to store error code
* \param itemId name of the variable to be read * \param[in] domainId the domain name of the variable to be read or NULL to read a VMD specific named variable
* \param componentId the component name * \param[in] itemId name of the variable to be read
* \param handler * \param[in] componentId the component name
* \param parameter * \param[in] handler
* * \param[in] parameter
* \return invoke ID of the request when the request was sent successfully
*/ */
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readVariableComponentAsync(MmsConnection self, MmsError* mmsError, MmsConnection_readVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, const char* componentId, const char* domainId, const char* itemId, const char* componentId,
MmsConnection_ReadVariableHandler handler, void* parameter); MmsConnection_ReadVariableHandler handler, void* parameter);
@ -565,17 +561,16 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const ch
* NOTE: The MmsValue object received by the callback function is either a simple or complex type if numberOfElements is 0, or an array * NOTE: The MmsValue object received by the callback function is either a simple or complex type if numberOfElements is 0, or an array
* containing the selected array elements of numberOfElements > 0. * containing the selected array elements of numberOfElements > 0.
* *
* \param self MmsConnection instance to operate on * \param[in] self MmsConnection instance to operate on
* \param mmsError user provided variable to store error code * \param[out] usedInvokeId the invoke ID of the request
* \param domainId the domain name of the variable to be read * \param[out] mmsError user provided variable to store error code
* \param itemId name of the variable to be read * \param[in] domainId the domain name of the variable to be read
* \param startIndex index of element to read or start index if a element range is to be read * \param[in] itemId name of the variable to be read
* \param numberOfElements Number of elements to read or 0 if a single element is to be read * \param[in] startIndex index of element to read or start index if a element range is to be read
* * \param[in] numberOfElements Number of elements to read or 0 if a single element is to be read
* \return invoke ID of the request when the request was sent successfully
*/ */
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readArrayElementsAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_readArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
uint32_t startIndex, uint32_t numberOfElements, uint32_t startIndex, uint32_t numberOfElements,
MmsConnection_ReadVariableHandler handler, void* parameter); MmsConnection_ReadVariableHandler handler, void* parameter);
@ -596,8 +591,8 @@ LIB61850_API MmsValue*
MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError, MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId, uint32_t index, const char* componentId); const char* domainId, const char* itemId, uint32_t index, const char* componentId);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, MmsError* mmsError, MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, const char* domainId, const char* itemId,
uint32_t index, const char* componentId, uint32_t index, const char* componentId,
MmsConnection_ReadVariableHandler handler, void* parameter); MmsConnection_ReadVariableHandler handler, void* parameter);
@ -618,8 +613,8 @@ LIB61850_API MmsValue*
MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId,
LinkedList /*<char*>*/ items); LinkedList /*<char*>*/ items);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readMultipleVariablesAsync(MmsConnection self, MmsError* mmsError, MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, LinkedList /*<char*>*/items, const char* domainId, LinkedList /*<char*>*/items,
MmsConnection_ReadVariableHandler handler, void* parameter); MmsConnection_ReadVariableHandler handler, void* parameter);
@ -643,8 +638,8 @@ MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
typedef void typedef void
(*MmsConnection_WriteVariableHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, MmsDataAccessError accessError); (*MmsConnection_WriteVariableHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, MmsDataAccessError accessError);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_writeVariableAsync(MmsConnection self, MmsError* mmsError, MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, MmsValue* value, const char* domainId, const char* itemId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter); MmsConnection_WriteVariableHandler handler, void* parameter);
@ -667,8 +662,8 @@ MmsConnection_writeSingleArrayElementWithComponent(MmsConnection self, MmsError*
const char* domainId, const char* itemId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* componentId, MmsValue* value); uint32_t arrayIndex, const char* componentId, MmsValue* value);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, MmsError* mmsError, MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* componentId, MmsValue* value, uint32_t arrayIndex, const char* componentId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter); MmsConnection_WriteVariableHandler handler, void* parameter);
@ -696,8 +691,8 @@ MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId, int index, int numberOfElements, const char* domainId, const char* itemId, int index, int numberOfElements,
MmsValue* value); MmsValue* value);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_writeArrayElementsAsync(MmsConnection self, MmsError* mmsError, MmsConnection_writeArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, int index, int numberOfElements, const char* domainId, const char* itemId, int index, int numberOfElements,
MmsValue* value, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter); MmsConnection_WriteVariableHandler handler, void* parameter);
@ -729,8 +724,8 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con
LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values, LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values,
LinkedList* /* <MmsValue*> */ accessResults); LinkedList* /* <MmsValue*> */ accessResults);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_writeMultipleVariablesAsync(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values, LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values,
MmsConnection_WriteMultipleVariablesHandler handler, void* parameter); MmsConnection_WriteMultipleVariablesHandler handler, void* parameter);
@ -756,8 +751,8 @@ MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, boo
LinkedList* /* <MmsValue*> */accessResults); LinkedList* /* <MmsValue*> */accessResults);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_writeNamedVariableListAsync(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific, MmsConnection_writeNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, bool isAssociationSpecific,
const char* domainId, const char* itemId, LinkedList /* <MmsValue*> */values, const char* domainId, const char* itemId, LinkedList /* <MmsValue*> */values,
MmsConnection_WriteMultipleVariablesHandler handler, void* parameter); MmsConnection_WriteMultipleVariablesHandler handler, void* parameter);
@ -779,8 +774,8 @@ typedef void
(*MmsConnection_GetVariableAccessAttributesHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, MmsVariableSpecification* spec); (*MmsConnection_GetVariableAccessAttributesHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, MmsVariableSpecification* spec);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getVariableAccessAttributesAsync(MmsConnection self, MmsError* mmsError, MmsConnection_getVariableAccessAttributesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, const char* domainId, const char* itemId,
MmsConnection_GetVariableAccessAttributesHandler, void* parameter); MmsConnection_GetVariableAccessAttributesHandler, void* parameter);
@ -804,8 +799,8 @@ LIB61850_API MmsValue*
MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, const char* domainId,
const char* listName, bool specWithResult); const char* listName, bool specWithResult);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* listName, bool specWithResult, const char* domainId, const char* listName, bool specWithResult,
MmsConnection_ReadVariableHandler handler, void* parameter); MmsConnection_ReadVariableHandler handler, void* parameter);
@ -826,8 +821,8 @@ LIB61850_API MmsValue*
MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self, MmsError* mmsError,
const char* listName, bool specWithResult); const char* listName, bool specWithResult);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* listName, bool specWithResult, const char* listName, bool specWithResult,
MmsConnection_ReadVariableHandler handler, void* parameter); MmsConnection_ReadVariableHandler handler, void* parameter);
@ -848,8 +843,8 @@ LIB61850_API void
MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId,
const char* listName, LinkedList variableSpecs); const char* listName, LinkedList variableSpecs);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_defineNamedVariableListAsync(MmsConnection self, MmsError* mmsError, const char* domainId, MmsConnection_defineNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
const char* listName, LinkedList variableSpecs, const char* listName, LinkedList variableSpecs,
MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_GenericServiceHandler handler, void* parameter);
@ -868,8 +863,8 @@ LIB61850_API void
MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsError* mmsError, MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsError* mmsError,
const char* listName, LinkedList variableSpecs); const char* listName, LinkedList variableSpecs);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_defineNamedVariableListAssociationSpecificAsync(MmsConnection self, MmsError* mmsError, MmsConnection_defineNamedVariableListAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* listName, LinkedList variableSpecs, const char* listName, LinkedList variableSpecs,
MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_GenericServiceHandler handler, void* parameter);
@ -897,8 +892,8 @@ typedef void
(*MmsConnection_ReadNVLDirectoryHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsVariableAccessSpecification*> */ specs, bool deletable); (*MmsConnection_ReadNVLDirectoryHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsVariableAccessSpecification*> */ specs, bool deletable);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readNamedVariableListDirectoryAsync(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListDirectoryAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* listName, const char* domainId, const char* listName,
MmsConnection_ReadNVLDirectoryHandler handler, void* parameter); MmsConnection_ReadNVLDirectoryHandler handler, void* parameter);
@ -916,8 +911,8 @@ LIB61850_API LinkedList /* <MmsVariableAccessSpecification*> */
MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection self, MmsError* mmsError,
const char* listName, bool* deletable); const char* listName, bool* deletable);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* listName, const char* listName,
MmsConnection_ReadNVLDirectoryHandler handler, void* parameter); MmsConnection_ReadNVLDirectoryHandler handler, void* parameter);
@ -938,8 +933,8 @@ LIB61850_API bool
MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName); MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_deleteNamedVariableListAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName, MmsConnection_deleteNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* listName,
MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_GenericServiceHandler handler, void* parameter);
/** /**
@ -956,8 +951,8 @@ MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, Mms
const char* listName); const char* listName);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_deleteAssociationSpecificNamedVariableListAsync(MmsConnection self, MmsError* mmsError, const char* listName, MmsConnection_deleteAssociationSpecificNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* listName,
MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_GenericServiceHandler handler, void* parameter);
/** /**
@ -1033,8 +1028,8 @@ typedef void
(*MmsConnection_IdentifyHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, (*MmsConnection_IdentifyHandler) (uint32_t invokeId, void* parameter, MmsError mmsError,
char* vendorName, char* modelName, char* revision); char* vendorName, char* modelName, char* revision);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_identifyAsync(MmsConnection self, MmsError* mmsError, MmsConnection_identifyAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
MmsConnection_IdentifyHandler handler, void* parameter); MmsConnection_IdentifyHandler handler, void* parameter);
LIB61850_API void LIB61850_API void
@ -1059,8 +1054,8 @@ MmsConnection_getServerStatus(MmsConnection self, MmsError* mmsError, int* vmdLo
typedef void typedef void
(*MmsConnection_GetServerStatusHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, int vmdLogicalStatus, int vmdPhysicalStatus); (*MmsConnection_GetServerStatusHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, int vmdLogicalStatus, int vmdPhysicalStatus);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getServerStatusAsync(MmsConnection self, MmsError* mmsError, bool extendedDerivation, MmsConnection_getServerStatusAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, bool extendedDerivation,
MmsConnection_GetServerStatusHandler handler, void* parameter); MmsConnection_GetServerStatusHandler handler, void* parameter);
/******************************************************************************* /*******************************************************************************
@ -1117,8 +1112,8 @@ MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filen
typedef void typedef void
(*MmsConnection_FileOpenHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, int32_t frsmId, uint32_t fileSize, uint64_t lastModified); (*MmsConnection_FileOpenHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, int32_t frsmId, uint32_t fileSize, uint64_t lastModified);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_fileOpenAsync(MmsConnection self, MmsError* mmsError, const char* filename, uint32_t initialPosition, MmsConnection_FileOpenHandler handler, MmsConnection_fileOpenAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* filename, uint32_t initialPosition, MmsConnection_FileOpenHandler handler,
void* parameter); void* parameter);
@ -1136,8 +1131,8 @@ MmsConnection_fileOpenAsync(MmsConnection self, MmsError* mmsError, const char*
LIB61850_API bool LIB61850_API bool
MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, MmsFileReadHandler handler, void* handlerParameter); MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, MmsFileReadHandler handler, void* handlerParameter);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_fileReadAsync(MmsConnection self, MmsError* mmsError, int32_t frsmId, MmsConnection_FileReadHandler handler, void* parameter); MmsConnection_fileReadAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, int32_t frsmId, MmsConnection_FileReadHandler handler, void* parameter);
/** /**
* \brief close the file with the specified frsmID * \brief close the file with the specified frsmID
@ -1149,8 +1144,8 @@ MmsConnection_fileReadAsync(MmsConnection self, MmsError* mmsError, int32_t frsm
LIB61850_API void LIB61850_API void
MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId); MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_fileCloseAsync(MmsConnection self, MmsError* mmsError, uint32_t frsmId, MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_fileCloseAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, uint32_t frsmId, MmsConnection_GenericServiceHandler handler, void* parameter);
/** /**
* \brief delete the file with the specified name * \brief delete the file with the specified name
@ -1162,8 +1157,8 @@ MmsConnection_fileCloseAsync(MmsConnection self, MmsError* mmsError, uint32_t fr
LIB61850_API void LIB61850_API void
MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fileName); MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fileName);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_fileDeleteAsync(MmsConnection self, MmsError* mmsError, const char* fileName, MmsConnection_fileDeleteAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* fileName,
MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_GenericServiceHandler handler, void* parameter);
/** /**
@ -1177,8 +1172,8 @@ MmsConnection_fileDeleteAsync(MmsConnection self, MmsError* mmsError, const char
LIB61850_API void LIB61850_API void
MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* currentFileName, const char* newFileName); MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* currentFileName, const char* newFileName);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_fileRenameAsync(MmsConnection self, MmsError* mmsError, const char* currentFileName, const char* newFileName, MmsConnection_fileRenameAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* currentFileName, const char* newFileName,
MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_GenericServiceHandler handler, void* parameter);
/** /**
@ -1192,8 +1187,8 @@ MmsConnection_fileRenameAsync(MmsConnection self, MmsError* mmsError, const char
LIB61850_API void LIB61850_API void
MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sourceFile, const char* destinationFile); MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sourceFile, const char* destinationFile);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_obtainFileAsync(MmsConnection self, MmsError* mmsError, const char* sourceFile, const char* destinationFile, MmsConnection_obtainFileAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* sourceFile, const char* destinationFile,
MmsConnection_GenericServiceHandler handler, void* parameter); MmsConnection_GenericServiceHandler handler, void* parameter);
/** /**
@ -1216,8 +1211,8 @@ LIB61850_API bool
MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const char* fileSpecification, const char* continueAfter, MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const char* fileSpecification, const char* continueAfter,
MmsFileDirectoryHandler handler, void* handlerParameter); MmsFileDirectoryHandler handler, void* handlerParameter);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_getFileDirectoryAsync(MmsConnection self, MmsError* mmsError, const char* fileSpecification, const char* continueAfter, MmsConnection_getFileDirectoryAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* fileSpecification, const char* continueAfter,
MmsConnection_FileDirectoryHandler handler, void* parameter); MmsConnection_FileDirectoryHandler handler, void* parameter);
typedef struct sMmsJournalEntry* MmsJournalEntry; typedef struct sMmsJournalEntry* MmsJournalEntry;
@ -1274,16 +1269,16 @@ LIB61850_API LinkedList /* <MmsJournalEntry> */
MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* startTime, MmsValue* endTime, bool* moreFollows); MmsValue* startTime, MmsValue* endTime, bool* moreFollows);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readJournalTimeRangeAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_readJournalTimeRangeAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* startTime, MmsValue* endTime, MmsConnection_ReadJournalHandler handler, void* parameter); MmsValue* startTime, MmsValue* endTime, MmsConnection_ReadJournalHandler handler, void* parameter);
LIB61850_API LinkedList /* <MmsJournalEntry> */ LIB61850_API LinkedList /* <MmsJournalEntry> */
MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* timeSpecification, MmsValue* entrySpecification, bool* moreFollows); MmsValue* timeSpecification, MmsValue* entrySpecification, bool* moreFollows);
LIB61850_API uint32_t LIB61850_API void
MmsConnection_readJournalStartAfterAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_readJournalStartAfterAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* timeSpecification, MmsValue* entrySpecification, MmsConnection_ReadJournalHandler handler, void* parameter); MmsValue* timeSpecification, MmsValue* entrySpecification, MmsConnection_ReadJournalHandler handler, void* parameter);
/** /**

@ -101,7 +101,7 @@ MmsVariableSpecification_getType(MmsVariableSpecification* self);
* \return true if type is matching, false otherwise * \return true if type is matching, false otherwise
*/ */
LIB61850_API bool LIB61850_API bool
MmsVariableSpecification_isValueOfType(MmsVariableSpecification* self, MmsValue* value); MmsVariableSpecification_isValueOfType(MmsVariableSpecification* self, const MmsValue* value);
/** /**
* \brief get the name of the variable * \brief get the name of the variable

@ -80,7 +80,7 @@ typedef struct sMmsValue MmsValue;
* \return a newly created array instance * \return a newly created array instance
*/ */
LIB61850_API MmsValue* LIB61850_API MmsValue*
MmsValue_createArray(MmsVariableSpecification* elementType, int size); MmsValue_createArray(const MmsVariableSpecification* elementType, int size);
/** /**
* \brief Get the size of an array. * \brief Get the size of an array.
@ -552,7 +552,22 @@ MmsValue_getBinaryTimeAsUtcMs(const MmsValue* self);
* \param size the size of the buffer that contains the new value * \param size the size of the buffer that contains the new value
*/ */
LIB61850_API void LIB61850_API void
MmsValue_setOctetString(MmsValue* self, uint8_t* buf, int size); MmsValue_setOctetString(MmsValue* self, const uint8_t* buf, int size);
/**
* \brief Set a single octet of an MmsValue object of type MMS_OCTET_STRING.
*
* This method will copy the provided octet to the internal buffer of the
* MmsValue instance, at the 'octetPos' position. This will only happen
* if the internal buffer size is large enough. Otherwise the object value is not changed.
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
* \param octetPos the position of the octet in the octet string. Starting with 0.
* The octet with position 0 is the first octet if the MmsValue instance is serialized.
* \param value the new value of the octet (0 to 255, or 0x00 to 0xFF)
*/
LIB61850_API void
MmsValue_setOctetStringOctet(MmsValue* self, int octetPos, uint8_t value);
/** /**
* \brief Returns the size in bytes of an MmsValue object of type MMS_OCTET_STRING. * \brief Returns the size in bytes of an MmsValue object of type MMS_OCTET_STRING.
@ -592,6 +607,20 @@ MmsValue_getOctetStringMaxSize(MmsValue* self);
LIB61850_API uint8_t* LIB61850_API uint8_t*
MmsValue_getOctetStringBuffer(MmsValue* self); MmsValue_getOctetStringBuffer(MmsValue* self);
/**
* \brief Get the value of a single octet of an MmsType object of type MMS_OCTET_STRING
*
* NOTE: The octet quantity of the octet string can be requested with
* the \ref MmsValue_getOctetStringSize function.
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
* \param octetPos the position of the octet in the octet string. Starting with 0. The octet
* with position 0 is the first octet if the MmsValue instance is serialized.
* \return the value of the octet (0 to 255, or 0x00 to 0xFF)
*/
LIB61850_API uint8_t
MmsValue_getOctetStringOctet(MmsValue* self, int octetPos);
/** /**
* \brief Update the value of an MmsValue instance by the value of another MmsValue instance. * \brief Update the value of an MmsValue instance by the value of another MmsValue instance.
* *
@ -839,7 +868,7 @@ MmsValue_newBinaryTime(bool timeOfDay);
* \return new MmsValue instance of type MMS_VISIBLE_STRING * \return new MmsValue instance of type MMS_VISIBLE_STRING
*/ */
LIB61850_API MmsValue* LIB61850_API MmsValue*
MmsValue_newVisibleStringFromByteArray(uint8_t* byteArray, int size); MmsValue_newVisibleStringFromByteArray(const uint8_t* byteArray, int size);
/** /**
* \brief Create a new MmsValue instance of type MMS_STRING from the specified byte array * \brief Create a new MmsValue instance of type MMS_STRING from the specified byte array
@ -850,7 +879,7 @@ MmsValue_newVisibleStringFromByteArray(uint8_t* byteArray, int size);
* \return new MmsValue instance of type MMS_STRING * \return new MmsValue instance of type MMS_STRING
*/ */
LIB61850_API MmsValue* LIB61850_API MmsValue*
MmsValue_newMmsStringFromByteArray(uint8_t* byteArray, int size); MmsValue_newMmsStringFromByteArray(const uint8_t* byteArray, int size);
/** /**
* \brief Create a new MmsValue instance of type MMS_STRING. * \brief Create a new MmsValue instance of type MMS_STRING.
@ -860,7 +889,7 @@ MmsValue_newMmsStringFromByteArray(uint8_t* byteArray, int size);
* \return new MmsValue instance of type MMS_STRING * \return new MmsValue instance of type MMS_STRING
*/ */
LIB61850_API MmsValue* LIB61850_API MmsValue*
MmsValue_newMmsString(char* string); MmsValue_newMmsString(const char* string);
/** /**
* \brief Set the value of MmsValue instance of type MMS_STRING * \brief Set the value of MmsValue instance of type MMS_STRING

@ -55,6 +55,10 @@ typedef struct {
ByteBuffer* writeBuffer; /* buffer to store TPKT packet to send */ ByteBuffer* writeBuffer; /* buffer to store TPKT packet to send */
ByteBuffer* readBuffer; /* buffer to store received TPKT packet */ ByteBuffer* readBuffer; /* buffer to store received TPKT packet */
uint16_t packetSize; /* size of the packet currently received */ uint16_t packetSize; /* size of the packet currently received */
uint8_t* socketExtensionBuffer; /* buffer to store data when TCP socket is not accepting all data */
int socketExtensionBufferSize; /* maximum number of bytes to store in the extension buffer */
int socketExtensionBufferFill; /* number of bytes in the extension buffer (bytes to write) */
} CotpConnection; } CotpConnection;
typedef enum { typedef enum {
@ -80,7 +84,8 @@ CotpConnection_setTpduSize(CotpConnection* self, int tpduSize /* in byte */);
LIB61850_INTERNAL void LIB61850_INTERNAL void
CotpConnection_init(CotpConnection* self, Socket socket, CotpConnection_init(CotpConnection* self, Socket socket,
ByteBuffer* payloadBuffer, ByteBuffer* readBuffer, ByteBuffer* writeBuffer); ByteBuffer* payloadBuffer, ByteBuffer* readBuffer, ByteBuffer* writeBuffer,
uint8_t* socketExtensionBuffer, int socketExtensionBufferSize);
LIB61850_INTERNAL CotpIndication LIB61850_INTERNAL CotpIndication
CotpConnection_parseIncomingMessage(CotpConnection* self); CotpConnection_parseIncomingMessage(CotpConnection* self);

@ -98,7 +98,7 @@ IsoConnection_getSecurityToken(IsoConnection self);
* \param handlerMode specifies if this function is used in the context of the connection handling thread * \param handlerMode specifies if this function is used in the context of the connection handling thread
* (handlerMode) * (handlerMode)
*/ */
LIB61850_INTERNAL void LIB61850_INTERNAL bool
IsoConnection_sendMessage(IsoConnection self, ByteBuffer* message); IsoConnection_sendMessage(IsoConnection self, ByteBuffer* message);
LIB61850_INTERNAL IsoServer LIB61850_INTERNAL IsoServer

@ -291,7 +291,7 @@ mmsClient_createDeleteNamedVariableListRequest(long invokeId, ByteBuffer* writeB
const char* domainId, const char* listNameId); const char* domainId, const char* listNameId);
LIB61850_INTERNAL bool LIB61850_INTERNAL bool
mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId); mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId, long* numberDeleted, long* numberMatched);
LIB61850_INTERNAL void LIB61850_INTERNAL void
mmsClient_createDeleteAssociationSpecificNamedVariableListRequest( mmsClient_createDeleteAssociationSpecificNamedVariableListRequest(

@ -118,6 +118,9 @@ mmsMsg_createStringFromAsnIdentifier(Identifier_t identifier);
LIB61850_INTERNAL void LIB61850_INTERNAL void
mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, int bufSize); mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, int bufSize);
LIB61850_INTERNAL char*
mmsMsg_getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, char* componentNameBuf, int nameBufPos);
LIB61850_INTERNAL void LIB61850_INTERNAL void
mmsMsg_deleteAccessResultList(AccessResult_t** accessResult, int variableCount); mmsMsg_deleteAccessResultList(AccessResult_t** accessResult, int variableCount);

@ -51,7 +51,7 @@ MmsServerConnection_destroy(MmsServerConnection connection);
LIB61850_INTERNAL int LIB61850_INTERNAL int
MmsServerConnection_getMaxMmsPduSize(MmsServerConnection self); MmsServerConnection_getMaxMmsPduSize(MmsServerConnection self);
LIB61850_INTERNAL void LIB61850_INTERNAL bool
MmsServerConnection_sendMessage(MmsServerConnection self, ByteBuffer* message); MmsServerConnection_sendMessage(MmsServerConnection self, ByteBuffer* message);
LIB61850_INTERNAL bool LIB61850_INTERNAL bool

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

Loading…
Cancel
Save