107 lines
11 KiB
Markdown
107 lines
11 KiB
Markdown
# 设备适配器
|
||
|
||
入口是 `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设备。
|