Client - Read/Write DataSet Values (GetDataSetValues, SetDataSetValues)
Dieses Beispiel zeigt die Verwendung der „GetDataSetValuesReq“- und „SetDataSetValuesReq“- Methoden des Client-Funktionsbausteins.
Download TwinCAT XAE Project (*.zip): Sample08.zip
Das hier beschriebene Beispiel nutzt die Statemachine, welche in dem Kapitel „Allgemeine Client - Projektstruktur“ beschrieben ist. Die States: 0,1,11 und 100 sind identisch zu der dort beschriebenen Statemachine. Andere States wurden für das Beispiel modifiziert oder auch neue States hinzugefügt.
Allgemeine Information über IEC 61850 DataSets
Ein DataSet ist eine Liste von Datenattributen oder Daten die gemeinsam (meistens in einem Report- oder einer GOOSE-Meldung) übertragen werden können. Die einzelnen Listeneinträge eines DataSets werden DataSet-Member genannt. Die DataSet-Member werden im TwinCAT Telecontrol Configurator konfiguriert. Der SPS Code der DataSets und DataSet-Member wird vom Konfigurator automatisch während der TwinCAT Projektgenerierung erzeugt. Mit einem DataSet können mehrere Daten oder Datenattribute gleichzeitig gelesen oder geschrieben werden. Die Client-Methode „GetDataSetValuesReq“ kann alle im DataSet definierten DataSet-Member gemeinsam auslesen (die Werte werden vom Server zum Client übertragen). Mit der Client-Methode „SetDataSetValuesReq“ können alle im DataSet definierten DataSet-Member beschrieben werden (die Werte werden vom Client zum Server übertragen).
Vorzugsweise befinden sich die DataSets im IEC 61850 Datenmodell unterhalb des logischen Knoten LLN0. Ein DataSet kann theoretisch aber auch jedem anderen logischen Knoten zugeordnet (mit diesem Knoten verlinkt) werden.
Folgende Abbildung zeigt die, während der Projektgenerierung erstellten DataSet-Funktionsbausteine im TwinCAT Projektbaum:
Die abgebildeten DataSet-Funktionsbausteine gehören zum LLN0 und aus diesem Grund werden sie auch im Deklarationsteil vom LLN0 instanziiert. Die Instanzen der DataSets:“DS1“ bis „DS8“ und die Verknüpfung mit dem logischen Knoten wurde ebenfalls automatisch generiert.
Der SPS-Code für die Konfiguration der DataSet-Member wird wie die DataSets selbst ebenfalls automatisch generiert. Dieser Code befindet sich in der OnInit-Methode des jeweiligen DataSet-Funktionsbausteins (siehe Bild unten).
Statische DataSets vs. dynamische DataSets
Die TwinCAT Projektgenerierung erzeugt statische, nicht löschbare DataSets (static created persistent data-sets). D.h. diese DataSets sind im Datenmodell immer verfügbar und können und sollen nicht gelöscht werden. Es gibt aber auch dynamische DataSets, die während der Kommunikation erstellt werden können. Die dynamisch erstellten DataSets können wieder gelöscht werden. Sie werden gelöscht entweder automatisch, beim Verbindungsabbruch (dynamic created non-persistent data-sets) oder zum späteren Zeitpunkt, auch von einem anderen Client (dynamic created persistent data-sets).
Beispielprojekt
In dem zip-Archiv befindet sich eine ICD-Konfigurationsdatei. Diese Datei kann verwendet werden, um z. B. einen Server mit Hilfe einer Drittherstellersoftware zu simulieren oder mit TwinCAT Telecontrol Configurator ein neues oder modifiziertes TwinCAT Projekt zu erzeugen.
Falls Sie vorhaben, ein DataSet zu verwenden, um mehrere Daten oder Datenattribute gleichzeitig zu beschreiben dann müssen Sie noch Folgendes beachten: Alle konfigurierten DataSet-Member müssen auch einen Schreibzugriff erlauben, andernfalls wird der Schreibzugriff fehlschlagen. Dies hängt hauptsächlich von der funktionalen Gruppe (FC) des DataSet-Members ab. In diesem Beispiel wurde zur Demonstration für den Schreibzugriff das DataSet „DS8“ so konfiguriert, dass alle Dataset-Member beschrieben werden können (funktionale Gruppe: „DC“). Bei unserem Client-Beispielprojekt muss natürlich der Schreibzugriff auf alle DataSet-Member auf der Serverseite möglich sein.
Im Funktionsbaustein FB_IEDClient sind mehrere booleschen Variablen definiert. Eine steigende Flanke an einer dieser Variablen aktiviert die Methode: „GetDataSetValuesReq“ oder „SetDataSetValuesReq“ mit dem jeweiligen DataSet als Eingangsparameter. Als Beispiel: Eine steigende Flanke an der Variablen „bGetDataSetValues_LLN0_DS1“ aktiviert den Befehl zum Lesen des DataSet: „DS1“ und eine steigende Flanke an „bSetDataSetValues_LLN0_DS8“ aktiviert den Befehl zum Schreiben des DataSet: „DS8“.
…
bGetDataSetValues_LLN0_DS1 : BOOL := TRUE;
bGetDataSetValues_LLN0_DS2 : BOOL := TRUE;
bGetDataSetValues_LLN0_DS3 : BOOL := TRUE;
bGetDataSetValues_LLN0_DS4 : BOOL := TRUE;
bGetDataSetValues_LLN0_DS5 : BOOL := TRUE;
bGetDataSetValues_LLN0_DS6 : BOOL := TRUE;
bGetDataSetValues_LLN0_DS7 : BOOL := TRUE;
bGetDataSetValues_LLN0_DS8 : BOOL := TRUE;
bSetDataSetValues_LLN0_DS8 : BOOL := TRUE;
…
Es folgt ein Auszug aus dem Beispielcode zum Lesen des DataSet: „DS8“. Zuerst aktiviert der Methodenaufruf „GetDataSetValuesReq“ die Übertragung der Daten vom Server zum Client. Danach, in weiteren SPS-Zyklen muss die Methode „ipResult.Execute()“ so lange aufgerufen werden, bis die Übertragung abgeschlossen wurde. „ipResult.IsBusy()“ liefert in diesem Fall FALSE. Die empfangenen Datenwerte oder Attributwerte der DataSet-Member werden beim Erfolg automatisch ins Client-Datenmodell kopiert (gemappt).
…
ELSIF bGetDataSetValues_LLN0_DS8 THEN
bGetDataSetValues_LLN0_DS8:= FALSE;
bSuccess:= fbConnection.GetDataSetValuesReq(ipDataSet:=fbIED.IEDLD1.LLN0.DS8, hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
…
Im Beispielcode zum Schreiben des DataSet: „DS8“ werden zum Demonstrationszweck jedes Mal die zu schreibenden Daten des DataSet: „DS8“ modifiziert. Zuerst werden neue Datenwerte oder Attributwerte den DataSet-Membern im Client-Datenmodell zugewiesen. Danach aktiviert der Methodenaufruf „SetDataSetValuesReq“ die Übertragung der Daten zum Server. In weiteren SPS-Zyklen muss analog wie schon oben beschrieben die Methode „ipResult.Execute()“ so lange aufgerufen werden, bis die Übertragung abgeschlossen wurde. Die neuen Datenwerte oder Attributwerte werden auf der Serverseite ins Server-Datenmodell kopiert (gemappt).
…
ELSIF bSetDataSetValues_LLN0_DS8 THEN
bSetDataSetValues_LLN0_DS8:= FALSE;
(* As example we modify some description data values *)
sConfigRev:= TO_STRING(nRev:=nRev+1);
sHwRev:= TO_STRING(TO_REAL(nRev));
sSwRev:= TO_STRING(TO_REAL(nRev));
fbIED.IEDLD1.LPHD1.PhyNam.Vendor.sValue:= sVendor;
fbIED.IEDLD1.LPHD1.PhyNam.hwRev.sValue:= sHwRev;
fbIED.IEDLD1.LPHD1.PhyNam.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LPHD1.PhyNam.serNum.sValue:= sSerNum;
fbIED.IEDLD1.LPHD1.PhyNam.model.sValue:= sModel;
fbIED.IEDLD1.LPHD1.PhyNam.location.sValue:= sLocation;
fbIED.IEDLD1.LLN0.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LLN0.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LLN0.NamPlt.d.sValue:= 'LLN0 demo node';
fbIED.IEDLD1.LLN0.NamPlt.configRev.sValue:= sConfigRev;
fbIED.IEDLD1.MMXU1.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.MMXU1.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.MMXU1.NamPlt.d.sValue:= 'MMXU1 demo node';
fbIED.IEDLD1.MMXU1.NamPlt.configRev.sValue:= sConfigRev;
fbIED.IEDLD1.XCBR1.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.XCBR1.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.XCBR1.NamPlt.d.sValue:= 'XCBR1 demo node';
fbIED.IEDLD1.XCBR1.NamPlt.configRev.sValue:= sConfigRev;
fbIED.IEDLD1.CSWI1.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.CSWI1.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.CSWI1.NamPlt.d.sValue:= 'CSWI1 demo node';
fbIED.IEDLD1.CSWI1.NamPlt.configRev.sValue:= sConfigRev;
fbIED.IEDLD1.LEDGGIO1.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO1.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO1.NamPlt.d.sValue:= 'LEDGGIO1 demo node';
fbIED.IEDLD1.LEDGGIO2.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO2.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO2.NamPlt.d.sValue:= 'LEDGGIO2 demo node';
fbIED.IEDLD1.LEDGGIO3.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO3.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO3.NamPlt.d.sValue:= 'LEDGGIO3 demo node';
fbIED.IEDLD1.LEDGGIO4.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO4.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO4.NamPlt.d.sValue:= 'LEDGGIO4 demo node';
fbIED.IEDLD1.LEDGGIO5.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO5.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO5.NamPlt.d.sValue:= 'LEDGGIO5 demo node';
fbIED.IEDLD1.LEDGGIO6.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO6.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO6.NamPlt.d.sValue:= 'LEDGGIO6 demo node';
fbIED.IEDLD1.LEDGGIO7.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO7.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO7.NamPlt.d.sValue:= 'LEDGGIO7 demo node';
fbIED.IEDLD1.LEDGGIO8.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.LEDGGIO8.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.LEDGGIO8.NamPlt.d.sValue:= 'LEDGGIO8 demo node';
fbIED.IEDLD1.ATCC1.NamPlt.vendor.sValue:= sVendor;
fbIED.IEDLD1.ATCC1.NamPlt.swRev.sValue:= sSwRev;
fbIED.IEDLD1.ATCC1.NamPlt.d.sValue:= 'ATCC1 demo node';
fbIED.IEDLD1.ATCC1.NamPlt.configRev.sValue:= sConfigRev;
bSuccess:= fbConnection.SetDataSetValuesReq(ipDataSet:=fbIED.IEDLD1.LLN0.DS8, hUser:=0, ipSink:=0, nInvokeID=>nInvokeID, ipResult=>ipResult);
state:= SEL(bSuccess, 100, 11);
…
…
CASE state OF
…
11:
IF ipResult <> 0 THEN
ipResult.Execute();
IF NOT (bBusy:=ipResult.IsBusy()) THEN
state:= SEL(ipResult.IsCompleted(), 100(* failed or aborted *), 0(* succeeded *));
END_IF
END_IF
…
END_CASE
…