diff --git a/app/bus/rs485_manager.py b/app/bus/rs485_manager.py index 26be648..ca0ec4c 100644 --- a/app/bus/rs485_manager.py +++ b/app/bus/rs485_manager.py @@ -91,61 +91,6 @@ class RS485Manager: crc >>= 1 return crc - @staticmethod - def _parse_modbus_rtu_float_default(response_bytes): - """ - 默认解析 Modbus RTU 响应中的 32 位 IEEE 754 单精度浮点数。 - 假定为大端 (ABCD) 字节序。 - """ - # 最小预期长度: 从站ID(1) + 功能码(1) + 字节计数(1) + 4字节数据 + CRC(2) = 9字节 - min_response_len = 9 - expected_data_byte_count = 4 # 32位浮点数占用4字节 - - if not response_bytes or len(response_bytes) < min_response_len: - log(f"警告: 响应字节过短或为空,无法解析为浮点数。响应: {response_bytes.hex() if response_bytes else 'None'}") - return None - - # 提取响应组件 - # 注意: Modbus RTU CRC是LSB在前,所以这里需要调整 - # response_bytes[:-2] 是用于CRC计算的数据部分 - # response_bytes[-2:] 是CRC本身 - data_for_crc = response_bytes[:-2] - received_crc = (response_bytes[-1] << 8) | response_bytes[-2] # CRC的低字节在前,高字节在后 - - # 1. CRC 校验 - calculated_crc = RS485Manager._calculate_crc16_modbus(data_for_crc) - if calculated_crc != received_crc: - log(f"错误: CRC校验失败。接收CRC: {received_crc:04X}, 计算CRC: {calculated_crc:04X}. 响应: {response_bytes.hex()}") - return None - - function_code = response_bytes[1] - byte_count = response_bytes[2] - data_bytes = response_bytes[3:3 + expected_data_byte_count] - - # 2. 功能码检查 (假设读取保持寄存器0x03或输入寄存器0x04) - if function_code not in [0x03, 0x04]: - log(f"警告: 响应功能码 {function_code:02X} 不符合预期 (期望0x03或0x04)。响应: {response_bytes.hex()}") - return None - - # 3. 字节计数检查 - if byte_count != expected_data_byte_count: - log(f"警告: 响应字节计数 {byte_count} 不符合预期 (期望{expected_data_byte_count})。响应: {response_bytes.hex()}") - return None - - # 4. 提取的数据字节长度检查 (与字节计数检查有重叠,但更安全) - if len(data_bytes) != expected_data_byte_count: - log(f"错误: 提取的数据字节长度不正确。期望{expected_data_byte_count}, 实际{len(data_bytes)}. 响应: {response_bytes.hex()}") - return None - - # 5. 转换为浮点数 (大端, ABCD) - try: - parsed_float = struct.unpack('>f', data_bytes)[0] - log(f"成功解析浮点数: {parsed_float}") - return parsed_float - except Exception as e: - log(f"错误: 浮点数转换失败: {e}. 数据字节: {data_bytes.hex()}. 响应: {response_bytes.hex()}") - return None - def execute_raw_command(self, bus_id, command): """ 【契约】执行一个“发后不理”的原始指令。 @@ -203,8 +148,9 @@ class RS485Manager: bus_id = command_info.get("bus_number") command_bytes = command_info.get("command_bytes") - if bus_id is None or command_bytes is None: - log("错误: Raw485Command 缺少 'bus_number' 或 'command_bytes' 字段。") + # 增加对命令有效性的检查 + if bus_id is None or not command_bytes or len(command_bytes) < 2: + log(f"错误: CollectTask 的 'command' 字段无效。bus_id: {bus_id}, command_bytes: {command_bytes}") return None except Exception as e: @@ -220,46 +166,54 @@ class RS485Manager: rts_pin = port_info['rts_pin'] lock = port_info['lock'] - response_buffer = bytearray() # 改:用缓冲累积 + response_bytes = None # 在锁外部初始化,确保其作用域 + response_buffer = bytearray() with lock: try: # II. 线程安全与指令发送 - rts_pin.value(1) # 设置为发送模式 (DE/RE = HIGH) - time.sleep_us(100) # 短暂延时,确保方向切换完成 + rts_pin.value(1) + time.sleep_us(100) uart.write(command_bytes) uart.flush() - time.sleep_us(100) # 短暂延时,确保数据完全发出 - rts_pin.value(0) # 切换回接收模式 (DE/RE = LOW) + time.sleep_us(100) + rts_pin.value(0) log(f"总线 {bus_id} 原始命令发送成功: {command_bytes.hex()}") - # III. 接收响应(修复:超时循环 + 小块读) + # III. 接收响应 start_time = time.ticks_ms() response_timeout = self.default_timeouts.get('rs485_response', 500) while time.ticks_diff(time.ticks_ms(), start_time) < response_timeout: if uart.any(): - chunk = uart.read(32) # 小块读 + chunk = uart.read(32) if chunk: response_buffer.extend(chunk) - start_time = time.ticks_ms() # 重置超时 + start_time = time.ticks_ms() # 收到数据就重置超时 time.sleep_ms(5) if response_buffer: - # 新增:搜索有效帧,跳前缀 - response_bytes = self._find_modbus_frame(response_buffer, bus_id, 4) - if response_bytes: - log(f"总线 {bus_id} 收到有效响应: {response_bytes.hex()}") + # 动态地从请求命令中获取预期的从站ID和功能码 + expected_slave_id = command_bytes[0] + expected_func_code = command_bytes[1] + + found_frame = self._find_modbus_frame(response_buffer, expected_slave_id, expected_func_code) + if found_frame: + log(f"总线 {bus_id} 收到有效响应: {found_frame.hex()}") + response_bytes = found_frame # 将找到的帧赋值给外部变量 else: log(f"警告: 总线 {bus_id} 响应中无有效帧。收到响应: {response_buffer.hex()}") - return None else: log(f"警告: 总线 {bus_id} 未收到响应。") except Exception as e: log(f"错误: 在总线 {bus_id} 上执行采集命令失败: {e}") - return None + + # IV. 统一处理和解析 + # 无论是因为超时、未找到有效帧还是发生异常,只要 response_bytes 仍为 None,就任务失败 + if response_bytes is None: + return None - # IV. 解析(用修复版) - parsed_value = RS485Manager._parse_modbus_rtu_default(response_bytes) # 改名,支持动态 + # 使用找到的有效帧进行解析 + parsed_value = RS485Manager._parse_modbus_rtu_default(response_bytes) return parsed_value diff --git a/app/processor.py b/app/processor.py index 9b16780..e0d18c2 100644 --- a/app/processor.py +++ b/app/processor.py @@ -49,10 +49,18 @@ class Processor: # 根据指令类型,分发到不同的业务处理方法 if 'raw_485_command' in instruction: - self._process_exec_command(instruction['raw_485_command']) + cmd = instruction['raw_485_command'] + if cmd: + self._process_exec_command(cmd) + else: + log("警告:'raw_485_command' 指令内容为空。") elif 'batch_collect_command' in instruction: - self._process_collect_command(instruction['batch_collect_command']) + cmd = instruction['batch_collect_command'] + if cmd: + self._process_collect_command(cmd) + else: + log("警告:'batch_collect_command' 指令内容为空。") else: log(f"警告:收到未知或不适用于此设备的指令类型: {instruction}")