SLMP Python User Guide
This guide documents the recommended high-level helper APIs.
For normal application code, start here instead of the low-level protocol methods.
Recommended Entry Points
Async connection with explicit profile selection
import asyncio
from slmp import SlmpConnectionOptions, open_and_connect, read_named
async def main() -> None:
options = SlmpConnectionOptions(
host="192.168.250.100",
port=1025,
plc_series="iqr",
frame_type="4e",
)
async with await open_and_connect(options) as client:
snapshot = await read_named(client, ["D100", "D200:F", "D50.3"])
print(snapshot)
asyncio.run(main())
Use this when:
- you are writing an async application
- you already know the target frame/profile pair
- you want the shortest path to
read_named,write_typed, andpoll
Async shared connection
import asyncio
from slmp import AsyncSlmpClient, QueuedAsyncSlmpClient, read_named
async def main() -> None:
inner = AsyncSlmpClient(
"192.168.250.100",
port=1025,
plc_series="iqr",
frame_type="4e",
)
async with QueuedAsyncSlmpClient(inner) as client:
first = await read_named(client, ["D100", "D200:F"])
second = await read_named(client, ["D300", "D50.3"])
print(first)
print(second)
asyncio.run(main())
Use this when multiple coroutines share one PLC connection.
Sync application code
from slmp import SlmpConnectionOptions, open_and_connect_sync, read_named_sync, write_typed_sync
options = SlmpConnectionOptions(
host="192.168.250.100",
port=1025,
plc_series="iqr",
frame_type="4e",
)
with open_and_connect_sync(options) as client:
print(read_named_sync(client, ["D100", "D200:F", "D50.3"]))
write_typed_sync(client, "D100", "U", 42)
For sync code, the recommended pattern is:
- open a
SlmpClient - use the
*_synchelper functions
Address normalization
from slmp import normalize_address
assert normalize_address("x20") == "X20"
assert normalize_address("d200") == "D200"
High-Level Helper Set
read_typed / read_typed_sync
Read one logical value with type conversion.
Supported dtype values:
| dtype | Meaning | Words |
|---|---|---|
U |
unsigned 16-bit | 1 |
S |
signed 16-bit | 1 |
D |
unsigned 32-bit | 2 |
L |
signed 32-bit | 2 |
F |
float32 | 2 |
value_u = await read_typed(client, "D100", "U")
value_f = await read_typed(client, "D200", "F")
value_l = await read_typed(client, "D300", "L")
write_typed / write_typed_sync
Write one logical value with type conversion.
await write_typed(client, "D100", "U", 1234)
await write_typed(client, "D200", "F", 3.14)
await write_typed(client, "D300", "L", -1000)
write_bit_in_word / write_bit_in_word_sync
Set or clear one bit inside a word device.
await write_bit_in_word(client, "D50", bit_index=3, value=True)
await write_bit_in_word(client, "D50", bit_index=3, value=False)
Use this when a PLC stores flags inside a control word and you need to toggle only one bit.
read_named / read_named_sync
Read multiple values with one high-level call.
Address notation:
| Form | Meaning |
|---|---|
D100 |
one unsigned 16-bit word |
D200:S |
signed 16-bit |
D300:D |
unsigned 32-bit |
D400:L |
signed 32-bit |
D500:F |
float32 |
D50.3 |
bit 3 inside D50 |
snapshot = await read_named(
client,
[
"D100",
"D200:S",
"D300:D",
"D400:L",
"D500:F",
"D50.3",
],
)
Use .bit notation only with word devices such as D50.3.
Address bit devices directly as M1000, M1001, X20, or Y20.
Long-device notes for the high-level helper layer:
LTN,LSTN, andLCNdefault to 32-bit current-value accessLTS,LTC,LSTS, andLSTCare resolved through the correspondingLTN/LSTNhelper-backed 4-word decode instead of direct state reads
This is the most useful helper for dashboards, logging, and application polling.
write_named / write_named_sync
Write multiple logical values using the same address notation.
await write_named(
client,
{
"D100": 42,
"D200:S": -1,
"D300:D": 123456,
"D400:L": -5000,
"D500:F": 1.25,
"D50.3": True,
},
)
Plain LTN, LSTN, and LCN addresses are treated as 32-bit current values in the high-level write helper too.
read_words_single_request / read_words_single_request_sync
Read a contiguous word range using exactly one PLC request.
words = await read_words_single_request(client, "D0", 10)
If the requested length does not fit in one request, this helper returns an error.
read_words_chunked / read_words_chunked_sync
Read a large contiguous word range using explicit multi-request chunking.
large_words = await read_words_chunked(client, "D0", 1000)
Use this only when protocol-defined chunk boundaries are acceptable for the caller.
read_dwords_single_request / read_dwords_single_request_sync
Read contiguous 32-bit values using exactly one PLC request.
dwords = await read_dwords_single_request(client, "D200", 8)
If the requested length does not fit in one request, this helper returns an error.
read_dwords_chunked / read_dwords_chunked_sync
Read a large contiguous dword range using explicit multi-request chunking.
large_dwords = await read_dwords_chunked(client, "D200", 200)
This helper preserves 32-bit boundaries. It does not split inside one logical dword.
poll / poll_sync
Yield repeated snapshots at a fixed interval.
async for snapshot in poll(client, ["D100", "D200:F", "D50.3"], interval=1.0):
print(snapshot)
Sync variant:
for snapshot in poll_sync(client, ["D100", "D200:F", "D50.3"], interval=1.0):
print(snapshot)
Practical Example Sets
Example 1: process values
snapshot = await read_named(
client,
[
"D100:F", # temperature
"D102:F", # pressure
"D200", # recipe number
"D50.0", # run flag
"D50.1", # alarm flag
],
)
Example 2: recipe download
await write_named(
client,
{
"D100": 10,
"D101": 20,
"D102": 30,
"D200:F": 12.5,
"D202:F": 6.75,
},
)
Example 3: large historian read
history_words = await read_words_chunked(client, "D1000", 1200)
history_dwords = await read_dwords_chunked(client, "D2000", 240)
Example 4: one shared async connection
inner = AsyncSlmpClient("192.168.250.100", port=1025, plc_series="iqr", frame_type="4e")
async with QueuedAsyncSlmpClient(inner) as client:
a = await read_named(client, ["D100", "D200:F"])
b = await read_named(client, ["D300", "D50.3"])
Sample Programs
The most complete examples are:
samples/high_level_sync.pysamples/high_level_async.py
They are designed to be directly runnable and syntax-check clean.
Run them from the repository root:
python samples/high_level_sync.py --host 192.168.250.100 --port 1025 --series iqr
python samples/high_level_async.py --host 192.168.250.100 --port 1025 --series iqr --frame-type 4e
See also: