基本功能完成

This commit is contained in:
2026-04-15 12:06:20 +08:00
parent a9f0b97d26
commit 0ea4139f20
3 changed files with 428 additions and 229 deletions

View File

@@ -13,7 +13,7 @@
主机发送的命令帧结构如下(从 Report ID 后开始计算偏移)。**SetReq 必须填充至 256 字节**,不足部分用 `0x00` 补齐。 主机发送的命令帧结构如下(从 Report ID 后开始计算偏移)。**SetReq 必须填充至 256 字节**,不足部分用 `0x00` 补齐。
| 偏移 | 长度 | 字段名 | 字节序 | 示例(打开蜂鸣器) | 说明 | | 偏移 | 长度 | 字段名 | 字节序 | 示例(打开蜂鸣器) | 说明 |
|:---|:---|:---|:---|:---|:---| | :----- | :--- | :----------- | :------- | :----------------- | :------------------------- |
| 0x00 | 1 | Report ID | - | `01` | 固定 `0x01` | | 0x00 | 1 | Report ID | - | `01` | 固定 `0x01` |
| 0x01 | 4 | Fixed | - | `00 00 00 00` | 固定 | | 0x01 | 4 | Fixed | - | `00 00 00 00` | 固定 |
| 0x05 | 2 | Frame Length | **大端** | `00 07` | 从Constant到Data的总字节数 | | 0x05 | 2 | Frame Length | **大端** | `00 07` | 从Constant到Data的总字节数 |
@@ -31,11 +31,10 @@
**Data 段格式** **Data 段格式**
| 偏移 | 长度 | 字段名 | 说明 | | 偏移 | 长度 | 字段名 | 说明 |
|:---|:---|:---|:---| | :--- | :--- | :------------ | :-------------------- |
| 0x00 | 1 | Status | `0x00` 成功,非零异常 | | 0x00 | 1 | Status | `0x00` 成功,非零异常 |
| 0x01 | M | Response Data | 实际返回数据 | | 0x01 | M | Response Data | 实际返回数据 |
## 3. 校验算法 ## 3. 校验算法
### 3.1 外层帧校验 ### 3.1 外层帧校验
@@ -44,6 +43,7 @@
**计算范围**`Data Length` 两字节+ `Data` 全部字节。 **计算范围**`Data Length` 两字节+ `Data` 全部字节。
**验证示例**(打开蜂鸣器): **验证示例**(打开蜂鸣器):
- Data Length: `00 02` - Data Length: `00 02`
- Data: `cd 01` - Data: `cd 01`
- XOR: `00 ^ 02 ^ cd ^ 01 = ce` - XOR: `00 ^ 02 ^ cd ^ 01 = ce`
@@ -59,6 +59,7 @@
**算法**CRC-16/GENIBUS **算法**CRC-16/GENIBUS
**参数** **参数**
- 多项式:`0x1021` - 多项式:`0x1021`
- 初始值:`0xFFFF` - 初始值:`0xFFFF`
- 输出异或:`0xFFFF` - 输出异或:`0xFFFF`
@@ -73,7 +74,7 @@
### 4.1 系统与配置类命令 ### 4.1 系统与配置类命令
| 功能 | SetReq Data | GetRes Data | 备注 | | 功能 | SetReq Data | GetRes Data | 备注 |
|:---|:---|:---|:---| | :----------- | :--------------------------- | :----------------------------------- | :--------------- |
| 读版本号 | `c0` | `00` + ASCII 字符串 | | | 读版本号 | `c0` | `00` + ASCII 字符串 | |
| 恢复出厂设置 | `cf` | `00 80` | | | 恢复出厂设置 | `cf` | `00 80` | |
| 读取格式 | `83` | `00 01 00 00 00 01 01 00 00`(示例) | 返回当前格式参数 | | 读取格式 | `83` | `00 01 00 00 00 01 01 00 00`(示例) | 返回当前格式参数 |
@@ -82,23 +83,25 @@
### 4.2 蜂鸣器控制 ### 4.2 蜂鸣器控制
| 功能 | SetReq Data | GetRes Data | | 功能 | SetReq Data | GetRes Data |
|:---|:---|:---| | :--------- | :---------- | :---------- |
| 打开蜂鸣器 | `cd 01` | `00 80` | | 打开蜂鸣器 | `cd 01` | `00 80` |
| 关闭蜂鸣器 | `cd 00` | `00 80` | | 关闭蜂鸣器 | `cd 00` | `00 80` |
### 4.3 射频电源控制 ### 4.3 射频电源控制
| 功能 | SetReq Data | GetRes Data | | 功能 | SetReq Data | GetRes Data |
|:---|:---|:---| | :------- | :---------- | :---------- |
| 打开射频 | `90 01` | `00 80` | | 打开射频 | `90 01` | `00 80` |
| 关闭射频 | `90 00` | `00 80` | | 关闭射频 | `90 00` | `00 80` |
### 4.4 工作模式与功率 ### 4.4 工作模式与功率
| 功能 | SetReq Data | GetRes Data | 参数说明 | | 功能 | SetReq Data | GetRes Data | 参数说明 |
|:---|:---|:---|:---| | :------------- | :---------- | :---------- | :----------- |
| 设置单标签巡查 | `0f 01` | `00 80` | 模式 1主动读卡 | | 主动读卡 | `0f 01` | `00 80` | 触发单次寻卡 |
| 设置被动模式 | `0f 02` | `00 80` | 模式 2被动读卡| | 被动读卡 | `0f 02` | `00 80` | 持续等待标签 |
| 设置单标签巡查 | `0f 01` | `00 80` | 模式 1 |
| 设置被动模式 | `0f 02` | `00 80` | 模式 2 |
| 设置多标签巡查 | `0f 03` | `00 80` | 模式 3 | | 设置多标签巡查 | `0f 03` | `00 80` | 模式 3 |
| 设置功率 = N | `cc N` | `00 80` | N 范围 0~9 | | 设置功率 = N | `cc N` | `00 80` | N 范围 0~9 |
@@ -107,7 +110,7 @@
所有 EPC 相关命令(读、选中、写)的 SetReq/GetRes Data 段均遵循以下内部结构: 所有 EPC 相关命令(读、选中、写)的 SetReq/GetRes Data 段均遵循以下内部结构:
| 偏移 | 长度 | 字段名 | 字节序 | 说明 | | 偏移 | 长度 | 字段名 | 字节序 | 说明 |
|:---|:---|:---|:---|:---| | :----- | :--- | :--------------- | :------- | :---------------------------------- |
| 0x00 | 1 | Command/Status | - | 固定 SetReq=`ce`GetRes=`00`(成功) | | 0x00 | 1 | Command/Status | - | 固定 SetReq=`ce`GetRes=`00`(成功) |
| 0x01 | 1 | Magic | - | 固定 `bb` | | 0x01 | 1 | Magic | - | 固定 `bb` |
| 0x02 | 2 | Card Op Command | - | 操作码(见各子命令) | | 0x02 | 2 | Card Op Command | - | 操作码(见各子命令) |
@@ -125,11 +128,13 @@
- Payload Checksum = `22` - Payload Checksum = `22`
- **GetRes Data 段(有卡)** - **GetRes Data 段(有卡)**
``` ```
00 bb 02 22 00 0b d3 18 00 11 22 33 44 55 66 75 ce c2 7e 00 bb 02 22 00 0b d3 18 00 11 22 33 44 55 66 75 ce c2 7e
``` ```
| 偏移 | 字段 | 值示例 | 说明 | | 偏移 | 字段 | 值示例 | 说明 |
|:---|:---|:---|:---| | :--- | :---------------- | :------------------ | :------------------- |
| 0x00 | Status | `00` | 成功 | | 0x00 | Status | `00` | 成功 |
| 0x01 | Magic | `bb` | 固定 | | 0x01 | Magic | `bb` | 固定 |
| 0x02 | Card Op Resp | `02 22` | 读卡成功标志 | | 0x02 | Card Op Resp | `02 22` | 读卡成功标志 |
@@ -149,9 +154,11 @@
**Card Op Command**`00 0c` **Card Op Command**`00 0c`
- **SetReq Data 示例**(选中 EPC `11 22` - **SetReq Data 示例**(选中 EPC `11 22`
``` ```
ce bb 00 0c 00 09 01 00 00 00 20 10 00 11 22 79 7e ce bb 00 0c 00 09 01 00 00 00 20 10 00 11 22 79 7e
``` ```
- Internal Length = `00 09` - Internal Length = `00 09`
- Payload7+N 字节7字节保留字段 + EPC Data - Payload7+N 字节7字节保留字段 + EPC Data
- Payload Checksum = `79` - Payload Checksum = `79`
@@ -160,6 +167,7 @@
``` ```
00 bb 01 0c 00 01 00 0e 7e 00 bb 01 0c 00 01 00 0e 7e
``` ```
- Card Op Resp = `01 0c` - Card Op Resp = `01 0c`
- Internal Length = `00 01` - Internal Length = `00 01`
- Payload = `00`(选中成功标志) - Payload = `00`(选中成功标志)
@@ -169,9 +177,11 @@
**Card Op Command**`00 49` **Card Op Command**`00 49`
- **SetReq Data 示例**(将 EPC `11 22` 改为 `11 23` - **SetReq Data 示例**(将 EPC `11 22` 改为 `11 23`
``` ```
ce bb 00 49 00 0f 00 00 00 00 01 00 00 00 03 ca 9e 08 00 11 23 00 7e ce bb 00 49 00 0f 00 00 00 00 01 00 00 00 03 ca 9e 08 00 11 23 00 7e
``` ```
- Internal Length = `00 0f` - Internal Length = `00 0f`
- Payload15 字节): - Payload15 字节):
| 偏移 | 字段 | 值示例 | 说明 | | 偏移 | 字段 | 值示例 | 说明 |
@@ -188,13 +198,14 @@
``` ```
00 bb 01 49 00 06 04 08 00 11 22 00 8f 7e 00 bb 01 49 00 06 04 08 00 11 22 00 8f 7e
``` ```
- Card Op Resp = `01 49` - Card Op Resp = `01 49`
- Payload 偏移0x03中开始原 EPC `11 22` 用于确认 - Payload 偏移0x03中开始原 EPC `11 22` 用于确认不重复
## 5. 附录命令速查表SetReq Data 部分) ## 5. 附录命令速查表SetReq Data 部分)
| 功能 | Data十六进制 | | 功能 | Data十六进制 |
|:---|:---| | :------------------ | :--------------------------------------------------------------------- |
| 读版本号 | `c0` | | 读版本号 | `c0` |
| 恢复出厂设置 | `cf` | | 恢复出厂设置 | `cf` |
| 读取格式 | `83` | | 读取格式 | `83` |
@@ -203,6 +214,8 @@
| 关闭蜂鸣器 | `cd 00` | | 关闭蜂鸣器 | `cd 00` |
| 打开射频 | `90 01` | | 打开射频 | `90 01` |
| 关闭射频 | `90 00` | | 关闭射频 | `90 00` |
| 主动读卡 | `0f 01` |
| 被动读卡 | `0f 02` |
| 设置单标签巡查 | `0f 01` | | 设置单标签巡查 | `0f 01` |
| 设置被动模式 | `0f 02` | | 设置被动模式 | `0f 02` |
| 设置多标签巡查 | `0f 03` | | 设置多标签巡查 | `0f 03` |

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
hidapi

View File

@@ -5,19 +5,21 @@ MT6 RFID 读卡器测试程序
基于 USB HID 私有协议 基于 USB HID 私有协议
""" """
import sys
import os import os
import sys
import time import time
# 添加 PySimpleGUI 目录到路径 # 添加 PySimpleGUI 目录到路径
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'PySimpleGUI')) sys.path.insert(
0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "PySimpleGUI")
)
import PySimpleGUI as sg import PySimpleGUI as sg
try: try:
import hid import hid
except ImportError: except ImportError:
sg.popup_error('请安装 hidapi 库:\npip install hidapi') sg.popup_error("请安装 hidapi 库:\npip install hidapi")
sys.exit(1) sys.exit(1)
@@ -32,6 +34,7 @@ PRODUCT_ID = 0x0035 # 默认PID
# ==================== 协议函数 ==================== # ==================== 协议函数 ====================
def calc_outer_checksum(data_len_bytes: bytes, payload: bytes) -> int: def calc_outer_checksum(data_len_bytes: bytes, payload: bytes) -> int:
"""计算外层帧校验XOR""" """计算外层帧校验XOR"""
result = 0 result = 0
@@ -40,6 +43,20 @@ def calc_outer_checksum(data_len_bytes: bytes, payload: bytes) -> int:
return result return result
def calc_epc_crc(epc_len_indicator: int, epc_status: int, epc_data: bytes) -> int:
data = bytes([epc_len_indicator, epc_status]) + epc_data
crc = 0xFFFF
for byte in data:
crc ^= byte << 8
for _ in range(8):
if crc & 0x8000:
crc = (crc << 1) ^ 0x1021
else:
crc = crc << 1
crc &= 0xFFFF
return crc ^ 0xFFFF
def build_setreq_frame(data: bytes) -> bytes: def build_setreq_frame(data: bytes) -> bytes:
""" """
构建 SetReq 帧 构建 SetReq 帧
@@ -49,10 +66,10 @@ def build_setreq_frame(data: bytes) -> bytes:
# Frame Length: 从 0x07 到 Checksum包含校验和不含结束符 # Frame Length: 从 0x07 到 Checksum包含校验和不含结束符
# = 2 (constant) + 2 (data length) + len(data) + 1 (checksum) # = 2 (constant) + 2 (data length) + len(data) + 1 (checksum)
frame_length = 5 + len(data) frame_length = 5 + len(data)
frame_length_bytes = frame_length.to_bytes(2, 'big') frame_length_bytes = frame_length.to_bytes(2, "big")
# Data Length # Data Length
data_length_bytes = len(data).to_bytes(2, 'big') data_length_bytes = len(data).to_bytes(2, "big")
# Checksum # Checksum
checksum = calc_outer_checksum(data_length_bytes, data) checksum = calc_outer_checksum(data_length_bytes, data)
@@ -60,13 +77,13 @@ def build_setreq_frame(data: bytes) -> bytes:
# 构建帧 # 构建帧
frame = bytearray(REPORT_SIZE) frame = bytearray(REPORT_SIZE)
frame[0] = REPORT_ID_SET_REQ frame[0] = REPORT_ID_SET_REQ
frame[1:5] = b'\x00\x00\x00\x00' # Fixed frame[1:5] = b"\x00\x00\x00\x00" # Fixed
frame[5:7] = frame_length_bytes # Frame Length frame[5:7] = frame_length_bytes # Frame Length
frame[7:9] = b'\x00\x02' # Constant frame[7:9] = b"\x00\x02" # Constant
frame[9:11] = data_length_bytes # Data Length frame[9:11] = data_length_bytes # Data Length
frame[11:11+len(data)] = data # Data frame[11 : 11 + len(data)] = data # Data
frame[11+len(data)] = checksum # Checksum frame[11 + len(data)] = checksum # Checksum
frame[12+len(data)] = 0x03 # End Marker frame[12 + len(data)] = 0x03 # End Marker
return bytes(frame) return bytes(frame)
@@ -87,8 +104,8 @@ def parse_getres_frame(data: bytes) -> tuple:
return None return None
# 解析帧结构(大端序) # 解析帧结构(大端序)
frame_length = int.from_bytes(data[5:7], 'big') frame_length = int.from_bytes(data[5:7], "big")
data_length = int.from_bytes(data[9:11], 'big') data_length = int.from_bytes(data[9:11], "big")
# 检查数据长度是否合理 # 检查数据长度是否合理
expected_total_len = 11 + data_length + 2 # header + data + checksum + end_marker expected_total_len = 11 + data_length + 2 # header + data + checksum + end_marker
@@ -97,11 +114,11 @@ def parse_getres_frame(data: bytes) -> tuple:
return None return None
# 提取 Data 段 # 提取 Data 段
response_data = data[11:11+data_length] response_data = data[11 : 11 + data_length]
# 提取 Checksum 和 End Marker # 提取 Checksum 和 End Marker
received_checksum = data[11+data_length] received_checksum = data[11 + data_length]
received_end_marker = data[12+data_length] received_end_marker = data[12 + data_length]
# 验证 End Marker固定为 0x03 # 验证 End Marker固定为 0x03
if received_end_marker != 0x03: if received_end_marker != 0x03:
@@ -111,10 +128,12 @@ def parse_getres_frame(data: bytes) -> tuple:
# 计算并验证 ChecksumXOR算法Data Length两字节 + Data全部字节 # 计算并验证 ChecksumXOR算法Data Length两字节 + Data全部字节
calculated_checksum = calc_outer_checksum(data[9:11], response_data) calculated_checksum = calc_outer_checksum(data[9:11], response_data)
if calculated_checksum != received_checksum: if calculated_checksum != received_checksum:
print(f"[DEBUG] Checksum错误: 计算=0x{calculated_checksum:02x}, 接收=0x{received_checksum:02x}") print(
f"[DEBUG] Checksum错误: 计算=0x{calculated_checksum:02x}, 接收=0x{received_checksum:02x}"
)
return None return None
print(f"[DEBUG] 帧校验通过: Checksum=0x{received_checksum:02x}, EndMarker=0x{received_end_marker:02x}") # print(f"[DEBUG] 帧校验通过: Checksum=0x{received_checksum:02x}, EndMarker=0x{received_end_marker:02x}")
if len(response_data) < 1: if len(response_data) < 1:
return None return None
@@ -125,14 +144,15 @@ def parse_getres_frame(data: bytes) -> tuple:
# ==================== 命令函数 ==================== # ==================== 命令函数 ====================
def cmd_get_version() -> bytes: def cmd_get_version() -> bytes:
"""读版本号命令""" """读版本号命令"""
return bytes([0xc0]) return bytes([0xC0])
def cmd_factory_reset() -> bytes: def cmd_factory_reset() -> bytes:
"""恢复出厂设置命令""" """恢复出厂设置命令"""
return bytes([0xcf]) return bytes([0xCF])
def cmd_read_format() -> bytes: def cmd_read_format() -> bytes:
@@ -142,12 +162,12 @@ def cmd_read_format() -> bytes:
def cmd_set_format() -> bytes: def cmd_set_format() -> bytes:
"""设置格式命令""" """设置格式命令"""
return bytes([0x82, 0x0f, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00]) return bytes([0x82, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00])
def cmd_buzzer(on: bool) -> bytes: def cmd_buzzer(on: bool) -> bytes:
"""蜂鸣器控制命令""" """蜂鸣器控制命令"""
return bytes([0xcd, 0x01 if on else 0x00]) return bytes([0xCD, 0x01 if on else 0x00])
def cmd_rf_power(on: bool) -> bytes: def cmd_rf_power(on: bool) -> bytes:
@@ -158,7 +178,7 @@ def cmd_rf_power(on: bool) -> bytes:
def cmd_set_power(power: int) -> bytes: def cmd_set_power(power: int) -> bytes:
"""设置功率命令 (0-9)""" """设置功率命令 (0-9)"""
power = max(0, min(9, power)) power = max(0, min(9, power))
return bytes([0xcc, power]) return bytes([0xCC, power])
def cmd_set_mode(mode: int) -> bytes: def cmd_set_mode(mode: int) -> bytes:
@@ -167,12 +187,12 @@ def cmd_set_mode(mode: int) -> bytes:
2: 被动模式 2: 被动模式
3: 多标签巡查 3: 多标签巡查
""" """
return bytes([0x0f, mode]) return bytes([0x0F, mode])
def cmd_read_epc() -> bytes: def cmd_read_epc() -> bytes:
"""读取 EPC 命令""" """读取 EPC 命令"""
return bytes([0xce, 0xbb, 0x00, 0x22, 0x00, 0x00, 0x22, 0x7e]) return bytes([0xCE, 0xBB, 0x00, 0x22, 0x00, 0x00, 0x22, 0x7E])
def cmd_select_card(epc_data: bytes) -> bytes: def cmd_select_card(epc_data: bytes) -> bytes:
@@ -181,14 +201,20 @@ def cmd_select_card(epc_data: bytes) -> bytes:
payload = bytes([0x01, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00]) + epc_data payload = bytes([0x01, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00]) + epc_data
# 计算 checksum包含 Card Op Command + Internal Length + Payload # 计算 checksum包含 Card Op Command + Internal Length + Payload
card_op_cmd = bytes([0x00, 0x0c]) card_op_cmd = bytes([0x00, 0x0C])
internal_len = len(payload).to_bytes(2, 'big') internal_len = len(payload).to_bytes(2, "big")
checksum = 0 checksum = 0
for b in card_op_cmd + internal_len + payload: for b in card_op_cmd + internal_len + payload:
checksum = (checksum + b) & 0xFF checksum = (checksum + b) & 0xFF
return bytes([0xce, 0xbb]) + card_op_cmd + internal_len + payload + bytes([checksum, 0x7e]) return (
bytes([0xCE, 0xBB])
+ card_op_cmd
+ internal_len
+ payload
+ bytes([checksum, 0x7E])
)
def cmd_write_epc(old_epc_crc: bytes, new_epc: bytes) -> bytes: def cmd_write_epc(old_epc_crc: bytes, new_epc: bytes) -> bytes:
@@ -200,7 +226,9 @@ def cmd_write_epc(old_epc_crc: bytes, new_epc: bytes) -> bytes:
word_count = 2 + len(new_epc) // 2 word_count = 2 + len(new_epc) // 2
payload = bytes([0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]) # Reserved (8 bytes) payload = bytes(
[0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]
) # Reserved (8 bytes)
payload += bytes([word_count]) # Word Count payload += bytes([word_count]) # Word Count
payload += old_epc_crc # Old EPC CRC (大端序) payload += old_epc_crc # Old EPC CRC (大端序)
payload += bytes([epc_len_indicator, 0x00]) # EPC Len Indicator, Status payload += bytes([epc_len_indicator, 0x00]) # EPC Len Indicator, Status
@@ -208,17 +236,24 @@ def cmd_write_epc(old_epc_crc: bytes, new_epc: bytes) -> bytes:
# 计算 checksum包含 Card Op Command + Internal Length + Payload # 计算 checksum包含 Card Op Command + Internal Length + Payload
card_op_cmd = bytes([0x00, 0x49]) card_op_cmd = bytes([0x00, 0x49])
internal_len = len(payload).to_bytes(2, 'big') internal_len = len(payload).to_bytes(2, "big")
checksum = 0 checksum = 0
for b in card_op_cmd + internal_len + payload: for b in card_op_cmd + internal_len + payload:
checksum = (checksum + b) & 0xFF checksum = (checksum + b) & 0xFF
return bytes([0xce, 0xbb]) + card_op_cmd + internal_len + payload + bytes([checksum, 0x7e]) return (
bytes([0xCE, 0xBB])
+ card_op_cmd
+ internal_len
+ payload
+ bytes([checksum, 0x7E])
)
# ==================== 设备管理类 ==================== # ==================== 设备管理类 ====================
class RFIDDevice: class RFIDDevice:
def __init__(self): def __init__(self):
self.device = None self.device = None
@@ -234,15 +269,17 @@ class RFIDDevice:
devices = [] devices = []
try: try:
for d in hid.enumerate(): for d in hid.enumerate():
devices.append({ devices.append(
'vendor_id': d['vendor_id'], {
'product_id': d['product_id'], "vendor_id": d["vendor_id"],
'manufacturer_string': d.get('manufacturer_string', ''), "product_id": d["product_id"],
'product_string': d.get('product_string', ''), "manufacturer_string": d.get("manufacturer_string", ""),
'serial_number': d.get('serial_number', ''), "product_string": d.get("product_string", ""),
'interface_number': d.get('interface_number', -1), "serial_number": d.get("serial_number", ""),
'path': d['path'] "interface_number": d.get("interface_number", -1),
}) "path": d["path"],
}
)
except Exception as e: except Exception as e:
print(f"枚举设备失败: {e}") print(f"枚举设备失败: {e}")
return devices return devices
@@ -259,8 +296,13 @@ class RFIDDevice:
self.device.open(self.vendor_id, self.product_id) self.device.open(self.vendor_id, self.product_id)
self.current_path = None self.current_path = None
self.current_interface = -1 self.current_interface = -1
print(f"[DEBUG] 通过VID/PID连接: VID=0x{self.vendor_id:04x}, PID=0x{self.product_id:04x}") print(
return True, f"已连接到设备 (VID: 0x{self.vendor_id:04x}, PID: 0x{self.product_id:04x}) - 注意:可能连接到任意接口" f"[DEBUG] 通过VID/PID连接: VID=0x{self.vendor_id:04x}, PID=0x{self.product_id:04x}"
)
return (
True,
f"已连接到设备 (VID: 0x{self.vendor_id:04x}, PID: 0x{self.product_id:04x}) - 注意:可能连接到任意接口",
)
except Exception as e: except Exception as e:
print(f"[DEBUG] VID/PID连接失败: {e}") print(f"[DEBUG] VID/PID连接失败: {e}")
return False, f"连接失败: {e}" return False, f"连接失败: {e}"
@@ -273,7 +315,7 @@ class RFIDDevice:
if isinstance(path, bytes): if isinstance(path, bytes):
path_str = path path_str = path
else: else:
path_str = path.encode('utf-8') if isinstance(path, str) else path path_str = path.encode("utf-8") if isinstance(path, str) else path
self.device.open_path(path_str) self.device.open_path(path_str)
self.current_path = path self.current_path = path
@@ -289,8 +331,15 @@ class RFIDDevice:
print(f"[DEBUG] 制造商: {info}") print(f"[DEBUG] 制造商: {info}")
print(f"[DEBUG] 产品: {product}") print(f"[DEBUG] 产品: {product}")
path_display = path.decode('utf-8', errors='ignore') if isinstance(path, bytes) else str(path) path_display = (
return True, f"已连接到设备 (IF={interface_number}, 路径: ...{path_display[-30:]})" path.decode("utf-8", errors="ignore")
if isinstance(path, bytes)
else str(path)
)
return (
True,
f"已连接到设备 (IF={interface_number}, 路径: ...{path_display[-30:]})",
)
except Exception as e: except Exception as e:
print(f"[DEBUG] 路径连接失败: {e}") print(f"[DEBUG] 路径连接失败: {e}")
return False, f"连接失败: {e}" return False, f"连接失败: {e}"
@@ -311,17 +360,17 @@ class RFIDDevice:
def is_connected(self): def is_connected(self):
"""检查是否已连接""" """检查是否已连接"""
if self.device: if self.device:
#print(f"[DEBUG] 设备已连接 - 接口: {self.current_interface}, 路径: {self.current_path}") # print(f"[DEBUG] 设备已连接 - 接口: {self.current_interface}, 路径: {self.current_path}")
return True return True
return False return False
def get_connection_info(self): def get_connection_info(self):
"""获取当前连接信息""" """获取当前连接信息"""
return { return {
'interface': self.current_interface, "interface": self.current_interface,
'path': self.current_path, "path": self.current_path,
'vendor_id': self.vendor_id, "vendor_id": self.vendor_id,
'product_id': self.product_id "product_id": self.product_id,
} }
def send_command(self, cmd_data: bytes): def send_command(self, cmd_data: bytes):
@@ -351,80 +400,111 @@ class RFIDDevice:
# ==================== GUI 界面 ==================== # ==================== GUI 界面 ====================
def create_window(): def create_window():
"""创建主窗口""" """创建主窗口"""
sg.theme('LightBlue2') sg.theme("LightBlue2")
# 设备连接区域 # 设备连接区域
device_frame = [ device_frame = [
[sg.Text('VID (十六进制):'), sg.Input('FFFF', size=(8, 1), key='-VID-'), [
sg.Text('PID (十六进制):'), sg.Input('0035', size=(8, 1), key='-PID-'), sg.Text("VID (十六进制):"),
sg.Button('连接', key='-CONNECT-'), sg.Input("FFFF", size=(8, 1), key="-VID-"),
sg.Button('断开', key='-DISCONNECT-'), sg.Text("PID (十六进制):"),
sg.Button('枚举设备', key='-ENUM-')], sg.Input("0035", size=(8, 1), key="-PID-"),
[sg.Text('状态: ', size=(12, 1)), sg.Text('连接', key='-STATUS-', text_color='red')] sg.Button("连接", key="-CONNECT-"),
sg.Button("断开", key="-DISCONNECT-"),
sg.Button("枚举设备", key="-ENUM-"),
],
[
sg.Text("状态: ", size=(12, 1)),
sg.Text("未连接", key="-STATUS-", text_color="red"),
],
] ]
# 基本命令区域 # 基本命令区域
basic_frame = [ basic_frame = [
[sg.Button('读取版本号', key='-VERSION-', size=(12, 1)), [
sg.Button('恢复出厂', key='-FACTORY_RESET-', size=(12, 1)), sg.Button("读取版本号", key="-VERSION-", size=(12, 1)),
sg.Button('读取格式', key='-READ_FORMAT-', size=(12, 1)), sg.Button("恢复出厂", key="-FACTORY_RESET-", size=(12, 1)),
sg.Button('设置格式', key='-SET_FORMAT-', size=(12, 1))], ],
[sg.Button('打开蜂鸣器', key='-BUZZER_ON-', size=(12, 1)), [
sg.Button('关闭蜂鸣器', key='-BUZZER_OFF-', size=(12, 1)), sg.Button("打开蜂鸣器", key="-BUZZER_ON-", size=(12, 1)),
sg.Button('打开射频', key='-RF_ON-', size=(12, 1)), sg.Button("关闭蜂鸣器", key="-BUZZER_OFF-", size=(12, 1)),
sg.Button('关闭射频', key='-RF_OFF-', size=(12, 1))], ],
[sg.Text('功率 (0-9):'), sg.Slider(range=(0, 9), default_value=8, orientation='h', size=(20, 15), key='-POWER-'), [
sg.Button('设置功率', key='-SET_POWER-')], sg.Text("功率 (0-9):"),
[sg.Text('工作模式:'), sg.Slider(
sg.Radio('单标签巡查', 'mode', key='-MODE1-', default=True), range=(0, 9),
sg.Radio('被动模式', 'mode', key='-MODE2-'), default_value=3,
sg.Radio('多标签巡查', 'mode', key='-MODE3-'), orientation="h",
sg.Button('设置模式', key='-SET_MODE-')] size=(20, 15),
key="-POWER-",
),
sg.Button("设置功率", key="-SET_POWER-"),
],
[
sg.Text("工作模式:"),
sg.Radio("单标签巡查", "mode", key="-MODE1-", default=True),
sg.Radio("被动模式", "mode", key="-MODE2-"),
sg.Radio("多标签巡查", "mode", key="-MODE3-"),
sg.Button("设置模式", key="-SET_MODE-"),
],
] ]
# EPC 操作区域 # EPC 操作区域
epc_frame = [ epc_frame = [
[sg.Button('读取 EPC', key='-READ_EPC-', size=(15, 1)), [
sg.Button('选中卡', key='-SELECT_CARD-', size=(15, 1))], sg.Button("读取 EPC", key="-READ_EPC-", size=(15, 1)),
[sg.Text('EPC 数据 (十六进制):'), sg.Input('', size=(40, 1), key='-EPC_DATA-')], sg.Text("", key="-READ_EPC_STATUS-", size=(40, 1), text_color="black"),
[sg.Text('新 EPC (十六进制):'), sg.Input('', size=(40, 1), key='-NEW_EPC-')], ], # 选中状态提示
[sg.Button('写入 EPC', key='-WRITE_EPC-', size=(15, 1))] [
sg.Text("原 EPC 数据(十六进制):"),
sg.Input("", size=(40, 1), key="-EPC_DATA-"),
sg.Button("选中卡", key="-SELECT_CARD-", size=(15, 1)),
],
[
sg.Text("新 EPC 数据(十六进制):"),
sg.Input("", size=(40, 1), key="-NEW_EPC-"),
],
[
sg.Button("写入 EPC", key="-WRITE_EPC-", size=(15, 1)),
sg.Text("", key="-WRITE_EPC_STATUS-", size=(40, 1), text_color="black"),
], # 写入状态提示
] ]
# 日志输出区域 # 日志输出区域
log_frame = [ log_frame = [
[sg.Multiline('', size=(80, 15), key='-LOG-', autoscroll=True, disabled=True)] [sg.Multiline("", size=(80, 15), key="-LOG-", autoscroll=True, disabled=True)]
] ]
layout = [ layout = [
[sg.Frame('设备连接', device_frame)], [sg.Frame("设备连接", device_frame)],
[sg.Frame('基本命令', basic_frame)], [sg.Frame("基本命令", basic_frame)],
[sg.Frame('EPC 操作', epc_frame)], [sg.Frame("EPC 操作", epc_frame)],
[sg.Frame('日志', log_frame)], [sg.Frame("日志", log_frame)],
[sg.Button('清空日志', key='-CLEAR_LOG-'), sg.Button('退出', key='-EXIT-')] [sg.Button("清空日志", key="-CLEAR_LOG-"), sg.Button("退出", key="-EXIT-")],
] ]
return sg.Window('MT6 RFID 读卡器测试程序', layout, finalize=True) return sg.Window("MT6 RFID 读卡器测试程序", layout, finalize=True)
def log_message(window, message, is_hex=False): def log_message(window, message, is_hex=False):
"""输出日志消息""" """输出日志消息"""
timestamp = time.strftime('%H:%M:%S') timestamp = time.strftime("%H:%M:%S")
if is_hex: if is_hex:
if isinstance(message, bytes): if isinstance(message, bytes):
hex_str = ' '.join(f'{b:02x}' for b in message) hex_str = " ".join(f"{b:02x}" for b in message)
window['-LOG-'].print(f'[{timestamp}] {hex_str}') window["-LOG-"].print(f"[{timestamp}] {hex_str}")
else: else:
window['-LOG-'].print(f'[{timestamp}] {message}') window["-LOG-"].print(f"[{timestamp}] {message}")
else: else:
window['-LOG-'].print(f'[{timestamp}] {message}') window["-LOG-"].print(f"[{timestamp}] {message}")
def hex_to_bytes(hex_str: str) -> bytes: def hex_to_bytes(hex_str: str) -> bytes:
"""十六进制字符串转字节""" """十六进制字符串转字节"""
hex_str = hex_str.replace(' ', '').replace(',', '') hex_str = hex_str.replace(" ", "").replace(",", "")
return bytes.fromhex(hex_str) return bytes.fromhex(hex_str)
@@ -437,23 +517,29 @@ def main():
while True: while True:
event, values = window.read() event, values = window.read()
if event in (sg.WIN_CLOSED, '-EXIT-'): if event in (sg.WIN_CLOSED, "-EXIT-"):
rfid.disconnect() rfid.disconnect()
break break
# 枚举设备 # 枚举设备
if event == '-ENUM-': if event == "-ENUM-":
device_list = rfid.list_devices() device_list = rfid.list_devices()
if device_list: if device_list:
log_message(window, f"找到 {len(device_list)} 个 HID 设备") log_message(window, f"找到 {len(device_list)} 个 HID 设备")
device_strs = [] device_strs = []
for i, d in enumerate(device_list): for i, d in enumerate(device_list):
# 显示路径信息帮助区分相同VID/PID的设备 # 显示路径信息帮助区分相同VID/PID的设备
path = d['path'] path = d["path"]
path_str = path.decode('utf-8', errors='ignore') if isinstance(path, bytes) else str(path) path_str = (
s = f"[{i}] VID:0x{d['vendor_id']:04x} PID:0x{d['product_id']:04x} " \ path.decode("utf-8", errors="ignore")
f"IF:{d['interface_number']} {d['manufacturer_string']} {d['product_string']} " \ if isinstance(path, bytes)
else str(path)
)
s = (
f"[{i}] VID:0x{d['vendor_id']:04x} PID:0x{d['product_id']:04x} "
f"IF:{d['interface_number']} {d['manufacturer_string']} {d['product_string']} "
f"Path:...{path_str[-20:]}" f"Path:...{path_str[-20:]}"
)
device_strs.append(s) device_strs.append(s)
log_message(window, s) log_message(window, s)
# 详细日志显示完整路径 # 详细日志显示完整路径
@@ -461,66 +547,81 @@ def main():
# 创建设备选择窗口 # 创建设备选择窗口
select_layout = [ select_layout = [
[sg.Listbox(values=device_strs, size=(80, min(10, len(device_strs))), key='-SELECTED-')], [
[sg.Button('使用选中设备'), sg.Button('取消')] sg.Listbox(
values=device_strs,
size=(80, min(10, len(device_strs))),
key="-SELECTED-",
)
],
[sg.Button("使用选中设备"), sg.Button("取消")],
] ]
select_window = sg.Window('选择设备', select_layout, modal=True) select_window = sg.Window("选择设备", select_layout, modal=True)
while True: while True:
sel_event, sel_values = select_window.read() sel_event, sel_values = select_window.read()
if sel_event in (sg.WIN_CLOSED, '取消'): if sel_event in (sg.WIN_CLOSED, "取消"):
select_window.close() select_window.close()
break break
if sel_event == '使用选中设备': if sel_event == "使用选中设备":
selected_idx = sel_values.get('-SELECTED-', []) selected_idx = sel_values.get("-SELECTED-", [])
if selected_idx: if selected_idx:
idx = device_strs.index(selected_idx[0]) idx = device_strs.index(selected_idx[0])
d = device_list[idx] d = device_list[idx]
window['-VID-'].update(f"{d['vendor_id']:04x}") window["-VID-"].update(f"{d['vendor_id']:04x}")
window['-PID-'].update(f"{d['product_id']:04x}") window["-PID-"].update(f"{d['product_id']:04x}")
# 使用路径连接设备 # 使用路径连接设备
path = d['path'] path = d["path"]
interface_number = d['interface_number'] interface_number = d["interface_number"]
log_message(window, f"正在连接设备...") log_message(window, f"正在连接设备...")
log_message(window, f" VID: 0x{d['vendor_id']:04x}, PID: 0x{d['product_id']:04x}") log_message(
window,
f" VID: 0x{d['vendor_id']:04x}, PID: 0x{d['product_id']:04x}",
)
log_message(window, f" 接口号: {interface_number}") log_message(window, f" 接口号: {interface_number}")
path_str = path.decode('utf-8', errors='ignore') if isinstance(path, bytes) else str(path) path_str = (
path.decode("utf-8", errors="ignore")
if isinstance(path, bytes)
else str(path)
)
log_message(window, f" 路径: {path_str}") log_message(window, f" 路径: {path_str}")
success, msg = rfid.connect_by_path(path, interface_number) success, msg = rfid.connect_by_path(path, interface_number)
log_message(window, msg) log_message(window, msg)
if success: if success:
window['-STATUS-'].update('已连接', text_color='green') window["-STATUS-"].update("已连接", text_color="green")
log_message(window, f"当前使用接口: IF{rfid.current_interface}") log_message(
window, f"当前使用接口: IF{rfid.current_interface}"
)
else: else:
window['-STATUS-'].update('连接失败', text_color='red') window["-STATUS-"].update("连接失败", text_color="red")
select_window.close() select_window.close()
break break
else: else:
log_message(window, "未找到 HID 设备") log_message(window, "未找到 HID 设备")
# 连接设备 # 连接设备
if event == '-CONNECT-': if event == "-CONNECT-":
try: try:
vid = int(values['-VID-'], 16) vid = int(values["-VID-"], 16)
pid = int(values['-PID-'], 16) pid = int(values["-PID-"], 16)
success, msg = rfid.connect(vid, pid) success, msg = rfid.connect(vid, pid)
log_message(window, msg) log_message(window, msg)
if success: if success:
window['-STATUS-'].update('已连接', text_color='green') window["-STATUS-"].update("已连接", text_color="green")
else: else:
window['-STATUS-'].update('连接失败', text_color='red') window["-STATUS-"].update("连接失败", text_color="red")
except ValueError: except ValueError:
log_message(window, "请输入有效的十六进制 VID/PID") log_message(window, "请输入有效的十六进制 VID/PID")
# 断开连接 # 断开连接
if event == '-DISCONNECT-': if event == "-DISCONNECT-":
msg = rfid.disconnect() msg = rfid.disconnect()
log_message(window, msg) log_message(window, msg)
window['-STATUS-'].update('未连接', text_color='red') window["-STATUS-"].update("未连接", text_color="red")
# 读取版本号 # 读取版本号
if event == '-VERSION-': if event == "-VERSION-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -531,13 +632,13 @@ def main():
else: else:
log_message(window, data, is_hex=True) log_message(window, data, is_hex=True)
try: try:
version = data.decode('ascii', errors='ignore') version = data.decode("ascii", errors="ignore")
log_message(window, f"版本: {version}") log_message(window, f"版本: {version}")
except: except:
pass pass
# 恢复出厂设置 # 恢复出厂设置
if event == '-FACTORY_RESET-': if event == "-FACTORY_RESET-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -549,7 +650,7 @@ def main():
log_message(window, data, is_hex=True) log_message(window, data, is_hex=True)
# 读取格式 # 读取格式
if event == '-READ_FORMAT-': if event == "-READ_FORMAT-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -561,7 +662,7 @@ def main():
log_message(window, data, is_hex=True) log_message(window, data, is_hex=True)
# 设置格式 # 设置格式
if event == '-SET_FORMAT-': if event == "-SET_FORMAT-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -573,7 +674,7 @@ def main():
log_message(window, data, is_hex=True) log_message(window, data, is_hex=True)
# 射频电源控制 # 射频电源控制
if event == '-RF_ON-': if event == "-RF_ON-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -584,7 +685,7 @@ def main():
else: else:
log_message(window, data, is_hex=True) log_message(window, data, is_hex=True)
if event == '-RF_OFF-': if event == "-RF_OFF-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -596,7 +697,7 @@ def main():
log_message(window, data, is_hex=True) log_message(window, data, is_hex=True)
# 蜂鸣器控制 # 蜂鸣器控制
if event == '-BUZZER_ON-': if event == "-BUZZER_ON-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -607,7 +708,7 @@ def main():
else: else:
log_message(window, f"响应: {data.hex() if data else '无数据'}") log_message(window, f"响应: {data.hex() if data else '无数据'}")
if event == '-BUZZER_OFF-': if event == "-BUZZER_OFF-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
@@ -619,11 +720,11 @@ def main():
log_message(window, f"响应: {data.hex() if data else '无数据'}") log_message(window, f"响应: {data.hex() if data else '无数据'}")
# 设置功率 # 设置功率
if event == '-SET_POWER-': if event == "-SET_POWER-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
power = int(values['-POWER-']) power = int(values["-POWER-"])
log_message(window, f"发送命令: 设置功率 = {power}") log_message(window, f"发送命令: 设置功率 = {power}")
data, err = rfid.send_command(cmd_set_power(power)) data, err = rfid.send_command(cmd_set_power(power))
if err: if err:
@@ -632,17 +733,19 @@ def main():
log_message(window, f"响应: {data.hex() if data else '无数据'}") log_message(window, f"响应: {data.hex() if data else '无数据'}")
# 设置工作模式 # 设置工作模式
if event == '-SET_MODE-': if event == "-SET_MODE-":
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
mode = 1 mode = 1
if values['-MODE2-']: if values["-MODE2-"]:
mode = 2 mode = 2
elif values['-MODE3-']: elif values["-MODE3-"]:
mode = 3 mode = 3
mode_names = {1: '单标签巡查', 2: '被动模式', 3: '多标签巡查'} mode_names = {1: "单标签巡查", 2: "被动模式", 3: "多标签巡查"}
log_message(window, f"发送命令: 设置模式 = {mode_names.get(mode, mode)}") log_message(
window, f"发送命令: 设置模式 = {mode_names.get(mode, mode)}"
)
data, err = rfid.send_command(cmd_set_mode(mode)) data, err = rfid.send_command(cmd_set_mode(mode))
if err: if err:
log_message(window, f"错误: {err}") log_message(window, f"错误: {err}")
@@ -650,14 +753,18 @@ def main():
log_message(window, f"响应: {data.hex() if data else '无数据'}") log_message(window, f"响应: {data.hex() if data else '无数据'}")
# 读取 EPC # 读取 EPC
if event == '-READ_EPC-': if event == "-READ_EPC-":
# 清空状态提示
window["-READ_EPC_STATUS-"].update("", text_color="black")
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
window["-READ_EPC_STATUS-"].update("✗ 设备未连接", text_color="red")
else: else:
log_message(window, "发送命令: 读取 EPC") log_message(window, "发送命令: 读取 EPC")
data, err = rfid.send_command(cmd_read_epc()) data, err = rfid.send_command(cmd_read_epc())
if err: if err:
log_message(window, f"错误: {err}") log_message(window, f"错误: {err}")
window["-READ_EPC_STATUS-"].update(f"{err}", text_color="red")
else: else:
log_message(window, f"响应 (hex): ", is_hex=True) log_message(window, f"响应 (hex): ", is_hex=True)
log_message(window, data, is_hex=True) log_message(window, data, is_hex=True)
@@ -666,33 +773,57 @@ def main():
# 原始格式: 00 bb 02 22 00 0b d3 18 00 [EPC] ... # 原始格式: 00 bb 02 22 00 0b d3 18 00 [EPC] ...
# 去掉status后: bb 02 22 00 0b d3 18 00 [EPC] ... # 去掉status后: bb 02 22 00 0b d3 18 00 [EPC] ...
# 所以: data[0]=bb, data[1:3]=Card Op Resp, data[5]=RSSI, data[6]=EPC Len, data[8:]=EPC # 所以: data[0]=bb, data[1:3]=Card Op Resp, data[5]=RSSI, data[6]=EPC Len, data[8:]=EPC
if data and len(data) > 10: if data and len(data) >= 8:
# 检查 Magic 和 Card Op Resp # 检查 Magic 和 Card Op Resp
if data[0:1] == b'\xbb' and data[1:3] == b'\x02\x22': if (
data[0:1] == b"\xbb"
and data[1:3] == b"\x02\x22"
and len(data) >= 12
):
# 有卡,解析 EPC # 有卡,解析 EPC
rssi = data[5] if len(data) > 5 else 0 rssi = data[5] if len(data) > 5 else 0
epc_len_indicator = data[6] if len(data) > 6 else 0 epc_len_indicator = data[6] if len(data) > 6 else 0
epc_len = epc_len_indicator // 4 if epc_len_indicator > 0 else 0 epc_len = (
epc_data = data[8:8+epc_len] if len(data) > 8 else b'' epc_len_indicator // 4 if epc_len_indicator > 0 else 0
)
epc_data = data[8 : 8 + epc_len] if len(data) > 8 else b""
log_message(window, f"Magic: 0x{data[0]:02x} (应为 bb)") log_message(window, f"Magic: 0x{data[0]:02x} (应为 bb)")
log_message(window, f"Card Op Resp: {data[1:3].hex()} (02 22=有卡)") log_message(
window, f"Card Op Resp: {data[1:3].hex()} (02 22=有卡)"
)
log_message(window, f"RSSI: 0x{rssi:02x}") log_message(window, f"RSSI: 0x{rssi:02x}")
log_message(window, f"EPC Len Indicator: 0x{epc_len_indicator:02x} (字节数={epc_len})") log_message(
window,
f"EPC Len Indicator: 0x{epc_len_indicator:02x} (字节数={epc_len})",
)
log_message(window, f"EPC: {epc_data.hex()}") log_message(window, f"EPC: {epc_data.hex()}")
window['-EPC_DATA-'].update(epc_data.hex()) window["-EPC_DATA-"].update(epc_data.hex())
elif data[0:1] == b'\xbb' and data[1:3] == b'\x01\xff': # 成功状态提示
window["-READ_EPC_STATUS-"].update(
f"✓ 读取成功: {epc_data.hex()}", text_color="green"
)
elif data[0:1] == b"\xbb" and data[1:3] == b"\x01\xff":
log_message(window, "无卡或读取失败 (Card Op Resp: 01 ff)") log_message(window, "无卡或读取失败 (Card Op Resp: 01 ff)")
# 无卡状态提示
window["-READ_EPC_STATUS-"].update(
"✗ 读取失败: 无卡", text_color="red"
)
else: else:
log_message(window, f"未知响应格式:") log_message(window, f"未知响应格式:")
log_message(window, f" Magic: {data[0:1].hex()} (应为 bb)") log_message(window, f" Magic: {data[0:1].hex()} (应为 bb)")
log_message(window, f" Card Op Resp: {data[1:3].hex()}") log_message(window, f" Card Op Resp: {data[1:3].hex()}")
# 未知响应状态提示
window["-READ_EPC_STATUS-"].update(
"✗ 未知响应格式", text_color="red"
)
# 选中卡 # 选中卡
if event == '-SELECT_CARD-': if event == "-SELECT_CARD-":
# 清空状态提示
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
else: else:
epc_hex = values['-EPC_DATA-'].strip() epc_hex = values["-EPC_DATA-"].strip()
if not epc_hex: if not epc_hex:
log_message(window, "错误: 请输入 EPC 数据") log_message(window, "错误: 请输入 EPC 数据")
else: else:
@@ -703,7 +834,10 @@ def main():
if err: if err:
log_message(window, f"错误: {err}") log_message(window, f"错误: {err}")
else: else:
log_message(window, f"响应 (hex): {data.hex() if data else '无数据'}") log_message(
window,
f"响应 (hex): {data.hex() if data else '无数据'}",
)
# 注意: parse_getres_frame返回的data是从bb开始的status已被去除 # 注意: parse_getres_frame返回的data是从bb开始的status已被去除
# 原始格式: 00 bb 01 0c 00 01 00 0e 7e # 原始格式: 00 bb 01 0c 00 01 00 0e 7e
# 去掉status后: bb 01 0c 00 01 00 0e 7e # 去掉status后: bb 01 0c 00 01 00 0e 7e
@@ -711,39 +845,90 @@ def main():
if data and len(data) > 5: if data and len(data) > 5:
if data[5] == 0x00: if data[5] == 0x00:
log_message(window, "选中成功") log_message(window, "选中成功")
window["-READ_EPC_STATUS-"].update(
f"✓ 选中当前卡", text_color="green"
)
else: else:
log_message(window, f"选中失败: 0x{data[5]:02x}") log_message(window, f"选中失败: 0x{data[5]:02x}")
except ValueError: except ValueError:
log_message(window, "错误: EPC 数据格式错误,请使用十六进制") log_message(window, "错误: EPC 数据格式错误,请使用十六进制")
# 写入 EPC # 写入 EPC
if event == '-WRITE_EPC-': if event == "-WRITE_EPC-":
# 清空状态提示
window["-WRITE_EPC_STATUS-"].update("", text_color="black")
if not rfid.is_connected(): if not rfid.is_connected():
log_message(window, "错误: 设备未连接") log_message(window, "错误: 设备未连接")
window["-WRITE_EPC_STATUS-"].update("✗ 设备未连接", text_color="red")
else: else:
new_epc_hex = values['-NEW_EPC-'].strip() new_epc_hex = values["-NEW_EPC-"].strip()
old_epc_hex = values["-EPC_DATA-"].strip()
if not new_epc_hex: if not new_epc_hex:
log_message(window, "错误: 请输入新的 EPC 数据") log_message(window, "错误: 请输入新的 EPC 数据")
window["-WRITE_EPC_STATUS-"].update(
"✗ 请输入新EPC", text_color="red"
)
elif not old_epc_hex:
log_message(window, "错误: 请先读取 EPC 数据")
window["-WRITE_EPC_STATUS-"].update("✗ 请先读卡", text_color="red")
else: else:
try: try:
new_epc = hex_to_bytes(new_epc_hex) new_epc = hex_to_bytes(new_epc_hex)
# 这里需要一个旧的 EPC CRC暂时使用示例值 old_epc = hex_to_bytes(old_epc_hex)
old_epc_crc = bytes([0xca, 0x9e]) # 需要根据实际 EPC 计算 # 需要根据实际 EPC 计算
old_epc_crc = calc_epc_crc(
len(old_epc) * 4, 0, old_epc
).to_bytes(2, "big")
log_message(window, f"发送命令: 写入 EPC={new_epc.hex()}") log_message(window, f"发送命令: 写入 EPC={new_epc.hex()}")
data, err = rfid.send_command(cmd_write_epc(old_epc_crc, new_epc)) data, err = rfid.send_command(
cmd_write_epc(old_epc_crc, new_epc)
)
if err: if err:
log_message(window, f"错误: {err}") log_message(window, f"错误: {err}")
window["-WRITE_EPC_STATUS-"].update(
f"{err}", text_color="red"
)
else: else:
log_message(window, f"响应 (hex): {data.hex() if data else '无数据'}") log_message(
window,
f"响应 (hex): {data.hex() if data else '无数据'}",
)
# 验证写入是否成功
if data and len(data) >= 3:
# 检查 Magic 和 Card Op Resp
if data[0] == 0xBB and data[1:3] == b"\x01\x49":
# 成功状态提示
window["-WRITE_EPC_STATUS-"].update(
f"✓ 写入成功: {new_epc.hex()}",
text_color="green",
)
else:
# 写入失败
log_message(
window,
f"写入失败: Card Op Resp = {data[1:3].hex()}",
)
window["-WRITE_EPC_STATUS-"].update(
"✗ 写入失败", text_color="red"
)
else:
# 无效响应
log_message(window, f"无效响应: 数据长度不足")
window["-WRITE_EPC_STATUS-"].update(
"✗ 无效响应", text_color="red"
)
except ValueError: except ValueError:
log_message(window, "错误: EPC 数据格式错误,请使用十六进制") log_message(window, "错误: EPC 数据格式错误,请使用十六进制")
window["-WRITE_EPC_STATUS-"].update(
"✗ EPC格式错误", text_color="red"
)
# 清空日志 # 清空日志
if event == '-CLEAR_LOG-': if event == "-CLEAR_LOG-":
window['-LOG-'].update('') window["-LOG-"].update("")
window.close() window.close()
if __name__ == '__main__': if __name__ == "__main__":
main() main()