Subrange types¶
A subrange type is a type whose range of values is only a subset of that of the basic type. The declaration can be carried out in the data types register, but a variable can also be directly declared with a subrange type:
Syntax for the declaration in the ‘Data types’ register:
TYPE <Name> : <Inttype> (<ug>..<og>) END_TYPE;
<Name> | must be a valid IEC identifier, |
<Inttype> | is one of the data types SINT, USINT, INT, UINT, DINT, UDINT, BYTE, WORD, DWORD (LINT, ULINT, LWORD). |
<ug> | Is a constant which must be compatible with the basic type and which sets the lower boundary of the range types. The lower boundary itself is included in this range. |
<og> | Is a constant that must be compatible with the basic type, and sets the upper boundary of the range types. The upper boundary itself is included in this basic type. |
Example:
TYPE
SubInt : INT (-4095..4095);
END_TYPE
Direct declaration of a variable with a subrange \ type:
VAR
i : INT (-4095..4095);
ui : UINT (0..10000);
END_VAR
If a constant is assigned to a subrange type (in the declaration or in the implementation) that does not fall into this range (e.g. 1:=5000), an error message is issued.
In order to check for observance of range boundaries at runtime, the functions CheckRangeSigned or CheckRangeUnsigned must be introduced. In these, boundary violations can be captured by the appropriate method and means (e.g. the value can be cut out or an error flag can be set.). They are implicitly called as soon as a variable is written as belonging to a subrange type constructed from either a signed or an unsigned type.
Example:
In the case of a variable belonging to a signed subrange type (like i, above), the function CheckRangeSigned is called; it could be programmed as follows to trim a value to the permissible range:
FUNCTION CheckRangeSigned : DINT
VAR_INPUT
value, lower, upper: DINT;
END_VAR
IF (value < lower) THEN
CheckRangeSigned := lower;
ELSIF(value > upper) THEN
CheckRangeSigned := upper;
ELSE
CheckRangeSigned := value;
END_IF
In calling up the function automatically, the function name CheckRangeSigned is obligatory, as is the interface specification: return value and three parameters of type DINT
When called, the function is parameterized as follows:
- value: the value to be assigned to the range type
- lower: the lower boundary of the range
- upper: the upper boundary of the range
- Return value: this is the value that is actually assigned to the range type
An assignment i:=10*y implicitly produces the following in this example:
i := CheckRangeSigned(10\*y, -4095, 4095);
Even if y for example has the value 1000, then i still has only the value 4095 after this assignment.
The same applies to the function CheckRangeUnsigned: function name and interface must be correct.
FUNCTION CheckRangeUnsigned : UDINT
VAR_INPUT
value, lower, upper: UDINT;
END_VAR
Warning
If neither of the functions CheckRangeSigned or CheckRangeUnsigned is present, no type checking of subrange types occurs during runtime! The variable i could then take on any value between 32768 and 32767 at any time!
Attention
If neither of the functions CheckRangeSigned or CheckRangeUnsigned is present like described above, there can result an endless loop if a subrange type is used in a FOR loop. This will happen when the range given for the FOR loop is as big or bigger than the range of the subrange type!
Attention
The CheckRangeSigned-function provided with the Check.Lib library is just a sample solution! Before using the library module check whether the function is working as requested for your project, or implement an appropriate CheckRange-function directly as a POU in the project.
Example:
VAR
ui : UINT (0..10000);
END_VAR
FOR ui:=0 TO 10000 DO
...
END_FOR
The FOR loop will never be finished, because ui cannot get bigger than 10000.
Also take care of the definition of the CheckRange functions when you define the incremental value of a FOR loop!