Skip to content

Commit c73d3cd

Browse files
gijzelaerrclaude
andcommitted
Unified Tag API replacing SymbolTable
Replace the homegrown SymbolTable class with a simpler Tag-based API using PLC4X / Siemens STEP7 address syntax that's familiar to every TIA Portal user. New API: - snap7.tags.Tag dataclass: area, db_number, byte_offset, bit, datatype, count, name - Tag.from_string("DB1.DBX0.0:BOOL") — full PLC4X parser - load_csv / load_json / load_tia_xml — return dict[str, Tag] - Client.read_tag(tag_or_str) — read typed value - Client.write_tag(tag_or_str, value) — write typed value - Client.read_tags([tag, ...]) — batch read via optimizer Address syntax supported: - DB1.DBX0.0:BOOL, DB1.DBB10:BYTE, DB1.DBW10:INT, DB1.DBD10:REAL - DB1:10:INT (short form), DB1:10:STRING[20], DB1:10:REAL[5] (array) - M10.5:BOOL, MW20:WORD (Merker) - I0.0:BOOL, Q0.0:BOOL (I/O) - Leading %% optional Removed: - snap7/util/symbols.py (SymbolTable, TagAddress) - tests/test_symbols.py - doc/API/symbols.rst SymbolTable was experimental, unreleased, and its homegrown dict syntax was not aligned with industry convention. The new Tag string syntax matches what TIA Portal watch tables use. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8a0ff08 commit c73d3cd

15 files changed

Lines changed: 1022 additions & 1189 deletions

CHANGES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ Major release: new `s7` package with S7CommPlus protocol support.
2323
* S7CommPlus CPU state reading and block transfer (upload/download)
2424
* Array read/write helpers (`db_read_array`, `db_write_array`)
2525
* Missing data type setters: `set_lint`, `set_ulint`, `set_ltime`, `set_ltod`, `set_ldt`
26-
* Optimized `SymbolTable.read_many()` with multi-variable batching
26+
* **Unified Tag API**: `client.read_tag("DB1.DBD0:REAL")` with PLC4X /
27+
Siemens STEP7 syntax, replacing the homegrown SymbolTable class.
28+
Loaders: `load_csv`, `load_json`, `load_tia_xml` return `dict[str, Tag]`
2729
* Optimizer excludes counter/timer areas from byte-range merging
2830
* Fixed `get_cpu_info` field offsets for real S7-300/1500 (thanks @qzertywsx)
2931
* Fixed `S7SZL.__str__` attribute name typo (thanks @qzertywsx)

doc/API/symbols.rst

Lines changed: 0 additions & 38 deletions
This file was deleted.

doc/API/tags.rst

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
Tags
2+
====
3+
4+
Symbolic and typed access to PLC variables.
5+
6+
A :class:`~snap7.tags.Tag` describes a typed value at a specific S7 address.
7+
Tags can be constructed from a PLC4X-style address string, or loaded in
8+
bulk from CSV, JSON, or TIA Portal XML exports.
9+
10+
.. code-block:: python
11+
12+
from s7 import Client, Tag, load_tia_xml
13+
14+
client = Client()
15+
client.connect("192.168.1.10", 0, 1)
16+
17+
# Ad-hoc access with PLC4X-style strings
18+
speed = client.read_tag("DB1.DBD0:REAL")
19+
running = client.read_tag("DB1.DBX4.0:BOOL")
20+
client.write_tag("DB1.DBW6:INT", 1500)
21+
22+
# Batch read (uses optimizer when enabled)
23+
values = client.read_tags(["DB1.DBD0:REAL", "DB1.DBW6:INT"])
24+
25+
# Load named tags from a TIA Portal XML export
26+
tags = load_tia_xml("db1.xml")
27+
temperature = client.read_tag(tags["Motor.Temperature"])
28+
29+
Address syntax
30+
--------------
31+
32+
Tag addresses follow the PLC4X / Siemens STEP7 convention::
33+
34+
DB1.DBX0.0:BOOL # bit in data block
35+
DB1.DBB10:BYTE # byte
36+
DB1.DBW10:INT # word (2 bytes)
37+
DB1.DBD10:REAL # double word (4 bytes)
38+
DB1:10:INT # short form (DB 1, offset 10)
39+
DB1:10:STRING[20] # variable-length string
40+
DB1:10:REAL[5] # array of 5 REALs
41+
M10.5:BOOL # Merker bit
42+
MW20:WORD # Merker word
43+
I0.0:BOOL # input bit
44+
Q0.0:BOOL # output bit
45+
46+
The leading ``%`` is optional (``%DB1.DBX0.0:BOOL`` also works).
47+
48+
Supported types
49+
---------------
50+
51+
``BOOL``, ``BYTE``, ``CHAR``, ``WCHAR``, ``SINT``, ``USINT``, ``INT``,
52+
``UINT``, ``WORD``, ``DINT``, ``UDINT``, ``DWORD``, ``LINT``, ``ULINT``,
53+
``LWORD``, ``REAL``, ``LREAL``, ``TIME``, ``LTIME``, ``TOD``, ``LTOD``,
54+
``DATE``, ``DT``, ``LDT``, ``DTL``, ``STRING[n]``, ``WSTRING[n]``,
55+
``FSTRING[n]``.
56+
57+
Arrays are supported for any fixed-size type via ``[count]`` suffix.
58+
59+
API reference
60+
-------------
61+
62+
.. automodule:: snap7.tags
63+
:members:

doc/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Welcome to python-snap7's documentation!
4444
API/partner
4545
API/logo
4646
API/util
47-
API/symbols
47+
API/tags
4848
API/optimizer
4949
API/log
5050
API/type

example/s7_symbols.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
1-
"""Symbolic addressing example — read/write by tag name.
1+
"""Tag-based symbolic addressing example.
22
33
Usage:
44
python example/s7_symbols.py 192.168.1.10
55
"""
66

77
import sys
8-
from s7 import Client, SymbolTable
98

10-
address = sys.argv[1] if len(sys.argv) > 1 else "192.168.1.10"
9+
from s7 import Client, Tag
1110

12-
# Define symbols (or use SymbolTable.from_csv("tags.csv"))
13-
symbols = SymbolTable(
14-
{
15-
"Motor1.Speed": {"db": 1, "offset": 0, "type": "REAL"},
16-
"Motor1.Running": {"db": 1, "offset": 4, "bit": 0, "type": "BOOL"},
17-
"SetPoint": {"db": 1, "offset": 6, "type": "INT"},
18-
}
19-
)
11+
address = sys.argv[1] if len(sys.argv) > 1 else "192.168.1.10"
2012

2113
client = Client()
2214
client.connect(address, 0, 1)
2315

24-
# Read by name
25-
speed = symbols.read(client, "Motor1.Speed")
26-
running = symbols.read(client, "Motor1.Running")
16+
# Ad-hoc tag read using PLC4X-style syntax
17+
speed = client.read_tag("DB1.DBD0:REAL")
18+
running = client.read_tag("DB1.DBX4.0:BOOL")
2719
print(f"Speed: {speed!r}, Running: {running!r}")
2820

29-
# Write by name
30-
symbols.write(client, "SetPoint", 1500)
21+
# Write a value
22+
client.write_tag("DB1.DBW6:INT", 1500)
23+
24+
# Read multiple tags in one optimized request
25+
values = client.read_tags(["DB1.DBD0:REAL", "DB1.DBW6:INT"])
26+
print(f"Batch: {values!r}")
27+
28+
# Or build tags programmatically / load from file
29+
# tags = load_csv("tags.csv") # returns dict[str, Tag]
30+
# value = client.read_tag(tags["Motor.Speed"])
3131

32-
# Batch read (uses optimizer when available)
33-
values = symbols.read_many(client, ["Motor1.Speed", "SetPoint"])
34-
print(f"Batch: {values}")
32+
# Tag instances are also accepted directly
33+
temperature_tag = Tag.from_string("DB1.DBD0:REAL", name="Temperature")
34+
temp = client.read_tag(temperature_tag)
35+
print(f"Temperature: {temp!r}")
3536

3637
client.disconnect()

s7/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
from snap7.type import Area, Block, WordLen, SrvEvent, SrvArea
2222
from snap7.util.db import Row, DB
23-
from snap7.util.symbols import SymbolTable
23+
from snap7.tags import Tag, load_csv, load_json, load_tia_xml
2424

2525
__all__ = [
2626
"Client",
@@ -36,5 +36,8 @@
3636
"SrvArea",
3737
"Row",
3838
"DB",
39-
"SymbolTable",
39+
"Tag",
40+
"load_csv",
41+
"load_json",
42+
"load_tia_xml",
4043
]

s7/_s7commplus_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ def browse(self) -> list[dict[str, Any]]:
327327
328328
Returns a flat list of variable info dicts with keys:
329329
``name``, ``db_number``, ``byte_offset``, ``data_type``, ``bit_size``.
330-
Results can be used to construct a :class:`~snap7.util.symbols.SymbolTable`.
330+
Results can be converted to :class:`~snap7.tags.Tag` objects for use
331+
with :meth:`~s7.client.Client.read_tag`.
331332
332333
Returns:
333334
List of variable info dicts.

s7/client.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,12 @@ def browse(self) -> list[dict[str, Any]]:
287287
288288
.. warning:: This method is **experimental** and may change.
289289
290-
Returns a flat list of variable info dicts. Can be used to create
291-
a :class:`~snap7.util.symbols.SymbolTable`::
290+
Returns a flat list of variable info dicts. Can be converted to
291+
:class:`~snap7.tags.Tag` objects::
292292
293-
symbols = SymbolTable.from_browse(client.browse())
293+
from snap7 import Tag
294+
variables = client.browse()
295+
tags = {v["name"]: Tag(Area.DB, v["db_number"], v["byte_offset"], v["data_type"]) for v in variables}
294296
295297
Requires S7CommPlus connection.
296298
"""

snap7/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from .partner import Partner
2222
from .logo import Logo
2323
from .util.db import Row, DB
24-
from .util.symbols import SymbolTable
24+
from .tags import Tag, load_csv, load_json, load_tia_xml
2525
from .type import Area, Block, WordLen, SrvEvent, SrvArea
2626

2727
__all__ = [
@@ -32,7 +32,10 @@
3232
"Logo",
3333
"Row",
3434
"DB",
35-
"SymbolTable",
35+
"Tag",
36+
"load_csv",
37+
"load_json",
38+
"load_tia_xml",
3639
"Area",
3740
"Block",
3841
"WordLen",

0 commit comments

Comments
 (0)