A PLC's description of one of its variables: its name, the name of its type, where it is, and how big.
A PLC has a table of its symbols when the project is built with symbols, as TwinCAT does by
default. ADS.Client.symbol/3 asks for one by name (index group 0xF009), and
ADS.Client.symbols/2 uploads all of them (0xF00B, its size from 0xF00F). Each is an entry
of variable length:
| Bytes | Field |
|---|---|
| 4 | the entry's length, padding included |
| 4, 4 | index group and offset |
| 4 | size in bytes |
| 4 | ADS data type id (see ADS.Type.id_name/1) |
| 4 | flags |
| 2, 2, 2 | lengths of the name, the type and the comment |
| ... | the name, the type and the comment, each with a zero after it |
| 16 | the type's GUID, if flagged (0x0008) |
| ... | attributes, if flagged (0x1000): a count of 2 bytes, then each with a byte for the length of its name and one for its value, and both with a zero after |
Beckhoff's manuals don't describe this layout, only how to ask for it; it's written here from general knowledge of the format, not from another implementation's code, and is yet to be checked against a PLC.
Beckhoff recommends reaching a variable by a handle from its name, as ADS.Client does, rather
than by its index group and offset, which change when the program does.
Summary
Functions
The symbol entry at the start of the bytes, as in the answer to a request by name; its strings
in UTF-8 if they are, as plain ASCII is, or else in the code page (see ADS.Codepage).
Every symbol entry in the bytes, as uploaded; the strings as for decode/2.
The flags of a symbol by name, those that are known: :persistent, :read_only and so on.
Types
@type t() :: %ADS.Symbol{ attributes: %{required(String.t()) => String.t()}, comment: String.t(), data_type: atom() | non_neg_integer(), flags: non_neg_integer(), guid: String.t() | nil, index_group: ADS.index_group(), index_offset: ADS.index_offset(), name: String.t(), size: non_neg_integer(), type: String.t() }
Functions
@spec decode(binary(), ADS.Codepage.t()) :: {:ok, t()} | :error
The symbol entry at the start of the bytes, as in the answer to a request by name; its strings
in UTF-8 if they are, as plain ASCII is, or else in the code page (see ADS.Codepage).
@spec decode_all(binary(), ADS.Codepage.t()) :: {:ok, [t()]} | :error
Every symbol entry in the bytes, as uploaded; the strings as for decode/2.
The flags of a symbol by name, those that are known: :persistent, :read_only and so on.