536 lines
19 KiB
Python
536 lines
19 KiB
Python
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}") |