第7章 建立最小端云闭环并发布v0.1
第6章已经确定设备、服务器和用户端之间的事件契约。本章据此建立第一个可运行版本。用户按下开发板上的按键后,设备生成结构化事件。服务器接收并确认事件,授权用户可以在页面中查看。
物理按键
→ 设备事件
→ 网络发送
→ 服务器校验与确认
→ 事件存储
→ 用户页面显示
按键不是最终传感器,也不代表AI模型。本章使用它是为了先验证项目中最长、最容易产生接口问题的端到端路径。第8章替换输入适配器时,事件、通信和用户反馈链路继续使用。
按组件分层开发可以提高局部效率,却可能把集成问题推迟到末期。端到端最小闭环先贯通一条完整路径,使接口分歧较早显现。它的代价是部分实现仍属临时方案,因此必须明确替换边界和版本限制。
完成本章后,应当能够:
- 从
v0.1里程碑选择一条最短端到端路径; - 按依赖关系拆分设备、服务器、用户端和契约任务;
- 使用接口契约减少并行开发中的猜测;
- 让AI在单个议题范围内生成或修改代码;
- 使用真实开发板、真实网络请求和真实服务器响应验证闭环;
- 区分设备日志、服务器日志、用户结果和Git提交四类证据;
- 集成多个任务分支并处理接口偏差;
- 更新
README.md、需求追踪和变更日志; - 为第一个可运行版本创建带注释标签和发布记录。
7.1 选择最短的端到端路径
7.1.1 从产品结果反推组件
第5章定义了产品的核心结果。设备确认目标事件后,服务器收到记录,授权用户能够查看。v0.1只实现支撑这一结果的必要组件:
| 组件 | v0.1实现 |
暂不实现 |
|---|---|---|
| 输入 | 板载按键或外接按键 | 连续传感器采样 |
| 判断 | 按键边沿形成事件 | 模型推理 |
| 策略 | 基本去抖和单次事件 | 概率平滑与复杂冷却 |
| 通信 | 一种课程网络链路 | 多协议切换、离线重传 |
| 服务器 | 校验、保存、确认 | 复杂分析和运维平台 |
| 用户端 | 查看最近事件 | 完整移动端产品 |
这个版本必须使用真实设备运行。数据回放或接口调用可以提供自动测试证据。它们不能替代开发板、网络和服务器的整体验证。
7.1.2 写出v0.1验收路径
在里程碑说明中保存:
# v0.1 最小端云闭环
## 入口
用户按下课程开发板上的指定按键。
## 成功路径
1. 设备只产生一次按键事件;
2. 事件符合EVENT-001;
3. 设备把事件发送到课程服务器;
4. 服务器返回明确成功响应和event_id;
5. 授权页面显示相同event_id、设备和事件时间。
## 失败路径
- 消息不合法时,服务器拒绝并返回可定位错误;
- 凭据无效时,服务器拒绝写入;
- 网络失败时,设备日志不得显示“服务器已确认”。
## 不在本版本范围
- 传感器数据;
- 正式模型;
- 断线持久化与批量重传;
- 多设备管理;
- 面向公众的部署。
成功路径和失败路径共同构成验收。只演示一次成功请求,不能说明错误处理正确。
7.1.3 先贯通路径,再扩展功能
早期项目常按设备、服务器和用户端分别开发。各部分可以独立完成,却可能在集成时暴露接口差异。发现问题越晚,修改范围通常越大。
端到端切片先贯通一条完整路径。它使字段、错误和确认方式较早接受检验,也为后续替换组件提供基线。其代价是早期版本功能较少,不能代表完整产品。v0.1的价值在于验证结构,不在于追求功能数量。
7.2 按依赖关系拆分议题
7.2.1 先合并契约,再并行实现
v0.1建议拆成五个议题:
| 议题 | 目标 | 依赖 |
|---|---|---|
| A | 完成EVENT-001模式和示例检查 | 无 |
| B | 设备按键产生EVENT-001消息 | A |
| C | 服务器接收、校验、保存并确认 | A |
| D | 用户端显示最近事件 | C |
| E | 端到端验证与v0.1发布 |
B、C、D |
依赖关系为:
┌→ B 设备事件 ─┐
A 契约 ├→ C 服务器 ───┼→ E 集成与发布
│ └→ D ──┘
└──────────────
A合并到main后,B和C才开始实施。否则设备和服务器可能各自解释字段,最终在集成时才发现名称、类型或错误语义不同。
7.2.2 议题应包含允许范围
设备任务可写为:
# 按键产生EVENT-001设备事件
## 目标
按下指定物理按键后,固件形成一条符合EVENT-001的事件,
并交给现有通信适配器发送。
## 允许修改
- firmware/include/input_source.h
- firmware/src/button_input.cpp
- firmware/src/main.cpp
- firmware/tests/button_input_test.cpp
## 约束
- 使用课程硬件配置中的按键引脚;
- 完成按键去抖;
- 一次按压只产生一个事件;
- 不修改EVENT-001;
- 不实现传感器和模型;
- 不在源代码中写入凭据。
## 验收
- 构建通过;
- 单次按压只增加一个sequence;
- 按住按键不连续产生事件;
- 释放后再次按下可以形成新事件;
- 事件示例通过契约检查;
- 实机日志记录固件提交标识。
AI实施范围与议题范围保持一致。任务需要改变契约时,应停止当前议题,先对契约建立独立合并请求。
7.3 建立可构建的项目骨架
7.3.1 只创建本版本实际使用的目录
从最新main建立任务分支:
git switch main
git pull --ff-only
git switch -c issue-31-v01-project-skeleton
v0.1需要的目录为:
intelligent-sensing-terminal/
├─ firmware/
│ ├─ include/
│ ├─ src/
│ └─ tests/
├─ server/
│ ├─ app/
│ └─ tests/
├─ contracts/
├─ docs/
├─ tools/
│ └─ project.py
├─ .env.example
└─ .gitignore
tools/project.py提供课程统一入口:
python tools/project.py doctor
python tools/project.py contract
python tools/project.py build-firmware
python tools/project.py test-server
python tools/project.py run-server
python tools/project.py flash
python tools/project.py monitor
统一入口不替代底层工具,而是固定课程所需参数、检查前置条件并给出一致错误信息。底层编译器、板卡包和服务器框架版本仍应记录在环境文件中。
7.3.2 配置和秘密分离
.env.example只保存变量名称和非敏感示例:
EVENT_SERVER_URL=http://127.0.0.1:8000
DEVICE_ID=course-device-001
DEVICE_TOKEN=replace-with-local-token
真实.env进入.gitignore:
.env
secrets.local.h
.venv/
build/
*.log
检查:
git check-ignore -v .env
git status --short
如果真实令牌已经进入提交,仅从文件中删除并不够。应立即通知教师、吊销令牌并检查Git历史。历史清理属于高影响操作,须按仓库维护流程执行。
7.4 用契约驱动设备事件
7.4.1 设备事件对象
固件内部先定义与传输无关的事件对象:
struct DeviceEvent {
const char* schema_version;
const char* event_id;
const char* device_id;
const char* event_type;
const char* observed_at;
std::uint32_t device_uptime_ms;
bool has_confidence;
float confidence;
const char* firmware_version;
const char* model_version;
std::uint32_t sequence;
};
代码片段用于说明接口。实际项目应处理字符串存储长度、时间来源和编码失败,不能直接把临时缓冲区地址保存到长期对象。服务器为每条成功接收的事件增加received_at;用户端必须区分设备观察时间与服务器接收时间。
v0.1的字段约定:
event_type固定为button_event;- 设备时间已经同步时填写
observed_at,否则编码为null,同时始终填写device_uptime_ms; - 确定性按键事件把
has_confidence设为false并把confidence编码为null; model_version标记为baseline-rule-1;sequence每产生一个新事件增加一次;firmware_version来自构建信息,不由学生手工多处复制;event_id在设备侧生成,同一事件重试时保持不变。
按钮只负责告诉系统“输入发生”。事件标识、编码和发送分别由其他组件负责,便于第8章替换输入。
7.4.2 去抖和单次事件
机械按键在一次按压中可能快速产生多次电平变化。输入适配器至少维护:
原始电平
→ 稳定等待
→ 确认按下边沿
→ 产生一次事件
→ 等待释放
去抖时间应是可配置参数,并记录单位。AI生成实现后,检查:
- 是否使用阻塞式长延时影响网络处理;
- 是否在“按住”期间重复产生事件;
- 是否混淆高电平与低电平有效;
- 是否引用课程硬件引脚配置;
- 是否在中断中执行网络发送等耗时操作。
嵌入式输入和中断的详细实现由硬件课程展开。本章主要验证它是否遵守InputSource接口和事件语义。
7.5 实现服务器接收和确认
7.5.1 请求处理的最小顺序
服务器接收一条事件时,按下面顺序处理:
认证设备
→ 解析JSON
→ 按EVENT-001校验
→ 检查event_id是否已存在
→ 保存新事件或返回已有结果
→ 返回明确响应
认证失败和消息不合法都不能写入事件表。服务器日志不得记录完整设备令牌。
接口成功响应示例:
{
"status": "accepted",
"event_id": "01JEXAMPLE0000000000000000",
"received_at": "<ISO 8601格式的实际时间>"
}
重复提交同一event_id时,可以返回:
{
"status": "already_accepted",
"event_id": "01JEXAMPLE0000000000000000",
"received_at": "<与首次接收相同的实际时间>"
}
两次响应都表示服务器已经拥有该事件,但只有第一次创建记录。这种行为使设备在超时后安全重试。
7.5.2 先用接口测试验证服务器
在连接设备前,用课程脚本提交契约示例:
python tools/project.py run-server
python tools/project.py test-server
测试至少覆盖:
| 编号 | 请求 | 预期 |
|---|---|---|
| API-01 | 合法事件和有效凭据 | 接受并返回相同event_id |
| API-02 | 重复event_id | 不重复写入 |
| API-03 | 缺少必填字段 | 拒绝并指出字段 |
| API-04 | 错误字段类型 | 拒绝 |
| API-05 | 无效凭据 | 拒绝且不写入 |
这一步验证服务器契约,不证明设备已经工作。测试记录要准确标注证据层次。
7.6 实现用户可见结果
v0.1用户端只显示最近事件:
事件类型 | 设备 | 观察时间 | 接收时间 | 状态
页面从服务器的查询接口读取已保存事件,不直接生成测试数据,也不在浏览器中伪造识别结果。验证时要确认页面中的event_id与设备日志和服务器响应一致。
本章不要求复杂前端框架。可以使用课程服务器提供的最小页面,重点验证:
- 未认证用户不能访问事件;
- 没有事件时显示明确空状态;
- 新事件能够在规定刷新方式下出现;
- 时间显示包含时区或明确转换规则;
- 页面不会显示设备令牌和内部错误堆栈。
用户界面修改同样建立议题和合并请求。AI生成页面时,不允许它同时改变服务器事件结构。
7.7 控制AI一次完成一个议题
7.7.1 设备任务的实施请求
请实施当前GitLab议题中的按键输入任务。
先读取:
- contracts/event.schema.json
- docs/architecture/specification.md
- firmware/include/input_source.h
- firmware/src/main.cpp
- hardware/pin-map.md
只允许修改议题列出的固件文件和对应测试。
要求:
1. 先复述EVENT-001中本任务必须产生的字段;
2. 说明按键状态机和去抖边界;
3. 分两步实施:输入适配器、主程序接入;
4. 每步后列出Git差异;
5. 不修改契约、物料清单、模型和服务器;
6. 不运行烧录,不提交,不推送;
7. 构建失败时只分析实际错误。
若AI建议为了按键事件重写通信组件,应把建议登记为问题,不在当前议题实施。
7.7.2 服务任务的实施请求
请只实施事件接收接口。
输入:
- contracts/event.schema.json
- contracts/examples/event.example.json
- docs/architecture/specification.md中的API-001
- server/app现有代码
约束:
- 不改变EVENT-001;
- 不添加用户管理功能;
- event_id必须幂等;
- 无效消息和无效凭据不得写入;
- 日志不得输出令牌;
- 使用项目已经固定的依赖版本;
- 增加API-01至API-05测试;
- 不提交、不推送。
代码生成结束后,学生分别查看设备和服务器分支的差异。AI对另一个分支尚未合并的内容没有可靠认知,不能要求它“自动把两边接起来”。
7.8 合并并集成端到端闭环
7.8.1 按依赖顺序合并
推荐顺序:
契约MR
→ 服务器MR
→ 用户端MR
→ 设备MR
→ 集成MR
顺序不是固定规则,关键是每个合并请求基于已经确认的接口,并在目标分支更新后重新验证。开始集成前执行:
git switch main
git pull --ff-only
git switch -c issue-35-v01-integration
7.8.2 运行完整系统
按课程环境卡配置本地服务或课程测试服务器,依次执行:
python tools/project.py doctor
python tools/project.py contract
python tools/project.py build-firmware
python tools/project.py test-server
python tools/project.py run-server
python tools/project.py flash
python tools/project.py monitor
运行顺序可因课程服务器部署方式调整,但记录中必须保存:
- 固件提交标识;
- 服务器提交标识;
- EVENT-001版本;
- 开发板和硬件配置版本;
- 服务器环境;
- 测试时间;
- 实际事件标识。
7.8.3 执行四层核对
按下一次物理按键,检查同一事件在四处的身份:
| 位置 | 应核对 |
|---|---|
| 设备日志 | event_id、sequence、发送状态 |
| 网络/服务器响应 | 相同event_id、accepted状态 |
| 服务器记录 | 相同设备、事件类型和时间 |
| 用户页面 | 相同event_id对应的可见结果 |
若页面出现事件但标识不同,不能判定端到端验证通过。完整链路应能从用户结果追溯到设备事件和代码版本。
再执行失败路径:
- 使用无效测试凭据,确认服务器拒绝;
- 提交缺少字段的测试消息,确认契约错误;
- 暂停服务器后按键,确认设备不显示已确认;
- 恢复服务器,记录
v0.1当前是否支持重试。
如果v0.1尚不支持断线重试,在已知限制中如实记录,由第8章实现。
7.9 处理集成偏差
常见偏差包括:
字段名相同但含义不同
例如,设备把observed_at写成启动后的毫秒数,服务器却按带时区日期解析。此时应回到EVENT-001明确时间格式。违反契约的一方负责修正,服务器不得私自兼容未记录的格式。
成功响应被误判
设备只检查网络函数是否返回,没有检查HTTP状态和响应体。修正后区分“请求已发出”和“服务器已确认”。
开发分支使用了不同契约版本
使用:
git log --oneline -- contracts/event.schema.json
git diff main...HEAD -- contracts
确认分支是否基于当前已批准的契约。不要把两个版本字段简单拼接。
AI为了通过测试修改测试本身
检查测试变化是否降低验收标准。修复实现与修复错误测试是不同任务;AI若同时改变两者,必须逐项说明。
7.10 更新工程文档
v0.1完成后,README.md不再写“尚无可运行版本”。更新:
## 当前版本
v0.1:物理按键产生事件,服务器确认并在授权页面显示。
## 快速验证
1. 检查环境;
2. 启动服务器;
3. 构建并烧录固件;
4. 按下指定按键;
5. 核对设备日志、响应和页面中的event_id。
## 当前限制
- 输入仍为按键;
- 未接入正式传感器和模型;
- <实际验证发现的其他限制>。
同步更新:
docs/architecture/traceability.md中的实际实现位置;docs/architecture/budgets.md中的初次测量;CHANGELOG.md中的v0.1变化;- 验证记录中的版本和环境;
- 议题和里程碑状态。
任何性能数据都必须来自这次真实运行,并写明测量条件。
7.11 发布v0.1
7.11.1 发布前检查
[ ] main工作区干净并与远程同步
[ ] 契约检查通过
[ ] 固件构建通过
[ ] 服务器测试通过
[ ] 物理按键端到端验证通过
[ ] 失败路径有实际结果
[ ] README.md与当前行为一致
[ ] 没有真实凭据和个人数据
[ ] 已知限制已经记录
[ ] 里程碑中的阻塞议题已关闭
7.11.2 创建带注释标签
在确认的主分支提交上执行:
git switch main
git pull --ff-only
git status --short
git tag -a v0.1.0 -m "v0.1.0 minimal device-to-service loop"
git show v0.1.0
git push origin v0.1.0
带注释标签保存标签创建者、时间和说明,并固定指向发布提交。标签创建后不要移动;修正发布应使用新的版本。
GitLab发布记录至少包含:
- 版本目标;
- 包含的功能;
- 验证环境和结果摘要;
- 固件、服务器和契约版本;
- 已知限制;
- 后续
v0.2计划。
7.12 本章小结
本章用真实按键建立了智能感知终端的第一个端到端版本。按键输入经过设备、契约、网络、服务器和用户显示等边界。这条可运行链路为接入传感器和模型提供了基线。
AI分别参与设备、服务器和用户端议题的实施。Git契约、分支、差异和合并请求控制AI的范围。验证证据说明变化是否合格。v0.1标签固定了第一条可复现的产品基线。
第8章将在保持EVENT-001和用户反馈链路稳定的前提下,接入真实传感器。数据窗口、上传、缓存和数据回放验证完成后,项目发布v0.2。
7.13 综合实践
- 根据本组产品结果,设计一条最短端到端路径。列出保留组件、临时替代组件和本版本明确排除的内容。
- 把这条路径拆成有依赖关系的议题,形成任务图。说明哪些任务可以并行,哪些接口必须先合并。
- 选择一个设备事件,实现或完善成功路径和一条失败路径。使用设备、服务器、用户端和Git证据说明结果。
- 为重复事件、无效凭据或网络中断设计处理规则。形成契约、测试样例和恢复行为,不以单次成功代替完整验证。
- 汇总合并请求、验证记录和已知限制,形成本组
v0.1.0发布说明与可复现入口。