robotengine.serial_io
serial_io 是 robotengine 控制硬件串口的节点。
1""" 2 3serial_io 是 robotengine 控制硬件串口的节点。 4 5""" 6 7from .node import Node 8import serial.tools.list_ports 9import serial 10from enum import Enum 11import random 12from robotengine.tools import hex2str, warning, error 13 14class DeviceType(Enum): 15 """ 设备类型枚举 """ 16 STM32F407 = 0 17 """ STM32F407 设备类型 """ 18 ARDUINO_MEGA2560 = 1 19 """ Arduino Mega2560 设备类型 """ 20 21class CheckSumType(Enum): 22 """ 校验和类型枚举 """ 23 NONE = 0 24 """ 无校验和 """ 25 SUM8 = 1 26 """ SUM8 校验和 """ 27 SUM16 = 2 28 """ SUM16 校验和 """ 29 XOR8 = 3 30 """ XOR8 校验和 """ 31 XOR16 = 4 32 """ XOR16 校验和 """ 33 CRC8 = 5 34 """ CRC8 校验和 """ 35 CRC16 = 6 36 """ CRC16 校验和 """ 37 38checksum_length_map = { 39 CheckSumType.SUM8: 1, 40 CheckSumType.SUM16: 2, 41 CheckSumType.XOR8: 1, 42 CheckSumType.XOR16: 2, 43 CheckSumType.CRC8: 1, 44 CheckSumType.CRC16: 2 45 } 46""" 校验和长度映射表 """ 47 48class SerialIO(Node): 49 """ 串口节点 """ 50 def __init__(self, name="SerialIO", device_type=DeviceType.STM32F407, checksum_type=CheckSumType.NONE, header=[], baudrate=115200, timeout=1.0, warn=True): 51 super().__init__(name) 52 self._device_type = device_type 53 self._checksum_type = checksum_type 54 self._header = header 55 self._device = None 56 self._serial: serial.Serial = None 57 self._baudrate = baudrate 58 self._timeout = timeout 59 60 self._warn = warn 61 self._receive_data = bytes() 62 63 self._initialize() 64 if self._device is None: 65 if self._warn: 66 warning(f"节点 {self.name} 初始化时未检测到 {self.device_type} 设备,将在内部更新中继续尝试") 67 68 def _update(self, delta) -> None: 69 if self._device is None: 70 self._initialize() 71 return 72 73 def _initialize(self): 74 self._device = self._find_device() 75 if self.device: 76 print(f"节点 {self.name} 初始化时检测到 {self._device_type} 设备,串口为 {self._device},波特率为 {self._baudrate}") 77 self._serial = serial.Serial(self.device, self.baudrate, timeout=self.timeout) 78 # 清空串口缓冲区 79 self._serial.reset_input_buffer() 80 self._serial.reset_output_buffer() 81 print(f"节点 {self.name} 初始化时清空串口缓冲区") 82 83 def _find_device(self): 84 if self._device_type == DeviceType.STM32F407: 85 target_vid = 0x1A86 86 target_pid = 0x7523 87 elif self._device_type == DeviceType.ARDUINO_MEGA2560: 88 target_vid = 0x2341 89 target_pid = 0x0043 90 91 ports = serial.tools.list_ports.comports() 92 for port in ports: 93 if port.vid == target_vid and port.pid == target_pid: 94 return port.device 95 return None 96 97 def _get_check_sum(self, data: bytes) -> bytes: 98 if self._checksum_type == CheckSumType.SUM8: 99 check_sum = sum(data) & 0xFF 100 return bytes([check_sum]) 101 elif self._checksum_type == CheckSumType.SUM16: 102 check_sum = sum(data) & 0xFFFF 103 return check_sum.to_bytes(2, byteorder='big') 104 elif self._checksum_type == CheckSumType.XOR8: 105 check_sum = 0 106 for byte in data: 107 check_sum ^= byte 108 return bytes([check_sum]) 109 elif self._checksum_type == CheckSumType.XOR16: 110 check_sum = 0 111 for byte in data: 112 check_sum ^= byte 113 return check_sum.to_bytes(2, byteorder='big') 114 elif self._checksum_type == CheckSumType.CRC8: 115 crc = 0x00 116 polynomial = 0x07 117 for byte in data: 118 crc ^= byte 119 for _ in range(8): 120 if crc & 0x80: 121 crc = (crc << 1) ^ polynomial 122 else: 123 crc <<= 1 124 crc &= 0xFF 125 return bytes([crc]) 126 elif self._checksum_type == CheckSumType.CRC16: 127 crc = 0xFFFF 128 polynomial = 0x8005 129 for byte in data: 130 crc ^= byte 131 for _ in range(8): 132 if crc & 0x0001: 133 crc = (crc >> 1) ^ polynomial 134 else: 135 crc >>= 1 # 否则仅右移 136 return crc.to_bytes(2, byteorder='big') 137 else: 138 raise ValueError("无效的校验和类型") 139 140 def _add_header(self, data: bytes) -> bytes: 141 return bytes(self._header) + data 142 143 def random_bytes(self, length: int) -> bytes: 144 """ 生成随机字节 """ 145 return bytes([random.randint(0, 255) for _ in range(length)]) 146 147 def fixed_bytes(self, byte: int, length: int) -> bytes: 148 """ 生成固定字节 """ 149 return bytes([byte for _ in range(length)]) 150 151 def transmit(self, data: bytes) -> bytes: 152 """ 发送串口数据 """ 153 if self._serial is None: 154 if self._warn: 155 warning(f"节点 {self.name} 串口未初始化,无法发送数据") 156 return 157 if self._checksum_type !=CheckSumType.NONE: 158 data += self._get_check_sum(data) 159 if self._header: 160 data = self._add_header(data) 161 self._serial.write(data) 162 return data 163 164 def receive(self, len: int) -> bytes: 165 """ 接收串口数据 """ 166 if self._serial is None: 167 if self._warn: 168 warning(f"节点 {self.name} 串口未初始化,无法接收数据") 169 return 170 if self._serial.in_waiting >= len: 171 return self._serial.read(len) 172 else: 173 return None 174 175 def check_sum(self, data: bytes) -> bool: 176 """ 校验串口数据 """ 177 if self._checksum_type == CheckSumType.NONE: 178 return True 179 checksum_length = checksum_length_map.get(self._checksum_type) 180 if checksum_length is None: 181 raise ValueError("无效的校验和类型,无法进行校验") 182 183 data_to_check = data[len(self._header):-checksum_length] 184 expected_checksum = data[-checksum_length:] 185 calculated_checksum = self._get_check_sum(data_to_check) 186 187 return calculated_checksum == expected_checksum 188 189 def __del__(self): 190 if self._serial: 191 self._serial.close()
class
DeviceType(enum.Enum):
15class DeviceType(Enum): 16 """ 设备类型枚举 """ 17 STM32F407 = 0 18 """ STM32F407 设备类型 """ 19 ARDUINO_MEGA2560 = 1 20 """ Arduino Mega2560 设备类型 """
设备类型枚举
Inherited Members
- enum.Enum
- name
- value
class
CheckSumType(enum.Enum):
22class CheckSumType(Enum): 23 """ 校验和类型枚举 """ 24 NONE = 0 25 """ 无校验和 """ 26 SUM8 = 1 27 """ SUM8 校验和 """ 28 SUM16 = 2 29 """ SUM16 校验和 """ 30 XOR8 = 3 31 """ XOR8 校验和 """ 32 XOR16 = 4 33 """ XOR16 校验和 """ 34 CRC8 = 5 35 """ CRC8 校验和 """ 36 CRC16 = 6 37 """ CRC16 校验和 """
校验和类型枚举
Inherited Members
- enum.Enum
- name
- value
checksum_length_map =
{<CheckSumType.SUM8: 1>: 1, <CheckSumType.SUM16: 2>: 2, <CheckSumType.XOR8: 3>: 1, <CheckSumType.XOR16: 4>: 2, <CheckSumType.CRC8: 5>: 1, <CheckSumType.CRC16: 6>: 2}
校验和长度映射表
49class SerialIO(Node): 50 """ 串口节点 """ 51 def __init__(self, name="SerialIO", device_type=DeviceType.STM32F407, checksum_type=CheckSumType.NONE, header=[], baudrate=115200, timeout=1.0, warn=True): 52 super().__init__(name) 53 self._device_type = device_type 54 self._checksum_type = checksum_type 55 self._header = header 56 self._device = None 57 self._serial: serial.Serial = None 58 self._baudrate = baudrate 59 self._timeout = timeout 60 61 self._warn = warn 62 self._receive_data = bytes() 63 64 self._initialize() 65 if self._device is None: 66 if self._warn: 67 warning(f"节点 {self.name} 初始化时未检测到 {self.device_type} 设备,将在内部更新中继续尝试") 68 69 def _update(self, delta) -> None: 70 if self._device is None: 71 self._initialize() 72 return 73 74 def _initialize(self): 75 self._device = self._find_device() 76 if self.device: 77 print(f"节点 {self.name} 初始化时检测到 {self._device_type} 设备,串口为 {self._device},波特率为 {self._baudrate}") 78 self._serial = serial.Serial(self.device, self.baudrate, timeout=self.timeout) 79 # 清空串口缓冲区 80 self._serial.reset_input_buffer() 81 self._serial.reset_output_buffer() 82 print(f"节点 {self.name} 初始化时清空串口缓冲区") 83 84 def _find_device(self): 85 if self._device_type == DeviceType.STM32F407: 86 target_vid = 0x1A86 87 target_pid = 0x7523 88 elif self._device_type == DeviceType.ARDUINO_MEGA2560: 89 target_vid = 0x2341 90 target_pid = 0x0043 91 92 ports = serial.tools.list_ports.comports() 93 for port in ports: 94 if port.vid == target_vid and port.pid == target_pid: 95 return port.device 96 return None 97 98 def _get_check_sum(self, data: bytes) -> bytes: 99 if self._checksum_type == CheckSumType.SUM8: 100 check_sum = sum(data) & 0xFF 101 return bytes([check_sum]) 102 elif self._checksum_type == CheckSumType.SUM16: 103 check_sum = sum(data) & 0xFFFF 104 return check_sum.to_bytes(2, byteorder='big') 105 elif self._checksum_type == CheckSumType.XOR8: 106 check_sum = 0 107 for byte in data: 108 check_sum ^= byte 109 return bytes([check_sum]) 110 elif self._checksum_type == CheckSumType.XOR16: 111 check_sum = 0 112 for byte in data: 113 check_sum ^= byte 114 return check_sum.to_bytes(2, byteorder='big') 115 elif self._checksum_type == CheckSumType.CRC8: 116 crc = 0x00 117 polynomial = 0x07 118 for byte in data: 119 crc ^= byte 120 for _ in range(8): 121 if crc & 0x80: 122 crc = (crc << 1) ^ polynomial 123 else: 124 crc <<= 1 125 crc &= 0xFF 126 return bytes([crc]) 127 elif self._checksum_type == CheckSumType.CRC16: 128 crc = 0xFFFF 129 polynomial = 0x8005 130 for byte in data: 131 crc ^= byte 132 for _ in range(8): 133 if crc & 0x0001: 134 crc = (crc >> 1) ^ polynomial 135 else: 136 crc >>= 1 # 否则仅右移 137 return crc.to_bytes(2, byteorder='big') 138 else: 139 raise ValueError("无效的校验和类型") 140 141 def _add_header(self, data: bytes) -> bytes: 142 return bytes(self._header) + data 143 144 def random_bytes(self, length: int) -> bytes: 145 """ 生成随机字节 """ 146 return bytes([random.randint(0, 255) for _ in range(length)]) 147 148 def fixed_bytes(self, byte: int, length: int) -> bytes: 149 """ 生成固定字节 """ 150 return bytes([byte for _ in range(length)]) 151 152 def transmit(self, data: bytes) -> bytes: 153 """ 发送串口数据 """ 154 if self._serial is None: 155 if self._warn: 156 warning(f"节点 {self.name} 串口未初始化,无法发送数据") 157 return 158 if self._checksum_type !=CheckSumType.NONE: 159 data += self._get_check_sum(data) 160 if self._header: 161 data = self._add_header(data) 162 self._serial.write(data) 163 return data 164 165 def receive(self, len: int) -> bytes: 166 """ 接收串口数据 """ 167 if self._serial is None: 168 if self._warn: 169 warning(f"节点 {self.name} 串口未初始化,无法接收数据") 170 return 171 if self._serial.in_waiting >= len: 172 return self._serial.read(len) 173 else: 174 return None 175 176 def check_sum(self, data: bytes) -> bool: 177 """ 校验串口数据 """ 178 if self._checksum_type == CheckSumType.NONE: 179 return True 180 checksum_length = checksum_length_map.get(self._checksum_type) 181 if checksum_length is None: 182 raise ValueError("无效的校验和类型,无法进行校验") 183 184 data_to_check = data[len(self._header):-checksum_length] 185 expected_checksum = data[-checksum_length:] 186 calculated_checksum = self._get_check_sum(data_to_check) 187 188 return calculated_checksum == expected_checksum 189 190 def __del__(self): 191 if self._serial: 192 self._serial.close()
串口节点
SerialIO( name='SerialIO', device_type=<DeviceType.STM32F407: 0>, checksum_type=<CheckSumType.NONE: 0>, header=[], baudrate=115200, timeout=1.0, warn=True)
51 def __init__(self, name="SerialIO", device_type=DeviceType.STM32F407, checksum_type=CheckSumType.NONE, header=[], baudrate=115200, timeout=1.0, warn=True): 52 super().__init__(name) 53 self._device_type = device_type 54 self._checksum_type = checksum_type 55 self._header = header 56 self._device = None 57 self._serial: serial.Serial = None 58 self._baudrate = baudrate 59 self._timeout = timeout 60 61 self._warn = warn 62 self._receive_data = bytes() 63 64 self._initialize() 65 if self._device is None: 66 if self._warn: 67 warning(f"节点 {self.name} 初始化时未检测到 {self.device_type} 设备,将在内部更新中继续尝试")
初始化节点, 需要指定节点名称
def
random_bytes(self, length: int) -> bytes:
144 def random_bytes(self, length: int) -> bytes: 145 """ 生成随机字节 """ 146 return bytes([random.randint(0, 255) for _ in range(length)])
生成随机字节
def
fixed_bytes(self, byte: int, length: int) -> bytes:
148 def fixed_bytes(self, byte: int, length: int) -> bytes: 149 """ 生成固定字节 """ 150 return bytes([byte for _ in range(length)])
生成固定字节
def
transmit(self, data: bytes) -> bytes:
152 def transmit(self, data: bytes) -> bytes: 153 """ 发送串口数据 """ 154 if self._serial is None: 155 if self._warn: 156 warning(f"节点 {self.name} 串口未初始化,无法发送数据") 157 return 158 if self._checksum_type !=CheckSumType.NONE: 159 data += self._get_check_sum(data) 160 if self._header: 161 data = self._add_header(data) 162 self._serial.write(data) 163 return data
发送串口数据
def
receive(self, len: int) -> bytes:
165 def receive(self, len: int) -> bytes: 166 """ 接收串口数据 """ 167 if self._serial is None: 168 if self._warn: 169 warning(f"节点 {self.name} 串口未初始化,无法接收数据") 170 return 171 if self._serial.in_waiting >= len: 172 return self._serial.read(len) 173 else: 174 return None
接收串口数据
def
check_sum(self, data: bytes) -> bool:
176 def check_sum(self, data: bytes) -> bool: 177 """ 校验串口数据 """ 178 if self._checksum_type == CheckSumType.NONE: 179 return True 180 checksum_length = checksum_length_map.get(self._checksum_type) 181 if checksum_length is None: 182 raise ValueError("无效的校验和类型,无法进行校验") 183 184 data_to_check = data[len(self._header):-checksum_length] 185 expected_checksum = data[-checksum_length:] 186 calculated_checksum = self._get_check_sum(data_to_check) 187 188 return calculated_checksum == expected_checksum
校验串口数据