nfcscript 0.0.6__tar.gz → 0.0.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. nfcscript-0.0.8/PKG-INFO +264 -0
  2. nfcscript-0.0.8/README.md +254 -0
  3. {nfcscript-0.0.6 → nfcscript-0.0.8}/pyproject.toml +7 -2
  4. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfc/__init__.py +1 -0
  5. nfcscript-0.0.8/src/nfc/cli.py +116 -0
  6. nfcscript-0.0.8/src/nfc/nfc_cli.py +202 -0
  7. nfcscript-0.0.8/src/nfc/reader.py +280 -0
  8. nfcscript-0.0.8/src/nfc/trace.py +73 -0
  9. nfcscript-0.0.8/src/nfcscript.egg-info/PKG-INFO +264 -0
  10. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfcscript.egg-info/SOURCES.txt +2 -0
  11. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfcscript.egg-info/entry_points.txt +1 -0
  12. nfcscript-0.0.8/src/nfcscript.egg-info/requires.txt +2 -0
  13. nfcscript-0.0.6/PKG-INFO +0 -12
  14. nfcscript-0.0.6/README.md +0 -3
  15. nfcscript-0.0.6/src/nfc/cli.py +0 -27
  16. nfcscript-0.0.6/src/nfc/reader.py +0 -197
  17. nfcscript-0.0.6/src/nfcscript.egg-info/PKG-INFO +0 -12
  18. nfcscript-0.0.6/src/nfcscript.egg-info/requires.txt +0 -1
  19. {nfcscript-0.0.6 → nfcscript-0.0.8}/setup.cfg +0 -0
  20. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfc/assertions.py +0 -0
  21. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfc/bits.py +0 -0
  22. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfc/checksum.py +0 -0
  23. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfc/delay.py +0 -0
  24. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfc/hex_util.py +0 -0
  25. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfcscript.egg-info/dependency_links.txt +0 -0
  26. {nfcscript-0.0.6 → nfcscript-0.0.8}/src/nfcscript.egg-info/top_level.txt +0 -0
  27. {nfcscript-0.0.6 → nfcscript-0.0.8}/tests/test_main.py +0 -0
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: nfcscript
3
+ Version: 0.0.8
4
+ Summary: A plugin-based core engine for DSL-style NFC testing automation.
5
+ Author: crthu
6
+ Requires-Python: >=3.14
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: nfctester
9
+ Requires-Dist: python-dotenv
10
+
11
+ # nfcscript
12
+
13
+ NFC 测试脚本 DSL 执行环境,基于 `nfctester` 插件化架构。
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ uv sync
19
+ ```
20
+
21
+ ## 使用
22
+
23
+ ### nfcscript - 脚本执行
24
+
25
+ ```bash
26
+ nfcscript test_card.py
27
+ ```
28
+
29
+ #### 常用参数
30
+
31
+ ```bash
32
+ # 指定串口
33
+ nfcscript test_card.py -p COM21
34
+
35
+ # 指定读卡器类型
36
+ nfcscript test_card.py -r acr122u
37
+
38
+ # 开启追踪层
39
+ nfcscript test_card.py --trace-driver --trace-protocol
40
+
41
+ # 设置日志级别
42
+ nfcscript test_card.py --trace-level DEBUG
43
+
44
+ # 组合使用
45
+ nfcscript test_card.py -p COM21 -r pn532 --trace-driver --trace-protocol --trace-level DEBUG
46
+ ```
47
+
48
+ ### nfc-cli - 交互式命令行
49
+
50
+ ```bash
51
+ # 列出所有可用卡片类型
52
+ nfc-cli --list-cards
53
+
54
+ # 连接卡片并进入交互模式
55
+ nfc-cli -c ntag224 -p COM21
56
+
57
+ # 指定读卡器类型
58
+ nfc-cli -c mifare_classic -p COM21 -r pn532
59
+
60
+ # 通过环境变量自动发现外部卡片
61
+ export NFC_CARD_PATH="../scripts/lib"
62
+ nfc-cli -c sm7 -p COM20
63
+
64
+ # 或手动指定
65
+ nfc-cli -c sm7 -p COM20 -i ../scripts/lib
66
+ ```
67
+
68
+ #### 交互命令
69
+
70
+ 进入交互模式后,可以输入命令操作卡片:
71
+
72
+ ```
73
+ > help # 显示可用命令
74
+ > auth 7 FFFFFFFFFFFF # 认证块 7
75
+ > read 10 # 读取页 10
76
+ > write 20 AABBCCDD... # 写入页 20
77
+ > quit # 退出
78
+ ```
79
+
80
+ 支持的值格式:
81
+ - 十六进制: `0xFF`, `0xAABBCCDD`
82
+ - 十进制: `7`, `255`
83
+ - 字节串: `b'\xff\x00'`
84
+
85
+ #### 导入外部卡片
86
+
87
+ 使用 `-i` 参数导入外部卡片模块,支持单个 `.py` 文件或包含 `__init__.py` 的目录:
88
+
89
+ ```bash
90
+ # 导入单个文件
91
+ nfc-cli -c my_card -p COM20 -i /path/to/my_card.py
92
+
93
+ # 导入目录(需要有 __init__.py)
94
+ nfc-cli -c sm7 -p COM20 -i /path/to/lib
95
+ ```
96
+
97
+ 外部卡片模块需要使用 `@CardRegistry.register("name")` 装饰器注册。
98
+
99
+ #### 示例
100
+
101
+ ```bash
102
+ # NTAG224 认证并读取
103
+ nfc-cli -c ntag224 -p COM21
104
+ > auth 1 AABBCCDD112233445566778899001122
105
+ > read 4
106
+
107
+ # Mifare Classic 操作
108
+ nfc-cli -c mifare_classic -p COM21
109
+ > authenticate 7 FFFFFFFFFFFF
110
+ > read_block 8
111
+ > write_block 9 AABBCCDD112233445566778899001122
112
+ ```
113
+
114
+ ### 脚本示例
115
+
116
+ ```python
117
+ from nfc import *
118
+
119
+ # 连接读卡器 (默认使用环境变量 NFC_PORT 或 COM20)
120
+ connect()
121
+
122
+ # 开启追踪
123
+ trace.set_layer("DRIVER", True)
124
+ trace.set_layer("PROTOCOL", True)
125
+
126
+ # 寻卡
127
+ card_info = active()
128
+ ASSERT_IS_NOT_NONE(card_info, "未扫描到卡片")
129
+ print(f"UID: {card_info['uid'].hex().upper()}")
130
+
131
+ # 创建卡片实例
132
+ from nfctester.registry import CardRegistry
133
+ card = CardRegistry.create("mifare", reader=get_reader())
134
+
135
+ # 关闭连接
136
+ close()
137
+ ```
138
+
139
+ ### 上下文管理器
140
+
141
+ ```python
142
+ from nfc import *
143
+
144
+ with session() as reader:
145
+ card_info = reader.find()
146
+ print(card_info)
147
+ # 自动断开连接
148
+ ```
149
+
150
+ ## 模块说明
151
+
152
+ | 模块 | 说明 |
153
+ |------|------|
154
+ | `reader.py` | 读卡器连接与通信 |
155
+ | `trace.py` | 日志追踪控制 |
156
+ | `assertions.py` | 测试断言 |
157
+ | `bits.py` | 位操作工具 |
158
+ | `hex_util.py` | 十六进制工具 |
159
+ | `checksum.py` | 校验工具 |
160
+ | `delay.py` | 延时工具 |
161
+
162
+ ## API 参考
163
+
164
+ ### reader.py
165
+
166
+ | 函数 | 说明 |
167
+ |------|------|
168
+ | `connect(port, reader_type)` | 连接读卡器 |
169
+ | `get_reader()` | 获取当前读卡器实例 |
170
+ | `active(ll, ignore_error)` | 寻卡,返回 uid/atq/sak |
171
+ | `transceive(data, tx_crc, rx_crc)` | 底层帧交互 |
172
+ | `transceive_bits(data, last_tx_bits, tx_crc, rx_crc)` | 位控制帧交互 |
173
+ | `reqa()` | ISO14443-A REQA |
174
+ | `wupa()` | ISO14443-A WUPA |
175
+ | `halt()` | ISO14443-A HALT |
176
+ | `select(cl_level, uid)` | ISO14443-A SELECT |
177
+ | `anticoll(cl_level, nvb, uid_prefix)` | ISO14443-A ANTICOLL |
178
+ | `field_on()` / `field_off()` | RF 场控制 |
179
+ | `session(port, reader_type)` | 上下文管理器 |
180
+ | `close()` | 断开连接 |
181
+
182
+ ### trace.py
183
+
184
+ | 函数 | 说明 |
185
+ |------|------|
186
+ | `trace.set_layer(layer, enable)` | 开启/关闭追踪层 |
187
+ | `trace.set_level(level)` | 设置日志级别 |
188
+ | `trace.info(msg)` / `trace.error(msg)` / `trace.warning(msg)` / `trace.success(msg)` / `trace.debug(msg)` | 输出日志 |
189
+
190
+ ### assertions.py
191
+
192
+ | 函数 | 说明 |
193
+ |------|------|
194
+ | `ASSERT_EQUAL(expected, actual, msg)` | 相等断言 |
195
+ | `ASSERT_LEN(data, expected_len, msg)` | 长度断言 |
196
+ | `ASSERT_IS_NOT_NONE(value, msg)` | 非空断言 |
197
+ | `ASSERT_IS_NONE(value, msg)` | 空值断言 |
198
+
199
+ ### bits.py
200
+
201
+ | 函数 | 说明 |
202
+ |------|------|
203
+ | `BITS_UPDATE(val, mask, data)` | Read-Modify-Write |
204
+ | `BITS_SET(val, mask)` | 置位 |
205
+ | `BITS_RESET(val, mask)` | 清位 |
206
+
207
+ ### hex_util.py
208
+
209
+ | 函数 | 说明 |
210
+ |------|------|
211
+ | `PARSE_HEX(text)` | 解析 hex 字符串 |
212
+ | `FORMAT_HEX(data, last_bits)` | 格式化为可视化 hex |
213
+
214
+ ### checksum.py
215
+
216
+ | 函数 | 说明 |
217
+ |------|------|
218
+ | `GET_BCC(data)` | 计算异或校验 |
219
+
220
+ ### delay.py
221
+
222
+ | 函数 | 说明 |
223
+ |------|------|
224
+ | `DELAY_MS(ms)` | 毫秒延时 |
225
+
226
+ ## 环境变量
227
+
228
+ 优先级: CLI 参数 > 内层 `.env` > 外层 `.env` > 系统环境变量 (必须配置,否则报错)
229
+
230
+ | 变量 | 说明 | 默认值 |
231
+ |------|------|--------|
232
+ | `NFC_PORT` | 串口号 | - |
233
+ | `NFC_READER` | 读卡器类型 | - |
234
+ | `NFC_TRACE_LEVEL` | 日志级别 | `INFO` |
235
+ | `NFC_TRACE_DRIVER` | 开启 DRIVER 层追踪 | `false` |
236
+ | `NFC_TRACE_PROTOCOL` | 开启 PROTOCOL 层追踪 | `false` |
237
+ | `NFC_CARD_PATH` | 外部卡片模块搜索路径 (分号分隔) | - |
238
+
239
+ 支持多层 `.env` 加载:从脚本目录向上搜索所有 `.env` 文件,按从外到内的顺序依次加载(内层覆盖外层同名变量)。
240
+
241
+ 例如,脚本位于 `/project/tests/run.py`,沿途有 `/project/.env` 和 `/project/tests/.env`,加载顺序为:
242
+
243
+ 1. `/project/.env` (基础配置)
244
+ 2. `/project/tests/.env` (覆盖基础配置中的同名变量)
245
+
246
+ 在项目根目录或脚本所在目录创建 `.env` 文件进行持久化配置:
247
+
248
+ ```
249
+ NFC_PORT=COM20
250
+ NFC_READER=pn532
251
+ NFC_TRACE_LEVEL=DEBUG
252
+ NFC_TRACE_DRIVER=true
253
+ NFC_TRACE_PROTOCOL=true
254
+ ```
255
+
256
+ ## 开发
257
+
258
+ ```bash
259
+ # 运行测试
260
+ uv run pytest
261
+
262
+ # 运行特定测试
263
+ uv run pytest tests/test_xxx.py
264
+ ```
@@ -0,0 +1,254 @@
1
+ # nfcscript
2
+
3
+ NFC 测试脚本 DSL 执行环境,基于 `nfctester` 插件化架构。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ uv sync
9
+ ```
10
+
11
+ ## 使用
12
+
13
+ ### nfcscript - 脚本执行
14
+
15
+ ```bash
16
+ nfcscript test_card.py
17
+ ```
18
+
19
+ #### 常用参数
20
+
21
+ ```bash
22
+ # 指定串口
23
+ nfcscript test_card.py -p COM21
24
+
25
+ # 指定读卡器类型
26
+ nfcscript test_card.py -r acr122u
27
+
28
+ # 开启追踪层
29
+ nfcscript test_card.py --trace-driver --trace-protocol
30
+
31
+ # 设置日志级别
32
+ nfcscript test_card.py --trace-level DEBUG
33
+
34
+ # 组合使用
35
+ nfcscript test_card.py -p COM21 -r pn532 --trace-driver --trace-protocol --trace-level DEBUG
36
+ ```
37
+
38
+ ### nfc-cli - 交互式命令行
39
+
40
+ ```bash
41
+ # 列出所有可用卡片类型
42
+ nfc-cli --list-cards
43
+
44
+ # 连接卡片并进入交互模式
45
+ nfc-cli -c ntag224 -p COM21
46
+
47
+ # 指定读卡器类型
48
+ nfc-cli -c mifare_classic -p COM21 -r pn532
49
+
50
+ # 通过环境变量自动发现外部卡片
51
+ export NFC_CARD_PATH="../scripts/lib"
52
+ nfc-cli -c sm7 -p COM20
53
+
54
+ # 或手动指定
55
+ nfc-cli -c sm7 -p COM20 -i ../scripts/lib
56
+ ```
57
+
58
+ #### 交互命令
59
+
60
+ 进入交互模式后,可以输入命令操作卡片:
61
+
62
+ ```
63
+ > help # 显示可用命令
64
+ > auth 7 FFFFFFFFFFFF # 认证块 7
65
+ > read 10 # 读取页 10
66
+ > write 20 AABBCCDD... # 写入页 20
67
+ > quit # 退出
68
+ ```
69
+
70
+ 支持的值格式:
71
+ - 十六进制: `0xFF`, `0xAABBCCDD`
72
+ - 十进制: `7`, `255`
73
+ - 字节串: `b'\xff\x00'`
74
+
75
+ #### 导入外部卡片
76
+
77
+ 使用 `-i` 参数导入外部卡片模块,支持单个 `.py` 文件或包含 `__init__.py` 的目录:
78
+
79
+ ```bash
80
+ # 导入单个文件
81
+ nfc-cli -c my_card -p COM20 -i /path/to/my_card.py
82
+
83
+ # 导入目录(需要有 __init__.py)
84
+ nfc-cli -c sm7 -p COM20 -i /path/to/lib
85
+ ```
86
+
87
+ 外部卡片模块需要使用 `@CardRegistry.register("name")` 装饰器注册。
88
+
89
+ #### 示例
90
+
91
+ ```bash
92
+ # NTAG224 认证并读取
93
+ nfc-cli -c ntag224 -p COM21
94
+ > auth 1 AABBCCDD112233445566778899001122
95
+ > read 4
96
+
97
+ # Mifare Classic 操作
98
+ nfc-cli -c mifare_classic -p COM21
99
+ > authenticate 7 FFFFFFFFFFFF
100
+ > read_block 8
101
+ > write_block 9 AABBCCDD112233445566778899001122
102
+ ```
103
+
104
+ ### 脚本示例
105
+
106
+ ```python
107
+ from nfc import *
108
+
109
+ # 连接读卡器 (默认使用环境变量 NFC_PORT 或 COM20)
110
+ connect()
111
+
112
+ # 开启追踪
113
+ trace.set_layer("DRIVER", True)
114
+ trace.set_layer("PROTOCOL", True)
115
+
116
+ # 寻卡
117
+ card_info = active()
118
+ ASSERT_IS_NOT_NONE(card_info, "未扫描到卡片")
119
+ print(f"UID: {card_info['uid'].hex().upper()}")
120
+
121
+ # 创建卡片实例
122
+ from nfctester.registry import CardRegistry
123
+ card = CardRegistry.create("mifare", reader=get_reader())
124
+
125
+ # 关闭连接
126
+ close()
127
+ ```
128
+
129
+ ### 上下文管理器
130
+
131
+ ```python
132
+ from nfc import *
133
+
134
+ with session() as reader:
135
+ card_info = reader.find()
136
+ print(card_info)
137
+ # 自动断开连接
138
+ ```
139
+
140
+ ## 模块说明
141
+
142
+ | 模块 | 说明 |
143
+ |------|------|
144
+ | `reader.py` | 读卡器连接与通信 |
145
+ | `trace.py` | 日志追踪控制 |
146
+ | `assertions.py` | 测试断言 |
147
+ | `bits.py` | 位操作工具 |
148
+ | `hex_util.py` | 十六进制工具 |
149
+ | `checksum.py` | 校验工具 |
150
+ | `delay.py` | 延时工具 |
151
+
152
+ ## API 参考
153
+
154
+ ### reader.py
155
+
156
+ | 函数 | 说明 |
157
+ |------|------|
158
+ | `connect(port, reader_type)` | 连接读卡器 |
159
+ | `get_reader()` | 获取当前读卡器实例 |
160
+ | `active(ll, ignore_error)` | 寻卡,返回 uid/atq/sak |
161
+ | `transceive(data, tx_crc, rx_crc)` | 底层帧交互 |
162
+ | `transceive_bits(data, last_tx_bits, tx_crc, rx_crc)` | 位控制帧交互 |
163
+ | `reqa()` | ISO14443-A REQA |
164
+ | `wupa()` | ISO14443-A WUPA |
165
+ | `halt()` | ISO14443-A HALT |
166
+ | `select(cl_level, uid)` | ISO14443-A SELECT |
167
+ | `anticoll(cl_level, nvb, uid_prefix)` | ISO14443-A ANTICOLL |
168
+ | `field_on()` / `field_off()` | RF 场控制 |
169
+ | `session(port, reader_type)` | 上下文管理器 |
170
+ | `close()` | 断开连接 |
171
+
172
+ ### trace.py
173
+
174
+ | 函数 | 说明 |
175
+ |------|------|
176
+ | `trace.set_layer(layer, enable)` | 开启/关闭追踪层 |
177
+ | `trace.set_level(level)` | 设置日志级别 |
178
+ | `trace.info(msg)` / `trace.error(msg)` / `trace.warning(msg)` / `trace.success(msg)` / `trace.debug(msg)` | 输出日志 |
179
+
180
+ ### assertions.py
181
+
182
+ | 函数 | 说明 |
183
+ |------|------|
184
+ | `ASSERT_EQUAL(expected, actual, msg)` | 相等断言 |
185
+ | `ASSERT_LEN(data, expected_len, msg)` | 长度断言 |
186
+ | `ASSERT_IS_NOT_NONE(value, msg)` | 非空断言 |
187
+ | `ASSERT_IS_NONE(value, msg)` | 空值断言 |
188
+
189
+ ### bits.py
190
+
191
+ | 函数 | 说明 |
192
+ |------|------|
193
+ | `BITS_UPDATE(val, mask, data)` | Read-Modify-Write |
194
+ | `BITS_SET(val, mask)` | 置位 |
195
+ | `BITS_RESET(val, mask)` | 清位 |
196
+
197
+ ### hex_util.py
198
+
199
+ | 函数 | 说明 |
200
+ |------|------|
201
+ | `PARSE_HEX(text)` | 解析 hex 字符串 |
202
+ | `FORMAT_HEX(data, last_bits)` | 格式化为可视化 hex |
203
+
204
+ ### checksum.py
205
+
206
+ | 函数 | 说明 |
207
+ |------|------|
208
+ | `GET_BCC(data)` | 计算异或校验 |
209
+
210
+ ### delay.py
211
+
212
+ | 函数 | 说明 |
213
+ |------|------|
214
+ | `DELAY_MS(ms)` | 毫秒延时 |
215
+
216
+ ## 环境变量
217
+
218
+ 优先级: CLI 参数 > 内层 `.env` > 外层 `.env` > 系统环境变量 (必须配置,否则报错)
219
+
220
+ | 变量 | 说明 | 默认值 |
221
+ |------|------|--------|
222
+ | `NFC_PORT` | 串口号 | - |
223
+ | `NFC_READER` | 读卡器类型 | - |
224
+ | `NFC_TRACE_LEVEL` | 日志级别 | `INFO` |
225
+ | `NFC_TRACE_DRIVER` | 开启 DRIVER 层追踪 | `false` |
226
+ | `NFC_TRACE_PROTOCOL` | 开启 PROTOCOL 层追踪 | `false` |
227
+ | `NFC_CARD_PATH` | 外部卡片模块搜索路径 (分号分隔) | - |
228
+
229
+ 支持多层 `.env` 加载:从脚本目录向上搜索所有 `.env` 文件,按从外到内的顺序依次加载(内层覆盖外层同名变量)。
230
+
231
+ 例如,脚本位于 `/project/tests/run.py`,沿途有 `/project/.env` 和 `/project/tests/.env`,加载顺序为:
232
+
233
+ 1. `/project/.env` (基础配置)
234
+ 2. `/project/tests/.env` (覆盖基础配置中的同名变量)
235
+
236
+ 在项目根目录或脚本所在目录创建 `.env` 文件进行持久化配置:
237
+
238
+ ```
239
+ NFC_PORT=COM20
240
+ NFC_READER=pn532
241
+ NFC_TRACE_LEVEL=DEBUG
242
+ NFC_TRACE_DRIVER=true
243
+ NFC_TRACE_PROTOCOL=true
244
+ ```
245
+
246
+ ## 开发
247
+
248
+ ```bash
249
+ # 运行测试
250
+ uv run pytest
251
+
252
+ # 运行特定测试
253
+ uv run pytest tests/test_xxx.py
254
+ ```
@@ -1,12 +1,13 @@
1
1
  [project]
2
2
  name = "nfcscript"
3
- version = "0.0.6"
3
+ version = "0.0.8"
4
4
  description = "A plugin-based core engine for DSL-style NFC testing automation."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
7
7
  authors = [{name = "crthu"}]
8
8
  dependencies = [
9
9
  "nfctester",
10
+ "python-dotenv",
10
11
  ]
11
12
 
12
13
  [tool.setuptools.packages.find]
@@ -14,6 +15,7 @@ where = ["src"]
14
15
 
15
16
  [project.scripts]
16
17
  nfcscript = "nfc.cli:main"
18
+ nfc-cli = "nfc.nfc_cli:main"
17
19
 
18
20
  [tool.pytest.ini_options]
19
21
  pythonpath = ["src"]
@@ -22,6 +24,9 @@ testpaths = ["tests"]
22
24
  [tool.uv]
23
25
  package = true
24
26
 
27
+ [tool.uv.sources]
28
+ nfctester = { workspace = true }
29
+
25
30
  [dependency-groups]
26
31
  dev = [
27
32
  "pytest",
@@ -29,7 +34,7 @@ dev = [
29
34
  ]
30
35
 
31
36
  [tool.bumpversion]
32
- current_version = "0.0.6"
37
+ current_version = "0.0.8"
33
38
  commit = true
34
39
  tag = true
35
40
  allow_dirty = true
@@ -4,3 +4,4 @@ from .assertions import *
4
4
  from .bits import *
5
5
  from .hex_util import *
6
6
  from .checksum import *
7
+ from . import trace
@@ -0,0 +1,116 @@
1
+ import sys
2
+ import argparse
3
+ import os
4
+ import runpy
5
+
6
+ from dotenv import load_dotenv
7
+
8
+ def _find_dotenv_all(start_dir):
9
+ chain = []
10
+ path = start_dir
11
+ while True:
12
+ env_path = os.path.join(path, ".env")
13
+ if os.path.exists(env_path):
14
+ chain.append(env_path)
15
+ parent = os.path.dirname(path)
16
+ if parent == path:
17
+ break
18
+ path = parent
19
+ chain.reverse()
20
+ return chain
21
+
22
+ def _parse_level(value: str) -> int:
23
+ """将字符串解析为解析级别,支持数字和别名"""
24
+ val = value.lower().strip()
25
+ if val in ("0", "false", "no", "off"):
26
+ return 0
27
+ if val in ("1", "simple", "summary"):
28
+ return 1
29
+ if val in ("2", "full", "tree"):
30
+ return 2
31
+ try:
32
+ return int(value)
33
+ except ValueError:
34
+ return 1
35
+
36
+ def run_script(script_path):
37
+ script_dir = os.path.dirname(os.path.abspath(script_path))
38
+ cwd = os.getcwd()
39
+
40
+ # 1. 加入当前工作目录 (CWD),优先级最高
41
+ if cwd not in sys.path:
42
+ sys.path.insert(0, cwd)
43
+
44
+ # 2. 加入脚本所在目录 (如果不是 CWD 的话)
45
+ if script_dir != cwd and script_dir not in sys.path:
46
+ sys.path.insert(0, script_dir)
47
+
48
+ runpy.run_path(script_path, run_name="__main__")
49
+
50
+ def main():
51
+ parser = argparse.ArgumentParser(
52
+ prog="nfcscript",
53
+ description="NFC Script Runner - 执行 NFC 测试脚本"
54
+ )
55
+ parser.add_argument("script", help="脚本文件路径")
56
+ parser.add_argument("-p", "--port", default=None, help="串口号 (默认: NFC_PORT 环境变量或 COM20)")
57
+ parser.add_argument("-r", "--reader", default=None, help="读卡器类型 (默认: NFC_READER 环境变量或 pn532)")
58
+ parser.add_argument("--trace-driver", action="store_true", help="开启 DRIVER 层追踪")
59
+ parser.add_argument("--trace-protocol", action="store_true", help="开启 PROTOCOL 层追踪")
60
+ parser.add_argument("--trace-parse", type=int, default=None, choices=[0, 1, 2],
61
+ help="解析级别: 0=关闭 1=简单 2=复杂 (默认: NFC_TRACE_PARSE 或 1)")
62
+ parser.add_argument("--trace-card-type", default=None,
63
+ help="卡片类型标识 (默认: NFC_TRACE_CARD_TYPE)")
64
+ parser.add_argument("--trace-level", default="INFO", help="日志级别 (默认: INFO)")
65
+
66
+ args, extra = parser.parse_known_args()
67
+
68
+ if not os.path.exists(args.script):
69
+ print(f"Error: Script not found: {args.script}")
70
+ sys.exit(1)
71
+
72
+ sys.argv = [args.script] + extra
73
+
74
+ script_dir = os.path.dirname(os.path.abspath(args.script))
75
+ dotenv_files = _find_dotenv_all(script_dir)
76
+ for dotenv_path in dotenv_files:
77
+ load_dotenv(dotenv_path, override=True)
78
+
79
+ if args.port is not None:
80
+ os.environ["NFC_PORT"] = args.port
81
+ if args.reader is not None:
82
+ os.environ["NFC_READER"] = args.reader
83
+
84
+ from nfc import trace
85
+
86
+ trace_level = os.environ.get("NFC_TRACE_LEVEL", "INFO")
87
+ trace_driver = os.environ.get("NFC_TRACE_DRIVER", "").lower() in ("1", "true", "yes")
88
+ trace_protocol = os.environ.get("NFC_TRACE_PROTOCOL", "").lower() in ("1", "true", "yes")
89
+ trace_parse = os.environ.get("NFC_TRACE_PARSE", "1")
90
+ trace_card_type = os.environ.get("NFC_TRACE_CARD_TYPE")
91
+ trace_width = os.environ.get("NFC_TRACE_WIDTH")
92
+
93
+ if args.trace_level != "INFO":
94
+ trace_level = args.trace_level
95
+ if args.trace_driver:
96
+ trace_driver = True
97
+ if args.trace_protocol:
98
+ trace_protocol = True
99
+ if args.trace_parse is not None:
100
+ trace_parse = str(args.trace_parse)
101
+ if args.trace_card_type is not None:
102
+ trace_card_type = args.trace_card_type
103
+
104
+ trace.set_level(trace_level)
105
+ trace.set_layer("DRIVER", trace_driver)
106
+ trace.set_layer("PROTOCOL", trace_protocol)
107
+ trace.set_parse(_parse_level(trace_parse))
108
+ if trace_card_type is not None:
109
+ trace.set_card_type(trace_card_type)
110
+ if trace_width is not None:
111
+ os.environ["CRFT_TRACE_WIDTH"] = trace_width
112
+
113
+ run_script(args.script)
114
+
115
+ if __name__ == "__main__":
116
+ main()