Strukturierung von Textblöcken
Textblöcke innerhalb von DUTs, GVLs und POUs bestehen aus Deklarationen, Ausdrücken, Zuordnungen, Aufrufen, Anweisungen oder Schleifen.
Deklarationen
- Deklarationen bestehen aus 3 bis 5 Spalten:
- Variablenname
- [Lokierung (z. B. AT %Q*)]
- Datentyp
- [Initialisierung]
- [Kommentar]
- Ob die Spalten für die Lokierung, die Initialisierung und den Kommentar existieren, hängt vom Programmierfall ab.
- Jede Deklarationsspalte innerhalb eines Programmelements beginnt auf gleicher Zeilenhöhe (Beispiel: alle Datentypen beginnen auf gleicher Zeilenhöhe).
- Diese Variablendeklarationen werden, soweit vorhanden, in einheitlicher Reihenfolge angelegt:
VAR_INPUT
END_VAR
VAR_IN_OUT CONSTANT
END_VAR
VAR_IN_OUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
VAR PERSISTENT
END_VAR
VAR CONSTANT
END_VAR
- Jede deklarierte Variable erhält einen möglichst selbsterklärenden Bezeichner. Falls weitere Informationen notwendig sind, um den Zweck bzw. die Funktionalität der Variablen zu verdeutlichen, wird diese/r mittels eines Kommentars im Anschluss an die Deklaration beschrieben (in der gleichen Zeile).
- Thematisch zusammenhängende Deklarationen werden
- in einem Deklarationsblock gebündelt,
- mithilfe eines Kommentars beschrieben
- und durch eine Leerzeile von anderen Deklarationsblöcken getrennt.
Beispiel:
VAR
// Movement control
bExecuteMovements AT%I* : BOOL; (* Controls the movement
execution of car and bike *)
bMovementsDone AT%Q* : BOOL; (* Indicates if the car and
bike finished their movement *)
// General distance variables
nMovementTime : INT; // Elapsed movement time in seconds
nDistanceTotal : INT; // Total distance being covered by car and bike
nStartPosition : INT := 100; // General start position where car and bike begin to move
// Car
fbCar : FB_Car; // Car-FB whose movement is controlled
nDistanceCar : INT; // Distance being covered by car
// Bike
fbBike : FB_Bike; // Bike-FB whose movement is controlled
nDistanceBike : INT; // Distance being covered by bike
END_VAR
Ausdrücke
- Bei Ausdrücken wird jeder Operator durch Leerzeichen separiert.
- Je nach Anzahl und Komplexität der Teilausdrücke können Klammern hinzugezogen werden, um die Abarbeitungsreihenfolge visuell zu verdeutlichen und die Lesbarkeit zu erhöhen.
Beispiele:
nDistanceCar := fbCar.nStartPosition + (fbCar.nVelocity * nMovementTime);
nDistanceBike := fbBike.nStartPosition + (fbBike.nVelocity * nMovementTime);
nDistanceTotal := nDistanceCar + nDistanceBike;
Zuordnungen
- Zuweisungsoperatoren eines Zuordnungsblocks beginnen auf gleicher Zeilenhöhe.
- Gleiche Zeilenhöhen werden durch die Tabulatortaste erreicht.
- Hinter einem Zuweisungsoperator befindet sich ein Leerzeichen.
- Sehr lange Zuordnungen, die z. B. über die Bildschirmbreite hinausgehen, werden durch einen Zeilenumbruch aufgeteilt. Der Textteil, der sich dadurch in einer neuen Zeile befindet, wird durch die Tabulatortaste auf eine Höhe nach dem Zuweisungsoperator eingerückt, z. B.:
bExpressionResult := bCondition1 AND (bCondition2 OR bCondition3)
AND bCondition4 AND (bCondition5 OR bCondition6);
- Thematisch zusammenhängende Zuordnungen werden
- in einem Zuordnungsblock gebündelt
- und durch eine Leerzeile von anderen Blöcken getrennt.
Beispiel:
//=========================================================
// Submodule enable
// Sensors
fbSensorEStop.bEnable := bGeneralEnable AND bSensorEnable;
fbSensorStartPos.bEnable := bGeneralEnable AND bSensorEnable;
// Cylinder
fbCylinderSystem1Main.bEnable := bGeneralEnable AND bCylinderEnable;
fbCylinderSystem1Sub.bEnable := bGeneralEnable AND bCylinderEnable;
fbCylinderSystem2Main.bEnable := bGeneralEnable AND bCylinderEnable;
fbCylinderSystem2Sub.bEnable := bGeneralEnable AND bCylinderEnable;
// Axes
fbAxisSubfoil.bEnable := bGeneralEnable AND bAxisEnable AND NOT bStop;
fbAxisMain.bEnable := bGeneralEnable AND bAxisEnable AND NOT bStop;
//=========================================================
Aufruf von Methoden/Funktionen/Funktionsblöcken
- Aufrufe von Methoden/Funktionen/Funktionsblöcken mit bis zu drei Parametern können einzeilig geschrieben werden.
- Mehrere einzeilige Aufrufe können in einem Textblock gebündelt werden.
- Besitzen die Programmelemente mehr als drei Parameter, stellen sie jeweils separate Textblöcke dar und werden durch eine Leerzeile von anderen Blöcken getrennt. Dabei befindet sich der erste Parameter in der Zeile des Aufrufs oder in der nachfolgenden Zeile. Die weiteren Parameter beginnen jeweils in einer neuen Zeile.
- Die Parameteranfänge werden mithilfe der Tabulatortaste auf eine Ebene eingerückt.
- Auch die Zuweisungsoperatoren beginnen auf einer Zeilenhöhe.
- Hinter einem Zuweisungsoperator befindet sich ein Leerzeichen.
- Werden einem Funktionsblock an der gleichen Programmstelle, an der die Instanz aufgerufen wird, Parameter übergeben oder werden die Parameter des Funktionsblocks gelesen, geschieht dies beim Aufruf des Funktionsblock und nicht unmittelbar vor bzw. nach dem Instanzaufruf.
- Um die Lesbarkeit des Programmcodes zu erhöhen, wird ein qualifizierter Aufruf von Methoden/Funktionen/Funktionsblöcken empfohlen. Dabei wird die Zuweisung ausformuliert, indem der Parametername beim Aufruf explizit genannt wird.
Negatives Beispiel:
fbTimerStart.IN := TRUE;
fbTimerStart.PT := T#10S;
fbTimerStart();
bStartExecution := fbTimerStart.Q;
Positives Beispiel:
fbTimerStart( IN := TRUE,
PT := T#10S,
Q => bStartExecution);
Ausführliches Beispiel:
//=========================================================
// Stop
// Cylinder
fbCylinderPos1.Stop(bExecute := TRUE);
fbCylinderPos2.Stop(bExecute := TRUE);
fbCylinderPos3.Stop(bExecute := TRUE);
// Axes
fbAxis1.Stop(bExecute := TRUE, bDone => bAxis1Stopped);
fbAxis2.Stop(bExecute := TRUE, bDone => bAxis2Stopped);
//=========================================================
Anweisungen
- RETURN, CONTINUE, EXIT, JMP, IF, CASE
- JMP-Anweisung: Sprunganweisungen können und sollten vermieden werden, da sie die Lesbarkeit des Codes vermindern.
- IF-Anweisung:
- Eine IF-Anweisung stellt einen separaten Textblock dar und wird durch eine Leerzeile von anderen Blöcken getrennt.
- Verschachtelte IF-Anweisungen sollten zum besseren Verständnis und zur Erhöhung der Softwarequalität vermieden werden. Stattdessen sollte immer ein definierter Zustand gelten, der mithilfe einer CASE-Anweisung implementiert wird.
- Alle IF-ELSIF-Anweisungen sollten einen ELSE-Zweig beinhalten.
Sehen Sie dazu auch den Themenpunkt IF-ELSIF-Anweisungen mit ELSE-Zweig im Abschnitt Programmierung. - Die Anweisungen werden um eine Ebene eingerückt, sodass sich nur die Schlüsselwörter IF/ELSIF/ELSE/END_IF auf einer Ebene befinden. Das Schlüsselwort THEN erhält keine eigene Zeile.
- Sehr lange Bedingungen, die über die Bildschirmbreite hinausgehen, werden durch einen Zeilenumbruch aufgeteilt. Der Textteil, der sich dadurch in einer neuen Zeile befindet, wird durch die Tabulatortaste auf eine passende Höhe der oberen Zeile eingerückt: auf die Höhe des IF-Schlüsselwortes oder bei verschachtelten Bedingungen auf die Höhe der dazugehörigen/gleichwertigen Teilbedingung, z. B.:
Beispiel:
IF a > 10 THEN
…
ELSIF a < 10 THEN
…
ELSE
…
END_IF
IF bCondition1 AND bCondition2 AND (bCondition3 OR bCondition4)
AND bCondition5 AND bCondition6
AND (bCondition7 OR bCondition8 OR bCondition9 OR bCondition10) THEN
…
END_IF
- CASE-Anweisung:
- Eine CASE-Anweisung stellt einen separaten Textblock dar und wird durch eine Leerzeile von anderen Blöcken getrennt.
- Die Enumerationswerte werden jeweils durch eine Leerzeile voneinander getrennt und im Verhältnis zur CASE-Anweisung um eine Ebene eingerückt, sodass sich nur die Schlüsselwörter CASE/ELSE/END_CASE auf einer Ebene befinden.
- Zusätzlich kann über jedem Werteblock ein Kommentar hilfreich sein.
- Die Anweisungen zu einem Enumerationswert beginnen 1-2 Zeilen unterhalb des Wertes (nicht direkt neben dem Wert) und werden im Verhältnis zu diesem Wert um eine Ebene eingerückt.
Beispiel:
//=========================================================
// Cylinder sorting process
CASE eSortingState OF
E_SortingState.DetectionOfBox:
fbCylinder.MoveToBase();
sVisuMessage := 'System in detection mode.';
IF fbSensorDelay.bOut THEN
eSortingState := eSorting_MoveCylToWorkPos;
END_IF
E_SortingState.MoveCylToWorkPos:
fbCylinder.MoveToWork();
IF fbCylinder.bAtWorkPos THEN
eSortingState := eSorting_MoveCylToBasePos;
sVisuMessage := '';
ELSE
sVisuMessage := 'Waiting for cylinder in work pos.';
END_IF
E_SortingState.MoveCylToBasePos:
fbCylinder.MoveToBase();
IF fbCylinder.bAtBasePos THEN
eSortingState := eSorting_DetectionOfBox;
sVisuMessage := '';
ELSE
sVisuMessage := 'Waiting for cylinder in base pos.';
END_IF
ELSE
sVisuMessage := 'System in non-existent state. Please check application.';
END_CASE
//=========================================================
Schleifen (FOR, WHILE, REPEAT)
- Die Schleifen-Parametrierungen befinden sich jeweils in einer Zeile (FOR ... DO, WHILE ... DO, REPEAT).
- Die Anweisungen werden um eine Ebene eingerückt, sodass sich nur die Schlüsselwörter FOR/END_FOR bzw. WHILE/END_WHILE bzw. REPEAT/UNTIL/END_REPEAT auf einer Ebene befinden.
Beispiel:
//=========================================================
// Initialize storage areas with FOR loop
FOR nAreaCounter := cStorageStart TO cStorageEnd BY 1 DO
aStorageAreas[nAreaCounter] := nAreaCounter;
END_FOR
//=========================================================
// Initialize delivery areas with WHILE loop
nAreaCounter := cDeliveryStart;
WHILE nAreaCounter <= cDeliveryEnd DO
aDeliveryAreas[nAreaCounter] := nAreaCounter;
nAreaCounter := nAreaCounter + 1;
END_WHILE
//=========================================================