PyModbus:Modbus TCP异常处理及服务器变量读写权限配置咨询
Hey there! Let's break down solutions for your two PyModbus server questions with practical, runnable code examples.
1. Generating Modbus TCP Exceptions for Out-of-Range Writes
To make your server throw a proper Modbus exception when a client sends an out-of-range value, you'll need to customize the server's data context to validate writes before they're applied. PyModbus lets you override default write methods to add your own range checks.
Here's how to implement it:
- Create a custom slave context by inheriting from
BaseModbusSlaveContext - Override the write method(s) matching your register type (e.g.,
write_registersfor holding registers) - Add range validation logic, and throw a
ModbusExceptionwith the correct error code if values are invalid
Example Code
from pymodbus.server import StartTcpServer from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.datastore.context import BaseModbusSlaveContext from pymodbus.exceptions import IllegalValueException class ValidatedSlaveContext(BaseModbusSlaveContext): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Define your allowed value ranges (tweak these to match your needs) self.valid_ranges = { "holding": (0, 100), # Holding registers 0-99 accept 0-100 } def write_registers(self, address, values, **kwargs): # Check if we're writing to the validated holding register range if 0 <= address <= 99: min_val, max_val = self.valid_ranges["holding"] for val in values: if not (min_val <= val <= max_val): # Throw Illegal Value exception (Modbus code 0x03) raise IllegalValueException(f"Value {val} is out of valid range [{min_val}, {max_val}]") # If validation passes, use the default write logic return super().write_registers(address, values, **kwargs) # Set up the server context with 100 holding registers slave_context = ValidatedSlaveContext( di=None, co=None, hr=[100], ir=None ) server_context = ModbusServerContext(slaves=slave_context, single=True) # Start the TCP server on port 502 StartTcpServer(context=server_context, address=("0.0.0.0", 502))
When a client writes a value outside 0-100 to holding registers 0-99, the server will respond with a Modbus Illegal Value Exception (code 0x03).
2. Setting Partial Variables as Read-Only
To make specific registers read-only while keeping others writable, you'll use a custom context to block writes to designated address ranges. The key is to check the target address in the write method and throw an exception if it's in a read-only zone.
Example Code
from pymodbus.server import StartTcpServer from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.datastore.context import BaseModbusSlaveContext from pymodbus.exceptions import IllegalFunctionException class ReadOnlySlaveContext(BaseModbusSlaveContext): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Define read-only address ranges (adjust these to your use case) self.read_only_zones = { "holding": (0, 9), # Holding registers 0-9 are read-only "coils": (0, 4) # Coils 0-4 are read-only } def write_registers(self, address, values, **kwargs): # Block writes to read-only holding registers if self.read_only_zones["holding"][0] <= address <= self.read_only_zones["holding"][1]: raise IllegalFunctionException("Write not allowed: holding registers 0-9 are read-only") return super().write_registers(address, values, **kwargs) def write_coils(self, address, values, **kwargs): # Block writes to read-only coils if self.read_only_zones["coils"][0] <= address <= self.read_only_zones["coils"][1]: raise IllegalFunctionException("Write not allowed: coils 0-4 are read-only") return super().write_coils(address, values, **kwargs) # Set up server context with 20 holding registers and 10 coils slave_context = ReadOnlySlaveContext( di=None, co=[10], hr=[20], ir=None ) server_context = ModbusServerContext(slaves=slave_context, single=True) # Start the TCP server StartTcpServer(context=server_context, address=("0.0.0.0", 502))
In this setup:
- Holding registers 0-9 are read-only (writes throw Illegal Function Exception 0x01)
- Holding registers 10-19 are fully writable
- Coils 0-4 are read-only, coils 5-9 are writable
You can easily adjust the read_only_zones dictionary to match your specific register types and address ranges.
内容的提问来源于stack exchange,提问作者MM87

