Rule:
Use constants for numerical values other than 0 and -1. This ensures, that the value can be changed at a central position and has a clear meaning.
Recommendation:
If possible, only use local constants inside a POU
TIP
Constants are text replacements for numerical values exchanged by the preprocessor. Using constants in the CPU neither causes a performance loss, nor does the memory consumption increase in the data memory. Only the memory consumption in the load memory of the CPU increases due to the increasing number of characters in the block sources.
//Correct --> Working with constants
IF Velocity > MAX_VELOCITY THEN
Velocity := MAX_VELOCITY;
ELSIF Velocity < 0 THEN
Velocity := -1.0 * MAX_VELOCITY;
ELSE
MotorSetpointSpeed := Velocity;
END_IF;
//Wrong --> Working with numerical values
IF (Velocity > 10.0) THEN
Velocity := 10.0;
ELSIF Velocity < 0 THEN
Velocity := -10.0;
ELSE
MotorSetpointSpeed := Velocity;
END_IF;
Recommendation:
Array limits start with 0 and end with a constant for the upper limit of the array (e. g. DiagBuffer[0..DIAG_BUFFER_UPPER_LIM]) or "CONSTANT – 1" (e.g. Drives[0..NUMBER_OF_DRIVES - 1]).
Rule:
A CASE instruction must always have an ELSE branch to be able to report the errors occurring during runtime.
CASE Select OF
1: //Comment
; //Statement
4: //Comment
; //Statement
2..5: //Comment
; //Statement
ELSE
; //Generate error message
END_CASE;
Recommendation:
If possible, a CASE instruction shall be used instead of an IF instruction with several ELSIF branches. This makes the program clearer.
Recommendation:
Use only symbolic constants or ENUM values inside a CASE instruction.
CASE MyStepCase OF
CONST_STEP_A:
;
CONST_STEP_B:
;
CONST_STEP_C:
;
ELSE
;
END_CASE
Rule:
Organize your POUs inside namespaces.
Recommendation:
The folder structure should match your namespace structure.
Recommendation:
Use only one namespace in each file.
//Bad: Multiple namespaces in one file
NAMESPACE a
; //your code in namespace a
END_NAMESPACE
NAMESPACE b
; //your code in namespace b, same file
END_NAMESPACE
Recommendation:
Sort your USING alphabetically.
Recommendation:
Provide data as read-only if possible.
CLASS MyClass
//Bad: Data can be written from outside without notice
VAR_PUBLIC
MyPublicValue : INT;
END_VAR
//Good: Keep data private and provide a public method for read-only access
VAR_PRIVATE
MyPublicReadOnlyValue : INT;
END_VAR
METHOD PUBLIC GetMyReadOnlyValue : INT;
GetMyPublicReadOnlyValue := MyPublicReadOnlyValue;
END_METHOD
END_CLASS
Rule:
If POUs provide error codes, then these must always be evaluated.
Rule:
If only one output parameter is used in a function, the return value is used instead of VAR_OUTPUT.
//Correct: Using return value
FUNCTION MyFunc : INT
; //functionality
END_FUNCTION
//Wrong: Using VAR_OUTPUT section
FUNCTION MyFunc
VAR_OUTPUT
ReturnValue : INT;
END_VAR
; //functionality
END_FUNCTION
Recommendation:
The name of a method should explain the type of actions inside a class. For this, a keyword does help significantly.
| Access | Example keywords |
|---|---|
| Write | Set, Write, Modify |
| Read-only | Read, Get, Is, Has, Can |
//A method that returns a value. It is recommend to use the return value
METHOD PUBLIC GetFillLevel : REAL
GetFillLevel := FillLevelSensorValue * SCALE_FACTOR;
END_METHOD
//A method that sets an internal parameter in a class. Here you can check if the input value is valid,
//then return some information if the value has been set successfully
METHOD PUBLIC SetTimeout : BOOL
VAR_INPUT
value : TIME;
END_VAR
IF value > T#0s THEN
InternalTimeout := value;
SetTimeout := TRUE;
END_IF;
END_METHOD
Rule:
Keep your POUs (functions, function blocks, methods) short, split them into independent units if necessary. This avoids complicated tests.