feat(factory): add GNSS test and LED status modes
This commit is contained in:
@@ -2,4 +2,5 @@ __pycache__/
|
||||
*.pyc
|
||||
build/
|
||||
dist/
|
||||
dist-*/
|
||||
vendor/
|
||||
|
||||
@@ -38,7 +38,7 @@ factory_host/
|
||||
- 持续检测烧录串口,支持多工位自动等待设备上电后烧录。
|
||||
- 同一设备烧录成功后锁定,端口断开并重新出现后才允许烧录下一台。
|
||||
- 自动识别 DBG UART,解析设备每 500 ms 广播的 FT1 状态。
|
||||
- 显示六个测试节点、IMEI、ICCID、NFC UID、失败码和实时日志。
|
||||
- 显示七个测试节点、IMEI、ICCID、NFC UID、失败码和实时日志。
|
||||
- 自动保存汇总 CSV 与单机 JSON,烧录和扫描过程不弹出控制台黑框。
|
||||
|
||||
## 快速验证
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"protocol": {
|
||||
"prefix": "FT1 ",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"auto_save": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ firmware/
|
||||
```
|
||||
|
||||
还应在一台真实设备上验证:自动等待上电烧录、断开后才允许下一台、DBG UART 自动识别、
|
||||
六项 FT1 状态、JSON/CSV 保存,以及产测固件和正式固件切换。
|
||||
七项 FT1 状态、GNSS 累计数据、JSON/CSV 保存,以及产测固件和正式固件切换。
|
||||
|
||||
## 7. 固件替换规则
|
||||
|
||||
|
||||
@@ -50,13 +50,13 @@ factory_host/dist/
|
||||
},
|
||||
"protocol": {
|
||||
"prefix": "FT1 ",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"auto_save": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `production`:产测固件,包含 FT1 测试程序、开机/Loopback/NFC/关机音频,以及暂不参与流程的 GNSS 音频。
|
||||
- `production`:产测固件,包含 FT1 测试程序以及开机、Loopback、NFC、GNSS、关机五段音频。
|
||||
- `official`:正式业务固件,供产测完成后换烧。
|
||||
- 固件路径可以使用相对于 EXE 所在发布目录的路径,也可以填写绝对路径。
|
||||
- 必须选择可直接烧录的内层 FT1 ZIP 或 `*-fac.zip`,不要选择外层 `*-target.zip` 归档。
|
||||
@@ -92,7 +92,11 @@ factory_host/dist/
|
||||
- IMEI、ICCID、NFC UID;
|
||||
- 失败码、结构化测试数据和实时串口日志。
|
||||
|
||||
产测固件当前依次执行:开机音频、电源 ADC、音频 Loopback、eSIM、数据连接和 NFC。
|
||||
产测固件开机后立即在后台启动 GNSS 搜索,同时依次执行:开机音频、电源 ADC、音频 Loopback、eSIM、数据连接和 NFC。最后的 GNSS 节点只播放一次提示音,然后循环读取此前累计的 NMEA、卫星数和定位状态,直到达到基础硬件判定门槛。
|
||||
|
||||
GNSS 的基础硬件判定门槛为:GNSS 成功打开、累计收到至少 1 条 NMEA,并且至少发现 1 颗卫星或已取得有效定位。经纬度、定位精度、TTFF 和 1PPS 不参与判定。
|
||||
|
||||
指示灯状态:普通流程长亮,Loopback 慢闪,NFC 快闪,GNSS 为呼吸灯。如果设备运行时无法启用 PWM,GNSS 会自动改为“双短闪 + 长灭”。GNSS 未通过时会无限等待并持续呼吸;通过后才播放关机音并自动关机。
|
||||
|
||||
## 5. 测试结果与日志
|
||||
|
||||
@@ -178,4 +182,4 @@ python -B -m unittest discover -s .\factory_host\tests -p "test_*.py" -v
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\factory_host\scripts\build_release.ps1
|
||||
```
|
||||
|
||||
发布前至少使用一台设备验证:产测固件烧录、调试 UART 自动识别、START 握手、六项测试结果、JSON/CSV 保存,以及正式固件换烧。
|
||||
发布前至少使用一台设备验证:产测固件烧录、调试 UART 自动识别、七项测试结果、GNSS 室外或窗边搜星、JSON/CSV 保存,以及正式固件换烧。
|
||||
|
||||
Binary file not shown.
@@ -94,7 +94,7 @@ DEFAULT_CONFIG = {
|
||||
"official": "firmware/EG800AKCN_11LG-official-fac.zip",
|
||||
},
|
||||
"serial": {"baudrate": 115200, "encoding": "utf-8", "line_ending": "\\r\\n"},
|
||||
"protocol": {"prefix": "FT1 ", "version": 1, "auto_save": True},
|
||||
"protocol": {"prefix": "FT1 ", "version": 2, "auto_save": True},
|
||||
}
|
||||
|
||||
|
||||
@@ -550,7 +550,7 @@ class FactoryHost(Tk):
|
||||
number = str(int(number) + 1) if number.isdigit() else f"{number}_new"
|
||||
station = Station(number=number, flash_port=flash_port, test_port=test_port, firmware_kind=self.firmware_kind.get())
|
||||
self.stations[number] = station
|
||||
self.table.insert("", END, iid=number, values=(number, flash_port, test_port, station.status, "", "", "0/6", "产测固件", ""))
|
||||
self.table.insert("", END, iid=number, values=(number, flash_port, test_port, station.status, "", "", f"0/{len(NODE_NAMES)}", "产测固件", ""))
|
||||
return station
|
||||
|
||||
def remove_station(self) -> None:
|
||||
@@ -592,7 +592,7 @@ class FactoryHost(Tk):
|
||||
protocol = station.protocol
|
||||
self.table.item(station.number, values=(
|
||||
station.number, station.flash_port, station.test_port, station.status,
|
||||
protocol.session, protocol.imei, f"{protocol.passed_count}/6",
|
||||
protocol.session, protocol.imei, f"{protocol.passed_count}/{len(NODE_NAMES)}",
|
||||
"产测固件" if station.firmware_kind == "production" else "正式固件", station.message,
|
||||
))
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Any
|
||||
|
||||
|
||||
PREFIX = "FT1 "
|
||||
VERSION = 1
|
||||
VERSION = 2
|
||||
MESSAGE_TYPES = {
|
||||
"session_start",
|
||||
"node_start",
|
||||
@@ -27,12 +27,13 @@ NODE_NAMES = (
|
||||
"ESIM",
|
||||
"DATA_CALL",
|
||||
"NFC",
|
||||
"GNSS",
|
||||
)
|
||||
ERROR_MESSAGES = {
|
||||
0: "成功",
|
||||
1001: "ES8311 初始化或设备 ID 检查失败",
|
||||
1002: "开机提示音文件不存在",
|
||||
1003: "开机提示音播放失败或超时",
|
||||
1002: "当前节点提示音文件不存在",
|
||||
1003: "当前节点提示音播放失败或超时",
|
||||
1101: "ADC 初始化或读取失败",
|
||||
1102: "电压低于 3500mV",
|
||||
1103: "电压高于 4200mV",
|
||||
@@ -46,6 +47,9 @@ ERROR_MESSAGES = {
|
||||
1501: "4G 网络注册超时",
|
||||
1502: "IPv4 PDP 激活失败",
|
||||
1503: "未获取有效 IPv4 地址",
|
||||
1601: "GNSS 打开失败",
|
||||
1602: "GNSS 未收到 NMEA",
|
||||
1603: "GNSS 已收到 NMEA,但未发现卫星",
|
||||
1701: "前置节点未全部通过,NFC 锁定",
|
||||
1702: "DP1312EA 版本不是 0x82",
|
||||
1703: "60 秒内未成功刷卡",
|
||||
|
||||
@@ -75,7 +75,7 @@ class FactoryHostTests(unittest.TestCase):
|
||||
def test_ft1_ignores_sdk_log_and_parses_node(self):
|
||||
self.assertIsNone(parse_ft1_line("DP1312EA is OK!"))
|
||||
message = parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"node_result","seq":3,"session":"BOOT-1",'
|
||||
'FT1 {"v":2,"type":"node_result","seq":3,"session":"BOOT-1",'
|
||||
'"uptime_ms":2450,"imei":"860123456789012","iccid":"","nfc_uid":"",'
|
||||
'"node":1,"name":"POWER_ADC","result":"PASS","code":0,"elapsed_ms":350,'
|
||||
'"data":{"voltage_mv":3865}}'
|
||||
@@ -85,17 +85,17 @@ class FactoryHostTests(unittest.TestCase):
|
||||
def test_ft1_session_result_and_sequence_guard(self):
|
||||
session = FT1Session()
|
||||
start = parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"session_start","seq":1,"session":"BOOT-1",'
|
||||
'FT1 {"v":2,"type":"session_start","seq":1,"session":"BOOT-1",'
|
||||
'"uptime_ms":2050,"imei":"860123456789012","iccid":"","nfc_uid":"",'
|
||||
'"fw":"FACTORY_TEST_1.0.0","start_time":null}'
|
||||
'"fw":"FACTORY_TEST_1.1.0","start_time":null}'
|
||||
)
|
||||
session.apply(start)
|
||||
result = parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"session_result","seq":2,"session":"BOOT-1",'
|
||||
'FT1 {"v":2,"type":"session_result","seq":2,"session":"BOOT-1",'
|
||||
'"uptime_ms":40120,"result":"FAIL","fail_code":1203,"imei":"860123456789012",'
|
||||
'"iccid":"","nfc_uid":"","start_time":null,"duration_ms":38070,"nodes":'
|
||||
'{"BOOT_AUDIO":"PASS","POWER_ADC":"PASS","AUDIO_LOOPBACK":"FAIL",'
|
||||
'"ESIM":"WAIT","DATA_CALL":"WAIT","NFC":"WAIT"}}'
|
||||
'"ESIM":"WAIT","DATA_CALL":"WAIT","NFC":"WAIT","GNSS":"WAIT"}}'
|
||||
)
|
||||
session.apply(result)
|
||||
self.assertEqual(session.result, "FAIL")
|
||||
@@ -107,37 +107,37 @@ class FactoryHostTests(unittest.TestCase):
|
||||
def test_ft1_rejects_node_name_mismatch(self):
|
||||
with self.assertRaises(ProtocolError):
|
||||
parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"node_start","seq":2,"session":"BOOT-1",'
|
||||
'FT1 {"v":2,"type":"node_start","seq":2,"session":"BOOT-1",'
|
||||
'"uptime_ms":2100,"node":1,"name":"NFC"}'
|
||||
)
|
||||
|
||||
def test_ft1_control_messages(self):
|
||||
ready = parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"device_ready","seq":1,"session":"BOOT-1",'
|
||||
'"uptime_ms":100,"fw":"FACTORY_TEST_1.0.0"}'
|
||||
'FT1 {"v":2,"type":"device_ready","seq":1,"session":"BOOT-1",'
|
||||
'"uptime_ms":100,"fw":"FACTORY_TEST_1.1.0"}'
|
||||
)
|
||||
ack = parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"command_ack","seq":2,"session":"BOOT-1",'
|
||||
'FT1 {"v":2,"type":"command_ack","seq":2,"session":"BOOT-1",'
|
||||
'"uptime_ms":120,"command":"START","accepted":true}'
|
||||
)
|
||||
session = FT1Session()
|
||||
self.assertEqual(session.apply(ready), "设备已识别 FACTORY_TEST_1.0.0")
|
||||
self.assertEqual(session.apply(ready), "设备已识别 FACTORY_TEST_1.1.0")
|
||||
self.assertEqual(session.apply(ack), "START 已确认")
|
||||
self.assertTrue(session.device_ready_seen)
|
||||
self.assertTrue(session.start_ack_seen)
|
||||
|
||||
def test_ft1_status_snapshot_keeps_passed_nodes(self):
|
||||
first = parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"status_snapshot","seq":1,"session":"BOOT-1",'
|
||||
'FT1 {"v":2,"type":"status_snapshot","seq":1,"session":"BOOT-1",'
|
||||
'"uptime_ms":100,"active_node":0,"nodes":{"BOOT_AUDIO":"WAIT",'
|
||||
'"POWER_ADC":"WAIT","AUDIO_LOOPBACK":"WAIT","ESIM":"WAIT",'
|
||||
'"DATA_CALL":"WAIT","NFC":"WAIT"}}'
|
||||
'"DATA_CALL":"WAIT","NFC":"WAIT","GNSS":"WAIT"}}'
|
||||
)
|
||||
second = parse_ft1_line(
|
||||
'FT1 {"v":1,"type":"status_snapshot","seq":2,"session":"BOOT-1",'
|
||||
'FT1 {"v":2,"type":"status_snapshot","seq":2,"session":"BOOT-1",'
|
||||
'"uptime_ms":600,"active_node":1,"nodes":{"BOOT_AUDIO":"PASS",'
|
||||
'"POWER_ADC":"WAIT","AUDIO_LOOPBACK":"WAIT","ESIM":"WAIT",'
|
||||
'"DATA_CALL":"WAIT","NFC":"WAIT"}}'
|
||||
'"DATA_CALL":"WAIT","NFC":"WAIT","GNSS":"WAIT"}}'
|
||||
)
|
||||
session = FT1Session()
|
||||
session.apply(first)
|
||||
@@ -145,6 +145,20 @@ class FactoryHostTests(unittest.TestCase):
|
||||
self.assertEqual(session.nodes["BOOT_AUDIO"], "PASS")
|
||||
self.assertEqual(session.current_node, "POWER_ADC")
|
||||
|
||||
def test_ft1_parses_low_threshold_gnss_result(self):
|
||||
message = parse_ft1_line(
|
||||
'FT1 {"v":2,"type":"node_result","seq":9,"session":"BOOT-1",'
|
||||
'"uptime_ms":30600,"imei":"860123456789012","iccid":"8986000000000000000",'
|
||||
'"nfc_uid":"04A1B2C3D4E5F6","node":6,"name":"GNSS","result":"PASS",'
|
||||
'"code":0,"elapsed_ms":800,"data":{"open_ret":0,"nmea_count":1,'
|
||||
'"sat_count":1,"fix_status":0,"search_ms":28500,'
|
||||
'"required_nmea_count":1,"required_sat_count":1}}'
|
||||
)
|
||||
session = FT1Session()
|
||||
session.apply(message)
|
||||
self.assertEqual(session.nodes["GNSS"], "PASS")
|
||||
self.assertEqual(session.node_details["GNSS"]["data"]["sat_count"], 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user