原笔记包含两个独立目标:用二进制差分减少升级传输量,以及让现场人员打开诊断页面。二者分别属于发布可靠性和故障反馈,不能仅靠两条补丁命令与一个隐藏手势完成。
本文给出设计边界和可复用示例。实际产品的分区、引导程序、硬件型号、固件版本和认证机制未提供,因此不宣称已经实现可部署的 OTA 系统。
差分文件解决什么问题
跳转到“差分文件解决什么问题”bsdiff 根据旧文件和新文件生成补丁;bspatch 根据旧文件和补丁重建新文件。它们处理二进制文件,不负责下载认证、硬件兼容、引导切换或掉电回滚。作者项目说明
在使用兼容版本的工具、具有足够磁盘和内存的离线工作目录中,可以验证:
# 制包端:old_firmware.bin 必须是明确版本的完整旧镜像bsdiff old_firmware.bin new_firmware.bin update.patch
# 离线验证:输出到新文件,保留原镜像bspatch old_firmware.bin candidate.bin update.patchcmp -s candidate.bin new_firmware.binsha256sum old_firmware.bin new_firmware.bin candidate.bin update.patchcmp 返回成功才能证明本次重建字节与目标一致。各文件的哈希应与发布记录关联,而不是只生成一串数字就算完成验证。以上命令尚未在本次环境执行;使用的 bsdiff 实现与补丁格式也应在项目中固定。
差分不保证总是更小:压缩、加密和镜像布局变化都可能影响收益。还要测量设备端重建所需 RAM、临时空间与时间。普通 MCU 未必能运行针对桌面文件设计的 bspatch;需要结合存储介质和更新框架选择算法,而不是照搬 Linux 命令。
从补丁到升级事务
跳转到“从补丁到升级事务”一个可审查的更新包需要明确:设备型号及硬件修订、允许的基础版本或基础镜像哈希、目标版本与目标哈希、补丁类型、长度、引导程序要求,以及经过认证的发布者信息。基础镜像不匹配时应拒绝该差分包,或转用已验证的完整镜像路径,不能尝试“将就打补丁”。
下面是过程关系,具体实现应由选定的更新框架承担:
确认包来源与适用设备 → 检查基础版本和空间 → 在非运行区域重建候选镜像 → 验证目标内容 → 请求引导程序试启动 → 健康检查通过后确认新版本 → 失败时按既定策略恢复可启动版本哈希能检测内容是否符合预期,但预期哈希本身必须来自可信的发布元数据;它不能单独认证发布者。签名验证、公钥更新和版本回退限制需要明确的信任设计。也不应在系统仍使用的文件或 Flash 区域上直接原地覆写,然后假定掉电后一定能恢复。
Linux 设备可以参考 RAUC 的签名 bundle、兼容性标识和 slot 机制。资源受限设备可以参考 MCUboot 的镜像验证、试启动与确认设计。二者有不同的存储和引导要求,不是给任意项目追加一个命令就能互换的库。RAUC 基础、MCUboot 设计
发布前的最小证据
跳转到“发布前的最小证据”| 验证项 | 需要回答的问题 |
|---|---|
| 基础版本 | 支持哪些旧版本,镜像被修改时如何处理? |
| 兼容性 | 错误型号、硬件版本和引导程序版本能否被拒绝? |
| 内容与认证 | 截断、损坏、非授权或不允许回退的包能否被拒绝? |
| 掉电 | 下载、重建、写入、切换和首次启动中断后能否恢复? |
| 启动健康 | 怎样确认核心服务与设备接口正常,失败如何回退? |
| 参数迁移 | 新版本改动配置格式后,旧版本还是否能读取? |
| 资源限制 | RAM、存储空间、耗时和写入寿命是否符合产品预算? |
配置迁移尤其不能只测试“程序能启动”:新固件若覆盖了旧配置格式,回到旧镜像也可能无法工作。镜像与持久化数据要一起设计回滚边界。
诊断入口:手势只是发现方式
跳转到“诊断入口:手势只是发现方式”Qt TapHandler 支持触摸和鼠标,tapCount 的连续点击受平台的时间和距离阈值影响。原示例只接受 TouchScreen,鼠标点击不会触发;也未给 Rectangle 明确尺寸或实现 showDebugMenu(),因此只是片段。Qt TapHandler 文档
下面给出可单独加载的 Qt 6 QML 组件。连续五次点按版本区域打开只读诊断,每次间隔超过 1.5 秒或组件隐藏时重置计数;同时接受鼠标和触摸。此规则使用独立计数器,避免把系统双击间隔当成产品的五击规则。
Diagnostics.qml
跳转到“Diagnostics.qml”import QtQuickimport QtQuick.Controls
Rectangle { id: root width: 360 height: 240 color: "#f5f7fa" property int tapStreak: 0 property bool diagnosticsOpen: false property string buildVersion: "demo"
function resetGesture() { tapStreak = 0 gestureTimeout.stop() } function registerTap() { tapStreak += 1 if (tapStreak === 5) { resetGesture() diagnosticsOpen = true } else { gestureTimeout.restart() } } onVisibleChanged: if (!visible) resetGesture() Timer { id: gestureTimeout interval: 1500 onTriggered: root.tapStreak = 0 } Rectangle { x: 20; y: 20; width: 320; height: 56 radius: 8 color: "white" Text { anchors.centerIn: parent text: "版本 " + root.buildVersion color: "#27364b" } TapHandler { acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchScreen acceptedButtons: Qt.LeftButton onTapped: root.registerTap() onCanceled: root.resetGesture() } } Rectangle { visible: root.diagnosticsOpen x: 20; y: 90; width: 320; height: 130 radius: 8 color: "#e6edf4" Text { x: 12; y: 12 text: "只读诊断\n构建版本:" + root.buildVersion color: "#27364b" } Button { x: 220; y: 78 width: 84; height: 40 text: "关闭" onClicked: root.diagnosticsOpen = false } }}把它作为界面组件使用,buildVersion 由应用提供真实构建版本。示例显示诊断信息,不执行升级、清空参数或直接控制硬件。若诊断页包含修改系统的功能,需要在后端核验相应权限和设备状态;知道五击手势不应成为执行特权操作的唯一条件。
项目中更易发现的“关于 → 诊断信息”入口也可以复用同一页面。原先希望隐藏入口,是交互选择,不是数据保密或权限校验机制。
让反馈能够复现
跳转到“让反馈能够复现”诊断记录应包含应用与固件版本、硬件修订、问题时间、操作步骤、错误码、最近的状态变化和必要的性能指标。原始采样数据或日志导出应有明确范围,避免把口令、令牌或无关个人信息混入反馈包。
将反馈关联到可复现案例和对应修复版本,再用相同步骤复测。只显示一个隐藏菜单,却没有保存版本、输入与事件过程,仍然难以定位问题。
本次 Qt 6.10.2 检查已完成组件加载、五次鼠标点击打开、关闭按钮、超时和隐藏时重置。测试使用离屏软件渲染,未连接真实触摸屏。差分命令、签名更新、真实分区切换和掉电恢复尚未实施;它们需要具体产品工程与硬件验收。