diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 00000000..c657fd28
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -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
diff --git a/CHANGELOG b/CHANGELOG
index 0ea01043..5137cd5c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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
--------------------------
diff --git a/Makefile b/Makefile
index 60a798f6..db06c7f8 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 387a87a3..cee30b14 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/config/stack_config.h b/config/stack_config.h
index 369ef2f1..8a71f0c3 100644
--- a/config/stack_config.h
+++ b/config/stack_config.h
@@ -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 */
diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake
index 17ac3120..dd93fcce 100644
--- a/config/stack_config.h.cmake
+++ b/config/stack_config.h.cmake
@@ -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
diff --git a/dotnet/IEC61850forCSharp/DataSet.cs b/dotnet/IEC61850forCSharp/DataSet.cs
index 2fcb1040..d7304bc9 100644
--- a/dotnet/IEC61850forCSharp/DataSet.cs
+++ b/dotnet/IEC61850forCSharp/DataSet.cs
@@ -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.
///
+ ///
+ /// 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.
+ ///
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 (nativeObject != IntPtr.Zero)
+ if (!disposed)
{
- ClientDataSet_destroy(nativeObject);
- nativeObject = IntPtr.Zero;
+ 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()
diff --git a/dotnet/IEC61850forCSharp/GooseControlBlock.cs b/dotnet/IEC61850forCSharp/GooseControlBlock.cs
index e0bfbe1c..1e91d6fe 100644
--- a/dotnet/IEC61850forCSharp/GooseControlBlock.cs
+++ b/dotnet/IEC61850forCSharp/GooseControlBlock.cs
@@ -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)
diff --git a/dotnet/IEC61850forCSharp/GooseSubscriber.cs b/dotnet/IEC61850forCSharp/GooseSubscriber.cs
index 9b2c6e69..29b64c01 100644
--- a/dotnet/IEC61850forCSharp/GooseSubscriber.cs
+++ b/dotnet/IEC61850forCSharp/GooseSubscriber.cs
@@ -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 subscribers = new List();
+
public GooseReceiver()
{
self = GooseReceiver_create ();
@@ -79,14 +82,29 @@ namespace IEC61850
GooseReceiver_setInterfaceId (self, interfaceId);
}
+ ///
+ /// Add the subscriber to be handled by this receiver instance
+ ///
+ /// A GooseSubscriber can only be added to one GooseReceiver!
+ ///
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,12 +344,20 @@ namespace IEC61850
{
if (isDisposed == false) {
isDisposed = true;
- GooseSubscriber_destroy (self);
+
+ if (attachedToReceiver == false)
+ GooseSubscriber_destroy (self);
+
self = IntPtr.Zero;
}
}
- }
+ ~GooseSubscriber()
+ {
+ Dispose();
+ }
+
+ }
}
diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
index aff8fc67..58bc9cbd 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
@@ -548,7 +548,7 @@ namespace IEC61850
///
/// Called when there is a change in the connection state
///
- 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.
///
/// The new GoCB instance
- /// The object reference of the GoCB
+ /// The object reference of the GoCB (e.g. "simpleIOGenericIO/LLN0.gcbAnalogValues")
public GooseControlBlock GetGooseControlBlock(string gocbObjectReference)
{
return new GooseControlBlock(gocbObjectReference, connection);
@@ -1788,7 +1788,7 @@ namespace IEC61850
/// by a prior function call.
/// The user provided callback handler
/// This exception is thrown if there is a connection or service error
- [Obsolete("ConnectionClosedHandler is deprecated, please use ConnectionEventHandler instead")]
+ [Obsolete("ConnectionClosedHandler is deprecated, please use StateChangedHandler instead")]
public void InstallConnectionClosedHandler(ConnectionClosedHandler handler)
{
if (connectionClosedHandler == null)
diff --git a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs
index 6b3b0b2b..df4103ed 100644
--- a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs
@@ -86,9 +86,9 @@ namespace IEC61850
public UInt16 vlanId;
public UInt16 appId;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)]
- public byte[] dstAddress = new byte[6];
- }
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)]
+ public byte[] dstAddress = new byte[6];
+ }
///
/// MMS data access error for MmsValue type MMS_DATA_ACCESS_ERROR
@@ -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
}
///
diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
index 8a0914a2..4cdfbf11 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
@@ -54,1609 +54,2514 @@ namespace IEC61850
///
/// Representation of the IED server data model
///
- public class IedModel
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedModel_create(string name);
+ public class IedModel : IDisposable
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedModel_create(string name);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedModel_destroy(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedModel_getModelNodeByObjectReference(IntPtr self, string objectReference);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedModel_getModelNodeByShortObjectReference(IntPtr self, string objectReference);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedModel_destroy(IntPtr self);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int ModelNode_getType(IntPtr self);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedModel_getModelNodeByObjectReference(IntPtr self, string objectReference);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedModel_setIedNameForDynamicModel(IntPtr self, string iedName);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedModel_getModelNodeByShortObjectReference(IntPtr self, string objectReference);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedModel_getDeviceByInst(IntPtr self, string ldInst);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int ModelNode_getType(IntPtr self);
+ internal IntPtr self = IntPtr.Zero;
- internal IntPtr self = IntPtr.Zero;
+ internal IedModel(IntPtr self)
+ {
+ this.self = self;
+ }
- internal IedModel(IntPtr self)
- {
- this.self = self;
- }
+ /* cache managed ModelNode instances of the IedModel */
+ internal Dictionary modelNodes = new Dictionary();
///
/// Initializes a new instance of the class.
///
/// IED name
- public IedModel(string name)
- {
- self = IedModel_create(name);
- }
-
- // causes undefined behavior
- //~IedModel()
- //{
- // if (self != IntPtr.Zero)
- // {
- // IedModel_destroy(self);
- // }
- //}
-
- public void Destroy()
- {
- IedModel_destroy(self);
- self = IntPtr.Zero;
- }
-
- public static IedModel CreateFromFile(string filePath)
- {
- return ConfigFileParser.CreateModelFromConfigFile(filePath);
- }
-
- private ModelNode getModelNodeFromNodeRef(IntPtr nodeRef)
- {
- int nodeType = ModelNode_getType (nodeRef);
-
- switch (nodeType) {
- case 0:
- return new LogicalDevice (nodeRef);
-
- case 1:
- return new LogicalNode (nodeRef);
-
- case 2:
- return new DataObject (nodeRef);
-
- case 3:
- return new DataAttribute (nodeRef);
-
- default:
- return new ModelNode (nodeRef);
- }
- }
-
- public ModelNode GetModelNodeByObjectReference(string objectReference)
- {
- IntPtr nodeRef = IedModel_getModelNodeByObjectReference(self, objectReference);
-
- if (nodeRef == IntPtr.Zero)
- return null;
-
- return getModelNodeFromNodeRef (nodeRef);
- }
-
- public ModelNode GetModelNodeByShortObjectReference(string objectReference)
- {
- IntPtr nodeRef = IedModel_getModelNodeByShortObjectReference(self, objectReference);
-
- if (nodeRef == IntPtr.Zero)
- return null;
-
- return getModelNodeFromNodeRef (nodeRef);
- }
- }
-
- public class LogicalDevice : ModelNode
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr LogicalDevice_create(string name, IntPtr parent);
-
- public LogicalDevice (IntPtr self) : base (self)
- {
- }
-
- public LogicalDevice(string name, IedModel parent)
- {
- self = LogicalDevice_create(name, parent.self);
- }
- }
-
- public class LogicalNode : ModelNode
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr LogicalNode_create(string name, IntPtr parent);
-
- public LogicalNode (IntPtr self) : base(self)
- {
- }
-
- public LogicalNode(string name, LogicalDevice parent)
- {
- base.self = LogicalNode_create(name, parent.self);
- }
- }
-
- public enum AccessPolicy {
- ACCESS_POLICY_ALLOW = 0,
- ACCESS_POLICY_DENY = 1
- }
-
- public enum DataAttributeType {
- BOOLEAN = 0,
- INT8 = 1,
- INT16 = 2,
- INT32 = 3,
- INT64 = 4,
- INT128 = 5,
- INT8U = 6,
- INT16U = 7,
- INT24U = 8,
- INT32U = 9,
- FLOAT32 = 10,
- FLOAT64 = 11,
- ENUMERATED = 12,
- OCTET_STRING_64 = 13,
- OCTET_STRING_6 = 14,
- OCTET_STRING_8 = 15,
- VISIBLE_STRING_32 = 16,
- VISIBLE_STRING_64 = 17,
- VISIBLE_STRING_65 = 18,
- VISIBLE_STRING_129 = 19,
- VISIBLE_STRING_255 = 20,
- UNICODE_STRING_255 = 21,
- TIMESTAMP = 22,
- QUALITY = 23,
- CHECK = 24,
- CODEDENUM = 25,
- GENERIC_BITSTRING = 26,
- CONSTRUCTED = 27,
- ENTRY_TIME = 28,
- PHYCOMADDR = 29,
- CURRENCY = 30
- }
-
- public enum ModeValues
- {
- ON = 1,
- BLOCKED = 2,
- TEST = 3,
- TEST_BLOCKED = 4,
- OFF = 5
- }
-
- public enum HealthValues
- {
- OK = 1,
- WARNING = 2,
- ALARM = 3
- }
-
- ///
- /// The CDC class contains helper functions to create DataObject instances for the
- /// most common Common Data Classes.
- ///
- public class CDC
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_SPS_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_DPS_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_VSS_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_SEC_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_CMV_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_SAV_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ACD_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ACT_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_SPG_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_VSG_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ENG_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ING_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ASG_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_WYE_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_DEL_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_HST_create(string name, IntPtr parent, uint options, ushort maxPts);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_INS_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_MV_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_INC_create(string name, IntPtr parent, uint options, uint controlOptions);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_LPL_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_DPL_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ENS_create(string name, IntPtr parent, uint options);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_SPC_create(string name, IntPtr parent, uint options, uint controlOptions);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_DPC_create(string name, IntPtr parent, uint options, uint controlOptions);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_BSC_create(string name, IntPtr parent, uint options, uint controlOptions, [MarshalAs(UnmanagedType.I1)] bool hasTransientIndicator);
+ public IedModel(string name)
+ {
+ self = IedModel_create(name);
+ }
+
+ ~IedModel()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Releases all resource used by the object.
+ ///
+ public void Destroy()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Releases all resource used by the object.
+ ///
+ /// Call when you are done using the . The
+ /// method leaves the in an unusable state. After
+ /// calling , you must release all references to the so
+ /// the garbage collector can reclaim the memory that the was occupying.
+ public void Dispose()
+ {
+ lock (this)
+ {
+ if (self != IntPtr.Zero)
+ {
+ IedModel_destroy(self);
+ self = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///
+ /// Creates a new instance with the data model of config file.
+ ///
+ /// new instance
+ /// fila name of the configuration (.cfg) file
+ public static IedModel CreateFromFile(string filename)
+ {
+ return ConfigFileParser.CreateModelFromConfigFile(filename);
+ }
+
+ ///
+ /// Get parent node. When not found create the parent node and add to modelNode list
+ ///
+ /// The parent node, or when not found
+ /// the native reference of the model node
+ private ModelNode GetParent(IntPtr nodeRef)
+ {
+ ModelNode parentNode = null;
+
+ IntPtr parentNodeRef = ModelNode.GetNativeParent(nodeRef);
+
+ if (parentNodeRef != IntPtr.Zero)
+ {
+ if (modelNodes.TryGetValue(parentNodeRef, out parentNode) == false)
+ {
+ int nodeType = ModelNode_getType(parentNodeRef);
+
+ if (nodeType == 0)
+ {
+ parentNode = new LogicalDevice(parentNodeRef, this);
+ }
+ else
+ {
+ ModelNode parentOfParent = GetParent(parentNodeRef);
+
+ if (parentOfParent != null)
+ {
+ switch (nodeType)
+ {
+ case 1:
+ parentNode = new LogicalNode(parentNodeRef, parentOfParent);
+ break;
+
+ case 2:
+ parentNode = new DataObject(parentNodeRef, parentOfParent);
+ break;
+
+ case 3:
+ parentNode = new DataAttribute(parentNodeRef, parentOfParent);
+ break;
+
+ default:
+ parentNode = new ModelNode(parentNodeRef, parentOfParent);
+ break;
+ }
+ }
+ }
+
+ if (parentNode != null)
+ {
+ modelNodes.Add(parentNodeRef, parentNode);
+ }
+ }
+ }
+
+ return parentNode;
+ }
+
+ public LogicalDevice GetDeviceByInst(string ldInst)
+ {
+ IntPtr ldPtr = IedModel_getDeviceByInst(self, ldInst);
+
+ if (ldPtr != IntPtr.Zero)
+ {
+ return new LogicalDevice(ldPtr, this);
+ }
+ else
+ return null;
+ }
+
+ internal ModelNode GetModelNodeFromNodeRef(IntPtr nodeRef)
+ {
+ ModelNode modelNode = null;
+
+ modelNodes.TryGetValue(nodeRef, out modelNode);
+
+ if (modelNode == null)
+ {
+ int nodeType = ModelNode_getType(nodeRef);
+
+ if (nodeType == 0)
+ {
+ modelNode = new LogicalDevice(nodeRef, this);
+ }
+ else
+ {
+ ModelNode parent = GetParent(nodeRef);
+
+ if (parent != null)
+ {
+ switch (nodeType)
+ {
+ case 1:
+ modelNode = new LogicalNode(nodeRef, parent);
+ break;
+
+ case 2:
+ modelNode = new DataObject(nodeRef, parent);
+ break;
+
+ case 3:
+ modelNode = new DataAttribute(nodeRef, parent);
+ break;
+
+ default:
+ modelNode = new ModelNode(nodeRef, parent);
+ break;
+ }
+ }
+ }
+
+ if (modelNode != null)
+ {
+ modelNodes.Add(nodeRef, modelNode);
+ }
+ }
+
+ return modelNode;
+ }
+
+ ///
+ /// Change the IED name of the data model
+ ///
+ /// the new IED name
+ public void SetIedName(string iedName)
+ {
+ IedModel_setIedNameForDynamicModel(self, iedName);
+ }
+
+ ///
+ /// Gets the model node by full object reference.
+ ///
+ /// The model node
+ /// Full object reference including the IED name
+ public ModelNode GetModelNodeByObjectReference(string objectReference)
+ {
+ IntPtr nodeRef = IedModel_getModelNodeByObjectReference(self, objectReference);
+
+ if (nodeRef == IntPtr.Zero)
+ return null;
+
+ return GetModelNodeFromNodeRef (nodeRef);
+ }
+
+ ///
+ /// Gets the model node by short object reference (without IED name)
+ ///
+ /// The model node
+ /// Object reference without IED name (e.g. LD0/GGIO1.Ind1.stVal)
+ public ModelNode GetModelNodeByShortObjectReference(string objectReference)
+ {
+ IntPtr nodeRef = IedModel_getModelNodeByShortObjectReference(self, objectReference);
+
+ if (nodeRef == IntPtr.Zero)
+ return null;
+
+ return GetModelNodeFromNodeRef (nodeRef);
+ }
+
+ }
+
+ public class LogicalDevice : ModelNode
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr LogicalDevice_create(string name, IntPtr parent);
+
+ private IedModel iedModel = null;
+
+ public IedModel IedModel { get => iedModel; }
+
+ public LogicalDevice (IntPtr self, IedModel iedModel) : base (self)
+ {
+ this.iedModel = iedModel;
+ }
+
+ public LogicalDevice(string name, IedModel parent)
+ {
+ this.iedModel = parent;
+
+ self = LogicalDevice_create(name, parent.self);
+ }
+ }
+
+ public class LogicalNode : ModelNode
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr LogicalNode_create(string name, IntPtr parent);
+
+ public LogicalNode (IntPtr self, ModelNode parent) : base(self)
+ {
+ this.parent = parent;
+ }
+
+ public LogicalNode(string name, LogicalDevice parent)
+ {
+ this.parent = parent;
+
+ base.self = LogicalNode_create(name, parent.self);
+ }
+ }
+
+ public enum AccessPolicy {
+ ACCESS_POLICY_ALLOW = 0,
+ ACCESS_POLICY_DENY = 1
+ }
+
+ public enum DataAttributeType {
+ BOOLEAN = 0,
+ INT8 = 1,
+ INT16 = 2,
+ INT32 = 3,
+ INT64 = 4,
+ INT128 = 5,
+ INT8U = 6,
+ INT16U = 7,
+ INT24U = 8,
+ INT32U = 9,
+ FLOAT32 = 10,
+ FLOAT64 = 11,
+ ENUMERATED = 12,
+ OCTET_STRING_64 = 13,
+ OCTET_STRING_6 = 14,
+ OCTET_STRING_8 = 15,
+ VISIBLE_STRING_32 = 16,
+ VISIBLE_STRING_64 = 17,
+ VISIBLE_STRING_65 = 18,
+ VISIBLE_STRING_129 = 19,
+ VISIBLE_STRING_255 = 20,
+ UNICODE_STRING_255 = 21,
+ TIMESTAMP = 22,
+ QUALITY = 23,
+ CHECK = 24,
+ CODEDENUM = 25,
+ GENERIC_BITSTRING = 26,
+ CONSTRUCTED = 27,
+ ENTRY_TIME = 28,
+ PHYCOMADDR = 29,
+ CURRENCY = 30
+ }
+
+ public enum ModeValues
+ {
+ ON = 1,
+ BLOCKED = 2,
+ TEST = 3,
+ TEST_BLOCKED = 4,
+ OFF = 5
+ }
+
+ public enum HealthValues
+ {
+ OK = 1,
+ WARNING = 2,
+ ALARM = 3
+ }
+
+ ///
+ /// The CDC class contains helper functions to create DataObject instances for the
+ /// most common Common Data Classes.
+ ///
+ public class CDC
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_SPS_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_DPS_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_VSS_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_SEC_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_CMV_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_SAV_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ACD_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ACT_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_SPG_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_VSG_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ENG_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ING_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ASG_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_WYE_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_DEL_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_HST_create(string name, IntPtr parent, uint options, ushort maxPts);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_INS_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_MV_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_INC_create(string name, IntPtr parent, uint options, uint controlOptions);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_LPL_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_DPL_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ENS_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_SPC_create(string name, IntPtr parent, uint options, uint controlOptions);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_DPC_create(string name, IntPtr parent, uint options, uint controlOptions);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_BSC_create(string name, IntPtr parent, uint options, uint controlOptions, [MarshalAs(UnmanagedType.I1)] bool hasTransientIndicator);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_APC_create(string name, IntPtr parent, uint options, uint controlOptions, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_BCR_create(string name, IntPtr parent, uint options);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ENC_create(string name, IntPtr parent, uint options, uint controlOptions);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_SPV_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasChaManRs);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_STV_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_CMD_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus, [MarshalAs(UnmanagedType.I1)] bool hasCmTm, [MarshalAs(UnmanagedType.I1)] bool hasCmCt);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_ALM_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_CTE_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasHisRs);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr CDC_TMS_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasHisRs);
+
+ public const int CDC_OPTION_DESC = (1 << 2);
+ public const int CDC_OPTION_DESC_UNICODE = (1 << 3);
+ public const int CDC_OPTION_AC_DLNDA = (1 << 4);
+ public const int CDC_OPTION_AC_DLN = (1 << 5);
+
+ // options that are only valid for DPL CDC
+ public const int CDC_OPTION_DPL_HWREV = (1 << 17);
+ public const int CDC_OPTION_DPL_SWREV = (1 << 18);
+ public const int CDC_OPTION_DPL_SERNUM = (1 << 19);
+ public const int CDC_OPTION_DPL_MODEL = (1 << 20);
+ public const int CDC_OPTION_DPL_LOCATION = (1 << 21);
+
+ // mandatory data attributes for LLN0 (e.g. LBL configRef)
+ public const int CDC_OPTION_AC_LN0_M = (1 << 24);
+ public const int CDC_OPTION_AC_LN0_EX = (1 << 25);
+ public const int CDC_OPTION_AC_DLD_M = (1 << 26);
+
+
+ public static DataObject Create_CDC_SPS(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_SPS_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject (self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_DPS(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_DPS_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_VSS(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_VSS_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_SEC(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_SEC_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_CMV(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_CMV_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_SAV(ModelNode parent, string name, uint options, bool isIntegerNotFloat)
+ {
+ IntPtr self = CDC_SAV_create(name, parent.self, options, isIntegerNotFloat);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ACD(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_ACD_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ACT(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_ACT_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_SPG(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_SPG_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_VSG(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_VSG_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ENG(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_ENG_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ING(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_ING_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ASG(ModelNode parent, string name, uint options, bool isIntegerNotFloat)
+ {
+ IntPtr self = CDC_ASG_create(name, parent.self, options, isIntegerNotFloat);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_WYE(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_WYE_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_DEL(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_DEL_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_HST(ModelNode parent, string name, uint options, ushort maxPts)
+ {
+ IntPtr self = CDC_HST_create(name, parent.self, options, maxPts);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_INS(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_INS_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject (self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_MV(ModelNode parent, string name, uint options, bool isIntegerNotFloat)
+ {
+ IntPtr self = CDC_MV_create(name, parent.self, options, isIntegerNotFloat);
+
+ if (self != IntPtr.Zero)
+ return new DataObject (self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_INC(ModelNode parent, string name, uint options, uint controlOptions)
+ {
+ IntPtr self = CDC_INC_create(name, parent.self, options, controlOptions);
+
+ if (self != IntPtr.Zero)
+ return new DataObject (self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_LPL(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_LPL_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject (self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_DPL(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_DPL_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject (self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ENS(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_ENS_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_SPC(ModelNode parent, string name, uint options, uint controlOptions)
+ {
+ IntPtr self = CDC_SPC_create(name, parent.self, options, controlOptions);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_DPC(ModelNode parent, string name, uint options, uint controlOptions)
+ {
+ IntPtr self = CDC_DPC_create(name, parent.self, options, controlOptions);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_BSC(ModelNode parent, string name, uint options, uint controlOptions, bool hasTransientIndicator)
+ {
+ IntPtr self = CDC_BSC_create(name, parent.self, options, controlOptions, hasTransientIndicator);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_APC(ModelNode parent, string name, uint options, uint controlOptions, bool isIntegerNotFloat)
+ {
+ IntPtr self = CDC_APC_create(name, parent.self, options, controlOptions, isIntegerNotFloat);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_BCR(ModelNode parent, string name, uint options)
+ {
+ IntPtr self = CDC_BCR_create(name, parent.self, options);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ENC(ModelNode parent, string name, uint options, uint controlOptions)
+ {
+ IntPtr self = CDC_ENC_create(name, parent.self, options, controlOptions);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_SPV(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasChaManRs)
+ {
+ IntPtr self = CDC_SPV_create(name, parent.self, options, controlOptions, wpOptions, hasChaManRs);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_STV(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus)
+ {
+ IntPtr self = CDC_STV_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_CMD(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus, bool hasCmTm, bool hasCmCt)
+ {
+ IntPtr self = CDC_CMD_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus, hasCmTm, hasCmCt);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_ALM(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus)
+ {
+ IntPtr self = CDC_ALM_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_CTE(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasHisRs)
+ {
+ IntPtr self = CDC_CTE_create(name, parent.self, options, controlOptions, wpOptions, hasHisRs);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+
+ public static DataObject Create_CDC_TMS(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasHisRs)
+ {
+ IntPtr self = CDC_TMS_create(name, parent.self, options, controlOptions, wpOptions, hasHisRs);
+
+ if (self != IntPtr.Zero)
+ return new DataObject(self, parent);
+ else
+ return null;
+ }
+ }
+
+ public class DataObject : ModelNode
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DataObject_create(string name, IntPtr parent, int arrayElements);
+
+ internal DataObject(IntPtr self, ModelNode parent) : base(self)
+ {
+ this.parent = parent;
+ }
+
+ public DataObject(string name, ModelNode parent) : this(name, parent, 0)
+ {
+ }
+
+ public DataObject(string name, ModelNode parent, int arrayElements)
+ {
+ this.parent = parent;
+
+ self = DataObject_create (name, parent.self, arrayElements);
+ }
+
+ }
+
+ public class DataAttribute : ModelNode
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DataAttribute_create(string name, IntPtr parent, int type, int fc,
+ byte triggerOptions, int arrayElements, UInt32 sAddr);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void DataAttribute_setValue(IntPtr self, IntPtr mmsValue);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int DataAttribute_getType(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern byte DataAttribute_getTrgOps(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int DataAttribute_getFC(IntPtr self);
+
+ internal DataAttribute(IntPtr self, ModelNode parent) : base(self)
+ {
+ this.parent = parent;
+ }
+
+ ///
+ /// Create a new data attribute and add it to a parent model node
+ ///
+ /// The parent model node has to be of type DataObject or DataAttribute
+ /// the name of the data attribute (e.g. "stVal")
+ /// the parent model node (of type DataObject or DataAttribute)
+ /// the type of the data attribute (CONSTRUCTED if the type contains sub data attributes)
+ /// the functional constraint (FC) of the data attribute
+ /// the trigger options (dupd, dchg, qchg) that cause an event notification
+ /// the number of array elements if the data attribute is an array or 0
+ /// an optional short address (deprecated)
+ public DataAttribute (string name, ModelNode parent, DataAttributeType type, FunctionalConstraint fc, TriggerOptions trgOps,
+ int arrayElements, UInt32 sAddr)
+ {
+ this.parent = parent;
+
+ self = DataAttribute_create (name, parent.self, (int)type, (int)fc, (byte)trgOps, arrayElements, sAddr);
+ }
+
+ ///
+ /// Create a new data attribute and add it to a parent model node
+ ///
+ /// The parent model node has to be of type DataObject or DataAttribute
+ /// the name of the data attribute (e.g. "stVal")
+ /// the parent model node (of type DataObject or DataAttribute)
+ /// the type of the data attribute (CONSTRUCTED if the type contains sub data attributes)
+ /// the functional constraint (FC) of the data attribute
+ /// the trigger options (dupd, dchg, qchg) that cause an event notification
+ /// the number of array elements if the data attribute is an array or 0
+ public DataAttribute(string name, ModelNode parent, DataAttributeType type, FunctionalConstraint fc, TriggerOptions trgOps,
+ int arrayElements)
+ {
+ this.parent = parent;
+
+ self = DataAttribute_create(name, parent.self, (int)type, (int)fc, (byte)trgOps, arrayElements, 0);
+ }
+
+ ///
+ /// Get IEC 61850 data attribute type of the data attribute
+ ///
+ public DataAttributeType Type
+ {
+ get
+ {
+ return (DataAttributeType)DataAttribute_getType(self);
+ }
+ }
+
+ ///
+ /// The trigger options (dchg, qchg, dupd) of the data attribute
+ ///
+ public TriggerOptions TrgOps
+ {
+ get
+ {
+ return (TriggerOptions)DataAttribute_getTrgOps(self);
+ }
+ }
+
+ ///
+ /// The functional constraint (FC) of the data attribute
+ ///
+ public FunctionalConstraint FC
+ {
+ get
+ {
+ return (FunctionalConstraint)DataAttribute_getFC(self);
+ }
+ }
+
+ ///
+ /// Set the value of the data attribute (can be used to set default values before server is created)
+ ///
+ /// New value for the data attribute
+ public void SetValue(MmsValue value)
+ {
+ DataAttribute_setValue(self, value.valueReference);
+ }
+ }
+
+ public class ModelNode
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ModelNode_getChild(IntPtr self, string name);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int ModelNode_getType(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ModelNode_getObjectReference(IntPtr self, IntPtr objectReference);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ModelNode_getObjectReferenceEx(IntPtr self, IntPtr objectReference, [MarshalAs(UnmanagedType.I1)] bool withoutIedName);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ModelNode_getParent(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ModelNode_getChildren(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ModelNode_getName(IntPtr self);
+
+ /****************
+ * LinkedList
+ ***************/
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr LinkedList_getNext(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr LinkedList_getData(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void LinkedList_destroyStatic(IntPtr self);
+
+ public IntPtr self;
+
+ internal ModelNode parent = null;
+
+ static internal IntPtr GetNativeParent(IntPtr self)
+ {
+ return ModelNode_getParent(self);
+ }
+
+ internal ModelNode()
+ {
+ self = IntPtr.Zero;
+ }
+
+ internal ModelNode(IntPtr self, ModelNode parent)
+ {
+ this.self = self;
+ this.parent = parent;
+ }
+
+ internal ModelNode(IntPtr self)
+ {
+ this.self = self;
+ }
+
+ ///
+ /// Gets the IedModel for this ModelNode instance
+ ///
+ /// the IedModel instance of this ModelNode.
+ public IedModel GetIedModel()
+ {
+ if (this is LogicalDevice)
+ {
+ return (this as LogicalDevice).IedModel;
+ }
+ else
+ {
+ if (this.parent != null)
+ return parent.GetIedModel();
+ else
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the name of the model node
+ ///
+ /// name of the model node
+ public string GetName()
+ {
+ return Marshal.PtrToStringAnsi(ModelNode_getName(self));
+ }
+
+ ///
+ /// Gets the parent node of this model node
+ ///
+ /// The parent node
+ public ModelNode GetParent()
+ {
+ return parent;
+ }
+
+
+ ///
+ /// Get the child node of this model node with the given name
+ ///
+ /// The child node or null when there is no child with the given name
+ /// name of the child node
+ public ModelNode GetChild(string name)
+ {
+ IntPtr childPtr = ModelNode_getChild(self, name);
+
+ if (childPtr == IntPtr.Zero)
+ return null;
+
+ ModelNode child = null;
+
+ IedModel iedModel = GetIedModel();
+
+ if (iedModel != null)
+ {
+ iedModel.modelNodes.TryGetValue(childPtr, out child);
+ }
+
+ if (child == null)
+ {
+ int nodeType = ModelNode_getType(childPtr);
+
+ switch (nodeType)
+ {
+ case 0:
+ child = new LogicalDevice(childPtr, iedModel);
+ break;
+
+ case 1:
+ child = new LogicalNode(childPtr, this);
+ break;
+
+ case 2:
+ child = new DataObject(childPtr, this);
+ break;
+
+ case 3:
+ child = new DataAttribute(childPtr, this);
+ break;
+
+ default:
+ child = new ModelNode(childPtr, this);
+ break;
+ }
+
+ if (child != null && iedModel != null)
+ {
+ iedModel.modelNodes.Add(childPtr, child);
+ }
+ }
+
+ return child;
+ }
+
+ internal static ModelNode CreateInstance(IntPtr instPtr, ModelNode parent)
+ {
+ int nodeType = ModelNode_getType(instPtr);
+
+ ModelNode newInstance = null;
+
+ switch (nodeType)
+ {
+ case 1:
+ newInstance = new LogicalNode(instPtr, parent);
+ break;
+
+ case 2:
+ newInstance = new DataObject(instPtr, parent);
+ break;
+
+ case 3:
+ newInstance = new DataAttribute(instPtr, parent);
+ break;
+
+ default:
+ newInstance = new ModelNode(instPtr, parent);
+ break;
+ }
+
+ return newInstance;
+ }
+
+ ///
+ /// Gets the direct child nodes of this ModelNode instance
+ ///
+ /// List of child nodes
+ public LinkedList GetChildren()
+ {
+ LinkedList children = new LinkedList();
+
+ IntPtr childListPtr = ModelNode_getChildren(self);
+
+ if (childListPtr != IntPtr.Zero)
+ {
+ IedModel iedModel = GetIedModel();
+
+ IntPtr listElem = LinkedList_getNext(childListPtr);
+
+ while (listElem != IntPtr.Zero)
+ {
+ IntPtr modelNodePtr = LinkedList_getData(listElem);
+
+ ModelNode childNode = null;
+
+ if (iedModel != null)
+ {
+ iedModel.modelNodes.TryGetValue(modelNodePtr, out childNode);
+ }
+
+ if (childNode == null)
+ {
+ childNode = ModelNode.CreateInstance(modelNodePtr, this);
+
+ if ((childNode != null) && (iedModel != null))
+ {
+ iedModel.modelNodes.Add(modelNodePtr, childNode);
+ }
+
+ }
+
+ if (childNode != null)
+ children.AddLast(childNode);
+
+ listElem = LinkedList_getNext(listElem);
+ }
+
+ LinkedList_destroyStatic(childListPtr);
+ }
+
+ return children;
+ }
+
+ ///
+ /// Gets the object reference of the model node
+ ///
+ /// the object reference
+ /// If set to true the object reference is created without IED name.
+ public string GetObjectReference(bool withoutIedName = false)
+ {
+ IntPtr nativeMemory = Marshal.AllocHGlobal(130);
+
+ IntPtr objRefPtr = ModelNode_getObjectReferenceEx(self, nativeMemory, withoutIedName);
+
+ if (objRefPtr != IntPtr.Zero) {
+ string objRef = Marshal.PtrToStringAnsi(objRefPtr);
+
+ Marshal.FreeHGlobal(nativeMemory);
+
+ return objRef;
+ }
+ else {
+ Marshal.FreeHGlobal(nativeMemory);
+
+ return null;
+ }
+ }
+
+ }
+
+ public class DataSet
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DataSet_create(string name, IntPtr parent);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DataSet_getName(IntPtr self);
+
+ public IntPtr self = IntPtr.Zero;
+
+ internal DataSet(IntPtr dataSetPtr)
+ {
+ self = dataSetPtr;
+ }
+
+ public DataSet(string name, LogicalNode parent)
+ {
+ self = DataSet_create(name, parent.self);
+ }
+
+ public string Name
+ {
+ get
+ {
+ return Marshal.PtrToStringAnsi(DataSet_getName(self));
+ }
+ }
+ }
+
+ public class DataSetEntry
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DataSetEntry_create(IntPtr dataSet, string variable, int index, string component);
+
+ public IntPtr self = IntPtr.Zero;
+
+ public DataSetEntry(DataSet dataSet, string variable, int index, string component)
+ {
+ self = DataSetEntry_create(dataSet.self, variable, index, component);
+ }
+ }
+
+ ///
+ /// Report control block (RCB) instance for server data model
+ ///
+ public class ReportControlBlock
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ReportControlBlock_create(string name, IntPtr parent, string rptId, [MarshalAs(UnmanagedType.I1)] bool isBuffered,
+ string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void ReportControlBlock_setPreconfiguredClient(IntPtr self, byte type, [Out] byte[] buf);
+
+ public IntPtr self = IntPtr.Zero;
+
+ public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered,
+ string dataSetName, uint confRev, byte trgOps, byte options, uint bufTm, uint intgPd)
+ {
+ self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd);
+ }
+
+ public void SetPreconfiguredClient(byte[] clientAddress)
+ {
+ if (clientAddress.Length == 4)
+ ReportControlBlock_setPreconfiguredClient(self, 4, clientAddress);
+ else if (clientAddress.Length == 6)
+ ReportControlBlock_setPreconfiguredClient(self, 6, clientAddress);
+ }
+ }
+
+ ///
+ /// GOOSE/GSE control block instance for server data model
+ ///
+ public class GSEControlBlock
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr GSEControlBlock_create(string name, IntPtr parent, string appId, string dataSet, UInt32 confRev,
+ [MarshalAs(UnmanagedType.I1)] bool fixedOffs, int minTime, int maxTime);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void GSEControlBlock_addPhyComAddress(IntPtr self, IntPtr phyComAddress);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr PhyComAddress_create(byte vlanPriority, UInt16 vlanId, UInt16 appId, [Out] byte[] buf);
+
+ internal IntPtr self = IntPtr.Zero;
+
+ public GSEControlBlock(string name, LogicalNode parent, string appId, string dataSetName, UInt32 confRev, bool fixedOffs, int minTime, int maxTime)
+ {
+ self = GSEControlBlock_create(name, parent.self, appId, dataSetName, confRev, fixedOffs, minTime, maxTime);
+ }
+
+ public void AddPhyComAddress(PhyComAddress addr)
+ {
+ IntPtr phyComAddrPtr = PhyComAddress_create(addr.vlanPriority, addr.vlanId, addr.appId, addr.dstAddress);
+
+ if (phyComAddrPtr != IntPtr.Zero)
+ {
+ GSEControlBlock_addPhyComAddress(self, phyComAddrPtr);
+ }
+ else
+ {
+ Console.WriteLine("ERROR: Failed to create native PhyComAddress instance!");
+ }
+ }
+ }
+
+ ///
+ /// Log control block (LCB) instance for server data model
+ ///
+ public class LogControlBlock
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr LogControlBlock_create(string name, IntPtr parent, string dataSetName, string logRef, byte trgOps, UInt32 intPeriod,
+ [MarshalAs(UnmanagedType.I1)] bool logEna, [MarshalAs(UnmanagedType.I1)] bool reasonCode);
+
+ internal IntPtr self = IntPtr.Zero;
+
+ public LogControlBlock(string name, LogicalNode parent, string dataSet, string logRef, byte trgOps, UInt32 intPerdiod, bool logEna, bool reasonCode)
+ {
+ self = LogControlBlock_create(name, parent.self, dataSet, logRef, trgOps, intPerdiod, logEna, reasonCode);
+ }
+ }
+
+ ///
+ /// Setting group control block for server data model
+ ///
+ public class SettingGroupControlBlock
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr SettingGroupControlBlock_create(IntPtr parent, byte actSG, byte numOfSGs);
+
+ internal IntPtr self = IntPtr.Zero;
+
+ public SettingGroupControlBlock(LogicalNode parent, UInt32 actSG, UInt32 numOfSGs)
+ {
+ self = SettingGroupControlBlock_create(parent.self, (byte) actSG, (byte) numOfSGs);
+ }
+ }
+
+ public class ClientConnection
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ClientConnection_getPeerAddress(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ClientConnection_getLocalAddress(IntPtr self);
+
+ internal IntPtr self;
+
+ internal ClientConnection (IntPtr self) {
+ this.self = self;
+ }
+
+ public string GetPeerAddress()
+ {
+ IntPtr peerAddrPtr = ClientConnection_getPeerAddress (self);
+
+ if (peerAddrPtr != IntPtr.Zero)
+ return Marshal.PtrToStringAnsi (peerAddrPtr);
+ else
+ return null;
+ }
+
+ public string GetLocalAddress()
+ {
+ IntPtr localAddrPtr = ClientConnection_getLocalAddress(self);
+
+ if (localAddrPtr != IntPtr.Zero)
+ return Marshal.PtrToStringAnsi(localAddrPtr);
+ else
+ return null;
+ }
+ }
+
+ public class MmsGooseControlBlock
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr MmsGooseControlBlock_getName(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr MmsGooseControlBlock_getLogicalNode(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr MmsGooseControlBlock_getDataSet(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool MmsGooseControlBlock_getGoEna(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int MmsGooseControlBlock_getMinTime(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int MmsGooseControlBlock_getMaxTime(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool MmsGooseControlBlock_getFixedOffs(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool MmsGooseControlBlock_getNdsCom(IntPtr self);
+
+ private IntPtr self;
+ private LogicalNode ln = null;
+ private DataSet dataSet = null;
+
+ internal MmsGooseControlBlock(IntPtr self, IedModel iedModel)
+ {
+ this.self = self;
+
+ IntPtr lnPtr = MmsGooseControlBlock_getLogicalNode(self);
+
+ ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(lnPtr);
+
+ if (lnModelNode != null && lnModelNode is LogicalNode)
+ {
+ this.ln = lnModelNode as LogicalNode;
+ }
+ }
+
+ public string Name
+ {
+ get
+ {
+ return Marshal.PtrToStringAnsi(MmsGooseControlBlock_getName(self));
+ }
+ }
+
+ public LogicalNode LN
+ {
+ get
+ {
+ return ln;
+ }
+ }
+
+ public DataSet DataSet
+ {
+ get
+ {
+ if (dataSet == null)
+ dataSet = new DataSet(MmsGooseControlBlock_getDataSet(self));
+
+ return dataSet;
+ }
+ }
+
+ public bool GoEna
+ {
+ get
+ {
+ return MmsGooseControlBlock_getGoEna(self);
+ }
+ }
+
+ public int MinTime
+ {
+ get
+ {
+ return MmsGooseControlBlock_getMinTime(self);
+ }
+ }
+
+ public int MaxTime
+ {
+ get
+ {
+ return MmsGooseControlBlock_getMaxTime(self);
+ }
+ }
+
+ public bool FixedOffs
+ {
+ get
+ {
+ return MmsGooseControlBlock_getFixedOffs(self);
+ }
+ }
+
+ public bool NdsCom
+ {
+ get
+ {
+ return MmsGooseControlBlock_getNdsCom(self);
+ }
+ }
+ }
+
+ ///
+ /// Represents additional context information of the control action that caused the callback invokation
+ ///
+ public class ControlAction
+ {
+ [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void ControlAction_setAddCause (IntPtr self, int addCause);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void ControlAction_setError(IntPtr self, int error);
+
+ [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int ControlAction_getOrCat (IntPtr self);
+
+ [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ControlAction_getOrIdent (IntPtr self, ref int size);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ControlAction_getClientConnection(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int ControlAction_getCtlNum(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern UInt64 ControlAction_getControlTime(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool ControlAction_isSelect(IntPtr self);
+
+ private IntPtr self;
+ private IedServer.ControlHandlerInfo info;
+ private IedServer iedServer;
+
+ internal ControlAction (IntPtr self, IedServer.ControlHandlerInfo info, IedServer iedServer)
+ {
+ this.self = self;
+ this.info = info;
+ this.iedServer = iedServer;
+ }
+
+ ///
+ /// Sets the error code for the next command termination or application error message.
+ ///
+ /// the errror code to use
+ public void SetError(ControlLastApplError error)
+ {
+ ControlAction_setError(self, (int)error);
+ }
+
+ ///
+ /// Sets the add cause for the next command termination or application error message
+ ///
+ /// the additional cause code
+ public void SetAddCause (ControlAddCause addCause)
+ {
+ ControlAction_setAddCause (self, (int)addCause);
+ }
+
+ ///
+ /// Gets the originator category provided by the client
+ ///
+ /// The or cat.
+ public OrCat GetOrCat ()
+ {
+ return (OrCat)ControlAction_getOrCat (self);
+ }
+
+ ///
+ /// Get the originator identifier provided by the client
+ ///
+ /// The or ident.
+ public byte [] GetOrIdent ()
+ {
+ int size = 0;
+
+ IntPtr orIdentPtr = ControlAction_getOrIdent (self, ref size);
+
+ if (orIdentPtr == IntPtr.Zero)
+ return null;
+
+ byte [] orIdent = new byte [size];
+
+ Marshal.Copy (orIdentPtr, orIdent, 0, size);
+
+ return orIdent;
+ }
+
+ ///
+ /// Gets the ctlNum attribute of the control action
+ ///
+ /// The ctlNum value. Valid values are restricted from 0 to 255, -1 means not present
+ public int GetCtlNum()
+ {
+ return ControlAction_getCtlNum(self);
+ }
+
+ ///
+ /// Gets the control object that is subject to this action
+ ///
+ /// the controllable data object instance
+ public DataObject GetControlObject ()
+ {
+ return info.controlObject;
+ }
+
+ ///
+ /// Gets the time of control execution, if it's a time activated control
+ ///
+ /// The time of control execution or 0 for immediate execution
+ public UInt64 GetControlTime()
+ {
+ return ControlAction_getControlTime(self);
+ }
+
+ ///
+ /// Gets the tome of control execution as data time offset.
+ ///
+ /// The control execution time as data time offset.
+ public DateTimeOffset GetControlTimeAsDataTimeOffset()
+ {
+ return MmsValue.MsTimeToDateTimeOffset(GetControlTime());
+ }
+
+ ///
+ /// Gets the client object associated with the client that caused the control action
+ ///
+ /// The client connection.
+ public ClientConnection GetClientConnection ()
+ {
+ ClientConnection con = null;
+
+ IntPtr conPtr = ControlAction_getClientConnection (self);
+
+ if (conPtr != IntPtr.Zero) {
+ iedServer.clientConnections.TryGetValue (conPtr, out con);
+ }
+
+ return con;
+ }
+
+ ///
+ /// Cehck if the control callback is called by a select or operate command
+ ///
+ /// true, if select, false otherwise.
+ public bool IsSelect()
+ {
+ return ControlAction_isSelect(self);
+ }
+ }
+
+ public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
+
+ public delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value,
+ ClientConnection connection, object parameter);
+
+ ///
+ /// Reason for the select state change
+ ///
+ public enum SelectStateChangedReason
+ {
+ ///
+ /// Control has been selected
+ ///
+ SELECT_STATE_REASON_SELECTED = 0,
+ ///
+ /// Cancel received for the control
+ ///
+ SELECT_STATE_REASON_CANCELED = 1,
+ ///
+ /// Unselected due to timeout (sboTimeout)
+ ///
+ SELECT_STATE_REASON_TIMEOUT = 2,
+ ///
+ /// Unselected due to successful operate
+ ///
+ SELECT_STATE_REASON_OPERATED = 3,
+ ///
+ /// Unselected due to failed operate
+ ///
+ SELECT_STATE_REASON_OPERATE_FAILED = 4,
+ ///
+ /// Unselected due to disconnection of selecting client
+ ///
+ SELECT_STATE_REASON_DISCONNECTED = 5
+ }
+
+ public delegate void ControlSelectStateChangedHandler(ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason);
+
+ ///
+ /// Return type of ControlHandler and ControlWaitForExecutionHandler
+ ///
+ public enum ControlHandlerResult {
+ ///
+ /// check or operation failed
+ ///
+ FAILED = 0,
+ ///
+ /// check or operation was successful
+ ///
+ OK = 1,
+ ///
+ /// check or operation is in progress
+ ///
+ WAITING = 2
+ }
+
+ public delegate ControlHandlerResult ControlWaitForExecutionHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool synchroCheck);
+
+ public delegate ControlHandlerResult ControlHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test);
+
+ public enum CheckHandlerResult {
+ ///
+ /// check passed
+ ///
+ ACCEPTED = -1,
+ ///
+ /// check failed due to hardware fault
+ ///
+ HARDWARE_FAULT = 1,
+ ///
+ /// control is already selected or operated
+ ///
+ TEMPORARILY_UNAVAILABLE = 2,
+ ///
+ /// check failed due to access control reason - access denied for this client or state
+ ///
+ OBJECT_ACCESS_DENIED = 3,
+ ///
+ /// object not visible in this security context ???
+ ///
+ OBJECT_UNDEFINED = 4
+ }
+
+ public delegate CheckHandlerResult CheckHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck);
+
+ ///
+ /// This class acts as the entry point for the IEC 61850 client API. It represents a single
+ /// (MMS) connection to a server.
+ ///
+ public class IedServer : IDisposable
+ {
+ [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
+ static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setLocalIpAddress(IntPtr self, string localIpAddress);
+
+ [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
+ static extern void IedServer_start(IntPtr self, int tcpPort);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_stop(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_destroy(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool IedServer_isRunning(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int IedServer_getNumberOfOpenConnections(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_lockDataModel(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_unlockDataModel(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateAttributeValue(IntPtr self, IntPtr DataAttribute, IntPtr MmsValue);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateBooleanAttributeValue(IntPtr self, IntPtr dataAttribute, [MarshalAs(UnmanagedType.I1)] bool value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateInt32AttributeValue(IntPtr self, IntPtr dataAttribute, int value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateInt64AttributeValue(IntPtr self, IntPtr dataAttribute, Int64 value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateFloatAttributeValue(IntPtr self, IntPtr dataAttribute, float value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateVisibleStringAttributeValue(IntPtr self, IntPtr dataAttribute, string value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateUTCTimeAttributeValue(IntPtr self, IntPtr dataAttribute, ulong value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateTimestampAttributeValue(IntPtr self, IntPtr dataAttribute, IntPtr timestamp);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_updateQuality(IntPtr self, IntPtr dataAttribute, ushort value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setServerIdentity(IntPtr self, string vendor, string model, string revision);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int InternalControlPerformCheckHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int InternalControlWaitForExecutionHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool synchoCheck);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int InternalControlHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void InternalSelectStateChangedHandler(IntPtr action, IntPtr parameter, [MarshalAs(UnmanagedType.I1)] bool isSelected, int reason);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setWaitForExecutionHandler(IntPtr self, IntPtr node, InternalControlWaitForExecutionHandler handler, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setPerformCheckHandler(IntPtr self, IntPtr node, InternalControlPerformCheckHandler handler, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setControlHandler (IntPtr self, IntPtr node, InternalControlHandler handler, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setSelectStateChangedHandler(IntPtr self, IntPtr node, InternalSelectStateChangedHandler handler, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setWriteAccessPolicy(IntPtr self, FunctionalConstraint fc, AccessPolicy policy);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int InternalWriteAccessHandler (IntPtr dataAttribute, IntPtr value, IntPtr connection, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_handleWriteAccess(IntPtr self, IntPtr dataAttribute,
+ InternalWriteAccessHandler handler, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_handleWriteAccessForComplexAttribute(IntPtr self, IntPtr dataAttribute,
+ InternalWriteAccessHandler handler, IntPtr parameter);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_APC_create(string name, IntPtr parent, uint options, uint controlOptions, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_BCR_create(string name, IntPtr parent, uint options);
+ public delegate void ConnectionIndicationHandler(IedServer iedServer, ClientConnection clientConnection, bool connected, object parameter);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ENC_create(string name, IntPtr parent, uint options, uint controlOptions);
+ private ConnectionIndicationHandler connectionHandler = null;
+ private object connectionHandlerParameter = null;
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_SPV_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasChaManRs);
+ public void SetConnectionIndicationHandler(ConnectionIndicationHandler handler, object parameter)
+ {
+ connectionHandler = handler;
+ connectionHandlerParameter = parameter;
+ }
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_STV_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void InternalConnectionHandler (IntPtr iedServer, IntPtr clientConnection, [MarshalAs(UnmanagedType.I1)] bool connected, IntPtr parameter);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_CMD_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus, [MarshalAs(UnmanagedType.I1)] bool hasCmTm, [MarshalAs(UnmanagedType.I1)] bool hasCmCt);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setConnectionIndicationHandler(IntPtr self, InternalConnectionHandler handler, IntPtr parameter);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_ALM_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_enableGoosePublishing(IntPtr self);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_CTE_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasHisRs);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_disableGoosePublishing(IntPtr self);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr CDC_TMS_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasHisRs);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setGooseInterfaceId(IntPtr self, string interfaceId);
- public const int CDC_OPTION_DESC = (1 << 2);
- public const int CDC_OPTION_DESC_UNICODE = (1 << 3);
- public const int CDC_OPTION_AC_DLNDA = (1 << 4);
- public const int CDC_OPTION_AC_DLN = (1 << 5);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setGooseInterfaceIdEx(IntPtr self, IntPtr ln, string gcbName, string interfaceId);
- // options that are only valid for DPL CDC
- public const int CDC_OPTION_DPL_HWREV = (1 << 17);
- public const int CDC_OPTION_DPL_SWREV = (1 << 18);
- public const int CDC_OPTION_DPL_SERNUM = (1 << 19);
- public const int CDC_OPTION_DPL_MODEL = (1 << 20);
- public const int CDC_OPTION_DPL_LOCATION = (1 << 21);
-
- // mandatory data attributes for LLN0 (e.g. LBL configRef)
- public const int CDC_OPTION_AC_LN0_M = (1 << 24);
- public const int CDC_OPTION_AC_LN0_EX = (1 << 25);
- public const int CDC_OPTION_AC_DLD_M = (1 << 26);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_useGooseVlanTag(IntPtr self, IntPtr ln, string gcbName, [MarshalAs(UnmanagedType.I1)] bool useVlanTag);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void InternalGoCBEventHandler(IntPtr goCB, int cbEvent, IntPtr parameter);
- public static DataObject Create_CDC_SPS(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_SPS_create(name, parent.self, options);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setGoCBHandler(IntPtr self, InternalGoCBEventHandler handler, IntPtr parameter);
- if (self != IntPtr.Zero)
- return new DataObject (self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_DPS(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_DPS_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_VSS(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_VSS_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_SEC(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_SEC_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_CMV(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_CMV_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_SAV(ModelNode parent, string name, uint options, bool isIntegerNotFloat)
- {
- IntPtr self = CDC_SAV_create(name, parent.self, options, isIntegerNotFloat);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ACD(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_ACD_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ACT(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_ACT_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_SPG(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_SPG_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_VSG(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_VSG_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ENG(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_ENG_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ING(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_ING_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ASG(ModelNode parent, string name, uint options, bool isIntegerNotFloat)
- {
- IntPtr self = CDC_ASG_create(name, parent.self, options, isIntegerNotFloat);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_WYE(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_WYE_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_DEL(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_DEL_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_HST(ModelNode parent, string name, uint options, ushort maxPts)
- {
- IntPtr self = CDC_HST_create(name, parent.self, options, maxPts);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_INS(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_INS_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject (self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_MV(ModelNode parent, string name, uint options, bool isIntegerNotFloat)
- {
- IntPtr self = CDC_MV_create(name, parent.self, options, isIntegerNotFloat);
-
- if (self != IntPtr.Zero)
- return new DataObject (self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_INC(ModelNode parent, string name, uint options, uint controlOptions)
- {
- IntPtr self = CDC_INC_create(name, parent.self, options, controlOptions);
-
- if (self != IntPtr.Zero)
- return new DataObject (self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_LPL(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_LPL_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject (self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_DPL(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_DPL_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject (self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ENS(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_ENS_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_SPC(ModelNode parent, string name, uint options, uint controlOptions)
- {
- IntPtr self = CDC_SPC_create(name, parent.self, options, controlOptions);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_DPC(ModelNode parent, string name, uint options, uint controlOptions)
- {
- IntPtr self = CDC_DPC_create(name, parent.self, options, controlOptions);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_BSC(ModelNode parent, string name, uint options, uint controlOptions, bool hasTransientIndicator)
- {
- IntPtr self = CDC_BSC_create(name, parent.self, options, controlOptions, hasTransientIndicator);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_APC(ModelNode parent, string name, uint options, uint controlOptions, bool isIntegerNotFloat)
- {
- IntPtr self = CDC_APC_create(name, parent.self, options, controlOptions, isIntegerNotFloat);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_BCR(ModelNode parent, string name, uint options)
- {
- IntPtr self = CDC_BCR_create(name, parent.self, options);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ENC(ModelNode parent, string name, uint options, uint controlOptions)
- {
- IntPtr self = CDC_ENC_create(name, parent.self, options, controlOptions);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_SPV(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasChaManRs)
- {
- IntPtr self = CDC_SPV_create(name, parent.self, options, controlOptions, wpOptions, hasChaManRs);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_STV(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus)
- {
- IntPtr self = CDC_STV_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_CMD(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus, bool hasCmTm, bool hasCmCt)
- {
- IntPtr self = CDC_CMD_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus, hasCmTm, hasCmCt);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_ALM(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus)
- {
- IntPtr self = CDC_ALM_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_CTE(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasHisRs)
- {
- IntPtr self = CDC_CTE_create(name, parent.self, options, controlOptions, wpOptions, hasHisRs);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
-
- public static DataObject Create_CDC_TMS(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasHisRs)
- {
- IntPtr self = CDC_TMS_create(name, parent.self, options, controlOptions, wpOptions, hasHisRs);
-
- if (self != IntPtr.Zero)
- return new DataObject(self);
- else
- return null;
- }
- }
-
- public class DataObject : ModelNode
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr DataObject_create(string name, IntPtr parent, int arrayElements);
-
- internal DataObject(IntPtr self) : base(self)
- {
- }
-
- public DataObject(string name, ModelNode parent) : this(name, parent, 0)
- {
- }
-
- public DataObject(string name, ModelNode parent, int arrayElements)
- {
- self = DataObject_create (name, parent.self, arrayElements);
- }
-
- }
-
- public class DataAttribute : ModelNode
- {
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr DataAttribute_create(string name, IntPtr parent, int type, int fc,
- byte triggerOptions, int arrayElements, UInt32 sAddr);
+ private IntPtr self = IntPtr.Zero;
- internal DataAttribute(IntPtr self) : base(self)
- {
- }
-
- public DataAttribute (string name, ModelNode parent, DataAttributeType type, FunctionalConstraint fc, TriggerOptions trgOps,
- int arrayElements, UInt32 sAddr)
- {
- self = DataAttribute_create (name, parent.self, (int)type, (int)fc, (byte)trgOps, arrayElements, sAddr);
- }
-
- }
+ private InternalControlHandler internalControlHandlerRef = null;
+ private InternalControlPerformCheckHandler internalControlPerformCheckHandlerRef = null;
+ private InternalControlWaitForExecutionHandler internalControlWaitForExecutionHandlerRef = null;
+ private InternalSelectStateChangedHandler internalSelectedStateChangedHandlerRef = null;
- public class ModelNode
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr ModelNode_getChild(IntPtr self, string name);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int ModelNode_getType(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr ModelNode_getObjectReference(IntPtr self, IntPtr objectReference);
-
- public IntPtr self;
-
- internal ModelNode()
- {
- }
+ internal class ControlHandlerInfo {
+ public DataObject controlObject = null;
+ public GCHandle handle;
- public ModelNode(IntPtr self)
- {
- this.self = self;
- }
+ public ControlHandler controlHandler = null;
+ public object controlHandlerParameter = null;
- public ModelNode GetChild(string name)
- {
- IntPtr childPtr = ModelNode_getChild(self, name);
+ public CheckHandler checkHandler = null;
+ public object checkHandlerParameter = null;
- if (childPtr == IntPtr.Zero)
- return null;
+ public ControlWaitForExecutionHandler waitForExecHandler = null;
+ public object waitForExecHandlerParameter = null;
- int nodeType = ModelNode_getType (childPtr);
+ public ControlSelectStateChangedHandler selectStateChangedHandler = null;
+ public object selectStateChangedHandlerParameter = null;
- switch (nodeType) {
- case 0:
- return new LogicalDevice (childPtr);
+ public ControlHandlerInfo(DataObject controlObject)
+ {
+ this.controlObject = controlObject;
+ this.handle = GCHandle.Alloc(this);
+ }
- case 1:
- return new LogicalNode (childPtr);
+ ~ControlHandlerInfo()
+ {
+ this.handle.Free();
+ }
+ }
- case 2:
- return new DataObject (childPtr);
+ private Dictionary controlHandlers = new Dictionary ();
- case 3:
- return new DataAttribute (childPtr);
+ int InternalControlHandlerImpl (IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test)
+ {
+ GCHandle handle = GCHandle.FromIntPtr (parameter);
- default:
- return new ModelNode (childPtr);
- }
+ ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
- }
+ ControlAction controlAction = new ControlAction (action, info, this);
- public string GetObjectReference()
- {
- IntPtr objRefPtr = ModelNode_getObjectReference(self, IntPtr.Zero);
+ if (info != null && info.controlHandler != null)
+ return (int)info.controlHandler (controlAction, info.controlHandlerParameter, new MmsValue (ctlVal), test);
+ else
+ return (int)ControlHandlerResult.FAILED;
+ }
- if (objRefPtr != IntPtr.Zero) {
- string objRef = Marshal.PtrToStringAnsi(objRefPtr);
+ int InternalCheckHandlerImpl(IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test, bool interlockCheck)
+ {
+ GCHandle handle = GCHandle.FromIntPtr (parameter);
- Marshal.FreeHGlobal(objRefPtr);
+ ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
- return objRef;
- }
- else {
- return null;
- }
- }
+ if (info != null && info.checkHandler != null)
+ {
+ ControlAction controlAction = new ControlAction (action, info, this);
- }
+ return (int)info.checkHandler (controlAction, info.checkHandlerParameter, new MmsValue (ctlVal), test, interlockCheck);
+ } else
+ return (int)CheckHandlerResult.OBJECT_UNDEFINED;
+ }
- public class DataSet
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr DataSet_create(string name, IntPtr parent);
+ int InternalControlWaitForExecutionHandlerImpl (IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test, bool synchoCheck)
+ {
+ GCHandle handle = GCHandle.FromIntPtr (parameter);
- public IntPtr self = IntPtr.Zero;
+ ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
- public DataSet(string name, LogicalNode parent)
- {
- self = DataSet_create(name, parent.self);
- }
- }
+ if (info != null && info.waitForExecHandler != null)
+ {
+ ControlAction controlAction = new ControlAction (action, info, this);
- public class DataSetEntry
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr DataSetEntry_create(IntPtr dataSet, string variable, int index, string component);
+ return (int)info.waitForExecHandler (controlAction, info.waitForExecHandlerParameter, new MmsValue (ctlVal), test, synchoCheck);
+ }
+ else
+ return (int)ControlHandlerResult.FAILED;
+ }
- public IntPtr self = IntPtr.Zero;
+ void InternalSelectStateChangedHandlerImpl(IntPtr action, IntPtr parameter, bool isSelected, int reason)
+ {
+ GCHandle handle = GCHandle.FromIntPtr(parameter);
- public DataSetEntry(DataSet dataSet, string variable, int index, string component)
- {
- self = DataSetEntry_create(dataSet.self, variable, index, component);
- }
- }
+ ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
- public class ReportControlBlock
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr ReportControlBlock_create(string name, IntPtr parent, string rptId, [MarshalAs(UnmanagedType.I1)] bool isBuffered,
- string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd);
+ if (info != null && info.selectStateChangedHandler != null)
+ {
+ ControlAction controlAction = new ControlAction(action, info, this);
- public IntPtr self = IntPtr.Zero;
+ info.selectStateChangedHandler(controlAction, info.selectStateChangedHandlerParameter, isSelected, (SelectStateChangedReason)reason);
+ }
+ }
- public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered,
- string dataSetName, uint confRev, byte trgOps, byte options, uint bufTm, uint intgPd)
- {
- self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd);
- }
- }
+ private struct WriteAccessHandlerInfo {
+ public WriteAccessHandler handler;
+ public InternalWriteAccessHandler internalHandler;
+ public object parameter;
+ public DataAttribute dataAttribute;
+
+ public WriteAccessHandlerInfo (WriteAccessHandler h, object p, DataAttribute da, InternalWriteAccessHandler internalHandler)
+ {
+ handler = h;
+ parameter = p;
+ dataAttribute = da;
+ this.internalHandler = internalHandler;
+ }
+ }
- public class ClientConnection
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr ClientConnection_getPeerAddress(IntPtr self);
+ int WriteAccessHandlerImpl (IntPtr dataAttribute, IntPtr value, IntPtr connection, IntPtr parameter)
+ {
+ WriteAccessHandlerInfo info;
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr ClientConnection_getLocalAddress(IntPtr self);
+ if (writeAccessHandlers.TryGetValue(dataAttribute, out info) == true)
+ {
+ ClientConnection con = null;
- internal IntPtr self;
+ clientConnections.TryGetValue(connection, out con);
- internal ClientConnection (IntPtr self) {
- this.self = self;
- }
+ MmsValue mmsValue = new MmsValue(value);
- public string GetPeerAddress()
- {
- IntPtr peerAddrPtr = ClientConnection_getPeerAddress (self);
+ return (int)info.handler(info.dataAttribute, mmsValue.Clone(), con, info.parameter);
+ }
+ else
+ {
+ return (int) MmsDataAccessError.OBJECT_ACCESS_DENIED;
+ }
+ }
- if (peerAddrPtr != IntPtr.Zero)
- return Marshal.PtrToStringAnsi (peerAddrPtr);
- else
- return null;
- }
+ private Dictionary writeAccessHandlers = new Dictionary ();
- public string GetLocalAddress()
- {
- IntPtr localAddrPtr = ClientConnection_getLocalAddress(self);
+ private void ConnectionIndicationHandlerImpl (IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter)
+ {
+ if (connected == false) {
+ ClientConnection con = null;
- if (localAddrPtr != IntPtr.Zero)
- return Marshal.PtrToStringAnsi(localAddrPtr);
- else
- return null;
- }
- }
+ clientConnections.TryGetValue (clientConnection, out con);
- ///
- /// Represents additional context information of the control action that caused the callback invokation
- ///
- public class ControlAction
- {
- [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void ControlAction_setAddCause (IntPtr self, int addCause);
+ if (con != null) {
+
+ if (connectionHandler != null)
+ connectionHandler (this, con, false, connectionHandlerParameter);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void ControlAction_setError(IntPtr self, int error);
+ clientConnections.Remove (clientConnection);
+ }
+ } else {
+ ClientConnection con = new ClientConnection (clientConnection);
- [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int ControlAction_getOrCat (IntPtr self);
+ clientConnections.Add (clientConnection, con);
- [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr ControlAction_getOrIdent (IntPtr self, ref int size);
+ if (connectionHandler != null)
+ connectionHandler (this, con, true, connectionHandlerParameter);
+ }
+ }
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr ControlAction_getClientConnection(IntPtr self);
+ internal Dictionary clientConnections = new Dictionary ();
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int ControlAction_getCtlNum(IntPtr self);
+ /* store IedModel instance to prevent garbage collector */
+ private IedModel iedModel = null;
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern UInt64 ControlAction_getControlTime(IntPtr self);
+ public IedServer(IedModel iedModel, IedServerConfig config = null)
+ {
+ this.iedModel = iedModel;
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool ControlAction_isSelect(IntPtr self);
+ IntPtr nativeConfig = IntPtr.Zero;
- private IntPtr self;
- private IedServer.ControlHandlerInfo info;
- private IedServer iedServer;
+ if (config != null)
+ nativeConfig = config.self;
- internal ControlAction (IntPtr self, IedServer.ControlHandlerInfo info, IedServer iedServer)
- {
- this.self = self;
- this.info = info;
- this.iedServer = iedServer;
+ self = IedServer_createWithConfig (iedModel.self, IntPtr.Zero, nativeConfig);
}
- ///
- /// Sets the error code for the next command termination or application error message.
- ///
- /// the errror code to use
- public void SetError(ControlLastApplError error)
+ public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
{
- ControlAction_setError(self, (int)error);
+ this.iedModel = iedModel;
+
+ IntPtr nativeConfig = IntPtr.Zero;
+ IntPtr nativeTLSConfig = IntPtr.Zero;
+
+ if (config != null)
+ nativeConfig = config.self;
+
+ if (tlsConfig != null)
+ nativeTLSConfig = tlsConfig.GetNativeInstance ();
+
+ self = IedServer_createWithConfig (iedModel.self, nativeTLSConfig, nativeConfig);
}
+ private InternalConnectionHandler internalConnectionHandler = null;
+
///
- /// Sets the add cause for the next command termination or application error message
+ /// Sets the local ip address for listening
///
- /// the additional cause code
- public void SetAddCause (ControlAddCause addCause)
+ /// Local IP address.
+ public void SetLocalIpAddress(string localIpAddress)
{
- ControlAction_setAddCause (self, (int)addCause);
+ IedServer_setLocalIpAddress (self, localIpAddress);
}
///
- /// Gets the originator category provided by the client
+ /// Start MMS server
///
- /// The or cat.
- public OrCat GetOrCat ()
+ /// Local IP address.
+ /// TCP port to use
+ public void Start(string localIpAddress, int tcpPort)
{
- return (OrCat)ControlAction_getOrCat (self);
+ SetLocalIpAddress (localIpAddress);
+ Start (tcpPort);
}
- ///
- /// Get the originator identifier provided by the client
- ///
- /// The or ident.
- public byte [] GetOrIdent ()
+ /// Start MMS server
+ /// TCP port to use
+ public void Start(int tcpPort)
{
- int size = 0;
-
- IntPtr orIdentPtr = ControlAction_getOrIdent (self, ref size);
-
- if (orIdentPtr == IntPtr.Zero)
- return null;
+ if (internalConnectionHandler == null)
+ internalConnectionHandler = new InternalConnectionHandler (ConnectionIndicationHandlerImpl);
- byte [] orIdent = new byte [size];
+ IedServer_setConnectionIndicationHandler (self, internalConnectionHandler, IntPtr.Zero);
- Marshal.Copy (orIdentPtr, orIdent, 0, size);
+ IedServer_start(self, tcpPort);
+ }
- return orIdent;
+ /// Start MMS server
+ public void Start ()
+ {
+ Start(-1);
}
///
- /// Gets the ctlNum attribute of the control action
+ /// Stop the MMS server.
///
- /// The ctlNum value. Valid values are restricted from 0 to 255, -1 means not present
- public int GetCtlNum()
+ /// This function will stop the server. This will close the TCP server socket and all client sockets.
+ public void Stop()
{
- return ControlAction_getCtlNum(self);
+ IedServer_stop(self);
+ internalConnectionHandler = null;
}
///
- /// Gets the control object that is subject to this action
+ /// Release all server resources (same as )
///
- /// the controllable data object instance
- public DataObject GetControlObject ()
+ /// This function releases all MMS server resources.
+ public void Destroy()
{
- return info.controlObject;
+ Dispose();
}
///
- /// Gets the time of control execution, if it's a time activated control
+ /// Releases all resource used by the object.
///
- /// The time of control execution or 0 for immediate execution
- public UInt64 GetControlTime()
+ /// Call when you are done using the . The
+ /// method leaves the in an unusable state. After
+ /// calling , you must release all references to the so
+ /// the garbage collector can reclaim the memory that the was occupying.
+ public void Dispose()
{
- return ControlAction_getControlTime(self);
+ lock (this)
+ {
+ if (self != IntPtr.Zero)
+ {
+ IedServer_destroy(self);
+ self = IntPtr.Zero;
+ internalConnectionHandler = null;
+ this.iedModel = null;
+ }
+ }
+ }
+
+ ~IedServer()
+ {
+ Dispose();
}
///
- /// Gets the tome of control execution as data time offset.
+ /// Set the identify for the MMS identify service
///
- /// The control execution time as data time offset.
- public DateTimeOffset GetControlTimeAsDataTimeOffset()
+ /// the IED vendor name
+ /// the IED model name
+ /// the IED revision/version number
+ public void SetServerIdentity(string vendor, string model, string revision)
{
- return MmsValue.MsTimeToDateTimeOffset(GetControlTime());
+ IedServer_setServerIdentity(self, vendor, model, revision);
}
///
- /// Gets the client object associated with the client that caused the control action
+ /// Check if server is running (accepting client connections)
///
- /// The client connection.
- public ClientConnection GetClientConnection ()
+ /// true, if running, false otherwise.
+ public bool IsRunning()
{
- ClientConnection con = null;
-
- IntPtr conPtr = ControlAction_getClientConnection (self);
-
- if (conPtr != IntPtr.Zero) {
- iedServer.clientConnections.TryGetValue (conPtr, out con);
- }
-
- return con;
+ return IedServer_isRunning(self);
}
///
- /// Cehck if the control callback is called by a select or operate command
+ /// Get number of open MMS connections
///
- /// true, if select, false otherwise.
- public bool IsSelect()
+ /// the number of open and accepted MMS connections
+ public int GetNumberOfOpenConnections()
{
- return ControlAction_isSelect(self);
+ return IedServer_getNumberOfOpenConnections(self);
}
- }
-
- public delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value,
- ClientConnection connection, object parameter);
-
- ///
- /// Reason for the select state change
- ///
- public enum SelectStateChangedReason
- {
- ///
- /// Control has been selected
- ///
- SELECT_STATE_REASON_SELECTED = 0,
- ///
- /// Cancel received for the control
- ///
- SELECT_STATE_REASON_CANCELED = 1,
- ///
- /// Unselected due to timeout (sboTimeout)
- ///
- SELECT_STATE_REASON_TIMEOUT = 2,
- ///
- /// Unselected due to successful operate
- ///
- SELECT_STATE_REASON_OPERATED = 3,
- ///
- /// Unselected due to failed operate
- ///
- SELECT_STATE_REASON_OPERATE_FAILED = 4,
- ///
- /// Unselected due to disconnection of selecting client
- ///
- SELECT_STATE_REASON_DISCONNECTED = 5
- }
-
- public delegate void ControlSelectStateChangedHandler(ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason);
-
- ///
- /// Return type of ControlHandler and ControlWaitForExecutionHandler
- ///
- public enum ControlHandlerResult {
- ///
- /// check or operation failed
- ///
- FAILED = 0,
- ///
- /// check or operation was successful
- ///
- OK = 1,
- ///
- /// check or operation is in progress
- ///
- WAITING = 2
- }
-
- public delegate ControlHandlerResult ControlWaitForExecutionHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool synchroCheck);
-
- public delegate ControlHandlerResult ControlHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test);
-
- public enum CheckHandlerResult {
- ///
- /// check passed
- ///
- ACCEPTED = -1,
- ///
- /// check failed due to hardware fault
- ///
- HARDWARE_FAULT = 1,
- ///
- /// control is already selected or operated
- ///
- TEMPORARILY_UNAVAILABLE = 2,
- ///
- /// check failed due to access control reason - access denied for this client or state
- ///
- OBJECT_ACCESS_DENIED = 3,
- ///
- /// object not visible in this security context ???
- ///
- OBJECT_UNDEFINED = 4
- }
-
- public delegate CheckHandlerResult CheckHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck);
-
- ///
- /// This class acts as the entry point for the IEC 61850 client API. It represents a single
- /// (MMS) connection to a server.
- ///
- public class IedServer
- {
- [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
- static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setLocalIpAddress(IntPtr self, string localIpAddress);
-
- [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
- static extern void IedServer_start(IntPtr self, int tcpPort);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_stop(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_destroy(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.Bool)]
- static extern bool IedServer_isRunning(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int IedServer_getNumberOfOpenConnections(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_lockDataModel(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_unlockDataModel(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateAttributeValue(IntPtr self, IntPtr DataAttribute, IntPtr MmsValue);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateBooleanAttributeValue(IntPtr self, IntPtr dataAttribute, [MarshalAs(UnmanagedType.I1)] bool value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateInt32AttributeValue(IntPtr self, IntPtr dataAttribute, int value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateInt64AttributeValue(IntPtr self, IntPtr dataAttribute, Int64 value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateFloatAttributeValue(IntPtr self, IntPtr dataAttribute, float value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateVisibleStringAttributeValue(IntPtr self, IntPtr dataAttribute, string value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateUTCTimeAttributeValue(IntPtr self, IntPtr dataAttribute, ulong value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateTimestampAttributeValue(IntPtr self, IntPtr dataAttribute, IntPtr timestamp);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_updateQuality(IntPtr self, IntPtr dataAttribute, ushort value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setServerIdentity(IntPtr self, string vendor, string model, string revision);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute);
-
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- private delegate int InternalControlPerformCheckHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck);
-
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- private delegate int InternalControlWaitForExecutionHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool synchoCheck);
-
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- private delegate int InternalControlHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- private delegate void InternalSelectStateChangedHandler(IntPtr action, IntPtr parameter, [MarshalAs(UnmanagedType.I1)] bool isSelected, int reason);
+ private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject)
+ {
+ ControlHandlerInfo info;
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setWaitForExecutionHandler(IntPtr self, IntPtr node, InternalControlWaitForExecutionHandler handler, IntPtr parameter);
+ controlHandlers.TryGetValue (controlObject, out info);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setPerformCheckHandler(IntPtr self, IntPtr node, InternalControlPerformCheckHandler handler, IntPtr parameter);
+ if (info == null) {
+ info = new ControlHandlerInfo (controlObject);
+ controlHandlers.Add (controlObject, info);
+ }
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setControlHandler (IntPtr self, IntPtr node, InternalControlHandler handler, IntPtr parameter);
+ return info;
+ }
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setSelectStateChangedHandler(IntPtr self, IntPtr node, InternalSelectStateChangedHandler handler, IntPtr parameter);
+ public void SetControlHandler (DataObject controlObject, ControlHandler handler, object parameter)
+ {
+ ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setWriteAccessPolicy(IntPtr self, FunctionalConstraint fc, AccessPolicy policy);
+ info.controlHandler = handler;
+ info.controlHandlerParameter = parameter;
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- private delegate int InternalWriteAccessHandler (IntPtr dataAttribute, IntPtr value, IntPtr connection, IntPtr parameter);
+ if (internalControlHandlerRef == null)
+ internalControlHandlerRef = new InternalControlHandler (InternalControlHandlerImpl);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_handleWriteAccess(IntPtr self, IntPtr dataAttribute,
- InternalWriteAccessHandler handler, IntPtr parameter);
+ IedServer_setControlHandler(self, controlObject.self, internalControlHandlerRef, GCHandle.ToIntPtr(info.handle));
+ }
- public delegate void ConnectionIndicationHandler(IedServer iedServer, ClientConnection clientConnection, bool connected, object parameter);
+ public void SetCheckHandler (DataObject controlObject, CheckHandler handler, object parameter)
+ {
+ ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
- private ConnectionIndicationHandler connectionHandler = null;
- private object connectionHandlerParameter = null;
+ info.checkHandler = handler;
+ info.checkHandlerParameter = parameter;
- public void SetConnectionIndicationHandler(ConnectionIndicationHandler handler, object parameter)
- {
- connectionHandler = handler;
- connectionHandlerParameter = parameter;
- }
+ if (internalControlPerformCheckHandlerRef == null)
+ internalControlPerformCheckHandlerRef = new InternalControlPerformCheckHandler (InternalCheckHandlerImpl);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- private delegate void InternalConnectionHandler (IntPtr iedServer, IntPtr clientConnection, [MarshalAs(UnmanagedType.I1)] bool connected, IntPtr parameter);
+ IedServer_setPerformCheckHandler(self, controlObject.self, internalControlPerformCheckHandlerRef, GCHandle.ToIntPtr(info.handle));
+ }
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServer_setConnectionIndicationHandler(IntPtr self, InternalConnectionHandler handler, IntPtr parameter);
+ public void SetWaitForExecutionHandler (DataObject controlObject, ControlWaitForExecutionHandler handler, object parameter)
+ {
+ ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
- private IntPtr self = IntPtr.Zero;
+ info.waitForExecHandler = handler;
+ info.waitForExecHandlerParameter = parameter;
- private InternalControlHandler internalControlHandlerRef = null;
- private InternalControlPerformCheckHandler internalControlPerformCheckHandlerRef = null;
- private InternalControlWaitForExecutionHandler internalControlWaitForExecutionHandlerRef = null;
- private InternalSelectStateChangedHandler internalSelectedStateChangedHandlerRef = null;
+ if (internalControlWaitForExecutionHandlerRef == null)
+ internalControlWaitForExecutionHandlerRef = new InternalControlWaitForExecutionHandler (InternalControlWaitForExecutionHandlerImpl);
- internal class ControlHandlerInfo {
- public DataObject controlObject = null;
- public GCHandle handle;
+ IedServer_setWaitForExecutionHandler(self, controlObject.self, internalControlWaitForExecutionHandlerRef, GCHandle.ToIntPtr(info.handle));
+ }
- public ControlHandler controlHandler = null;
- public object controlHandlerParameter = null;
+ ///
+ /// Set a callback handler for a controllable data object to track select state changes
+ ///
+ /// Control object.
+ /// Handler.
+ /// A user provided parameter that is passed to the callback handler.
+ public void SetSelectStateChangedHandler(DataObject controlObject, ControlSelectStateChangedHandler handler, object parameter)
+ {
+ ControlHandlerInfo info = GetControlHandlerInfo(controlObject);
- public CheckHandler checkHandler = null;
- public object checkHandlerParameter = null;
+ info.selectStateChangedHandler = handler;
+ info.selectStateChangedHandlerParameter = parameter;
- public ControlWaitForExecutionHandler waitForExecHandler = null;
- public object waitForExecHandlerParameter = null;
+ if (internalSelectedStateChangedHandlerRef == null)
+ internalSelectedStateChangedHandlerRef = new InternalSelectStateChangedHandler(InternalSelectStateChangedHandlerImpl);
- public ControlSelectStateChangedHandler selectStateChangedHandler = null;
- public object selectStateChangedHandlerParameter = null;
+ IedServer_setSelectStateChangedHandler(self, controlObject.self, internalSelectedStateChangedHandlerRef, GCHandle.ToIntPtr(info.handle));
+ }
- public ControlHandlerInfo(DataObject controlObject)
- {
- this.controlObject = controlObject;
- this.handle = GCHandle.Alloc(this);
- }
+ ///
+ /// Install a WriteAccessHandler for a data attribute.
+ ///
+ /// 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.
+ ///
+ /// 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!
+ ///
+ /// the data attribute to monitor
+ /// the callback function that is invoked if a client tries to write to the monitored data attribute.
+ /// a user provided parameter that is passed to the WriteAccessHandler when called.
+ public void HandleWriteAccess (DataAttribute dataAttr, WriteAccessHandler handler, object parameter)
+ {
+ InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl);
+
+ writeAccessHandlers.Add (dataAttr.self, new WriteAccessHandlerInfo(handler, parameter, dataAttr, internalHandler));
- ~ControlHandlerInfo()
- {
- this.handle.Free();
- }
- }
+ IedServer_handleWriteAccess (self, dataAttr.self, internalHandler, IntPtr.Zero);
+ }
- private Dictionary controlHandlers = new Dictionary ();
+ private void AddHandlerInfoForDataAttributeRecursive(DataAttribute da, WriteAccessHandler handler, object parameter, InternalWriteAccessHandler internalHandler)
+ {
+ writeAccessHandlers.Add(da.self, new WriteAccessHandlerInfo(handler, parameter, da, internalHandler));
+
+ foreach (ModelNode child in da.GetChildren())
+ {
+ if (child is DataAttribute)
+ {
+ AddHandlerInfoForDataAttributeRecursive(child as DataAttribute, handler, parameter, internalHandler);
+ }
+ }
+ }
- int InternalControlHandlerImpl (IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test)
- {
- GCHandle handle = GCHandle.FromIntPtr (parameter);
+ ///
+ /// 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.
+ ///
+ /// NOTE: 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 .
+ ///
+ /// the data attribute to monitor
+ /// the callback function that is invoked if a client tries to write to the monitored data attribute.
+ /// a user provided parameter that is passed to the WriteAccessHandler when called.
+ public void HandleWriteAccessForComplexAttribute(DataAttribute dataAttr, WriteAccessHandler handler, object parameter)
+ {
+ InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl);
- ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
+ AddHandlerInfoForDataAttributeRecursive(dataAttr, handler, parameter, internalHandler);
- ControlAction controlAction = new ControlAction (action, info, this);
+ IedServer_handleWriteAccessForComplexAttribute(self, dataAttr.self, internalHandler, IntPtr.Zero);
+ }
- if (info != null && info.controlHandler != null)
- return (int)info.controlHandler (controlAction, info.controlHandlerParameter, new MmsValue (ctlVal), test);
- else
- return (int)ControlHandlerResult.FAILED;
- }
+ ///
+ /// Set the defualt write access policy for a specific FC. The default policy is applied when no handler is installed for a data attribute
+ ///
+ /// The functional constraint (FC)
+ /// The new default access policy
+ public void SetWriteAccessPolicy(FunctionalConstraint fc, AccessPolicy policy)
+ {
+ IedServer_setWriteAccessPolicy (self, fc, policy);
+ }
- int InternalCheckHandlerImpl(IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test, bool interlockCheck)
- {
- GCHandle handle = GCHandle.FromIntPtr (parameter);
+ ///
+ /// Locks the data model for data update.
+ ///
+ /// This function should be called before the data model is updated.
+ /// After updating the data model the function should be called.
+ ///
+ ///
+ /// his method should never be called inside of a library callback function. In the context of
+ /// a library callback the data model is always already locked! Calling this function inside of a
+ /// library callback may lead to a deadlock condition.
+ ///
+ public void LockDataModel()
+ {
+ IedServer_lockDataModel(self);
+ }
- ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
+ ///
+ /// Unlocks the data model and process pending client requests.
+ ///
+ ///
+ ///
+ /// This method should never be called inside of a library callback function. In the context of
+ /// a library callback the data model is always already locked!
+ ///
+ public void UnlockDataModel()
+ {
+ IedServer_unlockDataModel(self);
+ }
- if (info != null && info.checkHandler != null)
- {
- ControlAction controlAction = new ControlAction (action, info, this);
+ public void UpdateAttributeValue(DataAttribute dataAttr, MmsValue value)
+ {
+ IedServer_updateAttributeValue (self, dataAttr.self, value.valueReference);
+ }
- return (int)info.checkHandler (controlAction, info.checkHandlerParameter, new MmsValue (ctlVal), test, interlockCheck);
- } else
- return (int)CheckHandlerResult.OBJECT_UNDEFINED;
- }
+ public void UpdateBooleanAttributeValue(DataAttribute dataAttr, bool value)
+ {
+ IedServer_updateBooleanAttributeValue(self, dataAttr.self, value);
+ }
- int InternalControlWaitForExecutionHandlerImpl (IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test, bool synchoCheck)
- {
- GCHandle handle = GCHandle.FromIntPtr (parameter);
+ public void UpdateFloatAttributeValue(DataAttribute dataAttr, float value)
+ {
+ IedServer_updateFloatAttributeValue(self, dataAttr.self, value);
+ }
- ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
+ public void UpdateInt32AttributeValue(DataAttribute dataAttr, int value)
+ {
+ IedServer_updateInt32AttributeValue(self, dataAttr.self, value);
+ }
- if (info != null && info.waitForExecHandler != null)
- {
- ControlAction controlAction = new ControlAction (action, info, this);
+ public void UpdateInt64AttributeValue(DataAttribute dataAttr, Int64 value)
+ {
+ IedServer_updateInt64AttributeValue (self, dataAttr.self, value);
+ }
- return (int)info.waitForExecHandler (controlAction, info.waitForExecHandlerParameter, new MmsValue (ctlVal), test, synchoCheck);
- }
- else
- return (int)ControlHandlerResult.FAILED;
- }
+ public void UpdateVisibleStringAttributeValue(DataAttribute dataAttr, string value)
+ {
+ IedServer_updateVisibleStringAttributeValue(self, dataAttr.self, value);
+ }
- void InternalSelectStateChangedHandlerImpl(IntPtr action, IntPtr parameter, bool isSelected, int reason)
- {
- GCHandle handle = GCHandle.FromIntPtr(parameter);
+ public void UpdateUTCTimeAttributeValue(DataAttribute dataAttr, DateTime timestamp)
+ {
+ DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ DateTime timestampUTC = timestamp.ToUniversalTime();
- ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
+ TimeSpan timeDiff = timestampUTC - epoch;
+ ulong timeVal = Convert.ToUInt64(timeDiff.TotalMilliseconds);
- if (info != null && info.selectStateChangedHandler != null)
- {
- ControlAction controlAction = new ControlAction(action, info, this);
+ IedServer_updateUTCTimeAttributeValue(self, dataAttr.self, timeVal);
+ }
- info.selectStateChangedHandler(controlAction, info.selectStateChangedHandlerParameter, isSelected, (SelectStateChangedReason)reason);
- }
- }
+ public void UpdateTimestampAttributeValue(DataAttribute dataAttr, Timestamp timestamp)
+ {
+ IedServer_updateTimestampAttributeValue (self, dataAttr.self, timestamp.timestampRef);
+ }
- private struct WriteAccessHandlerInfo {
- public WriteAccessHandler handler;
- public object parameter;
- public DataAttribute dataAttribute;
-
- public WriteAccessHandlerInfo (WriteAccessHandler h, object p, DataAttribute da)
- {
- handler = h;
- parameter = p;
- dataAttribute = da;
- }
- }
+ public void UpdateQuality(DataAttribute dataAttr, ushort value)
+ {
+ IedServer_updateQuality(self, dataAttr.self, value);
+ }
- int WriteAccessHandlerImpl (IntPtr dataAttribute, IntPtr value, IntPtr connection, IntPtr parameter)
- {
- WriteAccessHandlerInfo info;
-
- writeAccessHandlers.TryGetValue (dataAttribute, out info);
+ public MmsValue GetAttributeValue(DataAttribute dataAttr)
+ {
+ IntPtr mmsValuePtr = IedServer_getAttributeValue (self, dataAttr.self);
- ClientConnection con = null;
-
- clientConnections.TryGetValue (connection, out con);
-
- return (int) info.handler (info.dataAttribute, new MmsValue (value), con, info.parameter);
- }
-
- private Dictionary writeAccessHandlers = new Dictionary ();
-
- private void ConnectionIndicationHandlerImpl (IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter)
- {
- if (connected == false) {
- ClientConnection con = null;
-
- clientConnections.TryGetValue (clientConnection, out con);
-
- if (con != null) {
-
- if (connectionHandler != null)
- connectionHandler (this, con, false, connectionHandlerParameter);
-
- clientConnections.Remove (clientConnection);
- }
- } else {
- ClientConnection con = new ClientConnection (clientConnection);
-
- clientConnections.Add (clientConnection, con);
-
- if (connectionHandler != null)
- connectionHandler (this, con, true, connectionHandlerParameter);
- }
- }
-
- internal Dictionary clientConnections = new Dictionary ();
-
- public IedServer(IedModel iedModel, IedServerConfig config = null)
- {
- IntPtr nativeConfig = IntPtr.Zero;
-
- if (config != null)
- nativeConfig = config.self;
-
- self = IedServer_createWithConfig (iedModel.self, IntPtr.Zero, nativeConfig);
- }
-
- public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
- {
- IntPtr nativeConfig = IntPtr.Zero;
- IntPtr nativeTLSConfig = IntPtr.Zero;
-
- if (config != null)
- nativeConfig = config.self;
-
- if (tlsConfig != null)
- nativeTLSConfig = tlsConfig.GetNativeInstance ();
-
- self = IedServer_createWithConfig (iedModel.self, nativeTLSConfig, nativeConfig);
- }
-
- private InternalConnectionHandler internalConnectionHandler = null;
-
- ///
- /// Sets the local ip address for listening
- ///
- /// Local IP address.
- public void SetLocalIpAddress(string localIpAddress)
- {
- IedServer_setLocalIpAddress (self, localIpAddress);
- }
-
- ///
- /// Start MMS server
- ///
- /// Local IP address.
- /// TCP port to use
- public void Start(string localIpAddress, int tcpPort)
- {
- SetLocalIpAddress (localIpAddress);
- Start (tcpPort);
- }
-
- /// Start MMS server
- /// TCP port to use
- public void Start(int tcpPort)
- {
- if (internalConnectionHandler == null)
- internalConnectionHandler = new InternalConnectionHandler (ConnectionIndicationHandlerImpl);
-
- IedServer_setConnectionIndicationHandler (self, internalConnectionHandler, IntPtr.Zero);
-
- IedServer_start(self, tcpPort);
- }
-
- /// Start MMS server
- public void Start ()
- {
- Start(-1);
- }
-
- ///
- /// Stop the MMS server.
- ///
- /// This function will stop the server. This will close the TCP server socket and all client sockets.
- public void Stop()
- {
- IedServer_stop(self);
- internalConnectionHandler = null;
- }
-
- ///
- /// Release all server resources.
- ///
- /// This function releases all MMS server resources.
- public void Destroy()
- {
- IedServer_destroy(self);
- self = IntPtr.Zero;
- internalConnectionHandler = null;
- }
+ if (mmsValuePtr != IntPtr.Zero)
+ return new MmsValue (mmsValuePtr);
+ else
+ return null;
+ }
///
- /// Set the identify for the MMS identify service
+ /// Enable all GOOSE control blocks.
///
- /// the IED vendor name
- /// the IED model name
- /// the IED revision/version number
- public void SetServerIdentity(string vendor, string model, string revision)
+ /// This will set the GoEna attribute of all configured GOOSE control blocks
+ /// to true. If this method is not called at the startup or reset of the server
+ /// then configured GOOSE control blocks keep inactive until a MMS client enables
+ /// them by writing to the GOOSE control block.
+ public void EnableGoosePublishing()
{
- IedServer_setServerIdentity(self, vendor, model, revision);
+ IedServer_enableGoosePublishing(self);
}
///
- /// Check if server is running (accepting client connections)
+ /// Disable all GOOSE control blocks.
///
- /// true, if running, false otherwise.
- public bool IsRunning()
+ /// This will set the GoEna attribute of all configured GOOSE control blocks
+ /// to false. This will stop GOOSE transmission.
+ public void DisableGoosePublishing()
{
- return IedServer_isRunning(self);
+ IedServer_disableGoosePublishing(self);
}
- ///
- /// Get number of open MMS connections
- ///
- /// the number of open and accepted MMS connections
- public int GetNumberOfOpenConnections()
- {
- return IedServer_getNumberOfOpenConnections(self);
- }
-
- private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject)
- {
- ControlHandlerInfo info;
-
- controlHandlers.TryGetValue (controlObject, out info);
-
- if (info == null) {
- info = new ControlHandlerInfo (controlObject);
- controlHandlers.Add (controlObject, info);
- }
-
- return info;
- }
-
- public void SetControlHandler (DataObject controlObject, ControlHandler handler, object parameter)
- {
- ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
-
- info.controlHandler = handler;
- info.controlHandlerParameter = parameter;
-
- if (internalControlHandlerRef == null)
- internalControlHandlerRef = new InternalControlHandler (InternalControlHandlerImpl);
+ ///
+ /// Set the Ethernet interface to be used by GOOSE publishing
+ ///
+ ///
+ /// This function can be used to set the GOOSE interface ID. If not used or set to null the
+ /// default interface ID from stack_config.h is used.Note the interface ID is operating system
+ /// specific!
+ ///
+ /// the ID of the ethernet interface to be used for GOOSE publishing
+ public void SetGooseInterfaceId(string interfaceId)
+ {
+ IedServer_setGooseInterfaceId(self, interfaceId);
+ }
- IedServer_setControlHandler(self, controlObject.self, internalControlHandlerRef, GCHandle.ToIntPtr(info.handle));
- }
+ ///
+ /// Set the Ethernet interface to be used by GOOSE publishing
+ ///
+ ///
+ /// This function can be used to set the GOOSE interface ID for all GCBs (parameter ln = null) or for
+ /// a specific GCB specified by the logical node instance and the GCB name.
+ ///
+ /// ln the logical node that contains the GCB or null to set the ethernet interface ID for all GCBs
+ /// the name (not object reference!) of the GCB
+ /// the ID of the ethernet interface to be used for GOOSE publishing
+ public void SetGooseInterfaceId(LogicalNode ln, string gcbName, string interfaceId)
+ {
+ if (ln == null)
+ IedServer_setGooseInterfaceIdEx(self, IntPtr.Zero, gcbName, interfaceId);
+ else
+ IedServer_setGooseInterfaceIdEx(self, ln.self, gcbName, interfaceId);
+ }
- public void SetCheckHandler (DataObject controlObject, CheckHandler handler, object parameter)
- {
- ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
+ ///
+ /// Enable/disable the use of VLAN tags in GOOSE messages
+ ///
+ ///
+ /// This function can be used to enable/disable VLAN tagging for all GCBs (parameter ln = null) or for
+ /// a specific GCB specified by the logical node instance and the GCB name.
+ ///
+ /// the logical node that contains the GCB or null to enable/disable VLAN tagging for all GCBs
+ /// the name (not object reference!) of the GCB
+ /// true to enable VLAN tagging, false otherwise
+ public void UseGooseVlanTag(LogicalNode ln, string gcbName, bool useVlanTag)
+ {
+ if (ln == null)
+ IedServer_useGooseVlanTag(self, IntPtr.Zero, gcbName, useVlanTag);
+ else
+ IedServer_useGooseVlanTag(self, ln.self, gcbName, useVlanTag);
+ }
- info.checkHandler = handler;
- info.checkHandlerParameter = parameter;
+ private GoCBEventHandler goCbEventHandler = null;
+ private object goCbEventHandlerParameter = null;
- if (internalControlPerformCheckHandlerRef == null)
- internalControlPerformCheckHandlerRef = new InternalControlPerformCheckHandler (InternalCheckHandlerImpl);
+ private InternalGoCBEventHandler internalGoCBEventHandler = null;
- IedServer_setPerformCheckHandler(self, controlObject.self, internalControlPerformCheckHandlerRef, GCHandle.ToIntPtr(info.handle));
- }
+ private void InternalGoCBEventHandlerImplementation(IntPtr goCB, int cbEvent, IntPtr parameter)
+ {
+ if (goCbEventHandler != null)
+ {
+ MmsGooseControlBlock mmsGoCb = new MmsGooseControlBlock(goCB, iedModel);
- public void SetWaitForExecutionHandler (DataObject controlObject, ControlWaitForExecutionHandler handler, object parameter)
- {
- ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
+ goCbEventHandler.Invoke(mmsGoCb, cbEvent, goCbEventHandlerParameter);
+ }
+ }
- info.waitForExecHandler = handler;
- info.waitForExecHandlerParameter = parameter;
+ ///
+ /// Set a callback handler for GoCB events (enabled/disabled)
+ ///
+ /// the callback handler
+ /// user provided parameter that is passed to the callback handler
+ public void SetGoCBHandler(GoCBEventHandler handler, object parameter)
+ {
+ goCbEventHandler = handler;
+ goCbEventHandlerParameter = parameter;
+
+ if (internalGoCBEventHandler == null)
+ {
+ internalGoCBEventHandler = new InternalGoCBEventHandler(InternalGoCBEventHandlerImplementation);
+
+ IedServer_setGoCBHandler(self, internalGoCBEventHandler, IntPtr.Zero);
+ }
+ }
- if (internalControlWaitForExecutionHandlerRef == null)
- internalControlWaitForExecutionHandlerRef = new InternalControlWaitForExecutionHandler (InternalControlWaitForExecutionHandlerImpl);
+ }
- IedServer_setWaitForExecutionHandler(self, controlObject.self, internalControlWaitForExecutionHandlerRef, GCHandle.ToIntPtr(info.handle));
- }
-
- ///
- /// Set a callback handler for a controllable data object to track select state changes
- ///
- /// Control object.
- /// Handler.
- /// A user provided parameter that is passed to the callback handler.
- public void SetSelectStateChangedHandler(DataObject controlObject, ControlSelectStateChangedHandler handler, object parameter)
- {
- ControlHandlerInfo info = GetControlHandlerInfo(controlObject);
-
- info.selectStateChangedHandler = handler;
- info.selectStateChangedHandlerParameter = parameter;
-
- if (internalSelectedStateChangedHandlerRef == null)
- internalSelectedStateChangedHandlerRef = new InternalSelectStateChangedHandler(InternalSelectStateChangedHandlerImpl);
-
- IedServer_setSelectStateChangedHandler(self, controlObject.self, internalSelectedStateChangedHandlerRef, GCHandle.ToIntPtr(info.handle));
- }
-
- public void HandleWriteAccess (DataAttribute dataAttr, WriteAccessHandler handler, object parameter)
- {
- writeAccessHandlers.Add (dataAttr.self, new WriteAccessHandlerInfo(handler, parameter, dataAttr));
-
- IedServer_handleWriteAccess (self, dataAttr.self, WriteAccessHandlerImpl, IntPtr.Zero);
- }
-
- public void SetWriteAccessPolicy(FunctionalConstraint fc, AccessPolicy policy)
- {
- IedServer_setWriteAccessPolicy (self, fc, policy);
- }
-
-
- public void LockDataModel()
- {
- IedServer_lockDataModel(self);
- }
-
- public void UnlockDataModel()
- {
- IedServer_unlockDataModel(self);
- }
-
- public void UpdateAttributeValue(DataAttribute dataAttr, MmsValue value)
- {
- IedServer_updateAttributeValue (self, dataAttr.self, value.valueReference);
- }
-
- public void UpdateBooleanAttributeValue(DataAttribute dataAttr, bool value)
- {
- IedServer_updateBooleanAttributeValue(self, dataAttr.self, value);
- }
-
- public void UpdateFloatAttributeValue(DataAttribute dataAttr, float value)
- {
- IedServer_updateFloatAttributeValue(self, dataAttr.self, value);
- }
-
- public void UpdateInt32AttributeValue(DataAttribute dataAttr, int value)
- {
- IedServer_updateInt32AttributeValue(self, dataAttr.self, value);
- }
-
- public void UpdateInt64AttributeValue(DataAttribute dataAttr, Int64 value)
- {
- IedServer_updateInt64AttributeValue (self, dataAttr.self, value);
- }
-
- public void UpdateVisibleStringAttributeValue(DataAttribute dataAttr, string value)
- {
- IedServer_updateVisibleStringAttributeValue(self, dataAttr.self, value);
- }
-
- public void UpdateUTCTimeAttributeValue(DataAttribute dataAttr, DateTime timestamp)
- {
- DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
- DateTime timestampUTC = timestamp.ToUniversalTime();
-
- TimeSpan timeDiff = timestampUTC - epoch;
- ulong timeVal = Convert.ToUInt64(timeDiff.TotalMilliseconds);
-
- IedServer_updateUTCTimeAttributeValue(self, dataAttr.self, timeVal);
- }
-
- public void UpdateTimestampAttributeValue(DataAttribute dataAttr, Timestamp timestamp)
- {
- IedServer_updateTimestampAttributeValue (self, dataAttr.self, timestamp.timestampRef);
- }
-
- public void UpdateQuality(DataAttribute dataAttr, ushort value)
- {
- IedServer_updateQuality(self, dataAttr.self, value);
- }
-
- public MmsValue GetAttributeValue(DataAttribute dataAttr)
- {
- IntPtr mmsValuePtr = IedServer_getAttributeValue (self, dataAttr.self);
-
- if (mmsValuePtr != IntPtr.Zero)
- return new MmsValue (mmsValuePtr);
- else
- return null;
- }
- }
-
- }
+ }
}
diff --git a/dotnet/IEC61850forCSharp/IedServerConfig.cs b/dotnet/IEC61850forCSharp/IedServerConfig.cs
index d2b4f71b..9738f4ad 100644
--- a/dotnet/IEC61850forCSharp/IedServerConfig.cs
+++ b/dotnet/IEC61850forCSharp/IedServerConfig.cs
@@ -27,275 +27,354 @@ using IEC61850.Common;
namespace IEC61850.Server
{
- ///
- /// IedServer configuration object
- ///
- public class IedServerConfig : IDisposable
- {
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedServerConfig_create();
+ ///
+ /// IedServer configuration object
+ ///
+ public class IedServerConfig : IDisposable
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedServerConfig_create();
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedServerConfig_destroy(IntPtr self);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedServerConfig_destroy(IntPtr self);
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setReportBufferSize(IntPtr self, int reportBufferSize);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int IedServerConfig_getReportBufferSize(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setReportBufferSizeForURCBs(IntPtr self, int reportBufferSize);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int IedServerConfig_getReportBufferSizeForURCBs(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setFileServiceBasePath(IntPtr self, string basepath);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr IedServerConfig_getFileServiceBasePath(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_enableFileService(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
-
- [return: MarshalAs(UnmanagedType.I1)]
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern bool IedServerConfig_isFileServiceEnabled(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setEdition(IntPtr self, byte edition);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern byte IedServerConfig_getEdition(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setMaxMmsConnections(IntPtr self, int maxConnections);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int IedServerConfig_getMaxMmsConnections(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool IedServerConfig_isDynamicDataSetServiceEnabled(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_enableDynamicDataSetService(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setMaxAssociationSpecificDataSets(IntPtr self, int maxDataSets);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int IedServerConfig_getMaxAssociationSpecificDataSets(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setMaxDomainSpecificDataSets(IntPtr self, int maxDataSets);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int IedServerConfig_getMaxDomainSpecificDataSets(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_setMaxDataSetEntries(IntPtr self, int maxDataSetEntries);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern int IedServerConfig_getMaxDatasSetEntries(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void IedServerConfig_enableLogService(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool IedServerConfig_isLogServiceEnabled(IntPtr self);
-
- internal IntPtr self;
-
- public IedServerConfig ()
- {
- self = IedServerConfig_create ();
- }
-
- ///
- /// Gets or sets the size of the report buffer for buffered report control blocks
- ///
- /// The size of the report buffer.
- public int ReportBufferSize
- {
- get {
- return IedServerConfig_getReportBufferSize (self);
- }
- set {
- IedServerConfig_setReportBufferSize (self, value);
- }
- }
-
- ///
- /// Gets or sets the size of the report buffer for unbuffered report control blocks
- ///
- /// The size of the report buffer.
- public int ReportBufferSizeForURCBs
- {
- get
- {
- return IedServerConfig_getReportBufferSizeForURCBs(self);
- }
- set
- {
- IedServerConfig_setReportBufferSizeForURCBs(self, value);
- }
- }
-
- ///
- /// Gets or sets the file service base path.
- ///
- /// The file service base path.
- public string FileServiceBasePath
- {
- get {
- return Marshal.PtrToStringAnsi (IedServerConfig_getFileServiceBasePath (self));
- }
- set {
- IedServerConfig_setFileServiceBasePath (self, value);
- }
- }
-
- ///
- /// Enable/Disable file service for MMS
- ///
- /// true if file service is enabled; otherwise, false.
- public bool FileServiceEnabled
- {
- get
- {
- return IedServerConfig_isFileServiceEnabled(self);
- }
- set
- {
- IedServerConfig_enableFileService(self, value);
- }
- }
-
- ///
- /// Gets or sets the edition of the IEC 61850 standard to use
- ///
- /// The IEC 61850 edition to use.
- public Iec61850Edition Edition
- {
- get {
- return (Iec61850Edition)IedServerConfig_getEdition (self);
- }
- set {
- IedServerConfig_setEdition (self, (byte) value);
- }
- }
-
- ///
- /// Gets or sets maximum number of MMS clients
- ///
- /// The max number of MMS client connections.
- public int MaxMmsConnections
- {
- get {
- return IedServerConfig_getMaxMmsConnections (self);
- }
- set {
- IedServerConfig_setMaxMmsConnections (self, value);
- }
- }
-
- ///
- /// Enable/Disable dynamic data set service for MMS
- ///
- /// true if dynamic data set service enabled; otherwise, false.
- public bool DynamicDataSetServiceEnabled
- {
- get {
- return IedServerConfig_isDynamicDataSetServiceEnabled (self);
- }
- set {
- IedServerConfig_enableDynamicDataSetService (self, value);
- }
- }
-
- ///
- /// Gets or sets the maximum number of data set entries for dynamic data sets
- ///
- /// The max. number data set entries.
- public int MaxDataSetEntries
- {
- get {
- return IedServerConfig_getMaxDatasSetEntries (self);
- }
- set {
- IedServerConfig_setMaxDataSetEntries (self, value);
- }
- }
-
- ///
- /// Gets or sets the maximum number of association specific (non-permanent) data sets.
- ///
- /// The max. number of association specific data sets.
- public int MaxAssociationSpecificDataSets
- {
- get {
- return IedServerConfig_getMaxAssociationSpecificDataSets (self);
- }
- set {
- IedServerConfig_setMaxAssociationSpecificDataSets (self, value);
- }
- }
-
- ///
- /// Gets or sets the maximum number of domain specific (permanent) data sets.
- ///
- /// The max. numebr of domain specific data sets.
- public int MaxDomainSpecificDataSets
- {
- get {
- return IedServerConfig_getMaxDomainSpecificDataSets (self);
- }
- set {
- IedServerConfig_setMaxDomainSpecificDataSets (self, value);
- }
- }
-
- ///
- /// Enable/Disable log service for MMS
- ///
- /// true if log service is enabled; otherwise, false.
- public bool LogServiceEnabled
- {
- get
- {
- return IedServerConfig_isLogServiceEnabled(self);
- }
- set
- {
- IedServerConfig_enableLogService(self, value);
- }
- }
-
- ///
- /// Releases all resource used by the object.
- ///
- /// Call when you are finished using the . The
- /// method leaves the in an unusable state. After
- /// calling , you must release all references to the
- /// so the garbage collector can reclaim the memory that the
- /// was occupying.
- public void Dispose()
- {
- lock (this) {
- if (self != IntPtr.Zero) {
- IedServerConfig_destroy (self);
- self = IntPtr.Zero;
- }
- }
- }
-
- ~IedServerConfig()
- {
- Dispose ();
- }
- }
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setReportBufferSize(IntPtr self, int reportBufferSize);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int IedServerConfig_getReportBufferSize(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setReportBufferSizeForURCBs(IntPtr self, int reportBufferSize);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int IedServerConfig_getReportBufferSizeForURCBs(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setFileServiceBasePath(IntPtr self, string basepath);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedServerConfig_getFileServiceBasePath(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_enableFileService(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
+
+ [return: MarshalAs(UnmanagedType.I1)]
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern bool IedServerConfig_isFileServiceEnabled(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setEdition(IntPtr self, byte edition);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern byte IedServerConfig_getEdition(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setMaxMmsConnections(IntPtr self, int maxConnections);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int IedServerConfig_getMaxMmsConnections(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool IedServerConfig_isDynamicDataSetServiceEnabled(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_enableDynamicDataSetService(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setMaxAssociationSpecificDataSets(IntPtr self, int maxDataSets);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int IedServerConfig_getMaxAssociationSpecificDataSets(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setMaxDomainSpecificDataSets(IntPtr self, int maxDataSets);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int IedServerConfig_getMaxDomainSpecificDataSets(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_setMaxDataSetEntries(IntPtr self, int maxDataSetEntries);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int IedServerConfig_getMaxDatasSetEntries(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServerConfig_enableLogService(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [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()
+ {
+ self = IedServerConfig_create();
+ }
+
+ ///
+ /// Gets or sets the size of the report buffer for buffered report control blocks
+ ///
+ /// The size of the report buffer.
+ public int ReportBufferSize
+ {
+ get
+ {
+ return IedServerConfig_getReportBufferSize(self);
+ }
+ set
+ {
+ IedServerConfig_setReportBufferSize(self, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the size of the report buffer for unbuffered report control blocks
+ ///
+ /// The size of the report buffer.
+ public int ReportBufferSizeForURCBs
+ {
+ get
+ {
+ return IedServerConfig_getReportBufferSizeForURCBs(self);
+ }
+ set
+ {
+ IedServerConfig_setReportBufferSizeForURCBs(self, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the file service base path.
+ ///
+ /// The file service base path.
+ public string FileServiceBasePath
+ {
+ get
+ {
+ return Marshal.PtrToStringAnsi(IedServerConfig_getFileServiceBasePath(self));
+ }
+ set
+ {
+ IedServerConfig_setFileServiceBasePath(self, value);
+ }
+ }
+
+ ///
+ /// Enable/Disable file service for MMS
+ ///
+ /// true if file service is enabled; otherwise, false.
+ public bool FileServiceEnabled
+ {
+ get
+ {
+ return IedServerConfig_isFileServiceEnabled(self);
+ }
+ set
+ {
+ IedServerConfig_enableFileService(self, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the edition of the IEC 61850 standard to use
+ ///
+ /// The IEC 61850 edition to use.
+ public Iec61850Edition Edition
+ {
+ get
+ {
+ return (Iec61850Edition)IedServerConfig_getEdition(self);
+ }
+ set
+ {
+ IedServerConfig_setEdition(self, (byte)value);
+ }
+ }
+
+ ///
+ /// Gets or sets maximum number of MMS clients
+ ///
+ /// The max number of MMS client connections.
+ public int MaxMmsConnections
+ {
+ get
+ {
+ return IedServerConfig_getMaxMmsConnections(self);
+ }
+ set
+ {
+ IedServerConfig_setMaxMmsConnections(self, value);
+ }
+ }
+
+ ///
+ /// Enable/Disable dynamic data set service for MMS
+ ///
+ /// true if dynamic data set service enabled; otherwise, false.
+ public bool DynamicDataSetServiceEnabled
+ {
+ get
+ {
+ return IedServerConfig_isDynamicDataSetServiceEnabled(self);
+ }
+ set
+ {
+ IedServerConfig_enableDynamicDataSetService(self, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum number of data set entries for dynamic data sets
+ ///
+ /// The max. number data set entries.
+ public int MaxDataSetEntries
+ {
+ get
+ {
+ return IedServerConfig_getMaxDatasSetEntries(self);
+ }
+ set
+ {
+ IedServerConfig_setMaxDataSetEntries(self, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum number of association specific (non-permanent) data sets.
+ ///
+ /// The max. number of association specific data sets.
+ public int MaxAssociationSpecificDataSets
+ {
+ get
+ {
+ return IedServerConfig_getMaxAssociationSpecificDataSets(self);
+ }
+ set
+ {
+ IedServerConfig_setMaxAssociationSpecificDataSets(self, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum number of domain specific (permanent) data sets.
+ ///
+ /// The max. numebr of domain specific data sets.
+ public int MaxDomainSpecificDataSets
+ {
+ get
+ {
+ return IedServerConfig_getMaxDomainSpecificDataSets(self);
+ }
+ set
+ {
+ IedServerConfig_setMaxDomainSpecificDataSets(self, value);
+ }
+ }
+
+ ///
+ /// Enable/Disable log service for MMS
+ ///
+ /// true if log service is enabled; otherwise, false.
+ public bool LogServiceEnabled
+ {
+ get
+ {
+ return IedServerConfig_isLogServiceEnabled(self);
+ }
+ set
+ {
+ IedServerConfig_enableLogService(self, value);
+ }
+ }
+
+ ///
+ /// Enable/Disable the presence of ResvTms attribute in BRCBs (buffered report control blocks)
+ ///
+ /// true if BRCB has ResvTms; otherwise, false. Defaults to true
+ public bool BRCBHasResvTms
+ {
+ get
+ {
+ return IedServerConfig_isResvTmsForBRCBEnabled(self);
+ }
+ set
+ {
+ IedServerConfig_enableResvTmsForBRCB(self, value);
+ }
+ }
+
+ ///
+ /// Enable/Disable the presence of Owner attribute in RCBs (report control blocks)
+ ///
+ /// true if RCB has Owner; otherwise, false. Defaults to false
+ public bool RCBHasOwner
+ {
+ get
+ {
+ return IedServerConfig_isOwnerForRCBEnabled(self);
+ }
+ set
+ {
+ IedServerConfig_enableOwnerForRCB(self, value);
+ }
+ }
+
+ ///
+ /// Enable/disable using the integrated GOOSE publisher for configured GoCBs
+ ///
+ /// true when integrated GOOSE publisher is used; otherwise, false. Defaults to true
+ public bool UseIntegratedGoosePublisher
+ {
+ set
+ {
+ IedServerConfig_useIntegratedGoosePublisher(self, value);
+ }
+ }
+
+ ///
+ /// Releases all resource used by the object.
+ ///
+ /// Call when you are finished using the . The
+ /// method leaves the in an unusable state. After
+ /// calling , you must release all references to the
+ /// so the garbage collector can reclaim the memory that the
+ /// was occupying.
+ public void Dispose()
+ {
+ lock (this)
+ {
+ if (self != IntPtr.Zero)
+ {
+ IedServerConfig_destroy(self);
+ self = IntPtr.Zero;
+ }
+ }
+ }
+
+ ~IedServerConfig()
+ {
+ Dispose();
+ }
+ }
}
diff --git a/dotnet/IEC61850forCSharp/MmsValue.cs b/dotnet/IEC61850forCSharp/MmsValue.cs
index 5d6a90c7..3b5e06ee 100644
--- a/dotnet/IEC61850forCSharp/MmsValue.cs
+++ b/dotnet/IEC61850forCSharp/MmsValue.cs
@@ -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,13 +421,34 @@ namespace IEC61850
return new MmsValue (newValue, true);
}
- ///
- /// Gets the type of the value
+ ///
+ /// Create a new MmsValue instance of type MMS_VISIBLE_STRING - empty string with given maximum size
///
- ///
- /// The type.
- ///
- public new MmsType GetType ()
+ /// The maximum size
+ ///
+ public static MmsValue NewVisibleString(int size, bool responsibleForDeletion = false)
+ {
+ IntPtr newValue = MmsValue_newVisibleStringWithSize(size);
+
+ return new MmsValue(newValue, responsibleForDeletion);
+ }
+
+ ///
+ /// Set the value of an MmsValue instance of type MMS_VISIBLE_STRING
+ ///
+ /// the new string value
+ public void SetVisibleString(string value)
+ {
+ MmsValue_setVisibleString(valueReference, value);
+ }
+
+ ///
+ /// Gets the type of the value
+ ///
+ ///
+ /// The type.
+ ///
+ public new MmsType GetType ()
{
return (MmsType)MmsValue_getType (valueReference);
}
diff --git a/dotnet/dotnet.sln b/dotnet/dotnet.sln
index c2ab42d8..ba4aec17 100644
--- a/dotnet/dotnet.sln
+++ b/dotnet/dotnet.sln
@@ -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
diff --git a/dotnet/goose_subscriber/Program.cs b/dotnet/goose_subscriber/Program.cs
index df84f032..bfcdaff6 100644
--- a/dotnet/goose_subscriber/Program.cs
+++ b/dotnet/goose_subscriber/Program.cs
@@ -4,6 +4,9 @@ using IEC61850.GOOSE.Subscriber;
using System.Threading;
using IEC61850.Common;
+///
+/// This example is intended to be
+///
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;
diff --git a/dotnet/server1/Program.cs b/dotnet/server1/Program.cs
index bef0da90..569ba74d 100644
--- a/dotnet/server1/Program.cs
+++ b/dotnet/server1/Program.cs
@@ -5,33 +5,38 @@ using System.Threading;
namespace server1
{
- class MainClass
- {
- public static void Main (string[] args)
- {
- bool running = true;
+ class MainClass
+ {
+ 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;
- };
+ /* run until Ctrl-C is pressed */
+ 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!");
- return;
- }
+ if (iedModel == null)
+ {
+ Console.WriteLine("No valid data model found!");
+ return;
+ }
- DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.SPCSO1");
+ iedModel.SetIedName("TestIED");
- IedServerConfig config = new IedServerConfig ();
- config.ReportBufferSize = 100000;
+ DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO1");
- IedServer iedServer = new IedServer (iedModel, config);
+ IedServerConfig config = new IedServerConfig();
+ config.ReportBufferSize = 100000;
- iedServer.SetCheckHandler(spcso1, delegate(ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck) {
+ IedServer iedServer = new IedServer(iedModel, config);
+
+ 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,58 +45,60 @@ 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)
Console.WriteLine("execute binary control command: on");
else
Console.WriteLine("execute binary control command: off");
-
+
return ControlHandlerResult.OK;
- }, null);
+ }, null);
- DataObject spcso2 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO2");
+ DataObject spcso2 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO2");
- iedServer.SetSelectStateChangedHandler (spcso2, delegate (ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason) {
- DataObject cObj = action.GetControlObject();
+ 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());
+ Console.WriteLine("Control object " + cObj.GetObjectReference() + (isSelected ? " selected" : " unselected") + " reason: " + reason.ToString());
- }, null);
+ }, null);
- iedServer.Start (102);
+ iedServer.Start(102);
- if (iedServer.IsRunning())
- {
- Console.WriteLine("Server started");
+ if (iedServer.IsRunning())
+ {
+ Console.WriteLine("Server started");
- GC.Collect();
+ GC.Collect();
- DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.AnIn1");
+ DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.AnIn1");
- DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild("mag.f");
- DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild("t");
+ DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild("mag.f");
+ DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild("t");
- float floatVal = 1.0f;
+ float floatVal = 1.0f;
- while (running)
- {
- floatVal += 1f;
- iedServer.UpdateTimestampAttributeValue(ggio1AnIn1T, new Timestamp(DateTime.Now));
- iedServer.UpdateFloatAttributeValue(ggio1AnIn1magF, floatVal);
- Thread.Sleep(100);
- }
+ 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.Stop();
+ Console.WriteLine("Server stopped");
+ }
+ else
+ {
+ Console.WriteLine("Failed to start server");
+ }
- iedServer.Destroy ();
- }
- }
+ iedServer.Destroy();
+ }
+ }
}
diff --git a/dotnet/server1/server1.csproj b/dotnet/server1/server1.csproj
index d1c45eeb..ab6d9c85 100644
--- a/dotnet/server1/server1.csproj
+++ b/dotnet/server1/server1.csproj
@@ -48,4 +48,4 @@
PreserveNewest
-
+
\ No newline at end of file
diff --git a/dotnet/server_goose_publisher/App.config b/dotnet/server_goose_publisher/App.config
new file mode 100644
index 00000000..731f6de6
--- /dev/null
+++ b/dotnet/server_goose_publisher/App.config
@@ -0,0 +1,6 @@
+īģŋ
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/server_goose_publisher/Properties/AssemblyInfo.cs b/dotnet/server_goose_publisher/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..181aecad
--- /dev/null
+++ b/dotnet/server_goose_publisher/Properties/AssemblyInfo.cs
@@ -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")]
diff --git a/dotnet/server_goose_publisher/ServerExampleWithGoosePublisher.cs b/dotnet/server_goose_publisher/ServerExampleWithGoosePublisher.cs
new file mode 100644
index 00000000..dba14bf1
--- /dev/null
+++ b/dotnet/server_goose_publisher/ServerExampleWithGoosePublisher.cs
@@ -0,0 +1,129 @@
+īģŋusing System;
+using IEC61850.Server;
+using IEC61850.Common;
+using System.Threading;
+
+namespace server_goose_publisher
+{
+ ///
+ /// This example shows how to use the server integrated GOOSE publisher from .NET
+ ///
+ ///
+ /// This example requires that the native library (libiec61850) is compiled with GOOSE support.
+ ///
+ 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();
+ }
+ }
+}
diff --git a/dotnet/server_goose_publisher/server_goose_publisher.csproj b/dotnet/server_goose_publisher/server_goose_publisher.csproj
new file mode 100644
index 00000000..5cf3a856
--- /dev/null
+++ b/dotnet/server_goose_publisher/server_goose_publisher.csproj
@@ -0,0 +1,63 @@
+īģŋ
+
+
+
+ Debug
+ AnyCPU
+ {C14BB883-86B8-401C-B3D6-B655F55F3298}
+ Exe
+ server_goose_publisher
+ server_goose_publisher
+ v4.6.1
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+ {c35d624e-5506-4560-8074-1728f1fa1a4d}
+ IEC61850.NET
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/server_goose_publisher/simpleIO_direct_control_goose.cfg b/dotnet/server_goose_publisher/simpleIO_direct_control_goose.cfg
new file mode 100644
index 00000000..1ebb7159
--- /dev/null
+++ b/dotnet/server_goose_publisher/simpleIO_direct_control_goose.cfg
@@ -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);
+}
+}
+}
+}
diff --git a/dotnet/server_goose_publisher/simpleIO_direct_control_goose.cid b/dotnet/server_goose_publisher/simpleIO_direct_control_goose.cid
new file mode 100644
index 00000000..e25303f7
--- /dev/null
+++ b/dotnet/server_goose_publisher/simpleIO_direct_control_goose.cid
@@ -0,0 +1,275 @@
+
+
+
+
+
+
+
+ 10.0.0.2
+ 255.255.255.0
+ 10.0.0.1
+ 0001
+ 00000001
+ 0001
+
+
+
+ 1
+ 4
+ 01-0c-cd-01-00-01
+ 1000
+
+ 1000
+ 3000
+
+
+
+ 1
+ 4
+ 01-0c-cd-01-00-01
+ 1000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ status-only
+
+
+
+
+
+
+
+ status-only
+
+
+
+
+ direct-with-normal-security
+
+
+
+
+ direct-with-normal-security
+
+
+
+
+ direct-with-normal-security
+
+
+
+
+ direct-with-normal-security
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ status-only
+ direct-with-normal-security
+ sbo-with-normal-security
+ direct-with-enhanced-security
+ sbo-with-enhanced-security
+
+
+ not-supported
+ bay-control
+ station-control
+ remote-control
+ automatic-bay
+ automatic-station
+ automatic-remote
+ maintenance
+ process
+
+
+
diff --git a/dotnet/tests/Test.cs b/dotnet/tests/Test.cs
index 8b945f7d..de3fad63 100644
--- a/dotnet/tests/Test.cs
+++ b/dotnet/tests/Test.cs
@@ -244,7 +244,9 @@ namespace tests
Assert.AreEqual (list.ToArray () [0], "simpleIOGenericIO");
- iedServer.Stop ();
+ Assert.IsTrue(iedServer.IsRunning());
+
+ iedServer.Stop ();
iedServer.Destroy ();
}
@@ -289,7 +291,35 @@ namespace tests
Assert.IsNotNull (modelNode);
}
- [Test ()]
+ [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()
{
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("../../model.cfg");
@@ -382,10 +412,62 @@ namespace tests
iedServer.Destroy ();
}
- [Test()]
+ [Test()]
+ 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");
@@ -393,7 +475,7 @@ namespace tests
IedServer iedServer = new IedServer (iedModel);
- iedServer.HandleWriteAccess (opDlTmms, delegate(IEC61850.Server.DataAttribute dataAttr, MmsValue value, ClientConnection con, object parameter) {
+ iedServer.HandleWriteAccess (opDlTmms, delegate(IEC61850.Server.DataAttribute dataAttr, MmsValue value, ClientConnection con, object parameter) {
return MmsDataAccessError.SUCCESS;
}, null);
@@ -404,7 +486,9 @@ namespace tests
connection.Connect ("localhost", 10002);
- connection.WriteValue ("simpleIOGenericIO/PDUP1.RsDlTmms.setVal", FunctionalConstraint.SP, new MmsValue ((int)1234));
+ 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);
diff --git a/dotnet/tests/model2.cfg b/dotnet/tests/model2.cfg
new file mode 100644
index 00000000..1aa07669
--- /dev/null
+++ b/dotnet/tests/model2.cfg
@@ -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);
+}
+}
+}
+}
diff --git a/dotnet/tests/simpleIO_control_tests.scd b/dotnet/tests/simpleIO_control_tests.scd
new file mode 100644
index 00000000..57a242d5
--- /dev/null
+++ b/dotnet/tests/simpleIO_control_tests.scd
@@ -0,0 +1,430 @@
+
+
+
+
+
+ Station bus
+ 10
+
+
+ 10.0.0.2
+ 255.255.255.0
+ 10.0.0.1
+ 0001
+ 00000001
+ 0001
+
+
+
+ 1
+ 4
+ 01-0c-cd-01-00-01
+ 1000
+
+ 1000
+ 3000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ status-only
+
+
+
+
+
+
+
+ status-only
+
+
+
+
+ direct-with-normal-security
+
+
+
+
+ sbo-with-normal-security
+
+
+
+
+ direct-with-enhanced-security
+
+
+
+
+ sbo-with-enhanced-security
+
+
+
+
+ direct-with-normal-security
+
+
+
+
+ sbo-with-normal-security
+
+
+
+
+ direct-with-enhanced-security
+
+
+
+
+ sbo-with-enhanced-security
+
+
+
+
+
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ status-only
+ direct-with-normal-security
+ sbo-with-normal-security
+ direct-with-enhanced-security
+ sbo-with-enhanced-security
+
+
+ operate-once
+ operate-many
+
+
+ not-supported
+ bay-control
+ station-control
+ remote-control
+ automatic-bay
+ automatic-station
+ automatic-remote
+ maintenance
+ process
+
+
+
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 03326ad9..d787fb9b 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -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)
diff --git a/examples/Makefile b/examples/Makefile
index a6d6d5d9..87cb1d8e 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -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
diff --git a/examples/goose_subscriber/goose_subscriber_example.c b/examples/goose_subscriber/goose_subscriber_example.c
index 0f044dff..bea2280e 100644
--- a/examples/goose_subscriber/goose_subscriber_example.c
+++ b/examples/goose_subscriber/goose_subscriber_example.c
@@ -9,6 +9,7 @@
#include "goose_receiver.h"
#include "goose_subscriber.h"
#include "hal_thread.h"
+#include "linked_list.h"
#include
#include
@@ -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");
@@ -48,14 +50,14 @@ main(int argc, char** argv)
{
GooseReceiver receiver = GooseReceiver_create();
- if (argc > 1) {
- printf("Set interface id: %s\n", argv[1]);
- GooseReceiver_setInterfaceId(receiver, argv[1]);
- }
- else {
- printf("Using interface eth0\n");
- GooseReceiver_setInterfaceId(receiver, "eth0");
- }
+ if (argc > 1) {
+ printf("Set interface id: %s\n", argv[1]);
+ GooseReceiver_setInterfaceId(receiver, argv[1]);
+ }
+ else {
+ printf("Using interface eth0\n");
+ GooseReceiver_setInterfaceId(receiver, "eth0");
+ }
GooseSubscriber subscriber = GooseSubscriber_create("simpleIOGenericIO/LLN0$GO$gcbAnalogValues", NULL);
diff --git a/examples/server_example_basic_io/Makefile.standalone b/examples/server_example_basic_io/Makefile.standalone
new file mode 100644
index 00000000..87e4c9af
--- /dev/null
+++ b/examples/server_example_basic_io/Makefile.standalone
@@ -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
+
+
diff --git a/examples/server_example_deadband/CMakeLists.txt b/examples/server_example_deadband/CMakeLists.txt
new file mode 100644
index 00000000..bab251a5
--- /dev/null
+++ b/examples/server_example_deadband/CMakeLists.txt
@@ -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
+)
diff --git a/examples/server_example_deadband/Makefile b/examples/server_example_deadband/Makefile
new file mode 100644
index 00000000..e1d5674c
--- /dev/null
+++ b/examples/server_example_deadband/Makefile
@@ -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)
+
+
+
diff --git a/examples/server_example_deadband/cid_example_deadband.cid b/examples/server_example_deadband/cid_example_deadband.cid
new file mode 100644
index 00000000..e023dc0d
--- /dev/null
+++ b/examples/server_example_deadband/cid_example_deadband.cid
@@ -0,0 +1,442 @@
+īģŋ
+
+
+
+
+
+
+ 1,1,1,999,1
+ 12
+ 00000001
+ 0001
+ 0001
+ 0.0.0.0
+ 255.255.255.0
+ 0.0.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ on
+
+
+
+
+
+
+
+ on
+
+
+
+
+ 10000
+
+
+ 10
+
+
+ 1000
+
+
+ 10
+
+
+
+
+ 1000
+
+
+ 10
+
+
+ 1000
+
+
+ 10
+
+
+
+
+ 10000
+
+
+ 1000
+
+
+
+
+ -5
+
+
+
+
+ 5
+
+
+
+
+
+
+ 1000
+
+
+ 1000
+
+
+
+
+ -100
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ e1
+ e2
+ e3
+ e4
+ e5
+
+
+ ExternalAreaClock
+ LocalAreaClock
+ GlobalAreaClock
+
+
+ not-supported
+ bay-control
+ station-control
+ remote-control
+ automatic-bay
+ automatic-station
+ automatic-remote
+ maintenance
+ process
+
+
+ status-only
+ direct-with-normal-security
+ sbo-with-normal-security
+ direct-with-enhanced-security
+ sbo-with-enhanced-security
+
+
+ operate-once
+ operate-many
+
+
+ none
+ m
+ kg
+ s
+ A
+ K
+ mol
+ cd
+ deg
+ rad
+ sr
+ Gy
+ q
+ °C
+ Sv
+ F
+ C
+ S
+ H
+ V
+ ohm
+ J
+ N
+ Hz
+ lx
+ Lm
+ Wb
+ T
+ W
+ Pa
+ m²
+ mÂŗ
+ m/s
+ m/s²
+ mÂŗ/s
+ m/mÂŗ
+ M
+ kg/mÂŗ
+ m²/s
+ W/m K
+ J/K
+ ppm
+ 1/s
+ rad/s
+ VA
+ Watts
+ VAr
+ theta
+ cos(theta)
+ Vs
+ V²
+ As
+ A²
+ A²t
+ VAh
+ Wh
+ VArh
+ V/Hz
+
+
+ Yocto
+ Zepto
+ Atto
+ Femto
+ Pico
+ Nano
+ Micro
+ Milli
+ Centi
+ Deci
+ zeroNoValue
+ Deca
+ Hecto
+ Kilo
+ Mega
+ Giga
+ Tera
+ Petra
+ Exa
+ Zetta
+ Yotta
+
+
+ normal
+ high
+ low
+ high-high
+ low-low
+
+
+ pulse
+ persistent
+
+
+ Ok
+ Warning
+ Alarm
+
+
+ on
+ blocked
+ test
+ test/blocked
+ off
+
+
+ on
+ blocked
+ test
+ test/blocked
+ off
+
+
+
\ No newline at end of file
diff --git a/examples/server_example_deadband/server_example_deadband.c b/examples/server_example_deadband/server_example_deadband.c
new file mode 100644
index 00000000..a7d18bde
--- /dev/null
+++ b/examples/server_example_deadband/server_example_deadband.c
@@ -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
+#include
+#include
+#include
+
+#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() */
diff --git a/examples/server_example_deadband/static_model.c b/examples/server_example_deadband/static_model.c
new file mode 100644
index 00000000..77faa997
--- /dev/null
+++ b/examples/server_example_deadband/static_model.c
@@ -0,0 +1,1288 @@
+/*
+ * static_model.c
+ *
+ * automatically generated from cid_example_deadband.cid
+ */
+#include "static_model.h"
+
+static void initializeValues();
+
+extern DataSet iedModelds_LD1_LLN0_AnalogEvents;
+
+
+extern DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda0;
+extern DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda1;
+extern DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda2;
+extern DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda3;
+
+DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda0 = {
+ "LD1",
+ false,
+ "AnInGGIO1$MX$AnIn1",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_LD1_LLN0_AnalogEvents_fcda1
+};
+
+DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda1 = {
+ "LD1",
+ false,
+ "AnInGGIO1$MX$AnIn2",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_LD1_LLN0_AnalogEvents_fcda2
+};
+
+DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda2 = {
+ "LD1",
+ false,
+ "AnInGGIO1$MX$AnIn3",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_LD1_LLN0_AnalogEvents_fcda3
+};
+
+DataSetEntry iedModelds_LD1_LLN0_AnalogEvents_fcda3 = {
+ "LD1",
+ false,
+ "AnInGGIO1$MX$AnIn4",
+ -1,
+ NULL,
+ NULL,
+ NULL
+};
+
+DataSet iedModelds_LD1_LLN0_AnalogEvents = {
+ "LD1",
+ "LLN0$AnalogEvents",
+ 4,
+ &iedModelds_LD1_LLN0_AnalogEvents_fcda0,
+ NULL
+};
+
+LogicalDevice iedModel_LD1 = {
+ LogicalDeviceModelType,
+ "LD1",
+ (ModelNode*) &iedModel,
+ NULL,
+ (ModelNode*) &iedModel_LD1_LLN0
+};
+
+LogicalNode iedModel_LD1_LLN0 = {
+ LogicalNodeModelType,
+ "LLN0",
+ (ModelNode*) &iedModel_LD1,
+ (ModelNode*) &iedModel_LD1_LPHD1,
+ (ModelNode*) &iedModel_LD1_LLN0_Mod,
+};
+
+DataObject iedModel_LD1_LLN0_Mod = {
+ DataObjectModelType,
+ "Mod",
+ (ModelNode*) &iedModel_LD1_LLN0,
+ (ModelNode*) &iedModel_LD1_LLN0_Beh,
+ (ModelNode*) &iedModel_LD1_LLN0_Mod_stVal,
+ 0
+};
+
+DataAttribute iedModel_LD1_LLN0_Mod_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_LD1_LLN0_Mod,
+ (ModelNode*) &iedModel_LD1_LLN0_Mod_q,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_Mod_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_LLN0_Mod,
+ (ModelNode*) &iedModel_LD1_LLN0_Mod_t,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_Mod_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_LLN0_Mod,
+ (ModelNode*) &iedModel_LD1_LLN0_Mod_ctlModel,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_Mod_ctlModel = {
+ DataAttributeModelType,
+ "ctlModel",
+ (ModelNode*) &iedModel_LD1_LLN0_Mod,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_LLN0_Beh = {
+ DataObjectModelType,
+ "Beh",
+ (ModelNode*) &iedModel_LD1_LLN0,
+ (ModelNode*) &iedModel_LD1_LLN0_Health,
+ (ModelNode*) &iedModel_LD1_LLN0_Beh_stVal,
+ 0
+};
+
+DataAttribute iedModel_LD1_LLN0_Beh_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_LD1_LLN0_Beh,
+ (ModelNode*) &iedModel_LD1_LLN0_Beh_q,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_Beh_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_LLN0_Beh,
+ (ModelNode*) &iedModel_LD1_LLN0_Beh_t,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_Beh_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_LLN0_Beh,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_LLN0_Health = {
+ DataObjectModelType,
+ "Health",
+ (ModelNode*) &iedModel_LD1_LLN0,
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt,
+ (ModelNode*) &iedModel_LD1_LLN0_Health_stVal,
+ 0
+};
+
+DataAttribute iedModel_LD1_LLN0_Health_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_LD1_LLN0_Health,
+ (ModelNode*) &iedModel_LD1_LLN0_Health_q,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_Health_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_LLN0_Health,
+ (ModelNode*) &iedModel_LD1_LLN0_Health_t,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_Health_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_LLN0_Health,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_LLN0_NamPlt = {
+ DataObjectModelType,
+ "NamPlt",
+ (ModelNode*) &iedModel_LD1_LLN0,
+ NULL,
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt_vendor,
+ 0
+};
+
+DataAttribute iedModel_LD1_LLN0_NamPlt_vendor = {
+ DataAttributeModelType,
+ "vendor",
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt,
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt_swRev,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_NamPlt_swRev = {
+ DataAttributeModelType,
+ "swRev",
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt,
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt_d,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_NamPlt_d = {
+ DataAttributeModelType,
+ "d",
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt,
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt_configRev,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LLN0_NamPlt_configRev = {
+ DataAttributeModelType,
+ "configRev",
+ (ModelNode*) &iedModel_LD1_LLN0_NamPlt,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+LogicalNode iedModel_LD1_LPHD1 = {
+ LogicalNodeModelType,
+ "LPHD1",
+ (ModelNode*) &iedModel_LD1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+};
+
+DataObject iedModel_LD1_LPHD1_PhyNam = {
+ DataObjectModelType,
+ "PhyNam",
+ (ModelNode*) &iedModel_LD1_LPHD1,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_vendor,
+ 0
+};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_vendor = {
+ DataAttributeModelType,
+ "vendor",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_hwRev,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_hwRev = {
+ DataAttributeModelType,
+ "hwRev",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_swRev,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_swRev = {
+ DataAttributeModelType,
+ "swRev",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_serNum,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_serNum = {
+ DataAttributeModelType,
+ "serNum",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_model,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_model = {
+ DataAttributeModelType,
+ "model",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_location,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_location = {
+ DataAttributeModelType,
+ "location",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_name,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_name = {
+ DataAttributeModelType,
+ "name",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam_owner,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_64,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyNam_owner = {
+ DataAttributeModelType,
+ "owner",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyNam,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_LPHD1_PhyHealth = {
+ DataObjectModelType,
+ "PhyHealth",
+ (ModelNode*) &iedModel_LD1_LPHD1,
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth_stVal,
+ 0
+};
+
+DataAttribute iedModel_LD1_LPHD1_PhyHealth_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth_q,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyHealth_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth,
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth_t,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_PhyHealth_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_LPHD1_PhyHealth,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_LPHD1_Proxy = {
+ DataObjectModelType,
+ "Proxy",
+ (ModelNode*) &iedModel_LD1_LPHD1,
+ NULL,
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy_stVal,
+ 0
+};
+
+DataAttribute iedModel_LD1_LPHD1_Proxy_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy,
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy_q,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_Proxy_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy,
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy_t,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_Proxy_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy,
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy_d,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_LPHD1_Proxy_d = {
+ DataAttributeModelType,
+ "d",
+ (ModelNode*) &iedModel_LD1_LPHD1_Proxy,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+LogicalNode iedModel_LD1_AnInGGIO1 = {
+ LogicalNodeModelType,
+ "AnInGGIO1",
+ (ModelNode*) &iedModel_LD1,
+ NULL,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_Beh,
+};
+
+DataObject iedModel_LD1_AnInGGIO1_Beh = {
+ DataObjectModelType,
+ "Beh",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_Beh_stVal,
+ 0
+};
+
+DataAttribute iedModel_LD1_AnInGGIO1_Beh_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_Beh,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_Beh_q,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_Beh_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_Beh,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_Beh_t,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_Beh_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_Beh,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_AnInGGIO1_AnIn1 = {
+ DataObjectModelType,
+ "AnIn1",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_instMag,
+ 0
+};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_instMag = {
+ DataAttributeModelType,
+ "instMag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_mag,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_instMag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_instMag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_instMag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_q,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_mag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_mag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_t,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_db,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_db = {
+ DataAttributeModelType,
+ "db",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_zeroDb,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_zeroDb = {
+ DataAttributeModelType,
+ "zeroDb",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_dbRef,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_dbRef = {
+ DataAttributeModelType,
+ "dbRef",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1_zeroDbRef,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn1_zeroDbRef = {
+ DataAttributeModelType,
+ "zeroDbRef",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn1,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_AnInGGIO1_AnIn2 = {
+ DataObjectModelType,
+ "AnIn2",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_instMag,
+ 0
+};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_instMag = {
+ DataAttributeModelType,
+ "instMag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_mag,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_instMag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_instMag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_instMag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_q,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_mag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_mag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_t,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_db,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_db = {
+ DataAttributeModelType,
+ "db",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_zeroDb,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_zeroDb = {
+ DataAttributeModelType,
+ "zeroDb",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_dbRef,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_dbRef = {
+ DataAttributeModelType,
+ "dbRef",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2_zeroDbRef,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn2_zeroDbRef = {
+ DataAttributeModelType,
+ "zeroDbRef",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn2,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_AnInGGIO1_AnIn3 = {
+ DataObjectModelType,
+ "AnIn3",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_instMag,
+ 0
+};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_instMag = {
+ DataAttributeModelType,
+ "instMag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_mag,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_instMag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_instMag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_instMag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_q,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_mag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_mag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_t,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_db,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_db = {
+ DataAttributeModelType,
+ "db",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_zeroDb,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_zeroDb = {
+ DataAttributeModelType,
+ "zeroDb",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC = {
+ DataAttributeModelType,
+ "rangeC",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3,
+ NULL,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min = {
+ DataAttributeModelType,
+ "min",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min_f,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max = {
+ DataAttributeModelType,
+ "max",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC,
+ NULL,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max_f,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataObject iedModel_LD1_AnInGGIO1_AnIn4 = {
+ DataObjectModelType,
+ "AnIn4",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1,
+ NULL,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_instMag,
+ 0
+};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_instMag = {
+ DataAttributeModelType,
+ "instMag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_mag,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_instMag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_instMag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_instMag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_q,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_mag_f,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_mag,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_t,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_db,
+ NULL,
+ 0,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_db = {
+ DataAttributeModelType,
+ "db",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_zeroDb,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_zeroDb = {
+ DataAttributeModelType,
+ "zeroDb",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_INT32U,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC = {
+ DataAttributeModelType,
+ "rangeC",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4,
+ NULL,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min = {
+ DataAttributeModelType,
+ "min",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min_f,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max = {
+ DataAttributeModelType,
+ "max",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC,
+ NULL,
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max_f,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max,
+ NULL,
+ NULL,
+ 0,
+ IEC61850_FC_CF,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+extern ReportControlBlock iedModel_LD1_LLN0_report0;
+extern ReportControlBlock iedModel_LD1_LLN0_report1;
+extern ReportControlBlock iedModel_LD1_LLN0_report2;
+extern ReportControlBlock iedModel_LD1_LLN0_report3;
+
+ReportControlBlock iedModel_LD1_LLN0_report0 = {&iedModel_LD1_LLN0, "BRCB_Events01", NULL, true, "AnalogEvents", 1, 19, 247, 0, 5000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_LD1_LLN0_report1};
+ReportControlBlock iedModel_LD1_LLN0_report1 = {&iedModel_LD1_LLN0, "BRCB_Events02", NULL, true, "AnalogEvents", 1, 19, 247, 0, 5000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_LD1_LLN0_report2};
+ReportControlBlock iedModel_LD1_LLN0_report2 = {&iedModel_LD1_LLN0, "URCB_Events01", NULL, false, "AnalogEvents", 1, 19, 183, 0, 5000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_LD1_LLN0_report3};
+ReportControlBlock iedModel_LD1_LLN0_report3 = {&iedModel_LD1_LLN0, "URCB_Events02", NULL, false, "AnalogEvents", 1, 19, 183, 0, 5000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, NULL};
+
+
+
+
+
+
+
+IedModel iedModel = {
+ "IED1",
+ &iedModel_LD1,
+ &iedModelds_LD1_LLN0_AnalogEvents,
+ &iedModel_LD1_LLN0_report0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ initializeValues
+};
+
+static void
+initializeValues()
+{
+
+iedModel_LD1_LLN0_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_LD1_AnInGGIO1_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_LD1_AnInGGIO1_AnIn1_db.mmsValue = MmsValue_newUnsignedFromUint32(10000);
+
+iedModel_LD1_AnInGGIO1_AnIn1_zeroDb.mmsValue = MmsValue_newUnsignedFromUint32(1000);
+
+iedModel_LD1_AnInGGIO1_AnIn1_dbRef.mmsValue = MmsValue_newFloat(10.0);
+
+iedModel_LD1_AnInGGIO1_AnIn1_zeroDbRef.mmsValue = MmsValue_newFloat(10.0);
+
+iedModel_LD1_AnInGGIO1_AnIn2_db.mmsValue = MmsValue_newUnsignedFromUint32(1000);
+
+iedModel_LD1_AnInGGIO1_AnIn2_zeroDb.mmsValue = MmsValue_newUnsignedFromUint32(1000);
+
+iedModel_LD1_AnInGGIO1_AnIn2_dbRef.mmsValue = MmsValue_newFloat(10.0);
+
+iedModel_LD1_AnInGGIO1_AnIn2_zeroDbRef.mmsValue = MmsValue_newFloat(10.0);
+
+iedModel_LD1_AnInGGIO1_AnIn3_db.mmsValue = MmsValue_newUnsignedFromUint32(10000);
+
+iedModel_LD1_AnInGGIO1_AnIn3_zeroDb.mmsValue = MmsValue_newUnsignedFromUint32(1000);
+
+iedModel_LD1_AnInGGIO1_AnIn3_rangeC_min_f.mmsValue = MmsValue_newFloat(-5.0);
+
+iedModel_LD1_AnInGGIO1_AnIn3_rangeC_max_f.mmsValue = MmsValue_newFloat(5.0);
+
+iedModel_LD1_AnInGGIO1_AnIn4_db.mmsValue = MmsValue_newUnsignedFromUint32(1000);
+
+iedModel_LD1_AnInGGIO1_AnIn4_zeroDb.mmsValue = MmsValue_newUnsignedFromUint32(1000);
+
+iedModel_LD1_AnInGGIO1_AnIn4_rangeC_min_f.mmsValue = MmsValue_newFloat(-100.0);
+
+iedModel_LD1_AnInGGIO1_AnIn4_rangeC_max_f.mmsValue = MmsValue_newFloat(100.0);
+}
diff --git a/examples/server_example_deadband/static_model.h b/examples/server_example_deadband/static_model.h
new file mode 100644
index 00000000..3be3f25a
--- /dev/null
+++ b/examples/server_example_deadband/static_model.h
@@ -0,0 +1,207 @@
+/*
+ * static_model.h
+ *
+ * automatically generated from cid_example_deadband.cid
+ */
+
+#ifndef STATIC_MODEL_H_
+#define STATIC_MODEL_H_
+
+#include
+#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_ */
+
diff --git a/examples/server_example_logging/Makefile.standalone b/examples/server_example_logging/Makefile.standalone
new file mode 100644
index 00000000..cf61501b
--- /dev/null
+++ b/examples/server_example_logging/Makefile.standalone
@@ -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)
+
+
diff --git a/examples/sv_publisher/sv_publisher_example.c b/examples/sv_publisher/sv_publisher_example.c
index aa088fe9..48975351 100644
--- a/examples/sv_publisher/sv_publisher_example.c
+++ b/examples/sv_publisher/sv_publisher_example.c
@@ -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
@@ -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);
}
diff --git a/fuzz/fuzz_mms_decode.c b/fuzz/fuzz_mms_decode.c
new file mode 100644
index 00000000..3a2d9370
--- /dev/null
+++ b/fuzz/fuzz_mms_decode.c
@@ -0,0 +1,17 @@
+#include
+#include
+
+#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;
+}
diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt
index 2ca0ea94..6bf01739 100644
--- a/hal/CMakeLists.txt
+++ b/hal/CMakeLists.txt
@@ -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
diff --git a/hal/ethernet/bsd/ethernet_bsd.c b/hal/ethernet/bsd/ethernet_bsd.c
index 6b51ea44..536de0fb 100644
--- a/hal/ethernet/bsd/ethernet_bsd.c
+++ b/hal/ethernet/bsd/ethernet_bsd.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);
diff --git a/hal/ethernet/linux/ethernet_linux.c b/hal/ethernet/linux/ethernet_linux.c
index 728b5d10..57d4d3b2 100644
--- a/hal/ethernet/linux/ethernet_linux.c
+++ b/hal/ethernet/linux/ethernet_linux.c
@@ -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)
diff --git a/hal/ethernet/win32/ethernet_win32.c b/hal/ethernet/win32/ethernet_win32.c
index d2a1f2f5..c6269f37 100644
--- a/hal/ethernet/win32/ethernet_win32.c
+++ b/hal/ethernet/win32/ethernet_win32.c
@@ -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);
diff --git a/hal/inc/hal_time.h b/hal/inc/hal_time.h
index 52782959..7d5a942c 100644
--- a/hal/inc/hal_time.h
+++ b/hal/inc/hal_time.h
@@ -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);
diff --git a/hal/socket/win32/socket_win32.c b/hal/socket/win32/socket_win32.c
index 232ba17f..411265d8 100644
--- a/hal/socket/win32/socket_win32.c
+++ b/hal/socket/win32/socket_win32.c
@@ -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;
}
diff --git a/hal/thread/macos/thread_macos.c b/hal/thread/macos/thread_macos.c
index e51035d7..64d9a6dd 100644
--- a/hal/thread/macos/thread_macos.c
+++ b/hal/thread/macos/thread_macos.c
@@ -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) {
- printf("ERROR: Failed to create semaphore (errno = %i)\n", errno);
- }
-
- int ret = sem_unlink(tmpname);
+ 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);
- if (ret == -1)
- printf("ERROR: Failed to unlink semaphore %s\n", tmpname);
+ GLOBAL_FREEMEM(self);
+ self = NULL;
+ }
+ else {
+ int ret = sem_unlink(tmpname);
- return self;
+ if (ret == -1)
+ printf("ERROR: Failed to unlink semaphore %s\n", tmpname);
+ }
+ }
+ }
+
+ 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
diff --git a/hal/time/win32/time.c b/hal/time/win32/time.c
index 0b182d20..1e7adfe3 100644
--- a/hal/time/win32/time.c
+++ b/hal/time/win32/time.c
@@ -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);
+}
+
diff --git a/make/target_system.mk b/make/target_system.mk
index 8e9b7c1e..1c35c642 100644
--- a/make/target_system.mk
+++ b/make/target_system.mk
@@ -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
diff --git a/pyiec61850/eventHandlers/commandTermHandler.hpp b/pyiec61850/eventHandlers/commandTermHandler.hpp
new file mode 100644
index 00000000..64fb6e38
--- /dev/null
+++ b/pyiec61850/eventHandlers/commandTermHandler.hpp
@@ -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(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
diff --git a/pyiec61850/eventHandlers/eventHandler.hpp b/pyiec61850/eventHandlers/eventHandler.hpp
index 1ef20f7b..e69bf476 100644
--- a/pyiec61850/eventHandlers/eventHandler.hpp
+++ b/pyiec61850/eventHandlers/eventHandler.hpp
@@ -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 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::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::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
diff --git a/pyiec61850/eventHandlers/gooseHandler.hpp b/pyiec61850/eventHandlers/gooseHandler.hpp
index a01d3a6a..5984d478 100644
--- a/pyiec61850/eventHandlers/gooseHandler.hpp
+++ b/pyiec61850/eventHandlers/gooseHandler.hpp
@@ -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,17 +53,29 @@ 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");
+ }
}
// Setters
diff --git a/pyiec61850/eventHandlers/reportControlBlockHandler.hpp b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp
index ad798c1d..25411ae3 100644
--- a/pyiec61850/eventHandlers/reportControlBlockHandler.hpp
+++ b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp
@@ -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
diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i
index 71aceaef..26f6655f 100644
--- a/pyiec61850/iec61850.i
+++ b/pyiec61850/iec61850.i
@@ -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 */
%{
diff --git a/src/goose/goose_publisher.c b/src/goose/goose_publisher.c
index 51e455a0..97b536fa 100644
--- a/src/goose/goose_publisher.c
+++ b/src/goose/goose_publisher.c
@@ -318,7 +318,14 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
while (element != NULL) {
MmsValue* dataSetEntry = (MmsValue*) element->data;
- dataSetSize += MmsValue_encodeMmsData(dataSetEntry, NULL, 0, false);
+ 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;
- bufPos = MmsValue_encodeMmsData(dataSetEntry, buffer, bufPos, true);
+ if (dataSetEntry) {
+ bufPos = MmsValue_encodeMmsData(dataSetEntry, buffer, bufPos, true);
+ }
+ else {
+ /* TODO encode MMS NULL */
+ }
element = LinkedList_getNext(element);
}
diff --git a/src/goose/goose_receiver.c b/src/goose/goose_receiver.c
index bed369df..36a1229d 100644
--- a/src/goose/goose_receiver.c
+++ b/src/goose/goose_receiver.c
@@ -760,10 +760,12 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
matchingSubscriber->ndsCom = ndsCom;
matchingSubscriber->simulation = simulation;
- /* when confRev changed replaced old data set */
- if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev)) {
- MmsValue_delete(matchingSubscriber->dataSetValues);
- matchingSubscriber->dataSetValues = NULL;
+ 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;
diff --git a/src/goose/goose_subscriber.c b/src/goose/goose_subscriber.c
index 3659aa36..198ba595 100644
--- a/src/goose/goose_subscriber.c
+++ b/src/goose/goose_subscriber.c
@@ -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;
diff --git a/src/goose/goose_subscriber.h b/src/goose/goose_subscriber.h
index f88432dd..e58f4f96 100644
--- a/src/goose/goose_subscriber.h
+++ b/src/goose/goose_subscriber.h
@@ -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.
*
diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c
index ad31648d..4cd837ac 100644
--- a/src/iec61850/client/client_control.c
+++ b/src/iec61850/client/client_control.c
@@ -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)
{
diff --git a/src/iec61850/client/client_goose_control.c b/src/iec61850/client/client_goose_control.c
index 7fa44444..f6e0743a 100644
--- a/src/iec61850/client/client_goose_control.c
+++ b/src/iec61850/client/client_goose_control.c
@@ -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 {
diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index 3b9b4361..e8eb0923 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -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)
diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h
index c5c51818..9d1946e9 100644
--- a/src/iec61850/inc/iec61850_client.h
+++ b/src/iec61850/inc/iec61850_client.h
@@ -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
diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h
index c3202ef3..957883dd 100644
--- a/src/iec61850/inc/iec61850_common.h
+++ b/src/iec61850/inc/iec61850_common.h
@@ -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
/** @} */
diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h
index 13599698..4138d13c 100644
--- a/src/iec61850/inc/iec61850_dynamic_model.h
+++ b/src/iec61850/inc/iec61850_dynamic_model.h
@@ -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
*
diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h
index b788838c..1f10db3c 100644
--- a/src/iec61850/inc/iec61850_model.h
+++ b/src/iec61850/inc/iec61850_model.h
@@ -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
*
diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h
index 04d95a9c..e69f2c0e 100644
--- a/src/iec61850/inc/iec61850_server.h
+++ b/src/iec61850/inc/iec61850_server.h
@@ -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
diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h
index 6b1aad8a..ed6af512 100644
--- a/src/iec61850/inc_private/ied_server_private.h
+++ b/src/iec61850/inc_private/ied_server_private.h
@@ -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)
diff --git a/src/iec61850/inc_private/logging.h b/src/iec61850/inc_private/logging.h
index 9ec8f936..74caf056 100644
--- a/src/iec61850/inc_private/logging.h
+++ b/src/iec61850/inc_private/logging.h
@@ -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);
diff --git a/src/iec61850/inc_private/mms_goose.h b/src/iec61850/inc_private/mms_goose.h
index e2c6f93f..98923c2b 100644
--- a/src/iec61850/inc_private/mms_goose.h
+++ b/src/iec61850/inc_private/mms_goose.h
@@ -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
diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c
index 5aba1695..60df8ce3 100644
--- a/src/iec61850/server/impl/ied_server.c
+++ b/src/iec61850/server/impl/ied_server.c
@@ -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,56 +538,63 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->mmsMapping = MmsMapping_create(dataModel, self);
- self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping);
+ if (self->mmsMapping) {
- self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration);
+ self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping);
+
+ self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration);
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
- if (serverConfiguration) {
- MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService);
- MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService);
- MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets);
- MmsServer_setMaxDomainSpecificDataSets(self->mmsServer, serverConfiguration->maxDomainSpecificDataSets);
- MmsServer_setMaxDataSetEntries(self->mmsServer, serverConfiguration->maxDataSetEntries);
- MmsServer_enableJournalService(self->mmsServer, serverConfiguration->enableLogService);
- MmsServer_setFilestoreBasepath(self->mmsServer, serverConfiguration->fileServiceBasepath);
- MmsServer_setMaxConnections(self->mmsServer, serverConfiguration->maxMmsConnections);
- }
+ if (serverConfiguration) {
+ MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService);
+ MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService);
+ MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets);
+ MmsServer_setMaxDomainSpecificDataSets(self->mmsServer, serverConfiguration->maxDomainSpecificDataSets);
+ MmsServer_setMaxDataSetEntries(self->mmsServer, serverConfiguration->maxDataSetEntries);
+ MmsServer_enableJournalService(self->mmsServer, serverConfiguration->enableLogService);
+ MmsServer_setFilestoreBasepath(self->mmsServer, serverConfiguration->fileServiceBasepath);
+ MmsServer_setMaxConnections(self->mmsServer, serverConfiguration->maxMmsConnections);
+ }
#endif
- MmsMapping_setMmsServer(self->mmsMapping, self->mmsServer);
+ MmsMapping_setMmsServer(self->mmsMapping, self->mmsServer);
- MmsMapping_installHandlers(self->mmsMapping);
+ MmsMapping_installHandlers(self->mmsMapping);
- createMmsServerCache(self);
+ createMmsServerCache(self);
- dataModel->initializer();
+ dataModel->initializer();
- installDefaultValuesInCache(self); /* This will also connect cached MmsValues to DataAttributes */
+ installDefaultValuesInCache(self); /* This will also connect cached MmsValues to DataAttributes */
- updateDataSetsWithCachedValues(self);
+ updateDataSetsWithCachedValues(self);
- self->clientConnections = LinkedList_create();
+ self->clientConnections = LinkedList_create();
- /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */
- self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE;
+ /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */
+ self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE;
- MmsMapping_initializeControlObjects(self->mmsMapping);
+ MmsMapping_initializeControlObjects(self->mmsMapping);
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
- Reporting_activateBufferedReports(self->mmsMapping);
+ Reporting_activateBufferedReports(self->mmsMapping);
#endif
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
- MmsMapping_configureSettingGroups(self->mmsMapping);
+ MmsMapping_configureSettingGroups(self->mmsMapping);
#endif
#if (CONFIG_INCLUDE_GOOSE_SUPPORT)
- if (serverConfiguration) {
- MmsMapping_useIntegratedGoosePublisher(self->mmsMapping, serverConfiguration->useIntegratedGoosePublisher);
- }
-
+ if (serverConfiguration) {
+ MmsMapping_useIntegratedGoosePublisher(self->mmsMapping, serverConfiguration->useIntegratedGoosePublisher);
+ }
#endif
+
+ }
+ else {
+ IedServer_destroy(self);
+ self = NULL;
+ }
}
return self;
@@ -620,7 +637,8 @@ IedServer_destroy(IedServer self)
if (self->localIpAddress != NULL)
GLOBAL_FREEMEM(self->localIpAddress);
- MmsMapping_destroy(self->mmsMapping);
+ 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,9 +1727,14 @@ IedServer_setServerIdentity(IedServer self, const char* vendor, const char* mode
if (self->revision)
GLOBAL_FREEMEM(self->revision);
- self->vendorName = StringUtils_copyString(vendor);
- self->modelName = StringUtils_copyString(model);
- self->revision = StringUtils_copyString(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);
#endif
diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c
index 4f79819d..999ab4cd 100644
--- a/src/iec61850/server/impl/ied_server_config.c
+++ b/src/iec61850/server/impl/ied_server_config.c
@@ -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)
{
diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c
index 5bed8f1e..17dbf725 100644
--- a/src/iec61850/server/mms_mapping/control.c
+++ b/src/iec61850/server/mms_mapping/control.c
@@ -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,8 +1827,13 @@ 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) {
- indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
- ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
- ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
+ if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) {
+ indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
- if (DEBUG_IED_SERVER)
- printf("IED_SERVER: SBOw - select failed!\n");
+ 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 - 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) {
diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c
index 6d9e4551..e27d5f89 100644
--- a/src/iec61850/server/mms_mapping/logging.c
+++ b/src/iec61850/server/mms_mapping/logging.c
@@ -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);
diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c
index 730bbfff..df7a79ac 100644
--- a/src/iec61850/server/mms_mapping/mms_goose.c
+++ b/src/iec61850/server/mms_mapping/mms_goose.c
@@ -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,85 +437,112 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping)
if (self->dataSet != NULL) {
- MmsValue* goEna = MmsValue_getElement(self->mmsValue, 0);
+ 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);
+ MmsValue_setBoolean(goEna, true);
- MmsValue* dstAddress = MmsValue_getElement(self->mmsValue, 5);
+ MmsValue* dstAddress = MmsValue_getElement(self->mmsValue, 5);
- CommParameters commParameters;
- commParameters.appId = MmsValue_toInt32(MmsValue_getElement(dstAddress, 3));
- commParameters.vlanId = MmsValue_toInt32(MmsValue_getElement(dstAddress, 2));
- commParameters.vlanPriority = MmsValue_toInt32(MmsValue_getElement(dstAddress, 1));
+ CommParameters commParameters;
+ commParameters.appId = MmsValue_toInt32(MmsValue_getElement(dstAddress, 3));
+ commParameters.vlanId = MmsValue_toInt32(MmsValue_getElement(dstAddress, 2));
+ commParameters.vlanPriority = MmsValue_toInt32(MmsValue_getElement(dstAddress, 1));
- MmsValue* macAddress = MmsValue_getElement(dstAddress, 0);
+ MmsValue* macAddress = MmsValue_getElement(dstAddress, 0);
- memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6);
+ memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6);
- if (mmsMapping->useIntegratedPublisher) {
+ if (mmsMapping->useIntegratedPublisher) {
- if (self->gooseInterfaceId)
- self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag);
- else
- self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag);
+ if (self->gooseInterfaceId)
+ self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag);
+ else
+ self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag);
- if (self->publisher) {
- self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6));
- self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7));
+ if (self->publisher) {
+ self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6));
+ self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7));
- GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
+ GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
- GoosePublisher_setDataSetRef(self->publisher, self->dataSetRef);
+ GoosePublisher_setDataSetRef(self->publisher, self->dataSetRef);
- GoosePublisher_setGoCbRef(self->publisher, self->goCBRef);
+ GoosePublisher_setGoCbRef(self->publisher, self->goCBRef);
- uint32_t confRev = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 3));
+ uint32_t confRev = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 3));
- GoosePublisher_setConfRev(self->publisher, confRev);
+ GoosePublisher_setConfRev(self->publisher, confRev);
- bool needsCom = MmsValue_getBoolean(MmsValue_getElement(self->mmsValue, 4));
+ bool needsCom = MmsValue_getBoolean(MmsValue_getElement(self->mmsValue, 4));
- GoosePublisher_setNeedsCommission(self->publisher, needsCom);
+ GoosePublisher_setNeedsCommission(self->publisher, needsCom);
- if (self->goId != NULL)
- GoosePublisher_setGoID(self->publisher, self->goId);
+ if (self->goId != NULL)
+ GoosePublisher_setGoID(self->publisher, self->goId);
- /* prepare data set values */
- self->dataSetValues = LinkedList_create();
+ /* prepare data set values */
+ self->dataSetValues = LinkedList_create();
- DataSetEntry* dataSetEntry = self->dataSet->fcdas;
+ DataSetEntry* dataSetEntry = self->dataSet->fcdas;
- while (dataSetEntry != NULL) {
- LinkedList_add(self->dataSetValues, dataSetEntry->value);
- dataSetEntry = dataSetEntry->sibling;
- }
+ while (dataSetEntry != NULL) {
+ LinkedList_add(self->dataSetValues, dataSetEntry->value);
+ dataSetEntry = dataSetEntry->sibling;
+ }
+ }
+ else {
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: Failed to create GOOSE publisher!\n");
+ }
}
- else {
- if (DEBUG_IED_SERVER)
- printf("IED_SERVER: Failed to create GOOSE publisher!\n");
- }
- }
- self->goEna = true;
+ 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);
+ copyGCBValuesToTrackingObject(self);
+ updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, DATA_ACCESS_ERROR_SUCCESS);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
+ }
}
}
else {
- printf("GoCB already enabled!\n");
+ if (DEBUG_IED_SERVER)
+ printf("GoCB already enabled!\n");
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex);
#endif
+
+ return retVal;
}
void
@@ -506,37 +582,45 @@ 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);
+ Semaphore_wait(self->publisherMutex);
#endif
- GoosePublisher_publish(self->publisher, self->dataSetValues);
+ GoosePublisher_publish(self->publisher, self->dataSetValues);
- if (self->retransmissionsLeft > 0) {
- self->nextPublishTime = currentTime + self->minTime;
+ if (self->retransmissionsLeft > 0) {
+ self->nextPublishTime = currentTime + self->minTime;
- if (self->retransmissionsLeft > 1)
- GoosePublisher_setTimeAllowedToLive(self->publisher, self->minTime * 3);
- else
- GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
+ if (self->retransmissionsLeft > 1)
+ GoosePublisher_setTimeAllowedToLive(self->publisher, self->minTime * 3);
+ else
+ GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
- self->retransmissionsLeft--;
- }
- else {
- GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
+ self->retransmissionsLeft--;
+ }
+ else {
+ GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
- self->nextPublishTime = currentTime + self->maxTime;
- }
+ self->nextPublishTime = currentTime + self->maxTime;
+ }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
- Semaphore_post(self->publisherMutex);
+ 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;
diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
index 81d8a12a..feb73c00 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -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,9 +2009,13 @@ createMmsModelFromIedModel(MmsMapping* self, IedModel* iedModel)
int iedDeviceCount = IedModel_getLogicalDeviceCount(iedModel);
- createMmsDataModel(self, iedDeviceCount, mmsDevice, iedModel);
-
- createDataSets(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 (self->goCbHandler)
- self->goCbHandler(mmsGCB, IEC61850_GOCB_EVENT_ENABLE, self->goCbHandlerParameter);
+ 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,51 +2879,25 @@ 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) {
+ if (dataAttribute->mmsValue == cachedValue) {
- ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
- connection);
+ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
+ connection);
- MmsDataAccessError handlerResult =
- accessHandler->handler(dataAttribute, value, clientConnection,
- accessHandler->parameter);
+ MmsDataAccessError handlerResult =
+ accessHandler->handler(dataAttribute, value, clientConnection,
+ accessHandler->parameter);
- if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) {
- handlerFound = true;
+ 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;
+ if (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)
+ updateValue = false;
- break;
- }
- else
- return handlerResult;
+ break;
}
+ 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) {
+ MmsGooseControlBlock gcb = (MmsGooseControlBlock) LinkedList_getData(element);
- while ((element = LinkedList_getNext(element)) != NULL) {
- MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
+ if (MmsGooseControlBlock_enable(gcb, self) == false) {
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: failed to enable GoCB %s\n", MmsGooseControlBlock_getName(gcb));
+ }
- MmsGooseControlBlock_enable(gcb, self);
+ 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);
diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c
index 40454739..25823dc7 100644
--- a/src/iec61850/server/mms_mapping/reporting.c
+++ b/src/iec61850/server/mms_mapping/reporting.c
@@ -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 (strcmp(elementName, "ResvTms") == 0)
- return MmsValue_getElement(rc->rcbValues, 13);
- if (strcmp(elementName, "Owner") == 0)
- return MmsValue_getElement(rc->rcbValues, 14);
+ 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,7 +1124,8 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
if (reportControl->server->edition >= IEC_61850_EDITION_2) {
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
- brcbElementCount++;
+ if (reportControl->server->enableBRCBResvTms)
+ brcbElementCount++;
#endif
if (reportControl->hasOwner)
@@ -1249,16 +1256,17 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
int currentIndex = 13;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
-
int resvTmsIndex = currentIndex;
- namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
- namedVariable->name = StringUtils_copyString("ResvTms");
- namedVariable->type = MMS_INTEGER;
- namedVariable->typeSpec.integer = 16;
- rcb->typeSpec.structure.elements[currentIndex] = namedVariable;
- mmsValue->value.structure.components[currentIndex] = MmsValue_newInteger(16);
- currentIndex++;
+ if (reportControl->server->enableBRCBResvTms) {
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
+ namedVariable->name = StringUtils_copyString("ResvTms");
+ namedVariable->type = MMS_INTEGER;
+ namedVariable->typeSpec.integer = 16;
+ 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)
- MmsValue_setInt16(mmsValue->value.structure.components[resvTmsIndex], reportControl->resvTms);
+ 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)
- MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms");
- if (resvTmsVal)
- MmsValue_setInt16(resvTmsVal, rc->resvTms);
+ 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)
- MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms");
- if (resvTmsVal)
- MmsValue_setInt16(resvTmsVal, rc->resvTms);
+ 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)
diff --git a/src/iec61850/server/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c
index a55cab2e..ee9d2aec 100644
--- a/src/iec61850/server/model/config_file_parser.c
+++ b/src/iec61850/server/model/config_file_parser.c
@@ -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);
- GSEControlBlock_addPhyComAddress(currentGoCB, dstAddress);
+ 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;
}
diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c
index 4377de8b..b8e9816b 100644
--- a/src/iec61850/server/model/dynamic_model.c
+++ b/src/iec61850/server/model/dynamic_model.c
@@ -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)
{
diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c
index b8083ea0..7c3ef542 100644
--- a/src/iec61850/server/model/model.c
+++ b/src/iec61850/server/model/model.c
@@ -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;
- strncpy(objectReference, iedModel->name, 64);
- strncat(objectReference, lDevice->name, 64);
+ 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)
{
diff --git a/src/logging/drivers/sqlite/log_storage_sqlite.c b/src/logging/drivers/sqlite/log_storage_sqlite.c
index fcb02ec9..49094441 100644
--- a/src/logging/drivers/sqlite/log_storage_sqlite.c
+++ b/src/logging/drivers/sqlite/log_storage_sqlite.c
@@ -23,9 +23,9 @@
#include "logging_api.h"
-#include "libiec61850_platform_includes.h"
#include "sqlite3.h"
+#include
#ifndef DEBUG_LOG_STORAGE_DRIVER
#define DEBUG_LOG_STORAGE_DRIVER 0
diff --git a/src/logging/log_storage.c b/src/logging/log_storage.c
index 40335513..a4a52f22 100644
--- a/src/logging/log_storage.c
+++ b/src/logging/log_storage.c
@@ -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)
{
diff --git a/src/logging/logging_api.h b/src/logging/logging_api.h
index a4493535..7f5327fc 100644
--- a/src/logging/logging_api.h
+++ b/src/logging/logging_api.h
@@ -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
*
diff --git a/src/mms/asn1/ber_decode.c b/src/mms/asn1/ber_decode.c
index 454de0d4..12078610 100644
--- a/src/mms/asn1/ber_decode.c
+++ b/src/mms/asn1/ber_decode.c
@@ -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)
{
diff --git a/src/mms/asn1/ber_integer.c b/src/mms/asn1/ber_integer.c
index 39bac880..9a7e8030 100644
--- a/src/mms/asn1/ber_integer.c
+++ b/src/mms/asn1/ber_integer.c
@@ -203,63 +203,45 @@ 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;
-
- if (buf[0] & 0x80) /* sign extension */
- *nativeValue = 0xffffffff;
- else
- *nativeValue = 0;
-
- for (i = 0; i < self->size; i++)
- *nativeValue = (*nativeValue << 8) | buf[i];
+ uint8_t* buf = self->octets;
+ int i;
- return 1;
- }
+ if (buf[0] & 0x80) /* sign extension */
+ *nativeValue = 0xffffffff;
else
- return 0;
+ *nativeValue = 0;
+
+ for (i = 0; i < self->size; i++)
+ *nativeValue = (*nativeValue << 8) | buf[i];
}
-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;
-
- *nativeValue = 0;
+ uint8_t* buf = self->octets;
+ int i;
- for (i = 0; i < self->size; i++)
- *nativeValue = (*nativeValue << 8) | buf[i];
+ *nativeValue = 0;
- return 1;
- }
- else
- return 0;
+ for (i = 0; i < self->size; i++)
+ *nativeValue = (*nativeValue << 8) | buf[i];
}
-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;
-
- if (buf[0] & 0x80) /* sign extension */
- *nativeValue = 0xffffffffffffffff;
- else
- *nativeValue = 0;
-
- for (i = 0; i < self->size; i++)
- *nativeValue = (*nativeValue << 8) | buf[i];
+ uint8_t* buf = self->octets;
+ int i;
- return 1;
- }
+ if (buf[0] & 0x80) /* sign extension */
+ *nativeValue = 0xffffffffffffffff;
else
- return 0;
+ *nativeValue = 0;
+
+ for (i = 0; i < self->size; i++)
+ *nativeValue = (*nativeValue << 8) | buf[i];
}
diff --git a/src/mms/inc/mms_value.h b/src/mms/inc/mms_value.h
index 57e2f39e..28b1fab4 100644
--- a/src/mms/inc/mms_value.h
+++ b/src/mms/inc/mms_value.h
@@ -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);
+
/**@}*/
/**@}*/
diff --git a/src/mms/inc_private/ber_integer.h b/src/mms/inc_private/ber_integer.h
index 0e751bb5..054ad729 100644
--- a/src/mms/inc_private/ber_integer.h
+++ b/src/mms/inc_private/ber_integer.h
@@ -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
diff --git a/src/mms/inc_private/iso_server_private.h b/src/mms/inc_private/iso_server_private.h
index dac94c72..6249255e 100644
--- a/src/mms/inc_private/iso_server_private.h
+++ b/src/mms/inc_private/iso_server_private.h
@@ -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);
diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h
index 81f68e91..4dd10ded 100644
--- a/src/mms/inc_private/mms_client_internal.h
+++ b/src/mms/inc_private/mms_client_internal.h
@@ -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);
diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h
index df06d35f..5e2f458d 100644
--- a/src/mms/inc_private/mms_server_internal.h
+++ b/src/mms/inc_private/mms_server_internal.h
@@ -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) */
diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h
index 2e18f4a2..ef8f95f4 100644
--- a/src/mms/inc_private/mms_server_libinternal.h
+++ b/src/mms/inc_private/mms_server_libinternal.h
@@ -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_ */
diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c
index f6cc4b42..6e71263a 100644
--- a/src/mms/iso_client/iso_client_connection.c
+++ b/src/mms/iso_client/iso_client_connection.c
@@ -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);
@@ -280,10 +286,10 @@ releaseSocket(IsoClientConnection self)
if (self->socket) {
#if (CONFIG_MMS_SUPPORT_TLS == 1)
- if (self->cotpConnection->tlsSocket) {
- TLSSocket_close(self->cotpConnection->tlsSocket);
- self->cotpConnection->tlsSocket = NULL;
- }
+ if (self->cotpConnection->tlsSocket) {
+ TLSSocket_close(self->cotpConnection->tlsSocket);
+ self->cotpConnection->tlsSocket = NULL;
+ }
#endif
Socket_destroy(self->socket);
@@ -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);
diff --git a/src/mms/iso_cotp/cotp.c b/src/mms/iso_cotp/cotp.c
index b34bc0dd..68ce1c1f 100644
--- a/src/mms/iso_cotp/cotp.c
+++ b/src/mms/iso_cotp/cotp.c
@@ -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
diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c
index 5644912d..241ddcb4 100644
--- a/src/mms/iso_mms/client/mms_client_connection.c
+++ b/src/mms/iso_mms/client/mms_client_connection.c
@@ -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);
diff --git a/src/mms/iso_mms/client/mms_client_files.c b/src/mms/iso_mms/client/mms_client_files.c
index d9215abc..307ab534 100644
--- a/src/mms/iso_mms/client/mms_client_files.c
+++ b/src/mms/iso_mms/client/mms_client_files.c
@@ -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) */
diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c
index 3e155320..02d66814 100644
--- a/src/mms/iso_mms/common/mms_value.c
+++ b/src/mms/iso_mms/common/mms_value.c
@@ -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)
{
diff --git a/src/mms/iso_mms/server/mms_access_result.c b/src/mms/iso_mms/server/mms_access_result.c
index 0beda10f..06b50d28 100644
--- a/src/mms/iso_mms/server/mms_access_result.c
+++ b/src/mms/iso_mms/server/mms_access_result.c
@@ -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;
}
diff --git a/src/mms/iso_mms/server/mms_association_service.c b/src/mms/iso_mms/server/mms_association_service.c
index 34aecc73..a3075e27 100644
--- a/src/mms/iso_mms/server/mms_association_service.c
+++ b/src/mms/iso_mms/server/mms_association_service.c
@@ -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) */
diff --git a/src/mms/iso_mms/server/mms_file_service.c b/src/mms/iso_mms/server/mms_file_service.c
index d569fab6..eb3cfbab 100644
--- a/src/mms/iso_mms/server/mms_file_service.c
+++ b/src/mms/iso_mms/server/mms_file_service.c
@@ -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);
}
}
diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c
index 13d7adce..3fb97675 100644
--- a/src/mms/iso_mms/server/mms_read_service.c
+++ b/src/mms/iso_mms/server/mms_read_service.c
@@ -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 {
diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c
index 80af245b..c387cb8b 100644
--- a/src/mms/iso_mms/server/mms_server.c
+++ b/src/mms/iso_mms/server/mms_server.c
@@ -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)
- mmsServer_fileUploadTask(self, &(self->fileUploadTasks[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
+}
+
+
diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c
index afb81cef..56f66099 100644
--- a/src/mms/iso_mms/server/mms_server_connection.c
+++ b/src/mms/iso_mms/server/mms_server_connection.c
@@ -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)
diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c
index ad1fbf6a..31a171e4 100644
--- a/src/mms/iso_mms/server/mms_write_service.c
+++ b/src/mms/iso_mms/server/mms_write_service.c
@@ -501,6 +501,8 @@ mmsServer_handleWriteRequest(
goto exit_function;
}
+ MmsServer_lockModel(connection->server);
+
WriteRequest_t* writeRequest = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) {
@@ -695,13 +697,16 @@ end_of_main_loop:
if (sendResponse)
mmsServer_createMmsWriteResponse(connection, invokeId, response, numberOfWriteItems, accessResults);
}
- else { /* unknown request type */
+ else { /* unknown request type */
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
goto exit_function;
- }
+ }
exit_function:
- asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+
+ MmsServer_unlockModel(connection->server);
+
+ asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
}
#endif /* (MMS_WRITE_SERVICE == 1) */
diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c
index 5f5b70dc..7e2a5b3f 100644
--- a/src/mms/iso_server/iso_connection.c
+++ b/src/mms/iso_server/iso_connection.c
@@ -329,7 +329,6 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
ByteBuffer mmsResponseBuffer;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
- IsoServer_userLock(self->isoServer);
IsoConnection_lock(self);
#endif
@@ -370,7 +369,6 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
#if (CONFIG_MMS_THREADLESS_STACK != 1)
IsoConnection_unlock(self);
- IsoServer_userUnlock(self->isoServer);
#endif
}
else {
@@ -389,7 +387,6 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
printf("ISO_SERVER: iso_connection: presentation ok\n");
#if (CONFIG_MMS_THREADLESS_STACK != 1)
- IsoServer_userLock(self->isoServer);
IsoConnection_lock(self);
#endif
@@ -418,7 +415,6 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
#if (CONFIG_MMS_THREADLESS_STACK != 1)
IsoConnection_unlock(self);
- IsoServer_userUnlock(self->isoServer);
#endif
}
@@ -489,82 +485,84 @@ IsoConnection
IsoConnection_create(Socket socket, IsoServer isoServer, bool isSingleThread)
{
IsoConnection self = (IsoConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoConnection));
- self->socket = socket;
+
+ if (self) {
+ self->socket = socket;
#if (CONFIG_MMS_SUPPORT_TLS == 1)
- if (IsoServer_getTLSConfiguration(isoServer) != NULL) {
- self->tlsSocket = TLSSocket_create(socket, IsoServer_getTLSConfiguration(isoServer), true);
+ if (IsoServer_getTLSConfiguration(isoServer) != NULL) {
+ self->tlsSocket = TLSSocket_create(socket, IsoServer_getTLSConfiguration(isoServer), true);
- if (self->tlsSocket == NULL) {
- if (DEBUG_ISO_SERVER)
- printf("ISO_SERVER: IsoConnection - TLS initialization failed\n");
+ if (self->tlsSocket == NULL) {
+ if (DEBUG_ISO_SERVER)
+ printf("ISO_SERVER: IsoConnection - TLS initialization failed\n");
- GLOBAL_FREEMEM(self);
+ GLOBAL_FREEMEM(self);
- return NULL;
+ return NULL;
+ }
}
- }
#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */
-
- self->receiveBuffer = (uint8_t*) GLOBAL_MALLOC(RECEIVE_BUF_SIZE);
- self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(SEND_BUF_SIZE);
- self->msgRcvdHandler = NULL;
- self->tickHandler = NULL;
- self->handlerParameter = NULL;
- self->isoServer = isoServer;
- self->state = ISO_CON_STATE_RUNNING;
- self->clientAddress = Socket_getPeerAddress(self->socket);
- self->localAddress = Socket_getLocalAddress(self->socket);
+ self->receiveBuffer = (uint8_t*) GLOBAL_MALLOC(RECEIVE_BUF_SIZE);
+ self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(SEND_BUF_SIZE);
+ self->msgRcvdHandler = NULL;
+ self->tickHandler = NULL;
+ self->handlerParameter = NULL;
+ self->isoServer = isoServer;
+ self->state = ISO_CON_STATE_RUNNING;
+ self->clientAddress = Socket_getPeerAddress(self->socket);
+ self->localAddress = Socket_getLocalAddress(self->socket);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
- self->conMutex = Semaphore_create(1);
+ self->conMutex = Semaphore_create(1);
#endif
- ByteBuffer_wrap(&(self->rcvBuffer), self->receiveBuffer, 0, RECEIVE_BUF_SIZE);
+ ByteBuffer_wrap(&(self->rcvBuffer), self->receiveBuffer, 0, RECEIVE_BUF_SIZE);
- self->cotpReadBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
- self->cotpWriteBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
+ self->cotpReadBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
+ self->cotpWriteBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
- ByteBuffer_wrap(&(self->cotpReadBuffer), self->cotpReadBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
- ByteBuffer_wrap(&(self->cotpWriteBuffer), self->cotpWriteBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
+ ByteBuffer_wrap(&(self->cotpReadBuffer), self->cotpReadBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
+ ByteBuffer_wrap(&(self->cotpWriteBuffer), self->cotpWriteBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE);
- self->cotpConnection = (CotpConnection*) GLOBAL_CALLOC(1, sizeof(CotpConnection));
- CotpConnection_init(self->cotpConnection, self->socket, &(self->rcvBuffer), &(self->cotpReadBuffer), &(self->cotpWriteBuffer));
+ self->cotpConnection = (CotpConnection*) GLOBAL_CALLOC(1, sizeof(CotpConnection));
+ CotpConnection_init(self->cotpConnection, self->socket, &(self->rcvBuffer), &(self->cotpReadBuffer), &(self->cotpWriteBuffer));
#if (CONFIG_MMS_SUPPORT_TLS == 1)
- if (self->tlsSocket)
- self->cotpConnection->tlsSocket = self->tlsSocket;
+ if (self->tlsSocket)
+ self->cotpConnection->tlsSocket = self->tlsSocket;
#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */
- self->session = (IsoSession*) GLOBAL_CALLOC(1, sizeof(IsoSession));
- IsoSession_init(self->session);
+ self->session = (IsoSession*) GLOBAL_CALLOC(1, sizeof(IsoSession));
+ IsoSession_init(self->session);
- self->presentation = (IsoPresentation*) GLOBAL_CALLOC(1, sizeof(IsoPresentation));
- IsoPresentation_init(self->presentation);
+ self->presentation = (IsoPresentation*) GLOBAL_CALLOC(1, sizeof(IsoPresentation));
+ IsoPresentation_init(self->presentation);
- self->acseConnection = (AcseConnection*) GLOBAL_CALLOC(1, sizeof(AcseConnection));
+ self->acseConnection = (AcseConnection*) GLOBAL_CALLOC(1, sizeof(AcseConnection));
#if (CONFIG_MMS_SUPPORT_TLS == 1)
- AcseConnection_init(self->acseConnection, IsoServer_getAuthenticator(self->isoServer),
- IsoServer_getAuthenticatorParameter(self->isoServer), self->tlsSocket);
+ AcseConnection_init(self->acseConnection, IsoServer_getAuthenticator(self->isoServer),
+ IsoServer_getAuthenticatorParameter(self->isoServer), self->tlsSocket);
#else
- AcseConnection_init(self->acseConnection, IsoServer_getAuthenticator(self->isoServer),
- IsoServer_getAuthenticatorParameter(self->isoServer), NULL);
+ AcseConnection_init(self->acseConnection, IsoServer_getAuthenticator(self->isoServer),
+ IsoServer_getAuthenticatorParameter(self->isoServer), NULL);
#endif
- if (DEBUG_ISO_SERVER)
- printf("ISO_SERVER: IsoConnection: Start to handle connection for client %s\n", self->clientAddress);
+ if (DEBUG_ISO_SERVER)
+ printf("ISO_SERVER: IsoConnection: Start to handle connection for client %s\n", self->clientAddress);
#if (CONFIG_MMS_SINGLE_THREADED == 0)
#if (CONFIG_MMS_THREADLESS_STACK == 0)
- if (isSingleThread == false) {
- self->handleSet = Handleset_new();
- Handleset_addSocket(self->handleSet, self->socket);
- self->thread = Thread_create((ThreadExecutionFunction) handleTcpConnection, self, false);
- }
-#endif
+ if (isSingleThread == false) {
+ self->handleSet = Handleset_new();
+ Handleset_addSocket(self->handleSet, self->socket);
+ self->thread = Thread_create((ThreadExecutionFunction) handleTcpConnection, self, false);
+ }
+ #endif
#endif
+ }
return self;
}
@@ -596,6 +594,11 @@ IsoConnection_destroy(IsoConnection self)
if (self->socket != NULL)
Socket_destroy(self->socket);
+#if (CONFIG_MMS_SINGLE_THREADED != 1) || (CONFIG_MMS_THREADLESS_STACK == 1)
+ if (self->handleSet)
+ Handleset_destroy(self->handleSet);
+#endif
+
if (self->cotpConnection) {
if (self->cotpConnection->handleSet)
Handleset_destroy(self->cotpConnection->handleSet);
diff --git a/src/mms/iso_server/iso_server.c b/src/mms/iso_server/iso_server.c
index e45224f9..8501e41b 100644
--- a/src/mms/iso_server/iso_server.c
+++ b/src/mms/iso_server/iso_server.c
@@ -84,11 +84,6 @@ struct sIsoServer {
IsoConnection openClientConnections[CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS];
#endif /* (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) */
-#if (CONFIG_MMS_THREADLESS_STACK != 1)
- /* used to control access to server data model */
- Semaphore userLock;
-#endif
-
#if (CONFIG_MMS_THREADLESS_STACK != 1) && (CONFIG_MMS_SINGLE_THREADED == 0)
Semaphore openClientConnectionsMutex; /* mutex for openClientConnections list */
Semaphore connectionCounterMutex;
@@ -878,26 +873,3 @@ private_IsoServer_getConnectionCounter(IsoServer self)
return connectionCounter;
}
-#if (CONFIG_MMS_THREADLESS_STACK != 1)
-
-void
-IsoServer_setUserLock(IsoServer self, Semaphore userLock)
-{
- self->userLock = userLock;
-}
-
-void
-IsoServer_userLock(IsoServer self)
-{
- if (self->userLock != NULL)
- Semaphore_wait(self->userLock);
-}
-
-void
-IsoServer_userUnlock(IsoServer self)
-{
- if (self->userLock != NULL)
- Semaphore_post(self->userLock);
-}
-
-#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c
index f37b3fbc..ee0e25a2 100644
--- a/src/sampled_values/sv_subscriber.c
+++ b/src/sampled_values/sv_subscriber.c
@@ -21,6 +21,7 @@
* See COPYING file for the complete license text.
*/
+#define __STDC_FORMAT_MACROS 1
#include "stack_config.h"
#include
@@ -356,9 +357,12 @@ parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length)
if (SVSubscriber_ASDU_hasDatSet(&asdu))
printf("SV_SUBSCRIBER: DatSet: %s\n", asdu.datSet);
-#ifdef PRIu64
+
if (SVSubscriber_ASDU_hasRefrTm(&asdu))
+#ifndef _MSC_VER
printf("SV_SUBSCRIBER: RefrTm[ns]: %"PRIu64"\n", SVSubscriber_ASDU_getRefrTmAsNs(&asdu));
+#else
+ printf("SV_SUBSCRIBER: RefrTm[ns]: %llu\n", SVSubscriber_ASDU_getRefrTmAsNs(&asdu));
#endif
if (SVSubscriber_ASDU_hasSmpMod(&asdu))
printf("SV_SUBSCRIBER: SmpMod: %d\n", SVSubscriber_ASDU_getSmpMod(&asdu));
diff --git a/tools/model_generator/genconfig.jar b/tools/model_generator/genconfig.jar
index 5ad24fcb..8b2670b0 100644
Binary files a/tools/model_generator/genconfig.jar and b/tools/model_generator/genconfig.jar differ
diff --git a/tools/model_generator/src/com/libiec61850/scl/DataObjectDefinition.java b/tools/model_generator/src/com/libiec61850/scl/DataObjectDefinition.java
index 29c01204..56c9477c 100644
--- a/tools/model_generator/src/com/libiec61850/scl/DataObjectDefinition.java
+++ b/tools/model_generator/src/com/libiec61850/scl/DataObjectDefinition.java
@@ -28,11 +28,18 @@ import org.w3c.dom.Node;
public class DataObjectDefinition {
private String name;
private String type;
+ private boolean trans = false; /* transient attribute value */
private int count = 0;
public DataObjectDefinition(Node dataObjectNode) throws SclParserException {
this.name = ParserUtils.parseAttribute(dataObjectNode, "name");
this.type = ParserUtils.parseAttribute(dataObjectNode, "type");
+
+
+ Boolean isTransient = ParserUtils.parseBooleanAttribute(dataObjectNode, "transient");
+
+ if (isTransient != null)
+ this.trans = isTransient;
if ((this.type == null) || (this.name == null))
throw new SclParserException(dataObjectNode, "DO misses required attribute.");
@@ -53,5 +60,9 @@ public class DataObjectDefinition {
public int getCount() {
return count;
}
+
+ public boolean isTransient() {
+ return trans;
+ }
}
diff --git a/tools/model_generator/src/com/libiec61850/scl/model/DataObject.java b/tools/model_generator/src/com/libiec61850/scl/model/DataObject.java
index c7fae5e0..d61ef972 100644
--- a/tools/model_generator/src/com/libiec61850/scl/model/DataObject.java
+++ b/tools/model_generator/src/com/libiec61850/scl/model/DataObject.java
@@ -42,11 +42,13 @@ public class DataObject implements DataModelNode {
private List subDataObjects = null;
private SclType sclType;
private DataModelNode parent;
+ private boolean trans = false;
public DataObject(DataObjectDefinition doDefinition, TypeDeclarations typeDeclarations, DataModelNode parent) throws SclParserException {
this.name = doDefinition.getName();
this.count = doDefinition.getCount();
this.parent = parent;
+ this.trans = doDefinition.isTransient();
this.dataAttributes = new LinkedList();
this.subDataObjects = new LinkedList();
@@ -109,6 +111,10 @@ public class DataObject implements DataModelNode {
public int getCount() {
return count;
}
+
+ public boolean isTransient() {
+ return trans;
+ }
@Override
public DataModelNode getChildByName(String childName) {
diff --git a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java
index 44d68303..d5ec7ffc 100644
--- a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java
+++ b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java
@@ -132,7 +132,7 @@ public class DynamicModelGenerator {
for (DataObject dataObject : logicalNode.getDataObjects()) {
output.print("DO(" + dataObject.getName() + " " + dataObject.getCount() + "){\n");
- exportDataObject(output, dataObject);
+ exportDataObject(output, dataObject, false);
output.println("}");
}
@@ -369,27 +369,37 @@ public class DynamicModelGenerator {
output.println("}");
}
- private void exportDataObject(PrintStream output, DataObject dataObject) {
+ private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) {
+
+ if (dataObject.isTransient())
+ isTransient = true;
+
for (DataObject subDataObject : dataObject.getSubDataObjects()) {
output.print("DO(" + subDataObject.getName() + " " + subDataObject.getCount() + "){\n");
- exportDataObject(output, subDataObject);
+ exportDataObject(output, subDataObject, isTransient);
output.println("}");
}
for (DataAttribute dataAttribute : dataObject.getDataAttributes()) {
- exportDataAttribute(output, dataAttribute);
+ exportDataAttribute(output, dataAttribute, isTransient);
}
}
- private void exportDataAttribute(PrintStream output, DataAttribute dataAttribute) {
+ private void exportDataAttribute(PrintStream output, DataAttribute dataAttribute, boolean isTransient) {
output.print("DA(" + dataAttribute.getName() + " ");
output.print(dataAttribute.getCount() + " ");
output.print(dataAttribute.getType().getIntValue() + " ");
output.print(dataAttribute.getFc().getIntValue() + " ");
- output.print(dataAttribute.getTriggerOptions().getIntValue() + " ");
+
+ int trgOpsVal = dataAttribute.getTriggerOptions().getIntValue();
+
+ if (isTransient)
+ trgOpsVal += 128;
+
+ output.print(trgOpsVal + " ");
Long sAddr = null;
@@ -471,7 +481,7 @@ public class DynamicModelGenerator {
output.println("{");
for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) {
- exportDataAttribute(output, subDataAttribute);
+ exportDataAttribute(output, subDataAttribute, isTransient);
}
output.println("}");
diff --git a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java
index d47fff4a..8f49462c 100644
--- a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java
+++ b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java
@@ -594,7 +594,7 @@ public class StaticModelGenerator {
cOut.println("};\n");
- printDataObjectDefinitions(lnName, logicalNode.getDataObjects(), null);
+ printDataObjectDefinitions(lnName, logicalNode.getDataObjects(), null, false);
printReportControlBlocks(lnName, logicalNode);
@@ -610,7 +610,7 @@ public class StaticModelGenerator {
}
}
- private void printDataObjectDefinitions(String lnName, List dataObjects, String dataAttributeSibling) {
+ private void printDataObjectDefinitions(String lnName, List dataObjects, String dataAttributeSibling, boolean isTransient) {
for (int i = 0; i < dataObjects.size(); i++) {
DataObject dataObject = dataObjects.get(i);
@@ -650,17 +650,26 @@ public class StaticModelGenerator {
cOut.println(" " + dataObject.getCount());
cOut.println("};\n");
+
+ boolean isDoTransient = false;
+
+ if (isTransient)
+ isDoTransient = true;
+ else {
+ if (dataObject.isTransient())
+ isDoTransient = true;
+ }
if (dataObject.getSubDataObjects() != null)
- printDataObjectDefinitions(doName, dataObject.getSubDataObjects(), firstDataAttributeName);
+ printDataObjectDefinitions(doName, dataObject.getSubDataObjects(), firstDataAttributeName, isDoTransient);
if (dataObject.getDataAttributes() != null)
- printDataAttributeDefinitions(doName, dataObject.getDataAttributes());
+ printDataAttributeDefinitions(doName, dataObject.getDataAttributes(), isDoTransient);
}
}
- private void printDataAttributeDefinitions(String doName, List dataAttributes) {
+ private void printDataAttributeDefinitions(String doName, List dataAttributes, boolean isTransient) {
for (int i = 0; i < dataAttributes.size(); i++) {
DataAttribute dataAttribute = dataAttributes.get(i);
@@ -715,6 +724,9 @@ public class StaticModelGenerator {
if (trgOps.isQchg())
cOut.print(" + TRG_OPT_QUALITY_CHANGED");
+
+ if (isTransient)
+ cOut.print(" + TRG_OPT_TRANSIENT");
cOut.println(",");
@@ -737,7 +749,7 @@ public class StaticModelGenerator {
cOut.println("};\n");
if (dataAttribute.getSubDataAttributes() != null)
- printDataAttributeDefinitions(daName, dataAttribute.getSubDataAttributes());
+ printDataAttributeDefinitions(daName, dataAttribute.getSubDataAttributes(), isTransient);
DataModelValue value = dataAttribute.getValue();