from .crc16 import crc16 class command_hex: """ 命令十六进制转换模块 提供将十六进制和RFID参数的转换 """ #region RFID 工作模式(可设置主动模式推送信息) @staticmethod def build_set_mode_command(address, read_mode=0, mode_state=0, mem_inven=1, first_adr=0, word_num=1, tag_filt_time=0)->bytes: """ 构建设置读写器工作模式的指令(命令0x35) 工作模式中可设置主动模式下的起始地址,推送的存储区,以及标签输出间隔时间 参数: address: 读写器地址(0xXX格式) read_mode: 工作模式选择 (0=应答模式, 1=主动模式, 2=触发模式低电平, 3=触发模式高电平) mode_state: 输出方式和其他设置 Bit1: 0=韦根输出, 1=RS232输出 Bit2: 0=开蜂鸣器, 1=关蜂鸣器 Bit3: 0=First_Adr为字地址, 1=First_Adr为字节地址 mem_inven: 存储区选择 (0=保留区, 1=EPC, 2=TID, 3=用户, 4=多张查询, 5=单张查询, 6=多张TID询查) first_adr: 起始地址 word_num: 读取字数量 (1-32) tag_filt_time: 主动模式下标签输出间隔时间 (0-255)*1s 返回: bytes: 完整的命令字节串,包含CRC校验 """ # 验证参数 if word_num < 1 or word_num > 32: raise ValueError("Word_Num必须在1-32范围内") if mem_inven not in [0, 1, 2, 3, 4, 5, 6]: raise ValueError("Mem_Inven参数无效") # 构建命令帧 length = 0x0A # 命令长度 cmd = 0x35 # 命令代码 # 构建数据部分 data = [ read_mode & 0xFF, mode_state & 0xFF, mem_inven & 0xFF, first_adr & 0xFF, word_num & 0xFF, tag_filt_time & 0xFF ] # 构建命令帧 frame = [length, address & 0xFF, cmd] + data # 计算CRC16 crc_value = crc16.cal(frame) # 添加CRC(LSB在前,MSB在后) frame.append(crc_value & 0xFF) # LSB frame.append((crc_value >> 8) & 0xFF) # MSB return bytes(frame) @staticmethod def parse_set_mode_hex(response)->bool: """ 解析设置工作模式命令的应答数据 参数: response: 读写器返回的完整应答数据 返回: bool: True表示设置成功,False表示失败 """ if len(response)<=1 or len(response) != response[0]+1: # 0x05 + 2字节CRC = 7字节 raise ValueError("应答数据长度不正确") if response[2] != 0x35: # 确认是0x35命令的应答 raise ValueError("应答命令不匹配") if not crc16.verify(response): raise ValueError("CRC校验失败") return response[3] == 0x00 # 0x00表示成功 @staticmethod def build_read_mode_command(address)->bytes: """ 读取读写器工作模式参数的指令 参数: address: 读写器地址(0xXX格式) 返回: bytes: 完整的命令字节串,包含CRC校验 """ # 构建命令帧 length = 0x04 # 命令长度 cmd = 0x36 # 命令代码 # 构建命令帧 frame = [length, address & 0xFF, cmd] # 计算CRC16 crc_value = crc16.cal(frame) # 添加CRC(LSB在前,MSB在后) frame.append(crc_value & 0xFF) # LSB frame.append((crc_value >> 8) & 0xFF) # MSB return bytes(frame) @staticmethod def parse_read_mode_hex(response)->dict: """ 解析读取工作模式参数命令的应答数据 参数: response: 读写器返回的完整应答数据(包含CRC校验) 返回: dict: 解析后的参数字典 """ if len(response)<=1 or len(response) != response[0]+1: # 0x11 = 19字节 raise ValueError("应答数据长度不正确") if response[2] != 0x36: # 确认是0x36命令的应答 raise ValueError("应答命令不匹配") if response[3] != 0x00: # 检查状态是否成功 raise ValueError(f"命令执行失败,状态码: {response[3]:02X}") if not crc16.verify(response): raise ValueError("CRC校验失败") # 解析参数 params = { # 韦根参数 'wg_mode': response[4], 'wg_data_interval': response[5], 'wg_pulse_width': response[6], 'wg_pulse_interval': response[7], # 工作模式参数 'read_mode': response[8], 'mode_state': response[9], 'mem_inven': response[10], 'first_adr': response[11], 'word_num': response[12], 'tag_filt_time': response[13], 'reserved1': response[14], 'reserved2': response[15] } return params #endregion RFID 工作模式 #region RFID 功率设置(距离远近) @staticmethod def build_set_power_command(address, power_value)->bytes: """ 构建设置读写器功率的命令(命令0x2F) 参数: address: 读写器地址(0xXX格式) power_value: 功率值(十进制,范围0~26,单位dBm) 返回: bytes: 完整的命令字节串,包含CRC校验 """ # 验证功率参数范围 if power_value < 0 or power_value > 26: raise ValueError("功率值必须在0~26范围内") # 构建命令帧 length = 0x05 # 命令长度 cmd = 0x2F # 命令代码 # 构建命令帧 frame = [length, address & 0xFF, cmd, power_value & 0xFF] # 计算CRC16 crc_value = crc16.cal(frame) # 添加CRC(LSB在前,MSB在后) frame.append(crc_value & 0xFF) # LSB frame.append((crc_value >> 8) & 0xFF) # MSB return bytes(frame) @staticmethod def parse_set_power_hex(response)->bool: """ 解析设置功率命令的应答数据 参数: response: 读写器返回的完整应答数据 返回: bool: True表示设置成功,False表示失败 """ if len(response)<=1 or len(response) != response[0]+1: # 0x05 + 2字节CRC = 7字节 raise ValueError("应答数据长度不正确") if response[2] != 0x2F: # 确认是0x2F命令的应答 raise ValueError("应答命令不匹配") if not crc16.verify(response): raise ValueError("CRC校验失败") return response[3] == 0x00 # 0x00表示成功 #endregion RFID 功率设置 #region 接触模式中的读卡间隔设置 @staticmethod def build_trigger_delay_command(address, trigger_time): """ 构建主动模式触发延时设置命令(命令0x3B) 参数: address: 读写器地址(0xXX格式) trigger_time: 触发有效时间(范围0~254,单位秒;255表示获取当前设置) 返回: bytes: 完整的命令字节串,包含CRC校验 """ # 验证触发时间参数范围 if trigger_time < 0 or trigger_time > 255: raise ValueError("触发时间必须在0~255范围内") # 构建命令帧 length = 0x05 # 命令长度 cmd = 0x3B # 命令代码 # 构建命令帧 frame = [length, address & 0xFF, cmd, trigger_time & 0xFF] # 计算CRC16 crc_value = crc16.cal(frame) # 添加CRC(LSB在前,MSB在后) frame.append(crc_value & 0xFF) # LSB frame.append((crc_value >> 8) & 0xFF) # MSB return bytes(frame) @staticmethod def parse_trigger_delay_hex(response): """ 解析触发延时设置命令的应答数据 参数: response: 读写器返回的完整应答数据 返回: int: 当前触发有效时间值(0~254),表示秒数 """ if len(response)<=1 or len(response) != response[0]+1: # 0x06 + 2字节CRC = 8字节 raise ValueError("应答数据长度不正确") if response[2] != 0x3B: # 确认是0x3B命令的应答 raise ValueError("应答命令不匹配") if not crc16.verify(response): raise ValueError("CRC校验失败") if response[3] != 0x00: # 检查状态是否成功 raise ValueError(f"命令执行失败,状态码: {response[3]:02X}") # 返回触发时间值 return response[4] #endregion 接触模式中的读卡间隔设置 #region 接触模式中的读卡间隔设置 @staticmethod def build_set_readinterval_command(address, delay_time)->bytes: """ 构建设置主动模式读卡间隔命令(命令0x42) 参数: address: 读写器地址(0xXX格式) delay_time: 读卡间隔时间(范围0~254,单位毫秒;255表示读取参数) 返回: bytes: 完整的命令字节串,包含CRC校验 """ # 验证间隔时间参数范围 if delay_time < 0 or delay_time > 255: raise ValueError("间隔时间必须在0~255范围内") # 构建命令帧 length = 0x05 # 命令长度 cmd = 0x42 # 命令代码 # 构建命令帧 frame = [length, address & 0xFF, cmd, delay_time & 0xFF] # 计算CRC16 crc_value = crc16.cal(frame) # 添加CRC(LSB在前,MSB在后) frame.append(crc_value & 0xFF) # LSB frame.append((crc_value >> 8) & 0xFF) # MSB return bytes(frame) @staticmethod def parse_set_readinterval_hex(response)->bool: """ 解析设置主动模式读卡间隔命令的应答数据 参数: response: 读写器返回的完整应答数据 返回: bool: True表示设置成功,False表示失败 """ if len(response)<=1 or len(response) != response[0]+1: # 0x05 + 2字节CRC = 7字节 raise ValueError("应答数据长度不正确") if response[2] != 0x42: # 确认是0x42命令的应答 raise ValueError("应答命令不匹配") if not crc16.verify(response): raise ValueError("CRC校验失败") return response[3] == 0x00 # 0x00表示成功 #endregion 接触模式中的读卡间隔设置 #region 获取读写器信息 @staticmethod def build_reader_info_command(address): """ 构建读取读写器信息命令(命令0x21) 参数: address: 读写器地址(0xXX格式) 返回: bytes: 完整的命令字节串,包含CRC校验 """ # 构建命令帧 length = 0x04 # 命令长度 cmd = 0x21 # 命令代码 # 构建命令帧 frame = [length, address & 0xFF, cmd] # 计算CRC16 crc_value = crc16.cal(frame) # 添加CRC(LSB在前,MSB在后) frame.append(crc_value & 0xFF) # LSB frame.append((crc_value >> 8) & 0xFF) # MSB return bytes(frame) @staticmethod def parse_reader_info_hex(response): """ 解析读取读写器信息命令的应答数据 参数: response: 读写器返回的完整应答数据(包含CRC校验) 返回: dict: 解析后的读写器信息字典 """ # 根据文档和实际测试,有效应答应至少包含基本信息部分 if len(response)<=1 or len(response) != response[0]+1: # 至少需要包含基本部分 raise ValueError(f"应答数据长度不正确,预期至少13字节,实际收到{len(response)}字节") if response[2] != 0x21: # 确认是0x21命令的应答 raise ValueError("应答命令不匹配") if response[3] != 0x00: # 检查状态是否成功 raise ValueError(f"命令执行失败,状态码: {response[3]:02X}") if not crc16.verify(response): raise ValueError("CRC校验失败") # 解析版本号(2字节) version_main = response[4] # 主版本号 version_sub = response[5] # 子版本号 version_str = f"{version_main}.{version_sub}" # 解析读写器类型 reader_type = response[6] reader_type_desc = "UHFReader2001" if reader_type == 0x89 else f"未知类型(0x{reader_type:02X})" # 解析协议支持信息 protocol_support = response[7] support_6c = bool(protocol_support & 0x02) # Bit1 support_6b = bool(protocol_support & 0x01) # Bit0 # 解析频率信息 max_freq = response[8] min_freq = response[9] # 解析频段设置 max_freq_band_bits = (max_freq >> 6) & 0x03 # Bit7-Bit6 min_freq_band_bits = (min_freq >> 6) & 0x03 # Bit7-Bit6 # 根据频段设置位确定频段 freq_band = "未知" if max_freq_band_bits == 0x00: if min_freq_band_bits == 0x00: freq_band = "保留" elif min_freq_band_bits == 0x01: freq_band = "Chinese band2" elif min_freq_band_bits == 0x02: freq_band = "US band" elif min_freq_band_bits == 0x03: freq_band = "Korean band" elif max_freq_band_bits == 0x01 and min_freq_band_bits == 0x00: freq_band = "EU band" # 解析实际频率值(忽略频段设置位) actual_max_freq = max_freq & 0x3F # 清除Bit7-Bit6 actual_min_freq = min_freq & 0x3F # 清除Bit7-Bit6 # 解析功率 power = response[10] # 解析询查时间(已作废) scntm = response[11] # 构建返回数据 reader_info = { 'version': version_str, 'version_main': version_main, 'version_sub': version_sub, 'reader_type': reader_type, 'reader_type_desc': reader_type_desc, 'protocol_support': protocol_support, 'support_18000_6c': support_6c, 'support_18000_6b': support_6b, 'max_freq_raw': max_freq, 'min_freq_raw': min_freq, 'max_freq_value': actual_max_freq, 'min_freq_value': actual_min_freq, 'freq_band': freq_band, 'power': power, 'scntm': scntm, 'reserved1': response[11], 'reserved2': response[12] } return reader_info #endregion 获取读写器信息 #region 解析18000-6C协议,用户区数据 @staticmethod def parse_user_data_hex(response)->str: """ 解析18000-6C协议的读取数据应答(命令0xEE) 当读写器支持18000-6C协议,Mem_Inven为0x00~0x03时使用 数据格式: Len Adr reCmd Status Data[] CRC-16 0xXX 0xXX 0xEE 0x00 Word1,Word2,… LSB MSB 参数: response: 读写器返回的完整应答数据(包含CRC校验) 返回: - loc_string: 将数据转换为ASCII字符串 """ # 寻找连续三个0x2C的情况 for idx in range(len(response) - 2): if response[idx] == 0x2C and response[idx+1] == 0x2C and response[idx+2] == 0x2C: response=response[:idx] # 验证响应长度 if len(response) <= 1 or len(response) != response[0] + 1: raise ValueError("应答数据长度不正确") # 验证是否为0xEE命令的应答 if response[2] != 0xEE: raise ValueError("应答命令不匹配,预期0xEE") # 检查执行状态 status = response[3] if status != 0x00: raise ValueError(f"命令执行失败,状态码: {status:02X}") # 验证CRC校验 if not crc16.verify(response): raise ValueError("CRC校验失败") # 提取Data[]部分(不包含CRC字节) # 数据部分从第4个字节开始(索引3),直到倒数第2个字节(不含CRC) data_part = response[4:-2] # 过滤掉null字符(0x00),这些字符在ASCII解码后通常不可见但会占用字符串长度 data_part = bytes([b for b in data_part if b != 0x00]) loc_string = '' if data_part: try: loc_string = data_part.decode('ascii') except UnicodeDecodeError: print(f"无法将数据转换为ASCII字符串: {data_part}") return loc_string #endregion 解析18000-6C协议,用户区数据 if __name__ == "__main__": print("\n测试解析18000-6C协议数据响应(示例):") # 假设的18000-6C数据响应(长度为0x0C,包含4个字节的Data部分,2个字) # 示例数据:Len=0x0C, Adr=0x00, reCmd=0xEE, Status=0x00, Data=[0x12,0x34,0x56,0x78], CRC=计算值 # 构建测试数据 test_data_6c = [ 0x21, # 长度 0x00, # 地址 0xEE, # 命令 0x00, # 状态成功 0x53, 0x48, 0x52, 0x32, 0x42, 0x32, 0x2D, 0x31, 0x32, 0x2C, 0x42, 0x32, 0x2C, 0x36, 0x36, 0x30, 0x30, 0x2A, 0x31, 0x35, 0x30, 0x30, 0x2C, 0x31, 0x2E, 0x39, 0x31, 0x30 ] # 计算CRC并添加到数据末尾 crc_value = crc16.cal(test_data_6c) test_data_6c.append(crc_value & 0xFF) # LSB test_data_6c.append((crc_value >> 8) & 0xFF) # MSB test_data_6c_bytes = bytes(test_data_6c) print(f"测试数据: {' '.join(f'{b:02X}' for b in test_data_6c_bytes)}") test_data_6c.append(0x2C) test_data_6c.append(0x2C) test_data_6c.append(0x2C) try: parsed_6c_data =command_hex.parse_user_data_hex(test_data_6c_bytes) print("解析结果:") print(f" ASCII字符串: {parsed_6c_data}") except Exception as e: print(f"解析错误: {e}")