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
------------------------
- 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
if(DEFINED ENV{TOOLCHAIN})
@ -12,7 +12,7 @@ ENABLE_TESTING()
set(LIB_VERSION_MAJOR "1")
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(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_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_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" )

@ -25,12 +25,9 @@ Content:
* [Experimental Python bindings](#experimental-python-bindings)
* [Licensing](#commercial-licenses-and-support)
* [Contributing](#contributing)
* [Third-party contributions](#third-party-contributions)
## 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.
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)
** 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
@ -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.
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
/* 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 */
#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_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_report1 = {&iedModel_GenericIO_LLN0, "EventsRCB02", "Events1", false, "Events", 1, 24, 111, 50, 1000, &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_report3 = {&iedModel_GenericIO_LLN0, "EventsRCB04", "Events1", false, "Events", 1, 24, 111, 50, 1000, &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_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, 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, 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, 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, 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);
}
private ControlObjectClient_ControlActionHandler internalOperateHandler = null;
private void nativeOperateHandler (UInt32 invokeId, IntPtr parameter, int err, int type, bool success)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -487,7 +489,10 @@ namespace IEC61850
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)
{
@ -522,7 +527,10 @@ namespace IEC61850
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)
{
@ -634,7 +642,10 @@ namespace IEC61850
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)
{
@ -669,7 +680,10 @@ namespace IEC61850
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)
{

@ -1,7 +1,7 @@
/*
* IEC61850ClientAPI.cs
*
* Copyright 2014-2019 Michael Zillgith
* Copyright 2014-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -431,6 +431,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
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)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort);
@ -629,6 +632,24 @@ namespace IEC61850
IedConnection_readDataSetValuesAsync(IntPtr self, out int error, string dataSetReference, IntPtr dataSet,
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)]
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>
/// Gets the underlying MmsConnection instance.
/// </summary>
@ -1440,6 +1473,8 @@ namespace IEC61850
throw new IedConnectionException("Deleting file " + fileName + " failed", error);
}
private IedConnection_GenericServiceHandler internalGenericServiceHandler = null;
private void nativeGenericServiceHandler(UInt32 invokeId, IntPtr parameter, int err)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -1467,7 +1502,10 @@ namespace IEC61850
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)
{
@ -1570,6 +1608,12 @@ namespace IEC61850
static extern UInt32 IedConnection_getFile(IntPtr self, out int error, string fileName, InternalIedClientGetFileHandler handler,
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);
@ -1615,10 +1659,35 @@ namespace IEC61850
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)]
[return: MarshalAs(UnmanagedType.I1)]
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>
/// Callback handler for the asynchronous get file service. Will be invoked for each chunk of received data
@ -1629,6 +1698,7 @@ namespace IEC61850
/// <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="moreFollows">indicates that more file data follows</param>
/// <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)]
@ -1636,10 +1706,11 @@ namespace IEC61850
IedConnection_getFileAsync(IntPtr self, out int error, string fileName, IedConnection_GetFileAsyncHandler handler,
IntPtr parameter);
private IedConnection_GetFileAsyncHandler internalGetFileAsyncHandler = null;
private bool nativeGetFileAsyncHandler(UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId,
IntPtr buffer, UInt32 bytesRead, bool moreFollows)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple<GetFileAsyncHandler, object> callbackInfo = handle.Target as Tuple<GetFileAsyncHandler, object>;
@ -1647,8 +1718,6 @@ namespace IEC61850
GetFileAsyncHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
handle.Free();
IedClientError clientError = (IedClientError)err;
byte[] bytes = null;
@ -1660,7 +1729,28 @@ namespace IEC61850
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>
@ -1685,7 +1775,10 @@ namespace IEC61850
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)
{
@ -1696,9 +1789,6 @@ namespace IEC61850
return invokeId;
}
/// <summary>
/// Abort (close) the connection.
/// </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>
/// Delete a data set.
/// </summary>
@ -1978,6 +2112,38 @@ namespace IEC61850
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>
/// Get the directory of the data set.
/// </summary>
@ -2027,6 +2193,77 @@ namespace IEC61850
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>
/// Read object handler.
@ -2037,6 +2274,8 @@ namespace IEC61850
/// <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);
private IedConnection_ReadObjectHandler internalReadObjectHandler = null;
private void nativeReadObjectHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr value)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2075,7 +2314,10 @@ namespace IEC61850
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)
{
@ -2086,6 +2328,8 @@ namespace IEC61850
return invokeId;
}
private IedConnection_GetVariableSpecificationHandler internalGetVariableSpecificationHandler = null;
private void nativeGetVariableSpecifcationHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr spec)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2124,7 +2368,10 @@ namespace IEC61850
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)
{
@ -2135,6 +2382,8 @@ namespace IEC61850
return invokeId;
}
private IedConnection_ReadDataSetHandler internalReadDataSetHandler = null;
private void nativeReadDataSetHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr nativeDataSet)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2188,7 +2437,10 @@ namespace IEC61850
if (dataSet != null)
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)
{
@ -2207,6 +2459,8 @@ namespace IEC61850
/// <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);
private IedConnection_WriteObjectHandler internalWriteObjectHandler = null;
private void nativeWriteObjectHandler(UInt32 invokeId, IntPtr parameter, int err)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2232,7 +2486,10 @@ namespace IEC61850
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)
{
@ -2245,6 +2502,8 @@ namespace IEC61850
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)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2303,7 +2562,10 @@ namespace IEC61850
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)
{
@ -2327,7 +2589,10 @@ namespace IEC61850
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)
{
@ -2352,7 +2617,10 @@ namespace IEC61850
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)
{
@ -2365,6 +2633,8 @@ namespace IEC61850
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,
[MarshalAs(UnmanagedType.I1)] bool moreFollows)
{
@ -2402,7 +2672,10 @@ namespace IEC61850
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)
{
@ -2450,7 +2723,10 @@ namespace IEC61850
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)
{
@ -2501,6 +2777,7 @@ namespace IEC61850
}
}
private IedConnection_GetRCBValuesHandler internalGetRCBValuesHandler = null;
private void nativeGetRCBValuesHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr rcbPtr)
{
@ -2527,7 +2804,10 @@ namespace IEC61850
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)
{
@ -2538,6 +2818,8 @@ namespace IEC61850
return invokeId;
}
private IedConnection_GenericServiceHandler internalSetRcbValueHandler = null;
private void nativeSetRcbValuesHandler(UInt32 invokeId, IntPtr parameter, int err)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
@ -2563,7 +2845,10 @@ namespace IEC61850
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)
{

@ -1,7 +1,7 @@
/*
* IEC61850ServerAPI.cs
*
* Copyright 2016 Michael Zillgith
* Copyright 2016-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -331,12 +331,19 @@ namespace IEC61850
this.parent = parent;
}
internal Dictionary<IntPtr, ReportControlBlock> rcbs = new Dictionary<IntPtr, ReportControlBlock>();
public LogicalNode(string name, LogicalDevice parent)
{
this.parent = parent;
base.self = LogicalNode_create(name, parent.self);
}
internal void AddRcb(ReportControlBlock rcb)
{
rcbs.Add(rcb.self, rcb);
}
}
public enum AccessPolicy {
@ -1301,12 +1308,82 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
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;
private string name = null;
private LogicalNode parent = null;
public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered,
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);
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)
@ -1316,6 +1393,197 @@ namespace IEC61850
else if (clientAddress.Length == 6)
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>
@ -1691,6 +1959,55 @@ namespace IEC61850
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,
ClientConnection connection, object parameter);
@ -1841,6 +2158,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
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)]
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)]
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 InternalControlHandler internalControlHandlerRef = null;
@ -2456,6 +2782,22 @@ namespace IEC61850
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>
/// Enable all GOOSE control blocks.
/// </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)]
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;
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>
/// Releases all resource used by the <see cref="IEC61850.Server.IedServerConfig"/> object.
/// </summary>

@ -174,6 +174,12 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
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)]
static extern IntPtr MmsValue_getOctetStringBuffer(IntPtr self);
@ -518,6 +524,11 @@ namespace IEC61850
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)
{
if (GetType () == MmsType.MMS_OCTET_STRING) {
@ -531,6 +542,26 @@ namespace IEC61850
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>
/// Get an element of an array or structure
/// </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">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -7,7 +7,8 @@
<OutputType>Exe</OutputType>
<RootNamespace>client_example_async</RootNamespace>
<AssemblyName>client_example_async</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -18,6 +19,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
@ -41,4 +43,7 @@
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
</Project>

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

@ -4,6 +4,7 @@ using System.Collections.Generic;
using IEC61850.Client;
using IEC61850.Common;
using System.IO;
using System.Threading;
namespace files
{
@ -19,11 +20,13 @@ namespace files
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()));
if (file.GetFileName ().EndsWith ("/")) {
if (file.GetFileName().EndsWith("/"))
{
printFiles(con, prefix + " ", parent + file.GetFileName());
}
}
@ -56,13 +59,15 @@ namespace files
Console.WriteLine("Connect to " + hostname);
try {
try
{
con.Connect(hostname, 102);
Console.WriteLine("Files in server root directory:");
List<string> serverDirectory = con.GetServerDirectory(true);
foreach (string entry in serverDirectory) {
foreach (string entry in serverDirectory)
{
Console.WriteLine(entry);
}
@ -80,12 +85,45 @@ namespace files
FileStream fs = new FileStream(filename, FileMode.Create);
BinaryWriter w = new BinaryWriter(fs);
con.GetFile (filename, new IedConnection.GetFileHandler (getFileHandler), w);
bool fileDownloadFinished = false;
//con.GetFile (filename, new IedConnection.GetFileHandler (getFileHandler), w);
/// COMMENT SECTION WHEN USING SYNC VERSION -->
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);
if (moreFollows == false)
fileDownloadFinished = true;
}
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) {
}
catch (IedConnectionException e)
{
Console.WriteLine(e.Message);
}

@ -67,6 +67,34 @@ namespace server1
}, 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);
if (iedServer.IsRunning())

@ -24,6 +24,7 @@ print_help()
printf("-x <filename> delete file\n");
printf("-j <domainName/journalName> read journal\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("-m print raw MMS messages\n");
}
@ -123,10 +124,11 @@ int main(int argc, char** argv)
int printRawMmsMessages = 0;
int deleteFile = 0;
int readVariableList = 0;
int readDataSetDirectory = 0;
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) {
case 'm':
printRawMmsMessages = 1;
@ -167,6 +169,11 @@ int main(int argc, char** argv)
readVariableList = 1;
variableName = StringUtils_copyString(optarg);
break;
case 'z':
readDataSetDirectory = 1;
variableName = StringUtils_copyString(optarg);
break;
case 'f':
showFileList = 1;
break;
@ -192,12 +199,13 @@ int main(int argc, char** argv)
print_help();
return 0;
}
}
MmsConnection con = MmsConnection_create();
MmsError error;
/* Set maximum MMS PDU size (local detail) to 2000 byte */
/* Set maximum MMS PDU size (local detail) */
MmsConnection_setLocalDetail(con, maxPduSize);
if (printRawMmsMessages)
@ -404,6 +412,41 @@ int main(int argc, char** argv)
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) {
char lastName[300];
lastName[0] = 0;

@ -79,6 +79,27 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected,
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
main(int argc, char** argv)
{
@ -136,6 +157,8 @@ main(int argc, char** argv)
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.
* This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used
* 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* 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);
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
if(DEFINED ENV{TOOLCHAIN})

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

@ -1,7 +1,7 @@
/*
* ethernet_linux.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -136,22 +136,6 @@ getInterfaceIndex(int sock, const char* deviceName)
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;
}
@ -179,7 +163,6 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
}
}
EthernetSocket
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_hatype = ARPHRD_ETHER;
ethernetSocket->socketAddress.sll_pkttype = PACKET_OTHERHOST;
ethernetSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;
ethernetSocket->socketAddress.sll_halen = ETH_ALEN;
@ -223,6 +206,82 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
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
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));
}
void
Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode)
{
/* not implemented */
}
void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
/* not implemented */
}
void
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
Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType)
{

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

@ -1,25 +1,10 @@
/*
* hal_base.h
*
* Copyright 2018 MZ Automation GmbH
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of Platform Abstraction Layer (libpal)
* for libiec61850 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.
* for libiec61850, libmms, and lib60870.
*/
#ifndef HAL_INC_HAL_BASE_H_

@ -1,24 +1,10 @@
/*
* ethernet_hal.h
*
* Copyright 2013, 2014 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
* This file is part of Platform Abstraction Layer (libpal)
* for libiec61850, libmms, and lib60870.
*/
#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;
/** Opaque reference for a set of ethernet socket handles */
/** Opaque reference for a set of Ethernet socket handles */
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)
*
@ -115,7 +108,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr);
* destination MAC address.
*
* \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
Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress);
@ -131,9 +124,35 @@ Ethernet_destroySocket(EthernetSocket ethSocket);
PAL_API void
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
*
* 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 etherType the ether type of messages to accept
*/

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

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

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

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

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

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

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

@ -3,7 +3,7 @@
*
* 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
*
@ -39,6 +39,8 @@ typedef struct sTLSConfiguration* TLSConfiguration;
/**
* \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
*/
PAL_API TLSConfiguration
@ -88,21 +90,64 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate,
PAL_API bool
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
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
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
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
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
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
TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename);
@ -116,6 +161,51 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
PAL_API void
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
TLSConfiguration_destroy(TLSConfiguration self);

@ -3,7 +3,7 @@
*
* 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
*

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

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

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

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

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

@ -1,26 +1,14 @@
/*
* socket_win32.c
*
* Copyright 2013-2020 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
* This file is part of Platform Abstraction Layer (libpal)
* for libiec61850, libmms, and lib60870.
*/
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
@ -71,6 +59,7 @@ Handleset_new(void)
FD_ZERO(&result->handles);
result->maxHandle = INVALID_SOCKET;
}
return result;
}
@ -172,16 +161,16 @@ setSocketNonBlocking(Socket self)
}
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));
if (address != NULL) {
struct hostent *server;
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);
}
@ -240,7 +229,7 @@ TcpServerSocket_create(const char* address, int port)
struct sockaddr_in server_addr;
if (!prepareServerAddress(address, port, &server_addr))
if (!prepareAddress(address, port, &server_addr))
return NULL;
listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@ -271,12 +260,18 @@ TcpServerSocket_create(const char* address, int port)
serverSocket = (ServerSocket)GLOBAL_MALLOC(sizeof(struct sServerSocket));
if (serverSocket) {
serverSocket->fd = listen_socket;
serverSocket->backLog = 10;
setSocketNonBlocking((Socket)serverSocket);
socketCount++;
}
else {
closesocket(listen_socket);
wsaShutdown();
}
return serverSocket;
}
@ -346,6 +341,7 @@ TcpSocket_create()
if (sock != INVALID_SOCKET) {
self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
if (self) {
self->fd = sock;
self->connectTimeout = 5000;
@ -353,7 +349,16 @@ TcpSocket_create()
}
else {
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;
@ -365,6 +370,29 @@ Socket_setConnectTimeout(Socket self, uint32_t 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
Socket_connectAsync(Socket self, const char* address, int port)
{
@ -381,7 +409,7 @@ Socket_connectAsync(Socket self, const char* address, int port)
return false;
}
if (!prepareServerAddress(address, port, &serverAddress))
if (!prepareAddress(address, port, &serverAddress))
return false;
setSocketNonBlocking(self);
@ -417,11 +445,25 @@ Socket_checkAsyncConnectState(Socket self)
int 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_FAILED;
}
@ -436,30 +478,35 @@ Socket_checkAsyncConnectState(Socket self)
bool
Socket_connect(Socket self, const char* address, int port)
{
struct sockaddr_in serverAddress;
if (!prepareServerAddress(address, port, &serverAddress))
if (Socket_connectAsync(self, address, port) == 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_ZERO(&fdSet);
FD_SET(self->fd, &fdSet);
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK)
return false;
if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) {
/* 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;
timeout.tv_sec = self->connectTimeout / 1000;
timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
closesocket (self->fd);
self->fd = INVALID_SOCKET;
if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) <= 0)
return false;
else
return true;
}
static char*
@ -647,7 +694,7 @@ UdpSocket_bind(UdpSocket self, const char* address, int port)
{
struct sockaddr_in localAddress;
if (!prepareServerAddress(address, port, &localAddress)) {
if (!prepareAddress(address, port, &localAddress)) {
closesocket(self->fd);
self->fd = 0;
return false;
@ -673,7 +720,7 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in
{
struct sockaddr_in remoteAddress;
if (!prepareServerAddress(address, port, &remoteAddress)) {
if (!prepareAddress(address, port, &remoteAddress)) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to lookup remote address %s\n", address);

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

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

@ -1,7 +1,7 @@
/**
* thread_macos.c
*
* Copyright 2013-2019 MZ Automation GmbH
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of Platform Abstraction Layer (libpal)
* for libiec61850, libmms, and lib60870.
@ -9,10 +9,10 @@
/*
* 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 <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
@ -32,56 +32,52 @@ typedef struct sSemaphore* mSemaphore;
struct sSemaphore
{
sem_t* sem;
pthread_mutex_t mutex;
};
/*
* NOTE: initialValue is ignored because semaphore was replaced by mutex
*/
Semaphore
Semaphore_create(int initialValue)
{
mSemaphore self = NULL;
char tmpname[] = {"/tmp/libiec61850.XXXXXX"};
char* res = mktemp(tmpname);
if (res) {
self = (mSemaphore) GLOBAL_CALLOC(1, sizeof(struct sSemaphore));
if (self) {
self->sem = sem_open(tmpname, O_CREAT, 0666, initialValue);
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);
}
}
pthread_mutex_init(&(self->mutex), NULL);
}
return (Semaphore)self;
}
/* Wait until semaphore value is more than zero. Then decrease the semaphore value. */
/* lock mutex */
void
Semaphore_wait(Semaphore 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
Semaphore_post(Semaphore 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
@ -90,10 +86,7 @@ Semaphore_destroy(Semaphore self)
if (self) {
mSemaphore mSelf = (mSemaphore) self;
int ret = sem_close(mSelf->sem);
if (ret == -1)
printf("ERROR: Failed to close semaphore (errno = %i)\n", errno);
pthread_mutex_destroy(&(mSelf->mutex));
GLOBAL_FREEMEM(mSelf);
}

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

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

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

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

@ -3,7 +3,7 @@
*
* 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
*
@ -14,6 +14,7 @@
#include "tls_socket.h"
#include "hal_thread.h"
#include "lib_memory.h"
#include "hal_time.h"
#include "linked_list.h"
#include "mbedtls/platform.h"
@ -27,9 +28,7 @@
#include "mbedtls/debug.h"
#if (CONFIG_DEBUG_TLS == 1)
#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, \
appId, \
__VA_ARGS__)
#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__);
#else
#define DEBUG_PRINT(fmt, ...) {do {} while(0);}
#endif
@ -44,14 +43,24 @@ struct sTLSConfiguration {
mbedtls_x509_crt cacerts;
mbedtls_x509_crl crl;
mbedtls_ssl_config conf;
LinkedList /* <mbedtls_x509_crt*> */ allowedCertificates;
bool chainValidation;
bool allowOnlyKnownCertificates;
/* TLS session renegotioation time in milliseconds */
/* TLS session renegotiation interval in milliseconds */
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 {
@ -62,6 +71,9 @@ struct sTLSSocket {
bool storePeerCert;
uint8_t* peerCert;
int peerCertLength;
/* time of last session renegotiation (used to calculate next renegotiation time) */
uint64_t lastRenegotiationTime;
};
static bool
@ -145,7 +157,27 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth
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_create()
@ -156,6 +188,7 @@ TLSConfiguration_create()
mbedtls_ssl_config_init( &(self->conf) );
mbedtls_x509_crt_init( &(self->ownCertificate) );
mbedtls_x509_crt_init( &(self->cacerts) );
mbedtls_x509_crl_init( &(self->crl) );
mbedtls_pk_init( &(self->ownKey) );
mbedtls_entropy_init( &(self->entropy) );
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_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}; */
/* 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();
/* default behavior is to allow all certificates that are signed by the CA */
self->chainValidation = true;
self->allowOnlyKnownCertificates = false;
self->setupComplete = false;
}
return self;
@ -192,6 +231,18 @@ TLSConfiguration_setClientMode(TLSConfiguration self)
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
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);
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);
}
@ -306,6 +357,29 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
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
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->cacerts));
mbedtls_x509_crl_free(&(self->crl));
mbedtls_pk_free(&(self->ownKey));
mbedtls_ssl_config_free(&(self->conf));
@ -348,6 +423,50 @@ readFunction(void* ctx, unsigned char* buf, size_t len)
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_create(Socket socket, TLSConfiguration configuration, bool storeClientCert)
{
@ -361,13 +480,33 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient
self->peerCert = NULL;
self->peerCertLength = 0;
TLSConfiguration_setupComplete(configuration);
memcpy(&(self->conf), &(configuration->conf), sizeof(mbedtls_ssl_config));
mbedtls_ssl_conf_verify(&(self->conf), verifyCertificate, (void*) self);
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));
@ -379,23 +518,31 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient
if (ret != 0)
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);
while( (ret = mbedtls_ssl_handshake(&(self->ssl)) ) != 0 )
{
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));
if (self->peerCert) {
GLOBAL_FREEMEM(self->peerCert);
}
GLOBAL_FREEMEM(self);
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;
@ -422,6 +569,19 @@ TLSSocket_performHandshake(TLSSocket self)
int
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);
if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE))
@ -492,7 +652,6 @@ TLSSocket_close(TLSSocket self)
Thread_sleep(10);
mbedtls_ssl_config_free(&(self->conf));
mbedtls_ssl_free(&(self->ssl));
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 <Python.h>
class PyThreadStateLock
{
public:
@ -25,7 +24,6 @@ private:
};
class EventHandler {
public:
EventHandler() {}
@ -118,7 +116,7 @@ class EventSubscriber {
m_subscriber_map.erase(l_it);
}
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;}
DataAttribute* toDataAttribute(ModelNode * MN)
{ return (DataAttribute*)MN;}
DataObject* toDataObject(ModelNode * MN)
{ return (DataObject*)MN;}
%}
%apply int *OUTPUT {IedClientError* error};
@ -55,6 +57,7 @@ ModelNode* toModelNode(LogicalNode *);
ModelNode* toModelNode(DataObject *);
DataAttribute* toDataAttribute(DataObject *);
DataAttribute* toDataAttribute(ModelNode *);
DataObject* toDataObject(ModelNode *);
char* toCharP(void *);
/* Goose Subscriber section */
@ -99,17 +102,23 @@ void GooseSubscriber_setDstMac(GooseSubscriber subscriber,
%feature("director") RCBHandler;
%feature("director") GooseHandler;
%feature("director") CommandTermHandler;
%feature("director") CheckHandlerForPython;
%feature("director") WaitForExecutionHandlerForPython;
%feature("director") ControlHandlerForPython;
%{
#include "eventHandlers/eventHandler.hpp"
#include "eventHandlers/reportControlBlockHandler.hpp"
#include "eventHandlers/gooseHandler.hpp"
#include "eventHandlers/commandTermHandler.hpp"
#include "eventHandlers/controlActionHandler.hpp"
std::map< std::string, EventSubscriber*> EventSubscriber::m_subscriber_map = {};
%}
%include "eventHandlers/eventHandler.hpp"
%include "eventHandlers/reportControlBlockHandler.hpp"
%include "eventHandlers/gooseHandler.hpp"
%include "eventHandlers/commandTermHandler.hpp"
%include "eventHandlers/controlActionHandler.hpp"
/* Goose Publisher section */
%{
@ -145,3 +154,9 @@ void CommParameters_setDstAddress(CommParameters *gooseCommParameters,
uint8_t dst_mac_4,
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"
#define LIBIEC61850_VERSION "1.5.0"
#define LIBIEC61850_VERSION "1.5.1"
#ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME
#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
*/
LIB61850_INTERNAL bool
StringUtils_startsWith(char* string, char* prefix);
StringUtils_startsWith(const char* string, const char* prefix);
LIB61850_INTERNAL bool
StringUtils_endsWith(const char* str, const char* suffix);

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

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

@ -3,19 +3,45 @@
* \section intro_sec Introduction
*
* 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
* on top of TCP/IP as well as GOOSE for real-time data transfer inside of substations.
* 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 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
* share common elements. For both client and server there exist two different APIs. There is a
* "low-level" MMS API and the more high-level IEC 61850 API.
* The library implements the most important parts of IEC 61850 on top of the MMS mapping. It provides the MMS protocol stack
* on top of TCP/IP as well as GOOSE and sampled measured values (SMV) for real-time data transfer inside of substations.
*
* \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
*
@ -49,13 +75,31 @@
* \section hal Hardware/OS abstraction layer
*
* 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
* by this API. At the moment implementations for POSIX(Linux, Mac OS X ...) and Win32 exists. This API
* consists of the three files <code>hal/hal.h</code>, <code>hal/socket/socket.h</code> and
* <code>hal/thread/thread.h</code>.
* by this API. At the moment implementations for Linux, BSD, Windows and MacOS exists. This API
* consists of header files starting with <code>hal_</code> in the <code>hal/inc</code> folder.
*
* \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
* source distribution. It is assumed that a GCC toolchain and the Make tool is installed.

@ -1,6 +1,6 @@
# 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.

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

@ -1,7 +1,7 @@
/*
* goose_receiver.c
*
* Copyright 2014-2017 Michael Zillgith
* Copyright 2014-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -151,6 +151,12 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
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);
if (bufPos < 0) {
pe = GOOSE_PARSE_ERROR_TAGDECODE;
@ -267,10 +273,26 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0x89: /* 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;
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 {
pe = GOOSE_PARSE_ERROR_TYPE_MISMATCH;
@ -1008,6 +1030,7 @@ GooseReceiver_stop(GooseReceiver self)
void
GooseReceiver_destroy(GooseReceiver self)
{
if (self) {
#if (CONFIG_MMS_THREADLESS_STACK == 0)
if ((self->thread != NULL) && (GooseReceiver_isRunning(self)))
GooseReceiver_stop(self);
@ -1022,6 +1045,7 @@ GooseReceiver_destroy(GooseReceiver self)
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self);
}
}
/***************************************
* Functions for non-threaded operation
@ -1036,6 +1060,26 @@ GooseReceiver_startThreadless(GooseReceiver self)
if (self->ethSocket != NULL) {
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;
}
else

@ -1,7 +1,7 @@
/*
* goose_subscriber.c
*
* Copyright 2013, 2014 Michael Zillgith
* Copyright 2013-2022 Michael Zillgith
*
* 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));
if (self) {
strncpy(self->goCBRef, goCbRef, 129);
self->goCBRef[129] = 0;
@ -58,6 +59,7 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues)
self->isObserver = false;
self->vlanSet = false;
self->parseError = GOOSE_PARSE_ERROR_NO_ERROR;
}
return self;
}
@ -96,6 +98,7 @@ GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId)
void
GooseSubscriber_destroy(GooseSubscriber self)
{
if (self) {
MmsValue_delete(self->timestamp);
if (self->dataSetValuesSelfAllocated)
@ -103,6 +106,7 @@ GooseSubscriber_destroy(GooseSubscriber self)
GLOBAL_FREEMEM(self);
}
}
void
GooseSubscriber_setListener(GooseSubscriber self, GooseListener listener, void* parameter)

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

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

@ -624,7 +624,7 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const
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);
@ -758,7 +758,7 @@ writeMultipleVariablesHandler(uint32_t invokeId, void* parameter, MmsError mmsEr
}
else {
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;
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) {
handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError));
@ -1006,7 +1006,7 @@ IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, Clien
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);
@ -1035,7 +1035,7 @@ IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, Clien
char* variableId = (char*) LinkedList_getData(param->currentItemId);
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;
@ -1094,7 +1094,17 @@ IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientRepo
LinkedList itemIds = 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 (isBuffered)
goto error_invalid_parameter;
@ -1199,12 +1209,14 @@ IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientRepo
LinkedList_add(values, rcb->timeOfEntry);
}
if (ClientReportControlBlock_getRptEna(rcb) == true) {
if (parametersMask & RCB_ELEMENT_RPT_ENA) {
strcpy(itemId + itemIdLen, "$RptEna");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->rptEna);
}
}
if (sendGILast) {
strcpy(itemId + itemIdLen, "$GI");

@ -1,7 +1,7 @@
/*
* ied_connection.c
*
* Copyright 2013-2019 Michael Zillgith
* Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -672,6 +672,25 @@ IedConnection_getRequestTimeout(IedConnection self)
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
IedConnection_getState(IedConnection self)
{
@ -942,7 +961,7 @@ IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError*
call->callback = handler;
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;
@ -991,7 +1010,7 @@ IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error,
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) {
*error = iedConnection_mapMmsErrorToIedError(err);
@ -1007,9 +1026,6 @@ IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error,
return call->invokeId;
}
uint32_t
IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result,
IedConnection_GetNameListHandler handler, void* parameter)
@ -1026,7 +1042,7 @@ IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError*
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) {
*error = iedConnection_mapMmsErrorToIedError(err);
@ -1058,7 +1074,7 @@ IedConnection_getLogicalDeviceDataSetsAsync(IedConnection self, IedClientError*
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) {
*error = iedConnection_mapMmsErrorToIedError(err);
@ -1146,7 +1162,7 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c
*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
*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;
}
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)) {
@ -1559,7 +1575,7 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const
*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);
*error = iedConnection_mapMmsErrorToIedError(err);
@ -1571,7 +1587,7 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
}
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);
}
@ -1660,7 +1676,6 @@ IedConnection_writeOctetString(IedConnection self, IedClientError* error, const
mmsValue.type = MMS_OCTET_STRING;
mmsValue.value.octetString.buf = value;
mmsValue.value.octetString.size = valueLength;
mmsValue.value.octetString.size = valueLength;
IedConnection_writeObject(self, error, objectReference, fc, &mmsValue);
}
@ -1885,7 +1900,7 @@ IedConnection_getFileDirectoryAsyncEx(IedConnection self, IedClientError* error,
call->callbackParameter = parameter;
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);
*error = iedConnection_mapMmsErrorToIedError(err);
@ -2009,7 +2024,7 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
if (mmsError != MMS_ERROR_SERVICE_TIMEOUT) {
/* 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)
iedConnection_releaseOutstandingCall(self, call);
@ -2026,7 +2041,7 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
if ((moreFollows == false) || (cont == false)) {
/* 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)
iedConnection_releaseOutstandingCall(self, call);
@ -2034,7 +2049,7 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
else {
/* send next read request */
call->invokeId = MmsConnection_fileReadAsync(self->connection, &mmsError, frsmId,
MmsConnection_fileReadAsync(self->connection, &(call->invokeId), &mmsError, frsmId,
mmsConnectionFileReadHandler, self);
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);
/* 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) {
iedConnection_releaseOutstandingCall(self, call);
@ -2085,7 +2100,7 @@ mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsEr
}
else {
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) {
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);
/* 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)
iedConnection_releaseOutstandingCall(self, call);
@ -2124,7 +2139,7 @@ IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char
call->callback = handler;
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);
@ -2199,7 +2214,7 @@ IedConnection_setFileAsync(IedConnection self, IedClientError* error, const char
call->callback = handler;
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);
@ -2239,7 +2254,7 @@ IedConnection_deleteFileAsync(IedConnection self, IedClientError* error, const c
call->callback = handler;
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);
@ -2993,7 +3008,6 @@ void
IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference,
LinkedList /* <char*> */dataSetElements)
{
char domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
@ -3137,6 +3151,250 @@ exit_function:
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*> */
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();
while (entry != NULL) {
MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*) entry->data;
while (entry) {
MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(entry);
char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec);
@ -3225,6 +3483,120 @@ exit_function:
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
IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
ClientDataSet dataSet)
@ -3410,10 +3782,10 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error,
MmsError err = MMS_ERROR_NONE;
if (isAssociationSpecific)
call->invokeId = MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(self->connection,
MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(self->connection, &(call->invokeId),
&err, itemId, true, getDataSetHandlerInternal, self);
else
call->invokeId = MmsConnection_readNamedVariableListValuesAsync(self->connection, &err,
MmsConnection_readNamedVariableListValuesAsync(self->connection, &(call->invokeId), &err,
domainId, itemId, true, getDataSetHandlerInternal, self);
*error = iedConnection_mapMmsErrorToIedError(err);
@ -3569,7 +3941,7 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error,
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);
@ -3591,6 +3963,7 @@ IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const ch
char logRef[130];
strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef;
char* logName = strchr(logRef, '/');
@ -3654,6 +4027,7 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con
char logRef[130];
strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef;
char* logName = strchr(logRef, '/');
@ -3681,7 +4055,7 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con
MmsValue* endTimeMms = MmsValue_newBinaryTime(false);
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);
MmsValue_delete(startTimeMms);
@ -3710,6 +4084,7 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons
char logRef[130];
strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef;
char* logName = strchr(logRef, '/');
@ -3734,7 +4109,7 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons
MmsValue* timeStampMms = MmsValue_newBinaryTime(false);
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);
MmsValue_delete(timeStampMms);
@ -3763,6 +4138,7 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha
char logRef[130];
strncpy(logRef, logReference, 129);
logRef[129] = 0;
char* logDomain = logRef;
char* logName = strchr(logRef, '/');
@ -3793,8 +4169,6 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha
}
}
MmsConnection
IedConnection_getMmsConnection(IedConnection self)
{

@ -1,7 +1,7 @@
/*
* iec61850_common.c
*
* Copyright 2013-2020 Michael Zillgith
* Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -70,6 +70,20 @@ Quality_fromMmsValue(const MmsValue* 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_fromMmsValue(const MmsValue* mmsValue)
{
@ -242,7 +256,7 @@ Timestamp_create()
}
Timestamp*
Timestamp_createFromByteArray(uint8_t* byteArray)
Timestamp_createFromByteArray(const uint8_t* byteArray)
{
Timestamp* self = Timestamp_create();
@ -471,7 +485,7 @@ Timestamp_getTimeInNs(Timestamp* self)
}
void
Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue)
Timestamp_setByMmsUtcTime(Timestamp* self, const MmsValue* mmsValue)
{
if (MmsValue_getType(mmsValue) == MMS_UTC_TIME)
memcpy(self->val, mmsValue->value.utcTime, 8);
@ -491,6 +505,25 @@ Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue)
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*
MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer)
{

@ -1,7 +1,7 @@
/*
* iec61850_client.h
*
* Copyright 2013-2019 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -266,6 +266,18 @@ IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs);
LIB61850_API uint32_t
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)
*
@ -1759,6 +1771,31 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error,
LIB61850_API void
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
*
@ -1775,9 +1812,29 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha
LIB61850_API bool
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
* 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*> */
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
*
@ -2884,6 +2969,8 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil
* \param buffer the buffer that contains the received file data
* \param bytesRead the number of bytes read into the buffer
* \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
(*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
Quality_fromMmsValue(const MmsValue* mmsValue);
LIB61850_API MmsValue*
Quality_toMmsValue(Quality* self, MmsValue* mmsValue);
/** @} */
/**
@ -423,7 +426,7 @@ LIB61850_API Timestamp*
Timestamp_create(void);
LIB61850_API Timestamp*
Timestamp_createFromByteArray(uint8_t* byteArray);
Timestamp_createFromByteArray(const uint8_t* byteArray);
LIB61850_API void
Timestamp_destroy(Timestamp* self);
@ -504,7 +507,7 @@ LIB61850_API void
Timestamp_setTimeInNanoseconds(Timestamp* self, nsSinceEpoch nsTime);
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
@ -515,6 +518,17 @@ Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue);
LIB61850_API 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
*

@ -46,14 +46,14 @@ extern "C" {
/**
* \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*
IedModel_create(const char* name/*, MemoryAllocator allocator*/);
IedModel_create(const char* name);
/**
* \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.
*/
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);
/**
@ -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)
*/
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)
@ -243,7 +392,7 @@ ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clie
* \return the new LCB instance
*/
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);
/**
@ -288,7 +437,7 @@ SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numO
* \return the new GoCB instance
*/
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);
/**
@ -308,7 +457,7 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char*
* \return the new SvCB instance
*/
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);
LIB61850_API void

@ -272,7 +272,9 @@ struct sReportControlBlock {
type can be one of (0 - no reservation, 4 - IPv4 client, 6 - IPv6 client) */
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 {

@ -3,7 +3,7 @@
*
* IEC 61850 server API for libiec61850.
*
* Copyright 2013-2020 Michael Zillgith
* Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -96,6 +96,9 @@ struct sIedServerConfig
/** RCB has owner attribute (default: true) */
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
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
*
@ -911,9 +938,8 @@ IedServer_getBitStringAttributeValue(IedServer self, const DataAttribute* dataAt
LIB61850_API const char*
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)
* 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
*

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

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

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

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

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

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

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

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

@ -1,7 +1,7 @@
/*
* ied_server_config.c
*
* Copyright 2018-2020 Michael Zillgith
* Copyright 2018-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -55,8 +55,10 @@ IedServerConfig_create()
self->edition = IEC_61850_EDITION_2;
self->maxMmsConnections = 5;
self->enableEditSG = true;
self->enableResvTmsForSGCB = true;
self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
}
return self;
@ -65,9 +67,11 @@ IedServerConfig_create()
void
IedServerConfig_destroy(IedServerConfig self)
{
if (self) {
GLOBAL_FREEMEM(self->fileServiceBasepath);
GLOBAL_FREEMEM(self);
}
}
void
IedServerConfig_setEdition(IedServerConfig self, uint8_t edition)
@ -248,3 +252,15 @@ IedServerConfig_getMaxMmsConnections(IedServerConfig self)
{
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
*
* Copyright 2013-2021 Michael Zillgith
* Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -68,6 +68,9 @@ ControlObject_sendCommandTerminationPositive(ControlObject* self);
void
ControlObject_sendCommandTerminationNegative(ControlObject* self);
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping);
static MmsValue*
getOperParameterCtlNum(MmsValue* operParameters)
{
@ -413,7 +416,14 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
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
setState(ControlObject* self, int newState)
@ -482,7 +492,7 @@ updateSboTimeoutValue(ControlObject* self)
}
static void
selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection connection)
selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection connection, MmsMapping* mmsMapping)
{
if (DEBUG_IED_SERVER)
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);
setState(self, STATE_READY);
updateNextControlTimeout(mmsMapping, selectTime);
if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter,
@ -503,13 +515,16 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne
}
static void
unselectObject(ControlObject* self, SelectStateChangedReason reason)
unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* mmsMapping)
{
if (getState(self) != STATE_UNSELECTED) {
setState(self, STATE_UNSELECTED);
setStSeld(self, false);
/* trigger timeout check in next cycle to update the next timeout value */
mmsMapping->nextControlTimeout = 0;
if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter,
@ -523,7 +538,7 @@ unselectObject(ControlObject* self, SelectStateChangedReason reason)
}
static void
checkSelectTimeout(ControlObject* self, uint64_t currentTime)
checkSelectTimeout(ControlObject* self, uint64_t currentTime, MmsMapping* mmsMapping)
{
if ((self->ctlModel == 2) || (self->ctlModel == 4)) {
@ -531,10 +546,13 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime)
if (self->selectTimeout > 0) {
if (currentTime > (self->selectTime + self->selectTimeout)) {
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);
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
abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason)
abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason, MmsMapping* mmsMapping)
{
if ((self->ctlModel == 2) || (self->ctlModel == 4)) {
if (unconditional) {
unselectObject(self, reason);
unselectObject(self, reason, mmsMapping);
}
else {
if (isSboClassOperateOnce(self))
unselectObject(self, reason);
unselectObject(self, reason, mmsMapping);
else
setState(self, STATE_READY);
}
@ -716,7 +734,7 @@ executeStateMachine:
if (checkHandlerResult == CONTROL_ACCEPTED) {
LinkedList_add(values, controlObject->sbo);
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection);
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self);
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR);
@ -740,7 +758,7 @@ executeStateMachine:
else if (controlObject->ctlModel == 4) {
if (checkHandlerResult == CONTROL_ACCEPTED) {
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection);
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self);
if (controlObject->ctlNumSt)
MmsValue_update(controlObject->ctlNumSt, controlObject->ctlNum);
@ -783,11 +801,13 @@ executeStateMachine:
}
}
else {
updateNextControlTimeout(self, Hal_getTimeInMs() + 100);
}
}
break;
case STATE_WAIT_FOR_ACTIVATION_TIME:
case STATE_WAIT_FOR_EXECUTION:
{
@ -828,7 +848,7 @@ executeStateMachine:
resetAddCause(controlObject);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
exitControlTask(controlObject);
}
else if (dynamicCheckResult == CONTROL_RESULT_OK) {
@ -858,6 +878,9 @@ executeStateMachine:
goto executeStateMachine;
}
else {
updateNextControlTimeout(self, Hal_getTimeInMs() + 10);
}
}
break;
@ -879,7 +902,7 @@ executeStateMachine:
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
}
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED, self);
}
else {
@ -894,7 +917,7 @@ executeStateMachine:
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT);
#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);
@ -903,6 +926,9 @@ executeStateMachine:
resetAddCause(controlObject);
}
else {
updateNextControlTimeout(self, currentTimeInMs + 10);
}
}
break;
@ -1003,10 +1029,24 @@ ControlObject_initialize(ControlObject* self)
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");
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");
self->sboTimeout = MmsServer_getValueFromCache(mmsServer,
@ -1067,7 +1107,6 @@ ControlObject_initialize(ControlObject* self)
mxValType = da->type;
}
if (stValType == IEC61850_BOOLEAN && ctlValType == IEC61850_BOOLEAN)
{
self->cdc = CST_SPCTRK; /* SPC */
@ -1240,6 +1279,7 @@ ControlObject_handlePendingEvents(ControlObject* self)
void
ControlObject_destroy(ControlObject* self)
{
if (self) {
if (self->mmsValue)
MmsValue_delete(self->mmsValue);
@ -1271,6 +1311,7 @@ ControlObject_destroy(ControlObject* self)
GLOBAL_FREEMEM(self);
}
}
char*
ControlObject_getName(ControlObject* self)
@ -1315,10 +1356,10 @@ ControlObject_getMmsValue(ControlObject* self)
}
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection)
ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping)
{
if (self->mmsConnection == connection) {
abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED);
abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED, mmsMapping);
return true;
}
else
@ -1375,11 +1416,25 @@ ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataOb
void
Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
{
if (currentTimeInMs >= self->nextControlTimeout) {
/* invalidate nextControlTimeout */
self->nextControlTimeout = (uint64_t) 0xFFFFFFFFFFFFFFFFLLU;
LinkedList element = LinkedList_getNext(self->controlObjects);
while (element != NULL) {
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->operateTime <= currentTimeInMs) {
@ -1428,18 +1483,22 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
/* leave state Perform Test */
setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
resetAddCause(controlObject);
}
}
else {
updateNextControlTimeout(self, controlObject->operateTime);
}
} /* if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) */
else if (!((controlObject->state == STATE_UNSELECTED) || (controlObject->state == STATE_READY))) {
executeControlTask(self, controlObject, currentTimeInMs);
}
else if (controlObject->state == STATE_READY) {
checkSelectTimeout(controlObject, currentTimeInMs);
checkSelectTimeout(controlObject, currentTimeInMs, self);
}
}
ControlObject_handlePendingEvents(controlObject);
@ -1447,6 +1506,7 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
element = LinkedList_getNext(element);
}
}
}
ControlObject*
Control_lookupControlObject(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName)
@ -1768,7 +1828,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
value = &emptyString;
if (isDirectAccess == true) {
checkSelectTimeout(controlObject, currentTime);
checkSelectTimeout(controlObject, currentTime, self);
if (getState(controlObject) == STATE_UNSELECTED) {
CheckHandlerResult checkResult = CONTROL_ACCEPTED;
@ -1789,7 +1849,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
}
if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection);
selectObject(controlObject, currentTime, connection, self);
value = controlObject->sbo;
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
@ -1814,7 +1874,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
}
else {
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;
}
@ -1992,7 +2052,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
uint64_t currentTime = Hal_getTimeInMs();
checkSelectTimeout(controlObject, currentTime);
checkSelectTimeout(controlObject, currentTime, self);
if (state != STATE_UNSELECTED) {
@ -2049,7 +2109,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
}
if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection);
selectObject(controlObject, currentTime, connection, self);
indication = DATA_ACCESS_ERROR_SUCCESS;
@ -2066,6 +2126,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setState(controlObject, STATE_WAIT_FOR_SELECT);
updateNextControlTimeout(self, Hal_getTimeInMs() + 100);
indication = DATA_ACCESS_ERROR_NO_RESPONSE;
}
else {
@ -2115,7 +2177,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
ctlNum, origin, true);
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;
@ -2123,7 +2185,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
uint64_t currentTime = Hal_getTimeInMs();
checkSelectTimeout(controlObject, currentTime);
checkSelectTimeout(controlObject, currentTime, self);
int state = getState(controlObject);
@ -2170,7 +2232,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true);
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED);
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED, self);
goto free_and_return;
}
@ -2205,6 +2267,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setState(controlObject, STATE_WAIT_FOR_ACTIVATION_TIME);
updateNextControlTimeout(self, controlObject->operateTime);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: Oper - activate time activated control\n");
@ -2251,6 +2315,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setOpRcvd(controlObject, false);
initiateControlTask(controlObject);
updateNextControlTimeout(self, currentTime);
}
else {
indication = (MmsDataAccessError) checkResult;
@ -2258,7 +2324,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
/* leave state Perform Test */
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)) {
ControlObject_sendLastApplError(controlObject, connection, "Oper",
@ -2328,7 +2394,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (state != STATE_UNSELECTED) {
if (controlObject->mmsConnection == connection) {
indication = DATA_ACCESS_ERROR_SUCCESS;
unselectObject(controlObject, SELECT_STATE_REASON_CANCELED);
unselectObject(controlObject, SELECT_STATE_REASON_CANCELED, self);
goto free_and_return;
}
else {
@ -2345,7 +2411,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (controlObject->timeActivatedOperate) {
controlObject->timeActivatedOperate = false;
abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED, self);
indication = DATA_ACCESS_ERROR_SUCCESS;
@ -2356,14 +2422,17 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
free_and_return:
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
if (controlObject) {
if (serviceError == IEC61850_SERVICE_ERROR_NO_ERROR) {
if (indication != DATA_ACCESS_ERROR_NO_RESPONSE) {
updateGenericTrackingObjectValues(self, controlObject, serviceType,
private_IedServer_convertMmsDataAccessErrorToServiceError(indication));
}
}
else
else {
updateGenericTrackingObjectValues(self, controlObject, serviceType, serviceError);
}
}
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
resetAddCause(controlObject);

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

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

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

@ -1,7 +1,7 @@
/*
* mms_sv.c
*
* Copyright 2015 Michael Zillgith
* Copyright 2015-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -67,10 +67,12 @@ MmsSampledValueControlBlock_create()
void
MmsSampledValueControlBlock_destroy(MmsSampledValueControlBlock self)
{
if (self) {
MmsValue_delete(self->mmsValue);
GLOBAL_FREEMEM(self);
}
}
static MmsSampledValueControlBlock
lookupSVCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName)
@ -131,6 +133,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch
char variableId[130];
strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
char* separator = strchr(variableId, '$');
@ -214,6 +217,7 @@ LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* d
char variableId[130];
strncpy(variableId, variableIdOrig, 129);
variableId[129] = 0;
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;
}
/************************************************
* Common Data Classes - helper functions
***********************************************/
@ -674,7 +672,7 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot
addCommonOperateElements(oper, isTimeActivated, true);
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);

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

@ -1,7 +1,7 @@
/*
* dynamic_model.c
*
* Copyright 2014-2016 Michael Zillgith
* Copyright 2014-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -41,10 +41,12 @@ IedModel_setIedNameForDynamicModel(IedModel* self, const char* name)
}
IedModel*
IedModel_create(const char* name/*, MemoryAllocator allocator*/)
IedModel_create(const char* name)
{
IedModel* self = (IedModel*) GLOBAL_CALLOC(1, sizeof(IedModel));
if (self)
{
if (name)
self->name = StringUtils_copyString(name);
else
@ -63,6 +65,7 @@ IedModel_create(const char* name/*, MemoryAllocator allocator*/)
self->logs = NULL;
self->initializer = iedModel_emptyVariableInitializer;
}
return self;
}
@ -309,7 +312,7 @@ LogicalNode_addLogControlBlock(LogicalNode* self, LogControlBlock* lcb)
}
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)
{
LogControlBlock* self = (LogControlBlock*) GLOBAL_MALLOC(sizeof(LogControlBlock));
@ -347,7 +350,7 @@ LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb)
}
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)
{
ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock));
@ -381,7 +384,7 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bo
}
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 */
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)
static void
LogicalNode_addSettingGroupControlBlock(LogicalNode* self, SettingGroupControlBlock* sgcb)
@ -434,7 +455,7 @@ LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb)
}
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)
{
GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock));
@ -468,7 +489,7 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char*
}
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)
{
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];
strncpy(variableName, variable, 129);
variableName[129] = 0;
char* separator = strchr(variableName, '/');
@ -777,6 +799,9 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha
static void
ModelNode_destroy(ModelNode* modelNode)
{
if (modelNode) {
if (modelNode->name)
GLOBAL_FREEMEM(modelNode->name);
ModelNode* currentChild = modelNode->firstChild;
@ -800,10 +825,12 @@ ModelNode_destroy(ModelNode* modelNode)
GLOBAL_FREEMEM(modelNode);
}
}
void
IedModel_destroy(IedModel* model)
{
if (model) {
/* delete all model nodes and dynamically created strings */
/* delete all logical devices */
@ -811,10 +838,9 @@ IedModel_destroy(IedModel* model)
LogicalDevice* ld = model->firstChild;
while (ld != NULL) {
GLOBAL_FREEMEM (ld->name);
if (ld->ldName)
GLOBAL_FREEMEM (ld->ldName);
GLOBAL_FREEMEM (ld->name);
LogicalNode* ln = (LogicalNode*) ld->firstChild;
@ -963,13 +989,12 @@ IedModel_destroy(IedModel* model)
log = nextLog;
}
/* delete generic model parts */
if (model->name)
GLOBAL_FREEMEM(model->name);
GLOBAL_FREEMEM(model);
}
}

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

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

@ -181,16 +181,14 @@ void
BerEncoder_revertByteOrder(uint8_t* octets, const int size)
{
int i;
uint8_t temp;
for (i = 0; i < size / 2; i++) {
temp = octets[i];
uint8_t temp = octets[i];
octets[i] = octets[(size - 1) - i];
octets[(size - 1) - i] = temp;
}
}
int
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);
int requiredBytes = 0;
if (val == 0) {
buffer[encodedBytes++] = 0;
}
else {
int requiredBytes = 0;
int val2 = val;
while (val2 > 0) {
requiredBytes++;
val2 = val2 >> 7;

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

@ -80,7 +80,7 @@ typedef struct sMmsValue MmsValue;
* \return a newly created array instance
*/
LIB61850_API MmsValue*
MmsValue_createArray(MmsVariableSpecification* elementType, int size);
MmsValue_createArray(const MmsVariableSpecification* elementType, int size);
/**
* \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
*/
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.
@ -592,6 +607,20 @@ MmsValue_getOctetStringMaxSize(MmsValue* self);
LIB61850_API uint8_t*
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.
*
@ -839,7 +868,7 @@ MmsValue_newBinaryTime(bool timeOfDay);
* \return new MmsValue instance of type MMS_VISIBLE_STRING
*/
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
@ -850,7 +879,7 @@ MmsValue_newVisibleStringFromByteArray(uint8_t* byteArray, int size);
* \return new MmsValue instance of type MMS_STRING
*/
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.
@ -860,7 +889,7 @@ MmsValue_newMmsStringFromByteArray(uint8_t* byteArray, int size);
* \return new MmsValue instance of type MMS_STRING
*/
LIB61850_API MmsValue*
MmsValue_newMmsString(char* string);
MmsValue_newMmsString(const char* 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* readBuffer; /* buffer to store received TPKT packet */
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;
typedef enum {
@ -80,7 +84,8 @@ CotpConnection_setTpduSize(CotpConnection* self, int tpduSize /* in byte */);
LIB61850_INTERNAL void
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
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
* (handlerMode)
*/
LIB61850_INTERNAL void
LIB61850_INTERNAL bool
IsoConnection_sendMessage(IsoConnection self, ByteBuffer* message);
LIB61850_INTERNAL IsoServer

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

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

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

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

Loading…
Cancel
Save