@SHendrik
Ich habe mal was aus meiner Schatztruhe gekramt. Bei der Anwendung waren ca. 200 Ventile zu steuern und zu kontrollieren. Der Ventilzustand wurde auf einem großen Fließbild per LED angezeigt. Ich habe damals die EA-Liste per Excel erfasst und konnte so den nachstehenden Quelltext für den DB erzeugen, in dem das liegen würde, was du als Konfig bezeichnest.
Das mit Excel zu machen bietet sich an, weil das bei der Masse sicher fehlerfrei abläuft wenn man die Anfangsbelegung der Datenworte aus den EA-Adressen errechnet:
W#16#01E1 entspricht 60.1, bzw. W#16#01E1 = 481 = 60 * 8 + 1
Ein bereichsinterner Zeiger besteht "normalerweise" aus 4 Byte, ich hatte für meine Zwecke aber nur die "unteren" 2 Byte hergenommen, weil in den "oberen" 2 Byte eh nur Nullen gestanden wären.
Aus nahe liegenden Gründen habe ich nachfolgend den DB-Quelltext nur für die 1. Ventileinheit dargestellt:
Code:
DATA_BLOCK "VENTIL_PARM"
TITLE =Ventilüberwachung
AUTHOR : HL
FAMILY : Ventile
VERSION : 0.1
STRUCT
V001_FRG : BYTE := B#16#01; //Freigabe Ventil 001
V001_FLAGS : BYTE := B#16#01; //Flags Ventil 001
V001_ES1 : WORD := W#16#01E0; //Endschalter ZU Ventil 001
V001_ES2 : WORD := W#16#01E1; //Endschalter AUF Ventil 001
V001_LED1 : WORD := W#16#007C; //LED rot ZU Ventil 001
V001_LED2 : WORD := W#16#007D; //LED grün AUF Ventil 001
V001_MV : WORD := W#16#0320; //MV-Spule Ventil 001
V001_ZKT : INT := 30; //Zeitparm. Ventil 001
END_STRUCT ;
BEGIN
V001_FRG := B#16#01;
V001_FLAGS := B#16#01;
V001_ES1 := W#16#01E0;
V001_ES2 := W#16#01E1;
V001_LED1 := W#16#007C;
V001_LED2 := W#16#007D;
V001_MV := W#16#0320;
V001_ZKT := 30;
V001_TIMER := L#0;
END_DATA_BLOCK
Man hätte für jede Ventil-Instanz auch einen eigenen DB machen und dann jeweils eine Instanz eines FC oder FB anwenden können. Ich hab mir aber lästige Tipparbeit erspart - das Eintippen der EA-Liste in die Excelliste war schon Arbeit genug und habe mir daher nur einen DB für "alle" Ventileinheiten gegönnt. Man kann mit ein paar simplen Tricks aus der Excelliste direkt den Quelltext für den DB erzeugen, der Quelltext wird dann in die AWL-Umgebung importiert und übersetzt und das war's dann. So konnte ich mir sicher sein, bei der Umsetzung der EA, die geringst mögliche Fehlerrate zu haben.
Wenn also die Ventileinheiten systematisch in dem DB abgebildet wurden, dann war es nahe liegend, die Ventileinheiten mit einer Schleife abzufragen. Um die Zykluszeiten nicht über Gebühr zu belasten, habe ich die Auswertung insgesamt auf 3 Zyklen verteilt, deshalb habe ich 3 Instanzen des FCs verwendet, den ich nachstehend in Ausschnitten zeige.
Man hätte auch anstelle des FC einen FB verwenden können, aber das war in der vorliegenden Anwendung eher eine Frage des Geschmacks, bei einem FB hätte man den Parameter "LastTime" als "VAR" somit statisch anlegen können, das wär's dann aber auch schon gewesen:
Code:
FUNCTION "MV_CNTRL" : VOID
TITLE =Ventilkontrolle
AUTHOR : HL
FAMILY : Ventile
VERSION : 0.1
VAR_INPUT
Count : INT ; // Schleifenzahl
Start : INT ; // Byte-Zeiger auf 1. Ventileinheit
DB_NUM : INT ; // Nummer des Datenbausteins
RefTime : DINT ; // Zeitreferenz in Millisekunden
END_VAR
VAR_IN_OUT
LastTime : DINT ; // Zeit beim letzten Zugriff
END_VAR
VAR_TEMP
LOOPER : INT ; // Schleifenzähler
SWX01 : INT ; // Schmiermerker
SWX02 : INT ; // Schmiermerker
PTR1 : DWORD ; // temp. Zeiger
PTR_FRG : DWORD ; // Zeiger auf Freigabe
PTR_LED1 : DWORD ; // Zeiger auf LED 1
PTR_LED2 : DWORD ; // Zeiger auf LED 2
PTR_ZKT : DWORD ; // Zeiger auf Zeitparameter
PTR_TIMER : DWORD ; // Zeiger auf Zeitzelle
deltaTime : DINT ; // Differenzzeit seit letztem Zugriff
ES1 : BOOL ; // Kopie von Endschalter 1
ES2 : BOOL ; // Kopie von Endschalter 2
MV : BOOL ; // Kopie von Ventil-Ansteuerung
STOEM : BOOL ; // Störmeldung
SAVE_DBNO : WORD ;
SAVE_AR1 : DWORD ;
SAVE_AR2 : DWORD ;
END_VAR
Das war eine reine Vorsichtsmaßnahme (Registerinhalte retten) in dem ganzen Projekt, um irgendwelche Kollisionen zu vermeiden und hat damit jedes weitere Nachdenken überflüssig gemacht:
Code:
BEGIN
NETWORK
TITLE =Registerinhalte retten
L DBNO;
T #SAVE_DBNO;
TAR1 #SAVE_AR1;
TAR2 #SAVE_AR2;
Hier wurden alle internen Zeiger für einen Durchlauf bei der Bearbeitung einer Ventileinheit bestimmt:
Code:
NETWORK
TITLE =Lampenansteuerung für 2-Stellungsventile
// Datenbaustein mit den Parametern für die Indizierung aufschlagen
L #DB_NUM;
T #SWX01;
AUF DB [#SWX01];
L #Start;
T #SWX01; // DBB-Index auf 1. Ventileinheit
L #Count; // Anzahl der Durchgänge
LOPM: T #LOOPER;
L #SWX01; // DBX-Index auf Ventileinheit
SLD 3;
T #PTR_FRG; // Zeiger auf Freigabe
LAR1 ;
+ L#96; // = 12 * 8
T #PTR_ZKT; // Zeiger auf Zeitparameter
+ L#16; // = 2 * 8
T #PTR_TIMER; // Zeiger auf Zeitzelle
Und hier siehst du z.B. die Auswertung für die Ventilendschalter. Der erste Ladebefehl holt den "bereichsinternen Zeiger" aus dem DB. Merke: im AR1 verweist der Zeiger auf den 1. Datenpunkt der aktuellen Ventileinheit, der bereichsinternen Zeiger für den 1. Endschalter steht im 3. und 4. Byte, deshalb "P#2.0", äquivalent muss es dann für den 2. Endschalter “P#4.0“ heißen. Da ich noch reichliche Bitbumserei mit den Valenzen betrieben habe (hier nicht weiter dargestellt), habe ich die Valenzen der Ein- Und Ausgänge teilweise in temporären lokalen Variablen abgelegt, weil ein Zugriff mit "E [#PTR1]" mehr Zeit kostet als ein Zugriff auf "#ES1" usw.usf.:
Code:
L DBW [AR1, P#2.0];
T #PTR1;
U E [#PTR1];
= #ES1; // Valenz von Endschalter 1
L DBW [AR1, P#4.0];
T #PTR1;
U E [#PTR1];
= #ES2; // Valenz von Endschalter 2
Code:
O( ;
UN #MV; // Ventil-Ansteuerung
UN #ES1; // Endschalter 1
) ;
O( ;
U #MV; // Ventil-Ansteuerung
UN #ES2; // Endschalter 2
) ;
SPBN ANFT; // Zelle auf Startwert einstellen
Wie aus der FC-Schnittstelle ersichtlich, habe ich für verschiedene Ausgänge den "bereichsinternen Zeiger" in temporären Variablen abgelegt, weil Ausgänge weniger oft verknüpft werden. Eine Ventileinheit hat in dieser Anwendung 18 Byte belegt, klar das vor der Ausführung der Schleifenbedingung, der Zeiger auf den Beginn der nächsten Ventileinheit entsprechend eingestellt werden muss:
Code:
// Lampenprüfung
LAUS: SET ;
R A [#PTR_LED1]; // LED 1
R A [#PTR_LED2]; // LED 2
LMPT: U "DT_LED_TEST"; // Lampenprüfung
S A [#PTR_LED1]; // LED 1
S A [#PTR_LED2]; // LED 2
// ----------------------------------------------
L #SWX01; // DBX-Index auf aktuelleVentileinheit
+ 18;
T #SWX01; // DBX-Index auf nächste Ventileinheit
L #LOOPER;
LOOP LOPM;
Nachdem der FC seine eigentliche Arbeit beendet hat, wird der Zustand wieder eingestellt, der in den AR-Registern vor der Ausführung des Baustein vorhanden war:
Code:
NETWORK
TITLE =Registerinhalte wiederherstellen
AUF DB [#SAVE_DBNO];
LAR1 #SAVE_AR1;
LAR2 #SAVE_AR2;
END_FUNCTION
Nachstehend siehst du den Aufruf der 1. Instanz des FC:
Code:
CALL "MV_CONTRL" (
Count := 74, // Schleifenzahl
Start := 0, // Byte-Zeiger auf 1. Ventileinheit
DB_NUM := 3, // Nummer des Datenbausteins
RefTime := "REFTIME", // Zeitreferenz in Millisekunden
LastTime := "LAST1"); // Zeit beim letzten Zugriff
Nachstehend siehst du den Aufruf der 2. Instanz des FC. Der Start der 75. Ventileinheit liegt bei 74 * 18 = 1332, 74 Ventile waren beim 1. Durchlauf bearbeitet worden, 18 Byte ist die Blockgröße einer Ventileinheit im Konfig-DB:
Code:
CALL "MV_CONTRL" (
Count := 74, // Schleifenzahl
Start := 1332, // Byte-Zeiger auf 75. Ventileinheit
DB_NUM := 3, // Nummer des Datenbausteins
RefTime := "REFTIME", // Zeitreferenz in Millisekunden
LastTime := "LAST2"); // Zeit beim letzten Zugriff
In etwa entspricht das deinen Anforderungen zur Rolladensteuerung, die Konfiguration liegt in einem DB, das Programm versorgt sich mit EA-Zeigern aus dem DB.
Das ganze hat aber erhebliche Nachteile:
1. Keine dieser in dem FC angesprochenen EAs wird jemals in einem Querverweis auftauchen.
2. Bei Änderungen in der EA-Belegung wird's knifflig, wenn man das zu Fuß machen will.
Ich hoffe ich konnte dir helfen.
Gruß Barnee und noch ein schönes Rest-WE