python实现ModBusRTU服务端

python实现基于串口通信的ModBusRTU服务端是一件简单的事情,只要通过pymodbus模块就可以实现。

一、一个Demo及其引发的问题

1、一个Demo

import asyncio
import json
import threading
import time
from pymodbus.server import StartSerialServer, ServerAsyncStop
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusServerContext, ModbusSlaveContext
from pymodbus.transaction import ModbusRtuFramer
from Application.common.private_socket import Request

if __name__ == "__main__":
    # 定义串口配置
    port = "COM46"
    # Serial(port="COM46", baudrate=115200, timeout=2, bytesize=8, parity="N", stopbits=1)
    framer = ModbusRtuFramer

    # 创建数据存储区
    # data_block = ModbusSequentialDataBlock(0, [0] * 100)  # 100个保持寄存器?
    data_block = ModbusSlaveContext(
        hr=ModbusSequentialDataBlock(0, [0] * 100)
    )
    # store = ModbusSlaveContext(hr=data_block)
    # context = ModbusServerContext(slaves=store)
    context = ModbusServerContext(slaves={1: data_block}, single=False)

    # 启动Modbus RTU服务器
    # StartSerialServer(context=context, framer=framer, port="COM46")
    # thread = threading.Thread(target=StartSerialServer, kwargs={"context": context, "framer": framer, "port": port, "baudrate":9600, "timeout":2, "bytesize":8, "parity":"N", "stopbits":1})
    thread = threading.Thread(target=StartSerialServer, kwargs={"context": context, "framer": framer, "port": port, "baudrate":9600})
    thread.start()
    # 设置保持寄存器的0地址的值为s
    # 定义函数参数
    write_address = 0  # 起始地址
    read_address = 10
    read_count = 10
    values = [115]  # 触发指令s 要设置的多个值列表,如[10, 20, 30]

    # 调用函数,设置0地址为触发指令s
    data_block.setValues(16, write_address, values)

    while True:
        time.sleep(3)
        # 获取保持寄存器的值并打印
        hr_values = data_block.getValues(3, 0, count=20)

        print("Hold Register Values:", hr_values)
    asyncio.run(ServerAsyncStop())  # 停止服务器

在这里简单说明上述程序涉及到的对象:

(1)ModbusRtuFramer的作用

在 Modbus 通信协议中,数据以帧的形式进行传输。ModbusRtuFramer 是 pymodbus 库中的一个类,它的作用是负责处理 Modbus RTU 帧的编码和解码。

具体来说,ModbusRtuFramer 完成了以下几个主要任务:

①编码(封装)Modbus RTU 帧:当你需要发送 Modbus RTU 请求或响应时,ModbusRtuFramer 负责将请求或响应的数据按照 Modbus RTU 协议的格式进行封装,生成符合 Modbus RTU 规范的帧,以便发送到 Modbus 设备。

②解码(解析)Modbus RTU 帧:当你从 Modbus 设备接收到数据时,ModbusRtuFramer 负责将接收到的二进制数据按照 Modbus RTU 协议的格式进行解析,提取出请求或响应的数据,以便进行后续的处理和分析。

③错误检测和纠正:ModbusRtuFramer 也负责检测 Modbus RTU 帧中的错误,比如奇偶校验错误、帧起始符和结束符错误等。如果帧中存在错误,ModbusRtuFramer 会帮助你识别错误的位置和类型,便于进行纠正或错误处理。

总之,ModbusRtuFramer 是一个处理 Modbus RTU 帧的工具,它确保了在 Modbus RTU 通信中,数据的正确封装和解析,以及错误的检测和处理。

(2)ModbusSequentialDataBlock、ModbusSlaveContext与ModbusServerContext的作用

在 pymodbus 库中,ModbusSequentialDataBlock、ModbusSlaveContext 和 ModbusServerContext 是用来处理 Modbus 数据存储和上下文的类,它们的作用如下:

①ModbusSequentialDataBlock:

ModbusSequentialDataBlock 是一个用于创建顺序排列的 Modbus 寄存器块的类。
它通常用于模拟设备的保持寄存器(Holding Registers)或输入寄存器(Input Registers)。
这个类允许你定义寄存器的起始地址和初始化寄存器的值。
例如:ModbusSequentialDataBlock(0, [0] * 100) 创建了一个从地址 0 开始,包含 100 个初始值为 0 的保持寄存器的块。


②ModbusSlaveContext:

ModbusSlaveContext 是一个用于表示 Modbus 从设备的类。
它包含一个或多个数据存储块(比如保持寄存器块、输入寄存器块等)。
这个类可以用来创建一个模拟的 Modbus 从设备上下文。
你可以在这个上下文中添加多个不同类型的数据块,模拟一个完整的 Modbus 从设备。


③ModbusServerContext:

ModbusServerContext 是一个用于表示整个 Modbus 服务器的类。
它包含一个或多个 Modbus 从设备的上下文(ModbusSlaveContext 实例)。
这个类可以用来创建一个完整的 Modbus 服务器环境,包含多个模拟的 Modbus 从设备。

在搭建 Modbus 通信环境时,你通常会创建 ModbusSequentialDataBlock 实例作为寄存器的存储,然后将它们添加到 ModbusSlaveContext 中。最后,将多个 ModbusSlaveContext 实例添加到 ModbusServerContext 中,以构建一个包含多个从设备的 Modbus 服务器环境。这样的架构可以让你模拟多个不同类型的 Modbus 从设备。

(3)context = ModbusServerContext(slaves={1: data_block}, single=False)的解释

在这个代码行中,你创建了一个 ModbusServerContext 对象,该对象用于模拟一个 Modbus 服务器的上下文。ModbusServerContextpymodbus 模块中的一个类,它用于存储和管理 Modbus 服务器的数据。在这个特定的代码行中,你传递了一些参数给 ModbusServerContext 构造函数:

  • slaves={1: data_block}: 这个参数是一个字典,表示 Modbus 服务器的从设备。在这里,你创建了一个从设备,其 Modbus 地址为 1,并且将这个从设备关联到了一个 data_block 对象上。data_block 可能是一个 ModbusSequentialDataBlock 对象,用于存储 Modbus 寄存器的数据。

  • single=False: 这个参数用于确定是否将所有的从设备视为一个整体。当 single=True 时,所有的从设备共享相同的 Modbus 地址空间,它们没有独立的地址范围。而当 single=False 时,每个从设备都有独立的 Modbus 地址空间,它们的地址范围是相互独立的。

所以,这一行代码的意义是创建了一个 ModbusServerContext 对象,该对象包含一个从设备(Modbus 地址为 1),并且这个从设备拥有独立的 Modbus 地址空间。这个上下文可以在模拟多个独立的 Modbus 设备时使用。

(4)关于StartSerialServer启动服务器时必须的参数

必须传入以下参数才能正常启动

  • "context": context 表示传入的 Modbus 上下文。
  • "framer": framer 表示 Modbus 使用的帧格式。
  • "port": port 表示串口号,例如 "COM46"
  • "baudrate": baudrate表示波特率,即每秒传输的位数。

这些参数将被传递给 StartSerialServer 函数,用于启动 Modbus RTU 服务器。

这需要根据客户端的设置来对应设置。

2、权限问题

建议使用管理员权限运行脚本。

3、端口占用问题

确保端口没有被其他软件占用。

4、pymodbus版本问题

网上的示例代码,StartSerialServer要求传入serial模块的Serial(port=COM, baudrate=Baudrate, timeout=2, bytesize=Databits, parity=Parity, stopbits=Stopbits)实例,我不确定是哪个版本,你可以试一下,目前在新版本只要传入波特率和端口即可。

二、pymodbus相关文档

1、官方文档地址

Datastore — PyModbus 3.6.0dev documentation

2、Datastore对象

Datastore is responsible for managing registers for a server.

(1)pymodbus.datastore.ModbusSparseDataBlock

pymodbus.datastore.ModbusSparseDataBlock 是 PyModbus 库中用于创建稀疏数据块(sparse data block)的类。在 Modbus 协议中,数据通常被组织成多个数据块,而每个数据块包含一定数量的数据寄存器或者线圈。

ModbusSparseDataBlock 允许您创建包含不连续地址的数据块。具体来说,您可以在数据块中指定特定地址的数据,而无需为数据块的每个地址都分配内存。这种方式可以有效地节省内存空间,尤其是在处理大量数据时。

以下是 ModbusSparseDataBlock 的初始化参数:

  • values:一个字典,包含要存储的数据。字典的键是地址,值是相应地址的数据值。
  • address:数据块的起始地址。
  • size:数据块的大小,即包含的地址数量。

你可能感兴趣的:(python,python)