Merged in v1.6_develop_513 (pull request #7)

V1.6 develop 513

Approved-by: Michael Zillgith
v1.6
Maxson Anjos 4 weeks ago committed by Michael Zillgith
commit 5fb73a00cc

@ -0,0 +1,205 @@
/*
* AcseAuthenticationParameter.cs
*
* Copyright 2025-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using System.Text;
// IEC 61850 API for the libiec61850 .NET wrapper library
namespace IEC61850
{
/// <summary>
/// Authentication mechanism used by AcseAuthenticator
/// </summary>
public enum AcseAuthenticationMechanism
{
/** Neither ACSE nor TLS authentication used */
ACSE_AUTH_NONE = 0,
/** Use ACSE password for client authentication */
ACSE_AUTH_PASSWORD = 1,
/** Use ACSE certificate for client authentication */
ACSE_AUTH_CERTIFICATE = 2,
/** Use TLS certificate for client authentication */
ACSE_AUTH_TLS = 3
}
public class AcseAuthenticationParameter
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr AcseAuthenticationParameter_create();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void AcseAuthenticationParameter_setAuthMechanism(IntPtr self, int mechanism);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void AcseAuthenticationParameter_setPassword(IntPtr self, string password);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int AcseAuthenticationParameter_getAuthMechanism(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr AcseAuthenticationParameter_getPassword(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int AcseAuthenticationParameter_getPasswordLength(IntPtr self);
private IntPtr self = IntPtr.Zero;
public AcseAuthenticationParameter()
{
self = AcseAuthenticationParameter_create();
}
public AcseAuthenticationParameter(IntPtr self)
{
this.self = self;
}
public void SetAuthMechanism(AcseAuthenticationMechanism acseAuthenticationMechanism)
{
AcseAuthenticationParameter_setAuthMechanism(self, (int)acseAuthenticationMechanism);
}
public void SetPassword(string password)
{
AcseAuthenticationParameter_setPassword(self, password);
}
public AcseAuthenticationMechanism GetAuthMechanism()
{
return (AcseAuthenticationMechanism)AcseAuthenticationParameter_getAuthMechanism(self);
}
public string GetPasswordString()
{
try
{
byte[] password = GetPasswordByteArray();
return Encoding.UTF8.GetString(password);
}
catch (Exception)
{
return null;
}
}
public byte[] GetPasswordByteArray()
{
IntPtr password = AcseAuthenticationParameter_getPassword(self);
if (password != IntPtr.Zero)
{
int lenght = GetPasswordLenght();
byte[] result = new byte[lenght];
Marshal.Copy(password, result, 0, lenght);
return result;
}
else
return null;
}
public int GetPasswordLenght()
{
return AcseAuthenticationParameter_getPasswordLength(self);
}
}
public class IsoApplicationReference
{
private IntPtr self = IntPtr.Zero;
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int IsoApplicationReference_getAeQualifier(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IsoApplicationReference_getApTitle(IntPtr self);
public IsoApplicationReference(IntPtr self)
{
this.self = self;
}
public int GetAeQualifier()
{
return IsoApplicationReference_getAeQualifier(self);
}
public ItuObjectIdentifier GetApTitle()
{
IntPtr identfier = IsoApplicationReference_getApTitle(self);
if (identfier == IntPtr.Zero)
return null;
return new ItuObjectIdentifier(identfier);
}
}
public class ItuObjectIdentifier
{
private IntPtr self = IntPtr.Zero;
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ItuObjectIdentifier_getArcCount(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ItuObjectIdentifier_getArc(IntPtr self);
public ItuObjectIdentifier(IntPtr self)
{
this.self = self;
}
public int GetArcCount()
{
return ItuObjectIdentifier_getArcCount(self);
}
public ushort[] GetArcs()
{
int count = ItuObjectIdentifier_getArcCount(self);
if (count <= 0 || count > 10) return Array.Empty<ushort>();
IntPtr arcPtr = ItuObjectIdentifier_getArc(self);
ushort[] arcs = new ushort[count];
short[] temp = new short[count];
Marshal.Copy(arcPtr, temp, 0, count);
for (int i = 0; i < count; i++)
arcs[i] = (ushort)temp[i];
return arcs;
}
}
}

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

@ -1,7 +1,7 @@
/*
* Control.cs
*
* Copyright 2014 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -21,15 +21,15 @@
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
// IEC 61850 common API parts (used by client and server API)
namespace Common {
namespace Common
{
/// <summary>
/// Control model
@ -51,7 +51,8 @@ namespace IEC61850
/// <summary>
/// Originator category
/// </summary>
public enum OrCat {
public enum OrCat
{
/** Not supported - should not be used */
NOT_SUPPORTED = 0,
/** Control operation issued from an operator using a client located at bay level */
@ -92,9 +93,9 @@ namespace IEC61850
internal LastApplError(LastApplErrorInternal lastApplError)
{
this.addCause = (ControlAddCause) lastApplError.addCause;
this.error = lastApplError.error;
this.ctlNum = lastApplError.ctlNum;
addCause = (ControlAddCause)lastApplError.addCause;
error = lastApplError.error;
ctlNum = lastApplError.ctlNum;
}
}
@ -207,9 +208,9 @@ namespace IEC61850
{
this.iedConnection = iedConnection;
this.self = ControlObjectClient_create(objectReference, connection);
self = ControlObjectClient_create(objectReference, connection);
if (this.self == System.IntPtr.Zero)
if (self == System.IntPtr.Zero)
throw new IedConnectionException("Control object not found", 0);
intCommandTerminationHandler = new InternalCommandTerminationHandler(MyCommandTerminationHandler);
@ -259,8 +260,10 @@ namespace IEC61850
/// Gets the error code of the last synchronous control action (operate, select, select-with-value, cancel)
/// </summary>
/// <value>error code.</value>
public IedClientError LastError {
get {
public IedClientError LastError
{
get
{
return (IedClientError)ControlObjectClient_getLastError(self);
}
}
@ -760,18 +763,21 @@ namespace IEC61850
/// </param>
public void SetCommandTerminationHandler(CommandTerminationHandler handler, Object parameter)
{
this.commandTerminationHandler = handler;
this.commandTerminationHandlerParameter = parameter;
commandTerminationHandler = handler;
commandTerminationHandlerParameter = parameter;
}
protected virtual void Dispose(bool disposing) {
if (this.self != System.IntPtr.Zero) {
protected virtual void Dispose(bool disposing)
{
if (self != System.IntPtr.Zero)
{
ControlObjectClient_destroy(self);
this.self = System.IntPtr.Zero;
self = System.IntPtr.Zero;
}
}
public void Dispose() {
public void Dispose()
{
Dispose(true);
}

@ -1,7 +1,7 @@
/*
* DataSet.cs
*
* Copyright 2014-2018 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -20,12 +20,10 @@
*
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
namespace Client

@ -1,7 +1,7 @@
/*
* GooseControlBlock.cs
*
* Copyright 2017 Michael Zillgith
* Copyright 2017-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -20,18 +20,17 @@
*
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using IEC61850.Common;
namespace IEC61850
{
namespace Client
{
public class GooseControlBlock : IDisposable {
public class GooseControlBlock : IDisposable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientGooseControlBlock_create(string dataAttributeReference);
@ -120,7 +119,7 @@ namespace IEC61850
public string GetObjectReference()
{
return this.objectReference;
return objectReference;
}
/// <summary>
@ -272,7 +271,8 @@ namespace IEC61850
public void Dispose()
{
if (isDisposed == false) {
if (isDisposed == false)
{
isDisposed = true;
ClientGooseControlBlock_destroy(self);
self = IntPtr.Zero;

@ -1,7 +1,7 @@
/*
* GooseSubscriber.cs
*
* Copyright 2017 Michael Zillgith
* Copyright 2017-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -21,10 +21,10 @@
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
@ -124,7 +124,8 @@ namespace IEC61850
public void Dispose()
{
if (isDisposed == false) {
if (isDisposed == false)
{
isDisposed = true;
GooseReceiver_destroy(self);
self = IntPtr.Zero;
@ -216,13 +217,16 @@ namespace IEC61850
private void internalGooseListener(IntPtr subscriber, IntPtr parameter)
{
try {
try
{
if (listener != null) {
if (listener != null)
{
listener(this, listenerParameter);
}
} catch (Exception e)
}
catch (Exception e)
{
// older versions of mono 2.10 (for linux?) cause this exception
Console.WriteLine(e.Message);
@ -247,9 +251,10 @@ namespace IEC61850
public void SetListener(GooseListener listener, object parameter)
{
this.listener = listener;
this.listenerParameter = parameter;
listenerParameter = parameter;
if (internalListener == null) {
if (internalListener == null)
{
internalListener = new InternalGooseListener(internalGooseListener);
GooseSubscriber_setListener(self, internalListener, IntPtr.Zero);
@ -342,7 +347,8 @@ namespace IEC61850
/// </remarks>
public void Dispose()
{
if (isDisposed == false) {
if (isDisposed == false)
{
isDisposed = true;
if (attachedToReceiver == false)

@ -1,7 +1,7 @@
/*
* IEC61850ClientAPI.cs
*
* Copyright 2014-2023 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -20,14 +20,11 @@
*
* See COPYING file for the complete license text.
*/
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
using IEC61850.Common;
using IEC61850.TLS;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
// IEC 61850 API for the libiec61850 .NET wrapper library
namespace IEC61850
@ -2936,17 +2933,17 @@ namespace IEC61850
public IedConnectionException(string message)
: base(message)
{
this.errorCode = 0;
errorCode = 0;
}
public int GetErrorCode()
{
return this.errorCode;
return errorCode;
}
public IedClientError GetIedClientError()
{
return (IedClientError)this.errorCode;
return (IedClientError)errorCode;
}
}

@ -1,7 +1,7 @@
/*
* IEC61850CommonAPI.cs
*
* Copyright 2014-2024 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -62,8 +62,8 @@ namespace IEC61850
ulong seconds = msTime / 1000;
ulong millies = msTime % 1000;
dateTime.AddSeconds ((double) seconds);
dateTime.AddMilliseconds((double) millies);
dateTime.AddSeconds(seconds);
dateTime.AddMilliseconds(millies);
return dateTime;
}
@ -93,7 +93,8 @@ namespace IEC61850
/// <summary>
/// MMS data access error for MmsValue type MMS_DATA_ACCESS_ERROR
/// </summary>
public enum MmsDataAccessError {
public enum MmsDataAccessError
{
NO_RESPONSE = -2, /* for server internal purposes only! */
SUCCESS = -1,
OBJECT_INVALIDATED = 0,
@ -112,7 +113,8 @@ namespace IEC61850
}
[Flags]
public enum TriggerOptions {
public enum TriggerOptions
{
NONE = 0,
/** send report when value of data changed */
DATA_CHANGED = 1,
@ -131,7 +133,8 @@ namespace IEC61850
/// <summary>
/// SmpMod values
/// </summary>
public enum SmpMod {
public enum SmpMod
{
SAMPLES_PER_PERIOD = 0,
SAMPLES_PER_SECOND = 1,
SECONDS_PER_SAMPLE = 2
@ -141,7 +144,8 @@ namespace IEC61850
/// Values for Sampled Values (SV) OptFlds
/// </summary>
[Flags]
public enum SVOptions {
public enum SVOptions
{
NONE = 0,
REFRESH_TIME = 1,
SAMPLE_SYNC = 2,
@ -152,7 +156,8 @@ namespace IEC61850
}
[Flags]
public enum ReportOptions {
public enum ReportOptions
{
NONE = 0,
SEQ_NUM = 1,
TIME_STAMP = 2,
@ -235,133 +240,145 @@ namespace IEC61850
public bool Overflow
{
get { return ((this.value & QUALITY_DETAIL_OVERFLOW) != 0);}
set {
get { return ((value & QUALITY_DETAIL_OVERFLOW) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_OVERFLOW;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OVERFLOW));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_OVERFLOW));
}
}
public bool OutOfRange
{
get { return ((this.value & QUALITY_DETAIL_OUT_OF_RANGE) != 0);}
set {
get { return ((value & QUALITY_DETAIL_OUT_OF_RANGE) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_OUT_OF_RANGE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OUT_OF_RANGE));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_OUT_OF_RANGE));
}
}
public bool BadReference
{
get { return ((this.value & QUALITY_DETAIL_BAD_REFERENCE) != 0);}
set {
get { return ((value & QUALITY_DETAIL_BAD_REFERENCE) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_BAD_REFERENCE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_BAD_REFERENCE));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_BAD_REFERENCE));
}
}
public bool Oscillatory
{
get { return ((this.value & QUALITY_DETAIL_OSCILLATORY) != 0);}
set {
get { return ((value & QUALITY_DETAIL_OSCILLATORY) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_OSCILLATORY;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OSCILLATORY));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_OSCILLATORY));
}
}
public bool Failure
{
get { return ((this.value & QUALITY_DETAIL_FAILURE) != 0);}
set {
get { return ((value & QUALITY_DETAIL_FAILURE) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_FAILURE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_FAILURE));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_FAILURE));
}
}
public bool OldData
{
get { return ((this.value & QUALITY_DETAIL_OLD_DATA) != 0);}
set {
get { return ((value & QUALITY_DETAIL_OLD_DATA) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_OLD_DATA;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_OLD_DATA));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_OLD_DATA));
}
}
public bool Inconsistent
{
get { return ((this.value & QUALITY_DETAIL_INCONSISTENT) != 0);}
set {
get { return ((value & QUALITY_DETAIL_INCONSISTENT) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_INCONSISTENT;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_INCONSISTENT));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_INCONSISTENT));
}
}
public bool Inaccurate
{
get { return ((this.value & QUALITY_DETAIL_INACCURATE) != 0);}
set {
get { return ((value & QUALITY_DETAIL_INACCURATE) != 0); }
set
{
if (value)
this.value |= QUALITY_DETAIL_INACCURATE;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DETAIL_INACCURATE));
this.value = (ushort)(this.value & (~QUALITY_DETAIL_INACCURATE));
}
}
public bool Substituted
{
get { return ((this.value & QUALITY_SOURCE_SUBSTITUTED) != 0);}
set {
get { return ((value & QUALITY_SOURCE_SUBSTITUTED) != 0); }
set
{
if (value)
this.value |= QUALITY_SOURCE_SUBSTITUTED;
else
this.value = (ushort) ((int) this.value & (~QUALITY_SOURCE_SUBSTITUTED));
this.value = (ushort)(this.value & (~QUALITY_SOURCE_SUBSTITUTED));
}
}
public bool Test
{
get { return ((this.value & QUALITY_TEST) != 0);}
set {
get { return ((value & QUALITY_TEST) != 0); }
set
{
if (value)
this.value |= QUALITY_TEST;
else
this.value = (ushort) ((int) this.value & (~QUALITY_TEST));
this.value = (ushort)(this.value & (~QUALITY_TEST));
}
}
public bool OperatorBlocked
{
get { return ((this.value & QUALITY_OPERATOR_BLOCKED) != 0);}
set {
get { return ((value & QUALITY_OPERATOR_BLOCKED) != 0); }
set
{
if (value)
this.value |= QUALITY_OPERATOR_BLOCKED;
else
this.value = (ushort) ((int) this.value & (~QUALITY_OPERATOR_BLOCKED));
this.value = (ushort)(this.value & (~QUALITY_OPERATOR_BLOCKED));
}
}
public bool Derived
{
get { return ((this.value & QUALITY_DERIVED) != 0); }
set {
get { return ((value & QUALITY_DERIVED) != 0); }
set
{
if (value)
this.value |= QUALITY_DERIVED;
else
this.value = (ushort) ((int) this.value & (~QUALITY_DERIVED));
this.value = (ushort)(this.value & (~QUALITY_DERIVED));
}
}
}
@ -440,8 +457,8 @@ namespace IEC61850
internal Timestamp(IntPtr timestampRef, bool selfAllocated)
{
this.self = timestampRef;
this.responsibleForDeletion = selfAllocated;
self = timestampRef;
responsibleForDeletion = selfAllocated;
}
public Timestamp(DateTime timestamp) : this()
@ -505,7 +522,8 @@ namespace IEC61850
SetTimeInMilliseconds(timeVal);
}
public bool LeapSecondKnown {
public bool LeapSecondKnown
{
get { return IsLeapSecondKnown(); }
set { SetLeapSecondKnow(value); }
}
@ -520,7 +538,8 @@ namespace IEC61850
Timestamp_setLeapSecondKnown(self, value);
}
public bool ClockFailure {
public bool ClockFailure
{
get { return HasClockFailure(); }
set { SetClockFailure(value); }
}
@ -535,33 +554,40 @@ namespace IEC61850
Timestamp_setClockFailure(self, value);
}
public bool ClockNotSynchronized {
public bool ClockNotSynchronized
{
get { return IsClockNotSynchronized(); }
set { SetClockNotSynchronized(value); }
}
public bool IsClockNotSynchronized() {
public bool IsClockNotSynchronized()
{
return Timestamp_isClockNotSynchronized(self);
}
public void SetClockNotSynchronized(bool value) {
public void SetClockNotSynchronized(bool value)
{
Timestamp_setClockNotSynchronized(self, value);
}
public int SubsecondPrecision {
public int SubsecondPrecision
{
get { return GetSubsecondPrecision(); }
set { SetSubsecondPrecision(value); }
}
public int GetSubsecondPrecision() {
public int GetSubsecondPrecision()
{
return Timestamp_getSubsecondPrecision(self);
}
public void SetSubsecondPrecision(int subsecondPrecision) {
public void SetSubsecondPrecision(int subsecondPrecision)
{
Timestamp_setSubsecondPrecision(self, subsecondPrecision);
}
public UInt32 TimeInSeconds {
public UInt32 TimeInSeconds
{
get { return GetTimeInSeconds(); }
set { SetTimeInSeconds(value); }
}
@ -576,7 +602,8 @@ namespace IEC61850
Timestamp_setTimeInSeconds(self, secondsSinceEpoch);
}
public UInt64 TimeInMilliseconds {
public UInt64 TimeInMilliseconds
{
get { return GetTimeInMilliseconds(); }
set { SetTimeInMilliseconds(value); }
}
@ -586,7 +613,8 @@ namespace IEC61850
return Timestamp_getTimeInMs(self);
}
public void SetTimeInMilliseconds(UInt64 millisSinceEpoch) {
public void SetTimeInMilliseconds(UInt64 millisSinceEpoch)
{
Timestamp_setTimeInMilliseconds(self, millisSinceEpoch);
}
@ -599,7 +627,7 @@ namespace IEC61850
{
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
DateTime retVal = epoch.AddMilliseconds ((double) GetTimeInMilliseconds ());
DateTime retVal = epoch.AddMilliseconds(GetTimeInMilliseconds());
return retVal;
}
@ -695,7 +723,8 @@ namespace IEC61850
/// Definition for LastAppError error type for control models
/// Used in LastApplError and CommandTermination messages.
/// </summary>
public enum ControlLastApplError {
public enum ControlLastApplError
{
NO_ERROR = 0,
UNKNOWN = 1,
TIMEOUT_TEST = 2,
@ -706,7 +735,8 @@ namespace IEC61850
/// AddCause - additional cause information for control model errors
/// Used in LastApplError and CommandTermination messages.
/// </summary>
public enum ControlAddCause {
public enum ControlAddCause
{
ADD_CAUSE_UNKNOWN = 0,
ADD_CAUSE_NOT_SUPPORTED = 1,
ADD_CAUSE_BLOCKED_BY_SWITCHING_HIERARCHY = 2,
@ -740,7 +770,8 @@ namespace IEC61850
/// <summary>
/// Object reference. Helper function to handle object reference strings.
/// </summary>
public static class ObjectReference {
public static class ObjectReference
{
/// <summary>
/// Get the name part of an object reference with appended FC

@ -22,9 +22,7 @@
*/
using IEC61850.Server;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
// IEC 61850 API for the libiec61850 .NET wrapper library
namespace IEC61850
@ -67,7 +65,7 @@ namespace IEC61850
public SVControlBlock(string name, IedModel parent, string svID, string dataSet, UInt32 confRev, uint smpMod,
UInt16 smpRate, uint optFlds, bool isUnicast)
{
this.self = SVControlBlock_create(name, parent.self, svID, dataSet, confRev, smpMod, smpRate, optFlds, isUnicast);
self = SVControlBlock_create(name, parent.self, svID, dataSet, confRev, smpMod, smpRate, optFlds, isUnicast);
this.parent = parent;
}

@ -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;

@ -1,7 +1,7 @@
/*
* IedServerConfig.cs
*
* Copyright 2018 Michael Zillgith
* Copyright 2018-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -21,9 +21,9 @@
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850.Server
{

@ -1,7 +1,7 @@
/*
* IsoConnectionParameters.cs
*
* Copyright 2014 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -29,7 +29,8 @@ namespace IEC61850
namespace Client
{
public enum AcseAuthenticationMechanism {
public enum AcseAuthenticationMechanism
{
/** don't use authentication */
ACSE_AUTH_NONE = 0,
/** use password authentication */
@ -242,7 +243,8 @@ namespace IEC61850
/// </param>
public void UsePasswordAuthentication(string password)
{
if (authParameter == IntPtr.Zero) {
if (authParameter == IntPtr.Zero)
{
authParameter = AcseAuthenticationParameter_create();
AcseAuthenticationParameter_setAuthMechanism(authParameter, (int)AcseAuthenticationMechanism.ACSE_AUTH_PASSWORD);
AcseAuthenticationParameter_setPassword(authParameter, password);

@ -1,7 +1,7 @@
/*
* MmsValue.cs
*
* Copyright 2014 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -21,11 +21,8 @@
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text;
namespace IEC61850
@ -224,18 +221,18 @@ namespace IEC61850
internal MmsValue(IntPtr value)
{
valueReference = value;
this.responsableForDeletion = false;
responsableForDeletion = false;
}
public int EncodeMmsData(byte[] buffer, int bufPos, bool encode)
{
return MmsValue_encodeMmsData(this.valueReference, buffer, bufPos, encode);
return MmsValue_encodeMmsData(valueReference, buffer, bufPos, encode);
}
public int DecodeMmsData(int bufPos, int bufferLength, IntPtr endBufPo)
{
return MmsValue_decodeMmsData(this.valueReference, bufPos, bufferLength, endBufPo);
return MmsValue_decodeMmsData(valueReference, bufPos, bufferLength, endBufPo);
}
internal MmsValue(IntPtr value, bool responsableForDeletion)
@ -285,8 +282,10 @@ namespace IEC61850
public void Dispose()
{
lock (this) {
if (valueReference != IntPtr.Zero) {
lock (this)
{
if (valueReference != IntPtr.Zero)
{
if (responsableForDeletion)
MmsValue_delete(valueReference);
@ -343,7 +342,7 @@ namespace IEC61850
valueReference = MmsValue_newOctetString(octetString.Length, octetString.Length);
this.setOctetString (octetString);
setOctetString(octetString);
}
/// <summary>
@ -409,8 +408,9 @@ namespace IEC61850
/// Sets the binary time.
/// </summary>
/// <param name="timestamp">Timestamp.</param>
public void SetBinaryTime(UInt64 timestamp) {
MmsValue_setBinaryTime (this.valueReference, timestamp);
public void SetBinaryTime(UInt64 timestamp)
{
MmsValue_setBinaryTime(valueReference, timestamp);
}
/// <summary>
@ -426,9 +426,11 @@ namespace IEC61850
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public ulong GetBinaryTimeAsUtcMs()
{
if (GetType () == MmsType.MMS_BINARY_TIME) {
if (GetType() == MmsType.MMS_BINARY_TIME)
{
return MmsValue_getBinaryTimeAsUtcMs(valueReference);
} else
}
else
throw new MmsValueException("Value is not a time type");
}
@ -489,12 +491,16 @@ namespace IEC61850
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public int Size()
{
if ((GetType () == MmsType.MMS_ARRAY) || (GetType () == MmsType.MMS_STRUCTURE)) {
if ((GetType() == MmsType.MMS_ARRAY) || (GetType() == MmsType.MMS_STRUCTURE))
{
return MmsValue_getArraySize(valueReference);
} else if (GetType () == MmsType.MMS_BIT_STRING) {
}
else if (GetType() == MmsType.MMS_BIT_STRING)
{
return MmsValue_getBitStringSize(valueReference);
}
else if (GetType () == MmsType.MMS_OCTET_STRING) {
else if (GetType() == MmsType.MMS_OCTET_STRING)
{
return MmsValue_getOctetStringSize(valueReference);
}
else
@ -509,7 +515,8 @@ namespace IEC61850
/// </returns>
public int MaxSize()
{
if (GetType () == MmsType.MMS_OCTET_STRING) {
if (GetType() == MmsType.MMS_OCTET_STRING)
{
return MmsValue_getOctetStringMaxSize(valueReference);
}
else
@ -527,9 +534,10 @@ namespace IEC61850
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public byte[] getOctetString()
{
if (GetType () == MmsType.MMS_OCTET_STRING) {
if (GetType() == MmsType.MMS_OCTET_STRING)
{
IntPtr buffer = MmsValue_getOctetStringBuffer(valueReference);
int bufferSize = this.Size();
int bufferSize = Size();
byte[] octetString = new byte[bufferSize];
@ -548,9 +556,10 @@ namespace IEC61850
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type or the byte array is too large.</exception>
public void setOctetString(byte[] octetString)
{
if (GetType () == MmsType.MMS_OCTET_STRING) {
if (GetType() == MmsType.MMS_OCTET_STRING)
{
if (this.MaxSize() < octetString.Length)
if (MaxSize() < octetString.Length)
throw new MmsValueException("octet string is to large");
MmsValue_setOctetString(valueReference, octetString, octetString.Length);
@ -594,17 +603,21 @@ namespace IEC61850
{
MmsType type = GetType();
if ((type == MmsType.MMS_ARRAY) || (type == MmsType.MMS_STRUCTURE)) {
if ((index >= 0) && (index < Size ())) {
if ((type == MmsType.MMS_ARRAY) || (type == MmsType.MMS_STRUCTURE))
{
if ((index >= 0) && (index < Size()))
{
IntPtr value = MmsValue_getElement(valueReference, index);
if (value == IntPtr.Zero)
return null;
else
return new MmsValue(value);
} else
}
else
throw new MmsValueException("Index out of bounds");
} else
}
else
throw new MmsValueException("Value is of wrong type");
}
@ -692,9 +705,11 @@ namespace IEC61850
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public void SetUtcTimeMs(ulong timeval)
{
if (GetType () == MmsType.MMS_UTC_TIME) {
if (GetType() == MmsType.MMS_UTC_TIME)
{
MmsValue_setUtcTimeMs(valueReference, timeval);
} else
}
else
throw new MmsValueException("Value is not a time type");
}
@ -711,9 +726,11 @@ namespace IEC61850
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
public ulong GetUtcTimeInMs()
{
if (GetType () == MmsType.MMS_UTC_TIME) {
if (GetType() == MmsType.MMS_UTC_TIME)
{
return MmsValue_getUtcTimeInMs(valueReference);
} else
}
else
throw new MmsValueException("Value is not a time type");
}
@ -795,7 +812,7 @@ namespace IEC61850
{
DateTimeOffset retVal = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
return retVal.AddMilliseconds ((double) msTime);
return retVal.AddMilliseconds(msTime);
}
/// <summary>
@ -1087,13 +1104,14 @@ namespace IEC61850
{
MmsValue otherValue = (MmsValue)obj;
return MmsValue_equals(this.valueReference, otherValue.valueReference);
return MmsValue_equals(valueReference, otherValue.valueReference);
}
// override standard ToString() method
public override string ToString()
{
switch (GetType ()) {
switch (GetType())
{
case MmsType.MMS_VISIBLE_STRING:
case MmsType.MMS_STRING:
return Marshal.PtrToStringAnsi(MmsValue_toString(valueReference));
@ -1119,12 +1137,16 @@ namespace IEC61850
bool first = true;
foreach (MmsValue element in this) {
if (first) {
foreach (MmsValue element in this)
{
if (first)
{
retString += element.ToString();
first = false;
} else {
}
else
{
retString += ", " + element.ToString();
}
}
@ -1139,12 +1161,16 @@ namespace IEC61850
bool first = true;
foreach (MmsValue element in this) {
if (first) {
foreach (MmsValue element in this)
{
if (first)
{
retString += element.ToString();
first = false;
} else {
}
else
{
retString += ", " + element.ToString();
}
}
@ -1182,7 +1208,8 @@ namespace IEC61850
index = -1;
}
public object Current {
public object Current
{
get { return value.GetElement(index); }
}

@ -1,7 +1,7 @@
/*
* MmsVariableSpecification.cs
*
* Copyright 2014-2024 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -21,11 +21,9 @@
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace IEC61850
@ -73,7 +71,7 @@ namespace IEC61850
internal MmsVariableSpecification(IntPtr self, MmsVariableSpecification parent)
{
this.self = self;
this.responsableForDeletion = false;
responsableForDeletion = false;
this.parent = parent;
}

@ -1,7 +1,7 @@
/*
* ReportControlBlock.cs
*
* Copyright 2014-2018 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -20,11 +20,9 @@
*
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using IEC61850.Common;
namespace IEC61850
{
@ -199,7 +197,8 @@ namespace IEC61850
private void internalReportHandler(IntPtr parameter, IntPtr report)
{
try {
try
{
if (this.report == null)
this.report = new Report(report);
@ -207,7 +206,8 @@ namespace IEC61850
if (reportHandler != null)
reportHandler(this.report, reportHandlerParameter);
} catch (Exception e)
}
catch (Exception e)
{
// older versions of mono 2.10 (for linux?) cause this exception
Console.WriteLine(e.Message);
@ -218,7 +218,8 @@ namespace IEC61850
{
self = ClientReportControlBlock_create(objectReference);
if (self != IntPtr.Zero) {
if (self != IntPtr.Zero)
{
this.iedConnection = iedConnection;
this.objectReference = objectReference;
}
@ -234,8 +235,10 @@ namespace IEC61850
/// <see cref="IEC61850.Client.ReportControlBlock"/> was occupying.</remarks>
public void Dispose()
{
lock (this) {
if (self != IntPtr.Zero) {
lock (this)
{
if (self != IntPtr.Zero)
{
iedConnection.UninstallReportHandler(objectReference);
@ -255,7 +258,7 @@ namespace IEC61850
public string GetObjectReference()
{
return this.objectReference;
return objectReference;
}
/// <summary>
@ -276,11 +279,12 @@ namespace IEC61850
{
this.reportHandler = new ReportHandler(reportHandler);
this.reportHandlerParameter = parameter;
reportHandlerParameter = parameter;
if (reportHandlerInstalled == false) {
if (reportHandlerInstalled == false)
{
string reportId = this.GetRptId ();
string reportId = GetRptId();
if (internalHandler == null)
{
@ -418,11 +422,13 @@ namespace IEC61850
if (error != 0)
throw new IedConnectionException("setRCBValues service failed", error);
if (flagRptId) {
if (flagRptId)
{
if (reportHandlerInstalled) {
if (reportHandlerInstalled)
{
reportHandlerInstalled = false;
InstallReportHandler(this.reportHandler, this.reportHandlerParameter);
InstallReportHandler(reportHandler, reportHandlerParameter);
}
}
}
@ -483,7 +489,8 @@ namespace IEC61850
if (entryIdRef == IntPtr.Zero)
return null;
else {
else
{
MmsValue entryId = new MmsValue(entryIdRef);
return entryId.getOctetString();

@ -1,7 +1,7 @@
/*
* Reporting.cs
*
* Copyright 2014-2018 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -20,11 +20,10 @@
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace IEC61850
{

@ -1,7 +1,7 @@
/*
* SampledValuesControlBlock.cs
*
* Copyright 2017 Michael Zillgith
* Copyright 2017-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -20,11 +20,9 @@
*
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using IEC61850.Common;
namespace IEC61850
{
@ -106,7 +104,7 @@ namespace IEC61850
public string GetObjectReference()
{
return this.objectReference;
return objectReference;
}
public IedClientError GetLastComError()
@ -185,7 +183,8 @@ namespace IEC61850
public void Dispose()
{
if (isDisposed == false) {
if (isDisposed == false)
{
isDisposed = true;
ClientSVControlBlock_destroy(self);
self = IntPtr.Zero;

@ -1,7 +1,7 @@
/*
* SampledValuedSubscriber.cs
*
* Copyright 2017 Michael Zillgith
* Copyright 2017-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -21,9 +21,9 @@
* See COPYING file for the complete license text.
*/
using IEC61850.Common;
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
@ -145,7 +145,8 @@ namespace IEC61850
/// <see cref="IEC61850.SV.Subscriber.SVReceiver"/> was occupying.</remarks>
public void Dispose()
{
if (isDisposed == false) {
if (isDisposed == false)
{
isDisposed = true;
SVReceiver_destroy(self);
self = IntPtr.Zero;
@ -199,14 +200,17 @@ namespace IEC61850
private void internalSVUpdateListener(IntPtr subscriber, IntPtr parameter, IntPtr asdu)
{
try {
try
{
if (listener != null) {
if (listener != null)
{
listener(this, listenerParameter, new SVSubscriberASDU(asdu));
}
}
catch (Exception e) {
catch (Exception e)
{
// older versions of mono 2.10 (for linux?) cause this exception
Console.WriteLine(e.Message);
}
@ -214,9 +218,12 @@ namespace IEC61850
public SVSubscriber(byte[] ethAddr, UInt16 appID)
{
if (ethAddr == null) {
if (ethAddr == null)
{
self = SVSubscriber_create(IntPtr.Zero, appID);
} else {
}
else
{
if (ethAddr.Length != 6)
throw new ArgumentException("ethAddr argument has to be of 6 byte size");
@ -228,9 +235,10 @@ namespace IEC61850
public void SetListener(SVUpdateListener listener, object parameter)
{
this.listener = listener;
this.listenerParameter = parameter;
listenerParameter = parameter;
if (internalListener == null) {
if (internalListener == null)
{
internalListener = new InternalSVUpdateListener(internalSVUpdateListener);
SVSubscriber_setListener(self, internalListener, IntPtr.Zero);
@ -239,7 +247,8 @@ namespace IEC61850
public void Dispose()
{
if (isDisposed == false) {
if (isDisposed == false)
{
isDisposed = true;
SVSubscriber_destroy(self);
self = IntPtr.Zero;
@ -357,7 +366,7 @@ namespace IEC61850
public UInt16 GetSmpRate()
{
return (UInt16)SVSubscriber_ASDU_getSmpRate (self);
return SVSubscriber_ASDU_getSmpRate(self);
}
public bool HasDatSet()

@ -21,14 +21,9 @@
* See COPYING file for the complete license text.
*/
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using IEC61850.Common;
using System.Security.Cryptography.X509Certificates;
/// <summary>
/// IEC 61850 API for the libiec61850 .NET wrapper library
@ -114,7 +109,7 @@ namespace IEC61850
{
if (isValid)
{
return (TLSConfigVersion)TLSConnection_getTLSVersion((IntPtr)self);
return (TLSConfigVersion)TLSConnection_getTLSVersion(self);
}
else
{
@ -136,7 +131,7 @@ namespace IEC61850
if (isValid)
{
IntPtr peerAddrBuf = Marshal.AllocHGlobal(130);
IntPtr peerAddrStr = TLSConnection_getPeerAddress(this.self, peerAddrBuf);
IntPtr peerAddrStr = TLSConnection_getPeerAddress(self, peerAddrBuf);
string peerAddr = null;
@ -331,8 +326,8 @@ namespace IEC61850
public void SetEventHandler(TLSEventHandler handler, object parameter)
{
this.eventHandler = handler;
this.eventHandlerParameter = parameter;
eventHandler = handler;
eventHandlerParameter = parameter;
if (internalTLSEventHandlerRef == null)
{

@ -8,6 +8,7 @@
using System;
using IEC61850.Server;
using IEC61850.Common;
using IEC61850;
using System.Threading;
using System.Net;
using static IEC61850.Server.IedServer;
@ -17,6 +18,7 @@ using IEC61850.Client;
using ReportControlBlock = IEC61850.Server.ReportControlBlock;
using IEC61850.Model;
using System.Data.Common;
using System.Security.Cryptography;
namespace server_access_control
{
@ -142,12 +144,23 @@ namespace server_access_control
/* Install handler to control access to control blocks (RCBs, LCBs, GoCBs, SVCBs, SGCBs)*/
bool ControlBlockAccessCallBack(object parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice ld, LogicalNode ln, string objectName, string subObjectName, ControlBlockAccessType accessType)
{
string password = parameter as string;
object securityToken = connection.GetSecurityToken();
if(securityToken != null)
{
if ((securityToken as string == password))
Console.WriteLine("Correct securityToken");
else
Console.WriteLine("Incorrect securityToken");
}
Console.WriteLine(acsiClass.ToString() + " "+ accessType.ToString() + " access " + ld.GetName() + ln.GetName() +"/"+ objectName + "." + subObjectName + "\n");
return true;
}
iedServer.SetControlBlockAccessHandler(ControlBlockAccessCallBack, iedServer);
iedServer.SetControlBlockAccessHandler(ControlBlockAccessCallBack, "securityToken_password");
/* By default access to variables with FC=DC and FC=CF is not allowed.
* This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used
@ -332,6 +345,67 @@ namespace server_access_control
iedServer.SetSVCBHandler(sVCBEventHandler, sampledValuesControlBlock_1, null);
iedServer.SetSVCBHandler(sVCBEventHandler, sampledValuesControlBlock_2, null);
bool clientAuthenticator (object parameter, AcseAuthenticationParameter authParameter, object securityToken, IsoApplicationReference isoApplicationReference)
{
List<string> passwords = parameter as List<string>;
int aeQualifier = isoApplicationReference.GetAeQualifier();
ItuObjectIdentifier ituObjectIdentifier = isoApplicationReference.GetApTitle();
int arcCount = ituObjectIdentifier.GetArcCount();
ushort[] arc = ituObjectIdentifier.GetArcs();
Console.WriteLine("ACSE Authenticator:\n");
string appTitle = "";
for (int i = 0; i < arcCount; i++)
{
appTitle += arc[i];
if (i != (arcCount - 1))
appTitle += ".";
}
Console.WriteLine(" client ap-title: " + appTitle);
Console.WriteLine("\n client ae-qualifier: "+ aeQualifier + " \n");
IEC61850.AcseAuthenticationMechanism acseAuthenticationMechanism = authParameter.GetAuthMechanism();
if (acseAuthenticationMechanism == IEC61850.AcseAuthenticationMechanism.ACSE_AUTH_PASSWORD)
{
byte[] passArray = authParameter.GetPasswordByteArray();
int passwordLenght = passArray.Length;
string password = authParameter.GetPasswordString();
if (passwordLenght == passwords.First().Length)
{
if (password == passwords.First())
{
securityToken = passwords.First();
return true;
}
}
else if (passwordLenght == passwords[1].Length)
{
if (password == passwords[1])
{
securityToken = passwords[1];
return true;
}
}
}
return false;
}
List<string> passwords = new List<string>();
passwords.Add("user1@testpw");
passwords.Add("user2@testpw");
iedServer.SetAuthenticator(clientAuthenticator, passwords);
iedServer.Start(102);
if (iedServer.IsRunning())

@ -60,7 +60,7 @@ main(int argc, char** argv)
IsoConnectionParameters_setRemoteAddresses(parameters, remotePSelector, remoteSSelector, localTSelector);
IsoConnectionParameters_setLocalAddresses(parameters, localPSelector, localSSelector, remoteTSelector);
char* password = "top secret";
char* password = "user1@testpw";
/* use authentication */
AcseAuthenticationParameter auth = AcseAuthenticationParameter_create();
@ -84,6 +84,8 @@ main(int argc, char** argv)
printf("Failed to connect to %s:%i\n", hostname, tcpPort);
}
while (true);
IedConnection_destroy(con);
AcseAuthenticationParameter_destroy(auth);

@ -1,7 +1,7 @@
/*
* reporting.c
*
* Copyright 2013-2024 Michael Zillgith
* Copyright 2013-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*

@ -1,7 +1,7 @@
/*
* config_file_parser.c
*
* Copyright 2014-2024 Michael Zillgith
* Copyright 2014-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*

@ -91,9 +91,29 @@ AcseAuthenticationParameter_destroy(AcseAuthenticationParameter self);
LIB61850_API void
AcseAuthenticationParameter_setAuthMechanism(AcseAuthenticationParameter self, AcseAuthenticationMechanism mechanism);
LIB61850_API AcseAuthenticationMechanism
AcseAuthenticationParameter_getAuthMechanism(AcseAuthenticationParameter self);
LIB61850_API void
AcseAuthenticationParameter_setPassword(AcseAuthenticationParameter self, char* password);
LIB61850_API const char*
AcseAuthenticationParameter_getPassword(AcseAuthenticationParameter self);
LIB61850_API int
AcseAuthenticationParameter_getPasswordLength(AcseAuthenticationParameter self);
LIB61850_API int
IsoApplicationReference_getAeQualifier(IsoApplicationReference self);
LIB61850_API const ItuObjectIdentifier*
IsoApplicationReference_getApTitle(const IsoApplicationReference* self);
LIB61850_API int
ItuObjectIdentifier_getArcCount(ItuObjectIdentifier* self);
LIB61850_API const uint16_t*
ItuObjectIdentifier_getArc(ItuObjectIdentifier* self);
/**
* \brief Callback function to authenticate a client

@ -57,12 +57,76 @@ AcseAuthenticationParameter_setPassword(AcseAuthenticationParameter self, char*
self->value.password.passwordLength = strlen(password);
}
/* TODO
->One function returning as string and another as by array*/
const char*
AcseAuthenticationParameter_getPassword(AcseAuthenticationParameter self)
{
if (self == NULL)
return NULL;
if (self->mechanism != ACSE_AUTH_PASSWORD)
return NULL;
return (char*)self->value.password.octetString;
}
int
AcseAuthenticationParameter_getPasswordLength(AcseAuthenticationParameter self)
{
if (self == NULL)
return 0;
if (self->mechanism != ACSE_AUTH_PASSWORD)
return 0;
return self->value.password.passwordLength;
}
void
AcseAuthenticationParameter_setAuthMechanism(AcseAuthenticationParameter self, AcseAuthenticationMechanism mechanism)
{
self->mechanism = mechanism;
}
AcseAuthenticationMechanism
AcseAuthenticationParameter_getAuthMechanism(AcseAuthenticationParameter self)
{
return self->mechanism;
}
LIB61850_API int
IsoApplicationReference_getAeQualifier(IsoApplicationReference self)
{
return self.aeQualifier;
}
LIB61850_API const ItuObjectIdentifier*
IsoApplicationReference_getApTitle(const IsoApplicationReference* self)
{
if (self == NULL)
return NULL;
return &(self->apTitle);
}
LIB61850_API int
ItuObjectIdentifier_getArcCount(ItuObjectIdentifier* self)
{
if (self == NULL)
return NULL;
return self->arcCount;
}
LIB61850_API const uint16_t*
ItuObjectIdentifier_getArc(ItuObjectIdentifier* self)
{
if (self == NULL)
return NULL;
return self->arc;
}
IsoConnectionParameters
IsoConnectionParameters_create()
{

@ -1,7 +1,7 @@
/*
* mms_server.c
*
* Copyright 2013-2024 Michael Zillgith
* Copyright 2013-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*

Loading…
Cancel
Save