Server - User defined timestamp clock source

This sample shows the implementation of a user-defined clock/time for time stamping tasks in a TwinCAT IEC 61850 server project. The spontaneous time required by the server can be specified from an external source (e.g. from a GPS clock). Among other things, a current time is required when sending reports.

Download TwinCAT XAE Project (*.zip): Sample25.zip

The example described here uses the state machine that is described in the "General Server project structure" chapter. The States 0, 1, 10 and 100 are identical to the state machine described there. Other states were modified for the example or new states were also added.

The TwinCAT IEC 61850 server implementation uses the interface: "I_ScsmSystemClockEventSink" to query the time for time stamping tasks. It is possible to use a custom function block that implements this special interface and configures the server function block to use the new time source. In the Global Variable List "TcTelecontrol", the new time source is assigned to the server function block by setting the property "ipSystemClock:=fbMyClock". The server function block will then call the method: "OnGetSystemTime" each time it needs a new time stamp from it. In our sample, this is the case when reporting is active and a new report is to be sent. For demonstration purposes, the external time source is also queried when the attribute: "IEDLD1/MMXU1.TotW" or "IEDLD1/MMXU1.TotW.t" is read by the client. Whenever client reads the data, the method: "OnGetVPreEvent" of the function block: "FB_DO_IED_LD1_MMXU1_TotW" is called. In the method implementation the method: "GetSystemTime(tT=>tT)" is called via the interface pointer: "ipAssociation". This time request is forwarded to our custom clock.

The client-server communication uses time stamps in at least two different formats: UTC time and binary time. For this reason, this method returns the time stamp as output variables in these two formats.

VAR_GLOBAL
    ipCreator: I_AcsiCodeCreatorClass := GVL_AcsiVars.Creator.SetCodeRev(codeRev:=3).SetGuiVer(major:=1, minor:=1, build:=94, revision:=4);
    fbIED: FB_IED_IED;
    fbMyClock : FB_MyClock;
    fbIEDServer: FB_iec61850ServerClass := (ipIED:=fbIED, ipSystemClock:=fbMyClock, settings:=(bEnable:=TRUE, sLocalHost:='127.0.0.1'));
    fbIEDServerSession1: FB_IEDServerSession := (fbConnection:=(ipServer:=fbIEDServer, settings:=(bEnable:=TRUE)));
END_VAR

The sample demonstrates a very simple software clock, which was implemented with the help of the RTC_EX function block.

FUNCTION_BLOCK FB_MyClock IMPLEMENTS I_ScsmSystemClockEventSink
VAR
    _tT : T_UtcTime:=(secondSinceEpoch:=DT#2021-04-01-00:00:00, quality:=(ClockNotSynchronized:=TRUE, ClockFailure:=FALSE, LeapSecondsKnown:=FALSE), fractionOfSecond:=[0,0,0]);(* Actual UTC time. *)
    _tB : T_BinaryTime;(* Actual binary-time (EntryTime) *)
    clock : RTC_EX:=(EN:=TRUE, PDT:=DT#2021-04-01-00:00:00, PMSEK:=0);
    refreshTimer : TON:=(IN:=TRUE, PT:=T#1S);
END_VAR
METHOD FINAL Execute : BOOL
VAR_INPUT
END_VAR
refreshTimer();
IF Execute:=refreshTimer.Q THEN
    refreshTimer(IN:=FALSE); refreshTimer(IN:=TRUE);
    Update();
END_IF
METHOD FINAL OnGetSystemTime : BOOL
VAR_INPUT
    ipAA: I_ScsmAssociationClass;(* Application association. If = 0 => optional or unknown. *)
END_VAR
VAR_OUTPUT
    tT: T_UtcTime;(* UTC-time. *)
    tB: T_BinaryTime;(* Binary-time *)
END_VAR
tT:=_tT;
tB:=_tB;
OnGetSystemTime:=TRUE;
METHOD FINAL SetClock
VAR_INPUT
    tSet : DT;(* New time to set *)
END_VAR
clock(EN:=FALSE);
clock(EN:=TRUE, PDT:=tSet);
_tT.quality.ClockNotSynchronized:=FALSE;
Update();
METHOD FINAL Update
VAR_INPUT
END_VAR
clock();(* update clock time *)

(* convert to utc-time format *)
_tT.secondSinceEpoch:=clock.CDT;
_tT.fractionOfSecond:=LTIME_TO_UtcTimeFractionofSecond(in:=TIME_TO_LTIME(DWORD_TO_TIME(clock.CMSEK)));
Accuracy_To_UtcTimeQualityAccuracy(in:=E_UtcTimeAccuracy._03, bAccuracy0=>_tT.quality.Accuracy0, bAccuracy1=>_tT.quality.Accuracy1, bAccuracy2=>_tT.quality.Accuracy2, bAccuracy3=>_tT.quality.Accuracy3, bAccuracy4=>_tT.quality.Accuracy4);

(* convert to binary-time format *)
_tB.day:=Date_To_BinaryTime6Day(in:=DT_TO_DATE(clock.CDT));
_tB.timeOfDay:=DT_TO_TOD(clock.CDT) + DWORD_TO_TIME(clock.CMSEK);