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();