From a3a6f2c77fef7c21e89981bb3f9cbb2dfd87a052 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 21 Nov 2022 16:56:00 +0000 Subject: [PATCH] - IED Server: added function to set time quality for internally updated times (LIB61850-372) --- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 15 ++++++++++++ .../server_example_sg.c | 7 +++++- src/iec61850/inc/iec61850_server.h | 17 ++++++++++++-- src/iec61850/inc_private/ied_server_private.h | 2 ++ src/iec61850/server/impl/ied_server.c | 23 +++++++++++++++++-- src/iec61850/server/mms_mapping/control.c | 9 ++++---- src/iec61850/server/mms_mapping/logging.c | 2 +- src/iec61850/server/mms_mapping/mms_goose.c | 4 ++-- src/iec61850/server/mms_mapping/mms_mapping.c | 6 ++--- src/iec61850/server/mms_mapping/reporting.c | 2 +- src/mms/inc/mms_value.h | 20 +++++++++++++++- src/mms/iso_mms/common/mms_value.c | 20 +++++++++++++--- 12 files changed, 106 insertions(+), 21 deletions(-) diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 5c6a40d6..aefd1c7b 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -2273,6 +2273,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision); + private IntPtr self = IntPtr.Zero; private InternalControlHandler internalControlHandlerRef = null; @@ -2996,6 +2999,18 @@ namespace IEC61850 } } + /// + /// Set the time quality for all timestamps internally generated by this IedServer instance + /// + /// set/unset leap seconds known flag + /// set/unset clock failure flag + /// set/unset clock not synchronized flag + /// set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp) + public void SetTimeQuality(bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision) + { + IedServer_setTimeQuality(self, leapSecondKnown, clockFailure, clockNotSynchronized, subsecondPrecision); + } + } } diff --git a/examples/server_example_setting_groups/server_example_sg.c b/examples/server_example_setting_groups/server_example_sg.c index 4466c1ad..48f8c4d5 100644 --- a/examples/server_example_setting_groups/server_example_sg.c +++ b/examples/server_example_setting_groups/server_example_sg.c @@ -97,7 +97,10 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu { void* securityToken = ClientConnection_getSecurityToken(connection); - printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject->name); + if (dataObject) + printf("Read access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject->name, FunctionalConstraint_toString(fc)); + else + printf("Read access to %s/%s[%s]\n", ld->name, ln->name, FunctionalConstraint_toString(fc)); return DATA_ACCESS_ERROR_SUCCESS; } @@ -112,6 +115,8 @@ main(int argc, char** argv) iedServer = IedServer_createWithConfig(&iedModel, NULL, config); + IedServer_setTimeQuality(iedServer, true, false, false, 10); + IedServerConfig_destroy(config); LogicalDevice* ld = IEDMODEL_PROT; diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index b0097e3f..d206b904 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -687,6 +687,21 @@ IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcb LIB61850_API void IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag); +/** + * \brief Set the time quality for all timestamps internally generated by this IedServer instance + * + * You can call this function during the initialization of the server or whenever a time quality + * flag has to be updated (on clock failure or change of time synchronization state). + * + * \param self the instance of IedServer to operate on. + * \param leapSecondKnown set/unset leap seconds known flag + * \param clockFailure set/unset clock failure flag + * \param clockNotSynchronized set/unset clock not synchronized flag + * \param subsecondPrecision set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp) + */ +LIB61850_API void +IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision); + /**@}*/ /** @@ -710,8 +725,6 @@ IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, LIB61850_API void IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter); - - /** * \brief get the peer address of this connection as string * diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index 14a694b1..e6f6e412 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -78,6 +78,8 @@ struct sIedServer uint8_t edition; + uint8_t timeQuality; /* user settable time quality for internally updated times */ + bool running; }; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 70527812..9416d200 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -659,6 +659,7 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio } #endif + IedServer_setTimeQuality(self, true, false, false, 10); } else { IedServer_destroy(self); @@ -1479,7 +1480,7 @@ IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribu #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif - MmsValue_setUtcTimeMs(dataAttribute->mmsValue, value); + MmsValue_setUtcTimeMsEx(dataAttribute->mmsValue, value, self->timeQuality); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->dataModelLock); #endif @@ -1882,7 +1883,6 @@ private_IedServer_removeClientConnection(IedServer self, ClientConnection client #endif } - void IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId) { @@ -1890,3 +1890,22 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId) self->mmsMapping->gooseInterfaceId = StringUtils_copyString(interfaceId); #endif } + +void +IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision) +{ + uint8_t timeQuality = 0; + + if (clockNotSynchronized) + timeQuality += 0x20; + + if (clockFailure) + timeQuality += 0x40; + + if (leapSecondKnown) + timeQuality += 0x80; + + timeQuality += (subsecondPrecision & 0x1f); + + self->timeQuality = timeQuality; +} diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index dc2ae540..ba7c5a64 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -258,7 +258,7 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject MmsValue_update(trkInst->ctlNum->mmsValue, controlObject->ctlNum); if (trkInst->operTm) - MmsValue_setUtcTimeMs(trkInst->operTm->mmsValue, controlObject->operateTime); + MmsValue_setUtcTimeMsEx(trkInst->operTm->mmsValue, controlObject->operateTime, self->iedServer->timeQuality); if (trkInst->respAddCause) MmsValue_update(trkInst->respAddCause->mmsValue, controlObject->addCause); @@ -394,7 +394,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, errVal); @@ -590,9 +590,7 @@ setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs) if (self->tOpOk) { MmsValue* timestamp = self->tOpOk->mmsValue; - MmsValue_setUtcTimeMs(timestamp, currentTimeInMs); - - /* TODO update time quality */ + MmsValue_setUtcTimeMsEx(timestamp, currentTimeInMs, self->iedServer->timeQuality); } self->pendingEvents |= PENDING_EVENT_OP_OK_TRUE; @@ -862,6 +860,7 @@ executeStateMachine: MmsValue* operTm = getOperParameterOperTime(controlObject->oper); MmsValue_setUtcTime(operTm, 0); + MmsValue_setUtcTimeQuality(operTm, self->iedServer->timeQuality); } else { MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId, diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index 6fc8f7b4..e841feb2 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -418,7 +418,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, LogControl* logControl, IEC6 MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index a5352b00..5aab2585 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -1,7 +1,7 @@ /* * mms_goose.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -231,7 +231,7 @@ updateGenericTrackingObjectValues(MmsGooseControlBlock gc, IEC61850_ServiceType MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), gc->mmsMapping->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 3a716a27..fc080ea2 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -693,7 +693,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, SettingGroupControlBlock* sg MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, @@ -840,7 +840,7 @@ MmsMapping_changeActiveSettingGroup(MmsMapping* self, SettingGroupControlBlock* MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue_setUint8(actSg, sgcb->actSG); - MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) copySGCBValuesToTrackingObject(self, sgcb); @@ -2720,7 +2720,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue_setUint8(actSg, sg->sgcb->actSG); - MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality); } else retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index c44c2791..3b170a54 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -579,7 +579,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ReportControl* rc, IEC61850_ MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, diff --git a/src/mms/inc/mms_value.h b/src/mms/inc/mms_value.h index e67bccb2..206345c4 100644 --- a/src/mms/inc/mms_value.h +++ b/src/mms/inc/mms_value.h @@ -498,12 +498,30 @@ MmsValue_getUtcTimeInMsWithUs(const MmsValue* self, uint32_t* usec); * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time) * * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME. - * * \param timeQuality the byte representing the time quality */ LIB61850_API void MmsValue_setUtcTimeQuality(MmsValue* self, uint8_t timeQuality); +/** + * \brief Update an MmsValue object of type MMS_UTCTIME with a millisecond time. + * + * Meaning of the bits in the timeQuality byte: + * + * bit 7 = leapSecondsKnown + * bit 6 = clockFailure + * bit 5 = clockNotSynchronized + * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time) + * + * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME. + * \param timeval the new value in milliseconds since epoch (1970/01/01 00:00 UTC) + * \param timeQuality the byte representing the time quality + * + * \return the updated MmsValue instance + */ +LIB61850_API MmsValue* +MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality); + /** * \brief get the TimeQuality byte of the UtcTime * diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c index 5d80a630..29e96e4d 100644 --- a/src/mms/iso_mms/common/mms_value.c +++ b/src/mms/iso_mms/common/mms_value.c @@ -763,8 +763,8 @@ MmsValue_setUtcTime(MmsValue* self, uint32_t timeval) return self; } -MmsValue* -MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) +static void +setUtcTimeMs(MmsValue* self, uint64_t timeval, uint8_t timeQuality) { uint32_t timeval32 = (timeval / 1000LL); @@ -786,7 +786,21 @@ MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) valueArray[6] = (fractionOfSecond & 0xff); /* encode time quality */ - valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */ + valueArray[7] = timeQuality; +} + +MmsValue* +MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) +{ + setUtcTimeMs(self, timeval, 0x0a); /* set quality as 10 bit sub-second time accuracy */ + + return self; +} + +MmsValue* +MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality) +{ + setUtcTimeMs(self, timeval, timeQuality); return self; }