Derived data types
TYPE … END_TYPE
User-defined types live in a dedicated TYPE block (or in
ForgeIEC’s case, in their own DataType POU):
TYPE
MyStruct : STRUCT
iCount : INT;
xActive : BOOL;
END_STRUCT;
MyArray : ARRAY[1..10] OF INT;
MyEnum : (IDLE, RUNNING, FAULTED);
MyAlias : INT; (* type-safe alias *)
Percent : INT (0..100); (* subrange *)
END_TYPE
After the type is declared, it can be used in any subsequent variable declaration:
VAR
state : MyStruct;
values : MyArray;
mode : MyEnum := IDLE;
progress : Percent := 0;
END_VAR
STRUCT — record type
Bundles named members of any types (including other STRUCTs and ARRAYs):
TYPE
SensorReading : STRUCT
rValue : REAL;
tTimestamp : TIME;
xValid : BOOL;
sUnit : STRING(8);
END_STRUCT;
END_TYPE
VAR
reading : SensorReading;
END_VAR
(* Access members with . *)
reading.rValue := 23.5;
reading.tTimestamp := T#0s;
reading.xValid := TRUE;
IF reading.xValid AND reading.rValue > 100.0 THEN
DoSomething();
END_IF;
STRUCT members are stored contiguously in memory (with
implementation-defined alignment) and can be passed by
reference via VAR_IN_OUT.
ARRAY — fixed-size collection
VAR
arr : ARRAY[1..10] OF INT; (* 1-D *)
matrix : ARRAY[1..3, 1..3] OF REAL; (* 2-D *)
cube : ARRAY[0..9, 0..9, 0..9] OF BOOL; (* 3-D *)
arrInit : ARRAY[1..5] OF INT := [1, 2, 3, 4, 5]; (* with init *)
arrZero : ARRAY[1..100] OF INT; (* zero-init *)
END_VAR
(* Access with [] *)
arr[1] := 42;
matrix[2, 3] := 1.5;
xBit := cube[5, 5, 5];
Bounds in the declaration are inclusive on both ends —
ARRAY[1..10] has ten elements with indices 1 through 10.
The lower bound doesn’t have to be 0 or 1; ARRAY[-5..5]
is a valid 11-element array.
Array index expressions must be ANY_INT. Out-of-bounds
runtime access is implementation-defined in matiec — may
wrap, may corrupt adjacent memory. Validate indices yourself
when they’re computed at runtime.
Enumerations
TYPE
StateMachine : (IDLE, RUNNING, PAUSING, PAUSED, FAULTED);
END_TYPE
VAR
state : StateMachine := IDLE;
END_VAR
state := RUNNING;
IF state = FAULTED THEN ... END_IF;
Enum literals don’t need a type prefix — bare IDLE,
RUNNING, FAULTED. ForgeIEC’s editor convention adds a
single-letter e prefix to the type name (eStateMachine)
to distinguish enum types from STRUCT types in code review;
see Memory: feedback_enum_prefix.
Enum values are stored as INT-equivalent integers, but
arithmetic on them is rejected by matiec (state + 1 is a
type error). Use a real INT counter and convert.
Type-safe aliases
TYPE
Centimetres : INT;
Inches : INT;
END_TYPE
VAR
cm : Centimetres := 100;
inch : Inches := 25;
END_VAR
(* matiec allows the assignment because both alias INT *)
cm := inch; (* compiles, but semantically wrong *)
matiec’s type aliases are structural, not nominal —
Centimetres and Inches are interchangeable with each
other and with bare INT. If you need true unit safety, wrap
each unit in a single-field STRUCT:
TYPE
Centimetres : STRUCT iValue : INT; END_STRUCT;
Inches : STRUCT iValue : INT; END_STRUCT;
END_TYPE
Now cm := inch is a type error.
Subranges
TYPE
Percent : INT (0..100);
Pin : USINT (0..7);
END_TYPE
A subrange is <base_type> (<min>..<max>). matiec does not
range-check at runtime — assigning Percent := 200 compiles
and the variable holds 200. The subrange is a documentation
hint, not a runtime guard. If you need actual range
enforcement, use LIMIT at every assignment site.
IEC reference
IEC 61131-3 third edition (2013), clause 6.4.3 — “Derived data types”.
matiec conformance
- STRUCT, ARRAY, enums implemented per the standard.
- Type aliases are structural (no nominal type-safety).
- Subranges are unchecked at runtime.
- Forward type references across separate
TYPEblocks don’t work — seellm_signals.