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_VAR

Das 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_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);