You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PyModbus v2.5.3更新服务器,实现读取保持寄存器时触发函数

解决方案:PyModbus v2.5.3 服务器在功能码3读取时触发寄存器加1

核心思路

通过自定义ModbusSlaveContext或RequestHandler,拦截功能码3(读取保持寄存器)的请求,在返回数据前对目标寄存器执行加1操作。


方案一:自定义SlaveContext(推荐)

这种方式直接在数据存储层处理,逻辑更贴合PyModbus的架构:

from pymodbus.server.asynchronous import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
import threading
import time

# 自定义SlaveContext,拦截功能码3的读取请求
class CustomSlaveContext(ModbusSlaveContext):
    def getValues(self, function_code, address, count=1):
        # 仅对功能码3(读取保持寄存器)执行加1操作
        if function_code == 3:
            # 获取当前寄存器值
            current_values = self.store['hr'].getValues(address, count)
            # 每个寄存器值加1
            new_values = [val + 1 for val in current_values]
            # 更新回数据块
            self.store['hr'].setValues(address, new_values)
        # 调用父类方法返回最新值
        return super().getValues(function_code, address, count)

def updating_writer(context):
    """ 原示例的定时更新线程(可根据自身需求保留或修改) """
    while True:
        time.sleep(5)
        # 注意:如果这里固定设置值,会覆盖加1的结果,需根据业务调整
        # context[0].setValues(3, 0, [10])
        print("定时更新线程运行中...")

def run_updating_server():
    # 使用自定义SlaveContext初始化数据存储
    store = CustomSlaveContext(
        di=ModbusSequentialDataBlock(0, [0]*100),
        co=ModbusSequentialDataBlock(0, [0]*100),
        hr=ModbusSequentialDataBlock(0, [0]*100),  # 保持寄存器初始值全为0
        ir=ModbusSequentialDataBlock(0, [0]*100))
    context = ModbusServerContext(slaves=store, single=True)

    # 启动定时更新线程(如果不需要可删除)
    thread = threading.Thread(target=updating_writer, args=(context,))
    thread.daemon = True
    thread.start()

    # 设备标识
    identity = ModbusDeviceIdentification()
    identity.VendorName = 'Pymodbus'
    identity.ProductCode = 'PM'
    identity.ProductName = 'Pymodbus Server'
    identity.MajorMinorRevision = '2.5.3'

    # 启动TCP服务器
    StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020))

if __name__ == "__main__":
    run_updating_server()

方案二:自定义RequestHandler

通过拦截请求处理流程实现,适合需要对请求做额外校验的场景:

from pymodbus.server.asynchronous import StartTcpServer
from pymodbus.server.asynchronous.tcp import AsyncModbusTcpRequestHandler
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
import threading
import time

# 自定义RequestHandler,拦截功能码3的请求
class CustomRequestHandler(AsyncModbusTcpRequestHandler):
    def handle_read_holding_registers(self, request):
        # 获取请求的地址和寄存器数量
        addr = request.address
        count = request.count
        # 获取服务器上下文
        slave_context = self.server.context[0]
        
        # 对目标保持寄存器执行加1操作
        current_vals = slave_context.getValues(3, addr, count)
        updated_vals = [v + 1 for v in current_vals]
        slave_context.setValues(3, addr, updated_vals)
        
        # 调用父类方法生成响应返回给客户端
        return super().handle_read_holding_registers(request)

def updating_writer(context):
    while True:
        time.sleep(5)
        print("定时更新线程运行中...")

def run_updating_server():
    store = ModbusSlaveContext(
        di=ModbusSequentialDataBlock(0, [0]*100),
        co=ModbusSequentialDataBlock(0, [0]*100),
        hr=ModbusSequentialDataBlock(0, [0]*100),
        ir=ModbusSequentialDataBlock(0, [0]*100))
    context = ModbusServerContext(slaves=store, single=True)

    thread = threading.Thread(target=updating_writer, args=(context,))
    thread.daemon = True
    thread.start()

    identity = ModbusDeviceIdentification()
    identity.VendorName = 'Pymodbus'
    identity.ProductCode = 'PM'
    identity.ProductName = 'Pymodbus Server'
    identity.MajorMinorRevision = '2.5.3'

    # 启动服务器时指定自定义RequestHandler
    StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020), 
                   request_handler=CustomRequestHandler)

if __name__ == "__main__":
    run_updating_server()

注意事项

  1. 如果原示例中的updating_writer线程会定时覆盖保持寄存器的值,需调整该逻辑避免和加1操作冲突。
  2. 上述代码中,每次客户端用功能码3读取时,请求范围内的所有寄存器都会加1,如果只需要特定寄存器加1,可修改new_values的生成逻辑(比如只对地址0的寄存器操作)。
  3. 测试时可使用Modbus调试工具发送功能码3的读取请求,观察寄存器值是否每次读取后递增。

内容的提问来源于stack exchange,提问作者Bart Loeters

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 11:40:38