From f3b75dd3fcfe07b81df958c64047d6928368bcb1 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 6 Apr 2017 22:58:22 +0200 Subject: [PATCH] - added functions Timestamp_create, Timestamp_destroy, Timestamp_setByMmsUtcTime - C# API: Added Timestamp class - C# API: Added missing UpdateAttribute methods to IedServer --- CMakeLists.txt | 2 +- dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs | 6 +- dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs | 197 ++++++++++++++++++ dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 50 ++++- dotnet/server1/Program.cs | 12 +- dotnet/tests/Test.cs | 24 +++ .../inc/libiec61850_platform_includes.h | 2 +- src/doxygen.config | 2 +- src/iec61850/common/iec61850_common.c | 23 ++ src/iec61850/inc/iec61850_common.h | 9 + src/vs/libiec61850-wo-goose.def | 3 + src/vs/libiec61850.def | 3 + 12 files changed, 316 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 16994b0..cd880bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ ENABLE_TESTING() set(LIB_VERSION_MAJOR "1") set(LIB_VERSION_MINOR "0") -set(LIB_VERSION_PATCH "1") +set(LIB_VERSION_PATCH "2") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/") diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 70e7b1d..374d3a8 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -987,14 +987,14 @@ namespace IEC61850 /// The object reference of a BDA. /// The functional constraint (FC) of the object /// This exception is thrown if there is a connection or service error - public UInt64 ReadTimestampValue (string objectReference, FunctionalConstraint fc) + public Timestamp ReadTimestampValue (string objectReference, FunctionalConstraint fc) { var mmsValuePtr = readObjectInternalAndCheckDataAccessError (objectReference, fc); var mmsValue = new MmsValue (mmsValuePtr, true); - if (mmsValue.GetType () == MmsType.MMS_UTC_TIME) - return mmsValue.GetUtcTimeInMs (); + if (mmsValue.GetType () == MmsType.MMS_UTC_TIME) + return new Timestamp (mmsValue); else throw new IedConnectionException ("Result is not of type timestamp (MMS_UTC_TIME)", 0); } diff --git a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs index 6024dc1..0e80071 100644 --- a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs @@ -140,6 +140,203 @@ namespace IEC61850 } } + /// + /// Timestamp (represents IEC 61850 timestamps e.g. "t" attribute) + /// + public class Timestamp + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr Timestamp_create (); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_destroy (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_clearFlags (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool Timestamp_isLeapSecondKnown (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_setLeapSecondKnown (IntPtr self, bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool Timestamp_hasClockFailure (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_setClockFailure (IntPtr self, bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool Timestamp_isClockNotSynchronized (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_setClockNotSynchronized (IntPtr self, bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int Timestamp_getSubsecondPrecision (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_setSubsecondPrecision(IntPtr self, int subsecondPrecision); + + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_setTimeInSeconds (IntPtr self, UInt32 secondsSinceEpoch); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_setTimeInMilliseconds (IntPtr self, UInt64 millisSinceEpoch); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 Timestamp_getTimeInSeconds (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt64 Timestamp_getTimeInMs (IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Timestamp_setByMmsUtcTime (IntPtr self, IntPtr mmsValue); + + internal IntPtr timestampRef = IntPtr.Zero; + private bool responsableForDeletion; + + internal Timestamp(IntPtr timestampRef, bool selfAllocated) + { + this.timestampRef = timestampRef; + this.responsableForDeletion = selfAllocated; + } + + public Timestamp (DateTime timestamp) : this () + { + SetDateTime (timestamp); + } + + public Timestamp (MmsValue mmsUtcTime) : this() + { + SetByMmsUtcTime (mmsUtcTime); + } + + public Timestamp() + { + timestampRef = Timestamp_create (); + LeapSecondKnown = true; + responsableForDeletion = true; + } + + ~Timestamp () + { + if (responsableForDeletion) + Timestamp_destroy (timestampRef); + } + + public void ClearFlags() + { + Timestamp_clearFlags (timestampRef); + } + + public void SetDateTime(DateTime timestamp) + { + DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + DateTime timestampUTC = timestamp.ToUniversalTime(); + + TimeSpan timeDiff = timestampUTC - epoch; + ulong timeVal = Convert.ToUInt64(timeDiff.TotalMilliseconds); + + SetTimeInMilliseconds (timeVal); + } + + public bool LeapSecondKnown { + get { return IsLeapSecondKnown ();} + set { SetLeapSecondKnow (value);} + } + + public bool IsLeapSecondKnown() + { + return Timestamp_isLeapSecondKnown (timestampRef); + } + + public void SetLeapSecondKnow(bool value) + { + Timestamp_setLeapSecondKnown (timestampRef, value); + } + + public bool ClockFailure { + get { return HasClockFailure ();} + set { SetClockFailure (value);} + } + + public bool HasClockFailure() + { + return Timestamp_hasClockFailure (timestampRef); + } + + public void SetClockFailure(bool value) + { + Timestamp_setClockFailure (timestampRef, value); + } + + public bool ClockNotSynchronized { + get { return IsClockNotSynchronized (); } + set { SetClockNotSynchronized (value); } + } + + public bool IsClockNotSynchronized() { + return Timestamp_isClockNotSynchronized(timestampRef); + } + + public void SetClockNotSynchronized(bool value) { + Timestamp_setClockNotSynchronized (timestampRef, value); + } + + public int SubsecondPrecision { + get { return GetSubsecondPrecision (); } + set { SetSubsecondPrecision (value); } + } + + public int GetSubsecondPrecision() { + return Timestamp_getSubsecondPrecision (timestampRef); + } + + public void SetSubsecondPrecision(int subsecondPrecision) { + Timestamp_setSubsecondPrecision (timestampRef, subsecondPrecision); + } + + public UInt32 TimeInSeconds { + get { return GetTimeInSeconds (); } + set { SetTimeInSeconds (value); } + } + + public UInt32 GetTimeInSeconds() + { + return Timestamp_getTimeInSeconds (timestampRef); + } + + public void SetTimeInSeconds(UInt32 secondsSinceEpoch) + { + Timestamp_setTimeInSeconds (timestampRef, secondsSinceEpoch); + } + + public UInt64 TimeInMilliseconds { + get { return GetTimeInMilliseconds (); } + set { SetTimeInMilliseconds (value); } + } + + public UInt64 GetTimeInMilliseconds() + { + return Timestamp_getTimeInMs (timestampRef); + } + + public void SetTimeInMilliseconds(UInt64 millisSinceEpoch) { + Timestamp_setTimeInMilliseconds (timestampRef, millisSinceEpoch); + } + + public void SetByMmsUtcTime(MmsValue mmsValue) + { + Timestamp_setByMmsUtcTime (timestampRef, mmsValue.valueReference); + } + + } + public enum ACSIClass { /** data objects */ diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index b19835c..926d008 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -227,7 +227,8 @@ namespace IEC61850 GENERIC_BITSTRING = 26, CONSTRUCTED = 27, ENTRY_TIME = 28, - PHYCOMADDR = 29 + PHYCOMADDR = 29, + CURRENCY = 30 } public enum ModeValues @@ -246,7 +247,12 @@ namespace IEC61850 ALARM = 3 } - public class CDC { + /// + /// The CDC class contains helper functions to create DataObject instances for the + /// most common Common Data Classes. + /// + public class CDC + { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_SPS_create(string name, IntPtr parent, uint options); @@ -573,12 +579,18 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_unlockDataModel(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_updateAttributeValue(IntPtr self, IntPtr DataAttribute, IntPtr MmsValue); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateBooleanAttributeValue(IntPtr self, IntPtr dataAttribute, bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateInt32AttributeValue(IntPtr self, IntPtr dataAttribute, int value); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_updateInt64AttributeValue(IntPtr self, IntPtr dataAttribute, Int64 value); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateFloatAttributeValue(IntPtr self, IntPtr dataAttribute, float value); @@ -588,6 +600,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateUTCTimeAttributeValue(IntPtr self, IntPtr dataAttribute, ulong value); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_updateTimestampAttributeValue(IntPtr self, IntPtr dataAttribute, IntPtr timestamp); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateQuality(IntPtr self, IntPtr dataAttribute, ushort value); @@ -892,24 +907,34 @@ namespace IEC61850 IedServer_unlockDataModel(self); } - public void UpdateBooleanAttributeValue(DataAttribute dataAttr, bool val) + public void UpdateAttributeValue(DataAttribute dataAttr, MmsValue value) { - IedServer_updateBooleanAttributeValue(self, dataAttr.self, val); + IedServer_updateAttributeValue (self, dataAttr.self, value.valueReference); } - public void UpdateFloatAttributeValue(DataAttribute dataAttr, float val) + public void UpdateBooleanAttributeValue(DataAttribute dataAttr, bool value) { - IedServer_updateFloatAttributeValue(self, dataAttr.self, val); + IedServer_updateBooleanAttributeValue(self, dataAttr.self, value); } - public void UpdateInt32AttributeValue(DataAttribute dataAttr, int val) + public void UpdateFloatAttributeValue(DataAttribute dataAttr, float value) { - IedServer_updateInt32AttributeValue(self, dataAttr.self, val); + IedServer_updateFloatAttributeValue(self, dataAttr.self, value); } - public void UpdateVisibleStringAttributeValue(DataAttribute dataAttr, string val) + public void UpdateInt32AttributeValue(DataAttribute dataAttr, int value) { - IedServer_updateVisibleStringAttributeValue(self, dataAttr.self, val); + IedServer_updateInt32AttributeValue(self, dataAttr.self, value); + } + + public void UpdateInt64AttributeValue(DataAttribute dataAttr, Int64 value) + { + IedServer_updateInt64AttributeValue (self, dataAttr.self, value); + } + + public void UpdateVisibleStringAttributeValue(DataAttribute dataAttr, string value) + { + IedServer_updateVisibleStringAttributeValue(self, dataAttr.self, value); } public void UpdateUTCTimeAttributeValue(DataAttribute dataAttr, DateTime timestamp) @@ -923,6 +948,11 @@ namespace IEC61850 IedServer_updateUTCTimeAttributeValue(self, dataAttr.self, timeVal); } + public void UpdateTimestampAttributeValue(DataAttribute dataAttr, Timestamp timestamp) + { + IedServer_updateTimestampAttributeValue (self, dataAttr.self, timestamp.timestampRef); + } + public void UpdateQuality(DataAttribute dataAttr, ushort value) { IedServer_updateQuality(self, dataAttr.self, value); diff --git a/dotnet/server1/Program.cs b/dotnet/server1/Program.cs index 1776257..1977879 100644 --- a/dotnet/server1/Program.cs +++ b/dotnet/server1/Program.cs @@ -44,8 +44,18 @@ namespace server1 GC.Collect (); + DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.AnIn1"); + + DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild ("mag.f"); + DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild ("t"); + + float floatVal = 1.0f; + while (running) { - Thread.Sleep (1); + floatVal += 1f; + iedServer.UpdateTimestampAttributeValue (ggio1AnIn1T, new Timestamp (DateTime.Now)); + iedServer.UpdateFloatAttributeValue (ggio1AnIn1magF, floatVal); + Thread.Sleep (100); } iedServer.Stop (); diff --git a/dotnet/tests/Test.cs b/dotnet/tests/Test.cs index 2b9f4e4..8ec403e 100644 --- a/dotnet/tests/Test.cs +++ b/dotnet/tests/Test.cs @@ -105,6 +105,30 @@ namespace tests Assert.AreEqual (val.ToFloat (), (float)0.1234); } + [Test ()] + public void Timestamps() + { + Timestamp timestamp = new Timestamp (); + + Assert.IsTrue (timestamp.LeapSecondKnown); + Assert.IsFalse (timestamp.ClockFailure); + Assert.IsFalse (timestamp.ClockNotSynchronized); + + timestamp.LeapSecondKnown = false; + Assert.IsFalse (timestamp.LeapSecondKnown); + + timestamp.ClockFailure = true; + Assert.IsTrue (timestamp.ClockFailure); + + timestamp.ClockNotSynchronized = true; + Assert.IsTrue (timestamp.ClockNotSynchronized); + + Assert.AreEqual (0, timestamp.SubsecondPrecision); + + timestamp.SubsecondPrecision = 10; + Assert.AreEqual (10, timestamp.SubsecondPrecision); + } + [Test ()] public void CreateModelFromNonExistingFile() { diff --git a/src/common/inc/libiec61850_platform_includes.h b/src/common/inc/libiec61850_platform_includes.h index 674b554..4dc01c2 100644 --- a/src/common/inc/libiec61850_platform_includes.h +++ b/src/common/inc/libiec61850_platform_includes.h @@ -15,7 +15,7 @@ #include "platform_endian.h" -#define LIBIEC61850_VERSION "1.0.1" +#define LIBIEC61850_VERSION "1.0.2" #ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME #define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com" diff --git a/src/doxygen.config b/src/doxygen.config index c53fdd7..f188266 100644 --- a/src/doxygen.config +++ b/src/doxygen.config @@ -18,7 +18,7 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "libIEC61850" -PROJECT_NUMBER = 1.0.0 +PROJECT_NUMBER = 1.0.2 PROJECT_BRIEF = "Open-source IEC 61850 MMS/GOOSE/SV server and client library" diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index e16eb9a..3189203 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -26,6 +26,7 @@ #include "libiec61850_platform_includes.h" #include "conversions.h" +#include "mms_value_internal.h" Validity Quality_getValidity(Quality* self) @@ -223,6 +224,21 @@ FunctionalConstraint_fromString(const char* fcString) return IEC61850_FC_NONE; } +Timestamp* +Timestamp_create() +{ + Timestamp* self = (Timestamp*) GLOBAL_CALLOC(1, sizeof(Timestamp)); + + return self; +} + +void +Timestamp_destroy(Timestamp* self) +{ + if (self != NULL) + GLOBAL_FREEMEM(self); +} + void Timestamp_clearFlags(Timestamp* self) { @@ -382,6 +398,13 @@ Timestamp_getTimeInMs(Timestamp* self) return (uint64_t) msVal; } +void +Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue) +{ + if (MmsValue_getType(mmsValue) == MMS_UTC_TIME) + memcpy(self->val, mmsValue->value.utcTime, 8); +} + char* LibIEC61850_getVersionString() { diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index 5b73f33..47131c4 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -353,6 +353,12 @@ typedef union { uint8_t val[8]; } Timestamp; +Timestamp* +Timestamp_create(void); + +void +Timestamp_destroy(Timestamp* self); + void Timestamp_clearFlags(Timestamp* self); @@ -397,6 +403,9 @@ Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch); void Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch); +void +Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue); + /** * \brief Get the version of the library as string * diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index 2fee1d4..065650a 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -558,3 +558,6 @@ EXPORTS MmsConnection_obtainFile IedConnection_setFile IedConnection_readInt64Value + Timestamp_create + Timestamp_destroy + Timestamp_setByMmsUtcTime \ No newline at end of file diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index 2b4d1a6..3a2ca38 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -636,3 +636,6 @@ EXPORTS MmsConnection_obtainFile IedConnection_setFile IedConnection_readInt64Value + Timestamp_create + Timestamp_destroy + Timestamp_setByMmsUtcTime \ No newline at end of file