Files

107 lines
11 KiB
Markdown
Raw Permalink 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.
# 设备适配器
入口是 `scripts/device_adapter.py`,仅依赖 Python 标准库。所有设备操作必须由本次任务提供目标;分发包没有默认 IP、完整 SN、设备型号、密码或 ROM 路径。
先用 `capabilities` 查看实际支持范围,按 [协议范围](protocol-support.md) 确定设备协议。网络子命令接受 `--protocol v1/v2`,不传仍默认 V2。V1 搜索、信息查询、版本比较和 TCP4941/2170/MD5+ROM 下发见 [V1 接口](v1-protocol.md)。下文的 894x/EPTP/EPFP 仅属于 V2。
## 操作边界
| 命令 | 网络及写入范围 | 必要输入 | 完成含义 |
| --- | --- | --- | --- |
| `capabilities` | 不联网 | 无 | 列出已实现协议和缺失依赖,不证明设备在线 |
| `offline` | 只读取本地 ROM,不联网 | `--rom` | 文件有效且已计算摘要;没有验证设备 |
| `discover` | V1 UDP 4942;V2 UDP 8941,只读广播 | 按需要指定 `--interface`、身份筛选 | 候选发现,不能代替升级前 TCP 身份校验 |
| `identify` | 当前协议的只读信息查询 | `--host --expected-sn` | SN 精确匹配并观察上报型号;model_verified=false,无需预先猜型号 |
| `info` | V1 TCP 4941/2104;V2 TCP 8942/EPTP/30101,只读 | `--host --expected-sn --expected-model` | 完整 SN 与至少一个设备型号字段精确匹配;无需 ROM |
| `upgrade` | 默认本地 ROM 校验及当前协议的 TCP 在线预检 | 数字设备 IP、身份参数、`--rom --target-version --version-field` | `PREFLIGHT_ONLY`,未传包;V1还报告所选版本字段是否存在 |
| `upgrade --execute` | 预检后最多一次写入:V1 TCP4941/2170;V2 TCP8943/EPFP/4001 | 以上参数及 `--execute --rom-sha256` | 协议步骤结果;始终要求后续版本回读 |
| `verify-version --response` | 只读取已有响应 JSON,不联网 | 响应文件、身份及版本参数 | 验证文件记录中的身份和指定固件字段,不证明当前在线状态 |
| `verify-version --host` | 当前协议,只读回读一次 | 设备地址、身份及版本参数 | 当前协议响应中的身份和指定固件字段匹配 |
`upgrade` 默认是**在线预检**。完全不联网时使用 `offline` 或 `verify-version --response`。V2 discover 转调固定版本的发现函数,保留本地 8951、设备 8941;V1 discover 使用独立 ff00/ff01 实现,默认本地/设备 4942,参数见 `discover --protocol v1 --help`。同一完整 SN 对应冲突网络身份时禁止自动选择。
`upgrade --host` 必须是明确的数字 IPv4 或 IPv6 地址,包括未加 `--execute` 的在线预检。主机名会在联网前拒绝;适配器把地址规范化后用于当前协议的身份查询及受支持的写入,避免两次 DNS 解析选择不同目标。结果中的 `bound_ip` 与 `upgrade_target_bound` 事件保存实际绑定地址;预检后失败时也保留它。程序拒绝可确定的多播地址、未指定地址和 IPv4 有限广播地址 `255.255.255.255`,IPv4 映射 IPv6 地址执行相同检查。没有网段前缀时不能判断任意子网定向广播地址,应提供已确认的设备单播 IP。`info` 与只读在线版本回读仍可使用主机名。
`--expected-sn` 必须是完整 SN,按完整字符串比较,不接受末尾数位或模糊匹配。`--expected-model` 在 `info`、`upgrade` 和 `verify-version` 中必填:V1 只匹配 dev_type;V2 与 data.inner.type、data.outer.type、data.hardware.type 至少一个值完全相等。型号未知时先用 identify 观察,再带返回型号校验。产品简称不一定是协议型号;出现不匹配应审阅返回的原始身份,不能通过关闭校验继续写入。
## 常用命令
下面使用占位符。替换成本次任务已确认的目标后执行;不要把现场设备值保存回技能模板。
```powershell
python scripts/device_adapter.py capabilities
python scripts/device_adapter.py offline --rom "<ROM路径>"
python scripts/device_adapter.py discover --protocol v2 --expected-sn "<完整SN>" --expected-model "<协议完整型号>"
python scripts/device_adapter.py identify --protocol v2 --host "<设备地址>" --expected-sn "<完整SN>"
python scripts/device_adapter.py info --host "<设备地址>" --expected-sn "<完整SN>" --expected-model "<协议完整型号>"
python scripts/device_adapter.py upgrade --host "<数字设备IP>" --expected-sn "<完整SN>" --expected-model "<协议完整型号>" --rom "<ROM路径>" --target-version "<明确目标版本>" --version-field inner.version
```
写入还必须提供审阅过的 ROM SHA-256,并显式加 `--execute`:
```powershell
python scripts/device_adapter.py upgrade --host "<数字设备IP>" --expected-sn "<完整SN>" --expected-model "<协议完整型号>" --rom "<ROM路径>" --target-version "<明确目标版本>" --version-field inner.version --rom-sha256 "<64位SHA256>" --execute
```
摘要不匹配、缺少 `--rom-sha256`、身份不匹配都会阻断写入。适配器在在线预检后、调用写入函数前重新校验摘要;写入期间不要替换或修改 ROM。ROM 路径中的版本号仅保留为诊断线索,不能代替明确的 `--target-version`,也不能证明 ROM 与设备型号兼容。
升级协议报告成功后,等待任务规定的设备恢复条件,再执行独立的只读回读:
```powershell
python scripts/device_adapter.py verify-version --host "<设备地址>" --expected-sn "<完整SN>" --expected-model "<协议完整型号>" --target-version "<明确目标版本>" --version-field inner.version
python scripts/device_adapter.py verify-version --response "<已有30101响应JSON路径>" --expected-sn "<完整SN>" --expected-model "<协议完整型号>" --target-version "<明确目标版本>" --version-field inner.version
```
V2 响应文件可以是原始 30101 JSON;V1 必须是带 protocol=v1、cmd=0x5a0e2104、code=0 及原生 data 的适配器封套。两者均可直接使用 info 输出(读取 response 字段),不能跨协议冒充。不从任意日志中猜选“最后一条成功”。只读查询与升级均不自动重试;写入断开、超时或失败后先收集状态,不自动重新烧录。
## 结果与验收
除 V2 `discover` 保留原 JSONL 事件输出外,每次调用输出一个 UTF-8 JSON 对象,`events` 保留协议记录。导入模块不会修改调用方 stdout/stderr;执行 CLI 才统一配置 UTF-8。运行时记录可能包含现场完整身份与响应,应保存在任务证据目录,不纳入可分发技能。
退出码 0 表示本次命令完成,**不能统一解释为设备验收通过**。错误、BLOCKED、版本不符和数据不足返回 1;参数错误返回 2;V2 发现命令沿用其原退出码约定。
| `status` | 含义 |
| --- | --- |
| `CAPABILITIES` | 离线列出协议实现范围;不是设备验收结果 |
| `BLOCKED` | 协议不受支持、搜索身份冲突或未唯一找到目标;不能自动更换目标或协议 |
| `IDENTITY_OBSERVED` | identify 中完整 SN 已核实,型号仅作为实测值返回 |
| `OFFLINE_READY` | ROM 本地校验完成,无设备证据 |
| `IDENTITY_CONFIRMED` | 所选协议 SN/型号匹配,未做目标固件版本比较 |
| `PREFLIGHT_ONLY` | 在线预检完成,`write_executed=false` |
| `PROTOCOL_SUCCESS_POSTCHECK_REQUIRED` | V1收到2170/整数code=0,或V2收到状态2,且客户端发送ROM字节数严格等于文件大小;`firmware_verified=false`,`postcheck_required=true` |
| `PASS` | 仅在 `verify-version` 中:指定身份及固件字段完全匹配 |
| `FAIL` | 指定固件字段存在,但与明确目标版本不符 |
| `INCOMPLETE` | 指定固件字段缺失、为空或类型不正确,不能通过 |
| `ERROR` | 文件、身份、协议或传输错误;`firmware_verified=false` |
V2 版本字段只允许 inner.version 或 outer.version;V1 原生字段为 firmware、dev_ver,须由任务明确选择。V1响应如果直接上报字面 inner.version/outer.version,保留该值供指定字段比较;没有 V1→V2 字段映射依据,不能静默换名。指定缺失的 outer.version 得到 INCOMPLETE,已经授权的传包仍可执行。hardware/hardware.version 不作为固件验收,也不替补缺失字段。
`PASS` 的 `acceptance_scope` 固定为 `identity_and_selected_firmware_version_only`,不表示全部业务测试已通过。`evidence_source=recorded_response`、`live_readback=false` 表示只验证已有文件;`evidence_source=live_30101`(V2)或 live_v1_2104(V1)、`live_readback=true` 表示本次只读回读。已有响应的采集时间与来源需由任务证据链说明。
写入调用开始后发生错误,返回 `write_may_have_started=true` 与 `postcheck_required=true`,并设置 `automatic_retry=false`。这代表写入结果未确定,不能因命令失败而自动重试。
如果零字节或部分 ROM 上传就得到成功状态,适配器返回 `ERROR`。只有 `transfer.bytes_sent` 是整数且与 ROM 大小完全一致,并满足所选协议的应答条件,才设置 `protocol_success=true`。V1 的 `v1_upgrade_acknowledged`、V2 的 `device_reported_status_2` 只记录设备应答;`transfer_result` 记录客户端实际计数,不能代替独立版本回读。
## 来源与维护
`scripts/vendor/source-manifest.json` 固定上游文件及分发副本的 SHA-256。导入前重新计算分发副本摘要,不匹配就拒绝加载;维护代码时须审阅差异并同步更新清单,不能从临时下载目录动态加载。
| 上游文件 | 上游 SHA-256 |
| --- | --- |
| `embedded-app-pc-debug/scripts/device_upgrade.py` | `ce72435ff4a3dbe3cc0c28dd81c2a7873b60e55bb64f09055a64900b8f921578` |
| `embedded-app-pc-debug/scripts/v2_discover.py` | `f6634bfc3b86abb6d379d28b679f2e209694f9d856e343157e24b9ff629ea894` |
升级副本只提取协议、身份、ROM 校验与传输函数,去掉原 CLI、日志类与导入时输出重配置。发现副本只把输出重配置移入脚本入口,发现逻辑未改动。没有复制 `module_debug.py`、串口脚本、原技能说明、历史密钥或历史授权。
## 实际验证记录
2026-10-19,全部118项离线 unittest 通过,其中设备适配器28项、V1报文/身份/发现24项、V1升级14项。运行实际 V1/V2 编解码与传输函数,仅替换socket/select边界;使用合成身份及保留文档地址,不访问真机。覆盖默认不写入、摘要/身份门槛、实际发送计数、文件变化、错误不重试、时限、版本字段不足及明确协议分流。
同日在用户授权 V1 设备上完成单次19,420,244字节ROM下发,收到2170/code=0/update success。连续两次恢复查询与原SN/型号一致,firmware=2.0.2、dev_ver=2.0.20260908.6;升级前为2.0.1/2.0.20260908.5。任务指定outer.version缺失,验收保留INCOMPLETE,原生版本观测单独通过。现场证据独立保存,不能据此泛化所有V1设备。