diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 8a0914a2..e1b0c813 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -54,7 +54,7 @@ namespace IEC61850 /// /// Representation of the IED server data model /// - public class IedModel + public class IedModel : IDisposable { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedModel_create(string name); @@ -71,7 +71,10 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int ModelNode_getType(IntPtr self); - internal IntPtr self = IntPtr.Zero; + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedModel_setIedNameForDynamicModel(IntPtr self, string iedName); + + internal IntPtr self = IntPtr.Zero; internal IedModel(IntPtr self) { @@ -87,24 +90,46 @@ namespace IEC61850 self = IedModel_create(name); } - // causes undefined behavior - //~IedModel() - //{ - // if (self != IntPtr.Zero) - // { - // IedModel_destroy(self); - // } - //} + ~IedModel() + { + Dispose(); + } - public void Destroy() + /// + /// Releases all resource used by the object. + /// + public void Destroy() { - IedModel_destroy(self); - self = IntPtr.Zero; + Dispose(); } - public static IedModel CreateFromFile(string filePath) + /// + /// 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(filePath); + return ConfigFileParser.CreateModelFromConfigFile(filename); } private ModelNode getModelNodeFromNodeRef(IntPtr nodeRef) @@ -129,6 +154,20 @@ namespace IEC61850 } } + /// + /// 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); @@ -139,6 +178,11 @@ namespace IEC61850 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); @@ -148,9 +192,10 @@ namespace IEC61850 return getModelNodeFromNodeRef (nodeRef); } - } - public class LogicalDevice : ModelNode + } + + public class LogicalDevice : ModelNode { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr LogicalDevice_create(string name, IntPtr parent); @@ -1123,7 +1168,7 @@ namespace IEC61850 /// 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 + public class IedServer : IDisposable { [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio); @@ -1387,8 +1432,13 @@ namespace IEC61850 internal Dictionary clientConnections = new Dictionary (); + /* store IedModel instance to prevent garbage collector */ + private IedModel iedModel = null; + public IedServer(IedModel iedModel, IedServerConfig config = null) { + this.iedModel = iedModel; + IntPtr nativeConfig = IntPtr.Zero; if (config != null) @@ -1399,7 +1449,9 @@ namespace IEC61850 public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) { - IntPtr nativeConfig = IntPtr.Zero; + this.iedModel = iedModel; + + IntPtr nativeConfig = IntPtr.Zero; IntPtr nativeTLSConfig = IntPtr.Zero; if (config != null) @@ -1461,17 +1513,41 @@ namespace IEC61850 internalConnectionHandler = null; } - /// - /// Release all server resources. - /// - /// This function releases all MMS server resources. - public void Destroy() + /// + /// Release all server resources (same as ) + /// + /// This function releases all MMS server resources. + public void Destroy() { - IedServer_destroy(self); - self = IntPtr.Zero; - internalConnectionHandler = null; + 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) + { + IedServer_destroy(self); + self = IntPtr.Zero; + internalConnectionHandler = null; + this.iedModel = null; + } + } + } + + ~IedServer() + { + Dispose(); + } + /// /// Set the identify for the MMS identify service /// 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/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index 13599698..c7da23c0 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.