diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 771c1274..4ba47ea9 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -48,6 +48,9 @@ namespace IEC61850 public string revision; } + /// + /// Represents an MmsConnection object (a single connection to an MMS server) + /// public class MmsConnection { @@ -69,6 +72,25 @@ namespace IEC61850 [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] private static extern Int32 MmsConnection_getLocalDetail (IntPtr self); + [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] + private static extern IntPtr MmsConnection_readMultipleVariables(IntPtr self, out int mmsError, + string domainId, IntPtr items); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void MmsValue_delete (IntPtr self); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void LinkedListValueDeleteFunction(IntPtr pointer); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void LinkedList_destroyDeep(IntPtr list, LinkedListValueDeleteFunction valueDeleteFunction); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LinkedList_create (); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void LinkedList_add (IntPtr self, IntPtr data); + private IntPtr self = IntPtr.Zero; private bool selfDestroy = false; @@ -90,6 +112,15 @@ namespace IEC61850 MmsConnection_destroy(self); } + private void FreeHGlobaleDeleteFunction (IntPtr pointer) + { + Marshal.FreeHGlobal(pointer); + } + + /// + /// Requests the server identity information + /// + /// The server identity. public MmsServerIdentity GetServerIdentity () { int mmsError; @@ -100,6 +131,12 @@ namespace IEC61850 IntPtr identity = MmsConnection_identify(self, out mmsError); + if (mmsError != 0) + throw new IedConnectionException("Failed to read server identity", mmsError); + + if (identity == IntPtr.Zero) + throw new IedConnectionException("Failed to read server identity"); + MmsServerIdentity serverIdentity = (MmsServerIdentity) Marshal.PtrToStructure(identity, typeof(MmsServerIdentity)); @@ -108,14 +145,54 @@ namespace IEC61850 return serverIdentity; } + /// + /// Sets the local detail (maximum MMS PDU size) + /// + /// maximum accepted MMS PDU size in bytes public void SetLocalDetail(int localDetail) { MmsConnection_setLocalDetail (self, localDetail); } + /// + /// Gets the local detail (maximum MMS PDU size) + /// + /// maximum accepted MMS PDU size in bytes public int GetLocalDetail() { return MmsConnection_getLocalDetail (self); } + /// + /// Reads multipe MMS variables from the same domain + /// + /// MmsValue of type MMS_ARRAY containing the access results for the requested variables. + /// the domain name (logical device) + /// list of variable names (in MMS notation e.g. "GGIO$ST$Ind1") + public MmsValue ReadMultipleVariables(string domainName, List variables) + { + IntPtr linkedList = LinkedList_create (); + + foreach (string variableName in variables) { + IntPtr handle = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi (variableName); + + LinkedList_add (linkedList, handle); + } + + int error; + + IntPtr mmsValue = MmsConnection_readMultipleVariables(self, out error, domainName, linkedList); + + LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction)); + + if (error != 0) + { + if (mmsValue != IntPtr.Zero) + MmsValue_delete(mmsValue); + + throw new IedConnectionException("ReadMultipleVariables failed", error); + } + + return new MmsValue(mmsValue, true); + } } diff --git a/dotnet/IEC61850forCSharp/MmsValue.cs b/dotnet/IEC61850forCSharp/MmsValue.cs index fd00626f..5d9900df 100644 --- a/dotnet/IEC61850forCSharp/MmsValue.cs +++ b/dotnet/IEC61850forCSharp/MmsValue.cs @@ -186,8 +186,7 @@ namespace IEC61850 private bool responsableForDeletion; /* if .NET wrapper is responsable for the deletion of the native MmsValue instance */ - // TODO make internal - public MmsValue (IntPtr value) + internal MmsValue (IntPtr value) { valueReference = value; this.responsableForDeletion = false; @@ -939,9 +938,31 @@ namespace IEC61850 return retString; } + case MmsType.MMS_ARRAY: + { + string retString = "["; + + bool first = true; + + foreach (MmsValue element in this) { + if (first) { + retString += element.ToString (); + + first = false; + } else { + retString += ", " + element.ToString (); + } + } + + retString += "]"; + + return retString; + } + case MmsType.MMS_DATA_ACCESS_ERROR: + return "error: " + GetDataAccessError().ToString(); default: - return "unknown"; + return "unknown (type:" + GetType().ToString() + ")"; } } diff --git a/dotnet/example1/Main.cs b/dotnet/example1/Main.cs index 738d839e..b02b312f 100644 --- a/dotnet/example1/Main.cs +++ b/dotnet/example1/Main.cs @@ -67,7 +67,18 @@ namespace example1 DataSet dataSet = con.ReadDataSetValues("simpleIOGenericIO/LLN0.Events", null); - Console.WriteLine("Read data set " + dataSet.GetReference()); + Console.WriteLine("Read data set " + dataSet.GetReference()); + + /* read multiple variables (WARNING: this is not IEC 61850 standard compliant but might + * be supported by most servers). + */ + MmsConnection mmsConnection = con.GetMmsConnection(); + + MmsValue result = mmsConnection.ReadMultipleVariables("simpleIOGenericIO", new List() { + "GGIO1$ST$Ind1", "GGIO1$ST$Ind1", "GGIO1$ST$Ind1","GGIO1$ST$Ind1" + }); + + Console.WriteLine(result.ToString()); con.Abort(); }