From 4a8245caf3a147354be6cfebbcebd8f147c998b6 Mon Sep 17 00:00:00 2001 From: Maxson Ramon dos Anjos Medeiros Date: Thu, 3 Jul 2025 14:55:35 +0200 Subject: [PATCH] LogServies API added --- CHANGELOG | 9 ++- dotnet/IEC61850forCSharp/AssemblyInfo.cs | 16 ----- dotnet/IEC61850forCSharp/IEC61850.NET.csproj | 47 +++----------- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 44 +++++++++++++ dotnet/IEC61850forCSharp/MmsValue.cs | 23 ++++++- dotnet/log_client/Program.cs | 16 +++-- dotnet/log_server/App.config | 6 +- dotnet/log_server/Program.cs | 63 ++++++++++++++++++- dotnet/log_server/log_server.csproj | 3 +- mingw-w64-x86_64.cmake | 2 +- 10 files changed, 157 insertions(+), 72 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 353ecb83..0d6c7d86 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,16 @@ Changes to version 1.6.1 ------------------------ --.NET Tools: in the Tools folder you can find the .net project to generate Static and Dynamic Models +New features and improvements: + +- .NET API: IEC61850ServerAPI -> LogStorage function wrapper added. +- .NET API: MmsValue -> MmsValue_encodeMmsData and MmsValue_decodeMmsData added. +- .NET API: Fixed issue of not printing log entries in log_client and log_server examples. +- .NET Tools: in the Tools folder you can find the .net project to generate Static and Dynamic Models - SCLParser: also available on tools, you can load your SCL model and use it with our library + + Changes to version 1.6.0 ------------------------ diff --git a/dotnet/IEC61850forCSharp/AssemblyInfo.cs b/dotnet/IEC61850forCSharp/AssemblyInfo.cs index 479370f2..f5d02ba2 100644 --- a/dotnet/IEC61850forCSharp/AssemblyInfo.cs +++ b/dotnet/IEC61850forCSharp/AssemblyInfo.cs @@ -1,24 +1,8 @@ using System.Reflection; using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("IEC61850 API for C#")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("MZ Automation GmbH")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("Michael Zillgith")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. diff --git a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj index 21da8024..401218af 100644 --- a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj +++ b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj @@ -1,56 +1,23 @@ - - + - Debug - AnyCPU - {C35D624E-5506-4560-8074-1728F1FA1A4D} + netstandard2.0 Library iec61850dotnet iec61850dotnet - v4.8 - + IEC61850 API for C# + MZ Automation GmbH + Michael Zillgith + 1.0.%2a + false - true - full - false bin\Debug DEBUG; - prompt - 4 false - false none - true bin\Release - prompt - 4 false - false - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index d6852a10..1c021b26 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -22,9 +22,11 @@ */ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; using IEC61850.Common; using IEC61850.TLS; +using static IEC61850.Client.IedConnection; // IEC 61850 API for the libiec61850 .NET wrapper library namespace IEC61850 @@ -2186,6 +2188,22 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int LogStorage_getMaxLogEntries(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int LogStorage_addEntry(IntPtr self, long time); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern bool LogStorage_addEntryData(IntPtr self, int entryID, string dataRef, byte[] data, int dataSize, int reasonCode); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool LogEntryCallback(IntPtr self, long timeStamp, long entryID, bool moreFollows); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool LogEntryDataCallback(IntPtr self, string dataRef, byte[] data, int dataSize, int reasonCode, bool moreFollows); + + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern bool LogStorage_getEntries(IntPtr self, long startingTime, long endingTime, LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, object parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void LogStorage_destroy(IntPtr self); @@ -2210,6 +2228,21 @@ namespace IEC61850 { return LogStorage_getMaxLogEntries(self); } + public int AddEntry(long time) + { + return LogStorage_addEntry(self, time); + } + + public bool AddEntryData(int entryID, string dataRef, byte[] data, int dataSize, int reasonCode) + { + + return LogStorage_addEntryData(self, entryID, dataRef, data, dataSize, reasonCode); + } + + public bool GetEntries(long startingTime, long endingTime, LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, object parameter) + { + return LogStorage_getEntries(self, startingTime, endingTime, entryCallback, entryDataCallback, parameter); + } /// /// The maximum allowed number of log entries in the log storage @@ -2315,6 +2348,12 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedServer_getFunctionalConstrainedData(IntPtr self, IntPtr dataObject, int fc); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setListObjectsAccessHandler(IntPtr self, IedServer_ListObjectsAccessHandler handler, IntPtr parameter); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool IedServer_ListObjectsAccessHandler(IntPtr parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice ld, LogicalNode ln, string objectName, string subObjectName, FunctionalConstraint fc); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int InternalControlPerformCheckHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck); @@ -2538,6 +2577,11 @@ namespace IEC61850 } } + public void SetListObjectsAccessHandler(IedServer_ListObjectsAccessHandler handler, System.IntPtr parameter) + { + IedServer_setListObjectsAccessHandler(self, handler, parameter); + } + private Dictionary writeAccessHandlers = new Dictionary (); private void ConnectionIndicationHandlerImpl (IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter) diff --git a/dotnet/IEC61850forCSharp/MmsValue.cs b/dotnet/IEC61850forCSharp/MmsValue.cs index d262688a..97020ab1 100644 --- a/dotnet/IEC61850forCSharp/MmsValue.cs +++ b/dotnet/IEC61850forCSharp/MmsValue.cs @@ -206,7 +206,13 @@ namespace IEC61850 static extern void MmsValue_setElement(IntPtr complexValue, int index, IntPtr elementValue); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr MmsVariableSpecification_getChildValue(IntPtr self, IntPtr value, string childId); + static extern IntPtr MmsVariableSpecification_getChildValue(IntPtr self, IntPtr value, string childId); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int MmsValue_encodeMmsData(IntPtr self, byte[] buffer, int bufPos, bool encode); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int MmsValue_decodeMmsData(IntPtr self, int bufPos, int bufferLength, IntPtr endBufPo); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr MmsValue_clone(IntPtr self); @@ -220,8 +226,19 @@ namespace IEC61850 valueReference = value; this.responsableForDeletion = false; } - - internal MmsValue (IntPtr value, bool responsableForDeletion) + + public int EncodeMmsData(byte[] buffer, int bufPos, bool encode) + { + return MmsValue_encodeMmsData(this.valueReference, buffer, bufPos, encode); + } + + public int DecodeMmsData(int bufPos, int bufferLength, IntPtr endBufPo) + { + + return MmsValue_decodeMmsData(this.valueReference, bufPos, bufferLength, endBufPo); + } + + internal MmsValue (IntPtr value, bool responsableForDeletion) { valueReference = value; this.responsableForDeletion = responsableForDeletion; diff --git a/dotnet/log_client/Program.cs b/dotnet/log_client/Program.cs index b857a0f3..9007adcc 100644 --- a/dotnet/log_client/Program.cs +++ b/dotnet/log_client/Program.cs @@ -10,7 +10,7 @@ namespace log_client private static void PrintJournalEntries(List journalEntries) { foreach (MmsJournalEntry entry in journalEntries) { Console.WriteLine("EntryID: " + BitConverter.ToString(entry.GetEntryID())); - Console.WriteLine(" occurence time: " + MmsValue.MsTimeToDateTimeOffset(entry.GetOccurenceTime()).ToString()); + Console.WriteLine(" occurence time: " + MmsValue.MsTimeToDateTimeOffset(entry.GetOccurenceTime()).ToString()); foreach (MmsJournalVariable variable in entry.GetJournalVariables()) { Console.WriteLine(" variable: " + variable.GetTag()); Console.WriteLine(" value: " + variable.GetValue().ToString()); @@ -47,12 +47,14 @@ namespace log_client Console.WriteLine("LD: " + deviceName); List deviceDirectory = con.GetLogicalDeviceDirectory(deviceName); - foreach (string lnName in deviceDirectory) { + foreach (string lnName in deviceDirectory) + { Console.WriteLine(" LN: " + lnName); List lcbs = con.GetLogicalNodeDirectory(deviceName + "/" + lnName, IEC61850.Common.ACSIClass.ACSI_CLASS_LCB); - foreach(string lcbName in lcbs) { + foreach (string lcbName in lcbs) + { Console.WriteLine(" LCB: " + lcbName); MmsValue lcbValues = con.ReadValue(deviceName + "/" + lnName + "." + lcbName, FunctionalConstraint.LG); @@ -62,7 +64,8 @@ namespace log_client List logs = con.GetLogicalNodeDirectory(deviceName + "/" + lnName, IEC61850.Common.ACSIClass.ACSI_CLASS_LOG); - foreach(string logName in logs) { + foreach (string logName in logs) + { Console.WriteLine(" LOG: " + logName); } } @@ -72,7 +75,10 @@ namespace log_client Console.WriteLine("\nQueryLogAfter:"); - List journalEntries = con.QueryLogAfter("simpleIOGenericIO/LLN0$EventLog", + Console.WriteLine(DateTime.UtcNow); + + + List journalEntries = con.QueryLogAfter("simpleIOGenericIO/LLN0$EventLog", new byte[]{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0, out moreFollows); PrintJournalEntries(journalEntries); diff --git a/dotnet/log_server/App.config b/dotnet/log_server/App.config index aee9adf4..9b6bf3fe 100644 --- a/dotnet/log_server/App.config +++ b/dotnet/log_server/App.config @@ -1,6 +1,6 @@ - + - + - \ No newline at end of file + diff --git a/dotnet/log_server/Program.cs b/dotnet/log_server/Program.cs index 26617d7e..6df1061b 100644 --- a/dotnet/log_server/Program.cs +++ b/dotnet/log_server/Program.cs @@ -2,10 +2,15 @@ using IEC61850.Server; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data.Common; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; +using static IEC61850.Server.IedServer; +using static IEC61850.Server.LogStorage; namespace log_server { @@ -13,6 +18,25 @@ namespace log_server { public static void Main(string[] args) { + + bool entryCallback(System.IntPtr self, long timestamp, long entryID1, bool moreFollow) + { + if (moreFollow) + Console.WriteLine($"Found entry ID:{entryID1} timestamp:{timestamp}"); + return true; + } + + + bool entryDataCallback(System.IntPtr self, string dataRef, byte[] data, int dataSize, int reasonCode, bool moreFollow) + { + if (moreFollow) + { + Console.WriteLine($"EntryData: ref: {dataRef}\n"); + } + + return true; + } + bool running = true; /* run until Ctrl-C is pressed */ @@ -41,7 +65,37 @@ namespace log_server iedServer.SetLogStorage("GenericIO/LLN0$EventLog", statusLog); - iedServer.Start(10002); + long time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + int entryID = statusLog.AddEntry(time); + + MmsValue value = new MmsValue(123); + byte[] blob = new byte[256]; + + int blobSize = value.EncodeMmsData(blob, 0, true); + + bool restun = statusLog.AddEntryData(entryID, "GenericIO/GGIO1.SPCSO1$stVal", blob, blobSize, 0); + + value.Dispose(); + ulong unixTimeMs = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + + entryID = statusLog.AddEntry((long)unixTimeMs); + + value = MmsValue.NewUtcTime(unixTimeMs); + blob = new byte[256]; + + blobSize = value.EncodeMmsData(blob, 0, true); + + value.Dispose(); + + + bool restun1 = statusLog.AddEntryData(entryID, "simpleIOGenerioIO/GPIO1$ST$SPCSO1$t", blob, blobSize, 0); + + + bool return3 = statusLog.GetEntries(0, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), (LogEntryCallback)entryCallback, (LogEntryDataCallback)entryDataCallback, (System.IntPtr)null); + + iedServer.Start(102); if (iedServer.IsRunning()) { @@ -67,7 +121,11 @@ namespace log_server stVal = !stVal; iedServer.LockDataModel(); - var ts = new Timestamp(DateTime.Now); + + long unixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + DateTime utcTime = DateTimeOffset.FromUnixTimeSeconds(unixSeconds).UtcDateTime; + + var ts = new Timestamp(utcTime); iedServer.UpdateTimestampAttributeValue(ggio1AnIn1T, ts); iedServer.UpdateFloatAttributeValue(ggio1AnIn1magF, floatVal); iedServer.UpdateTimestampAttributeValue(ggio1Spcso1T, ts); @@ -86,6 +144,7 @@ namespace log_server } iedServer.Destroy(); + statusLog.Dispose(); } } } diff --git a/dotnet/log_server/log_server.csproj b/dotnet/log_server/log_server.csproj index 1e2bab4b..784db49d 100644 --- a/dotnet/log_server/log_server.csproj +++ b/dotnet/log_server/log_server.csproj @@ -8,7 +8,7 @@ Exe log_server log_server - v4.8.1 + v4.8 512 true true @@ -27,6 +27,7 @@ false false true + AnyCPU diff --git a/mingw-w64-x86_64.cmake b/mingw-w64-x86_64.cmake index e86af924..9afbe678 100644 --- a/mingw-w64-x86_64.cmake +++ b/mingw-w64-x86_64.cmake @@ -3,7 +3,7 @@ # Typical usage: # *) install cross compiler: `sudo apt-get install mingw-w64` # *) cd build -# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/mingw-w64-x86_64.cmake .. +# *) cmake -DCMAKE_TOOLCHAIN_FILE=../mingw-w64-x86_64.cmake .. # This is free and unencumbered software released into the public domain. set(CMAKE_SYSTEM_NAME Windows)