Annotating a TwinCAT3 project¶
Pytmc is capable of generating most of the DB file but some settings require human direction. Developers set this configuration by adding an attribute pragma to TwinCAT3 variables when they’re declared. These pragmas can be appended to variables in project files and library files.
Data and Record Types¶
TwinCAT data types and their corresponding record types are as follows:
Data type |
Lower bound |
Upper bound |
Memory space |
Record Type |
Scalar DTYP |
Waveform DTYP |
---|---|---|---|---|---|---|
BOOL |
0 |
1 |
8 bit |
bi, bo |
asynInt32 |
asynInt8ArrayIn, asynInt8ArrayOut |
BYTE |
0 |
255 |
8 bit |
longin, longout |
asynInt32 |
asynInt8ArrayIn, asynInt8ArrayOut |
SINT |
-128 |
127 |
8 bit |
longin, longout |
asynInt32 |
asynInt8ArrayIn, asynInt8ArrayOut |
USINT |
0 |
255 |
8 bit |
longin, longout |
asynInt32 |
asynInt8ArrayIn, asynInt8ArrayOut |
WORD |
0 |
65535 |
16 bit |
longin, longout |
asynInt32 |
asynInt16ArrayIn, asynInt16ArrayOut |
INT |
-32768 |
32767 |
16 bit |
longin, longout |
asynInt32 |
asynInt16ArrayIn, asynInt16ArrayOut |
UINT |
0 |
65535 |
16 bit |
longin, longout |
asynInt32 |
asynInt16ArrayIn, asynInt16ArrayOut |
ENUM |
0 |
4294967295 |
32 bit |
longin, longout |
asynInt32 |
asynInt16ArrayIn, asynInt16ArrayOut |
DWORD |
0 |
4294967295 |
32 bit |
longin, longout |
asynInt32 |
asynInt32ArrayIn, asynInt32ArrayOut |
DINT |
-2147483648 |
2147483647 |
32 bit |
longin, longout |
asynInt32 |
asynInt32ArrayIn, asynInt32ArrayOut |
UDINT |
0 |
4294967295 |
32 bit |
longin, longout |
asynInt32 |
asynInt32ArrayIn, asynInt32ArrayOut |
LWORD |
0 |
2**64-1 |
64 bit |
N/A |
N/A |
N/A |
LINT |
-2**63 |
2**63-1 |
64 bit |
N/A |
N/A |
N/A |
ULINT |
0 |
2**64-1 |
64 bit |
N/A |
N/A |
N/A |
REAL |
-3.4E+38 |
3.4E+38 |
32 bit |
ai, ao |
asynFloat64 |
asynFloat32ArrayIn, AsynFloat32ArrayOut |
LREAL |
-1.797693134862316e+308 |
1.797693134862358e+308 |
64 bit |
ai, ao |
asynFloat64 |
asynFloat64ArrayIn, AsynFloat64ArrayOut |
STRING |
Varies |
waveform |
asynFloat64 |
asynInt8ArrayIn, asynInt8ArrayOut |
Lines marked as N/A are not supported by pytmc.
Pragma syntax¶
At a minimum, developers must specify a PV. Specifying an IO direction for each field is recommended but not required. This would look like the following:
{attribute 'pytmc' := '
pv: TEST:MAIN:SCALE
io: i
'}
scale : LREAL := 0.0;
The {attribute 'pytmc' := '
and '}
specify the beginning and end of the
pragma that pytmc will recognize. The middle two lines specify the
configuration for this variable.
Pytmc uses a custom system of configuration where newlines and white space in
a line is important. All lines begin with a title and the title ends before the
colon. All parts thereafter are the ‘tag’ or the configuration state for this
setting. Some title types such as field
can have multiple settings for a
single PV.
A pragma could have multiple fields specified. For example, an ai
record
TEST:MAIN:SCALE
would be generated from the following, with a slope of
2.0 and an offset of 1.0, updating only at a rate of once per second:
{attribute 'pytmc' := '
pv: TEST:MAIN:SCALE
io: i
field: SCAN 1 second
field: AOFF 1.0
field: ASLO 2.0
'}
scale : LREAL := 0.0;
Reducing update rate¶
By default, all records will have a scan rate of I/O Intr
. This means that
even if the value updates on every PLC cycle, EPICS will see (most) of those
events.
In the case of values that update quickly, it may be preferable to reduce the rate at which EPICS sees updates. This can be done by setting the SCAN field to poll at a fixed rate.
Declaring top level variables¶
This is an example of the simplest configuration a developer can provide to instantiate a variable.
{attribute 'pytmc' := '
pv: TEST:MAIN:SCALE
io: i
'}
scale : LREAL := 0.0;
The developer must specify the PV name (pv:
line). All other fields are
optional. We recommend that the user specify the direction of the
data (io:
line) however.
Pytmc needs no additional information but users have the option to override
default settings manually. For example a developer can specify the SCAN
field , which configures how and when the value is updated, even though this is
not required. For additional information on all the pragma fields, consult the
Pragma fields section.
Declaring encapsulated variables¶
Variables declared inside of data structures can be processed by pytmc so long as each level of encapsulation, all the way down to the first level, is marked for pytmc.
The instantiation of encapsulating data types only needs the pv:
line. The
instantiation of a function block could resemble the following:
{attribute 'pytmc' := '
pv: TEST:MAIN:COUNTER_B
'}
counter_b : counter;
A variable declared within the counter
function block could resemble the
following:
{attribute 'pytmc' := '
pv: VALUE
io: i
'}
value_d : DINT;
When combined, the PV specified at the instantiation of the user-defined data
type will be appended to the beginning of the PV for all data types defined
within. Each step further into a data structure can add an additional section
to the PV. In the example above the final PV will be
TEST:MAIN:COUNTER_B:VALUE
. The colons are automatically included.
This can be recursively applied to data types containing data types.
Information other than the PV name name can be specified at the datatype instantiation if you wish to make generalizations about the variables contained inside. These generalizations are overridden if the same field is specified either on a contained datatype or variable.
For example the following code block will assign a field:
of SCAN 1
second
to all the variables and datatypes that it contains unless they
specify their own version of the
{attribute 'pytmc' := '
pv: BASE
field: SCAN 1 second
'}
counter_b : counter;
{attribute 'pytmc' := '
pv: VALUE_F_R
field: SCAN 1 second
io: i
'}
value_d : DINT;
Declaring bidirectional PVs¶
In instances where a single TwinCAT variable should be able to be both written and read, multiple PVs can be specified. This allows multiple EPICS records to be tied to a single TwinCAT variable.
{attribute 'pytmc' := '
pv: TEST:MAIN:ULIMIT
io: io
'}
upper_limit : DINT := 5000;
In this case, two records will be generated: TEST:MAIN:ULIMIT
and
TEST:MAIN:ULIMIT_RBV
.
Pragma fields¶
Each line of the pragma indicates to pytmc how to generate the corresponding records in the database file output.
pv¶
This constructs the PV name that will represent this variable in EPICS. It is the only mandatory configuration line. This line can be used on specific variables as well as the instantiations of data types.
When used on variables declared in the main scope, the PV for the variable will be generated verbatim. When used on instantiations, this string will be appended to the front of any PVs that are declared within the data type.
io¶
This is a guessed field that defaults to ‘io’. Specify the whether the IOC is reading or writing this value. Values being sent from the PLC to the IOC should be marked as input with ‘i’ and values being sent to the PLC from the IOC should be marked ‘o’. Bidirectional PVs can be specified with ‘io’.
fields¶
This specifies additional field(s) to be set on the generated EPICS record(s). Multiple field lines are allowed. These lines determine the PV’s behaviors such as alarm limits and scanning frequency.
The format is as follows:
field: FIELD_NAME field value
This would correspond to a field in the record being generated as follows:
record(ai, "my_record") {
...
field(FIELD_NAME, "field value")
}
SCAN¶
The SCAN
field is special. Pytmc will guess a scan field if not provided
but like io
and pv
, the correct setting is on a case-by-case basis.
pytmc itself cannot know at what rate a variable will update on the PLC side.
Valid options for this field are:
"Passive"
"I/O Intr"
"10 second"
"5 second"
"2 second"
"1 second"
".5 second"
".2 second"
".1 second"