基于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()
注意事项
- 如果原示例中的
updating_writer线程会定时覆盖保持寄存器的值,需调整该逻辑避免和加1操作冲突。 - 上述代码中,每次客户端用功能码3读取时,请求范围内的所有寄存器都会加1,如果只需要特定寄存器加1,可修改
new_values的生成逻辑(比如只对地址0的寄存器操作)。 - 测试时可使用Modbus调试工具发送功能码3的读取请求,观察寄存器值是否每次读取后递增。
内容的提问来源于stack exchange,提问作者Bart Loeters
相关产品推荐
相关产品推荐

