SLMP Protocol for Rust
Async Rust implementation of the SLMP library, based on the plc-comm-slmp-dotnet
implementation and aligned with the shared plc-comm-slmp-cross-verify harness.
The crate focuses on Binary 3E / 4E SLMP over TCP and UDP and keeps the same operation meaning as the existing Python, .NET, C++, Node-RED, and Rust verification clients.
What This Repo Contains
- async Rust library crate:
src/ cross-verifywrapper binary:src/bin/slmp_verify_client.rs- runnable examples:
examples/ - address and usage guides:
docs/ - minimal
napi-rsworkspace member for future Node packaging:crates/slmp-node
Current Scope
- raw device access: word, bit, dword, float32
- random read/write
- block read/write
- extended-device read/write
- memory read/write
- extend-unit read/write
- remote operations and self-test
- high-level typed helpers
- high-level named read/write and polling helpers
slmp_verify_clientwrapper forplc-comm-slmp-cross-verify- minimal
napi-rsNode binding scaffold incrates/slmp-node
Installation
Install from crates.io:
The public package name is plc-comm-slmp-rust, and the library import path is
plc_comm_slmp.
Requires Rust 1.85 or newer.
Cargo.toml:
[]
= "0.1.1"
= { = "1", = ["rt-multi-thread", "macros"] }
Quick Start
Raw Client Usage
use ;
async
Recommended High-Level Usage
use ;
async
Typed Access
use ;
# async
Runnable Examples
The repository includes examples that compile as part of the crate and can be run directly against a PLC or mock server.
raw_read_write
Low-level word read plus optional write/read-back.
SLMP_HOST=192.168.250.100 \
SLMP_PORT=1025 \
SLMP_FRAME=4e \
SLMP_SERIES=iqr \
Enable writes explicitly:
SLMP_ENABLE_WRITES=1 \
SLMP_WRITE_ADDRESS=D600 \
SLMP_WRITE_VALUES=111,222 \
named_helpers
Named snapshot, typed decoding, optional write_named, and one poll_named
tick.
SLMP_HOST=192.168.250.100 \
SLMP_NAMED_ADDRESSES='D100,D200:F,D50.3,LTN10:D,LTS10' \
advanced_operations
Safe read-heavy sample that covers type-name, random read, block read, extended device read, and self-test loopback.
SLMP_HOST=192.168.250.100 \
SLMP_RANDOM_WORDS='D100,R10' \
SLMP_RANDOM_DWORDS='D200,LTN10' \
SLMP_EXT_DEVICE='J1\W10' \
device_matrix_compare
Real-PLC regression sample that writes the same address through multiple command paths and checks that read-back stays aligned.
- bit devices:
write_bits,write_random_bits,write_typed,write_named, rawrequest - word devices:
write_words,write_random_words,write_typed,write_named, rawrequest - 32-bit devices:
write_dwords,write_random_words,write_typed,write_named, rawrequest J1\\...devices: extended helper APIs plus rawrequest
SLMP_HOST=192.168.250.100 \
SLMP_PORT=1025 \
SLMP_FRAME=4e \
SLMP_SERIES=iqr \
This example exits non-zero when command paths for the same address disagree.
Focus on a subset while debugging:
SLMP_COMPARE_ONLY='LTS10,LTC10,LCS10,LCC10,LTN10,LSTN10' \
The shared environment variables for these examples are documented in
docs/RECIPES.md.
Public API Surface
Main exports:
SlmpConnectionOptionsSlmpClientSlmpAddressread_typed/write_typedwrite_bit_in_wordread_named/write_namedpoll_namedread_words_single_request/read_dwords_single_requestread_words_chunked/read_dwords_chunkedwrite_words_single_request/write_dwords_single_requestwrite_words_chunked/write_dwords_chunked
Important model types:
SlmpDeviceAddressSlmpQualifiedDeviceAddressSlmpTargetAddressSlmpExtensionSpecSlmpTypeNameInfoSlmpRandomReadResultSlmpBlockRead,SlmpBlockWrite,SlmpBlockReadResultSlmpLongTimerResultSlmpValue
Supported Address Forms
High-level helpers are intended to cover these forms first.
- plain word devices:
D100,R50,ZR0,TN0,CN0 - plain bit devices:
M1000,X20,Y20,B10 - typed suffixes:
D100:S,D200:D,D300:L,D400:F - bit-in-word form:
D50.3 - long current-value forms:
LTN10:D,LSTN20:D,LCN30:D - extended devices:
J1\\SW0,U3\\G100,U1\\HG0
.bit notation is only valid for word devices. Address bit devices directly.
See also:
Choosing the Right API
- Use raw device methods when you need exact SLMP request control.
- Use
read_typedandwrite_typedwhen one address maps to one scalar value. - Use
read_namedandwrite_namedwhen your application needs a snapshot with mixed dtypes and bit-in-word decoding. - Use
poll_namedfor a lightweight periodic stream. - Use
read_randomandread_blockwhen you want to keep request counts low. - Use the extended-device methods for
J...andU...paths. read_namedandwrite_namedcurrently target plain device addresses, notJ...orU...qualified addresses.
Long-Family Behavior
The Rust implementation follows the same normalized behavior as the other libraries:
LTN,LSTN,LCN, andLZdefault to 32-bit readsLTS,LTC,LSTS,LSTC,LCS, andLCCare state readsLCSandLCCare decoded from theLCN4-word status block, not by a standalone direct-bit batch readLCSandLCCare rejected forRead Random (0x0403),Read Block (0x0406),Write Block (0x1406), andEntry Monitor Device (0x0801)- direct reads for
LTS,LTC,LSTS, andLSTCare rejected by the Rust client API; use helper APIs or 4-word block reads fromLTN/LSTN - direct dword reads for
LTNandLSTNare rejected; use helper APIs or explicit 4-word block reads
That behavior is intentional and is enforced through
plc-comm-slmp-cross-verify.
Cross-Verify
This repo is designed to participate in:
plc-comm-slmp-cross-verify/specs/sharedpython verify.py --clients rust- full parity runs with Python, .NET, C++, and Node-RED
- live PLC verification through the same saved baseline/profile flow
The wrapper binary used by the harness is:
Development
Format and test:
Run the Rust tests:
Check the Node binding scaffold:
Run Rust-only parity through the canonical harness:
Run full parity:
Run live PLC verification with the validated R120 profile:
Node Binding
crates/slmp-node is currently a thin napi-rs scaffold. It is not yet the
main delivery path. The current purpose is to keep the Rust workspace ready for
future Node package work without redesigning the crate layout later.
License
Distributed under the MIT License.