Schleifen und Bedingungen

Themenpunkte:

  1. CASE-Anweisung nur mit Enumerationsinstanz [++]
  2. Grenzen einer Schleife als Konstante deklarieren [+]
  3. In CASE-Anweisung alle Enumerationswerte der Enumerationsvariablen behandeln [+]
  4. IF-ELSIF-Anweisungen mit ELSE-Zweig [+]
  5. Reihenfolge der IF Bedingungen [+]

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:

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.

Beachten Sie auch die folgenden Empfehlungen:

Static Analysis:

Thematisch empfohlene Static Analysis Regeln:

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