Tags - Krystian-L-Lis/Stage GitHub Wiki
#Guide #Tags
The primary characteristic of a decoupled system, like the one the Stage aims to implement, is its ability to identify entities and components in a human-readable way. This ensures they can be accessed at any point during program execution, as long as the querying object possesses the corresponding unique ID string. TwinCAT supports automatic generation of path strings through the instance-path attribute, but manually typing these paths can be cumbersome and error-prone since attributes are not validated during static analysis. To address this, Tag
and other related function blocks are introduced to simplify this process.
The Tag
instance simplifies handling of reflection and path modifications for the user. It ensures that paths are human-readable and minimizes complexity by stripping unnecessary segments such as proj
, plc_proj
, and _tag
. This simplifies navigation and makes paths easier to manage.
Example:
Standard Path: Input:
proj.plc_proj.MAIN.taggedFb._tag
Processed:
MAIN.taggedFb
Array Path: Input:
proj.plc_proj.MAIN.taggedFb[5]._tag
Processed:
MAIN.taggedFb.5
This simplification enables easier use of wildcards and enhances browsing flexibility.
The Tag
instance includes an optional Option
segment, which is internally part of the Id
but can be accessed separately. However, Option
is read-only through the interface and can only be modified via the original instance to ensure integrity.
Example:
FUNCTION_BLOCK TaggedFb IMPLEMENTS I_Tagged
VAR
_tag : Tag := (Option := 'Tagged'); // Tag instance with 'Tagged' as the Option
END_VAR
PROPERTY Tag : I_Tag
GET:
Tag := _tag;
END_PROPERTY
PROGRAM Main
VAR
taggedFb : TaggedFb; // Instance of the function block
iTag : I_Tag := taggedFb; // Interface reference
sTagId : STRING; // Holds the tag ID
sOption : STRING; // Holds the Option value
END_VAR
// Accessing the tag's ID
sTagId := taggedFb.Tag.Id;
// Result: sTagId = 'MAIN.taggedFb'
// Accessing the Option value
sOption := taggedFb.Option;
// Result: sOption = 'Tagged'
// Attempting to modify the Option via the function block instance
taggedFb.Option := 'NewOption';
sOption := taggedFb.Option;
// Result: sOption = 'NewOption'
// Accessing Option via the interface
sOption := iTag.Option;
// Result: sOption = 'NewOption'
// Attempting to modify Option via the interface
iTag.Option := 'SecondOption';
// Compiler error
END_PROGRAM
TagCmp
is a chainable comparison tool for I_Tag
components, allowing users to compare tags with advanced functionality similar to a CASE
statement. It operates in three primary steps:
-
Set: Define the base
Tag
(orI_Tag
) for comparison. -
Chain Matches: Use methods like
HasOption
,MatchId
, orMatchWcdId
to define matching conditions. -
Evaluate Results: Check results using methods like
IsTrue
,IsFalse
, orIsErr
.
If any condition evaluates to TRUE
, IsTrue
returns TRUE
, locks the internal state, and halts further evaluations in the chain. If any condition triggers an error, IsErr
returns TRUE
and locks. If no conditions are met, IsFalse
returns TRUE
and locks.
**Example 1: Basic Usage
PROGRAM Main
VAR
taggedFb: ARRAY[0..11] OF TaggedFb;
iSelected: I_Tag;
cmp: TagCmp;
i: UDINT;
sResult: STRING;
END_VAR
iSelected := taggedFb[REAL_TO_UDINT(Random() * 10.0)].Tag;
cmp.Set(iSelected);
FOR i := 1 TO 11 DO
IF cmp.Match(taggedFb[i].Tag).IsTrue() THEN
sResult := CONCAT('Selected: ', TO_STRING(i));
END_IF
END_FOR
**Example 2: Chain Matching
iSelected := taggedFb[REAL_TO_UDINT(Random() * 10.0)].Tag;
cmp.Set(iSelected);
IF cmp.Match(taggedFb[0].Tag).Match(taggedFb[1].Tag).IsTrue() THEN
sResult := 'I\'m 0 or 1!';
END_IF
IF cmp.Match(taggedFb[2].Tag).Match(taggedFb[3].Tag).IsTrue() THEN
sResult := 'I\'m 2 or 3!';
END_IF
IF cmp.IsFalse() THEN
sResult := 'I\'m any other value!';
END_IF
**Resetting Comparisons
iSelected := taggedFb[1].Tag;
cmp.Set(iSelected);
IF cmp.Match(taggedFb[1].Tag).IsTrue() THEN
sResult := 'I\'m 1';
END_IF
// Subsequent conditions won't execute after the first match:
IF cmp.Match(taggedFb[1].Tag).IsTrue() THEN
// This block will never execute.
END_IF
// Reset comparison state:
cmp.Set(iSelected);
IF cmp.Match(taggedFb[1].Tag).IsTrue() THEN
// This block will execute again.
sResult := 'I\'m 1';
END_IF
The MatchId
method compares tag IDs from right to left for flexibility.
Example 4: Using MatchId
PROGRAM Main
VAR
taggedFb_01: TaggedFb;
taggedFb_02: TaggedFb;
cmp: TagCmp;
sResult: STRING;
END_VAR
cmp.Set(taggedFb_01);
IF cmp.MatchId('taggedFb_01').IsTrue() THEN
sResult := 'Im taggedFb_01!';
END_IF
IF cmp.MatchId('taggedFb_02').IsTrue() THEN
sResult := 'Im taggedFb_02!';
END_IF
IF cmp.IsFalse() THEN
sResult := 'Im neither!';
END_IF
The MatchWcdId
method supports wildcards (*
), allowing flexible comparisons.
Example: Wildcard pattern
*.nestedFb.*.taggedFb
This pattern indicates that the system will ignore any number of segments between the first segment and nestedFb
, as well as any number of segments between nestedFb
and taggedFb
. For instance, consider the following example with a wildcard.
Example 5: Using MatchWcdId
with Wildcards
FUNCTION_BLOCK TaggedFb IMPLEMENTS I_Tagged
VAR
_tag : Tag := (Option := 'Tagged'); // Tag instance with 'Tagged' as the Option
END_VAR
PROPERTY Tag : I_Tag
GET:
Tag := _tag;
END_PROPERTY
END_FUNCTION_BLOCK
FUNCTION_BLOCK NestedFb
VAR
taggedFb_01: TaggedFb;
taggedFb_02: TaggedFb;
END_VAR
PROPERTY Tag_1 : I_Tag
GET:
Tag := taggedFb_01.Tag;
END_PROPERTY
PROPERTY Tag_2 : I_Tag
GET:
Tag := taggedFb_02.Tag;
END_PROPERTY
END_FUNCTION_BLOCK
PROGRAM Main
VAR
nestedFb_01: NestedFb;
nestedFb_02: NestedFb;
cmp: TagCmp;
sResult: STRING;
END_VAR
cmp.Set(nestedFb_01.Tag_1);
IF cmp.MatchWcdId('*.nestedFb_01.*').IsTrue() THEN
sResult := 'Im part of nestedFb_01!';
END_IF
cmp.Set(nestedFb_01.Tag_2);
IF cmp.MatchWcdId('*.nestedFb_01.*').IsTrue() THEN
sResult := 'Im part of nestedFb_01!';
END_IF
cmp.Set(nestedFb_02.Tag_1);
IF cmp.MatchWcdId('*.nestedFb_01.*').IsFalse() THEN
sResult := 'Im not part of nestedFb_01!';
END_IF
< Previous | Home | Next >