import struct import time from typing import Union from .Consts import Config def calculate_modbus_crc16(data: bytes) -> tuple[int, int]: """ 计算 Modbus RTU 的 CRC16 校验码,返回 (low_byte, high_byte)。 data 可以是 bytes 或者 bytearray """ crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if (crc & 0x0001): crc = (crc >> 1) ^ 0xA001 else: crc >>= 1 # 低字节在前、高字节在后 low_byte = crc & 0xFF high_byte = (crc >> 8) & 0xFF return low_byte, high_byte def create_command(slave_id, func_code, address, data): """ 生成完整的 Modbus 通信指令: - 第1字节: 从站地址 - 第2字节: 功能码 - 第3~4字节: 寄存器地址 (大端) - 第5~6字节: 数据(或读寄存器个数) (大端) - 第7~8字节: CRC校验, 先低后高 """ # 按照大端格式打包:B(1字节), B(1字节), H(2字节), H(2字节) # 例如: 0x03, 0x03, 0x0191, 0x0001 # 生成的命令将是: 03 03 01 91 00 01 (不含 CRC) command = struct.pack(">B B H H", slave_id, func_code, address, data) # 计算CRC,并将低字节、后高字节拼到末尾 low_byte, high_byte = calculate_modbus_crc16(command) return command + bytes([low_byte, high_byte]) def send_command(ser, command) -> Union[bytes, str]: """通过串口发送指令并打印响应""" # Modbus RTU 半双工,发送前拉高 RTS ser.setRTS(True) time.sleep(0.02) ser.write(command) # 发送指令 if Config.OPEN_DEVICE: # 如果是实际串口,就打印16进制的发送内容 print(f"发送的数据: ", end="") for ind, c in enumerate(command.hex().upper()): if ind % 2 == 0 and ind != 0: print(" ", end="") print(c, end="") # 发送完成后,切换到接收模式 ser.setRTS(False) # 读取响应,具体长度要看从站返回,有时多字节 response = ser.read(8) # 假设响应是8字节 print(f"接收到的数据: ", end="") for ind, c in enumerate(response.hex().upper()): if ind % 2 == 0 and ind != 0: print(" ", end="") print(c, end="") print() return response def get_result_byte_int(data: bytes, byte_start: int = 6, byte_end: int = 10) -> int: return int(data.hex()[byte_start:byte_end], 16) def get_result_byte_str(data: bytes, byte_start: int = 6, byte_end: int = 10) -> str: return data.hex()[byte_start:byte_end] def run_commands(ser, duration=0.1, *commands): for cmd in commands: if isinstance(cmd, list): for c in cmd: send_command(ser, c) time.sleep(duration) else: send_command(ser, cmd) time.sleep(duration)