v1.6_develop_rgoose_sntp
Michael Zillgith 4 years ago
commit 25394b11e1

@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ v1.5 ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ v1.5 ]
schedule:
- cron: '38 23 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

@ -2,8 +2,57 @@ Changes to version 1.5.0
------------------------
- added support for time with ns resolution
- IEC 61850 server: control models - allow delaying select response with check handler (new handler return value CONTROL_WAITING_FOR_SELECT)
- added support for server to listen on multiple ports
- IEC 61850 server: added support to listen on multiple IP addresses and ports (new function IedServer_addAccessPoint)
- added support for service tracking
- added tool support for transient data objects
- .NET API: added more functions to create and access server data model
- IED server - control model - send AddCause with operate- for DOes, SBOes control models
- IED server: integrated GOOSE publisher - lock data model during GOOSE retransmission to avoid corrupted GOOSE data
- added server example for dead band handling
- IED server: make presence of RCB.Owner configurable at runtime with function IedServerConfig_enableOwnerForRCB (B1502/S1634)
- IED server: make presence of BRCB.ResvTms configurable at runtime with function IedServerConfig_enableResvTmsForBRCB (F1558)
- restrict maximum recursion depth in BerDecoder_decodeLength when indefinite length encoding is used to avoid stack overflow when receiving malformed messages
- fixed oss-fuzz issues 31399, 31340, 31341, 31344, 31346
- IED server: fixed bug in log service - old-entry and old-entry-time not updated
- IED server: added new function IedServer_handleWriteAccessForComplexAttribute. Changed WriteAccessHandler behavior when ACCESS_POLICY_ALLOW.
- MMS server: add compile time configuration options to enable/disable fileDelete and fileRename services (fileRename is now disabled by default)
- MMS server: better data model lock handling for performance improvements
- Linux - Ethernet: replace IFF_PROMISC by IFF_ALLMULTI
- improvements in Python wrapper code
- IED server: control models - fixed bug that only one control is unselected when connection closes
- IED server: fixed bug - logs (journals) are added to all logical devices instead of just the parents
- IED Server: prevent integrated GOOSE publisher to crash when ethernet socket cannot be created
- IED server: make compatible with tissue 1178
- IED server: reporting - implemented behavior according to tissue 1432
- IED server: WriteAccessHandler can tell the stack not to update the value when returning DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE
- IED server: fixed problem that BL FC is not writable (#287)
- IEC 61850 client: fixed dead lock in IedConnection_getFileAsync when fileRead times out (#285)
- IED server: added ControlSelectStateChangedHandler callback for control mode
- Client: fixed - IedConnection_getRCBValues doesn't check type of server response (#283)
- GOOSE subscriber: changed maximum GoID size according to tissue 770 (129 bytes)
- IED server: send AddCause for invalid origin also in case of direct control models
- IED server: support for configuration of EditSG service and online visibility of SGCB.ResvTms at runtime
- IED server: changed types TrkOps and OptFlds to variable length bit strings
- MMS: changed handling of variable sized bit strings (now also accepts bit strings of larger size, ignoring the bits that exceed the specified size)
- IED server: add support for correct CBB handling (required for test case sAss4) and initiate error PDU
- IED server: add support for tissue 807 (owner attribute in RCB is only present when ReportSettings@owner attribute is true)
- IED server: implemented tissue 1453 also for writing to "RptId" (purgeBuf only executed when value changes)
- GOOSE subscriber: always copy GoID and DatSet from GOOSE message; always create new MmsValue instance for GOOSE data set when subscriber is observer
- IED server: added configuration file support for data set entries with array elements or array element components
- fixed problems in handling array elements and array element components
- fixed bug in MmsConnection_readMultipleVariables: send invaid messsage and memory access errors when too many items are passed to the function exhausting MMS payload size
- IEC 61850 server: fixed problem with test case sRp4 - RCB RptID attribute is not empty after writing empty string
- fixed program crash when normal mode parameers are missing in presentation layer (#252)
- IED Server/GOOSE: Don't send GOOSE message with new event while data model is locked
- GOOSE: added GOOSE observer feature (GooseSubscriber listening to all GOOSE messages) and GOOSE observer example
- COTP: fixed possible heap buffer overflow when handling message with invalid (zero) value in length field (#250)
- IEC 61850 server: fixed - cancel command for time activated control returns object-access-denied even in case of success
- IEC 61850 client: fixed bug - IedConnection_setRCBValuesAsync always return 0 instead of invoke-ID
- MMS: fixed problem in handling of indefinite length encoded BER elements
- IEC 61850 client: reporting - support data set entries with multiple reasons for inclusion
- Java tools: moved minTime, maxTime from GSEControl to GSE; updated GOOSE server example CID file
- IEC 61850 server: Added ControlAction_setError function - with this function the user application can control the error code used in LastApplError and CommandTermination messages
- IEC 61850 server: fixed problem with logging when log data set contains FCDO (#225)
Changes to version 1.4.2.1
--------------------------

@ -53,6 +53,13 @@ LIB_SOURCE_DIRS += hal/ethernet/bsd
LIB_SOURCE_DIRS += hal/filesystem/linux
LIB_SOURCE_DIRS += hal/time/unix
LIB_SOURCE_DIRS += hal/memory
else ifeq ($(HAL_IMPL), MACOS)
LIB_SOURCE_DIRS += hal/socket/bsd
LIB_SOURCE_DIRS += hal/thread/macos
LIB_SOURCE_DIRS += hal/ethernet/bsd
LIB_SOURCE_DIRS += hal/filesystem/linux
LIB_SOURCE_DIRS += hal/time/unix
LIB_SOURCE_DIRS += hal/memory
endif
LIB_INCLUDE_DIRS += config
LIB_INCLUDE_DIRS += hal/inc
@ -105,6 +112,7 @@ LIB_API_HEADER_FILES += src/mms/inc/mms_common.h
LIB_API_HEADER_FILES += src/mms/inc/mms_types.h
LIB_API_HEADER_FILES += src/mms/inc/mms_type_spec.h
LIB_API_HEADER_FILES += src/mms/inc/mms_client_connection.h
LIB_API_HEADER_FILES += src/mms/inc/mms_server.h
LIB_API_HEADER_FILES += src/mms/inc/iso_connection_parameters.h
LIB_API_HEADER_FILES += src/goose/goose_subscriber.h
LIB_API_HEADER_FILES += src/goose/goose_receiver.h

@ -146,6 +146,16 @@ Depending on the system you don't have to provide a generator to the cmake comma
To select some configuration options you can use ccmake or cmake-gui.
For newer version of Visual Studio you can use one of the following commands (for 64 bit builds):
For Visual Studio 2017:
cmake -G "Visual Studio 15 2017 Win64" ..
For Visual Studio 2019 (new way to specify the x64 platform):
cmake -G "Visual Studio 16 2019" .. -A x64
## Using the log service with sqlite

@ -215,6 +215,8 @@
#define MMS_IDENTIFY_SERVICE 1
#define MMS_FILE_SERVICE 1
#define MMS_OBTAIN_FILE_SERVICE 1 /* requires MMS_FILE_SERVICE */
#define MMS_DELETE_FILE_SERVICE 1 /* requires MMS_FILE_SERVICE */
#define MMS_RENAME_FILE_SERVICE 0 /* requires MMS_FILE_SERVICE */
#endif /* MMS_DEFAULT_PROFILE */

@ -205,6 +205,8 @@
#define MMS_IDENTIFY_SERVICE 1
#define MMS_FILE_SERVICE 1
#define MMS_OBTAIN_FILE_SERVICE 1
#define MMS_DELETE_FILE_SERVICE 1
#define MMS_RENAME_FILE_SERVICE 0
#endif /* MMS_DEFAULT_PROFILE */
/* Sort getNameList response according to the MMS specified collation order - this is required by the standard

@ -34,6 +34,11 @@ namespace IEC61850
/// This class is used to represent a data set. It is used to store the values
/// of a data set.
/// </summary>
/// <remarks>
/// This class manages native resource. Take care that the finalizer/Dispose is not
/// called while running a method or the object is still in use by another object.
/// If in doubt please use the "using" statement.
/// </remarks>
public class DataSet : IDisposable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
@ -108,21 +113,34 @@ namespace IEC61850
return ClientDataSet_getDataSetSize(nativeObject);
}
public void Dispose()
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
lock (this)
{
if (!disposed)
{
if (nativeObject != IntPtr.Zero)
{
ClientDataSet_destroy(nativeObject);
nativeObject = IntPtr.Zero;
}
disposed = true;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~DataSet()
{
Dispose();
Dispose(false);
}
internal IntPtr getNativeInstance()

@ -31,7 +31,7 @@ namespace IEC61850
namespace Client
{
public class GooseControlBlock {
public class GooseControlBlock : IDisposable {
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientGooseControlBlock_create (string dataAttributeReference);
@ -84,6 +84,18 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern PhyComAddress ClientGooseControlBlock_getDstAddress (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientGooseControlBlock_getDstAddress_addr(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern byte ClientGooseControlBlock_getDstAddress_priority(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 ClientGooseControlBlock_getDstAddress_vid(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 ClientGooseControlBlock_getDstAddress_appid(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientGooseControlBlock_setDstAddress (IntPtr self, PhyComAddress value);
@ -232,7 +244,23 @@ namespace IEC61850
public PhyComAddress GetDstAddress()
{
return ClientGooseControlBlock_getDstAddress (self);
PhyComAddress addr = new PhyComAddress();
IntPtr value = ClientGooseControlBlock_getDstAddress_addr(self);
MmsValue mmsValue = new MmsValue(value);
byte[] dstMacAddr = mmsValue.getOctetString();
dstMacAddr.CopyTo(addr.dstAddress, 0);
addr.dstAddress = dstMacAddr;
addr.appId = ClientGooseControlBlock_getDstAddress_appid(self);
addr.vlanId = ClientGooseControlBlock_getDstAddress_vid(self);
addr.vlanPriority = ClientGooseControlBlock_getDstAddress_priority(self);
return addr;
}
public void SetDstAddress(PhyComAddress value)

@ -22,6 +22,7 @@
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using IEC61850.Common;
@ -69,6 +70,8 @@ namespace IEC61850
private bool isDisposed = false;
private List<GooseSubscriber> subscribers = new List<GooseSubscriber>();
public GooseReceiver()
{
self = GooseReceiver_create ();
@ -79,14 +82,29 @@ namespace IEC61850
GooseReceiver_setInterfaceId (self, interfaceId);
}
/// <summary>
/// Add the subscriber to be handled by this receiver instance
/// </summary>
/// <remarks>A GooseSubscriber can only be added to one GooseReceiver!</remarks>
/// <param name="subscriber"></param>
public void AddSubscriber(GooseSubscriber subscriber)
{
GooseReceiver_addSubscriber (self, subscriber.self);
if (subscriber.attachedToReceiver == false)
{
subscriber.attachedToReceiver = true;
GooseReceiver_addSubscriber(self, subscriber.self);
subscribers.Add(subscriber);
}
}
public void RemoveSubscriber(GooseSubscriber subscriber)
{
GooseReceiver_removeSubscriber (self, subscriber.self);
if (subscriber.attachedToReceiver)
{
GooseReceiver_removeSubscriber(self, subscriber.self);
subscribers.Remove(subscriber);
subscriber.attachedToReceiver = false;
}
}
public void Start()
@ -175,10 +193,22 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void GooseSubscriber_setListener (IntPtr self, InternalGooseListener listener, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GooseSubscriber_getGoId(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GooseSubscriber_getGoCbRef(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GooseSubscriber_getDataSet(IntPtr self);
internal IntPtr self;
private bool isDisposed = false;
// don't call native destructor when attached to a receiver
internal bool attachedToReceiver = false;
private GooseListener listener = null;
private object listenerParameter = null;
@ -214,7 +244,6 @@ namespace IEC61850
return GooseSubscriber_isValid (self);
}
public void SetListener(GooseListener listener, object parameter)
{
this.listener = listener;
@ -227,6 +256,21 @@ namespace IEC61850
}
}
public string GetGoId()
{
return Marshal.PtrToStringAnsi(GooseSubscriber_getGoId(self));
}
public string GetGoCbRef()
{
return Marshal.PtrToStringAnsi(GooseSubscriber_getGoCbRef(self));
}
public string GetDataSet()
{
return Marshal.PtrToStringAnsi(GooseSubscriber_getDataSet(self));
}
public UInt32 GetStNum()
{
return GooseSubscriber_getStNum (self);
@ -300,11 +344,19 @@ namespace IEC61850
{
if (isDisposed == false) {
isDisposed = true;
if (attachedToReceiver == false)
GooseSubscriber_destroy (self);
self = IntPtr.Zero;
}
}
~GooseSubscriber()
{
Dispose();
}
}
}

@ -548,7 +548,7 @@ namespace IEC61850
/// <summary>
/// Called when there is a change in the connection state
/// </summary>
public delegate void StateChangedHandler(IedConnection connection,IedConnectionState newState);
public delegate void StateChangedHandler(IedConnection connection, IedConnectionState newState);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_installStateChangedHandler(IntPtr connection, InternalStateChangedHandler handler, IntPtr parameter);
@ -886,7 +886,7 @@ namespace IEC61850
/// Creates a new SampledValuesControlBlock instance.
/// </summary>
/// <returns>The new GoCB instance</returns>
/// <param name="gocbObjectReference">The object reference of the GoCB</param>
/// <param name="gocbObjectReference">The object reference of the GoCB (e.g. "simpleIOGenericIO/LLN0.gcbAnalogValues")</param>
public GooseControlBlock GetGooseControlBlock(string gocbObjectReference)
{
return new GooseControlBlock(gocbObjectReference, connection);
@ -1788,7 +1788,7 @@ namespace IEC61850
/// by a prior function call.</description>
/// <param name="handler">The user provided callback handler</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
[Obsolete("ConnectionClosedHandler is deprecated, please use ConnectionEventHandler instead")]
[Obsolete("ConnectionClosedHandler is deprecated, please use StateChangedHandler instead")]
public void InstallConnectionClosedHandler(ConnectionClosedHandler handler)
{
if (connectionClosedHandler == null)

@ -123,7 +123,9 @@ namespace IEC61850
/** periodic transmission of all data set values */
INTEGRITY = 8,
/** general interrogation (on client request) */
GI = 16
GI = 16,
/** Report will be triggered only on rising edge (transient variable) */
TRG_OPT_TRANSIENT = 128
}
/// <summary>

File diff suppressed because it is too large Load Diff

@ -107,11 +107,28 @@ namespace IEC61850.Server
[return: MarshalAs(UnmanagedType.I1)]
static extern bool IedServerConfig_isLogServiceEnabled(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_enableResvTmsForBRCB(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool IedServerConfig_isResvTmsForBRCBEnabled(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_enableOwnerForRCB(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool IedServerConfig_isOwnerForRCBEnabled(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_useIntegratedGoosePublisher(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
internal IntPtr self;
public IedServerConfig ()
public IedServerConfig()
{
self = IedServerConfig_create ();
self = IedServerConfig_create();
}
/// <summary>
@ -120,11 +137,13 @@ namespace IEC61850.Server
/// <value>The size of the report buffer.</value>
public int ReportBufferSize
{
get {
return IedServerConfig_getReportBufferSize (self);
get
{
return IedServerConfig_getReportBufferSize(self);
}
set {
IedServerConfig_setReportBufferSize (self, value);
set
{
IedServerConfig_setReportBufferSize(self, value);
}
}
@ -150,11 +169,13 @@ namespace IEC61850.Server
/// <value>The file service base path.</value>
public string FileServiceBasePath
{
get {
return Marshal.PtrToStringAnsi (IedServerConfig_getFileServiceBasePath (self));
get
{
return Marshal.PtrToStringAnsi(IedServerConfig_getFileServiceBasePath(self));
}
set {
IedServerConfig_setFileServiceBasePath (self, value);
set
{
IedServerConfig_setFileServiceBasePath(self, value);
}
}
@ -180,11 +201,13 @@ namespace IEC61850.Server
/// <value>The IEC 61850 edition to use.</value>
public Iec61850Edition Edition
{
get {
return (Iec61850Edition)IedServerConfig_getEdition (self);
get
{
return (Iec61850Edition)IedServerConfig_getEdition(self);
}
set {
IedServerConfig_setEdition (self, (byte) value);
set
{
IedServerConfig_setEdition(self, (byte)value);
}
}
@ -194,11 +217,13 @@ namespace IEC61850.Server
/// <value>The max number of MMS client connections.</value>
public int MaxMmsConnections
{
get {
return IedServerConfig_getMaxMmsConnections (self);
get
{
return IedServerConfig_getMaxMmsConnections(self);
}
set {
IedServerConfig_setMaxMmsConnections (self, value);
set
{
IedServerConfig_setMaxMmsConnections(self, value);
}
}
@ -208,11 +233,13 @@ namespace IEC61850.Server
/// <value><c>true</c> if dynamic data set service enabled; otherwise, <c>false</c>.</value>
public bool DynamicDataSetServiceEnabled
{
get {
return IedServerConfig_isDynamicDataSetServiceEnabled (self);
get
{
return IedServerConfig_isDynamicDataSetServiceEnabled(self);
}
set {
IedServerConfig_enableDynamicDataSetService (self, value);
set
{
IedServerConfig_enableDynamicDataSetService(self, value);
}
}
@ -222,11 +249,13 @@ namespace IEC61850.Server
/// <value>The max. number data set entries.</value>
public int MaxDataSetEntries
{
get {
return IedServerConfig_getMaxDatasSetEntries (self);
get
{
return IedServerConfig_getMaxDatasSetEntries(self);
}
set {
IedServerConfig_setMaxDataSetEntries (self, value);
set
{
IedServerConfig_setMaxDataSetEntries(self, value);
}
}
@ -236,11 +265,13 @@ namespace IEC61850.Server
/// <value>The max. number of association specific data sets.</value>
public int MaxAssociationSpecificDataSets
{
get {
return IedServerConfig_getMaxAssociationSpecificDataSets (self);
get
{
return IedServerConfig_getMaxAssociationSpecificDataSets(self);
}
set {
IedServerConfig_setMaxAssociationSpecificDataSets (self, value);
set
{
IedServerConfig_setMaxAssociationSpecificDataSets(self, value);
}
}
@ -250,11 +281,13 @@ namespace IEC61850.Server
/// <value>The max. numebr of domain specific data sets.</value>
public int MaxDomainSpecificDataSets
{
get {
return IedServerConfig_getMaxDomainSpecificDataSets (self);
get
{
return IedServerConfig_getMaxDomainSpecificDataSets(self);
}
set {
IedServerConfig_setMaxDomainSpecificDataSets (self, value);
set
{
IedServerConfig_setMaxDomainSpecificDataSets(self, value);
}
}
@ -274,6 +307,50 @@ namespace IEC61850.Server
}
}
/// <summary>
/// Enable/Disable the presence of ResvTms attribute in BRCBs (buffered report control blocks)
/// </summary>
/// <value><c>true</c> if BRCB has ResvTms; otherwise, <c>false</c>. Defaults to true</value>
public bool BRCBHasResvTms
{
get
{
return IedServerConfig_isResvTmsForBRCBEnabled(self);
}
set
{
IedServerConfig_enableResvTmsForBRCB(self, value);
}
}
/// <summary>
/// Enable/Disable the presence of Owner attribute in RCBs (report control blocks)
/// </summary>
/// <value><c>true</c> if RCB has Owner; otherwise, <c>false</c>. Defaults to false</value>
public bool RCBHasOwner
{
get
{
return IedServerConfig_isOwnerForRCBEnabled(self);
}
set
{
IedServerConfig_enableOwnerForRCB(self, value);
}
}
/// <summary>
/// Enable/disable using the integrated GOOSE publisher for configured GoCBs
/// </summary>
/// <value><c>true</c> when integrated GOOSE publisher is used; otherwise, <c>false</c>. Defaults to true</value>
public bool UseIntegratedGoosePublisher
{
set
{
IedServerConfig_useIntegratedGoosePublisher(self, value);
}
}
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.Server.IedServerConfig"/> object.
/// </summary>
@ -284,9 +361,11 @@ namespace IEC61850.Server
/// <see cref="IEC61850.Server.IedServerConfig"/> was occupying.</remarks>
public void Dispose()
{
lock (this) {
if (self != IntPtr.Zero) {
IedServerConfig_destroy (self);
lock (this)
{
if (self != IntPtr.Zero)
{
IedServerConfig_destroy(self);
self = IntPtr.Zero;
}
}
@ -294,7 +373,7 @@ namespace IEC61850.Server
~IedServerConfig()
{
Dispose ();
Dispose();
}
}
}

@ -147,6 +147,12 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newVisibleString(string value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_newVisibleStringWithSize(int size);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsValue_setVisibleString(IntPtr self, string value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_createArray(IntPtr elementType, int size);
@ -201,7 +207,7 @@ namespace IEC61850
internal IntPtr valueReference; /* reference to native MmsValue instance */
private bool responsableForDeletion; /* if .NET wrapper is responsable for the deletion of the native MmsValue instance */
private bool responsableForDeletion = false; /* if .NET wrapper is responsable for the deletion of the native MmsValue instance */
internal MmsValue (IntPtr value)
{
@ -415,6 +421,27 @@ namespace IEC61850
return new MmsValue (newValue, true);
}
/// <summary>
/// Create a new MmsValue instance of type MMS_VISIBLE_STRING - empty string with given maximum size
/// </summary>
/// <param name="size">The maximum size </param>
/// <returns></returns>
public static MmsValue NewVisibleString(int size, bool responsibleForDeletion = false)
{
IntPtr newValue = MmsValue_newVisibleStringWithSize(size);
return new MmsValue(newValue, responsibleForDeletion);
}
/// <summary>
/// Set the value of an MmsValue instance of type MMS_VISIBLE_STRING
/// </summary>
/// <param name="value">the new string value</param>
public void SetVisibleString(string value)
{
MmsValue_setVisibleString(valueReference, value);
}
/// <summary>
/// Gets the type of the value
/// </summary>

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
VisualStudioVersion = 12.0.40629.0
# Visual Studio 15
VisualStudioVersion = 15.0.28307.779
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEC61850.NET", "IEC61850forCSharp\IEC61850.NET.csproj", "{C35D624E-5506-4560-8074-1728F1FA1A4D}"
EndProject
@ -48,94 +48,104 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_setting_grou
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_async", "client_example_async\client_example_async.csproj", "{71902641-776A-47D8-9C0E-9ACBBEAC1370}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server_goose_publisher", "server_goose_publisher\server_goose_publisher.csproj", "{C14BB883-86B8-401C-B3D6-B655F55F3298}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.Build.0 = Release|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Release|Any CPU.Build.0 = Release|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.Build.0 = Release|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Release|Any CPU.Build.0 = Release|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Release|Any CPU.Build.0 = Release|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Release|Any CPU.Build.0 = Release|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Release|Any CPU.Build.0 = Release|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Release|Any CPU.Build.0 = Release|Any CPU
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{59B85486-F48D-4978-BD35-8F5C3A8288D4}.Release|Any CPU.Build.0 = Release|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Release|Any CPU.Build.0 = Release|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Release|Any CPU.Build.0 = Release|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Release|Any CPU.Build.0 = Release|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A226B6D-1D1F-4BFE-B8CC-158116F71270}.Release|Any CPU.Build.0 = Release|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.Build.0 = Release|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Release|Any CPU.Build.0 = Release|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Release|Any CPU.Build.0 = Release|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Release|Any CPU.Build.0 = Release|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Release|Any CPU.Build.0 = Release|Any CPU
{71485F99-2976-45E6-B73D-4946E594C15C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71485F99-2976-45E6-B73D-4946E594C15C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71485F99-2976-45E6-B73D-4946E594C15C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71485F99-2976-45E6-B73D-4946E594C15C}.Release|Any CPU.Build.0 = Release|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Release|Any CPU.Build.0 = Release|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Release|Any CPU.Build.0 = Release|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Release|Any CPU.Build.0 = Release|Any CPU
{9286D2AB-96ED-4631-AB3C-ED20FF5D6E6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9286D2AB-96ED-4631-AB3C-ED20FF5D6E6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9286D2AB-96ED-4631-AB3C-ED20FF5D6E6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9286D2AB-96ED-4631-AB3C-ED20FF5D6E6C}.Release|Any CPU.Build.0 = Release|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Release|Any CPU.Build.0 = Release|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}.Release|Any CPU.Build.0 = Release|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.Build.0 = Release|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44651D2D-3252-4FD5-8B8B-5552DBE1B499}.Release|Any CPU.Build.0 = Release|Any CPU
{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Release|Any CPU.Build.0 = Release|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C351CFA4-E54E-49A1-86CE-69643535541A}.Release|Any CPU.Build.0 = Release|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C35D624E-5506-4560-8074-1728F1FA1A4D}.Release|Any CPU.Build.0 = Release|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C616A6DF-831E-443C-9310-3F343A6E3D1A}.Release|Any CPU.Build.0 = Release|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}.Release|Any CPU.Build.0 = Release|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}.Release|Any CPU.Build.0 = Release|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Release|Any CPU.Build.0 = Release|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71902641-776A-47D8-9C0E-9ACBBEAC1370}.Release|Any CPU.Build.0 = Release|Any CPU
{C14BB883-86B8-401C-B3D6-B655F55F3298}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C14BB883-86B8-401C-B3D6-B655F55F3298}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C14BB883-86B8-401C-B3D6-B655F55F3298}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C14BB883-86B8-401C-B3D6-B655F55F3298}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F590B86-C80C-4658-83BC-855A87751CEF}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
@ -149,7 +159,4 @@ Global
$2.inheritsScope = text/plain
StartupItem = IEC61850forCSharp\IEC61850forCSharp.csproj
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

@ -4,6 +4,9 @@ using IEC61850.GOOSE.Subscriber;
using System.Threading;
using IEC61850.Common;
/// <summary>
/// This example is intended to be
/// </summary>
namespace goose_subscriber
{
class MainClass
@ -11,9 +14,10 @@ namespace goose_subscriber
private static void gooseListener (GooseSubscriber subscriber, object parameter)
{
Console.WriteLine ("Received GOOSE message:\n-------------------------");
Console.WriteLine (" GoID: " + subscriber.GetGoId());
Console.WriteLine (" GoCbRef: " + subscriber.GetGoCbRef());
Console.WriteLine (" DatSet: " + subscriber.GetDataSet());
Console.WriteLine (" stNum: " + subscriber.GetStNum ());
Console.WriteLine (" sqNum: " + subscriber.GetSqNum ());
@ -33,17 +37,27 @@ namespace goose_subscriber
GooseReceiver receiver = new GooseReceiver ();
receiver.SetInterfaceId ("eth0");
//receiver.SetInterfaceId("0"); // on windows use the interface index starting with 0
GooseSubscriber subscriber = new GooseSubscriber ("simpleIOGenericIO/LLN0$GO$gcbAnalogValues");
subscriber.SetAppId(1000);
// APP-ID has to match the APP-ID of the publisher
subscriber.SetAppId(4096);
subscriber.SetListener (gooseListener, null);
receiver.AddSubscriber (subscriber);
GooseSubscriber subscriber2 = new GooseSubscriber("simpleIOGenericIO/LLN0$GO$gcbEvents");
subscriber2.SetAppId(4096);
subscriber2.SetListener(gooseListener, null);
receiver.AddSubscriber(subscriber2);
receiver.Start ();
subscriber = null;
if (receiver.IsRunning ()) {
bool running = true;

@ -7,31 +7,36 @@ namespace server1
{
class MainClass
{
public static void Main (string[] args)
public static void Main(string[] args)
{
bool running = true;
/* run until Ctrl-C is pressed */
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
{
e.Cancel = true;
running = false;
};
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("model.cfg");
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("model.cfg");
if (iedModel == null) {
Console.WriteLine ("No valid data model found!");
if (iedModel == null)
{
Console.WriteLine("No valid data model found!");
return;
}
DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.SPCSO1");
iedModel.SetIedName("TestIED");
DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO1");
IedServerConfig config = new IedServerConfig ();
IedServerConfig config = new IedServerConfig();
config.ReportBufferSize = 100000;
IedServer iedServer = new IedServer (iedModel, config);
IedServer iedServer = new IedServer(iedModel, config);
iedServer.SetCheckHandler(spcso1, delegate(ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck) {
iedServer.SetCheckHandler(spcso1, delegate (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck)
{
Console.WriteLine("Received binary control command:");
Console.WriteLine(" ctlNum: " + action.GetCtlNum());
@ -40,7 +45,8 @@ namespace server1
return CheckHandlerResult.ACCEPTED;
}, null);
iedServer.SetControlHandler (spcso1, delegate(ControlAction action, object parameter, MmsValue ctlVal, bool test) {
iedServer.SetControlHandler(spcso1, delegate (ControlAction action, object parameter, MmsValue ctlVal, bool test)
{
bool val = ctlVal.GetBoolean();
if (val)
@ -53,14 +59,15 @@ namespace server1
DataObject spcso2 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO2");
iedServer.SetSelectStateChangedHandler (spcso2, delegate (ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason) {
iedServer.SetSelectStateChangedHandler(spcso2, delegate (ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason)
{
DataObject cObj = action.GetControlObject();
Console.WriteLine("Control object " + cObj.GetObjectReference() + (isSelected ? " selected" : " unselected") + " reason: " + reason.ToString());
}, null);
iedServer.Start (102);
iedServer.Start(102);
if (iedServer.IsRunning())
{
@ -91,7 +98,7 @@ namespace server1
Console.WriteLine("Failed to start server");
}
iedServer.Destroy ();
iedServer.Destroy();
}
}
}

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("server_goose_publisher")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("server_goose_publisher")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c14bb883-86b8-401c-b3d6-b655f55f3298")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

@ -0,0 +1,129 @@
using System;
using IEC61850.Server;
using IEC61850.Common;
using System.Threading;
namespace server_goose_publisher
{
/// <summary>
/// This example shows how to use the server integrated GOOSE publisher from .NET
/// </summary>
/// <remarks>
/// This example requires that the native library (libiec61850) is compiled with GOOSE support.
/// </remarks>
class ServerExampleWithGoosePublisher
{
public static void Main(string[] args)
{
bool running = true;
/* run until Ctrl-C is pressed */
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
{
e.Cancel = true;
running = false;
};
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("simpleIO_direct_control_goose.cfg");
if (iedModel == null)
{
Console.WriteLine("No valid data model found!");
return;
}
iedModel.SetIedName("TestIED");
DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO1");
IedServerConfig config = new IedServerConfig();
config.ReportBufferSize = 100000;
IedServer iedServer = new IedServer(iedModel, config);
// Set GOOSE interface (e.g. "eth0" for Linux or "0" for Windows (first interface)
iedServer.SetGooseInterfaceId("0");
// The GoCBEventHandler can be used to track GoCB events
iedServer.SetGoCBHandler(delegate (MmsGooseControlBlock goCB, int cbEvent, object parameter)
{
if (cbEvent == 1)
{
Console.WriteLine("GCB " + goCB.LN.GetObjectReference() + ":" + goCB.Name + " enabled");
}
else
{
Console.WriteLine("GCB " + goCB.LN.GetObjectReference() + ":" + goCB.Name + " disabled");
}
}, null);
iedServer.SetCheckHandler(spcso1, delegate (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck)
{
Console.WriteLine("Received binary control command:");
Console.WriteLine(" ctlNum: " + action.GetCtlNum());
Console.WriteLine(" execution-time: " + action.GetControlTimeAsDataTimeOffset().ToString());
return CheckHandlerResult.ACCEPTED;
}, null);
iedServer.SetControlHandler(spcso1, delegate (ControlAction action, object parameter, MmsValue ctlVal, bool test)
{
bool val = ctlVal.GetBoolean();
if (val)
Console.WriteLine("execute binary control command: on");
else
Console.WriteLine("execute binary control command: off");
return ControlHandlerResult.OK;
}, null);
DataObject spcso2 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO2");
iedServer.SetSelectStateChangedHandler(spcso2, delegate (ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason)
{
DataObject cObj = action.GetControlObject();
Console.WriteLine("Control object " + cObj.GetObjectReference() + (isSelected ? " selected" : " unselected") + " reason: " + reason.ToString());
}, null);
iedServer.Start(102);
// Enable GOOSE publishing for all GOOSE control blocks
iedServer.EnableGoosePublishing();
if (iedServer.IsRunning())
{
Console.WriteLine("Server started");
GC.Collect();
DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.AnIn1");
DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild("mag.f");
DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild("t");
float floatVal = 1.0f;
while (running)
{
floatVal += 1f;
iedServer.UpdateTimestampAttributeValue(ggio1AnIn1T, new Timestamp(DateTime.Now));
iedServer.UpdateFloatAttributeValue(ggio1AnIn1magF, floatVal);
Thread.Sleep(100);
}
iedServer.Stop();
Console.WriteLine("Server stopped");
}
else
{
Console.WriteLine("Failed to start server");
}
iedServer.Destroy();
}
}
}

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C14BB883-86B8-401C-B3D6-B655F55F3298}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>server_goose_publisher</RootNamespace>
<AssemblyName>server_goose_publisher</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ServerExampleWithGoosePublisher.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="simpleIO_direct_control_goose.cfg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{c35d624e-5506-4560-8074-1728f1fa1a4d}</Project>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

@ -0,0 +1,217 @@
MODEL(simpleIO){
LD(GenericIO){
LN(LLN0){
DO(Mod 0){
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=0;
}
DO(Beh 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Health 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(NamPlt 0){
DA(vendor 0 20 5 0 0);
DA(swRev 0 20 5 0 0);
DA(d 0 20 5 0 0);
DA(configRev 0 20 5 0 0);
DA(ldNs 0 20 11 0 0);
}
DS(Events){
DE(GGIO1$ST$SPCSO1$stVal);
DE(GGIO1$ST$SPCSO2$stVal);
DE(GGIO1$ST$SPCSO3$stVal);
DE(GGIO1$ST$SPCSO4$stVal);
}
DS(Events2){
DE(GGIO1$ST$SPCSO1);
DE(GGIO1$ST$SPCSO2);
DE(GGIO1$ST$SPCSO3);
DE(GGIO1$ST$SPCSO4);
}
DS(Events3){
DE(GGIO1$ST$SPCSO1$stVal);
DE(GGIO1$ST$SPCSO1$q);
DE(GGIO1$ST$SPCSO2$stVal);
DE(GGIO1$ST$SPCSO2$q);
DE(GGIO1$ST$SPCSO3$stVal);
DE(GGIO1$ST$SPCSO3$q);
DE(GGIO1$ST$SPCSO4$stVal);
DE(GGIO1$ST$SPCSO4$q);
}
DS(AnalogValues){
DE(GGIO1$MX$AnIn1);
DE(GGIO1$MX$AnIn2);
DE(GGIO1$MX$AnIn3);
DE(GGIO1$MX$AnIn4);
}
RC(EventsRCB01 Events 0 Events 1 24 175 50 1000);
RC(AnalogValuesRCB01 AnalogValues 0 AnalogValues 1 24 175 50 1000);
GC(gcbEvents events Events3 2 0 1000 3000 ){
PA(4 1 4096 010ccd010001);
}
GC(gcbAnalogValues analog AnalogValues 2 0 -1 -1 ){
PA(4 1 4096 010ccd010001);
}
}
LN(LPHD1){
DO(PhyNam 0){
DA(vendor 0 20 5 0 0);
}
DO(PhyHealth 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Proxy 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
}
LN(GGIO1){
DO(Mod 0){
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=0;
}
DO(Beh 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Health 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(NamPlt 0){
DA(vendor 0 20 5 0 0);
DA(swRev 0 20 5 0 0);
DA(d 0 20 5 0 0);
}
DO(AnIn1 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(AnIn2 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(AnIn3 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(AnIn4 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(SPCSO1 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(ctlModel 0 12 4 0 0)=1;
DA(t 0 22 0 0 0);
}
DO(SPCSO2 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(ctlModel 0 12 4 0 0)=1;
DA(t 0 22 0 0 0);
}
DO(SPCSO3 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(ctlModel 0 12 4 0 0)=1;
DA(t 0 22 0 0 0);
}
DO(SPCSO4 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(ctlModel 0 12 4 0 0)=1;
DA(t 0 22 0 0 0);
}
DO(Ind1 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Ind2 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Ind3 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Ind4 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
}
}
}

@ -0,0 +1,275 @@
<?xml version="1.0" encoding="UTF-8"?>
<SCL xmlns="http://www.iec.ch/61850/2003/SCL">
<Header id="" nameStructure="IEDName">
</Header>
<Communication>
<SubNetwork name="subnetwork1" type="8-MMS">
<ConnectedAP iedName="simpleIO" apName="accessPoint1">
<Address>
<P type="IP">10.0.0.2</P>
<P type="IP-SUBNET">255.255.255.0</P>
<P type="IP-GATEWAY">10.0.0.1</P>
<P type="OSI-TSEL">0001</P>
<P type="OSI-PSEL">00000001</P>
<P type="OSI-SSEL">0001</P>
</Address>
<GSE ldInst="GenericIO" cbName="gcbEvents">
<Address>
<P type="VLAN-ID">1</P>
<P type="VLAN-PRIORITY">4</P>
<P type="MAC-Address">01-0c-cd-01-00-01</P>
<P type="APPID">1000</P>
</Address>
<MinTime>1000</MinTime>
<MaxTime>3000</MaxTime>
</GSE>
<GSE ldInst="GenericIO" cbName="gcbAnalogValues">
<Address>
<P type="VLAN-ID">1</P>
<P type="VLAN-PRIORITY">4</P>
<P type="MAC-Address">01-0c-cd-01-00-01</P>
<P type="APPID">1000</P>
</Address>
</GSE>
</ConnectedAP>
</SubNetwork>
</Communication>
<IED name="simpleIO">
<Services>
<DynAssociation />
<GetDirectory />
<GetDataObjectDefinition />
<GetDataSetValue />
<DataSetDirectory />
<ReadWrite />
<GetCBValues />
<ConfLNs fixPrefix="true" fixLnInst="true" />
<GOOSE max="5" />
<GSSE max="5" />
<FileHandling />
<GSEDir />
<TimerActivatedControl />
</Services>
<AccessPoint name="accessPoint1">
<Server>
<Authentication />
<LDevice inst="GenericIO">
<LN0 lnClass="LLN0" lnType="LLN01" inst="">
<DataSet name="Events" desc="Events">
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO1" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO2" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO3" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO4" daName="stVal" />
</DataSet>
<DataSet name="Events2" desc="Events2">
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO1" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO2" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO3" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO4" />
</DataSet>
<DataSet name="Events3" desc="Events3">
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO1" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO1" daName="q" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO2" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO2" daName="q" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO3" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO3" daName="q" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO4" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO4" daName="q" />
</DataSet>
<DataSet name="AnalogValues" desc="analog values">
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1" doName="AnIn1" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1" doName="AnIn2" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1" doName="AnIn3" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1" doName="AnIn4" />
</DataSet>
<ReportControl name="EventsRCB" confRev="1" datSet="Events" rptID="Events" buffered="false" intgPd="1000" bufTime="50">
<TrgOps period="true" />
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" configRef="true" />
<RptEnabled max="1" />
</ReportControl>
<ReportControl name="AnalogValuesRCB" confRev="1" datSet="AnalogValues" rptID="AnalogValues" buffered="false" intgPd="1000" bufTime="50">
<TrgOps period="true" />
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" configRef="true" />
<RptEnabled max="1" />
</ReportControl>
<GSEControl appID="events" name="gcbEvents" type="GOOSE" datSet="Events3" confRev="2" />
<GSEControl appID="analog" name="gcbAnalogValues" type="GOOSE" datSet="AnalogValues" confRev="2"/>
<DOI name="Mod">
<DAI name="ctlModel">
<Val>status-only</Val>
</DAI>
</DOI>
</LN0>
<LN lnClass="LPHD" lnType="LPHD1" inst="1" prefix="" />
<LN lnClass="GGIO" lnType="GGIO1" inst="1" prefix="">
<DOI name="Mod">
<DAI name="ctlModel">
<Val>status-only</Val>
</DAI>
</DOI>
<DOI name="SPCSO1">
<DAI name="ctlModel">
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO2">
<DAI name="ctlModel">
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO3">
<DAI name="ctlModel">
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO4">
<DAI name="ctlModel">
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
</LN>
</LDevice>
</Server>
</AccessPoint>
</IED>
<DataTypeTemplates>
<LNodeType id="LLN01" lnClass="LLN0">
<DO name="Mod" type="INC_1_Mod" />
<DO name="Beh" type="INS_1_Beh" />
<DO name="Health" type="INS_1_Beh" />
<DO name="NamPlt" type="LPL_1_NamPlt" />
</LNodeType>
<LNodeType id="LPHD1" lnClass="LPHD">
<DO name="PhyNam" type="DPL_1_PhyNam" />
<DO name="PhyHealth" type="INS_1_Beh" />
<DO name="Proxy" type="SPS_1_Proxy" />
</LNodeType>
<LNodeType id="GGIO1" lnClass="GGIO">
<DO name="Mod" type="INC_2_Mod" />
<DO name="Beh" type="INS_1_Beh" />
<DO name="Health" type="INS_1_Beh" />
<DO name="NamPlt" type="LPL_2_NamPlt" />
<DO name="AnIn1" type="MV_1_AnIn1" />
<DO name="AnIn2" type="MV_1_AnIn1" />
<DO name="AnIn3" type="MV_1_AnIn1" />
<DO name="AnIn4" type="MV_1_AnIn1" />
<DO name="SPCSO1" type="SPC_2_SPCSO1" />
<DO name="SPCSO2" type="SPC_1_SPCSO2" />
<DO name="SPCSO3" type="SPC_1_SPCSO3" />
<DO name="SPCSO4" type="SPC_1_SPCSO1" />
<DO name="Ind1" type="SPS_1_Proxy" />
<DO name="Ind2" type="SPS_1_Proxy" />
<DO name="Ind3" type="SPS_1_Proxy" />
<DO name="Ind4" type="SPS_1_Proxy" />
</LNodeType>
<DOType id="INC_1_Mod" cdc="INC">
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="INS_1_Beh" cdc="INS">
<DA name="stVal" bType="INT32" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="LPL_1_NamPlt" cdc="LPL">
<DA name="vendor" bType="VisString255" fc="DC" />
<DA name="swRev" bType="VisString255" fc="DC" />
<DA name="d" bType="VisString255" fc="DC" />
<DA name="configRev" bType="VisString255" fc="DC" />
<DA name="ldNs" bType="VisString255" fc="EX" />
</DOType>
<DOType id="DPL_1_PhyNam" cdc="DPL">
<DA name="vendor" bType="VisString255" fc="DC" />
</DOType>
<DOType id="SPS_1_Proxy" cdc="SPS">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="LPL_2_NamPlt" cdc="LPL">
<DA name="vendor" bType="VisString255" fc="DC" />
<DA name="swRev" bType="VisString255" fc="DC" />
<DA name="d" bType="VisString255" fc="DC" />
</DOType>
<DOType id="MV_1_AnIn1" cdc="MV">
<DA name="mag" type="AnalogueValue_1" bType="Struct" fc="MX" dchg="true" />
<DA name="q" bType="Quality" fc="MX" qchg="true" />
<DA name="t" bType="Timestamp" fc="MX" />
</DOType>
<DOType id="SPC_1_SPCSO1" cdc="SPC">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="INC_2_Mod" cdc="INC">
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="SPC_2_SPCSO1" cdc="SPC">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="SPC_1_SPCSO2" cdc="SPC">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="SPC_1_SPCSO3" cdc="SPC">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DAType id="AnalogueValue_1">
<BDA name="f" bType="FLOAT32" />
</DAType>
<DAType id="Originator_1">
<BDA name="orCat" type="OrCat" bType="Enum" />
<BDA name="orIdent" bType="Octet64" />
</DAType>
<DAType id="SPCOperate_1">
<BDA name="ctlVal" bType="BOOLEAN" />
<BDA name="origin" type="Originator_1" bType="Struct" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
<BDA name="Check" bType="Check" />
</DAType>
<EnumType id="CtlModels">
<EnumVal ord="0">status-only</EnumVal>
<EnumVal ord="1">direct-with-normal-security</EnumVal>
<EnumVal ord="2">sbo-with-normal-security</EnumVal>
<EnumVal ord="3">direct-with-enhanced-security</EnumVal>
<EnumVal ord="4">sbo-with-enhanced-security</EnumVal>
</EnumType>
<EnumType id="OrCat">
<EnumVal ord="0">not-supported</EnumVal>
<EnumVal ord="1">bay-control</EnumVal>
<EnumVal ord="2">station-control</EnumVal>
<EnumVal ord="3">remote-control</EnumVal>
<EnumVal ord="4">automatic-bay</EnumVal>
<EnumVal ord="5">automatic-station</EnumVal>
<EnumVal ord="6">automatic-remote</EnumVal>
<EnumVal ord="7">maintenance</EnumVal>
<EnumVal ord="8">process</EnumVal>
</EnumType>
</DataTypeTemplates>
</SCL>

@ -244,6 +244,8 @@ namespace tests
Assert.AreEqual (list.ToArray () [0], "simpleIOGenericIO");
Assert.IsTrue(iedServer.IsRunning());
iedServer.Stop ();
iedServer.Destroy ();
@ -289,6 +291,34 @@ namespace tests
Assert.IsNotNull (modelNode);
}
[Test()]
public void AccessDataModelServerSideNavigateModelNode()
{
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("../../model.cfg");
ModelNode modelNode = iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.AnIn1");
Assert.IsNotNull(modelNode);
Assert.IsTrue(modelNode.GetType().Equals(typeof(DataObject)));
var children = modelNode.GetChildren();
Assert.AreEqual(3, children.Count);
ModelNode mag = children.First.Value;
Assert.AreEqual("mag", mag.GetName());
ModelNode t = children.Last.Value;
Assert.AreEqual("t", t.GetName());
//modelNode = iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.AnIn1.mag.f");
//Assert.IsTrue(modelNode.GetType().Equals(typeof(IEC61850.Server.DataAttribute)));
}
[Test ()]
public void AccessDataModelClientServer()
{
@ -383,9 +413,61 @@ namespace tests
}
[Test()]
public void WriteAccessPolicy()
public void ControlWriteAccessComplexDAToServer()
{
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("../../model2.cfg");
IEC61850.Server.DataAttribute setAnVal_setMag = (IEC61850.Server.DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/LLN0.SetAnVal.setMag");
IedServer iedServer = new IedServer(iedModel);
int handlerCalled = 0;
MmsValue receivedValue = null;
iedServer.SetWriteAccessPolicy(FunctionalConstraint.SP, AccessPolicy.ACCESS_POLICY_DENY);
iedServer.HandleWriteAccessForComplexAttribute(setAnVal_setMag, delegate (IEC61850.Server.DataAttribute dataAttr, MmsValue value, ClientConnection con, object parameter) {
receivedValue = value;
handlerCalled++;
return MmsDataAccessError.SUCCESS;
}, null);
iedServer.Start(10002);
IedConnection connection = new IedConnection();
connection.Connect("localhost", 10002);
MmsValue complexValue = MmsValue.NewEmptyStructure(1);
complexValue.SetElement(0, new MmsValue((float)1.0));
connection.WriteValue("simpleIOGenericIO/LLN0.SetAnVal.setMag", FunctionalConstraint.SP, complexValue);
Assert.NotNull(receivedValue);
Assert.AreEqual(MmsType.MMS_STRUCTURE, receivedValue.GetType());
Assert.AreEqual(1.0, receivedValue.GetElement(0).ToFloat());
receivedValue.Dispose();
receivedValue = null;
connection.WriteValue("simpleIOGenericIO/LLN0.SetAnVal.setMag.f", FunctionalConstraint.SP, new MmsValue((float)2.0));
Assert.NotNull(receivedValue);
Assert.AreEqual(MmsType.MMS_FLOAT, receivedValue.GetType());
Assert.AreEqual(2.0, receivedValue.ToFloat());
connection.Abort();
iedServer.Stop();
iedServer.Dispose();
}
[Test()]
public void WriteAccessPolicy()
{
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("../../model.cfg");
IEC61850.Server.DataAttribute opDlTmms = (IEC61850.Server.DataAttribute) iedModel.GetModelNodeByShortObjectReference("GenericIO/PDUP1.OpDlTmms.setVal");
@ -404,6 +486,8 @@ namespace tests
connection.Connect ("localhost", 10002);
iedServer.SetWriteAccessPolicy(FunctionalConstraint.SP, AccessPolicy.ACCESS_POLICY_ALLOW);
connection.WriteValue ("simpleIOGenericIO/PDUP1.RsDlTmms.setVal", FunctionalConstraint.SP, new MmsValue ((int)1234));
iedServer.SetWriteAccessPolicy (FunctionalConstraint.SP, AccessPolicy.ACCESS_POLICY_DENY);
@ -425,11 +509,10 @@ namespace tests
iedServer.Stop ();
iedServer.Destroy ();
iedServer.Dispose();
}
[Test()]
[Ignore("has to be fixed")]
public void ControlHandler()
{
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("../../model.cfg");
@ -476,7 +559,7 @@ namespace tests
iedServer.Stop ();
iedServer.Destroy ();
iedServer.Dispose();
}
@ -525,6 +608,8 @@ namespace tests
Assert.AreEqual ("127.0.0.1:", ipAddress.Substring (0, 10));
iedServer.Stop ();
iedServer.Dispose();
}
[Test()]
@ -555,7 +640,7 @@ namespace tests
}
[Test()]
public void MmsValaueCreateStructureAndAddElement()
public void MmsValueCreateStructureAndAddElement()
{
MmsValue structure1 = MmsValue.NewEmptyStructure(1);
MmsValue structure2 = MmsValue.NewEmptyStructure(1);

@ -0,0 +1,446 @@
MODEL(simpleIO){
LD(GenericIO){
LN(LLN0){
DO(Mod 0){
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=0;
}
DO(Beh 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Health 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(NamPlt 0){
DA(vendor 0 20 5 0 0);
DA(swRev 0 20 5 0 0);
DA(d 0 20 5 0 0);
DA(configRev 0 20 5 0 0);
DA(ldNs 0 20 11 0 0);
}
DO(SetInt1 0){
DA(setVal 0 3 2 1 0);
}
DO(SetInt2 0){
DA(setVal 0 3 2 1 0);
}
DO(SetInt3 0){
DA(setVal 0 3 2 1 0);
}
DO(SetInt4 0){
DA(setVal 0 3 2 1 0);
}
DO(SetBool1 0){
DA(setVal 0 0 2 1 0);
}
DO(SetBool2 0){
DA(setVal 0 0 2 1 0);
}
DO(SetBool3 0){
DA(setVal 0 0 2 1 0);
}
DO(SetBool4 0){
DA(setVal 0 0 2 1 0);
}
DO(Int64 0){
DA(stVal 0 4 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(SetAnVal 0){
DA(setMag 0 27 2 1 0){
DA(f 0 10 2 1 0);
}
}
DS(Events){
DE(GGIO1$ST$SPCSO1$stVal);
DE(GGIO1$ST$SPCSO2$stVal);
DE(GGIO1$ST$SPCSO3$stVal);
DE(GGIO1$ST$SPCSO4$stVal);
}
DS(EventsFCDO){
DE(GGIO1$ST$SPCSO1);
DE(GGIO1$ST$SPCSO2);
DE(GGIO1$ST$SPCSO3);
DE(GGIO1$ST$SPCSO4);
}
DS(Booleans){
DE(LLN0$SP$SetBool1$setVal);
DE(LLN0$SP$SetBool2$setVal);
DE(LLN0$SP$SetBool3$setVal);
DE(LLN0$SP$SetBool4$setVal);
}
DS(Integers){
DE(LLN01$SP$SetInt1$setVal);
DE(LLN01$SP$SetInt2$setVal);
DE(LLN01$SP$SetInt3$setVal);
DE(LLN01$SP$SetInt4$setVal);
}
RC(EventsRCB01 Events1 0 Events 1 24 239 50 1000);
RC(EventsRCB02 Events1 0 Events 1 24 239 50 1000);
RC(BufferedRCB01 Events1 1 Events 1 24 239 50 1000);
RC(BufferedRCB02 Events1 1 Events 1 24 239 50 1000);
GC(gcbEvents events Events 1 0 1000 3000 ){
PA(4 1 4096 010ccd010001);
}
}
LN(LPHD1){
DO(PhyNam 0){
DA(vendor 0 20 5 0 0);
}
DO(PhyHealth 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Proxy 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
}
LN(GGIO1){
DO(Mod 0){
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=0;
}
DO(Beh 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Health 0){
DA(stVal 0 3 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(NamPlt 0){
DA(vendor 0 20 5 0 0);
DA(swRev 0 20 5 0 0);
DA(d 0 20 5 0 0);
DA(dU 0 21 5 0 0);
}
DO(AnIn1 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(AnIn2 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(AnIn3 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(AnIn4 0){
DA(mag 0 27 1 1 0){
DA(f 0 10 1 1 0);
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DO(SPCSO1 0){
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=1;
}
DO(SPCSO2 0){
DA(SBO 0 17 12 0 0);
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Cancel 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
}
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=2;
DA(sboClass 0 12 4 0 0);
}
DO(SPCSO3 0){
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Cancel 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
}
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=3;
}
DO(SPCSO4 0){
DA(SBOw 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Cancel 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
}
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=4;
}
DO(SPCSO5 0){
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=1;
DA(Cancel 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
}
}
DO(SPCSO6 0){
DA(SBO 0 17 12 0 0);
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Cancel 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
}
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=2;
}
DO(SPCSO7 0){
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Cancel 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
}
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=3;
}
DO(SPCSO8 0){
DA(SBOw 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Oper 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
DA(Check 0 24 12 0 0);
}
DA(Cancel 0 27 12 0 0){
DA(ctlVal 0 0 12 0 0);
DA(operTm 0 22 12 0 0);
DA(origin 0 27 12 0 0){
DA(orCat 0 12 12 0 0);
DA(orIdent 0 13 12 0 0);
}
DA(ctlNum 0 6 12 0 0);
DA(T 0 22 12 0 0);
DA(Test 0 0 12 0 0);
}
DA(origin 0 27 0 0 0){
DA(orCat 0 12 0 0 0);
DA(orIdent 0 13 0 0 0);
}
DA(ctlNum 0 6 0 0 0);
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
DA(ctlModel 0 12 4 0 0)=4;
}
DO(Ind1 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Ind2 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Ind3 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(Ind4 0){
DA(stVal 0 0 0 1 0);
DA(q 0 23 0 2 0);
DA(t 0 22 0 0 0);
}
DO(SchdAbsTm 0){
DA(val 24 10 2 1 0);
}
}
LN(MHAI1){
DO(HA 0){
DO(phsAHar 16){
DA(cVal 0 27 1 5 0){
DA(mag 0 27 1 5 0){
DA(f 0 10 1 5 0);
}
DA(ang 0 27 1 5 0){
DA(f 0 10 1 5 0);
}
}
DA(q 0 23 1 2 0);
DA(t 0 22 1 0 0);
}
DA(numHar 0 7 4 1 0)=16;
DA(numCyc 0 7 4 1 0);
DA(evalTm 0 7 4 1 0);
DA(frequency 0 10 4 1 0);
}
}
}
}

@ -0,0 +1,430 @@
<?xml version="1.0" encoding="UTF-8"?>
<SCL xmlns="http://www.iec.ch/61850/2003/SCL">
<Header id="" version="4.0.2" revision="" toolID="" nameStructure="IEDName">
</Header>
<Communication>
<SubNetwork name="subnetwork1" type="8-MMS">
<Text>Station bus</Text>
<BitRate unit="b/s">10</BitRate>
<ConnectedAP iedName="simpleIO" apName="accessPoint1">
<Address>
<P type="IP">10.0.0.2</P>
<P type="IP-SUBNET">255.255.255.0</P>
<P type="IP-GATEWAY">10.0.0.1</P>
<P type="OSI-TSEL">0001</P>
<P type="OSI-PSEL">00000001</P>
<P type="OSI-SSEL">0001</P>
</Address>
<GSE ldInst="GenericIO" cbName="gcbEvents">
<Address>
<P type="VLAN-ID">1</P>
<P type="VLAN-PRIORITY">4</P>
<P type="MAC-Address">01-0c-cd-01-00-01</P>
<P type="APPID">1000</P>
</Address>
<MinTime>1000</MinTime>
<MaxTime>3000</MaxTime>
</GSE>
</ConnectedAP>
</SubNetwork>
</Communication>
<IED name="simpleIO">
<Services>
<DynAssociation />
<GetDirectory />
<GetDataObjectDefinition />
<GetDataSetValue />
<DataSetDirectory />
<ReadWrite />
<GetCBValues />
<ConfLNs fixPrefix="true" fixLnInst="true" />
<GOOSE max="5" />
<GSSE max="5" />
<FileHandling />
<GSEDir />
<TimerActivatedControl />
</Services>
<AccessPoint name="accessPoint1">
<Server>
<Authentication />
<LDevice inst="GenericIO">
<LN0 lnClass="LLN0" lnType="LLN01" inst="">
<DataSet name="Events" desc="Events">
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO1" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO2" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO3" daName="stVal" />
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO4" daName="stVal" />
</DataSet>
<DataSet name="EventsFCDO" desc="EventsFCDO">
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO1"/>
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO2"/>
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO3"/>
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO4"/>
</DataSet>
<DataSet name="Booleans" desc="Booleans">
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" doName="SetBool1" daName="setVal" />
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" doName="SetBool2" daName="setVal" />
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" doName="SetBool3" daName="setVal" />
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" doName="SetBool4" daName="setVal" />
</DataSet>
<DataSet name="Integers" desc="Integers">
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" lnInst="1" doName="SetInt1" daName="setVal" />
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" lnInst="1" doName="SetInt2" daName="setVal" />
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" lnInst="1" doName="SetInt3" daName="setVal" />
<FCDA ldInst="GenericIO" lnClass="LLN0" fc="SP" lnInst="1" doName="SetInt4" daName="setVal" />
</DataSet>
<ReportControl name="EventsRCB" confRev="1" datSet="Events" rptID="Events1" buffered="false" intgPd="1000" bufTime="50" indexed="true">
<TrgOps period="true" />
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" entryID="true" configRef="true" />
<RptEnabled max="2" />
</ReportControl>
<ReportControl name="BufferedRCB" confRev="1" datSet="Events" rptID="Events1" buffered="true" intgPd="1000" bufTime="50" indexed="true">
<TrgOps period="true" />
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" entryID="true" configRef="true" />
<RptEnabled max="2" />
</ReportControl>
<GSEControl appID="events" name="gcbEvents" type="GOOSE" datSet="Events" confRev="1"/>
<DOI name="Mod">
<DAI name="ctlModel">
<Val>status-only</Val>
</DAI>
</DOI>
</LN0>
<LN lnClass="LPHD" lnType="LPHD1" inst="1" prefix="" />
<LN lnClass="GGIO" lnType="GGIO1" inst="1" prefix="">
<DOI name="Mod">
<DAI name="ctlModel">
<Val>status-only</Val>
</DAI>
</DOI>
<DOI name="SPCSO1">
<DAI name="ctlModel">
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO2">
<DAI name="ctlModel">
<Val>sbo-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO3">
<DAI name="ctlModel">
<Val>direct-with-enhanced-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO4">
<DAI name="ctlModel">
<Val>sbo-with-enhanced-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO5">
<DAI name="ctlModel">
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO6">
<DAI name="ctlModel">
<Val>sbo-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO7">
<DAI name="ctlModel">
<Val>direct-with-enhanced-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO8">
<DAI name="ctlModel">
<Val>sbo-with-enhanced-security</Val>
</DAI>
</DOI>
</LN>
<LN lnClass="MHAI" lnType="MHAI1" inst="1" prefix="">
<DOI name="HA">
<DAI name="numHar">
<Val>16</Val>
</DAI>
</DOI>
</LN>
</LDevice>
</Server>
</AccessPoint>
</IED>
<DataTypeTemplates>
<LNodeType id="LLN01" lnClass="LLN0">
<DO name="Mod" type="INC_1_Mod" />
<DO name="Beh" type="INS_1_Beh" />
<DO name="Health" type="INS_1_Beh" />
<DO name="NamPlt" type="LPL_1_NamPlt" />
<DO name="SetInt1" type="ISP" />
<DO name="SetInt2" type="ISP" />
<DO name="SetInt3" type="ISP" />
<DO name="SetInt4" type="ISP" />
<DO name="SetBool1" type="SPG_1" />
<DO name="SetBool2" type="SPG_1" />
<DO name="SetBool3" type="SPG_1" />
<DO name="SetBool4" type="SPG_1" />
<DO name="Int64" type="INS_64" />
<DO name="SetAnVal" type="ASG" />
</LNodeType>
<LNodeType id="LPHD1" lnClass="LPHD">
<DO name="PhyNam" type="DPL_1_PhyNam" />
<DO name="PhyHealth" type="INS_1_Beh" />
<DO name="Proxy" type="SPS_1_Proxy" />
</LNodeType>
<LNodeType id="GGIO1" lnClass="GGIO">
<DO name="Mod" type="INC_1_Mod" />
<DO name="Beh" type="INS_1_Beh" />
<DO name="Health" type="INS_1_Beh" />
<DO name="NamPlt" type="LPL_2_NamPlt" />
<DO name="AnIn1" type="MV_1_AnIn1" />
<DO name="AnIn2" type="MV_1_AnIn1" />
<DO name="AnIn3" type="MV_1_AnIn1" />
<DO name="AnIn4" type="MV_1_AnIn1" />
<DO name="SPCSO1" type="SPC_1_SPCSO1" />
<DO name="SPCSO2" type="SPC_1_SPCSO2" />
<DO name="SPCSO3" type="SPC_1_SPCSO3" />
<DO name="SPCSO4" type="SPC_1_SPCSO4" />
<DO name="SPCSO5" type="SPC_1_SPCSO5" />
<DO name="SPCSO6" type="SPC_1_SPCSO6" />
<DO name="SPCSO7" type="SPC_1_SPCSO7" />
<DO name="SPCSO8" type="SPC_1_SPCSO8" />
<DO name="Ind1" type="SPS_1_Proxy" />
<DO name="Ind2" type="SPS_1_Proxy" />
<DO name="Ind3" type="SPS_1_Proxy" />
<DO name="Ind4" type="SPS_1_Proxy" />
<DO name="SchdAbsTm" type="SCA_1_SchdAbsTm" />
</LNodeType>
<LNodeType id="MHAI1" lnClass="MHAI">
<DO name="HA" type="HWYE_1_HA" />
</LNodeType>
<DOType id="SPG_1" cdc="SPG">
<DA name="setVal" bType="BOOLEAN" fc="SP" dchg="true" />
</DOType>
<DOType id="ASG" cdc="ASG">
<DA name="setMag" bType="Struct" type="AnalogueValue" fc="SP" dchg="true" />
</DOType>
<DOType id="ISP" cdc="INS">
<DA name="setVal" bType="INT32" fc="SP" dchg="true" />
</DOType>
<DOType id="HWYE_1_HA" cdc="HWYE">
<SDO name="phsAHar" type="CMV_1_phsAHar" count="16" />
<DA name="numHar" bType="INT16U" fc="CF" dchg="true" />
<DA name="numCyc" bType="INT16U" fc="CF" dchg="true" />
<DA name="evalTm" bType="INT16U" fc="CF" dchg="true" />
<DA name="frequency" bType="FLOAT32" fc="CF" dchg="true" />
</DOType>
<DOType id="SCA_1_SchdAbsTm" cdc="SCA">
<DA name="val" bType="FLOAT32" count="24" fc="SP" dchg="true" />
</DOType>
<DOType id="CMV_1_phsAHar" cdc="CMV">
<DA name="cVal" bType="Struct" type="Vector" fc="MX" dchg="true" dupd="true" />
<DA name="q" bType="Quality" fc="MX" qchg="true" />
<DA name="t" bType="Timestamp" fc="MX" />
</DOType>
<DAType id="Vector">
<BDA name="mag" bType="Struct" type="AnalogueValue" /> <!-- M -->
<BDA name="ang" bType="Struct" type="AnalogueValue" /> <!-- O -->
</DAType>
<DOType id="INC_1_Mod" cdc="INC">
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="INS_1_Beh" cdc="INS">
<DA name="stVal" bType="INT32" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="INS_64" cdc="INS">
<DA name="stVal" bType="INT64" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="LPL_1_NamPlt" cdc="LPL">
<DA name="vendor" bType="VisString255" fc="DC" />
<DA name="swRev" bType="VisString255" fc="DC" />
<DA name="d" bType="VisString255" fc="DC" />
<DA name="configRev" bType="VisString255" fc="DC" />
<DA name="ldNs" bType="VisString255" fc="EX" />
</DOType>
<DOType id="DPL_1_PhyNam" cdc="DPL">
<DA name="vendor" bType="VisString255" fc="DC" />
</DOType>
<DOType id="SPS_1_Proxy" cdc="SPS">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="SPC_1_SPCSO8" cdc="SPC">
<DA name="SBOw" type="SPCOperate_5" bType="Struct" fc="CO" />
<DA name="Oper" type="SPCOperate_5" bType="Struct" fc="CO" />
<DA name="Cancel" type="SPCCancel_5" bType="Struct" fc="CO" />
<DA name="origin" type="Originator_1" bType="Struct" fc="ST" />
<DA name="ctlNum" bType="INT8U" fc="ST" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="SPC_1_SPCSO7" cdc="SPC">
<DA name="Oper" type="SPCOperate_5" bType="Struct" fc="CO" />
<DA name="Cancel" type="SPCCancel_5" bType="Struct" fc="CO" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="SPC_1_SPCSO3" cdc="SPC">
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="Cancel" type="SPCCancel_1" bType="Struct" fc="CO" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="MV_1_AnIn1" cdc="MV">
<DA name="mag" type="" bType="Struct" fc="MX" dchg="true" />
<DA name="q" bType="Quality" fc="MX" qchg="true" />
<DA name="t" bType="Timestamp" fc="MX" />
</DOType>
<DOType id="SPC_1_SPCSO6" cdc="SPC">
<DA name="SBO" bType="VisString64" fc="CO" />
<DA name="Oper" type="SPCOperate_5" bType="Struct" fc="CO" />
<DA name="Cancel" type="SPCCancel_5" bType="Struct" fc="CO" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="SPC_1_SPCSO5" cdc="SPC">
<DA name="Oper" type="SPCOperate_5" bType="Struct" fc="CO" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="Cancel" type="SPCCancel_1" bType="Struct" fc="CO" />
</DOType>
<DOType id="SPC_1_SPCSO4" cdc="SPC">
<DA name="SBOw" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="Cancel" type="SPCCancel_1" bType="Struct" fc="CO" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="LPL_2_NamPlt" cdc="LPL">
<DA name="vendor" bType="VisString255" fc="DC" />
<DA name="swRev" bType="VisString255" fc="DC" />
<DA name="d" bType="VisString255" fc="DC" />
<DA name="dU" fc="DC" bType="Unicode255"/>
</DOType>
<DOType id="SPC_1_SPCSO2" cdc="SPC">
<DA name="SBO" bType="VisString64" fc="CO" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="Cancel" type="SPCCancel_1" bType="Struct" fc="CO" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="sboClass" type="SboClasses" bType="Enum" fc="CF" />
</DOType>
<DOType id="SPC_1_SPCSO1" cdc="SPC">
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DAType id="SPCOperate_1">
<BDA name="ctlVal" bType="BOOLEAN" />
<BDA name="origin" type="Originator_1" bType="Struct" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
<BDA name="Check" bType="Check" />
</DAType>
<DAType id="Originator_1">
<BDA name="orCat" type="OrCat" bType="Enum" />
<BDA name="orIdent" bType="Octet64" />
</DAType>
<DAType id="SPCOperate_5">
<BDA name="ctlVal" bType="BOOLEAN" />
<BDA name="operTm" bType="Timestamp" />
<BDA name="origin" type="Originator_1" bType="Struct" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
<BDA name="Check" bType="Check" />
</DAType>
<DAType id="">
<BDA name="f" bType="FLOAT32" />
</DAType>
<DAType id="SPCCancel_1">
<BDA name="ctlVal" bType="BOOLEAN" />
<BDA name="origin" type="Originator_1" bType="Struct" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
</DAType>
<DAType id="SPCCancel_5">
<BDA name="ctlVal" bType="BOOLEAN" />
<BDA name="operTm" bType="Timestamp" />
<BDA name="origin" type="Originator_1" bType="Struct" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
</DAType>
<DAType id="AnalogueValue"><!-- sec 6.3 -->
<!--<BDA name="i" bType="INT32" /> --><!-- GC_1 -->
<BDA name="f" bType="FLOAT32" /><!-- GC_1 -->
</DAType>
<EnumType id="CtlModels">
<EnumVal ord="0">status-only</EnumVal>
<EnumVal ord="1">direct-with-normal-security</EnumVal>
<EnumVal ord="2">sbo-with-normal-security</EnumVal>
<EnumVal ord="3">direct-with-enhanced-security</EnumVal>
<EnumVal ord="4">sbo-with-enhanced-security</EnumVal>
</EnumType>
<EnumType id="SboClasses">
<EnumVal ord="0">operate-once</EnumVal>
<EnumVal ord="1">operate-many</EnumVal>
</EnumType>
<EnumType id="OrCat">
<EnumVal ord="0">not-supported</EnumVal>
<EnumVal ord="1">bay-control</EnumVal>
<EnumVal ord="2">station-control</EnumVal>
<EnumVal ord="3">remote-control</EnumVal>
<EnumVal ord="4">automatic-bay</EnumVal>
<EnumVal ord="5">automatic-station</EnumVal>
<EnumVal ord="6">automatic-remote</EnumVal>
<EnumVal ord="7">maintenance</EnumVal>
<EnumVal ord="8">process</EnumVal>
</EnumType>
</DataTypeTemplates>
</SCL>

@ -13,6 +13,7 @@ add_subdirectory(server_example_logging)
add_subdirectory(server_example_files)
add_subdirectory(server_example_substitution)
add_subdirectory(server_example_service_tracking)
add_subdirectory(server_example_deadband)
add_subdirectory(iec61850_client_example1)
add_subdirectory(iec61850_client_example2)

@ -20,6 +20,7 @@ EXAMPLE_DIRS += server_example_threadless
EXAMPLE_DIRS += server_example_setting_groups
EXAMPLE_DIRS += server_example_files
EXAMPLE_DIRS += server_example_substitution
EXAMPLE_DIRS += server_example_deadband
EXAMPLE_DIRS += goose_subscriber
EXAMPLE_DIRS += goose_publisher
EXAMPLE_DIRS += sv_subscriber

@ -9,6 +9,7 @@
#include "goose_receiver.h"
#include "goose_subscriber.h"
#include "hal_thread.h"
#include "linked_list.h"
#include <stdlib.h>
#include <stdio.h>
@ -16,12 +17,13 @@
static int running = 1;
void sigint_handler(int signalId)
static void
sigint_handler(int signalId)
{
running = 0;
}
void
static void
gooseListener(GooseSubscriber subscriber, void* parameter)
{
printf("GOOSE event:\n");

@ -0,0 +1,35 @@
LIBIEC_HOME=../..
PROJECT_BINARY_NAME = server_example_basic_io
PROJECT_SOURCES = server_example_basic_io.c
PROJECT_SOURCES += static_model.c
PROJECT_ICD_FILE = simpleIO_direct_control.cid
all: $(PROJECT_BINARY_NAME)
LDLIBS += -lm -lpthread
CP = cp
LIBIEC61850_INSTALL_DIR = ../../.install
LIBIEC61850_LIB_DIR = $(LIBIEC61850_INSTALL_DIR)/lib
LIBIEC61850_INC_DIR = $(LIBIEC61850_INSTALL_DIR)/include
LIBIEC61850_INCLUDES = -I$(LIBIEC61850_INC_DIR)
INCLUDES += $(LIBIEC61850_INCLUDES)
model: $(PROJECT_ICD_FILE)
java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE)
$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) -L$(LIBIEC61850_LIB_DIR) -liec61850 $(LDLIBS)
mkdir -p vmd-filestore
$(CP) $(PROJECT_BINARY_NAME) vmd-filestore/IEDSERVER.BIN
clean:
rm -f $(PROJECT_BINARY_NAME)
rm -f vmd-filestore/IEDSERVER.BIN

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

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

@ -0,0 +1,442 @@
<?xml version="1.0" encoding="UTF-8"?>
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" revision="A" version="2007">
<Header id="" nameStructure="IEDName" toolID="FUHUA Model Designer ICT">
<History>
<Hitem revision="" version="" when="" />
</History>
</Header>
<Communication>
<SubNetwork name="W1">
<ConnectedAP apName="AP1" iedName="IED1">
<Address>
<P type="OSI-AP-Title">1,1,1,999,1</P>
<P type="OSI-AE-Qualifier">12</P>
<P type="OSI-PSEL">00000001</P>
<P type="OSI-SSEL">0001</P>
<P type="OSI-TSEL">0001</P>
<P type="IP">0.0.0.0</P>
<P type="IP-SUBNET">255.255.255.0</P>
<P type="IP-GATEWAY">0.0.0.0</P>
</Address>
</ConnectedAP>
</SubNetwork>
</Communication>
<IED name="IED1" originalSclRevision="B" originalSclVersion="2007">
<Services nameLength="64">
<ClientServices />
<ConfDataSet max="50" maxAttributes="100" />
<ConfReportControl max="50" />
<ReportSettings bufTime="Dyn" cbName="Conf" datSet="Conf" intgPd="Dyn" optFields="Dyn" trgOps="Dyn" />
<ConfLNs fixLnInst="true" fixPrefix="true" />
<DynAssociation />
<GetDirectory />
<GetDataObjectDefinition />
<GetCBValues />
<DataObjectDirectory />
<GetDataSetValue />
<DataSetDirectory />
<ReadWrite />
<GSESettings appID="Conf" cbName="Conf" datSet="Conf" />
<GOOSE max="10" />
</Services>
<AccessPoint name="AP1">
<Server>
<Authentication none="true" />
<LDevice inst="LD1">
<LN0 inst="" lnClass="LLN0" lnType="LLN0_0">
<DataSet name="AnalogEvents">
<FCDA doName="AnIn1" fc="MX" ldInst="LD1" prefix="AnIn" lnClass="GGIO" lnInst="1" />
<FCDA doName="AnIn2" fc="MX" ldInst="LD1" prefix="AnIn" lnClass="GGIO" lnInst="1" />
<FCDA doName="AnIn3" fc="MX" ldInst="LD1" prefix="AnIn" lnClass="GGIO" lnInst="1" />
<FCDA doName="AnIn4" fc="MX" ldInst="LD1" prefix="AnIn" lnClass="GGIO" lnInst="1" />
</DataSet>
<ReportControl bufTime="0" buffered="true" confRev="1" datSet="AnalogEvents" intgPd="5000" name="BRCB_Events">
<TrgOps dchg="true" dupd="false" period="false" qchg="true" />
<OptFields configRef="true" dataRef="true" dataSet="false" entryID="true" reasonCode="true" seqNum="true" timeStamp="true" />
<RptEnabled max="2" />
</ReportControl>
<ReportControl bufTime="0" buffered="false" confRev="1" datSet="AnalogEvents" intgPd="5000" name="URCB_Events">
<TrgOps dchg="true" dupd="false" period="false" qchg="true" />
<OptFields configRef="true" dataRef="true" dataSet="false" reasonCode="true" seqNum="true" timeStamp="true" />
<RptEnabled max="2" />
</ReportControl>
<DOI name="Mod">
<DAI name="stVal">
<Val>on</Val>
</DAI>
</DOI>
</LN0>
<LN inst="1" lnClass="LPHD" lnType="LPHD" />
<LN inst="1" prefix="AnIn" lnClass="GGIO" lnType="GGIO_AnIn_MV">
<DOI name="Beh">
<DAI name="stVal">
<Val>on</Val>
</DAI>
</DOI>
<DOI name="AnIn1">
<DAI name="db">
<Val>10000</Val>
</DAI>
<DAI name="dbRef">
<Val>10</Val>
</DAI>
<DAI name="zeroDb">
<Val>1000</Val>
</DAI>
<DAI name="zeroDbRef">
<Val>10</Val>
</DAI>
</DOI>
<DOI name="AnIn2">
<DAI name="db">
<Val>1000</Val>
</DAI>
<DAI name="dbRef">
<Val>10</Val>
</DAI>
<DAI name="zeroDb">
<Val>1000</Val>
</DAI>
<DAI name="zeroDbRef">
<Val>10</Val>
</DAI>
</DOI>
<DOI name="AnIn3">
<DAI name="db">
<Val>10000</Val>
</DAI>
<DAI name="zeroDb">
<Val>1000</Val>
</DAI>
<SDI name="rangeC">
<SDI name="min">
<DAI name="f">
<Val>-5</Val>
</DAI>
</SDI>
<SDI name="max">
<DAI name="f">
<Val>5</Val>
</DAI>
</SDI>
</SDI>
</DOI>
<DOI name="AnIn4">
<DAI name="db">
<Val>1000</Val>
</DAI>
<DAI name="zeroDb">
<Val>1000</Val>
</DAI>
<SDI name="rangeC">
<SDI name="min">
<DAI name="f">
<Val>-100</Val>
</DAI>
</SDI>
<SDI name="max">
<DAI name="f">
<Val>100</Val>
</DAI>
</SDI>
</SDI>
</DOI>
</LN>
</LDevice>
</Server>
</AccessPoint>
</IED>
<DataTypeTemplates>
<LNodeType id="LPHD" lnClass="LPHD">
<DO name="PhyNam" type="DPL" />
<DO name="PhyHealth" type="ENS_Health" />
<DO name="Proxy" type="SPS" />
</LNodeType>
<LNodeType id="LLN0_0" lnClass="LLN0">
<DO name="Mod" type="ENC_Mod" />
<DO name="Beh" type="ENS_Beh" />
<DO name="Health" type="ENS_Health" />
<DO name="NamPlt" type="LPL_NamPlt" />
</LNodeType>
<LNodeType id="GGIO_AnIn_MV" lnClass="GGIO">
<DO name="Beh" type="ENS_Beh" />
<DO name="AnIn1" type="MV_db" />
<DO name="AnIn2" type="MV_db" />
<DO name="AnIn3" type="MV_db_rangeC" />
<DO name="AnIn4" type="MV_db_rangeC" />
</LNodeType>
<DOType cdc="DPL" id="DPL">
<DA bType="VisString255" fc="DC" name="vendor" />
<DA bType="VisString255" fc="DC" name="hwRev" />
<DA bType="VisString255" fc="DC" name="swRev" />
<DA bType="VisString255" fc="DC" name="serNum" />
<DA bType="VisString255" fc="DC" name="model" />
<DA bType="VisString255" fc="DC" name="location" />
<DA bType="VisString64" fc="DC" name="name" />
<DA bType="VisString255" fc="DC" name="owner" />
</DOType>
<DOType cdc="ENS" id="ENS_TmSyn">
<DA bType="Enum" fc="ST" name="stVal" type="TmSynEnum" />
<DA bType="Quality" fc="ST" name="q" />
<DA bType="Timestamp" fc="ST" name="t" />
</DOType>
<DOType cdc="ENS" id="ENS_Beh">
<DA bType="Enum" fc="ST" name="stVal" type="BehKind" />
<DA bType="Quality" fc="ST" name="q" />
<DA bType="Timestamp" fc="ST" name="t" />
</DOType>
<DOType cdc="SPS" id="SPS">
<DA bType="BOOLEAN" dchg="true" fc="ST" name="stVal" />
<DA bType="Quality" fc="ST" name="q" qchg="true" />
<DA bType="Timestamp" fc="ST" name="t" />
<DA bType="VisString255" fc="DC" name="d" />
</DOType>
<DOType cdc="ENC" id="ENC_Mod">
<DA bType="Enum" dchg="true" fc="ST" name="stVal" type="ModKind" />
<DA bType="Quality" fc="ST" name="q" qchg="true" />
<DA bType="Timestamp" fc="ST" name="t" />
<DA bType="Enum" fc="CF" name="ctlModel" type="ctlModel" />
</DOType>
<DOType cdc="ENC" id="ENC">
<DA bType="Struct" fc="CO" name="Oper" type="ENCOperate" />
<DA bType="VisString129" fc="CO" name="SBO" />
<DA bType="Struct" fc="CO" name="SBOw" type="ENCOperate" />
<DA bType="Struct" fc="ST" name="origin" type="Originator" />
<DA bType="INT8U" fc="ST" name="ctlNum" />
<DA bType="Enum" dchg="true" fc="ST" name="stVal" type="tempEnum" />
<DA bType="Quality" fc="ST" name="q" qchg="true" />
<DA bType="Timestamp" fc="ST" name="t" />
<DA bType="Enum" fc="CF" name="ctlModel" type="ctlModel" />
</DOType>
<DOType cdc="INS" id="INS">
<DA bType="INT32" dchg="true" fc="ST" name="stVal" />
<DA bType="Quality" fc="ST" name="q" qchg="true" />
<DA bType="Timestamp" fc="ST" name="t" />
<DA bType="INT32U" fc="CF" name="db" />
<DA bType="INT32U" fc="CF" name="zeroDb" />
</DOType>
<DOType cdc="MV" id="MV_db">
<DA bType="Struct" fc="MX" name="instMag" type="AnalogueValue"/>
<DA bType="Struct" dchg="true" dupd="false" fc="MX" name="mag" type="AnalogueValue" />
<DA bType="Quality" fc="MX" name="q" qchg="true" />
<DA bType="Timestamp" fc="MX" name="t" />
<DA bType="INT32U" dchg="true" fc="CF" name="db" />
<DA bType="INT32U" dchg="true" fc="CF" name="zeroDb" />
<DA bType="FLOAT32" dchg="true" fc="CF" name="dbRef" />
<DA bType="FLOAT32" dchg="true" fc="CF" name="zeroDbRef" />
</DOType>
<DOType cdc="MV" id="MV_db_rangeC">
<DA bType="Struct" fc="MX" name="instMag" type="AnalogueValue"/>
<DA bType="Struct" dchg="true" dupd="false" fc="MX" name="mag" type="AnalogueValue" />
<DA bType="Quality" fc="MX" name="q" qchg="true" />
<DA bType="Timestamp" fc="MX" name="t" />
<DA bType="INT32U" dchg="true" fc="CF" name="db" />
<DA bType="INT32U" dchg="true" fc="CF" name="zeroDb" />
<DA bType="Struct" dchg="true" fc="CF" name="rangeC" type="RangeConfig" />
</DOType>
<DOType cdc="ENS" id="ENS_Health">
<DA bType="Enum" dchg="true" fc="ST" name="stVal" type="HealthKind" />
<DA bType="Quality" fc="ST" name="q" qchg="true" />
<DA bType="Timestamp" fc="ST" name="t" />
</DOType>
<DOType cdc="LPL" id="LPL_NamPlt">
<DA bType="VisString255" fc="DC" name="vendor" />
<DA bType="VisString255" fc="DC" name="swRev" />
<DA bType="VisString255" fc="DC" name="d" />
<DA bType="VisString255" fc="DC" name="configRev" />
</DOType>
<DAType id="Originator">
<BDA bType="Enum" name="orCat" type="orCat" />
<BDA bType="Octet64" name="orIdent" />
</DAType>
<DAType id="ENCOperate">
<BDA bType="Enum" name="ctlVal" type="tempEnum" />
<BDA bType="Timestamp" name="operTm" />
<BDA bType="Struct" name="origin" type="Originator" />
<BDA bType="INT8U" name="ctlNum" />
<BDA bType="Timestamp" name="T" />
<BDA bType="BOOLEAN" name="Test" />
<BDA bType="Check" name="Check" />
</DAType>
<DAType id="ENCCancel">
<BDA bType="Enum" name="ctlVal" type="tempEnum" />
<BDA bType="Timestamp" name="operTm" />
<BDA bType="Struct" name="origin" type="Originator" />
<BDA bType="INT8U" name="ctlNum" />
<BDA bType="Timestamp" name="T" />
<BDA bType="BOOLEAN" name="Test" />
</DAType>
<DAType id="Unit">
<BDA bType="Enum" name="SIUnit" type="SIUnit" />
<BDA bType="Enum" name="multiplier" type="multiplier" />
</DAType>
<DAType id="Vector">
<BDA name="mag" bType="Struct" type="AnalogueValue" />
<BDA name="ang" bType="Struct" type="AnalogueValue" />
</DAType>
<DAType id="AnalogueValue">
<BDA bType="FLOAT32" name="f" />
</DAType>
<DAType id="ScaledValueConfig">
<BDA bType="FLOAT32" name="scaleFactor" />
<BDA bType="FLOAT32" name="offset" />
</DAType>
<DAType id="RangeConfig">
<BDA bType="Struct" name="min" type="AnalogueValue" />
<BDA bType="Struct" name="max" type="AnalogueValue" />
</DAType>
<DAType id="PulseConfig">
<BDA bType="Enum" name="cmdQual" type="cmdQual" />
<BDA bType="INT32U" name="onDur" />
<BDA bType="INT32U" name="offDur" />
<BDA bType="INT32U" name="numPls" />
</DAType>
<EnumType id="tempEnum">
<EnumVal ord="0">e1</EnumVal>
<EnumVal ord="1">e2</EnumVal>
<EnumVal ord="2">e3</EnumVal>
<EnumVal ord="3">e4</EnumVal>
<EnumVal ord="4">e5</EnumVal>
</EnumType>
<EnumType id="TmSynEnum">
<EnumVal ord="0">ExternalAreaClock</EnumVal>
<EnumVal ord="1">LocalAreaClock</EnumVal>
<EnumVal ord="2">GlobalAreaClock</EnumVal>
</EnumType>
<EnumType id="orCat">
<EnumVal ord="0">not-supported</EnumVal>
<EnumVal ord="1">bay-control</EnumVal>
<EnumVal ord="2">station-control</EnumVal>
<EnumVal ord="3">remote-control</EnumVal>
<EnumVal ord="4">automatic-bay</EnumVal>
<EnumVal ord="5">automatic-station</EnumVal>
<EnumVal ord="6">automatic-remote</EnumVal>
<EnumVal ord="7">maintenance</EnumVal>
<EnumVal ord="8">process</EnumVal>
</EnumType>
<EnumType id="ctlModel">
<EnumVal ord="0">status-only</EnumVal>
<EnumVal ord="1">direct-with-normal-security</EnumVal>
<EnumVal ord="2">sbo-with-normal-security</EnumVal>
<EnumVal ord="3">direct-with-enhanced-security</EnumVal>
<EnumVal ord="4">sbo-with-enhanced-security</EnumVal>
</EnumType>
<EnumType id="sboClass">
<EnumVal ord="0">operate-once</EnumVal>
<EnumVal ord="1">operate-many</EnumVal>
</EnumType>
<EnumType id="SIUnit">
<EnumVal ord="1">none</EnumVal>
<EnumVal ord="2">m</EnumVal>
<EnumVal ord="3">kg</EnumVal>
<EnumVal ord="4">s</EnumVal>
<EnumVal ord="5">A</EnumVal>
<EnumVal ord="6">K</EnumVal>
<EnumVal ord="7">mol</EnumVal>
<EnumVal ord="8">cd</EnumVal>
<EnumVal ord="9">deg</EnumVal>
<EnumVal ord="10">rad</EnumVal>
<EnumVal ord="11">sr</EnumVal>
<EnumVal ord="21">Gy</EnumVal>
<EnumVal ord="22">q</EnumVal>
<EnumVal ord="23">°C</EnumVal>
<EnumVal ord="24">Sv</EnumVal>
<EnumVal ord="25">F</EnumVal>
<EnumVal ord="26">C</EnumVal>
<EnumVal ord="27">S</EnumVal>
<EnumVal ord="28">H</EnumVal>
<EnumVal ord="29">V</EnumVal>
<EnumVal ord="30">ohm</EnumVal>
<EnumVal ord="31">J</EnumVal>
<EnumVal ord="32">N</EnumVal>
<EnumVal ord="33">Hz</EnumVal>
<EnumVal ord="34">lx</EnumVal>
<EnumVal ord="35">Lm</EnumVal>
<EnumVal ord="36">Wb</EnumVal>
<EnumVal ord="37">T</EnumVal>
<EnumVal ord="38">W</EnumVal>
<EnumVal ord="39">Pa</EnumVal>
<EnumVal ord="41">m²</EnumVal>
<EnumVal ord="42">m³</EnumVal>
<EnumVal ord="43">m/s</EnumVal>
<EnumVal ord="44">m/s²</EnumVal>
<EnumVal ord="45">m³/s</EnumVal>
<EnumVal ord="46">m/m³</EnumVal>
<EnumVal ord="47">M</EnumVal>
<EnumVal ord="48">kg/m³</EnumVal>
<EnumVal ord="49">m²/s</EnumVal>
<EnumVal ord="50">W/m K</EnumVal>
<EnumVal ord="51">J/K</EnumVal>
<EnumVal ord="52">ppm</EnumVal>
<EnumVal ord="53">1/s</EnumVal>
<EnumVal ord="54">rad/s</EnumVal>
<EnumVal ord="61">VA</EnumVal>
<EnumVal ord="62">Watts</EnumVal>
<EnumVal ord="63">VAr</EnumVal>
<EnumVal ord="64">theta</EnumVal>
<EnumVal ord="65">cos(theta)</EnumVal>
<EnumVal ord="66">Vs</EnumVal>
<EnumVal ord="67">V²</EnumVal>
<EnumVal ord="68">As</EnumVal>
<EnumVal ord="69">A²</EnumVal>
<EnumVal ord="70">A²t</EnumVal>
<EnumVal ord="71">VAh</EnumVal>
<EnumVal ord="72">Wh</EnumVal>
<EnumVal ord="73">VArh</EnumVal>
<EnumVal ord="74">V/Hz</EnumVal>
</EnumType>
<EnumType id="multiplier">
<EnumVal ord="-24">Yocto</EnumVal>
<EnumVal ord="-21">Zepto</EnumVal>
<EnumVal ord="-18">Atto</EnumVal>
<EnumVal ord="-15">Femto</EnumVal>
<EnumVal ord="-12">Pico</EnumVal>
<EnumVal ord="-9">Nano</EnumVal>
<EnumVal ord="-6">Micro</EnumVal>
<EnumVal ord="-3">Milli</EnumVal>
<EnumVal ord="-2">Centi</EnumVal>
<EnumVal ord="-1">Deci</EnumVal>
<EnumVal ord="0">zeroNoValue</EnumVal>
<EnumVal ord="1">Deca</EnumVal>
<EnumVal ord="2">Hecto</EnumVal>
<EnumVal ord="3">Kilo</EnumVal>
<EnumVal ord="6">Mega</EnumVal>
<EnumVal ord="9">Giga</EnumVal>
<EnumVal ord="12">Tera</EnumVal>
<EnumVal ord="15">Petra</EnumVal>
<EnumVal ord="18">Exa</EnumVal>
<EnumVal ord="21">Zetta</EnumVal>
<EnumVal ord="24">Yotta</EnumVal>
</EnumType>
<EnumType id="range">
<EnumVal ord="0">normal</EnumVal>
<EnumVal ord="1">high</EnumVal>
<EnumVal ord="2">low</EnumVal>
<EnumVal ord="3">high-high</EnumVal>
<EnumVal ord="4">low-low</EnumVal>
</EnumType>
<EnumType id="cmdQual">
<EnumVal ord="0">pulse</EnumVal>
<EnumVal ord="1">persistent</EnumVal>
</EnumType>
<EnumType id="HealthKind">
<EnumVal ord="1">Ok</EnumVal>
<EnumVal ord="2">Warning</EnumVal>
<EnumVal ord="3">Alarm</EnumVal>
</EnumType>
<EnumType id="BehKind">
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="2">blocked</EnumVal>
<EnumVal ord="3">test</EnumVal>
<EnumVal ord="4">test/blocked</EnumVal>
<EnumVal ord="5">off</EnumVal>
</EnumType>
<EnumType id="ModKind">
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="2">blocked</EnumVal>
<EnumVal ord="3">test</EnumVal>
<EnumVal ord="4">test/blocked</EnumVal>
<EnumVal ord="5">off</EnumVal>
</EnumType>
</DataTypeTemplates>
</SCL>

@ -0,0 +1,221 @@
/*
* server_example_deadband.c
*
* - How to handle dead bands for measured values (MV)
* - Use variable and fixed dead band with db and dbRef (edition 2.1)
* - Use fixed dead band with db and rangeC (edition 2)
*/
#include "iec61850_server.h"
#include "hal_thread.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "static_model.h"
/* import IEC 61850 device model created from SCL-File */
extern IedModel iedModel;
static int running = 0;
static IedServer iedServer = NULL;
void
sigint_handler(int signalId)
{
running = 0;
}
static void
connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter)
{
if (connected)
printf("Connection opened\n");
else
printf("Connection closed\n");
}
int
main(int argc, char** argv)
{
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create new server configuration object */
IedServerConfig config = IedServerConfig_create();
/* Set buffer size for buffered report control blocks to 200000 bytes */
IedServerConfig_setReportBufferSize(config, 200000);
/* Set stack compliance to a specific edition of the standard (WARNING: data model has also to be checked for compliance) */
IedServerConfig_setEdition(config, IEC_61850_EDITION_2);
/* disable MMS file service */
IedServerConfig_enableFileService(config, false);
/* enable dynamic data set service */
IedServerConfig_enableDynamicDataSetService(config, true);
/* disable log service */
IedServerConfig_enableLogService(config, false);
/* set maximum number of clients */
IedServerConfig_setMaxMmsConnections(config, 5);
/* Create a new IEC 61850 server instance */
iedServer = IedServer_createWithConfig(&iedModel, NULL, config);
/* configuration object is no longer required */
IedServerConfig_destroy(config);
/* set the identity values for MMS identify service */
IedServer_setServerIdentity(iedServer, "libiec61850.com", "deadband example", "1.5.0");
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
/* Allow write access to CF parameters (here "db" and "rangeC") */
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_CF, ACCESS_POLICY_ALLOW);
/* MMS server will be instructed to start listening for client connections. */
IedServer_start(iedServer, 102);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n");
IedServer_destroy(iedServer);
exit(-1);
}
running = 1;
signal(SIGINT, sigint_handler);
float t = 0.f;
float an1InstMag = 5 * sinf(t);
float an2InstMag = 5 * sinf(t + 1.f);
float an3InstMag = 5 * sinf(t + 2.f);
float an4InstMag = 5 * sinf(t + 3.f);
float an1Mag = an1InstMag;
float an2Mag = an2InstMag;
float an3Mag = an3InstMag;
float an4Mag = an4InstMag;
float anIn1DbRef = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn1_dbRef));
float anIn1ZeroDbRef = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn1_zeroDbRef));
float anIn2DbRef = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn2_dbRef));
float anIn2ZeroDbRef = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn2_zeroDbRef));
while (running) {
uint64_t timestamp = Hal_getTimeInMs();
t += 0.001f;
an1InstMag = 5 * sinf(t);
an2InstMag = 5 * sinf(t + 1.f);
an3InstMag = 5 * sinf(t + 2.f);
an4InstMag = 5 * sinf(t + 3.f);
Timestamp iecTimestamp;
Timestamp_clearFlags(&iecTimestamp);
Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
Timestamp_setLeapSecondKnown(&iecTimestamp, true);
float dbValF;
float minValF;
float maxValF;
IedServer_lockDataModel(iedServer);
/* handle AnIn1 (using db and dbRef as in edition 2.1) */
dbValF = (float)MmsValue_toUint32(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn1_db)) / 1000.f;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn1_instMag_f, an1InstMag);
if (anIn1DbRef == 0) { /* dbRef = 0 */
if (fabsf(an1InstMag - an1Mag) > (fabsf(an1Mag) * dbValF * 0.01f)) {
/* dead band condition -> updated "mag" attribute */
an1Mag = an1InstMag;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn1_mag_f, an1Mag);
}
}
else {
if (fabsf(an1InstMag - an1Mag) > (fabsf(anIn1DbRef) * dbValF * 0.01f)) {
/* dead band condition -> updated "mag" attribute */
an1Mag = an1InstMag;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn1_mag_f, an1Mag);
}
}
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn1_t, &iecTimestamp);
/* handle AnIn2 (using db and dbRef as in edition 2.1) */
dbValF = (float)MmsValue_toUint32(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn2_db)) / 1000.f;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn2_instMag_f, an2InstMag);
if (anIn1DbRef == 0) { /* dbRef = 0 */
if (fabsf(an2InstMag - an2Mag) > (fabsf(an2Mag) * dbValF * 0.01f)) {
/* dead band condition -> updated "mag" attribute */
an2Mag = an2InstMag;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn2_mag_f, an2Mag);
}
}
else {
if (fabsf(an2InstMag - an2Mag) > (fabsf(anIn2DbRef) * dbValF * 0.01f)) {
/* dead band condition -> updated "mag" attribute */
an2Mag = an2InstMag;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn2_mag_f, an2Mag);
}
}
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn2_t, &iecTimestamp);
/* handle AnIn3 (using db and rangeC as in edition 2) */
dbValF = (float)MmsValue_toUint32(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn3_db)) / 1000.f;
minValF = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn3_rangeC_min_f));
maxValF = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn3_rangeC_max_f));
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn3_instMag_f, an3InstMag);
if (fabsf(an3InstMag - an3Mag) > (fabsf(maxValF - minValF) * dbValF * 0.01f)) {
/* dead band condition -> updated "mag" attribute */
an3Mag = an3InstMag;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn3_mag_f, an3Mag);
}
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn3_t, &iecTimestamp);
/* handle AnIn4 (using db and rangeC as in edition 2) */
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn4_instMag_f, an4InstMag);
dbValF = (float)MmsValue_toUint32(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn4_db)) / 1000.f;
minValF = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn4_rangeC_min_f));
maxValF = MmsValue_toFloat(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn4_rangeC_max_f));
if (fabsf(an4InstMag - an4Mag) > (fabsf(maxValF - minValF) * dbValF * 0.01f)) {
/* dead band condition -> updated "mag" attribute */
an4Mag = an4InstMag;
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn4_mag_f, an4Mag);
}
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_AnInGGIO1_AnIn4_t, &iecTimestamp);
IedServer_unlockDataModel(iedServer);
Thread_sleep(10);
}
/* stop MMS server - close TCP server socket and all client sockets */
IedServer_stop(iedServer);
/* Cleanup - free all resources */
IedServer_destroy(iedServer);
return 0;
} /* main() */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,207 @@
/*
* static_model.h
*
* automatically generated from cid_example_deadband.cid
*/
#ifndef STATIC_MODEL_H_
#define STATIC_MODEL_H_
#include <stdlib.h>
#include "iec61850_model.h"
extern IedModel iedModel;
extern LogicalDevice iedModel_LD1;
extern LogicalNode iedModel_LD1_LLN0;
extern DataObject iedModel_LD1_LLN0_Mod;
extern DataAttribute iedModel_LD1_LLN0_Mod_stVal;
extern DataAttribute iedModel_LD1_LLN0_Mod_q;
extern DataAttribute iedModel_LD1_LLN0_Mod_t;
extern DataAttribute iedModel_LD1_LLN0_Mod_ctlModel;
extern DataObject iedModel_LD1_LLN0_Beh;
extern DataAttribute iedModel_LD1_LLN0_Beh_stVal;
extern DataAttribute iedModel_LD1_LLN0_Beh_q;
extern DataAttribute iedModel_LD1_LLN0_Beh_t;
extern DataObject iedModel_LD1_LLN0_Health;
extern DataAttribute iedModel_LD1_LLN0_Health_stVal;
extern DataAttribute iedModel_LD1_LLN0_Health_q;
extern DataAttribute iedModel_LD1_LLN0_Health_t;
extern DataObject iedModel_LD1_LLN0_NamPlt;
extern DataAttribute iedModel_LD1_LLN0_NamPlt_vendor;
extern DataAttribute iedModel_LD1_LLN0_NamPlt_swRev;
extern DataAttribute iedModel_LD1_LLN0_NamPlt_d;
extern DataAttribute iedModel_LD1_LLN0_NamPlt_configRev;
extern LogicalNode iedModel_LD1_LPHD1;
extern DataObject iedModel_LD1_LPHD1_PhyNam;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_vendor;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_hwRev;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_swRev;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_serNum;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_model;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_location;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_name;
extern DataAttribute iedModel_LD1_LPHD1_PhyNam_owner;
extern DataObject iedModel_LD1_LPHD1_PhyHealth;
extern DataAttribute iedModel_LD1_LPHD1_PhyHealth_stVal;
extern DataAttribute iedModel_LD1_LPHD1_PhyHealth_q;
extern DataAttribute iedModel_LD1_LPHD1_PhyHealth_t;
extern DataObject iedModel_LD1_LPHD1_Proxy;
extern DataAttribute iedModel_LD1_LPHD1_Proxy_stVal;
extern DataAttribute iedModel_LD1_LPHD1_Proxy_q;
extern DataAttribute iedModel_LD1_LPHD1_Proxy_t;
extern DataAttribute iedModel_LD1_LPHD1_Proxy_d;
extern LogicalNode iedModel_LD1_AnInGGIO1;
extern DataObject iedModel_LD1_AnInGGIO1_Beh;
extern DataAttribute iedModel_LD1_AnInGGIO1_Beh_stVal;
extern DataAttribute iedModel_LD1_AnInGGIO1_Beh_q;
extern DataAttribute iedModel_LD1_AnInGGIO1_Beh_t;
extern DataObject iedModel_LD1_AnInGGIO1_AnIn1;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_instMag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_instMag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_mag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_mag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_q;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_t;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_db;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_zeroDb;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_dbRef;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_zeroDbRef;
extern DataObject iedModel_LD1_AnInGGIO1_AnIn2;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_instMag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_instMag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_mag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_mag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_q;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_t;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_db;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_zeroDb;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_dbRef;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_zeroDbRef;
extern DataObject iedModel_LD1_AnInGGIO1_AnIn3;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_instMag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_instMag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_mag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_mag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_q;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_t;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_db;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_zeroDb;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max_f;
extern DataObject iedModel_LD1_AnInGGIO1_AnIn4;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_instMag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_instMag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_mag;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_mag_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_q;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_t;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_db;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_zeroDb;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min_f;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max;
extern DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max_f;
#define IEDMODEL_LD1 (&iedModel_LD1)
#define IEDMODEL_LD1_LLN0 (&iedModel_LD1_LLN0)
#define IEDMODEL_LD1_LLN0_Mod (&iedModel_LD1_LLN0_Mod)
#define IEDMODEL_LD1_LLN0_Mod_stVal (&iedModel_LD1_LLN0_Mod_stVal)
#define IEDMODEL_LD1_LLN0_Mod_q (&iedModel_LD1_LLN0_Mod_q)
#define IEDMODEL_LD1_LLN0_Mod_t (&iedModel_LD1_LLN0_Mod_t)
#define IEDMODEL_LD1_LLN0_Mod_ctlModel (&iedModel_LD1_LLN0_Mod_ctlModel)
#define IEDMODEL_LD1_LLN0_Beh (&iedModel_LD1_LLN0_Beh)
#define IEDMODEL_LD1_LLN0_Beh_stVal (&iedModel_LD1_LLN0_Beh_stVal)
#define IEDMODEL_LD1_LLN0_Beh_q (&iedModel_LD1_LLN0_Beh_q)
#define IEDMODEL_LD1_LLN0_Beh_t (&iedModel_LD1_LLN0_Beh_t)
#define IEDMODEL_LD1_LLN0_Health (&iedModel_LD1_LLN0_Health)
#define IEDMODEL_LD1_LLN0_Health_stVal (&iedModel_LD1_LLN0_Health_stVal)
#define IEDMODEL_LD1_LLN0_Health_q (&iedModel_LD1_LLN0_Health_q)
#define IEDMODEL_LD1_LLN0_Health_t (&iedModel_LD1_LLN0_Health_t)
#define IEDMODEL_LD1_LLN0_NamPlt (&iedModel_LD1_LLN0_NamPlt)
#define IEDMODEL_LD1_LLN0_NamPlt_vendor (&iedModel_LD1_LLN0_NamPlt_vendor)
#define IEDMODEL_LD1_LLN0_NamPlt_swRev (&iedModel_LD1_LLN0_NamPlt_swRev)
#define IEDMODEL_LD1_LLN0_NamPlt_d (&iedModel_LD1_LLN0_NamPlt_d)
#define IEDMODEL_LD1_LLN0_NamPlt_configRev (&iedModel_LD1_LLN0_NamPlt_configRev)
#define IEDMODEL_LD1_LPHD1 (&iedModel_LD1_LPHD1)
#define IEDMODEL_LD1_LPHD1_PhyNam (&iedModel_LD1_LPHD1_PhyNam)
#define IEDMODEL_LD1_LPHD1_PhyNam_vendor (&iedModel_LD1_LPHD1_PhyNam_vendor)
#define IEDMODEL_LD1_LPHD1_PhyNam_hwRev (&iedModel_LD1_LPHD1_PhyNam_hwRev)
#define IEDMODEL_LD1_LPHD1_PhyNam_swRev (&iedModel_LD1_LPHD1_PhyNam_swRev)
#define IEDMODEL_LD1_LPHD1_PhyNam_serNum (&iedModel_LD1_LPHD1_PhyNam_serNum)
#define IEDMODEL_LD1_LPHD1_PhyNam_model (&iedModel_LD1_LPHD1_PhyNam_model)
#define IEDMODEL_LD1_LPHD1_PhyNam_location (&iedModel_LD1_LPHD1_PhyNam_location)
#define IEDMODEL_LD1_LPHD1_PhyNam_name (&iedModel_LD1_LPHD1_PhyNam_name)
#define IEDMODEL_LD1_LPHD1_PhyNam_owner (&iedModel_LD1_LPHD1_PhyNam_owner)
#define IEDMODEL_LD1_LPHD1_PhyHealth (&iedModel_LD1_LPHD1_PhyHealth)
#define IEDMODEL_LD1_LPHD1_PhyHealth_stVal (&iedModel_LD1_LPHD1_PhyHealth_stVal)
#define IEDMODEL_LD1_LPHD1_PhyHealth_q (&iedModel_LD1_LPHD1_PhyHealth_q)
#define IEDMODEL_LD1_LPHD1_PhyHealth_t (&iedModel_LD1_LPHD1_PhyHealth_t)
#define IEDMODEL_LD1_LPHD1_Proxy (&iedModel_LD1_LPHD1_Proxy)
#define IEDMODEL_LD1_LPHD1_Proxy_stVal (&iedModel_LD1_LPHD1_Proxy_stVal)
#define IEDMODEL_LD1_LPHD1_Proxy_q (&iedModel_LD1_LPHD1_Proxy_q)
#define IEDMODEL_LD1_LPHD1_Proxy_t (&iedModel_LD1_LPHD1_Proxy_t)
#define IEDMODEL_LD1_LPHD1_Proxy_d (&iedModel_LD1_LPHD1_Proxy_d)
#define IEDMODEL_LD1_AnInGGIO1 (&iedModel_LD1_AnInGGIO1)
#define IEDMODEL_LD1_AnInGGIO1_Beh (&iedModel_LD1_AnInGGIO1_Beh)
#define IEDMODEL_LD1_AnInGGIO1_Beh_stVal (&iedModel_LD1_AnInGGIO1_Beh_stVal)
#define IEDMODEL_LD1_AnInGGIO1_Beh_q (&iedModel_LD1_AnInGGIO1_Beh_q)
#define IEDMODEL_LD1_AnInGGIO1_Beh_t (&iedModel_LD1_AnInGGIO1_Beh_t)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1 (&iedModel_LD1_AnInGGIO1_AnIn1)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_instMag (&iedModel_LD1_AnInGGIO1_AnIn1_instMag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_instMag_f (&iedModel_LD1_AnInGGIO1_AnIn1_instMag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_mag (&iedModel_LD1_AnInGGIO1_AnIn1_mag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_mag_f (&iedModel_LD1_AnInGGIO1_AnIn1_mag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_q (&iedModel_LD1_AnInGGIO1_AnIn1_q)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_t (&iedModel_LD1_AnInGGIO1_AnIn1_t)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_db (&iedModel_LD1_AnInGGIO1_AnIn1_db)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_zeroDb (&iedModel_LD1_AnInGGIO1_AnIn1_zeroDb)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_dbRef (&iedModel_LD1_AnInGGIO1_AnIn1_dbRef)
#define IEDMODEL_LD1_AnInGGIO1_AnIn1_zeroDbRef (&iedModel_LD1_AnInGGIO1_AnIn1_zeroDbRef)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2 (&iedModel_LD1_AnInGGIO1_AnIn2)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_instMag (&iedModel_LD1_AnInGGIO1_AnIn2_instMag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_instMag_f (&iedModel_LD1_AnInGGIO1_AnIn2_instMag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_mag (&iedModel_LD1_AnInGGIO1_AnIn2_mag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_mag_f (&iedModel_LD1_AnInGGIO1_AnIn2_mag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_q (&iedModel_LD1_AnInGGIO1_AnIn2_q)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_t (&iedModel_LD1_AnInGGIO1_AnIn2_t)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_db (&iedModel_LD1_AnInGGIO1_AnIn2_db)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_zeroDb (&iedModel_LD1_AnInGGIO1_AnIn2_zeroDb)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_dbRef (&iedModel_LD1_AnInGGIO1_AnIn2_dbRef)
#define IEDMODEL_LD1_AnInGGIO1_AnIn2_zeroDbRef (&iedModel_LD1_AnInGGIO1_AnIn2_zeroDbRef)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3 (&iedModel_LD1_AnInGGIO1_AnIn3)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_instMag (&iedModel_LD1_AnInGGIO1_AnIn3_instMag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_instMag_f (&iedModel_LD1_AnInGGIO1_AnIn3_instMag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_mag (&iedModel_LD1_AnInGGIO1_AnIn3_mag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_mag_f (&iedModel_LD1_AnInGGIO1_AnIn3_mag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_q (&iedModel_LD1_AnInGGIO1_AnIn3_q)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_t (&iedModel_LD1_AnInGGIO1_AnIn3_t)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_db (&iedModel_LD1_AnInGGIO1_AnIn3_db)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_zeroDb (&iedModel_LD1_AnInGGIO1_AnIn3_zeroDb)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_rangeC (&iedModel_LD1_AnInGGIO1_AnIn3_rangeC)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_rangeC_min (&iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_rangeC_min_f (&iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_rangeC_max (&iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max)
#define IEDMODEL_LD1_AnInGGIO1_AnIn3_rangeC_max_f (&iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4 (&iedModel_LD1_AnInGGIO1_AnIn4)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_instMag (&iedModel_LD1_AnInGGIO1_AnIn4_instMag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_instMag_f (&iedModel_LD1_AnInGGIO1_AnIn4_instMag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_mag (&iedModel_LD1_AnInGGIO1_AnIn4_mag)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_mag_f (&iedModel_LD1_AnInGGIO1_AnIn4_mag_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_q (&iedModel_LD1_AnInGGIO1_AnIn4_q)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_t (&iedModel_LD1_AnInGGIO1_AnIn4_t)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_db (&iedModel_LD1_AnInGGIO1_AnIn4_db)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_zeroDb (&iedModel_LD1_AnInGGIO1_AnIn4_zeroDb)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_rangeC (&iedModel_LD1_AnInGGIO1_AnIn4_rangeC)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_rangeC_min (&iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_rangeC_min_f (&iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min_f)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_rangeC_max (&iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max)
#define IEDMODEL_LD1_AnInGGIO1_AnIn4_rangeC_max_f (&iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max_f)
#endif /* STATIC_MODEL_H_ */

@ -0,0 +1,33 @@
LIBIEC_HOME=../..
PROJECT_BINARY_NAME = server_example_logging
PROJECT_SOURCES = server_example_logging.c
PROJECT_SOURCES += static_model.c
PROJECT_SOURCES += $(LIBIEC_HOME)/src/logging/drivers/sqlite/log_storage_sqlite.c
PROJECT_ICD_FILE = simpleIO_direct_control.cid
all: $(PROJECT_BINARY_NAME)
LDLIBS += -lm -lpthread -lsqlite3
CP = cp
LIBIEC61850_INSTALL_DIR = ../../.install
LIBIEC61850_LIB_DIR = $(LIBIEC61850_INSTALL_DIR)/lib
LIBIEC61850_INC_DIR = $(LIBIEC61850_INSTALL_DIR)/include
LIBIEC61850_INCLUDES = -I$(LIBIEC61850_INC_DIR)
INCLUDES += $(LIBIEC61850_INCLUDES)
model: $(PROJECT_ICD_FILE)
java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE)
$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) -L$(LIBIEC61850_LIB_DIR) -liec61850 $(LDLIBS)
clean:
rm -f $(PROJECT_BINARY_NAME)

@ -1,8 +1,7 @@
/*
* sv_subscriber_example.c
*
* Example program for Sampled Values (SV) subscriber
* sv_publisher_example.c
*
* Example program for Sampled Values (SV) publisher
*/
#include <signal.h>
@ -35,12 +34,16 @@ main(int argc, char** argv)
if (svPublisher) {
/* Create first ASDU and add data points */
SVPublisher_ASDU asdu1 = SVPublisher_addASDU(svPublisher, "svpub1", NULL, 1);
int float1 = SVPublisher_ASDU_addFLOAT(asdu1);
int float2 = SVPublisher_ASDU_addFLOAT(asdu1);
int ts1 = SVPublisher_ASDU_addTimestamp(asdu1);
/* Create second ASDU and add data points */
SVPublisher_ASDU asdu2 = SVPublisher_addASDU(svPublisher, "svpub2", NULL, 1);
int float3 = SVPublisher_ASDU_addFLOAT(asdu2);
@ -57,6 +60,8 @@ main(int argc, char** argv)
Timestamp_clearFlags(&ts);
Timestamp_setTimeInMilliseconds(&ts, Hal_getTimeInMs());
/* update the values in the SV ASDUs */
SVPublisher_ASDU_setFLOAT(asdu1, float1, fVal1);
SVPublisher_ASDU_setFLOAT(asdu1, float2, fVal2);
SVPublisher_ASDU_setTimestamp(asdu1, ts1, ts);
@ -65,14 +70,22 @@ main(int argc, char** argv)
SVPublisher_ASDU_setFLOAT(asdu2, float4, fVal2 * 2);
SVPublisher_ASDU_setTimestamp(asdu2, ts2, ts);
/* update the sample counters */
SVPublisher_ASDU_increaseSmpCnt(asdu1);
SVPublisher_ASDU_increaseSmpCnt(asdu2);
fVal1 += 1.1f;
fVal2 += 0.1f;
/* send the SV message */
SVPublisher_publish(svPublisher);
/*
* For real applications this sleep time has to be adjusted to match the SV sample rate!
* Platform specific functions like usleep or timer interrupt service routines have to be used instead
* to realize the required time accuracy for sending messages.
*/
Thread_sleep(50);
}

@ -0,0 +1,17 @@
#include <stdio.h>
#include <stdlib.h>
#include "iec61850_server.h"
#include "hal_thread.h"
int LLVMFuzzerTestOneInput(const char *data, size_t size) {
int out;
MmsValue* value = NULL;
value = MmsValue_decodeMmsData(data, 0, size, &out);
if (value != NULL) {
MmsValue_delete(value);
}
return 0;
}

@ -65,7 +65,7 @@ endif(WITH_WPCAP)
set (libhal_bsd_SRCS
${CMAKE_CURRENT_LIST_DIR}/socket/bsd/socket_bsd.c
${CMAKE_CURRENT_LIST_DIR}/ethernet/bsd/ethernet_bsd.c
${CMAKE_CURRENT_LIST_DIR}/thread/bsd/thread_macos.c
${CMAKE_CURRENT_LIST_DIR}/thread/bsd/thread_bsd.c
${CMAKE_CURRENT_LIST_DIR}/filesystem/linux/file_provider_linux.c
${CMAKE_CURRENT_LIST_DIR}/time/unix/time.c
${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c

@ -176,7 +176,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
/* Get info about all local network interfaces. */
if (getifaddrs(&ifap))
{
printf("Error getting network interfaces list!");
printf("Error getting network interfaces list!\n");
return;
}
@ -197,7 +197,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
memcpy(addr, LLADDR(link), link->sdl_alen);
}
else
printf("Could not find the network interface %s!", interfaceId);
printf("Could not find the network interface %s!\n", interfaceId);
/* Free network interface info structure. */
freeifaddrs(ifap);
@ -313,7 +313,8 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
}
/* Activate immediate mode. */
if (ioctl(self->bpf, BIOCIMMEDIATE, &self->bpfBufferSize) == -1)
optval = 1;
if (ioctl(self->bpf, BIOCIMMEDIATE, &optval) == -1)
{
printf("Unable to activate immediate mode!\n");
GLOBAL_FREEMEM(self->bpfProgram.bf_insns);

@ -124,6 +124,7 @@ static int
getInterfaceIndex(int sock, const char* deviceName)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1);
@ -142,7 +143,8 @@ getInterfaceIndex(int sock, const char* deviceName)
return -1;
}
ifr.ifr_flags |= IFF_PROMISC;
/* 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)

@ -33,6 +33,9 @@
#define DEBUG_HAL_ETHERNET 1
#endif
// Set to 1 to workaround WaitForMutlipleObjects problem (returns timeout even when packets are received)
#define ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET 1
#if (CONFIG_INCLUDE_ETHERNET_WINDOWS == 1)
@ -137,6 +140,8 @@ EthernetHandleSet_new(void)
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
#if ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET == 1
#else
if (self != NULL && sock != NULL) {
int i = self->nhandles++;
@ -145,11 +150,14 @@ EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
self->handles[i] = pcap_getevent(sock->rawSocket);
}
#endif
}
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
#if ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET == 1
#else
if ((self != NULL) && (sock != NULL)) {
HANDLE h = pcap_getevent(sock->rawSocket);
@ -163,21 +171,33 @@ EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock
}
}
}
#endif
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
#if ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET == 1
return 1;
#else
int result;
if ((self != NULL) && (self->nhandles > 0)) {
result = WaitForMultipleObjects(self->nhandles, self->handles, 0, timeoutMs);
DWORD ret = WaitForMultipleObjects(self->nhandles, self->handles, 0, timeoutMs);
if (ret == WAIT_TIMEOUT)
result = 0;
else if (ret == WAIT_FAILED)
result = -1;
else
result = (int)ret;
}
else {
result = -1;
}
return result;
#endif
}
void
@ -328,7 +348,7 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
char* interfaceName = getInterfaceName(interfaceIndex);
if ((pcapSocket = pcap_open_live(interfaceName, 65536, PCAP_OPENFLAG_PROMISCUOUS, 10, errbuf)) == NULL)
if ((pcapSocket = pcap_open_live(interfaceName, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf)) == NULL)
{
printf("Open ethernet socket failed for device %s\n", interfaceName);
free(interfaceName);

@ -71,6 +71,14 @@ Hal_getTimeInMs(void);
PAL_API nsSinceEpoch
Hal_getTimeInNs(void);
/**
* Set the system time from ns time
*
* The time value returned as 64-bit unsigned integer should represent the nanoseconds
* since the UNIX epoch (1970/01/01 00:00 UTC).
*
* \return true on success, otherwise false
*/
PAL_API bool
Hal_setTimeInNs(nsSinceEpoch nsTime);

@ -106,7 +106,7 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
{
int result;
if ((self != NULL) && (self->maxHandle >= 0)) {
if ((self != NULL) && (self->maxHandle != INVALID_SOCKET)) {
struct timeval timeout;
timeout.tv_sec = timeoutMs / 1000;
@ -136,12 +136,14 @@ static int socketCount = 0;
void
Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count)
{
(void)count; /* not supported in windows socket API */
struct tcp_keepalive keepalive;
DWORD retVal=0;
keepalive.onoff = 1;
keepalive.keepalivetime = CONFIG_TCP_KEEPALIVE_IDLE * 1000;
keepalive.keepaliveinterval = CONFIG_TCP_KEEPALIVE_INTERVAL * 1000;
keepalive.keepalivetime = idleTime * 1000;
keepalive.keepaliveinterval = interval * 1000;
if (WSAIoctl(self->fd, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive),
NULL, 0, &retVal, NULL, NULL) == SOCKET_ERROR)
@ -192,7 +194,8 @@ prepareServerAddress(const char* address, int port, struct sockaddr_in* sockaddr
return true;
}
static bool wsaStartUp()
static bool
wsaStartUp(void)
{
if (wsaStartupCalled == false) {
int ec;
@ -213,7 +216,8 @@ static bool wsaStartUp()
return true;
}
static void wsaShutdown()
static void
wsaShutdown(void)
{
if (wsaStartupCalled) {
if (socketCount == 0) {
@ -286,13 +290,11 @@ ServerSocket_listen(ServerSocket self)
Socket
ServerSocket_accept(ServerSocket self)
{
int fd;
Socket conSocket = NULL;
fd = accept(self->fd, NULL, NULL);
SOCKET fd = accept(self->fd, NULL, NULL);
if (fd >= 0) {
if (fd != INVALID_SOCKET) {
conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket));
conSocket->fd = fd;
@ -339,7 +341,7 @@ TcpSocket_create()
if (wsaStartUp() == false)
return NULL;
int sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock != INVALID_SOCKET) {
self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
@ -623,9 +625,9 @@ UdpSocket_create()
{
UdpSocket self = NULL;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != -1) {
if (sock != INVALID_SOCKET) {
self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket));
self->fd = sock;
@ -697,11 +699,12 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in
}
int
UdpSocket_receiveFrom(UdpSocket self, char** address, int maxAddrSize, uint8_t* msg, int msgSize)
UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize)
{
struct sockaddr_in remoteAddress;
struct sockaddr_storage remoteAddress;
socklen_t structSize = sizeof(struct sockaddr_storage);
int result = recvfrom(self->fd, (char*) msg, msgSize, 0, NULL, NULL);
int result = recvfrom(self->fd, (char*) msg, msgSize, 0, (struct sockaddr*)&remoteAddress, &structSize);
if (result == 0) /* peer has closed socket */
return -1;
@ -713,5 +716,31 @@ UdpSocket_receiveFrom(UdpSocket self, char** address, int maxAddrSize, uint8_t*
return -1;
}
if (address) {
bool isIPv6;
char addrString[INET6_ADDRSTRLEN + 7];
int port;
if (remoteAddress.ss_family == AF_INET) {
struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress;
port = ntohs(ipv4Addr->sin_port);
inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN);
isIPv6 = false;
}
else if (remoteAddress.ss_family == AF_INET6) {
struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress;
port = ntohs(ipv6Addr->sin6_port);
inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN);
isIPv6 = true;
}
else
return result ;
if (isIPv6)
snprintf(address, maxAddrSize, "[%s]:%i", addrString, port);
else
snprintf(address, maxAddrSize, "%s:%i", addrString, port);
}
return result;
}

@ -28,43 +28,75 @@ struct sThread {
bool autodestroy;
};
typedef struct sSemaphore* mSemaphore;
struct sSemaphore
{
sem_t* sem;
};
Semaphore
Semaphore_create(int initialValue)
{
mSemaphore self = NULL;
char tmpname[] = {"/tmp/libiec61850.XXXXXX"};
mktemp(tmpname);
char* res = mktemp(tmpname);
Semaphore self = (Semaphore) sem_open(tmpname, O_CREAT, 0666, initialValue);
if (res) {
self = (mSemaphore) GLOBAL_CALLOC(1, sizeof(struct sSemaphore));
if (self == SEM_FAILED) {
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);
}
}
}
return self;
return (Semaphore)self;
}
/* Wait until semaphore value is more than zero. Then decrease the semaphore value. */
void
Semaphore_wait(Semaphore self)
{
sem_wait((sem_t*) self);
mSemaphore mSelf = (mSemaphore) self;
sem_wait(mSelf->sem);
}
void
Semaphore_post(Semaphore self)
{
sem_post((sem_t*) self);
mSemaphore mSelf = (mSemaphore) self;
sem_post(mSelf->sem);
}
void
Semaphore_destroy(Semaphore self)
{
sem_close(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);
GLOBAL_FREEMEM(mSelf);
}
}
Thread

@ -33,7 +33,7 @@ Hal_getTimeInMs()
FILETIME ft;
uint64_t now;
static const uint64_t DIFF_TO_UNIXTIME = 11644473600000LL;
static const uint64_t DIFF_TO_UNIXTIME = 11644473600000ULL;
GetSystemTimeAsFileTime(&ft);
@ -47,7 +47,7 @@ Hal_getTimeInNs()
{
FILETIME ft;
static const uint64_t DIFF_TO_UNIXTIME = 11644473600000000000LL;
static const uint64_t DIFF_TO_UNIXTIME = 11644473600000000000ULL;
GetSystemTimeAsFileTime(&ft);
@ -57,3 +57,21 @@ Hal_getTimeInNs()
return nsTime;
}
bool
Hal_setTimeInNs(nsSinceEpoch nsTime)
{
uint64_t t = (nsTime / 100ULL) + 116444736000000000ULL;
FILETIME ft;
ft.dwLowDateTime = (uint32_t)(t & 0xffffffff);
ft.dwHighDateTime = (uint32_t)(t >> 32);
SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st);
return SetSystemTime(&st);
}

@ -20,7 +20,7 @@ TARGET=POSIX
else ifeq ($(findstring MINGW,$(UNAME)), MINGW)
TARGET=WIN32
else ifeq ($(UNAME), Darwin)
TARGET=BSD
TARGET=MACOS
else ifeq ($(UNAME), FreeBSD)
TARGET=BSD
endif
@ -117,10 +117,14 @@ endif
else
ifeq ($(TARGET), BSD)
HAL_IMPL = BSD
else ifeq ($(TARGET), MACOS)
HAL_IMPL = MACOS
else
HAL_IMPL = POSIX
endif
LDLIBS = -lpthread
ifeq ($(TARGET), LINUX-MIPSEL)
@ -174,7 +178,11 @@ else
ifeq ($(TARGET), BSD)
DYN_LIB_NAME = $(LIB_OBJS_DIR)/libiec61850.dylib
else
ifeq ($(TARGET), MACOS)
DYN_LIB_NAME = $(LIB_OBJS_DIR)/libiec61850.dylib
else
DYN_LIB_NAME = $(LIB_OBJS_DIR)/libiec61850.so
endif
endif
endif

@ -0,0 +1,97 @@
#ifndef PYIEC61850_COMMANDTERMHANDLER_HPP
#define PYIEC61850_COMMANDTERMHANDLER_HPP
#include "eventHandler.hpp"
/*
* Abstract class for processing the received 'Command Termination' events.
*/
class CommandTermHandler: public EventHandler {
public:
virtual ~CommandTermHandler() {}
virtual void setReceivedData(void *i_data_p)
{
// copy the received data
ControlObjectClient *l_my_data_p = static_cast<ControlObjectClient*>(i_data_p);
_libiec61850_control_object_client = *l_my_data_p;
}
ControlObjectClient _libiec61850_control_object_client;
};
/*
* Class for the subscription to the 'Command Termination' events
*/
class CommandTermSubscriber: public EventSubscriber {
public:
CommandTermSubscriber(): EventSubscriber()
{
m_libiec61850_control_object_client = nullptr;
}
virtual ~CommandTermSubscriber() {}
virtual bool subscribe()
{
// preconditions
if (nullptr == m_libiec61850_control_object_client) {
fprintf(stderr, "CommandTermSubscriber::subscribe() failed: 'control object client' is null\n");
return false;
}
// install the libiec61850 callback:
// the 'function pointer' is the 'static' method of this class
ControlObjectClient_setCommandTerminationHandler(
m_libiec61850_control_object_client,
CommandTermSubscriber::triggerCommandTermHandler,
NULL);
std::string l_object_ref = ControlObjectClient_getObjectReference(m_libiec61850_control_object_client);
return (EventSubscriber::registerNewSubscriber(this, l_object_ref));
}
// Static method: it is the 'callback' for libiec61850 in C
static void triggerCommandTermHandler(void *parameter, ControlObjectClient connection)
{
PyThreadStateLock PyThreadLock;
// Preconditions
if (nullptr == connection) {
fprintf(stderr, "CommandTermSubscriber::triggerCommandTermHandler() failed: input object is null\n");
return;
}
// Search the appropriate 'EventSubscriber' object
std::string l_subscriber_id = ControlObjectClient_getObjectReference(connection);
EventSubscriber *l_registered_subscriber = EventSubscriber::findSubscriber(l_subscriber_id);
if (l_registered_subscriber) {
EventHandler *l_event_handler_p = l_registered_subscriber->getEventHandler();
if (l_event_handler_p) {
l_event_handler_p->setReceivedData(&connection);
l_event_handler_p->trigger();
}
else {
fprintf(stderr, "CommandTermSubscriber::triggerCommandTermHandler() failed: EventHandler is undefined\n");
}
}
else {
fprintf(stderr, "CommandTermSubscriber::triggerCommandTermHandler() failed: subscriber is not registered\n");
}
}
// Setters
void setLibiec61850ControlObjectClient(const ControlObjectClient &i_libiec61850_control_object_client)
{
m_libiec61850_control_object_client = i_libiec61850_control_object_client;
}
protected:
// Parameters
ControlObjectClient m_libiec61850_control_object_client;
};
#endif

@ -28,6 +28,7 @@ private:
class EventHandler {
public:
EventHandler() {}
virtual ~EventHandler() {}
virtual void setReceivedData(void *i_data_p) = 0;
virtual void trigger() = 0;
@ -36,13 +37,9 @@ class EventHandler {
class EventSubscriber {
public:
// TODO: use a map to store and find the instantiated EventSubscriber
static EventSubscriber* m_last_created_event_subscriber;
EventSubscriber(): _event_handler_p(nullptr)
{
m_last_created_event_subscriber = this;
// add python thread support
Py_Initialize();
PyEval_InitThreads();
@ -50,11 +47,11 @@ class EventSubscriber {
virtual ~EventSubscriber()
{
EventSubscriber::unregisterSubscriber(m_subscriber_id);
deleteEventHandler();
m_last_created_event_subscriber = nullptr;
}
virtual void subscribe() = 0;
virtual bool subscribe() = 0;
void deleteEventHandler()
{
@ -75,8 +72,59 @@ class EventSubscriber {
return _event_handler_p;
}
void setSubscriberId(const std::string &i_id)
{
m_subscriber_id = i_id;
}
protected:
static std::map<std::string, EventSubscriber*> m_subscriber_map;
static bool registerNewSubscriber(EventSubscriber *i_new_subscriber, const std::string &i_id)
{
// Preconditions
if (i_id.empty()) {
fprintf(stderr, "EventSubscriber::subscribe() failed: the subscriber id is empty\n");
return false;
}
if (m_subscriber_map.end() != m_subscriber_map.find(i_id)) {
fprintf(stderr, "EventSubscriber::subscribe() failed: the subscriber is already registered\n");
return false;
}
m_subscriber_map[i_id] = i_new_subscriber;
i_new_subscriber->setSubscriberId(i_id);
return true;
}
static EventSubscriber* findSubscriber(const std::string &i_id)
{
EventSubscriber *o_found_event_subscriber_p = nullptr;
std::map<std::string, EventSubscriber*>::iterator l_it = m_subscriber_map.find(i_id);
if (m_subscriber_map.end() != l_it) {
o_found_event_subscriber_p = l_it->second;
}
return o_found_event_subscriber_p;
}
static void unregisterSubscriber(const std::string &i_subscriber_id)
{
std::map<std::string, EventSubscriber*>::iterator l_it = m_subscriber_map.find(i_subscriber_id);
if (m_subscriber_map.end() != l_it) {
m_subscriber_map.erase(l_it);
}
else {
fprintf(stderr, "EventSubscriber::unregisterSubscriber() failed: '%s' is not registered\n");
}
}
private:
EventHandler *_event_handler_p;
std::string m_subscriber_id;
};
#endif

@ -22,13 +22,30 @@ class GooseHandler: public EventHandler {
class GooseSubscriberForPython: public EventSubscriber {
public:
GooseSubscriberForPython(): EventSubscriber()
{
m_libiec61850_goose_subscriber = nullptr;
}
virtual ~GooseSubscriberForPython() {}
virtual bool subscribe()
{
// preconditions
if (nullptr == m_libiec61850_goose_subscriber) {
fprintf(stderr, "GooseSubscriberForPython::subscribe() failed: 'GOOSE subscriber' is null\n");
return false;
}
virtual void subscribe() {
// install the libiec61850 callback:
// the 'function pointer' is the 'static' method of this class
GooseSubscriber_setListener(m_libiec61850_goose_subscriber,
GooseSubscriberForPython::triggerGooseHandler,
NULL);
std::string l_go_cb_ref = GooseSubscriber_getGoCbRef(m_libiec61850_goose_subscriber);
return (EventSubscriber::registerNewSubscriber(this, l_go_cb_ref));
}
// Static method: it is the 'callback' for libiec61850 in C
@ -36,16 +53,28 @@ class GooseSubscriberForPython: public EventSubscriber {
{
PyThreadStateLock PyThreadLock;
// TODO: search the appropriate 'EventSubscriber' object
if (m_last_created_event_subscriber) {
EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler();
// Preconditions
if (nullptr == subscriber) {
fprintf(stderr, "GooseSubscriberForPython::triggerGooseHandler() failed: input object is null\n");
return;
}
// Search the appropriate 'EventSubscriber' object
std::string l_subscriber_id = GooseSubscriber_getGoCbRef(subscriber);
EventSubscriber *l_registered_subscriber = EventSubscriber::findSubscriber(l_subscriber_id);
if (l_registered_subscriber) {
EventHandler *l_event_handler_p = l_registered_subscriber->getEventHandler();
if (l_event_handler_p) {
l_event_handler_p->setReceivedData(&subscriber);
l_event_handler_p->trigger();
}
else {
printf("The EventHandler is undefined\n");
fprintf(stderr, "GooseSubscriberForPython::triggerGooseHandler() failed: EventHandler is undefined\n");
}
}
else {
fprintf(stderr, "GooseSubscriberForPython::triggerGooseHandler() failed: subscriber is not registered\n");
}
}

@ -22,8 +22,20 @@ class RCBHandler: public EventHandler {
class RCBSubscriber: public EventSubscriber {
public:
RCBSubscriber(): EventSubscriber()
{
m_ied_connection = nullptr;
}
virtual ~RCBSubscriber() {}
virtual bool subscribe() {
// preconditions
if (nullptr == m_ied_connection) {
fprintf(stderr, "RCBSubscriber::subscribe() failed: 'IedConnection' is null\n");
return false;
}
virtual void subscribe() {
// install the libiec61850 callback:
// the 'function pointer' is the 'static' method of this class
IedConnection_installReportHandler(m_ied_connection,
@ -31,6 +43,8 @@ class RCBSubscriber: public EventSubscriber {
m_rcb_rpt_id.c_str(),
RCBSubscriber::triggerRCBHandler,
NULL);
return (EventSubscriber::registerNewSubscriber(this, m_rcb_reference));
}
// Static method: it is the 'callback' for libiec61850 in C
@ -38,17 +52,29 @@ class RCBSubscriber: public EventSubscriber {
{
PyThreadStateLock PyThreadLock;
// TODO: search the appropriate 'EventSubscriber' object
if (m_last_created_event_subscriber) {
EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler();
// Preconditions
if (nullptr == report) {
fprintf(stderr, "RCBSubscriber::triggerRCBHandler() failed: input object is null\n");
return;
}
// Search the appropriate 'EventSubscriber' object
std::string l_subscriber_id = ClientReport_getRcbReference(report);
EventSubscriber *l_registered_subscriber = EventSubscriber::findSubscriber(l_subscriber_id);
if (l_registered_subscriber) {
EventHandler *l_event_handler_p = l_registered_subscriber->getEventHandler();
if (l_event_handler_p) {
l_event_handler_p->setReceivedData(&report);
l_event_handler_p->trigger();
}
else {
printf("The EventHandler is undefined\n");
fprintf(stderr, "RCBSubscriber::triggerRCBHandler() failed: EventHandler is undefined\n");
}
}
else {
fprintf(stderr, "RCBSubscriber::triggerRCBHandler() failed: subscriber is not registered\n");
}
}
// Setters

@ -98,15 +98,18 @@ void GooseSubscriber_setDstMac(GooseSubscriber subscriber,
/* Event Handler section */
%feature("director") RCBHandler;
%feature("director") GooseHandler;
%feature("director") CommandTermHandler;
%{
#include "eventHandlers/eventHandler.hpp"
#include "eventHandlers/reportControlBlockHandler.hpp"
#include "eventHandlers/gooseHandler.hpp"
EventSubscriber* EventSubscriber::m_last_created_event_subscriber = nullptr;
#include "eventHandlers/commandTermHandler.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"
/* Goose Publisher section */
%{

@ -318,7 +318,14 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
while (element != NULL) {
MmsValue* dataSetEntry = (MmsValue*) element->data;
if (dataSetEntry) {
dataSetSize += MmsValue_encodeMmsData(dataSetEntry, NULL, 0, false);
}
else {
/* TODO encode MMS NULL */
if (DEBUG_GOOSE_PUBLISHER)
printf("GOOSE_PUBLISHER: NULL value in data set!\n");
}
element = LinkedList_getNext(element);
}
@ -384,7 +391,12 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
while (element != NULL) {
MmsValue* dataSetEntry = (MmsValue*) element->data;
if (dataSetEntry) {
bufPos = MmsValue_encodeMmsData(dataSetEntry, buffer, bufPos, true);
}
else {
/* TODO encode MMS NULL */
}
element = LinkedList_getNext(element);
}

@ -760,11 +760,13 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
matchingSubscriber->ndsCom = ndsCom;
matchingSubscriber->simulation = simulation;
if (matchingSubscriber->dataSetValuesSelfAllocated) {
/* when confRev changed replaced old data set */
if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev)) {
MmsValue_delete(matchingSubscriber->dataSetValues);
matchingSubscriber->dataSetValues = NULL;
}
}
matchingSubscriber->confRev = confRev;

@ -47,8 +47,10 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues)
self->timestamp = MmsValue_newUtcTime(0);
self->dataSetValues = dataSetValues;
if (dataSetValues != NULL)
if (dataSetValues)
self->dataSetValuesSelfAllocated = false;
else
self->dataSetValuesSelfAllocated = true;
memset(self->dstMac, 0xFF, 6);
self->dstMacSet = false;

@ -1,7 +1,7 @@
/*
* goose_subscriber.h
*
* Copyright 2013-2018 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -82,12 +82,27 @@ typedef void (*GooseListener)(GooseSubscriber subscriber, void* parameter);
LIB61850_API GooseSubscriber
GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues);
/**
* \brief Get the GoId value of the received GOOSE message
*
* \param self GooseSubscriber instance to operate on.
*/
LIB61850_API char*
GooseSubscriber_getGoId(GooseSubscriber self);
/**
* \brief Get the GOOSE Control Block reference value of the received GOOSE message
*
* \param self GooseSubscriber instance to operate on.
*/
LIB61850_API char*
GooseSubscriber_getGoCbRef(GooseSubscriber self);
/**
* \brief Get the DatSet value of the received GOOSE message
*
* \param self GooseSubscriber instance to operate on.
*/
LIB61850_API char*
GooseSubscriber_getDataSet(GooseSubscriber self);
@ -133,6 +148,14 @@ GooseSubscriber_isValid(GooseSubscriber self);
LIB61850_API GooseParseError
GooseSubscriber_getParseError(GooseSubscriber self);
/**
* \brief Destroy the GooseSubscriber instance
*
* Do not call this function when the GooseSubscriber instance was added to a GooseReceiver.
* The GooseReceiver will call the destructor when \ref GooseReceiver_destroy is called!
*
* \param self GooseSubscriber instance to operate on.
*/
LIB61850_API void
GooseSubscriber_destroy(GooseSubscriber self);
@ -146,14 +169,32 @@ GooseSubscriber_destroy(GooseSubscriber self);
LIB61850_API void
GooseSubscriber_setListener(GooseSubscriber self, GooseListener listener, void* parameter);
/**
* \brief Get the APPID value of the received GOOSE message
*
* \param self GooseSubscriber instance to operate on.
*/
LIB61850_API int32_t
GooseSubscriber_getAppId(GooseSubscriber self);
/**
* \brief Get the source MAC address of the received GOOSE message
*
* \param self GooseSubscriber instance to operate on.
* \param buffer buffer to store the MAC address (at least 6 byte)
*/
LIB61850_API void
GooseSubscriber_getSrcMac(GooseSubscriber self, uint8_t *buffer);
GooseSubscriber_getSrcMac(GooseSubscriber self, uint8_t* buffer);
/**
* \brief Get the destination MAC address of the received GOOSE message
*
* \param self GooseSubscriber instance to operate on.
* \param buffer buffer to store the MAC address (at least 6 byte)
*/
LIB61850_API void
GooseSubscriber_getDstMac(GooseSubscriber self, uint8_t *buffer);
GooseSubscriber_getDstMac(GooseSubscriber self, uint8_t* buffer);
/**
* \brief return the state number (stNum) of the last received GOOSE message.
*

@ -1285,6 +1285,12 @@ ControlObjectClient_getLastApplError(ControlObjectClient self)
return self->lastApplError;
}
void
ControlObjectClient_setCtlNum(ControlObjectClient self, uint8_t ctlNum)
{
self->ctlNum = ctlNum;
}
void
controlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self)
{

@ -569,6 +569,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo
if (singleRequest) {
LinkedList accessResults = NULL;
*error = IED_ERROR_OK;
MmsConnection_writeMultipleVariables(self->connection, &mmsError, domainId, itemIds, values, &accessResults);
if (accessResults != NULL) {
@ -577,8 +579,12 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo
while (element != NULL) {
MmsValue* accessResult = (MmsValue*) element->data;
MmsDataAccessError resErr = MmsValue_getDataAccessError(accessResult);
if (MmsValue_getDataAccessError(accessResult) != DATA_ACCESS_ERROR_SUCCESS) {
mmsError = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT;
*error = iedConnection_mapDataAccessErrorToIedError(resErr);
break;
}
@ -588,8 +594,6 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo
LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction) MmsValue_delete);
}
*error = iedConnection_mapMmsErrorToIedError(mmsError);
goto exit_function;
}
else {

@ -2171,10 +2171,11 @@ deleteFileAndSetFileHandler (uint32_t invokeId, void* parameter, MmsError mmsErr
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback;
handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(mmsError));
iedConnection_releaseOutstandingCall(self, call);
}
else {
if (DEBUG_IED_CLIENT)

@ -2231,6 +2231,12 @@ ControlObjectClient_enableInterlockCheck(ControlObjectClient self);
LIB61850_API DEPRECATED void
ControlObjectClient_enableSynchroCheck(ControlObjectClient self);
/**
* \deprecated Do not use (ctlNum is handled automatically by the library)! Intended for test purposes only.
*/
LIB61850_API DEPRECATED void
ControlObjectClient_setCtlNum(ControlObjectClient self, uint8_t ctlNum);
/**
* \brief Set the value of the interlock check flag when a control command is sent
*
@ -2255,7 +2261,12 @@ ControlObjectClient_setSynchroCheck(ControlObjectClient self, bool value);
*
* This callback is invoked whenever a CommandTermination+ or CommandTermination- message is received.
* To distinguish between a CommandTermination+ and CommandTermination- please use the
* ControlObjectClient_getLastApplError function.
* \ref ControlObjectClient_getLastApplError function.
*
* In case of CommandTermination+ the return value
* of \ref ControlObjectClient_getLastApplError has error=CONTROL_ERROR_NO_ERROR and
* addCause=ADD_CAUSE_UNKNOWN set. When addCause is different from ADD_CAUSE_UNKNOWN then the client
* received a CommandTermination- message.
*
* NOTE: Do not call \ref ControlObjectClient_destroy inside of this callback! Doing so will cause a dead-lock.
*
@ -2269,7 +2280,10 @@ typedef void (*CommandTerminationHandler) (void* parameter, ControlObjectClient
*
* This callback is invoked whenever a CommandTermination+ or CommandTermination- message is received.
* To distinguish between a CommandTermination+ and CommandTermination- please use the
* ControlObjectClient_getLastApplError function.
* \ref ControlObjectClient_getLastApplError function. In case of CommandTermination+ the return value
* of \ref ControlObjectClient_getLastApplError has error=CONTROL_ERROR_NO_ERROR and
* addCause=ADD_CAUSE_UNKNOWN set. When addCause is different from ADD_CAUSE_UNKNOWN then the client
* received a CommandTermination- message.
*
* \param self the ControlObjectClient instance
* \param handler the callback function to be used

@ -31,6 +31,7 @@ extern "C" {
#include "libiec61850_common_api.h"
#include "logging_api.h"
#include "linked_list.h"
/**
* @defgroup iec61850_common_api_group IEC 61850 API common parts
@ -107,9 +108,6 @@ typedef enum {
/** Report will be triggered by GI (general interrogation) request */
#define TRG_OPT_GI 16
/** RCB has the owner attribute */
#define RPT_OPT_HAS_OWNER 64
/** Report will be triggered only on rising edge (transient variable */
#define TRG_OPT_TRANSIENT 128
/** @} */

@ -58,7 +58,7 @@ IedModel_create(const char* name/*, MemoryAllocator allocator*/);
/**
* \brief Set the name of the IED (use only for dynamic model!)
*
* This will change the default name (usualy "TEMPLATE") to a user configured values.
* This will change the default name (usually "TEMPLATE") to a user configured values.
* NOTE: This function has to be called before IedServer_create !
* NOTE: For dynamic model (and configuration file date model) this function has to be
* used instead of IedModel_setIedName.
@ -121,12 +121,12 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements);
/**
* \brief create a new data attribute and add it to a parent model node
*
* The parent model node has to be of type LogicalNode or DataObject
* The parent model node has to be of type DataObject or DataAttribute
*
* \param name the name of the data attribute (e.g. "stVal")
* \param parent the parent model node
* \param type the type of the data attribute (CONSTRUCTED if the type contains sub data attributes)
* \param fc the functional constraint (FC) of the data attribte
* \param fc the functional constraint (FC) of the data attribute
* \param triggerOptions the trigger options (dupd, dchg, qchg) that cause an event notification
* \param arrayElements the number of array elements if the data attribute is an array or 0
* \param sAddr an optional short address
@ -137,6 +137,45 @@ LIB61850_API DataAttribute*
DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type, FunctionalConstraint fc,
uint8_t triggerOptions, int arrayElements, uint32_t sAddr);
/**
* \brief Get the data type of the data attribute
*
* \param self the data attribute instance
*
* \return the data attribute type
*/
LIB61850_API DataAttributeType
DataAttribute_getType(DataAttribute* self);
/**
* \brief Get the functional constraint (FC) of the data attribute
*
* \param self the data attribute instance
*
* \return the functional constraint (FC) of the data attribute
*/
LIB61850_API FunctionalConstraint
DataAttribute_getFC(DataAttribute* self);
/**
* \brief Get the trigger options of the data attribute
*
* \param self the data attribute instance
*
* \return the trigger options (dupd, dchg, qchg) that cause an event notification
*/
LIB61850_API uint8_t
DataAttribute_getTrgOps(DataAttribute* self);
/**
* \brief Set the value of the data attribute (can be used to set default values before server is created)
*
* \param self the data attribute instance
* \param value the new default value
*/
LIB61850_API void
DataAttribute_setValue(DataAttribute* self, MmsValue* value);
/**
* \brief create a new report control block (RCB)
*
@ -290,6 +329,16 @@ PhyComAddress_create(uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint
LIB61850_API DataSet*
DataSet_create(const char* name, LogicalNode* parent);
/**
* \brief Get the name of the data set
*
* \param self the instance of the data set
*
* \returns the name of the data set (not the object reference).
*/
LIB61850_API const char*
DataSet_getName(DataSet* self);
/**
* \brief returns the number of elements (entries) of the data set
*

@ -400,6 +400,19 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint
LIB61850_API char*
ModelNode_getObjectReference(ModelNode* self, char* objectReference);
/**
* \brief Return the IEC 61850 object reference of a model node
*
* \param self the model node instance
* \param objectReference pointer to a buffer where to write the object reference string. If NULL
* is given the buffer is allocated by the function.
* \param withoutIedName create object reference without IED name part
*
* \return the object reference string
*/
LIB61850_API char*
ModelNode_getObjectReferenceEx(ModelNode* node, char* objectReference, bool withoutIedName);
/**
* \brief Get the type of the ModelNode
*
@ -410,6 +423,36 @@ ModelNode_getObjectReference(ModelNode* self, char* objectReference);
LIB61850_API ModelNodeType
ModelNode_getType(ModelNode* self);
/**
* \brief Get the name of the ModelNode
*
* \param self the ModelNode instance
*
* \return the name of the ModelNode
*/
LIB61850_API const char*
ModelNode_getName(ModelNode* self);
/**
* \brief Get the parent ModelNode of this ModelNode instance
*
* \param self the ModelNode instance
*
* \return the parent instance, or NULL when the ModelNode has no parent
*/
LIB61850_API ModelNode*
ModelNode_getParent(ModelNode* self);
/**
* \brief Get the list of direct child nodes
*
* \param self the ModelNode instance
*
* \return the list of private child nodes, or NULL when the node has no children
*/
LIB61850_API LinkedList
ModelNode_getChildren(ModelNode* self);
/**
* \brief Set the name of the IED
*

@ -39,6 +39,7 @@ extern "C" {
#include "iec61850_dynamic_model.h"
#include "iec61850_model.h"
#include "hal_filesystem.h"
#include "iso_connection_parameters.h"
#include "iec61850_config_file_parser.h"
/**
@ -89,6 +90,12 @@ struct sIedServerConfig
/** enable visibility of SGCB.ResvTms (default: true) */
bool enableResvTmsForSGCB;
/** BRCB has resvTms attribute - only edition 2 (default: true) */
bool enableResvTmsForBRCB;
/** RCB has owner attribute (default: true) */
bool enableOwnerForRCB;
};
/**
@ -304,6 +311,38 @@ IedServerConfig_enableEditSG(IedServerConfig self, bool enable);
LIB61850_API void
IedServerConfig_enableResvTmsForSGCB(IedServerConfig self, bool enable);
/**
* \brief Enable/disable the presence of BRCB.ResvTms (default value is true)
*
* \param[in] enable set true to enable, otherwise false
*/
LIB61850_API void
IedServerConfig_enableResvTmsForBRCB(IedServerConfig self, bool enable);
/**
* \brief ResvTms for BRCB enabled (visible)
*
* \return true if enabled, false otherwise
*/
LIB61850_API bool
IedServerConfig_isResvTmsForBRCBEnabled(IedServerConfig self);
/**
* \brief Enable/disable the presence of owner in report control blocks (default value is false);
*
* \param[in] enable set true to enable, otherwise false
*/
LIB61850_API void
IedServerConfig_enableOwnerForRCB(IedServerConfig self, bool enable);
/**
* \brief Owner for RCBs enabled (visible)
*
* \return true if enabled, false otherwise
*/
LIB61850_API bool
IedServerConfig_isOwnerForRCBEnabled(IedServerConfig self);
/**
* \brief Enable/disable using the integrated GOOSE publisher for configured GoCBs
*
@ -598,7 +637,7 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId);
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on.
* \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs
* \param ln the logical node that contains the GCB or NULL to set the ethernet interface ID for all GCBs
* \param gcbName the name (not object reference!) of the GCB
* \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing
*/
@ -1545,6 +1584,16 @@ typedef struct sMmsGooseControlBlock* MmsGooseControlBlock;
typedef void (*GoCBEventHandler) (MmsGooseControlBlock goCb, int event, void* parameter);
/**
* \brief Set a callback handler for GoCB events (enabled/disabled)
*
* The callback handler is called whenever a GOOSE control block is enabled or disabled.
* It can be used to integrate the external GOOSE publisher with the IEC 61850/MMS server.
*
* \param self the instance of IedServer to operate on.
* \param handler the callback handler
* \param parameter user provided parameter that is passed to the callback handler
*/
LIB61850_API void
IedServer_setGoCBHandler(IedServer self, GoCBEventHandler handler, void* parameter);
@ -1569,6 +1618,9 @@ MmsGooseControlBlock_getMaxTime(MmsGooseControlBlock self);
LIB61850_API bool
MmsGooseControlBlock_getFixedOffs(MmsGooseControlBlock self);
LIB61850_API bool
MmsGooseControlBlock_getNdsCom(MmsGooseControlBlock self);
/**@}*/
/**
@ -1615,6 +1667,10 @@ typedef MmsDataAccessError
* or denied. If a WriteAccessHandler is set for a specific data attribute - the
* default write access policy will not be performed for that data attribute.
*
* NOTE: If the data attribute has sub data attributes, the WriteAccessHandler is not
* set for the sub data attributes and will not be called when the sub data attribute is
* written directly!
*
* \param self the instance of IedServer to operate on.
* \param dataAttribute the data attribute to monitor
* \param handler the callback function that is invoked if a client tries to write to
@ -1625,6 +1681,29 @@ LIB61850_API void
IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute,
WriteAccessHandler handler, void* parameter);
/**
* \brief Install a WriteAccessHandler for a data attribute and for all sub data attributes
*
* This instructs the server to monitor write attempts by MMS clients to specific
* data attributes. If a client tries to write to the monitored data attribute the
* handler is invoked. The handler can decide if the write access will be allowed
* or denied. If a WriteAccessHandler is set for a specific data attribute - the
* default write access policy will not be performed for that data attribute.
*
* When the data attribute is a complex attribute then the handler will also be installed
* for all sub data attributes. When the data attribute is a basic data attribute then
* this function behaves like \ref IedServer_handleWriteAccess.
*
* \param self the instance of IedServer to operate on.
* \param dataAttribute the data attribute to monitor
* \param handler the callback function that is invoked if a client tries to write to
* the monitored data attribute.
* \param parameter a user provided parameter that is passed to the WriteAccessHandler when called.
*/
LIB61850_API void
IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* dataAttribute,
WriteAccessHandler handler, void* parameter);
typedef enum {
ACCESS_POLICY_ALLOW,
ACCESS_POLICY_DENY

@ -47,6 +47,8 @@ struct sIedServer
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
int reportBufferSizeBRCBs;
int reportBufferSizeURCBs;
bool enableBRCBResvTms;
bool enableOwnerForRCB;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)

@ -81,6 +81,9 @@ LogInstance_create(LogicalNode* parentLN, const char* name);
LIB61850_INTERNAL void
LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage);
LIB61850_INTERNAL void
LogInstance_updateStatus(LogInstance* self);
LIB61850_INTERNAL void
LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* value, uint8_t flag);

@ -55,7 +55,7 @@ LIB61850_INTERNAL bool
MmsGooseControlBlock_isEnabled(MmsGooseControlBlock self);
LIB61850_INTERNAL void
MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t currentTime);
MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t currentTime, MmsMapping* mapping);
LIB61850_INTERNAL void
MmsGooseControlBlock_setStateChangePending(MmsGooseControlBlock self);
@ -63,7 +63,7 @@ MmsGooseControlBlock_setStateChangePending(MmsGooseControlBlock self);
LIB61850_INTERNAL void
MmsGooseControlBlock_publishNewState(MmsGooseControlBlock self);
LIB61850_INTERNAL void
LIB61850_INTERNAL bool
MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping);
LIB61850_INTERNAL void

@ -252,10 +252,13 @@ installDefaultValuesForDataAttribute(IedServer self, LogicalDevice* ld, DataAttr
if (ld->ldName == NULL) {
strncpy(domainName, self->model->name, 64);
MmsMapping_getMmsDomainFromObjectReference(objectReference, domainName + strlen(domainName));
domainName[64] = 0;
}
else {
strncpy(domainName, ld->ldName, 64);
domainName[64] = 0;
}
MmsMapping_getMmsDomainFromObjectReference(objectReference, domainName + strlen(domainName));
MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName);
@ -477,7 +480,6 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer));
if (self) {
self->model = dataModel;
self->running = false;
@ -508,10 +510,18 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
if (serverConfiguration) {
self->reportBufferSizeBRCBs = serverConfiguration->reportBufferSize;
self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs;
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB;
}
else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
self->enableBRCBResvTms = true;
#else
self->enableBRCBResvTms = false;
#endif
}
#endif
@ -528,6 +538,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->mmsMapping = MmsMapping_create(dataModel, self);
if (self->mmsMapping) {
self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping);
self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration);
@ -576,8 +588,13 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
if (serverConfiguration) {
MmsMapping_useIntegratedGoosePublisher(self->mmsMapping, serverConfiguration->useIntegratedGoosePublisher);
}
#endif
}
else {
IedServer_destroy(self);
self = NULL;
}
}
return self;
@ -620,6 +637,7 @@ IedServer_destroy(IedServer self)
if (self->localIpAddress != NULL)
GLOBAL_FREEMEM(self->localIpAddress);
if (self->mmsMapping)
MmsMapping_destroy(self->mmsMapping);
LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy);
@ -1538,6 +1556,26 @@ IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute, WriteA
}
}
void
IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter)
{
if (dataAttribute == NULL) {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: IedServer_handleWriteAccessForComplexAttribute - dataAttribute == NULL!\n");
}
else {
MmsMapping_installWriteAccessHandler(self->mmsMapping, dataAttribute, handler, parameter);
DataAttribute* subDa = (DataAttribute*) dataAttribute->firstChild;
while (subDa) {
IedServer_handleWriteAccessForComplexAttribute(self, subDa, handler, parameter);
subDa = (DataAttribute*) subDa->sibling;
}
}
}
void
IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter)
{
@ -1668,6 +1706,10 @@ IedServer_setLogStorage(IedServer self, const char* logRef, LogStorage logStorag
{
#if (CONFIG_IEC61850_LOG_SERVICE == 1)
MmsMapping_setLogStorage(self->mmsMapping, logRef, logStorage);
#else
(void)self;
(void)logRef;
(void)logStorage;
#endif
}
@ -1685,8 +1727,13 @@ IedServer_setServerIdentity(IedServer self, const char* vendor, const char* mode
if (self->revision)
GLOBAL_FREEMEM(self->revision);
if (vendor)
self->vendorName = StringUtils_copyString(vendor);
if (model)
self->modelName = StringUtils_copyString(model);
if (revision)
self->revision = StringUtils_copyString(revision);
MmsServer_setServerIdentity(self->mmsServer, self->vendorName, self->modelName, self->revision);

@ -55,6 +55,8 @@ IedServerConfig_create()
self->edition = IEC_61850_EDITION_2;
self->maxMmsConnections = 5;
self->enableEditSG = true;
self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false;
}
return self;
@ -199,6 +201,30 @@ IedServerConfig_enableResvTmsForSGCB(IedServerConfig self, bool enable)
self->enableResvTmsForSGCB = enable;
}
void
IedServerConfig_enableResvTmsForBRCB(IedServerConfig self, bool enable)
{
self->enableResvTmsForBRCB = enable;
}
bool
IedServerConfig_isResvTmsForBRCBEnabled(IedServerConfig self)
{
return self->enableResvTmsForBRCB;
}
void
IedServerConfig_enableOwnerForRCB(IedServerConfig self, bool enable)
{
self->enableOwnerForRCB = enable;
}
bool
IedServerConfig_isOwnerForRCBEnabled(IedServerConfig self)
{
return self->enableOwnerForRCB;
}
void
IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable)
{

@ -1,7 +1,7 @@
/*
* control.c
*
* Copyright 2013-2020 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -39,13 +39,13 @@
#define DEBUG_IED_SERVER 0
#endif
#define STATE_UNSELECTED 0
#define STATE_READY 1
#define STATE_WAIT_FOR_ACTIVATION_TIME 2
#define STATE_PERFORM_TEST 3
#define STATE_WAIT_FOR_EXECUTION 4
#define STATE_OPERATE 5
#define STATE_WAIT_FOR_SELECT 6
#define STATE_UNSELECTED 0 /* idle state for SBO controls */
#define STATE_READY 1 /* idle state for direct controls, or selected state for SBO controls */
#define STATE_WAIT_FOR_ACTIVATION_TIME 2 /* time activated control is waiting for execution time */
#define STATE_PERFORM_TEST 3 /* waiting for application to perform tests */
#define STATE_WAIT_FOR_EXECUTION 4 /* control is scheduled and waiting for execution */
#define STATE_OPERATE 5 /* waiting for application to execute the command */
#define STATE_WAIT_FOR_SELECT 6 /* waiting for application to perform/confirm selection */
#define PENDING_EVENT_SELECTED 1
#define PENDING_EVENT_UNSELECTED 2
@ -953,7 +953,6 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char*
printf("IED_SERVER: control object %s/%s.%s has no ctlVal element!\n", domain->domainName, lnName, name);
}
MmsVariableSpecification* originSpec = MmsVariableSpecification_getChildSpecificationByName(operSpec, "origin", NULL);
if (originSpec) {
@ -1400,7 +1399,7 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
controlObject->errorValue = CONTROL_ERROR_NO_ERROR;
controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_INTERLOCKING;
checkResult = controlObject->checkHandler((ControlAction) self,
checkResult = controlObject->checkHandler((ControlAction) controlObject,
controlObject->checkHandlerParameter, controlObject->ctlVal, controlObject->testMode,
controlObject->interlockCheck);
}
@ -1828,9 +1827,14 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
ControlObject_getTypeSpec(controlObject), varName);
}
}
else
else {
value = ControlObject_getMmsValue(controlObject);
}
}
else {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: Control object not found %s/%s.%s\n", domain->domainName, lnName, objectName);
}
return value;
}
@ -1984,20 +1988,37 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
goto free_and_return;
}
int state = getState(controlObject);
uint64_t currentTime = Hal_getTimeInMs();
checkSelectTimeout(controlObject, currentTime);
int state = getState(controlObject);
if (state != STATE_UNSELECTED) {
if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
ControlObject_sendLastApplError(controlObject, connection, "SBOw",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION,
ctlNum, origin, true);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: SBOw - select failed - already in execution!\n");
goto free_and_return;
}
else {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: SBOw - select failed!\n");
printf("IED_SERVER: SBOw - select failed - already selected!\n");
goto free_and_return;
}
}
else {
@ -2122,8 +2143,6 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
bool testCondition = MmsValue_getBoolean(test);
controlObject->testMode = testCondition;
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
if (controlObject->mmsConnection != connection) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
@ -2141,7 +2160,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
MmsValue_equals(origin, controlObject->origin) &&
MmsValue_equals(ctlNum, controlObject->ctlNum) &&
(controlObject->interlockCheck == interlockCheck) &&
(controlObject->synchroCheck == synchroCheck)
(controlObject->synchroCheck == synchroCheck) &&
(controlObject->testMode == testCondition)
) == false)
{
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
@ -2157,6 +2177,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
}
}
controlObject->testMode = testCondition;
updateControlParameters(controlObject, ctlVal, ctlNum, origin, synchroCheck, interlockCheck);
MmsValue* operTm = getOperParameterOperTime(value);
@ -2164,7 +2186,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (operTm != NULL) {
controlObject->operateTime = MmsValue_getUtcTimeInMs(operTm);
if (controlObject->operateTime != 0) {
if (controlObject->operateTime > currentTime) {
controlObject->timeActivatedOperate = true;
controlObject->synchroCheck = synchroCheck;
controlObject->interlockCheck = interlockCheck;
@ -2206,6 +2228,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
/* enter state Perform Test */
setOpRcvd(controlObject, true);
controlObject->errorValue = CONTROL_ERROR_NO_ERROR;
controlObject->addCauseValue = ADD_CAUSE_UNKNOWN;
controlObject->mmsConnection = connection;
@ -2236,6 +2259,12 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
if ((controlObject->ctlModel == 3) || (controlObject->ctlModel == 4)) {
ControlObject_sendLastApplError(controlObject, connection, "Oper",
controlObject->errorValue, controlObject->addCauseValue,
ctlNum, origin, true);
}
}
}
@ -2270,11 +2299,11 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
serviceType = IEC61850_SERVICE_TYPE_CANCEL;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: control received cancel!\n");
int state = getState(controlObject);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: control received cancel (state: %i)!\n", state);
MmsValue* ctlNum = getCancelParameterCtlNum(value);
MmsValue* origin = getCancelParameterOrigin(value);
@ -2285,6 +2314,16 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
goto free_and_return;
}
if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
ControlObject_sendLastApplError(controlObject, connection, "Cancel",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION,
ctlNum, origin, true);
goto free_and_return;
}
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
if (state != STATE_UNSELECTED) {
if (controlObject->mmsConnection == connection) {

@ -163,13 +163,21 @@ LogInstance_logEntryFinished(LogInstance* self, uint64_t entryID)
self->locked = false;
}
void
LogInstance_updateStatus(LogInstance* self)
{
if (self->logStorage) {
LogStorage_getOldestAndNewestEntries(self->logStorage, &(self->newEntryId), &(self->newEntryTime),
&(self->oldEntryId), &(self->oldEntryTime));
}
}
void
LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage)
{
self->logStorage = logStorage;
LogStorage_getOldestAndNewestEntries(logStorage, &(self->newEntryId), &(self->newEntryTime),
&(self->oldEntryId), &(self->oldEntryTime));
LogInstance_updateStatus(self);
}
LogControl*
@ -357,6 +365,9 @@ updateLogStatusInLCB(LogControl* self)
LogInstance* logInstance = self->logInstance;
if (logInstance != NULL) {
LogInstance_updateStatus(logInstance);
MmsValue_setBinaryTime(self->oldEntrTm, logInstance->oldEntryTime);
MmsValue_setBinaryTime(self->newEntrTm, logInstance->newEntryTime);

@ -1,7 +1,7 @@
/*
* mms_goose.c
*
* Copyright 2013-2020 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -25,6 +25,8 @@
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
#define GOOSE_MAX_MESSAGE_SIZE 1518
#include "libiec61850_platform_includes.h"
#include "mms_mapping.h"
#include "linked_list.h"
@ -80,6 +82,35 @@ struct sMmsGooseControlBlock {
bool stateChangePending;
};
static void
setNdsCom(MmsGooseControlBlock mmsGCB, bool value)
{
MmsValue* ndsComValue = MmsValue_getElement(mmsGCB->mmsValue, 4);
if (ndsComValue) {
MmsValue_setBoolean(ndsComValue, value);
}
}
static bool
getNdsCom(MmsGooseControlBlock mmsGCB)
{
bool ndsCom = true;
MmsValue* ndsComValue = MmsValue_getElement(mmsGCB->mmsValue, 4);
if (ndsComValue)
ndsCom = MmsValue_getBoolean(ndsComValue);
return ndsCom;
}
bool
MmsGooseControlBlock_getNdsCom(MmsGooseControlBlock self)
{
return getNdsCom(self);
}
bool
MmsGooseControlBlock_getGoEna(MmsGooseControlBlock self)
{
@ -341,9 +372,27 @@ MmsGooseControlBlock_isEnabled(MmsGooseControlBlock self)
return self->goEna;
}
void
static int
calculateMaxDataSetSize(DataSet* dataSet)
{
int dataSetSize = 0;
DataSetEntry* dataSetEntry = dataSet->fcdas;
while (dataSetEntry) {
dataSetSize += MmsValue_getMaxEncodedSize(dataSetEntry->value);
dataSetEntry = dataSetEntry->sibling;
}
return dataSetSize;
}
bool
MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
{
bool retVal = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex);
#endif
@ -388,6 +437,28 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
if (self->dataSet != NULL) {
int dataSetSize = calculateMaxDataSetSize(self->dataSet);
/* Calculate maximum GOOSE message size */
int maxGooseMessageSize = 26 + 51 + 6;
maxGooseMessageSize += strlen(self->goCBRef);
if (self->goId)
maxGooseMessageSize += strlen(self->goId);
else
maxGooseMessageSize += strlen(self->goCBRef);
maxGooseMessageSize += strlen(self->dataSetRef);
maxGooseMessageSize += dataSetSize;
if (maxGooseMessageSize > GOOSE_MAX_MESSAGE_SIZE) {
setNdsCom(self, true);
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
copyGCBValuesToTrackingObject(self);
updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
}
else {
MmsValue* goEna = MmsValue_getElement(self->mmsValue, 0);
MmsValue_setBoolean(goEna, true);
@ -451,22 +522,27 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
self->goEna = true;
retVal = true;
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS;
copyGCBValuesToTrackingObject(self);
updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, retVal);
updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, DATA_ACCESS_ERROR_SUCCESS);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
}
}
}
else {
if (DEBUG_IED_SERVER)
printf("GoCB already enabled!\n");
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex);
#endif
return retVal;
}
void
@ -506,11 +582,15 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
void
MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t currentTime)
MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t currentTime, MmsMapping* mapping)
{
if (self->publisher) {
if (currentTime >= self->nextPublishTime) {
IedServer_lockDataModel(mapping->iedServer);
if (currentTime >= self->nextPublishTime) {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex);
#endif
@ -538,6 +618,10 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current
Semaphore_post(self->publisherMutex);
#endif
}
IedServer_unlockDataModel(mapping->iedServer);
}
else if ((self->nextPublishTime - currentTime) > ((uint32_t) self->maxTime * 2)) {
self->nextPublishTime = currentTime + self->minTime;
}
@ -746,11 +830,13 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
mmsGCB->goId = StringUtils_copyString(gooseControlBlock->appId);
}
if (gooseControlBlock->dataSetName != NULL)
if ((gooseControlBlock->dataSetName != NULL) && (gooseControlBlock->dataSetName[0] != 0)) {
mmsGCB->dataSetRef = createDataSetReference(MmsDomain_getName(domain),
logicalNode->name, gooseControlBlock->dataSetName);
else
}
else {
mmsGCB->dataSetRef = NULL;
}
MmsValue* dataSetRef = MmsValue_getElement(gseValues, 2);
@ -815,6 +901,11 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
MmsValue* maxTime = MmsValue_getElement(gseValues, 7);
MmsValue_setUint32(maxTime, mmsGCB->maxTime);
if (mmsGCB->dataSetRef)
setNdsCom(mmsGCB, false);
else
setNdsCom(mmsGCB, true);
mmsGCB->mmsMapping = self;
mmsGCB->stateChangePending = false;

@ -1,7 +1,7 @@
/*
* mms_mapping.c
*
* Copyright 2013-2019 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -763,7 +763,9 @@ MmsMapping_configureSettingGroups(MmsMapping* self)
void
MmsMapping_useIntegratedGoosePublisher(MmsMapping* self, bool enable)
{
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
self->useIntegratedPublisher = enable;
#endif
}
void
@ -1710,8 +1712,6 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
}
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
if (LogicalNode_hasFCData(logicalNode, IEC61850_FC_SV)) {
namedVariable->typeSpec.structure.elements[currentComponent] =
createFCNamedVariable(logicalNode, IEC61850_FC_SV);
@ -1763,7 +1763,6 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
currentComponent++;
}
@ -1792,9 +1791,15 @@ createMmsDomainFromIedDevice(MmsMapping* self, LogicalDevice* logicalDevice)
if (logicalDevice->ldName == NULL) {
int modelNameLength = strlen(self->model->name);
int ldInstName = strlen(logicalDevice->name);
if ((modelNameLength + ldInstName) > 64) {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: Resulting domain name (IEDName+LDInst) too long (%i)\n", modelNameLength + ldInstName);
if (modelNameLength > 64)
goto exit_function;
}
strncpy(domainName, self->model->name, 64);
strncat(domainName, logicalDevice->name, 64 - modelNameLength);
@ -1876,22 +1881,29 @@ exit_function:
return domain;
}
static void
static bool
createMmsDataModel(MmsMapping* self, int iedDeviceCount,
MmsDevice* mmsDevice, IedModel* iedModel)
{
mmsDevice->domains = (MmsDomain**) GLOBAL_MALLOC((iedDeviceCount) * sizeof(MmsDomain*));
mmsDevice->domains = (MmsDomain**) GLOBAL_CALLOC(1, (iedDeviceCount) * sizeof(MmsDomain*));
mmsDevice->domainCount = iedDeviceCount;
LogicalDevice* logicalDevice = iedModel->firstChild;
int i = 0;
while (logicalDevice != NULL) {
mmsDevice->domains[i] = createMmsDomainFromIedDevice(self,
logicalDevice);
mmsDevice->domains[i] = createMmsDomainFromIedDevice(self, logicalDevice);
if (mmsDevice->domains[i] == NULL) {
mmsDevice->domainCount = i;
return false;
}
i++;
logicalDevice = (LogicalDevice*) logicalDevice->sibling;
}
return true;
}
static void
@ -1997,10 +2009,14 @@ createMmsModelFromIedModel(MmsMapping* self, IedModel* iedModel)
int iedDeviceCount = IedModel_getLogicalDeviceCount(iedModel);
createMmsDataModel(self, iedDeviceCount, mmsDevice, iedModel);
if (createMmsDataModel(self, iedDeviceCount, mmsDevice, iedModel)) {
createDataSets(mmsDevice, iedModel);
}
else {
MmsDevice_destroy(mmsDevice);
mmsDevice = NULL;
}
}
return mmsDevice;
}
@ -2050,6 +2066,11 @@ MmsMapping_create(IedModel* model, IedServer iedServer)
/* create data model specification */
self->mmsDevice = createMmsModelFromIedModel(self, model);
if (self->mmsDevice == false) {
MmsMapping_destroy(self);
self = NULL;
}
return self;
}
@ -2064,7 +2085,7 @@ MmsMapping_destroy(MmsMapping* self)
}
#endif
if (self->mmsDevice != NULL)
if (self->mmsDevice)
MmsDevice_destroy(self->mmsDevice);
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
@ -2313,17 +2334,23 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable
if (MmsValue_getType(value) != MMS_BOOLEAN)
return DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
if (MmsValue_getBoolean(value)) {
MmsGooseControlBlock_enable(mmsGCB, self);
if (MmsGooseControlBlock_getNdsCom(mmsGCB))
return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
if (MmsValue_getBoolean(value)) {
if (MmsGooseControlBlock_enable(mmsGCB, self)) {
if (self->goCbHandler)
self->goCbHandler(mmsGCB, IEC61850_GOCB_EVENT_ENABLE, self->goCbHandlerParameter);
}
else {
return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
}
}
else {
MmsGooseControlBlock_disable(mmsGCB, self);
if (self->goCbHandler)
self->goCbHandler(mmsGCB, IEC61850_GOCB_EVENT_ENABLE, self->goCbHandlerParameter);
self->goCbHandler(mmsGCB, IEC61850_GOCB_EVENT_DISABLE, self->goCbHandlerParameter);
}
return DATA_ACCESS_ERROR_SUCCESS;
@ -2411,6 +2438,7 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
#if 0
static MmsValue*
checkIfValueBelongsToModelNode(DataAttribute* dataAttribute, MmsValue* value, MmsValue* newValue)
{
@ -2445,6 +2473,7 @@ checkIfValueBelongsToModelNode(DataAttribute* dataAttribute, MmsValue* value, Mm
return NULL;
}
#endif
static FunctionalConstraint
getFunctionalConstraintForWritableNode(char* separator)
@ -2850,31 +2879,6 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
AttributeAccessHandler* accessHandler = (AttributeAccessHandler*) writeHandlerListElement->data;
DataAttribute* dataAttribute = accessHandler->attribute;
if (nodeAccessPolicy == ACCESS_POLICY_ALLOW) {
MmsValue* matchingValue = checkIfValueBelongsToModelNode(dataAttribute, cachedValue, value);
if (matchingValue != NULL) {
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
MmsDataAccessError handlerResult =
accessHandler->handler(dataAttribute, matchingValue, clientConnection,
accessHandler->parameter);
if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) {
handlerFound = true;
if (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)
updateValue = false;
}
else
return handlerResult;
}
}
else { /* if ACCESS_POLICY_DENY only allow direct access to handled data attribute */
if (dataAttribute->mmsValue == cachedValue) {
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
@ -2895,7 +2899,6 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
else
return handlerResult;
}
}
writeHandlerListElement = LinkedList_getNext(writeHandlerListElement);
}
@ -3679,7 +3682,6 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
}
if (DataSet_isMemberValue(rc->dataSet, value, &index)) {
ReportControl_valueUpdated(rc, index, flag, modelLocked);
}
}
@ -3721,15 +3723,18 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
void
MmsMapping_enableGoosePublishing(MmsMapping* self)
{
LinkedList element = LinkedList_getNext(self->gseControls);
LinkedList element = self->gseControls;
while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
while (element) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) LinkedList_getData(element);
MmsGooseControlBlock_enable(gcb, self);
if (MmsGooseControlBlock_enable(gcb, self) == false) {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: failed to enable GoCB %s\n", MmsGooseControlBlock_getName(gcb));
}
element = LinkedList_getNext(element);
}
}
void
@ -3809,7 +3814,7 @@ GOOSE_processGooseEvents(MmsMapping* self, uint64_t currentTimeInMs)
MmsGooseControlBlock mmsGCB = (MmsGooseControlBlock) element->data;
if (MmsGooseControlBlock_isEnabled(mmsGCB)) {
MmsGooseControlBlock_checkAndPublish(mmsGCB, currentTimeInMs);
MmsGooseControlBlock_checkAndPublish(mmsGCB, currentTimeInMs, self);
}
element = LinkedList_getNext(element);

@ -269,10 +269,16 @@ ReportControl_getRCBValue(ReportControl* rc, char* elementName)
if (rc->server->edition >= IEC_61850_EDITION_2) {
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
if (rc->server->enableBRCBResvTms) {
if (strcmp(elementName, "ResvTms") == 0)
return MmsValue_getElement(rc->rcbValues, 13);
if (strcmp(elementName, "Owner") == 0)
return MmsValue_getElement(rc->rcbValues, 14);
}
else {
if (strcmp(elementName, "Owner") == 0)
return MmsValue_getElement(rc->rcbValues, 13);
}
#else
if (strcmp(elementName, "Owner") == 0)
return MmsValue_getElement(rc->rcbValues, 13);
@ -1118,6 +1124,7 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
if (reportControl->server->edition >= IEC_61850_EDITION_2) {
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
if (reportControl->server->enableBRCBResvTms)
brcbElementCount++;
#endif
@ -1249,9 +1256,9 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
int currentIndex = 13;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
int resvTmsIndex = currentIndex;
if (reportControl->server->enableBRCBResvTms) {
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = StringUtils_copyString("ResvTms");
namedVariable->type = MMS_INTEGER;
@ -1259,6 +1266,7 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
rcb->typeSpec.structure.elements[currentIndex] = namedVariable;
mmsValue->value.structure.components[currentIndex] = MmsValue_newInteger(16);
currentIndex++;
}
#endif /* (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) */
if (reportControl->hasOwner) {
@ -1281,7 +1289,9 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
}
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
if (reportControl->server->enableBRCBResvTms) {
MmsValue_setInt16(mmsValue->value.structure.components[resvTmsIndex], reportControl->resvTms);
}
#endif
}
@ -1344,8 +1354,7 @@ Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain,
ReportControlBlock* reportControlBlock = getRCBForLogicalNodeWithIndex(
self, logicalNode, currentReport, true);
if (reportControlBlock->trgOps & RPT_OPT_HAS_OWNER)
rc->hasOwner = true;
rc->hasOwner = self->iedServer->enableOwnerForRCB;
rc->name = StringUtils_createString(3, logicalNode->name, "$BR$",
reportControlBlock->name);
@ -1386,8 +1395,7 @@ Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain,
ReportControlBlock* reportControlBlock = getRCBForLogicalNodeWithIndex(
self, logicalNode, currentReport, false);
if (reportControlBlock->trgOps & RPT_OPT_HAS_OWNER)
rc->hasOwner = true;
rc->hasOwner = self->iedServer->enableOwnerForRCB;
rc->name = StringUtils_createString(3, logicalNode->name, "$RP$",
reportControlBlock->name);
@ -1569,9 +1577,11 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc)
printf("IED_SERVER: reservation timeout expired for %s.%s\n", rc->parentLN->name, rc->name);
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
if (self->iedServer->enableBRCBResvTms) {
MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms");
if (resvTmsVal)
MmsValue_setInt16(resvTmsVal, rc->resvTms);
}
#endif
rc->reservationTimeout = 0;
@ -1641,9 +1651,11 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection)
if (rc->buffered) {
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
if (rc->server->enableBRCBResvTms) {
MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms");
if (resvTmsVal)
MmsValue_setInt16(resvTmsVal, rc->resvTms);
}
#endif
}
else {
@ -1655,7 +1667,6 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection)
updateOwner(rc, connection);
}
#if 1
MmsDataAccessError
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value,
MmsServerConnection connection)
@ -2087,7 +2098,6 @@ exit_function:
return retVal;
}
#endif
void
Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection)

@ -130,6 +130,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
ModelNode* currentModelNode = NULL;
DataSet* currentDataSet = NULL;
GSEControlBlock* currentGoCB = NULL;
SVControlBlock* currentSMVCB = NULL;
char nameString[130];
char nameString2[130];
@ -289,6 +290,23 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
indendation = 4;
}
else if (StringUtils_startsWith((char*) lineBuffer, "SMVC")) {
uint32_t confRev;
int smpMod;
int smpRate;
int optFlds;
int isUnicast;
int matchedItems = sscanf((char*) lineBuffer, "SMVC(%s %s %s %u %i %i %i %i)",
nameString, nameString2, nameString3, &confRev, &smpMod, &smpRate, &optFlds, &isUnicast);
if (matchedItems < 5) goto exit_error;
currentSMVCB = SVControlBlock_create(nameString, currentLN, nameString2, nameString3, confRev, smpMod, smpRate, optFlds, (bool) isUnicast);
indendation = 4;
}
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
else if (StringUtils_startsWith((char*) lineBuffer, "SG")) {
@ -442,7 +460,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
}
}
int lineLength = strlen((char*) lineBuffer);
int lineLength = (int) strlen((char*) lineBuffer);
if (lineBuffer[lineLength - 1] == '{') {
indendation++;
@ -488,7 +506,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int matchedItems = sscanf((char*) lineBuffer, "PA(%u %u %u %s)", &vlanPrio, &vlanId, &appId, nameString);
if ((matchedItems != 4) || (currentGoCB == NULL)) goto exit_error;
if ((matchedItems != 4) || ((currentGoCB == NULL) && (currentSMVCB == NULL))) goto exit_error;
terminateString(nameString, ')');
@ -502,8 +520,13 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
PhyComAddress_create((uint8_t) vlanPrio, (uint16_t) vlanId, (uint16_t) appId,
(uint8_t*) nameString2);
if (currentGoCB) {
GSEControlBlock_addPhyComAddress(currentGoCB, dstAddress);
}
if (currentSMVCB) {
SVControlBlock_addPhyComAddress(currentSMVCB, dstAddress);
}
}
else
goto exit_error;
@ -535,7 +558,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
exit_error:
if (DEBUG_IED_SERVER)
printf("IED_SERVER: error parsing line %i (indendation level = %i)\n", currentLine, indendation);
printf("IED_SERVER: error parsing line %i (indentation level = %i)\n", currentLine, indendation);
IedModel_destroy(model);
return NULL;
}

@ -607,6 +607,35 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
return self;
}
DataAttributeType
DataAttribute_getType(DataAttribute* self)
{
return self->type;
}
FunctionalConstraint
DataAttribute_getFC(DataAttribute* self)
{
return self->fc;
}
uint8_t
DataAttribute_getTrgOps(DataAttribute* self)
{
return self->triggerOptions;
}
void
DataAttribute_setValue(DataAttribute* self, MmsValue* value)
{
if (self->mmsValue) {
MmsValue_update(self->mmsValue, value);
}
else {
self->mmsValue = MmsValue_clone(value);
}
}
DataSet*
DataSet_create(const char* name, LogicalNode* parent)
{
@ -625,6 +654,12 @@ DataSet_create(const char* name, LogicalNode* parent)
return self;
}
const char*
DataSet_getName(DataSet* self)
{
return self->name;
}
int
DataSet_getSize(DataSet* self)
{

@ -534,12 +534,12 @@ LogicalDevice_getChildByMmsVariableName(LogicalDevice* logicalDevice, const char
}
static int
createObjectReference(ModelNode* node, char* objectReference)
createObjectReference(ModelNode* node, char* objectReference, bool withoutIedName)
{
int bufPos;
if (node->modelType != LogicalNodeModelType) {
bufPos = createObjectReference(node->parent, objectReference);
bufPos = createObjectReference(node->parent, objectReference, withoutIedName);
objectReference[bufPos++] = '.';
}
@ -552,10 +552,18 @@ createObjectReference(ModelNode* node, char* objectReference)
bufPos = 0;
int nameLength = strlen (iedModel->name) + strlen(lDevice->name);
int nameLength = 0;
if (withoutIedName) {
nameLength = strlen(lDevice->name);
strncpy(objectReference, lDevice->name, 64);
}
else {
nameLength = strlen (iedModel->name) + strlen(lDevice->name);
strncpy(objectReference, iedModel->name, 64);
strncat(objectReference, lDevice->name, 64);
}
bufPos += nameLength;
@ -579,7 +587,20 @@ ModelNode_getObjectReference(ModelNode* node, char* objectReference)
if (objectReference == NULL)
objectReference = (char*) GLOBAL_MALLOC(130);
int bufPos = createObjectReference(node, objectReference);
int bufPos = createObjectReference(node, objectReference, false);
objectReference[bufPos] = 0;
return objectReference;
}
char*
ModelNode_getObjectReferenceEx(ModelNode* node, char* objectReference, bool withoutIedName)
{
if (objectReference == NULL)
objectReference = (char*) GLOBAL_MALLOC(130);
int bufPos = createObjectReference(node, objectReference, withoutIedName);
objectReference[bufPos] = 0;
@ -706,6 +727,37 @@ ModelNode_getType(ModelNode* self)
return self->modelType;
}
const char*
ModelNode_getName(ModelNode* self)
{
return self->name;
}
ModelNode*
ModelNode_getParent(ModelNode* self)
{
return self->parent;
}
LinkedList
ModelNode_getChildren(ModelNode* self)
{
LinkedList childNodes = NULL;
if (self->firstChild)
childNodes = LinkedList_create();
ModelNode* childNode = self->firstChild;
while (childNode) {
LinkedList_add(childNodes, childNode);
childNode = childNode->sibling;
}
return childNodes;
}
LogicalNode*
LogicalDevice_getLogicalNode(LogicalDevice* self, const char* nodeName)
{

@ -23,9 +23,9 @@
#include "logging_api.h"
#include "libiec61850_platform_includes.h"
#include "sqlite3.h"
#include <stdio.h>
#ifndef DEBUG_LOG_STORAGE_DRIVER
#define DEBUG_LOG_STORAGE_DRIVER 0

@ -30,6 +30,12 @@ LogStorage_setMaxLogEntries(LogStorage self, int maxEntries)
self->maxLogEntries = maxEntries;
}
int
LogStorage_getMaxLogEntries(LogStorage self)
{
return self->maxLogEntries;
}
uint64_t
LogStorage_addEntry(LogStorage self, uint64_t timestamp)
{

@ -107,6 +107,16 @@ struct sLogStorage {
LIB61850_API void
LogStorage_setMaxLogEntries(LogStorage self, int maxEntries);
/**
* \brief Get the maximum allowed number of log entries for this log
*
* \param self the pointer of the LogStorage instance
*
* \return the maximum number of log entries
*/
LIB61850_API int
LogStorage_getMaxLogEntries(LogStorage self);
/**
* \brief Add an entry to the log
*

@ -25,12 +25,20 @@
#include "ber_decode.h"
static int
getIndefiniteLength(uint8_t* buffer, int bufPos, int maxBufPos)
BerDecoder_decodeLengthRecursive(uint8_t* buffer, int* length, int bufPos, int maxBufPos, int depth, int maxDepth);
static int
getIndefiniteLength(uint8_t* buffer, int bufPos, int maxBufPos, int depth, int maxDepth)
{
depth++;
if (depth > maxDepth)
return -1;
int length = 0;
while (bufPos < maxBufPos) {
if ((buffer[bufPos] == 0) && (buffer[bufPos+1] == 0)) {
if ((buffer[bufPos] == 0) && ((bufPos + 1) < maxBufPos) && (buffer[bufPos+1] == 0)) {
return length + 2;
}
else {
@ -44,7 +52,7 @@ getIndefiniteLength(uint8_t* buffer, int bufPos, int maxBufPos)
int subLength = -1;
int newBufPos = BerDecoder_decodeLength(buffer, &subLength, bufPos, maxBufPos);
int newBufPos = BerDecoder_decodeLengthRecursive(buffer, &subLength, bufPos, maxBufPos, depth, maxDepth);
if (newBufPos == -1)
return -1;
@ -58,8 +66,8 @@ getIndefiniteLength(uint8_t* buffer, int bufPos, int maxBufPos)
return -1;
}
int
BerDecoder_decodeLength(uint8_t* buffer, int* length, int bufPos, int maxBufPos)
static int
BerDecoder_decodeLengthRecursive(uint8_t* buffer, int* length, int bufPos, int maxBufPos, int depth, int maxDepth)
{
if (bufPos >= maxBufPos)
return -1;
@ -70,7 +78,7 @@ BerDecoder_decodeLength(uint8_t* buffer, int* length, int bufPos, int maxBufPos)
int lenLength = len1 & 0x7f;
if (lenLength == 0) { /* indefinite length form */
*length = getIndefiniteLength(buffer, bufPos, maxBufPos);
*length = getIndefiniteLength(buffer, bufPos, maxBufPos, depth, maxDepth);
}
else {
*length = 0;
@ -80,6 +88,9 @@ BerDecoder_decodeLength(uint8_t* buffer, int* length, int bufPos, int maxBufPos)
if (bufPos >= maxBufPos)
return -1;
if (bufPos + (*length) > maxBufPos)
return -1;
*length <<= 8;
*length += buffer[bufPos++];
}
@ -102,6 +113,12 @@ BerDecoder_decodeLength(uint8_t* buffer, int* length, int bufPos, int maxBufPos)
return bufPos;
}
int
BerDecoder_decodeLength(uint8_t* buffer, int* length, int bufPos, int maxBufPos)
{
return BerDecoder_decodeLengthRecursive(buffer, length, bufPos, maxBufPos, 0, 50);
}
char*
BerDecoder_decodeString(uint8_t* buffer, int strlen, int bufPos, int maxBufPos)
{

@ -203,10 +203,9 @@ BerInteger_createFromInt64(int64_t value)
return asn1Value;
}
int /* 1 - if conversion is possible, 0 - out of range */
void
BerInteger_toInt32(Asn1PrimitiveValue* self, int32_t* nativeValue)
{
if (self->size < 5) {
uint8_t* buf = self->octets;
int i;
@ -217,17 +216,11 @@ BerInteger_toInt32(Asn1PrimitiveValue* self, int32_t* nativeValue)
for (i = 0; i < self->size; i++)
*nativeValue = (*nativeValue << 8) | buf[i];
return 1;
}
else
return 0;
}
int /* 1 - if conversion is possible, 0 - out of range */
void
BerInteger_toUint32(Asn1PrimitiveValue* self, uint32_t* nativeValue)
{
if (self->size < 6) {
uint8_t* buf = self->octets;
int i;
@ -235,17 +228,11 @@ BerInteger_toUint32(Asn1PrimitiveValue* self, uint32_t* nativeValue)
for (i = 0; i < self->size; i++)
*nativeValue = (*nativeValue << 8) | buf[i];
return 1;
}
else
return 0;
}
int /* 1 - if conversion is possible, 0 - out of range */
void
BerInteger_toInt64(Asn1PrimitiveValue* self, int64_t* nativeValue)
{
if (self->size < 9) {
uint8_t* buf = self->octets;
int i;
@ -256,10 +243,5 @@ BerInteger_toInt64(Asn1PrimitiveValue* self, int64_t* nativeValue)
for (i = 0; i < self->size; i++)
*nativeValue = (*nativeValue << 8) | buf[i];
return 1;
}
else
return 0;
}

@ -985,6 +985,24 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu
LIB61850_API int
MmsValue_encodeMmsData(MmsValue* self, uint8_t* buffer, int bufPos, bool encode);
/**
* \brief Get the maximum possible BER encoded size of the MMS data element
*
* \param self the MmsValue instance
*
* \return the maximum encoded size in bytes of the MMS data element
*/
LIB61850_API int
MmsValue_getMaxEncodedSize(MmsValue* self);
/**
* \brief Calculate the maximum encoded size of a variable of this type
*
* \param self the MMS variable specification instance
*/
LIB61850_API int
MmsVariableSpecification_getMaxEncodedSize(MmsVariableSpecification* self);
/**@}*/
/**@}*/

@ -66,13 +66,13 @@ BerInteger_createInt64(void);
LIB61850_INTERNAL int
BerInteger_setInt64(Asn1PrimitiveValue* self, int64_t value);
LIB61850_INTERNAL int /* 1 - if conversion is possible, 0 - out of range */
LIB61850_INTERNAL void
BerInteger_toInt32(Asn1PrimitiveValue* self, int32_t* nativeValue);
LIB61850_INTERNAL int /* 1 - if conversion is possible, 0 - out of range */
LIB61850_INTERNAL void
BerInteger_toUint32(Asn1PrimitiveValue* self, uint32_t* nativeValue);
LIB61850_INTERNAL int /* 1 - if conversion is possible, 0 - out of range */
LIB61850_INTERNAL void
BerInteger_toInt64(Asn1PrimitiveValue* self, int64_t* nativeValue);
#ifdef __cplusplus

@ -70,18 +70,6 @@ private_IsoServer_decreaseConnectionCounter(IsoServer self);
LIB61850_INTERNAL int
private_IsoServer_getConnectionCounter(IsoServer self);
/**
* \brief User provided lock that will be called when higher layer (MMS) is called
*/
LIB61850_INTERNAL void
IsoServer_setUserLock(IsoServer self, Semaphore userLock);
LIB61850_INTERNAL void
IsoServer_userLock(IsoServer self);
LIB61850_INTERNAL void
IsoServer_userUnlock(IsoServer self);
LIB61850_INTERNAL bool
IsoConnection_isRunning(IsoConnection self);

@ -370,10 +370,13 @@ mmsClient_handleFileReadRequest(
LIB61850_INTERNAL void
mmsClient_handleFileCloseRequest(
MmsConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response);
MmsConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response);
LIB61850_INTERNAL void
mmsClient_closeOutstandingOpenFiles(MmsConnection connection);
LIB61850_INTERNAL MmsOutstandingCall
mmsClient_getMatchingObtainFileRequest(MmsConnection self, const char* filename);

@ -98,6 +98,9 @@ struct sMmsObtainFileTask {
uint64_t nextTimeout;
int32_t frmsId;
int state;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore taskLock;
#endif
};
#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */

@ -193,4 +193,7 @@ MmsServer_getConnectionCounter(MmsServer self);
LIB61850_INTERNAL void
MmsServer_stopListeningThreadless(MmsServer self);
LIB61850_INTERNAL const char*
MmsServer_getFilesystemBasepath(MmsServer self);
#endif /* MMS_SERVER_LIBINTERNAL_H_ */

@ -147,7 +147,6 @@ IsoClientConnection_create(IsoConnectionParameters parameters, IsoIndicationCall
IsoClientConnection self = (IsoClientConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoClientConnection));
if (self) {
self->parameters = parameters;
self->callback = callback;
self->callbackParameter = callbackParameter;
@ -194,6 +193,13 @@ IsoClientConnection_create(IsoConnectionParameters parameters, IsoIndicationCall
static bool
sendConnectionRequestMessage(IsoClientConnection self)
{
if (self->cotpConnection) {
/* Destroy existing handle set when connection is reused */
if (self->cotpConnection->handleSet)
Handleset_destroy(self->cotpConnection->handleSet);
self->cotpConnection->handleSet = NULL;
}
/* COTP (ISO transport) handshake */
CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer);
@ -750,7 +756,7 @@ void
IsoClientConnection_destroy(IsoClientConnection self)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_destroy\n");
printf("ISO_CLIENT: IsoClientConnection_destroy(%p)\n", self);
int state = getState(self);

@ -451,8 +451,9 @@ CotpConnection_init(CotpConnection* self, Socket socket,
{
self->state = 0;
self->socket = socket;
self->handleSet = Handleset_new( );
self->handleSet = Handleset_new();
Handleset_addSocket( self->handleSet, self->socket );
#if (CONFIG_MMS_SUPPORT_TLS == 1)
self->tlsSocket = NULL;
#endif

@ -1035,6 +1035,25 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
if (self->connectionLostHandler != NULL)
self->connectionLostHandler(self, self->connectionLostHandlerParameter);
/* Cleanup outstanding calls */
{
int i;
for (i = 0; i < OUTSTANDING_CALLS; i++) {
if (self->outstandingCalls[i].isUsed) {
if (self->outstandingCalls[i].type != MMS_CALL_TYPE_NONE)
handleAsyncResponse(self, NULL, 0, &(self->outstandingCalls[i]), MMS_ERROR_SERVICE_TIMEOUT);
self->outstandingCalls[i].isUsed = false;
break;
}
}
}
Semaphore_post(self->outstandingCallsLock);
return true;
}
@ -1541,6 +1560,9 @@ MmsConnection_destroy(MmsConnection self)
if (self->filestoreBasepath != NULL)
GLOBAL_FREEMEM(self->filestoreBasepath);
#endif
/* Close outstanding open files */
mmsClient_closeOutstandingOpenFiles(self);
#endif
GLOBAL_FREEMEM(self);

@ -224,6 +224,19 @@ mmsClient_handleFileCloseRequest(
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_OTHER);
}
void
mmsClient_closeOutstandingOpenFiles(MmsConnection connection)
{
int i;
for (i = 0; i < CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION; i++) {
if (connection->frsms[i].fileHandle != NULL) {
FileSystem_closeFile(connection->frsms[i].fileHandle);
connection->frsms[i].fileHandle = NULL;
}
}
}
#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */

@ -193,6 +193,9 @@ MmsValue_equals(const MmsValue* self, const MmsValue* otherValue)
bool
MmsValue_equalTypes(const MmsValue* self, const MmsValue* otherValue)
{
if ((self == NULL) || (otherValue == NULL))
return false;
if (self->type == otherValue->type) {
switch (self->type)
{

@ -159,6 +159,9 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu
int dataEndBufPos = bufferLength;
if (bufferLength < 1)
goto exit_with_error;
uint8_t tag = buffer[bufPos++];
int dataLength;
@ -168,6 +171,10 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu
if (bufPos < 0)
goto exit_with_error;
/* if not indefinite length end tag, data length must be > 0 */
if ((tag != 0) && (dataLength == 0))
goto exit_with_error;
switch (tag) {
case 0xa1: /* MMS_ARRAY */
@ -253,6 +260,7 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu
value = MmsValue_newUnsigned(dataLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, dataLength);
value->value.integer->size = dataLength;
bufPos += dataLength;
break;
@ -326,6 +334,168 @@ exit_with_error:
return NULL;
}
static int
MmsValue_getMaxStructSize(MmsValue* self)
{
int componentsSize = 0;
int i;
int size;
int componentCount = self->value.structure.size;
MmsValue** components = self->value.structure.components;
for (i = 0; i < componentCount; i++)
componentsSize += MmsValue_getMaxEncodedSize(components[i]);
size = 1 + componentsSize + BerEncoder_determineLengthSize(componentsSize);
return size;
}
int
MmsValue_getMaxEncodedSize(MmsValue* self)
{
int size = 0;
int elementSize = 0;
switch (self->type)
{
case MMS_STRUCTURE:
size = MmsValue_getMaxStructSize(self);
break;
case MMS_ARRAY:
size = MmsValue_getMaxStructSize(self);
break;
case MMS_BOOLEAN:
size = 3;
break;
case MMS_DATA_ACCESS_ERROR:
size = 7; /* TL * size of uint32 max */
break;
case MMS_VISIBLE_STRING:
elementSize = abs(self->value.visibleString.size);
size = 1 + elementSize + BerEncoder_determineLengthSize(elementSize);
break;
case MMS_UNSIGNED:
size = 2 + self->value.integer->maxSize;
break;
case MMS_INTEGER:
size = 2 + self->value.integer->maxSize;
break;
case MMS_UTC_TIME:
size = 10;
break;
case MMS_BIT_STRING:
elementSize = abs(self->value.bitString.size);
size = BerEncoder_determineEncodedBitStringSize(elementSize);
break;
case MMS_BINARY_TIME:
size = 2 + self->value.binaryTime.size;
break;
case MMS_OCTET_STRING:
elementSize = self->value.octetString.maxSize;
size = 1 + BerEncoder_determineLengthSize(elementSize) + elementSize;
break;
case MMS_FLOAT:
elementSize = (self->value.floatingPoint.formatWidth / 8) + 1;
size = elementSize + 2; /* 2 for tag and length */
break;
case MMS_STRING:
elementSize = abs(self->value.visibleString.size);
size = 1 + elementSize + BerEncoder_determineLengthSize(elementSize);
break;
default:
if (DEBUG_MMS_SERVER)
printf("MmsVariableSpecification_getMaxEncodedSize: error unsupported type!\n");
break;
}
return size;
}
static int
getMaxStructSize(MmsVariableSpecification* variable)
{
int componentsSize = 0;
int i;
int size;
int componentCount = variable->typeSpec.structure.elementCount;
MmsVariableSpecification** components = variable->typeSpec.structure.elements;
for (i = 0; i < componentCount; i++)
componentsSize += MmsVariableSpecification_getMaxEncodedSize(components[i]);
size = 1 + componentsSize + BerEncoder_determineLengthSize(componentsSize);
return size;
}
int
MmsVariableSpecification_getMaxEncodedSize(MmsVariableSpecification* self)
{
int size = 0;
int elementSize = 0;
switch (self->type)
{
case MMS_STRUCTURE:
size = getMaxStructSize(self);
break;
case MMS_ARRAY:
elementSize = MmsVariableSpecification_getMaxEncodedSize(self->typeSpec.array.elementTypeSpec)
* self->typeSpec.array.elementCount;
size = 1 + elementSize + BerEncoder_determineLengthSize(elementSize);
break;
case MMS_BOOLEAN:
size = 3;
break;
case MMS_DATA_ACCESS_ERROR:
size = 7; /* TL * size of uint32 max */
break;
case MMS_VISIBLE_STRING:
elementSize = abs(self->typeSpec.visibleString);
size = 1 + elementSize + BerEncoder_determineLengthSize(elementSize);
break;
case MMS_UNSIGNED:
size = 2 + (self->typeSpec.unsignedInteger / 8) + 1;
break;
case MMS_INTEGER:
size = 2 + (self->typeSpec.integer / 8) + 1;
break;
case MMS_UTC_TIME:
size = 10;
break;
case MMS_BIT_STRING:
elementSize = abs(self->typeSpec.bitString);
size = BerEncoder_determineEncodedBitStringSize(elementSize);
break;
case MMS_BINARY_TIME:
size = 2 + self->typeSpec.binaryTime;
break;
case MMS_OCTET_STRING:
elementSize = abs(self->typeSpec.octetString);
size = 1 + BerEncoder_determineLengthSize(elementSize) + elementSize;
break;
case MMS_FLOAT:
elementSize = (self->typeSpec.floatingpoint.formatWidth / 8) + 1;
size = elementSize + 2; /* 2 for tag and length */
break;
case MMS_STRING:
elementSize = abs(self->typeSpec.mmsString);
size = 1 + elementSize + BerEncoder_determineLengthSize(elementSize);
break;
default:
if (DEBUG_MMS_SERVER)
printf("MmsVariableSpecification_getMaxEncodedSize: error unsupported type!\n");
break;
}
return size;
}
int
MmsValue_encodeMmsData(MmsValue* self, uint8_t* buffer, int bufPos, bool encode)
{
@ -436,7 +606,7 @@ MmsValue_encodeMmsData(MmsValue* self, uint8_t* buffer, int bufPos, bool encode)
break;
default:
if (DEBUG_MMS_SERVER)
printf("encodeAccessResult: error unsupported type!\n");
printf("MmsValue_encodeMmsData: error unsupported type!\n");
size = 0;
break;
}

@ -137,8 +137,12 @@ encodeInitResponseDetail(MmsServerConnection self, uint8_t* buffer, int bufPos,
servicesSupported[9] |= MMS_SERVICE_FILE_OPEN;
servicesSupported[9] |= MMS_SERVICE_FILE_READ;
servicesSupported[9] |= MMS_SERVICE_FILE_CLOSE;
#if (MMS_RENAME_FILE_SERVICE == 1)
servicesSupported[9] |= MMS_SERVICE_FILE_RENAME;
#endif
#if (MMS_DELETE_FILE_SERVICE == 1)
servicesSupported[9] |= MMS_SERVICE_FILE_DELETE;
#endif
servicesSupported[9] |= MMS_SERVICE_FILE_DIRECTORY;
#endif /* (MMS_FILE_SERVICE == 1) */

@ -431,7 +431,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
FileSystem_closeFile(task->fileHandle);
task->fileHandle = NULL;
}
deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename);
deleteFile(MmsServer_getFilesystemBasepath(self), task->destinationFilename);
}
}
break;
@ -471,7 +471,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
FileSystem_closeFile(task->fileHandle);
task->fileHandle = NULL;
}
deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename);
deleteFile(MmsServer_getFilesystemBasepath(self), task->destinationFilename);
}
break;
@ -510,7 +510,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
FileSystem_closeFile(task->fileHandle);
task->fileHandle = NULL;
deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename);
deleteFile(MmsServer_getFilesystemBasepath(self), task->destinationFilename);
}
break;
@ -536,7 +536,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
task->fileHandle = NULL;
}
deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename);
deleteFile(MmsServer_getFilesystemBasepath(self), task->destinationFilename);
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: ObtainFile service: failed to open file from client\n");
@ -565,7 +565,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
task->fileHandle = NULL;
if (task->destinationFilename[0])
deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename);
deleteFile(MmsServer_getFilesystemBasepath(self), task->destinationFilename);
}
if (DEBUG_MMS_SERVER)
@ -606,7 +606,7 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
task->fileHandle = NULL;
if (task->destinationFilename[0])
deleteFile(MmsServerConnection_getFilesystemBasepath(task->connection), task->destinationFilename);
deleteFile(MmsServer_getFilesystemBasepath(self), task->destinationFilename);
}
task->state = MMS_FILE_UPLOAD_STATE_NOT_USED;
}
@ -628,8 +628,13 @@ mmsServerConnection_stopFileUploadTasks(MmsServerConnection self)
if (server->fileUploadTasks[i].state != 0) {
if (server->fileUploadTasks[i].connection == self) {
Semaphore_wait(server->fileUploadTasks[i].taskLock);
/* stop file upload task */
server->fileUploadTasks[i].state = MMS_FILE_UPLOAD_STATE_INTERRUPTED;
Semaphore_post(server->fileUploadTasks[i].taskLock);
}
}

@ -928,11 +928,19 @@ mmsServer_handleReadRequest(
request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.read);
if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) {
MmsServer_lockModel(connection->server);
handleReadListOfVariablesRequest(connection, request, invokeId, response);
MmsServer_unlockModel(connection->server);
}
#if (MMS_DATA_SET_SERVICE == 1)
else if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) {
MmsServer_lockModel(connection->server);
handleReadNamedVariableListRequest(connection, request, invokeId, response);
MmsServer_unlockModel(connection->server);
}
#endif
else {

@ -81,10 +81,6 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration)
if (isoServer == NULL)
goto exit_error;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
IsoServer_setUserLock(isoServer, self->modelMutex);
#endif
LinkedList_add(self->isoServerList, isoServer);
}
@ -132,8 +128,6 @@ MmsServer_addAP(MmsServer self, const char* ipAddr, int tcpPort, TLSConfiguratio
if (isoServer) {
IsoServer_setUserLock(isoServer, self->modelMutex);
IsoServer_setLocalIpAddress(isoServer, ipAddr);
if (tcpPort != -1)
@ -285,10 +279,11 @@ MmsServer_reserveTransmitBuffer(MmsServer self)
void
MmsServer_releaseTransmitBuffer(MmsServer self)
{
self->transmitBuffer->size = 0;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->transmitBufferMutex);
#endif
self->transmitBuffer->size = 0;
}
#if (MMS_OBTAIN_FILE_SERVICE == 1)
@ -302,6 +297,8 @@ MmsServer_getObtainFileTask(MmsServer self)
if (self->fileUploadTasks[i].state == 0) {
self->fileUploadTasks[i].state = 1;
if (self->fileUploadTasks[i].taskLock == NULL)
self->fileUploadTasks[i].taskLock = Semaphore_create(1);
return &(self->fileUploadTasks[i]);
}
@ -415,7 +412,7 @@ MmsServer_destroy(MmsServer self)
Map_deleteDeep(self->openConnections, false, closeConnection);
Map_deleteDeep(self->valueCaches, false, (void (*) (void*)) deleteSingleCache);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
#if (CONFIG_MMS_THREADLESS_STACK != 1)
if (self->openConnectionsLock)
Semaphore_destroy(self->openConnectionsLock);
@ -424,15 +421,23 @@ MmsServer_destroy(MmsServer self)
if (self->transmitBufferMutex)
Semaphore_destroy(self->transmitBufferMutex);
#endif
#endif
if (self->transmitBuffer)
ByteBuffer_destroy(self->transmitBuffer);
#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1)
#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1)
if (self->filestoreBasepath != NULL)
GLOBAL_FREEMEM(self->filestoreBasepath);
#endif
#endif
#if (MMS_OBTAIN_FILE_SERVICE == 1)
int i;
for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) {
if (self->fileUploadTasks[i].taskLock)
Semaphore_destroy(self->fileUploadTasks[i].taskLock);
}
#endif
GLOBAL_FREEMEM(self);
}
@ -479,7 +484,8 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va
if (self->writeHandler != NULL) {
indication = self->writeHandler(self->writeHandlerParameter, domain,
itemId, value, connection);
} else {
}
else {
MmsValue* cachedValue;
if (domain == NULL)
@ -497,7 +503,6 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va
return indication;
}
MmsValue*
mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess)
{
@ -705,8 +710,15 @@ MmsServer_handleBackgroundTasks(MmsServer self)
int i;
for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++)
{
if (self->fileUploadTasks[i].state != 0) {
Semaphore_wait(self->fileUploadTasks[i].taskLock);
if (self->fileUploadTasks[i].state != 0)
mmsServer_fileUploadTask(self, &(self->fileUploadTasks[i]));
Semaphore_post(self->fileUploadTasks[i].taskLock);
}
}
#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */
@ -756,3 +768,17 @@ MmsServer_stopListeningThreadless(MmsServer self)
}
}
const char*
MmsServer_getFilesystemBasepath(MmsServer self)
{
#if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1)
if (self->filestoreBasepath != NULL)
return self->filestoreBasepath;
else
return CONFIG_VIRTUAL_FILESTORE_BASEPATH;
#else
return CONFIG_VIRTUAL_FILESTORE_BASEPATH;
#endif
}

@ -244,6 +244,8 @@ handleConfirmedRequestPdu(
break;
#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */
#if (MMS_RENAME_FILE_SERVICE == 1)
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
case 0x4b: /* file-rename-request */
if (self->server->fileServiceEnabled)
@ -257,6 +259,11 @@ handleConfirmedRequestPdu(
break;
#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */
#endif /* (MMS_RENAME_FILE_SERVICE == 1) */
#if (MMS_DELETE_FILE_SERVICE == 1)
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
case 0x4c: /* file-delete-request */
if (self->server->fileServiceEnabled)
@ -270,6 +277,8 @@ handleConfirmedRequestPdu(
break;
#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */
#endif /* (MMS_DELETE_FILE_SERVICE == 1) */
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
case 0x4d: /* file-directory-request */
if (self->server->fileServiceEnabled)

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

Loading…
Cancel
Save