使用pyModbusTCP先读线圈再读保持寄存器时出现NoneType下标错误
异常原因分析与解决
核心原因
报错'NoneType' object is not subscriptable本质是read_holding_registers()返回了None,代码直接取[0]触发异常。而先读线圈后出现这个问题,是因为pyModbusTCP的Client对象是状态化的,读线圈操作可能导致连接进入异常状态,后续复用该连接读保持寄存器时直接失败返回None。
具体可能的触发场景:
- 读线圈的请求(0-34)超出PLC支持的线圈地址范围,PLC返回错误响应,导致Client连接状态异常。
- 读线圈时出现通信超时、网络波动,Client未自动重建连接,后续请求复用失效连接。
- pyModbusTCP 0.2.0版本存在连接复用的bug,单次请求失败后未重置连接状态。
代码里的问题点
- 未检查读取方法的返回值:不管是
read_coils()还是read_holding_registers(),在通信失败时都会返回None,但代码直接使用返回值,没有做有效性判断。 - 变量名笔误:读保持寄存器的代码里存在变量名不匹配问题:定义了
read__timer,但调用时用了read_erector_timer.readSingleRegister(),这会直接触发错误。 - 连接复用的风险:如果
ReadFromModbusAddress类复用同一个Client实例,读线圈的异常会影响后续所有请求。
解决步骤
- 修正变量名笔误:把
timer = read_erector_timer.readSingleRegister()改成timer = read__timer.readSingleRegister()。 - 添加返回值有效性检查:修改读取方法,先判断返回值是否为
None,再进行后续操作:
def readSingleRegister(self): holdingRegisterData = self.ModbusClient_call.read_holding_registers(self.modbus_address) if holdingRegisterData is None: raise RuntimeError("读取保持寄存器失败") return holdingRegisterData[0]
def readManyCoils(self): coilData = self.ModbusClient_call.read_coils(self.modbus_address, self.modbus_address_read_count) if coilData is None: raise RuntimeError("读取线圈失败") return coilData
- 验证线圈地址范围合法性:确认PLC的线圈地址0-34是否存在,若超出范围,调整
modbus_address_read_count参数为PLC支持的数量。 - 强制重建连接(针对旧版本bug):如果是pyModbusTCP 0.2.0的版本问题,可在每次读取前检查连接状态,断开后重连:
# 在ReadFromModbusAddress类中添加连接检查逻辑 def ensure_connection(self): if not self.ModbusClient_call.is_open(): self.ModbusClient_call.close() self.ModbusClient_call.open() # 读取前调用连接检查 def readManyCoils(self): self.ensure_connection() coilData = self.ModbusClient_call.read_coils(self.modbus_address, self.modbus_address_read_count) if coilData is None: # 尝试重连一次再读 self.ModbusClient_call.close() self.ModbusClient_call.open() coilData = self.ModbusClient_call.read_coils(self.modbus_address, self.modbus_address_read_count) if coilData is None: raise RuntimeError("读取线圈失败") return coilData
内容的提问来源于stack exchange,提问作者user21051037
相关产品推荐
相关产品推荐

