Allgemeine Client - Projektstruktur
Jedes der Client-Beispielprojekte folgt einer allgemeinen Struktur, welche in diesem Dokument erläutert werden soll. Zu dieser Struktur gehört die Verschachtelung der Funktionsbausteine im Solution-Explorer und der Aufbau der Statemachine im Client-Baustein, der die Verbindung zum Server herstellt und den Datenaustausch realisiert. Die Grundstruktur der TwinCAT IEC 61850 Beispielprojekte basiert auf der automatisch vom TwinCAT Telecontrol Configurator generierten SPS-Projektstruktur. Der TwinCAT Telecontrol Configurator verwendet bei der Generierung der Solution wiederum das „TwinCAT XAE Project (XML format)“ als Vorlage. Die in den Beispielen kommunizierten Datenmodelle spiegeln unterschiedliche IEC 61850 Server wider. Sie unterscheiden sich von Beispiel zu Beispiel und liegen dem Quellcode als icd-Dateien bei. Die ICD-Dateien können auch von einer Third-Party-Software zur Simulation eines Servers verwendet werden.
Zuerst eine kurze Betrachtung des Aufbaus eines TwinCAT IEC 61850 SPS-Client-Projekts:
Der generierte Solution-Name (wenn nicht anders angegeben) entspricht dem TwinCAT Telecontrol Configurator Projektnamen. Der automatisch generierte TwinCAT SPS-Projektname (wenn nicht anders angegeben) hat wiederum folgenden Aufbau: „[Projektname]_[IEDName]_Client“.
Standardmäßig besitzt jedes Beispielprojekt einen „DUTs“, „GVLs“ und „POUs“ Ordner. In dem „GVLs“-Ordner ist eine globale Variablenliste (GVL) mit dem Namen: „TcTelecontrol“ hinterlegt. In dieser globalen Variablenliste (siehe Quellcode unten) werden folgende Funktionsbausteine instanziiert und initialisiert:
- Eine Client-Bausteininstanz des Typs: FB_[IEDName]Client (Verbindungsmanagement und Datenaustausch).
- Eine IED-Datenmodell-Bausteininstanz des Typs: FB_IED_[IEDName].
- Optional (abhängig von der GOOSE-Subscriber-Konfiguration): Eine oder mehrere Bausteininstanzen des Typs: „FB_[IEDName]Gse“ für GOOSE-Kommunikation und GSE-Management.
Außerdem wird dort auch die während der Codegenerierung verwendete Codeversion und die verwendete Version des TwinCAT Telecontrol Configurator abgelegt.
Namensraum: TcTelecontrol
Typ: Globale Variablenliste (GVL)
VAR_GLOBAL
ipCreator : I_AcsiCodeCreatorClass := GVL_AcsiVars.Creator.SetCodeRev(codeRev:=2).SetGuiVer(major:=1, minor:=0, build:=93, revision:=10);
fb[IEDName] : FB_IED_[IEDName];
fb[IEDName]Client : FB_[IEDName]Client := (fbConnection:=(ipIED:=fb[IEDName],settings:=(sRemoteHost:='127.0.0.1')));
fb[IEDName]Gse : FB_[IEDName]Gse := (fbAdapter:=(ipIED:=fb[IEDName], settings:=(sMulticastAddr:='01-0C-CD-01-00-00', eDispatchMode:=E_GseDispatchMode.NonPromiscuous)));
END_VAR
Im „POUs“-Ordner befindet sich ein weiterer Ordner: „[IEDName]“, der die gesamte hierarchische Struktur des IED-Datenmodels als Funktionsbausteine beinhaltet. In diesem Ordner befindet sich auch der IED-Baustein, der in der globalen Variablenliste instanziiert wird und bereits erwähnt wurde. Auf der gleichen Ebene liegt der Client-Baustein: „FB_[IEDName]Client“, welcher den Verbindungsaufbau und den Datenaustausch mit einem IEC 61850 Server realisiert. Das TwinCAT SPS-Projekt enthält standardmäßig ein „MAIN“-Programm. Dieses wird zyklisch von einer TwinCAT Task aufgerufen und ruft wiederum das Programm „P_IEC61850MAIN“ auf. Das Programm „P_IEC61850MAIN“ kapselt den Aufruf des Client-Bausteins und des optionalen GSE-Bausteins, trennt die IEC 61850-Kommunikation von dem restlichen SPS-Maschinenprogramm und hilft z. B. bei der Implementierung weiterer Clients.
PROGRAM MAIN
VAR
END_VAR
P_IEC61850MAIN();
PROGRAM P_IEC61850MAIN
VAR
END_VAR
fb[IEDName]Client();
fb[IEDName]Gse();
In dem FB_[IEDName]Client-Baustein befindet sich eine Statemachine, von der die grundlegenden Zustände in jedem Client-Beispiel genutzt werden. Diese Zustände sind im folgenden Diagramm grafisch dargestellt:
State 0 (Init state): Die Statemachine befindet sich in diesem Zustand, sobald das SPS-Programm gestartet wurde. Hier (und im State 1) werden Befehle zur Verwaltung der Client-Server-Verbindung behandelt. Gesteuert wird dies hauptsächlich über vier boolesche Variablen. Diese Variablen, wenn sie gesetzt sind, aktivieren die entsprechenden Befehle (in diesem Fall sind es einmalige Methodenaufrufe am Client-Baustein).
- _bAbort: Ruft die Methode „AbortReq“ auf, welche den Befehl zum Abbruch der Client-Verbindung zum Server aktiviert.
- _bConnect: Ruft die Methode „AssociateReq“ auf, welche den Befehl zum Aufbau einer neuen Client-Verbindung zum Server aktiviert.
- _bDisconnect: Ruft die Methode „ReleaseReq“ auf, welche den Befehl zum geregelten Abbau einer bestehenden Client-Verbindung zum Server aktiviert.
- _bReconnect: Ruft ebenfalls die Methode „AssociateReq“ auf, falls die Client-Verbindung zum Server abgebrochen/getrennt wurde, aber automatisch wiederhergestellt werden soll.
Die zuvor aufgezählten Methoden, welche in diesem State einmalig aufgerufen werden, benötigen für die Abarbeitung länger als einen SPS-Zyklus. Aus diesem Grund wechselt die Statemachine in einen Warte-Zustand (State 1), in dem auf die Beendigung des aktivierten Befehls gewartet wird.
Falls die Client-Verbindung zum Server zuvor bereits hergestellt wurde, wird die Statemachine in den Zustand zur Datenübertragung wechseln (State 10).
State 1 (Wait State): In diesem Zustand verweilt der Client bis die Befehlsabarbeitung zur Verwaltung der Client-Server-Verbindung nicht mehr beschäftigt (busy) ist. Solange die Verbindung hergestellt, abgebaut oder abgebrochen wird, befindet sich die Statemachine im State 1. Sobald der Befehl erfolgreich abgearbeitet wurde, wird die Statemachine zurück in den State 0 (Init State) versetzt.
State 10 (Data exchange): Wenn die Statemachine in diesem Zustand ist, dann ist die Client-Verbindung zum Server bereits erfolgreich hergestellt worden. Der Client ist bereit für die Datenübertragung zum Server. Während der Datenübertragung werden Befehle zum Versenden oder Empfangen der Daten aktiviert.
In diesem Zustand unterscheiden sich die Beispielprojekte untereinander. Abhängig von der gewünschten Funktionalität oder Logik in der Applikation werden hier unterschiedliche Methoden oder Hilfsbausteine aufgerufen. Außerdem kann der Client-Baustein um weitere States erweitert werden.
Sämtliche Methodenaufrufe, die Befehle zur Datenübertragung aktivieren, benötigen für eine erfolgreiche Abarbeitung mehrere SPS-Zyklen, daher muss die Statemachine, nach dem Aufruf einer solchen Methode, in den State 11 (Wait state) versetzt werden.
Bei einer aktiven Verbindung und keiner Datenübertragung wechselt die Statemachine zwischen dem State 0 und 10. Die Statemachine wird wieder in den State 0 versetzt, um auf Änderungen im Client-Server-Verbindungstatus zu reagieren und sie im State 0 zu behandeln.
State 11 (Wait state): Dieser Zustand ist ein weiterer Warte-Zustand. Sobald ein Befehl zur Datenübertragung (aktiviert im State 10) abgearbeitet wurde, wird die Statemachine in den State 11 versetzt und wartet bis die Befehlsabarbeitung nicht mehr beschäftigt (busy) ist. Danach wechselt die Statemachine in den State 0.
State 100 (Error state): Sobald ein Fehler bei der Aktivierung oder Abarbeitung eines Befehls auftritt, wird die Statemachine in den State 100 versetzt. Hier wird der Fehler geloggt und die Statemachine zurück in den State 0 versetzt.
FUNCTION_BLOCK FB_[IEDName]Client
VAR_INPUT
fbConnection : FB_iec61850ClientClass;
END_VAR
VAR
_bAbort : BOOL;
_bDisconnect : BOOL;
_bConnect : BOOL;
_bReconnect : BOOL := TRUE;
_bReadAllData : BOOL := TRUE;
state : BYTE;
eState : E_AsyncEnvironmentState;
bBusy : BOOL;
bSuccess : BOOL;
ipResult : I_AsyncServiceResultClass;
sLastErrorResult : T_MaxString;
fbAbortReason : FB_ServiceErrorClass := (stError:=SUCCESS_EVENT);
sLastAbortReason : T_MaxString;
nInvokeID : UDINT;
eServiceError : E_AcsiServiceError;
nServiceError : UDINT;
nCmdError : UDINT;
sObjReference : T_AcsiObjectReference;
sCtrlReference : T_AcsiObjectReference;
bGetServerDirectory : BOOL := TRUE;
bGetLogicalDeviceDirectory: BOOL := TRUE;
bGetLogicalNodeDirectory : BOOL := TRUE;
bGetAllServerValues : BOOL := TRUE;
bGetAllServerValues : BOOL := TRUE;
bGetAllDataValues_LLN0_ST : BOOL := TRUE;
bGetDataValues_LLN0_ST_Beh: BOOL := TRUE;
END_VAR
fbConnection.Execute();
eState:= fbConnection.eState;
CASE state OF
0:
IF _bAbort THEN
_bAbort:= FALSE;
bSuccess:= fbConnection.AbortReq(ipReason:=fbAbortReason, ipSink:=0, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 1);
ELSIF eState = E_AsyncEnvironmentState.Idle AND (_bConnect OR _bReconnect) THEN
_bConnect:= FALSE;
bGetAllServerValues:= SEL(_bReadAllData, bGetAllServerValues, TRUE);
bSuccess:= fbConnection.AssociateReq(ipSink:=0, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 1);
ELSIF eState = E_AsyncEnvironmentState.Established AND _bDisconnect THEN
_bDisconnect:= FALSE;
bSuccess:= fbConnection.ReleaseReq(ipSink:=0, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 1);
ELSIF eState = E_AsyncEnvironmentState.Established THEN
state:= 10;
END_IF
_bConnect:= FALSE;
_bDisconnect:= FALSE;
1:
IF ipResult <> 0 THEN
ipResult.Execute();
IF NOT (bBusy:=ipResult.IsBusy()) THEN
state:= SEL(ipResult.IsCompleted(), 100, 0);
END_IF
END_IF
10:
IF bGetServerDirectory THEN
bGetServerDirectory:= FALSE;
bSuccess:= fbConnection.GetServerDirectoryReq(ipServer:=fb[IEDName], eClass:=E_AcsiServerDirectoryClass.LogicalDevice, hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
ELSIF bGetLogicalDeviceDirectory THEN
bGetLogicalDeviceDirectory:= FALSE;
bSuccess:= fbConnection.GetLogicalDeviceDirectoryReq(ipLogicalDevice:=fb[IEDName].IEDLD1, hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
ELSIF bGetLogicalNodeDirectory THEN
bGetLogicalNodeDirectory:= FALSE;
bSuccess:= fbConnection.GetLogicalNodeDirectoryReq(ipLogicalNode:=fb[IEDName].IEDLD1.LLN0, eClass:=E_AcsiLogicalNodeClass.DataSet, hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
ELSIF bGetAllServerValues THEN
bGetAllServerValues:= FALSE;
bSuccess:= fbConnection.GetAllServerValuesReq(ipServer:=fb[IEDName], hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
ELSIF bGetAllDataValues_LLN0_ST THEN
bGetAllDataValues_LLN0_ST:= FALSE;
bSuccess:= fbConnection.GetAllDataValuesReq(ipLogicalNode:=fb[IEDName].IEDLD1.LLN0, eFc:=E_AcsiFc.ST_, hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
ELSIF bGetDataValues_LLN0_ST_Beh THEN
bGetDataValues_LLN0_ST_Beh:= FALSE;
bSuccess:= fbConnection.GetDataValuesReq(ipData:=fb[IEDName].IEDLD1.LLN0.Beh, eFc:=E_AcsiFc.ST_, hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
ELSE
state:= 0;
END_IF
11:
IF ipResult <> 0 THEN
ipResult.Execute();
IF NOT (bBusy:=ipResult.IsBusy()) THEN
state:= SEL(ipResult.IsCompleted(), 100, 0);
END_IF
END_IF
100:
state:= 0;
IF ipResult <> 0 THEN
nCmdError:= nCmdError + 1;
sLastErrorResult:= ipResult.Dump();
END_IF
END_CASE
GOOSE-Subscriber (optional)
TwinCAT Telecontrol Configurator kann in einem Client-Projekt, während der SPS-Code-Generierung den SPS-Code für einen GOOSE-Subscriber ebenfalls generieren (siehe Codebeispiel unten). Dies setzt voraus, dass der Anwender vorher die GOOSE-Komponenten wie z.B. GoCBs (goose control blocks) im TwinCAT Telecontrol Configurator erstellt oder aus einer SCL-Datei (z. B. ICD-Datei) importiert hat.
Standardmäßig wird bei der Codegenerierung ein Baustein mit dem Namen: „FB_[IEDName]Gse“ instanziiert und der globalen Variablenliste „TcTelecontrol“ hinzugefügt. Dieser Baustein stellt die Verbindung zwischen einem Netzwerkadapter des TwinCAT Steuerungsrechners, dem IED-Datenmodell und der GOOSE-Konfiguration in den GoCBs her. Die GoCBs werden in dem IED-Datenmodell (normalerweise im LLN0) instanziiert. Jeder GoCB besitzt ein Baustein-Unterelement mit dem Namen: „Subscriber“. Über die „Subscriber“-Methodenaufrufe kann die Subscription aus dem SPS-Code aktiviert („Enable“) bzw. deaktiviert werden („Disable“). Defaultmäßig wird die Subscription beim SPS-Programmstart für alle GoCBs aktiviert. Dies wird durch die mit „TRUE“ initialisierte „bSubscriber“-Variable gesteuert. Über eine steigende Flanke an der „bUnsubscribe“-Variablen kann die Subscription für alle GoCBs deaktiviert werden. Die über diese Methoden abgesetzten Subscriber-Befehle werden sofort ausgeführt, ohne Wartezyklen oder weitere States die nötig sind, um die Befehlsabarbeitung abzuschließen.
Der hier beschriebene „Subscriber“ liest die Konfiguration und aktualisiert zwar den Status des GoCBs (Attribute „GoEna“ wird z.B. auf „TRUE“ oder „FALSE“ gesetzt), er nutzt aber nicht die Client-Server-Dienste wie „SetGoCBValues“ oder „GetGoCBValues“, um den „Publisher“ auf der Serverseite zu aktivieren bzw. zu deaktivieren. D.h. der generierte Code implementiert bereits einen Subscriber der z. B. im ersten SPS-Zyklus oder aus dem SPS-Code zum beliebigen Zeitpunkt aktiviert bzw. deaktiviert werden kann. Die benötigten GoCB-Konfigurationseinstellungen (GoCB-Attributwerte) können über Initialisierungswerte vorgenommen werden. Die Konfiguration der GoCBs kann aber bereits in dem TwinCAT Telecontrol Configurator geschehen. Die Initialisierungswerte werden während der Codegenerierung automatisch generiert und zugewiesen. Wenn der Subscriber aktiviert wurde und die Konfiguration des GoCBs und des Netzwerkadapters zu dem empfangenden GOOSE-Frame eine Übereinstimmung ergibt, werden die GOOSE-Daten ins TwinCAT IED-Datenmodell umkopiert (gemappt). Die „Execute“-Methode muss in der übrigen Zeit zyklisch aufgerufen werden. Sie ist unter anderem für die Aktualisierung der Statusinformation im GoCB verantwortlich.
FUNCTION_BLOCK FB_[IEDName]Gse IMPLEMENTS I_GseLinkStatusEventSink
VAR_INPUT
fbAdapter : FB_GseAdapterClass := (ipLinkStatus:=THIS^);
END_VAR
VAR
eLinkStatus : E_GseLinkStatus;
bSuccess : BOOL;
ipError : I_ServiceErrorClass;
bSubscribe : BOOL := TRUE;
bUnsubscribe : BOOL;
END_VAR
bSuccess:= fbAdapter.Execute(ipError=>ipError);
IF bSubscribe THEN
bSubscribe:= FALSE;
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb01.Subscriber.Enable(ipAdapter:=fbAdapter, ipError=>ipError);
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb02.Subscriber.Enable(ipAdapter:=fbAdapter, ipError=>ipError);
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb03.Subscriber.Enable(ipAdapter:=fbAdapter, ipError=>ipError);
ELSIF bUnsubscribe THEN
bUnsubscribe:= FALSE;
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb01.Subscriber.Disable(ipError=>ipError);
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb02.Subscriber.Disable(ipError=>ipError);
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb03.Subscriber.Disable(ipError=>ipError);
ELSE
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb01.Subscriber.Execute(ipError=>ipError);
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb02.Subscriber.Execute(ipError=>ipError);
bSuccess:= fb[IEDName].IEDLD1.LLN0.gocb03.Subscriber.Execute(ipError=>ipError);
END_IF
Im Projektbaum unter dem Zweig I/O-Device finden Sie eine Netzwerkadapterinstanz mit dem Namen „GSE (RT-Ethernet Adapter)“. Diese Adapterinstanz muss entsprechend konfiguriert werden, d.h. die I/O-Konfiguration muss an die vorhandene Hardware und auf die Zielplattform, auf der das Projekt laufen soll, angepasst werden.
Eine erneute I/O-Konfiguration ist auch dann nötig, wenn Sie die Zielplattform wechseln. Diese Konfiguration muss manuell in TwinCAT XAE vorgenommen werden. Zusätzlich zu der I/O-Konfiguration des Netzwerkadapters muss ein Link zwischen dem Netzwerkadapter und den SPS-Bausteinen für die Goose-Kommunikation hergestellt werden. Mit dem Link können die vom Netzwerkadapter empfangenen Daten an die Instanz des Funktionsbausteins: „FB_[IEDName]Gse“ weitergeleitet werden. In umgekehrter Richtung kann die Instanz des Funktionsbausteins „FB_[IEDName]Gse“ die zu sendenden Daten an den Netzwerkadapter weiterleiten.
Hier finden Sie weitere Informationen: RT – Ethernet Adapter Konfiguration.