Server - User defined timestamp clock source
Dieses Beispiel zeigt die Implementierung einer benutzerdefinierten Uhr/Uhrzeit für Zeitstempelungsaufgaben in einem TwinCAT IEC 61850 Server Projekt. Die spontan vom Server benötigte Uhrzeit kann von einer externen Quelle (z.B. von einer GPS-Uhr) vorgegeben werden. Eine aktuelle Uhrzeit wird unter anderen beim Versenden der Reports benötigt.
Download TwinCAT XAE Project (*.zip): Sample25.zip
Das hier beschriebene Beispiel nutzt die Statemachine, welche in dem Kapitel „Allgemeine Server - Projektstruktur“ beschrieben ist. Die States: 0,1,10 und 100 sind identisch zu der dort beschriebenen Statemachine. Andere States wurden für das Beispiel modifiziert oder auch neue States hinzugefügt.
Die TwinCAT IEC 61850 Server-Implementierung verwendet die Schnittstelle: „I_ScsmSystemClockEventSink“ um die Uhrzeit für Zeitstempelungsaufgaben abzufragen. Es ist möglich einen eigenen Funktionsbaustein zu verwenden, der diese spezielle Schnittstelle implementiert und den Server-Baustein so konfiguriert, dass dieser die neue Zeitquelle verwendet. In der globalen Variablenliste „TcTelecontrol“ wird dem Server-Baustein durch das Setzen der Eigenschaft „ipSystemClock:=fbMyClock“ die neue Zeitquelle zugewiesen. Der Server-Baustein wird danach jedes Mal die Methode „OnGetSystemTime“ aufrufen, wenn von ihm ein neuer Zeitstempel benötigt wird. In unserem Beispiel ist es dann z.B. der Fall, wenn Reporting aktiv ist und ein neuer Report gesendet werden soll. Zur Demonstrationszwecken wird die externe Zeitquelle auch abgefragt, wenn das Attribut: "IEDLD1/MMXU1.TotW" oder "IEDLD1/MMXU1.TotW.t" vom Client gelesen wird. Immer dann, wenn Client die Daten liest, wird die Methode: „OnGetVPreEvent“ des Bausteins: „FB_DO_IED_LD1_MMXU1_TotW“ aufgerufen. In der Implementierung der Methode wird über den Schnittstellenzeiger: „ipAssociation“ die Methode: „GetSystemTime(tT=>tT)“ aufgerufen. Diese Uhrzeitabfrage wird an unsere benutzerdefinierte Uhr weitergeleitet.
Die Client-Server-Kommunikation verwendet Zeitstempel in mindestens zwei verschiedenen Formaten: UTC-Time und Binary-Time. Aus diesem Grund liefert diese Methode den Zeitstempel als Ausgangsvariablen in diesen zwei Formaten.
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_VARDas Beispiel demonstriert eine sehr einfache Software-Uhr, die mit Hilfe des RTC_EX-Funktionsbausteins realisiert wurde.
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_VARMETHOD FINAL Execute : BOOL
VAR_INPUT
END_VARrefreshTimer();
IF Execute:=refreshTimer.Q THEN
refreshTimer(IN:=FALSE); refreshTimer(IN:=TRUE);
Update();
END_IFMETHOD 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_VARtT:=_tT;
tB:=_tB;
OnGetSystemTime:=TRUE;METHOD FINAL SetClock
VAR_INPUT
tSet : DT;(* New time to set *)
END_VARclock(EN:=FALSE);
clock(EN:=TRUE, PDT:=tSet);
_tT.quality.ClockNotSynchronized:=FALSE;
Update();METHOD FINAL Update
VAR_INPUT
END_VARclock();(* 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);