- .NET API: Added IDisposable interface to IedServer and IedModel classes

- .NET API: Added method IedModel.SetIedName
pull/331/head
Michael Zillgith 5 years ago
parent 35713550fb
commit cdd00da057

@ -54,7 +54,7 @@ namespace IEC61850
/// <summary> /// <summary>
/// Representation of the IED server data model /// Representation of the IED server data model
/// </summary> /// </summary>
public class IedModel public class IedModel : IDisposable
{ {
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedModel_create(string name); static extern IntPtr IedModel_create(string name);
@ -71,7 +71,10 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ModelNode_getType(IntPtr self); 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) internal IedModel(IntPtr self)
{ {
@ -87,24 +90,46 @@ namespace IEC61850
self = IedModel_create(name); self = IedModel_create(name);
} }
// causes undefined behavior ~IedModel()
//~IedModel() {
//{ Dispose();
// if (self != IntPtr.Zero) }
// {
// IedModel_destroy(self);
// }
//}
public void Destroy() /// <summary>
/// Releases all resource used by the <see cref="IEC61850.Server.IedModel"/> object.
/// </summary>
public void Destroy()
{ {
IedModel_destroy(self); Dispose();
self = IntPtr.Zero;
} }
public static IedModel CreateFromFile(string filePath) /// <summary>
/// Releases all resource used by the <see cref="IEC61850.Server.IedModel"/> object.
/// </summary>
/// <remarks>Call <see cref="Dispose"/> when you are done using the <see cref="IEC61850.Server.IedModel"/>. The
/// <see cref="Dispose"/> method leaves the <see cref="IEC61850.Server.IedModel"/> in an unusable state. After
/// calling <see cref="Dispose"/>, you must release all references to the <see cref="IEC61850.Server.IedModel"/> so
/// the garbage collector can reclaim the memory that the <see cref="IEC61850.Server.IedModel"/> was occupying.</remarks>
public void Dispose()
{
lock (this)
{
if (self != IntPtr.Zero)
{
IedModel_destroy(self);
self = IntPtr.Zero;
}
}
}
/// <summary>
/// Creates a new <see cref="IEC61850.Server.IedModel"/> instance with the data model of <see langword="async"/> config file.
/// </summary>
/// <returns>new <see cref="IEC61850.Server.IedModel"/> instance</returns>
/// <param name="filename">fila name of the configuration (.cfg) file</param>
public static IedModel CreateFromFile(string filename)
{ {
return ConfigFileParser.CreateModelFromConfigFile(filePath); return ConfigFileParser.CreateModelFromConfigFile(filename);
} }
private ModelNode getModelNodeFromNodeRef(IntPtr nodeRef) private ModelNode getModelNodeFromNodeRef(IntPtr nodeRef)
@ -129,6 +154,20 @@ namespace IEC61850
} }
} }
/// <summary>
/// Change the IED name of the data model
/// </summary>
/// <param name="iedName">the new IED name</param>
public void SetIedName(string iedName)
{
IedModel_setIedNameForDynamicModel(self, iedName);
}
/// <summary>
/// Gets the model node by full object reference.
/// </summary>
/// <returns>The model node</returns>
/// <param name="objectReference">Full object reference including the IED name</param>
public ModelNode GetModelNodeByObjectReference(string objectReference) public ModelNode GetModelNodeByObjectReference(string objectReference)
{ {
IntPtr nodeRef = IedModel_getModelNodeByObjectReference(self, objectReference); IntPtr nodeRef = IedModel_getModelNodeByObjectReference(self, objectReference);
@ -139,6 +178,11 @@ namespace IEC61850
return getModelNodeFromNodeRef (nodeRef); return getModelNodeFromNodeRef (nodeRef);
} }
/// <summary>
/// Gets the model node by short object reference (without IED name)
/// </summary>
/// <returns>The model node</returns>
/// <param name="objectReference">Object reference without IED name (e.g. LD0/GGIO1.Ind1.stVal)</param>
public ModelNode GetModelNodeByShortObjectReference(string objectReference) public ModelNode GetModelNodeByShortObjectReference(string objectReference)
{ {
IntPtr nodeRef = IedModel_getModelNodeByShortObjectReference(self, objectReference); IntPtr nodeRef = IedModel_getModelNodeByShortObjectReference(self, objectReference);
@ -148,9 +192,10 @@ namespace IEC61850
return getModelNodeFromNodeRef (nodeRef); return getModelNodeFromNodeRef (nodeRef);
} }
}
public class LogicalDevice : ModelNode }
public class LogicalDevice : ModelNode
{ {
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr LogicalDevice_create(string name, IntPtr parent); 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 /// This class acts as the entry point for the IEC 61850 client API. It represents a single
/// (MMS) connection to a server. /// (MMS) connection to a server.
/// </summary> /// </summary>
public class IedServer public class IedServer : IDisposable
{ {
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio); static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio);
@ -1387,8 +1432,13 @@ namespace IEC61850
internal Dictionary<IntPtr, ClientConnection> clientConnections = new Dictionary<IntPtr, ClientConnection> (); internal Dictionary<IntPtr, ClientConnection> clientConnections = new Dictionary<IntPtr, ClientConnection> ();
/* store IedModel instance to prevent garbage collector */
private IedModel iedModel = null;
public IedServer(IedModel iedModel, IedServerConfig config = null) public IedServer(IedModel iedModel, IedServerConfig config = null)
{ {
this.iedModel = iedModel;
IntPtr nativeConfig = IntPtr.Zero; IntPtr nativeConfig = IntPtr.Zero;
if (config != null) if (config != null)
@ -1399,7 +1449,9 @@ namespace IEC61850
public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
{ {
IntPtr nativeConfig = IntPtr.Zero; this.iedModel = iedModel;
IntPtr nativeConfig = IntPtr.Zero;
IntPtr nativeTLSConfig = IntPtr.Zero; IntPtr nativeTLSConfig = IntPtr.Zero;
if (config != null) if (config != null)
@ -1461,17 +1513,41 @@ namespace IEC61850
internalConnectionHandler = null; internalConnectionHandler = null;
} }
/// <summary> /// <summary>
/// Release all server resources. /// Release all server resources (same as <see cref="Dispose"/>)
/// </summary> /// </summary>
/// <description>This function releases all MMS server resources.</description> /// <description>This function releases all MMS server resources.</description>
public void Destroy() public void Destroy()
{ {
IedServer_destroy(self); Dispose();
self = IntPtr.Zero;
internalConnectionHandler = null;
} }
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.Server.IedServer"/> object.
/// </summary>
/// <remarks>Call <see cref="Dispose"/> when you are done using the <see cref="IEC61850.Server.IedServer"/>. The
/// <see cref="Dispose"/> method leaves the <see cref="IEC61850.Server.IedServer"/> in an unusable state. After
/// calling <see cref="Dispose"/>, you must release all references to the <see cref="IEC61850.Server.IedServer"/> so
/// the garbage collector can reclaim the memory that the <see cref="IEC61850.Server.IedServer"/> was occupying.</remarks>
public void Dispose()
{
lock (this)
{
if (self != IntPtr.Zero)
{
IedServer_destroy(self);
self = IntPtr.Zero;
internalConnectionHandler = null;
this.iedModel = null;
}
}
}
~IedServer()
{
Dispose();
}
/// <summary> /// <summary>
/// Set the identify for the MMS identify service /// Set the identify for the MMS identify service
/// </summary> /// </summary>

@ -5,33 +5,38 @@ using System.Threading;
namespace server1 namespace server1
{ {
class MainClass class MainClass
{ {
public static void Main (string[] args) public static void Main(string[] args)
{ {
bool running = true; bool running = true;
/* run until Ctrl-C is pressed */ /* run until Ctrl-C is pressed */
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
e.Cancel = true; {
running = false; e.Cancel = true;
}; running = false;
};
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("model.cfg"); IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("model.cfg");
if (iedModel == null) { if (iedModel == null)
Console.WriteLine ("No valid data model found!"); {
return; Console.WriteLine("No valid data model found!");
} return;
}
DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.SPCSO1"); iedModel.SetIedName("TestIED");
IedServerConfig config = new IedServerConfig (); DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO1");
config.ReportBufferSize = 100000;
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("Received binary control command:");
Console.WriteLine(" ctlNum: " + action.GetCtlNum()); Console.WriteLine(" ctlNum: " + action.GetCtlNum());
@ -40,58 +45,60 @@ namespace server1
return CheckHandlerResult.ACCEPTED; return CheckHandlerResult.ACCEPTED;
}, null); }, 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(); bool val = ctlVal.GetBoolean();
if (val) if (val)
Console.WriteLine("execute binary control command: on"); Console.WriteLine("execute binary control command: on");
else else
Console.WriteLine("execute binary control command: off"); Console.WriteLine("execute binary control command: off");
return ControlHandlerResult.OK; 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) { iedServer.SetSelectStateChangedHandler(spcso2, delegate (ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason)
DataObject cObj = action.GetControlObject(); {
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()) if (iedServer.IsRunning())
{ {
Console.WriteLine("Server started"); 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 ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild("mag.f");
DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild("t"); DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild("t");
float floatVal = 1.0f; float floatVal = 1.0f;
while (running) while (running)
{ {
floatVal += 1f; floatVal += 1f;
iedServer.UpdateTimestampAttributeValue(ggio1AnIn1T, new Timestamp(DateTime.Now)); iedServer.UpdateTimestampAttributeValue(ggio1AnIn1T, new Timestamp(DateTime.Now));
iedServer.UpdateFloatAttributeValue(ggio1AnIn1magF, floatVal); iedServer.UpdateFloatAttributeValue(ggio1AnIn1magF, floatVal);
Thread.Sleep(100); Thread.Sleep(100);
} }
iedServer.Stop(); iedServer.Stop();
Console.WriteLine("Server stopped"); Console.WriteLine("Server stopped");
} }
else else
{ {
Console.WriteLine("Failed to start server"); Console.WriteLine("Failed to start server");
} }
iedServer.Destroy (); iedServer.Destroy();
} }
} }
} }

@ -58,7 +58,7 @@ IedModel_create(const char* name/*, MemoryAllocator allocator*/);
/** /**
* \brief Set the name of the IED (use only for dynamic model!) * \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: This function has to be called before IedServer_create !
* NOTE: For dynamic model (and configuration file date model) this function has to be * NOTE: For dynamic model (and configuration file date model) this function has to be
* used instead of IedModel_setIedName. * used instead of IedModel_setIedName.

Loading…
Cancel
Save