Files
Feeding_control_system/hardware/RFID/command_hex.py
2025-10-24 14:55:58 +08:00

536 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
# 添加CRCLSB在前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)
# 添加CRCLSB在前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)
# 添加CRCLSB在前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)
# 添加CRCLSB在前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)
# 添加CRCLSB在前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)
# 添加CRCLSB在前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 Word1Word2,… 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}")