|
|
|
@ -1,7 +1,7 @@
|
|
|
|
|
/*
|
|
|
|
|
* IEC61850ServerAPI.cs
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2016-2024 Michael Zillgith
|
|
|
|
|
* Copyright 2016-2025 Michael Zillgith
|
|
|
|
|
*
|
|
|
|
|
* This file is part of libIEC61850.
|
|
|
|
|
*
|
|
|
|
@ -44,7 +44,8 @@ namespace IEC61850
|
|
|
|
|
public static IedModel CreateModelFromConfigFile(string filePath)
|
|
|
|
|
{
|
|
|
|
|
IntPtr retVal = ConfigFileParser_createModelFromConfigFileEx(filePath);
|
|
|
|
|
if (retVal == IntPtr.Zero) {
|
|
|
|
|
if (retVal == IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -331,7 +332,7 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
public LogicalDevice(IntPtr self, IedModel iedModel) : base(self)
|
|
|
|
|
{
|
|
|
|
|
this.IedModel = iedModel;
|
|
|
|
|
IedModel = iedModel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -341,7 +342,7 @@ namespace IEC61850
|
|
|
|
|
/// <param name="parent">Model containing this logical device</param>
|
|
|
|
|
public LogicalDevice(string inst, IedModel parent)
|
|
|
|
|
{
|
|
|
|
|
this.IedModel = parent;
|
|
|
|
|
IedModel = parent;
|
|
|
|
|
|
|
|
|
|
self = LogicalDevice_create(inst, parent.self);
|
|
|
|
|
}
|
|
|
|
@ -354,7 +355,7 @@ namespace IEC61850
|
|
|
|
|
/// <param name="ldName">LD name (for functional naming). When set, the exernally visible domain name for this LD</param>
|
|
|
|
|
public LogicalDevice(string inst, IedModel parent, string ldName)
|
|
|
|
|
{
|
|
|
|
|
this.IedModel = parent;
|
|
|
|
|
IedModel = parent;
|
|
|
|
|
|
|
|
|
|
self = LogicalDevice_createEx(inst, parent.self, ldName);
|
|
|
|
|
}
|
|
|
|
@ -365,7 +366,7 @@ namespace IEC61850
|
|
|
|
|
/// <returns>the SGCB instance or NULL if no SGCB is available</returns>
|
|
|
|
|
public SettingGroupControlBlock GetSettingGroupControlBlock()
|
|
|
|
|
{
|
|
|
|
|
IntPtr sgcb = LogicalDevice_getSettingGroupControlBlock(this.self);
|
|
|
|
|
IntPtr sgcb = LogicalDevice_getSettingGroupControlBlock(self);
|
|
|
|
|
|
|
|
|
|
if (sgcb == IntPtr.Zero)
|
|
|
|
|
return null;
|
|
|
|
@ -407,12 +408,14 @@ namespace IEC61850
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum AccessPolicy {
|
|
|
|
|
public enum AccessPolicy
|
|
|
|
|
{
|
|
|
|
|
ACCESS_POLICY_ALLOW = 0,
|
|
|
|
|
ACCESS_POLICY_DENY = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum DataAttributeType {
|
|
|
|
|
public enum DataAttributeType
|
|
|
|
|
{
|
|
|
|
|
BOOLEAN = 0,
|
|
|
|
|
INT8 = 1,
|
|
|
|
|
INT16 = 2,
|
|
|
|
@ -965,7 +968,7 @@ namespace IEC61850
|
|
|
|
|
public DataAttribute GetChildWithFc(string objRef, FunctionalConstraint fc)
|
|
|
|
|
{
|
|
|
|
|
DataAttribute dataAttribute = null;
|
|
|
|
|
IntPtr da = ModelNode_getChildWithFc(this.self, objRef, (int)fc);
|
|
|
|
|
IntPtr da = ModelNode_getChildWithFc(self, objRef, (int)fc);
|
|
|
|
|
|
|
|
|
|
if (da != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
@ -1152,7 +1155,7 @@ namespace IEC61850
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (this.parent != null)
|
|
|
|
|
if (parent != null)
|
|
|
|
|
return parent.GetIedModel();
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
@ -1324,14 +1327,16 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
IntPtr objRefPtr = ModelNode_getObjectReferenceEx(self, nativeMemory, withoutIedName);
|
|
|
|
|
|
|
|
|
|
if (objRefPtr != IntPtr.Zero) {
|
|
|
|
|
if (objRefPtr != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
string objRef = Marshal.PtrToStringAnsi(objRefPtr);
|
|
|
|
|
|
|
|
|
|
Marshal.FreeHGlobal(nativeMemory);
|
|
|
|
|
|
|
|
|
|
return objRef;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Marshal.FreeHGlobal(nativeMemory);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
@ -1749,6 +1754,9 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
public class ClientConnection : IDisposable
|
|
|
|
|
{
|
|
|
|
|
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
|
|
|
|
static extern IntPtr ClientConnection_getSecurityToken(IntPtr self);
|
|
|
|
|
|
|
|
|
|
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
|
|
|
|
static extern IntPtr ClientConnection_getPeerAddress(IntPtr self);
|
|
|
|
|
|
|
|
|
@ -1772,6 +1780,31 @@ namespace IEC61850
|
|
|
|
|
this.self = ClientConnection_claimOwnership(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the security token associated with this connection
|
|
|
|
|
/// The security token is an opaque handle that is associated with the connection.It is provided by the
|
|
|
|
|
/// authenticator (if one is present). If no security token is used then this function returns NULL
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>the security token or NULL</returns>
|
|
|
|
|
public object GetSecurityToken()
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
if (self != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
IntPtr token = ClientConnection_getSecurityToken(self);
|
|
|
|
|
|
|
|
|
|
if (token != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
GCHandle handle = GCHandle.FromIntPtr(token);
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string GetPeerAddress()
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
@ -1884,7 +1917,7 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
if (lnModelNode != null && lnModelNode is LogicalNode)
|
|
|
|
|
{
|
|
|
|
|
this.ln = lnModelNode as LogicalNode;
|
|
|
|
|
ln = lnModelNode as LogicalNode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2101,7 +2134,8 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
IntPtr conPtr = ControlAction_getClientConnection(self);
|
|
|
|
|
|
|
|
|
|
if (conPtr != IntPtr.Zero) {
|
|
|
|
|
if (conPtr != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
iedServer.clientConnections.TryGetValue(conPtr, out con);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2230,7 +2264,8 @@ namespace IEC61850
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Return type of ControlHandler and ControlWaitForExecutionHandler
|
|
|
|
|
/// </summary>
|
|
|
|
|
public enum ControlHandlerResult {
|
|
|
|
|
public enum ControlHandlerResult
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// check or operation failed
|
|
|
|
|
/// </summary>
|
|
|
|
@ -2249,7 +2284,8 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
public delegate ControlHandlerResult ControlHandler(ControlAction action, object parameter, MmsValue ctlVal, bool test);
|
|
|
|
|
|
|
|
|
|
public enum CheckHandlerResult {
|
|
|
|
|
public enum CheckHandlerResult
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// check passed
|
|
|
|
|
/// </summary>
|
|
|
|
@ -2611,6 +2647,29 @@ namespace IEC61850
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
|
|
|
private delegate void IedServer_SVCBEventHandler(IntPtr svcb, int eventType, IntPtr parameter);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// set the authenticator for this server
|
|
|
|
|
/// This function sets a user specified authenticator that is used to identify and authenticate clients that
|
|
|
|
|
/// wants to connect.The authenticator is called on each connection attempt.Depending on the return value
|
|
|
|
|
/// connections are accepted.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="self">the instance of IedServer to operate on.</param>
|
|
|
|
|
/// <param name="authenticator">the user provided authenticator callback</param>
|
|
|
|
|
/// <param name="authenticatorParameter">user provided parameter that is passed to the authenticator</param>
|
|
|
|
|
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
|
|
|
|
static extern void IedServer_setAuthenticator(IntPtr self, IedServer_AcseAuthenticator authenticator, IntPtr authenticatorParameter);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Callback function to authenticate a client
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="parameter">user provided parameter - set when user registers the authenticator</param>
|
|
|
|
|
/// <param name="authParameter">the authentication parameters provided by the client</param>
|
|
|
|
|
/// <param name="securityToken">pointer where to store an application specific security token - can be ignored if not used.</param>
|
|
|
|
|
/// <param name="appReference">ISO application reference (ap-title + ae-qualifier)</param>
|
|
|
|
|
/// <returns>true if client connection is accepted, false otherwise</returns>
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
|
|
|
private delegate bool IedServer_AcseAuthenticator(IntPtr parameter, IntPtr authParameter, IntPtr securityToken, IntPtr appReference);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// callback handler to control client read access to data attributes
|
|
|
|
|
/// User provided callback function to control MMS client read access to IEC 61850
|
|
|
|
@ -2795,7 +2854,8 @@ namespace IEC61850
|
|
|
|
|
// hold references to managed LogStorage instances to avoid problems with GC
|
|
|
|
|
private Dictionary<string, LogStorage> logStorages = new Dictionary<string, LogStorage>();
|
|
|
|
|
|
|
|
|
|
internal class ControlHandlerInfo {
|
|
|
|
|
internal class ControlHandlerInfo
|
|
|
|
|
{
|
|
|
|
|
public DataObject controlObject = null;
|
|
|
|
|
public GCHandle handle;
|
|
|
|
|
|
|
|
|
@ -2814,12 +2874,12 @@ namespace IEC61850
|
|
|
|
|
public ControlHandlerInfo(DataObject controlObject)
|
|
|
|
|
{
|
|
|
|
|
this.controlObject = controlObject;
|
|
|
|
|
this.handle = GCHandle.Alloc(this);
|
|
|
|
|
handle = GCHandle.Alloc(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ControlHandlerInfo()
|
|
|
|
|
{
|
|
|
|
|
this.handle.Free();
|
|
|
|
|
handle.Free();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2850,7 +2910,8 @@ namespace IEC61850
|
|
|
|
|
ControlAction controlAction = new ControlAction(action, info, this);
|
|
|
|
|
|
|
|
|
|
return (int)info.checkHandler(controlAction, info.checkHandlerParameter, new MmsValue(ctlVal), test, interlockCheck);
|
|
|
|
|
} else
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return (int)CheckHandlerResult.OBJECT_UNDEFINED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2884,7 +2945,8 @@ namespace IEC61850
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private struct WriteAccessHandlerInfo {
|
|
|
|
|
private struct WriteAccessHandlerInfo
|
|
|
|
|
{
|
|
|
|
|
public WriteAccessHandler handler;
|
|
|
|
|
public InternalWriteAccessHandler internalHandler;
|
|
|
|
|
public object parameter;
|
|
|
|
@ -2938,7 +3000,7 @@ namespace IEC61850
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = null;
|
|
|
|
|
|
|
|
|
|
this.clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
|
|
|
|
|
ModelNode ldModelNode = iedModel.GetModelNodeFromNodeRef(ld);
|
|
|
|
|
ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(ln);
|
|
|
|
@ -3000,7 +3062,7 @@ namespace IEC61850
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = null;
|
|
|
|
|
|
|
|
|
|
this.clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
|
|
|
|
|
return dataSetAccessHandler(dataSetAccessHandlerParameter, con, (DataSetOperation)operation, datasetRef);
|
|
|
|
|
}
|
|
|
|
@ -3040,7 +3102,7 @@ namespace IEC61850
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = null;
|
|
|
|
|
|
|
|
|
|
this.clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
|
|
|
|
|
return activeSettingGroupChangedHandler(activeSettingGroupChangedHandlerParameter, new SettingGroupControlBlock(sgcb), newActSg, con);
|
|
|
|
|
}
|
|
|
|
@ -3075,7 +3137,7 @@ namespace IEC61850
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = null;
|
|
|
|
|
|
|
|
|
|
this.clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
|
|
|
|
|
return editSettingGroupChangedHandler(editSettingGroupChangedHandlerParameter, new SettingGroupControlBlock(sgcb), newEditSg, con);
|
|
|
|
|
}
|
|
|
|
@ -3127,12 +3189,12 @@ namespace IEC61850
|
|
|
|
|
public SVCHandlerInfo(SVControlBlock sampledValuesControlBlock)
|
|
|
|
|
{
|
|
|
|
|
this.sampledValuesControlBlock = sampledValuesControlBlock;
|
|
|
|
|
this.handle = GCHandle.Alloc(this);
|
|
|
|
|
handle = GCHandle.Alloc(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~SVCHandlerInfo()
|
|
|
|
|
{
|
|
|
|
|
this.handle.Free();
|
|
|
|
|
handle.Free();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3166,7 +3228,6 @@ namespace IEC61850
|
|
|
|
|
IedServer_setSVCBHandler(self, sampledValuesControlBlock.Self, internalSVCBEventHandler, GCHandle.ToIntPtr(info.handle));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void InternalSVCBEventHandlerImplementation(IntPtr svcb, int eventType, IntPtr parameter)
|
|
|
|
|
{
|
|
|
|
|
GCHandle handle = GCHandle.FromIntPtr(parameter);
|
|
|
|
@ -3177,6 +3238,38 @@ namespace IEC61850
|
|
|
|
|
info.sVCBEventHandler(info.sampledValuesControlBlock, (SMVEvent)eventType, info.svcHandlerParameter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//TODO -> Add appReference
|
|
|
|
|
public delegate bool AcseAuthenticatorHandler(object parameter, AcseAuthenticationParameter authParameter, object securityToken, IsoApplicationReference isoApplicationReference);
|
|
|
|
|
|
|
|
|
|
private AcseAuthenticatorHandler acseAuthenticatorHandler = null;
|
|
|
|
|
private object acseAuthenticatorHandlerParameter = null;
|
|
|
|
|
private IedServer_AcseAuthenticator iedServer_AcseAuthenticator = null;
|
|
|
|
|
|
|
|
|
|
public void SetAuthenticator(AcseAuthenticatorHandler acseAuthenticatorHandler, object acseAuthenticatorHandlerParameter)
|
|
|
|
|
{
|
|
|
|
|
this.acseAuthenticatorHandler = acseAuthenticatorHandler;
|
|
|
|
|
this.acseAuthenticatorHandlerParameter = acseAuthenticatorHandlerParameter;
|
|
|
|
|
|
|
|
|
|
if (iedServer_AcseAuthenticator == null)
|
|
|
|
|
{
|
|
|
|
|
iedServer_AcseAuthenticator = new IedServer_AcseAuthenticator(InternalAcseAuthenticator);
|
|
|
|
|
IedServer_setAuthenticator(self, iedServer_AcseAuthenticator, IntPtr.Zero);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool InternalAcseAuthenticator(IntPtr parameter, IntPtr authParameter, IntPtr securityToken, IntPtr appReference)
|
|
|
|
|
{
|
|
|
|
|
if (acseAuthenticatorHandler != null && authParameter != IntPtr.Zero && appReference != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
AcseAuthenticationParameter acseAuthenticationParameter = new AcseAuthenticationParameter(authParameter);
|
|
|
|
|
IsoApplicationReference isoApplicationReference = new IsoApplicationReference(appReference);
|
|
|
|
|
GCHandle token = GCHandle.FromIntPtr(securityToken);
|
|
|
|
|
return acseAuthenticatorHandler(acseAuthenticatorHandlerParameter, acseAuthenticationParameter, token, isoApplicationReference);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public delegate MmsDataAccessError ReadAccessHandler(LogicalDevice ld, LogicalNode ln, DataObject dataObject, FunctionalConstraint fc, ClientConnection connection, object parameter);
|
|
|
|
|
|
|
|
|
|
private ReadAccessHandler readAccessHandler = null;
|
|
|
|
@ -3204,7 +3297,7 @@ namespace IEC61850
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = null;
|
|
|
|
|
|
|
|
|
|
this.clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
|
|
|
|
|
ModelNode ldModelNode = iedModel.GetModelNodeFromNodeRef(ld);
|
|
|
|
|
ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(ln);
|
|
|
|
@ -3254,7 +3347,7 @@ namespace IEC61850
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = null;
|
|
|
|
|
|
|
|
|
|
this.clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
|
|
|
|
|
ModelNode ldModelNode = null;
|
|
|
|
|
|
|
|
|
@ -3272,19 +3365,23 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
private void ConnectionIndicationHandlerImpl(IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter)
|
|
|
|
|
{
|
|
|
|
|
if (connected == false) {
|
|
|
|
|
if (connected == false)
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = null;
|
|
|
|
|
|
|
|
|
|
clientConnections.TryGetValue(clientConnection, out con);
|
|
|
|
|
|
|
|
|
|
if (con != null) {
|
|
|
|
|
if (con != null)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (connectionHandler != null)
|
|
|
|
|
connectionHandler(this, con, false, connectionHandlerParameter);
|
|
|
|
|
|
|
|
|
|
clientConnections.Remove(clientConnection);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ClientConnection con = new ClientConnection(clientConnection);
|
|
|
|
|
|
|
|
|
|
clientConnections.Add(clientConnection, con);
|
|
|
|
@ -3317,7 +3414,7 @@ namespace IEC61850
|
|
|
|
|
public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
|
|
|
|
|
{
|
|
|
|
|
this.iedModel = iedModel;
|
|
|
|
|
this.tlsConfiguration = tlsConfig;
|
|
|
|
|
tlsConfiguration = tlsConfig;
|
|
|
|
|
|
|
|
|
|
IntPtr nativeConfig = IntPtr.Zero;
|
|
|
|
|
IntPtr nativeTLSConfig = IntPtr.Zero;
|
|
|
|
@ -3406,8 +3503,8 @@ namespace IEC61850
|
|
|
|
|
IedServer_destroy(self);
|
|
|
|
|
self = IntPtr.Zero;
|
|
|
|
|
internalConnectionHandler = null;
|
|
|
|
|
this.iedModel = null;
|
|
|
|
|
this.tlsConfiguration = null;
|
|
|
|
|
iedModel = null;
|
|
|
|
|
tlsConfiguration = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -3452,7 +3549,8 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
controlHandlers.TryGetValue(controlObject, out info);
|
|
|
|
|
|
|
|
|
|
if (info == null) {
|
|
|
|
|
if (info == null)
|
|
|
|
|
{
|
|
|
|
|
info = new ControlHandlerInfo(controlObject);
|
|
|
|
|
controlHandlers.Add(controlObject, info);
|
|
|
|
|
}
|
|
|
|
@ -3854,7 +3952,7 @@ namespace IEC61850
|
|
|
|
|
|
|
|
|
|
if (connection != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
this.clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
clientConnections.TryGetValue(connection, out con);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReportControlBlock reportControlBlock = null;
|
|
|
|
|