欢迎光临
我们一直在努力

ModbusLink 客户端使用说明

📚 ModbusLink 客户端使用说明

本文档详细介绍了 ModbusLink 软件包中客户端的使用方法。ModbusLink 的客户端分为 异步 (Async) 和 同步 (Sync) 两种实现,分别适用于高并发/异步框架(如 FastAPI, GUI)和传统的阻塞式脚本开发。


核心概念:依赖注入

ModbusLink 采用 传输层 (Transport) 与 协议层 (Client) 分离的设计。 在使用客户端之前,您必须先创建对应的传输层实例(TCP, RTU 或 ASCII),然后将其传递给客户端。

  • Transport: 负责底层的字节发送与接收(处理 Socket, Serial, 帧校验等)。
  • Client: 负责 Modbus 协议的逻辑封装(构建 PDU, 解析响应, 数据转换)。

1. 异步客户端 (AsyncModbusClient)

适用于需要非阻塞 I/O、高并发采集或集成到异步框架的场景。

1.1 初始化

使用 async with 语法可以自动管理连接的建立与断开。

import asyncio
from modbuslink import AsyncModbusClient
# 根据硬件选择对应的传输层:
from modbuslink import AsyncTcpTransport # 以太网
# from modbuslink import AsyncRtuTransport # 串口 RTU
# from modbuslink import AsyncAsciiTransport # 串口 ASCII

async def main():
# 1. 配置传输层 (以 TCP 为例)
transport = AsyncTcpTransport(host='127.0.0.1', port=502, timeout=1.0)

# 2. 注入传输层创建客户端
# 使用 async with 自动处理 connect/close
async with AsyncModbusClient(transport) as client:
# 在此处执行操作…
pass

if __name__ == "__main__":
asyncio.run(main())

1.2 基本操作示例 (Basic Operations)

涵盖 Modbus 标准协议的基础读写功能。

🔹 读线圈 / 离散输入 / 寄存器
函数名Modbus 功能码描述参数说明
read_coils 0x01 读线圈状态 (RW) slave_id: 从站地址start_address: 起始地址quantity: 数量 (1-2000)
read_discrete_inputs 0x02 读离散输入状态 (RO) 同上
read_holding_registers 0x03 读保持寄存器 (RW) slave_id: 从站地址start_address: 起始地址quantity: 数量 (1-125)
read_input_registers 0x04 读输入寄存器 (RO) 同上

代码示例:

# 1. 读取线圈 (0x01)
# 结果: [True, False, True, …]
coils = await client.read_coils(slave_id=1, start_address=0, quantity=10)

# 2. 读取离散输入 (0x02)
inputs = await client.read_discrete_inputs(slave_id=1, start_address=0, quantity=10)

# 3. 读取保持寄存器 (0x03)
# 结果: [100, 200, 300, …] (原始 16位 整数)
holds = await client.read_holding_registers(slave_id=1, start_address=0, quantity=10)

# 4. 读取输入寄存器 (0x04)
in_regs = await client.read_input_registers(slave_id=1, start_address=0, quantity=10)

🔹 写线圈 / 寄存器
函数名Modbus 功能码描述参数说明
write_single_coil 0x05 写单个线圈 slave_id: 从站地址address: 地址value: bool (True/False)
write_single_register 0x06 写单个寄存器 slave_id: 从站地址address: 地址value: int (0-65535)
write_multiple_coils 0x0F 写多个线圈 start_address: 起始地址values: List[bool] (数量 1-1968)
write_multiple_registers 0x10 写多个寄存器 start_address: 起始地址values: List[int] (数量 1-123)

代码示例:

# 5. 写单个线圈 (0x05)
await client.write_single_coil(slave_id=1, address=0, value=True)

# 6. 写单个寄存器 (0x06)
await client.write_single_register(slave_id=1, address=10, value=12345)

# 7. 写多个线圈 (0x0F)
await client.write_multiple_coils(
slave_id=1,
start_address=0,
values=[True, False, True, True]
)

# 8. 写多个寄存器 (0x10)
await client.write_multiple_registers(
slave_id=1,
start_address=10,
values=[11, 22, 33, 44]
)

1.3 高级数据类型示例 (Advanced Operations)

ModbusLink 内置了 PayloadCoder,可以自动将多个 16 位寄存器合并转换为浮点数、32/64位整数或字符串。

通用参数说明:

  • byte_order: 字节序 (“big” / “little”), 默认为 “big”。
  • word_order: 字序 (“high” / “low”), 默认为 “high”。
  • encoding: 字符串编码 (如 “utf-8”, “ascii”), 默认为 “utf-8”。
🔹 浮点数与整数 (32位/64位)

# — 32位 浮点数 (Float32) —
# 写入 (占用 2 个寄存器)
await client.write_float32(slave_id=1, start_address=100, value=25.6)
# 读取
temp = await client.read_float32(slave_id=1, start_address=100)

# — 32位 整数 (Int32 / UInt32) —
# 写入有符号整数 (占用 2 个寄存器)
await client.write_int32(slave_id=1, start_address=102, value=50000)
# 读取无符号整数
speed = await client.read_uint32(slave_id=1, start_address=104)

# — 64位 整数 (Int64 / UInt64) —
# 写入 (占用 4 个寄存器)
await client.write_int64(slave_id=1, start_address=106, value=9876543210)
# 读取
total_energy = await client.read_int64(slave_id=1, start_address=106)

# — 指定字节序/字序 (解决设备兼容性) —
# 例如:某些 PLC 使用 Little Endian, Low Word First (CDAB格式)
val = await client.read_float32(
slave_id=1,
start_address=200,
byte_order="little",
word_order="low"
)

🔹 字符串 (String)

# 写入字符串
# 库会自动计算所需的寄存器数量并进行填充
await client.write_string(slave_id=1, start_address=300, value="Hello Modbus")

# 读取字符串
# 注意:length 为字节长度,不是寄存器数量
text = await client.read_string(slave_id=1, start_address=300, length=12)
print(text) # 输出: Hello Modbus

1.4 回调操作示例 (Callback)

异步客户端的所有读写方法都支持 callback 参数。当 I/O 操作完成并收到数据后,会在后台 Task 中自动调用该函数,主程序无需等待处理逻辑。

def on_temp_read(value):
print(f"[回调] 当前温度: {value}°C")

def on_write_done():
print("[回调] 参数写入成功")

# 1. 带回调的读取
# 该行代码会立即返回,不会阻塞后续代码执行
# 当数据从设备返回时,on_temp_read 会被触发
await client.read_float32(
slave_id=1,
start_address=100,
callback=on_temp_read
)

# 2. 带回调的写入
await client.write_single_register(
slave_id=1,
address=200,
value=1,
callback=on_write_done
)

# 模拟主线程继续做其他事情
print("主线程继续运行…")
await asyncio.sleep(1)

1.5 并发操作示例 (Concurrency)

利用 asyncio.gather,可以同时向设备发送多个请求(或向多个设备发送请求),极大地缩短总轮询时间。

# 创建任务列表 (此时请求尚未发送)
tasks = [
# 任务A: 读取电压
client.read_float32(slave_id=1, start_address=0),
# 任务B: 读取电流
client.read_float32(slave_id=1, start_address=2),
# 任务C: 读取功率
client.read_float32(slave_id=1, start_address=4),
# 任务D: 读取状态位 (10个)
client.read_coils(slave_id=1, start_address=0, quantity=10)
]

print("开始并发采集…")
start_time = asyncio.get_event_loop().time()

# 并发执行所有任务
# results 列表将按 tasks 的顺序包含返回结果
results = await asyncio.gather(*tasks)

end_time = asyncio.get_event_loop().time()
print(f"采集耗时: {end_time start_time:.3f}秒")

# 解包结果
voltage, current, power, status = results
print(f"V={voltage}, A={current}, W={power}, St={status}")


2. 同步客户端 (SyncModbusClient)

适用于简单的脚本、测试工具或不需要并发能力的场景。接口命名与异步客户端完全一致,但调用方式为阻塞式。

2.1 初始化

from modbuslink import SyncModbusClient
from modbuslink import SyncRtuTransport # 以 RTU 为例

def main():
# 1. 配置串口参数
transport = SyncRtuTransport(
port='COM3',
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1,
timeout=1.0
)

# 2. 创建同步客户端
with SyncModbusClient(transport) as client:
# 执行操作…
pass

if __name__ == "__main__":
main()

2.2 基本操作示例

# 读取操作 (直接返回结果)
coils = client.read_coils(slave_id=1, start_address=0, quantity=10)
regs = client.read_holding_registers(slave_id=1, start_address=0, quantity=10)

print(f"线圈: {coils}")
print(f"寄存器: {regs}")

# 写入操作
client.write_single_coil(slave_id=1, address=0, value=True)
client.write_multiple_registers(slave_id=1, start_address=10, values=[100, 200])

2.3 高级数据类型示例

同步客户端同样支持自动编解码,使用方法与异步版本一致(除了没有 await 和 callback)。

# 读写 Float32
client.write_float32(slave_id=1, start_address=0, value=123.45)
val = client.read_float32(slave_id=1, start_address=0)

# 读写 Int32 (指定大端序)
client.write_int32(slave_id=1, start_address=4, value=1000, byte_order="big")
val_i32 = client.read_int32(slave_id=1, start_address=4, byte_order="big")

# 读写 String
client.write_string(slave_id=1, start_address=10, value="SYNC_TEST")
val_str = client.read_string(slave_id=1, start_address=10, length=9)


附录:数据编码参数说明

在高级操作 (read_float32, write_int32 等) 中,byte_order 和 word_order 参数用于适配不同厂商 PLC 的数据存储格式。

参数名可选值说明
byte_order “big” (默认) 大端序 (Big-Endian)。高位字节在前。例如 0x1234 存储为 12 34。
“little” 小端序 (Little-Endian)。低位字节在前。例如 0x1234 存储为 34 12。
word_order “high” (默认) 高字在前。32位数据的高16位存储在第一个寄存器地址。
“low” 低字在前。32位数据的低16位存储在第一个寄存器地址。

常见组合速查:

  • ABCD (标准): byte_order=“big”, word_order=“high”
  • CDAB (常见于部分PLC): byte_order=“big”, word_order=“low”
  • BADC: byte_order=“little”, word_order=“high”
  • DCBA: byte_order=“little”, word_order=“low”

GitHub地址

Miraitowa-la/ModbusLink

赞(0)
未经允许不得转载:171主机测评 » ModbusLink 客户端使用说明
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址