Schleifen und Bedingungen
Themenpunkte:
CASE-Anweisung nur mit Enumerationsinstanz
In einer CASE-Anweisung sollten Sie statt „magischer“ Zahlen eine Enumerationsinstanz samt den dazugehörigen Enumeratoren zur Definition der Zustandsmaschine verwenden. Dadurch sind die Werte der Zustandsmaschine selbstsprechend und es werden keine „Magic Numbers“ verwendet.
Beachten Sie auch die folgenden Themenpunkte der Programmierkonventionen:
- Attribute 'qualified_only' und 'strict' bei Enumeration verwenden
- Keine „magischen Werte/magic numbers“
Negatives Beispiel:
PROGRAM Sample_neg
VAR
nState : INT; // Used to operate the sample state machine
END_VAR
// NON COMPLIANT: Integer variable and "magic values" are used to define state machine
CASE nState OF
0: F_DoSomethingUsefulHere();
1: F_DoSomethingUsefulHere();
2: F_DoSomethingUsefulHere();
ELSE
F_DoSomethingUsefulHere();
END_CASE
Positives Beispiel:
// Enumeration for the positive sample
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_SampleState :
(
Entry, // Entrance part of a state
Work, // Action part of a state
Finish // Exit part of a state
);
END_TYPE
PROGRAM Sample_pos
VAR
eState : E_SampleState; // Used to operate the sample state machine
END_VAR
// COMPLIANT: Enum instance and enum values are used to define state machine
CASE eState OF
E_SampleState.Entry:
F_DoSomethingUsefulHere();
E_SampleState.Work:
F_DoSomethingUsefulHere();
E_SampleState.Finish:
F_DoSomethingUsefulHere();
END_CASE
Grenzen einer Schleife als Konstante deklarieren
Als Grenzen einer Schleife sollten konstante Variablen verwendet werden. Wenn innerhalb der Schleife auf ein Array zugegriffen wird, sollte das Array über die gleichen konstanten Variablen deklariert sein.
- Untergrenze des Arrays = Untergrenze der Schleife = konstante Variable 1
- Obergrenze des Arrays = Obergrenze der Schleife = konstante Variable 2
Beachten Sie auch die folgenden Empfehlungen:
Static Analysis:
Thematisch empfohlene Static Analysis Regeln:
- SA0072: Ungültige Verwendung einer Zählervariablen
- SA0080: Schleifenindexvariable für Arrayindex überschreitet Array-Bereich
- SA0081: Obergrenze ist kein konstanter Wert
Allgemeine Programmelemente für die folgenden Beispiele:
TYPE ST_Object :
STRUCT
sName : STRING;
nID : UINT;
END_STRUCT
END_TYPE
PROGRAM Sample
VAR CONSTANT
cMin : UINT := 1;
cMax : UINT := 10;
END_VAR
VAR
aObjects : ARRAY[cMin..cMax] OF ST_Object;
bInitDone : BOOL;
nForIdx : UINT;
END_VAR
Negatives Beispiel:
VAR
nUpperBorder : UINT;
END_VAR
// Initialize objects
IF NOT bInitDone THEN
nUpperBorder := cMin;
FOR nForIdx := nUpperBorder TO 10 BY 1 DO
aObjects[nForIdx].sName := CONCAT('Object_', UDINT_TO_STRING(nForIdx));
aObjects[nForIdx].nID := nForIdx;
END_FOR
bInitDone := TRUE;
END_IF
Positives Beispiel:
// Initialize objects
IF NOT bInitDone THEN
FOR nForIdx := cMin TO cMax BY 1 DO
aObjects[nForIdx].sName := CONCAT('Object_', UDINT_TO_STRING(nForIdx));
aObjects[nForIdx].nID := nForIdx;
END_FOR
bInitDone := TRUE;
END_IF
In CASE-Anweisung alle Enumerationswerte der Enumerationsvariablen behandeln
In einer CASE-Anweisung sollten Sie alle Enumerationswerte der Enumerationsvariablen behandeln. Falls dies bei einer großen Anzahl von gleichen oder ungenutzten Enumerationswerten nicht sinnvoll sein sollte, kann für diese Fälle ein ELSE-Zweig verwendet werden.
Static Analysis:
Überprüfen mit Hilfe der folgenden Static Analysis Regeln:
Allgemeine Programmelemente für die folgenden Beispiele:
// Enumeration for all samples in this rule
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_ColorTrafficLight :
(
Red := 0,
Yellow,
Green
);
END_TYPE
PROGRAM Sample
VAR
eColorTrafficLight : E_ColorTrafficLight; // Used to handle the state of the traffic light
END_VAR
Negatives Beispiel:
CASE eColorTrafficLight OF // NON COMPLIANT: enum value Green is not handled
E_ColorTrafficLight.Red:
SetLightRed();
E_ColorTrafficLight.Yellow:
SetLightYellow();
END_CASE
Positives Beispiel:
CASE eColorTrafficLight OF // COMPLIANT: all enum values are handled
E_ColorTrafficLight.Red:
SetLightRed();
E_ColorTrafficLight.Yellow:
SetLightYellow();
E_ColorTrafficLight.Green:
SetLightGreen();
END_CASE
IF-ELSIF-Anweisungen mit ELSE-Zweig
Aus Sicherheitsgründen wird empfohlen, einem IF-Konstrukt ein ELSE hinzuzufügen, falls es über ein ELSIF verfügen sollte. Ein ELSIF hat empfohlener Weise ein ELSE zur Folge, um zu verdeutlichen, dass alle möglichen Fälle bedacht worden sind. Falls für den ELSE-Zweig keine Anweisungen geplant sind, sollte nach dem ELSE ein Semikolon folgen. Per Kommentar können Sie für die Abnahme/Inbetriebnahme des Programms erläutern, dass der ELSE-Fall bedacht wurde und warum er über keine Anweisungen verfügt.
Allgemeine Programmelemente für die folgenden Beispiele:
PROGRAM Sample
VAR
nTest : INT:= 10; // Test value for sample
END_VAR
Negatives Beispiel:
IF nTest < 10 THEN // NON COMPLIANT: the ELSE should be used in any way
nTest := 20;
ELSIF nTest > 20 THEN
nTest := 10;
END_IF
Positives Beispiel 1:
IF nTest < 10 THEN // COMPLIANT with any action in ELSE
nTest := 20;
ELSIF nTest > 20 THEN
nTest := 10;
ELSE
F_DoSomethingUsefulHere(); // it's just a sample
END_IF
Positives Beispiel 2:
IF nTest < 10 THEN // COMPLIANT with a comment in ELSE
nTest := 20;
ELSIF nTest > 20 THEN
nTest := 10;
ELSE
;(* No action needed for the case that nTest is >= 10 and <= 20 *)
END_IF
Reihenfolge der IF-Bedingungen
Die Reihenfolge der IF-/ELSIF-/ELSE-Bedingungen entsprechend der Eintrittswahrscheinlichkeit anordnen. So sollte der am wahrscheinlichsten eintretende Fall zuerst abgehandelt werden. Dies verbessert sowohl die Lesbarkeit als auch in einigen Fällen die Performanz, weil nichtzutreffende Abfragen seltener durchlaufen werden.
Positives Beispiel:
IF SUCCEEDED(hrErrorCode) THEN
IF nReqIdx < cIdxMax THEN // requested index in range
; // handle request
ELSIF nReqIdx = cIdxMax THEN // requested index in range but at upper limit
; // handle request
ELSE
; // add error output (error caused by invalid index)
END_IF
ELSE
; // add error handling (error in previous step)
END_IF