diff --git a/config/stack_config.h b/config/stack_config.h
index 2559069c..bc3045d0 100644
--- a/config/stack_config.h
+++ b/config/stack_config.h
@@ -170,7 +170,7 @@
/* allow application to set server identity (for MMS identity service) at runtime */
#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1
-/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
+/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1
/* overwrite default results for MMS identify service */
diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake
index 437d87d9..adfeca96 100644
--- a/config/stack_config.h.cmake
+++ b/config/stack_config.h.cmake
@@ -160,7 +160,7 @@
/* allow application to set server identity (for MMS identity service) at runtime */
#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1
-/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
+/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1
/* default results for MMS identify service */
diff --git a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs
index 818276c4..980d9ceb 100644
--- a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs
@@ -1,7 +1,7 @@
/*
* IEC61850CommonAPI.cs
*
- * Copyright 2014-2017 Michael Zillgith
+ * Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -454,6 +454,9 @@ namespace IEC61850
SetByMmsUtcTime (mmsUtcTime);
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
public Timestamp()
{
self = Timestamp_create ();
@@ -461,6 +464,19 @@ namespace IEC61850
responsibleForDeletion = true;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Timestamp(Timestamp other) : this()
+ {
+ SetTimeInSeconds (other.GetTimeInSeconds ());
+ SetSubsecondPrecision (other.GetSubsecondPrecision ());
+ SetFractionOfSecondPart (other.GetFractionOfSecondPart ());
+ SetLeapSecondKnow(other.IsLeapSecondKnown());
+ SetClockFailure(other.HasClockFailure());
+ SetClockNotSynchronized(other.IsClockNotSynchronized());
+ }
+
public Timestamp(byte[] value)
{
self = Timestamp_createFromByteArray (value);
diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
index 97adae02..8a0c5bf3 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
@@ -1874,6 +1874,9 @@ namespace IEC61850
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_getInterlockCheck(IntPtr self);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ControlAction_getT(IntPtr self);
+
private IntPtr self;
private IedServer.ControlHandlerInfo info;
private IedServer iedServer;
@@ -2003,6 +2006,18 @@ namespace IEC61850
{
return ControlAction_getInterlockCheck(self);
}
+
+ ///
+ /// Gets the time (paramter T) of the control action
+ ///
+ public Timestamp GetT()
+ {
+ IntPtr tPtr = ControlAction_getT(self);
+
+ Timestamp t = new Timestamp(tPtr, false);
+
+ return new Timestamp(t);
+ }
}
public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h
index d33d1a00..6dc69524 100644
--- a/src/iec61850/inc/iec61850_server.h
+++ b/src/iec61850/inc/iec61850_server.h
@@ -1449,7 +1449,7 @@ LIB61850_API DataObject*
ControlAction_getControlObject(ControlAction self);
/**
- * \brief Gets the time of the control, if it's a timeActivatedControl, returns 0, if it's not.
+ * \brief Gets the time of the control (attribute "operTm"), if it's a timeActivatedControl, returns 0, if it's not.
*
* \param self the control action instance
*
@@ -1458,6 +1458,16 @@ ControlAction_getControlObject(ControlAction self);
LIB61850_API uint64_t
ControlAction_getControlTime(ControlAction self);
+/**
+ * \brief Gets the time (attribute "T") of the last received control action (Oper or Select)
+ *
+ * \param self the control action instance
+ *
+ * \return the time of the last received control action
+ */
+LIB61850_API Timestamp*
+ControlAction_getT(ControlAction self);
+
/**
* \brief Control model callback to perform the static tests (optional).
*
diff --git a/src/iec61850/inc_private/control.h b/src/iec61850/inc_private/control.h
index 66cb6706..adf56731 100644
--- a/src/iec61850/inc_private/control.h
+++ b/src/iec61850/inc_private/control.h
@@ -1,7 +1,7 @@
/*
* control.h
*
- * Copyright 2013-2019 Michael Zillgith
+ * Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -90,6 +90,8 @@ struct sControlObject
MmsValue* origin;
MmsValue* timestamp;
+ Timestamp T;
+
MmsValue* ctlNumSt;
MmsValue* originSt;
diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c
index 34fb18d5..07ef4324 100644
--- a/src/iec61850/server/mms_mapping/control.c
+++ b/src/iec61850/server/mms_mapping/control.c
@@ -169,6 +169,7 @@ getCancelParameterTest(MmsValue* operParameters)
return NULL;
}
+/* access the MmsValue of Oper.T or SBOw.T */
static MmsValue*
getOperParameterTime(MmsValue* operParameters)
{
@@ -182,7 +183,7 @@ getOperParameterTime(MmsValue* operParameters)
timeParameter = MmsValue_getElement(operParameters, 3);
}
- if (timeParameter != NULL)
+ if (timeParameter)
if ((MmsValue_getType(timeParameter) == MMS_UTC_TIME) || (MmsValue_getType(timeParameter) == MMS_BINARY_TIME))
return timeParameter;
@@ -2125,6 +2126,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char
MmsValue* origin = getOperParameterOrigin(value);
MmsValue* check = getOperParameterCheck(value);
MmsValue* test = getOperParameterTest(value);
+ MmsValue* t = getOperParameterTime(value);
if (checkValidityOfOriginParameter(origin) == false)
{
@@ -2139,6 +2141,11 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char
goto free_and_return;
}
+ if (t)
+ {
+ Timestamp_fromMmsValue(&(controlObject->T), t);
+ }
+
int state = getState(controlObject);
uint64_t currentTime = Hal_getTimeInMs();
@@ -2270,6 +2277,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char
goto free_and_return;
}
+ Timestamp_fromMmsValue(&(controlObject->T), timeParameter);
+
if (checkValidityOfOriginParameter(origin) == false)
{
indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
@@ -2690,4 +2699,12 @@ ControlAction_getControlTime(ControlAction self)
return controlObject->operateTime;
}
+Timestamp*
+ControlAction_getT(ControlAction self)
+{
+ ControlObject* controlObject = (ControlObject*) self;
+
+ return &(controlObject->T);
+}
+
#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */