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.