|
| 1 | +# Lemon串口监控无串口设备启动设计 |
| 2 | + |
| 3 | +## 背景与根因 |
| 4 | + |
| 5 | +2026-07-15 16:05 的真实开机验证中,`CommMonitorService` 被服务控制管理器按自动启动方式拉起,但在 45 秒内未进入运行状态。系统事件记录了 7009、7000 和 .NET Runtime 1026;未处理异常来自 `CaptureAuthority.EnsureStartupReconciledAsync`,原因是当时没有连接串口设备,内核过滤驱动尚未创建设备控制端点。 |
| 6 | + |
| 7 | +这是服务启动容错缺陷,不是安装文件缺失。驱动按 PnP 设备栈加载;没有匹配设备时控制端点暂时不存在是允许发生的正常状态。现有服务把 `DRIVER_UNAVAILABLE` 当作宿主启动致命错误,导致界面和 AI 接口也无法使用。 |
| 8 | + |
| 9 | +## 目标 |
| 10 | + |
| 11 | +- 没有连接任何串口设备时,Windows 服务仍须在 SCM 超时前进入 Running。 |
| 12 | +- AI 管道和界面控制管道须可用,并能报告驱动暂不可用。 |
| 13 | +- 串口设备随后出现时,不需要重启服务或计算机;下一次状态检查或捕获操作须重新尝试驱动初始化。 |
| 14 | +- 无法确认内核捕获状态时不得开始新的捕获,不得把未知状态当成已停止。 |
| 15 | +- 仅 `DRIVER_UNAVAILABLE` 可降级继续启动;取消、存储损坏、配置错误和其他异常必须继续阻止服务启动。 |
| 16 | + |
| 17 | +## 方案比较 |
| 18 | + |
| 19 | +### A. 在宿主启动边界容忍驱动暂不可用(采用) |
| 20 | + |
| 21 | +增加一个可测试的启动协调组件。它调用 `CaptureAuthority.InitializeAsync`,只捕获错误码为 `DRIVER_UNAVAILABLE` 的 `CaptureLeaseException`,记录警告后允许宿主继续启动。`CaptureAuthority` 本身的安全语义不变,因此后续每个捕获变更仍会再次执行启动协调;设备出现后即可成功,设备仍不存在时继续返回结构化错误。 |
| 22 | + |
| 23 | +优点是改动边界清晰,保留内核状态安全约束,且无需后台轮询。缺点是恢复发生在下一次调用时,而不是设备插入瞬间;这符合用户实际操作流程。 |
| 24 | + |
| 25 | +### B. 把未知驱动状态视为核心初始化成功(不采用) |
| 26 | + |
| 27 | +让 `CaptureAuthority.InitializeAsync` 在统计未知时直接返回。虽然代码更少,但会使 `_startupReconciled` 与实际未知内核状态混淆,后续捕获操作可能绕过安全检查。 |
| 28 | + |
| 29 | +### C. 增加常驻设备轮询器(不采用) |
| 30 | + |
| 31 | +后台按周期重试驱动端点。它可以主动恢复状态,但会增加定时器、退避、停止时取消和日志抑制等生命周期复杂度;当前按需重试已经满足需求。 |
| 32 | + |
| 33 | +## 架构与数据流 |
| 34 | + |
| 35 | +1. `Program.cs` 构建依赖注入容器和宿主。 |
| 36 | +2. 新的启动协调组件在 `host.RunAsync` 前调用 `CaptureAuthority.InitializeAsync`。 |
| 37 | +3. 初始化成功时正常继续。 |
| 38 | +4. 初始化返回 `DRIVER_UNAVAILABLE` 时记录降级警告,继续启动 Windows 服务、界面管道和 AI 管道。 |
| 39 | +5. 其他异常原样向上传播,服务启动失败并留下可诊断事件。 |
| 40 | +6. 后续界面或 AI 发起捕获时,现有 `EnsureStartupReconciledAsync` 因尚未设置 `_startupReconciled` 而再次读取驱动状态: |
| 41 | + - 设备已连接:完成协调并允许捕获; |
| 42 | + - 设备仍未连接:返回 `DRIVER_UNAVAILABLE`,不创建捕获会话。 |
| 43 | + |
| 44 | +## 错误与日志 |
| 45 | + |
| 46 | +- 降级日志必须包含驱动暂不可用的原因,并说明服务会保持运行等待设备。 |
| 47 | +- 不吞掉 `OperationCanceledException`。 |
| 48 | +- 不吞掉非 `DRIVER_UNAVAILABLE` 的 `CaptureLeaseException` 或普通异常。 |
| 49 | +- 不改变驱动服务启动类型、PnP 绑定、3010 重启和安装/卸载事务。 |
| 50 | + |
| 51 | +## 测试与验收 |
| 52 | + |
| 53 | +自动化测试先失败后实现: |
| 54 | + |
| 55 | +- 启动协调遇到 `DRIVER_UNAVAILABLE` 时完成而不抛出,宿主可继续启动。 |
| 56 | +- 第一次驱动不可用后,状态源变为可用,再次执行核心协调能够成功,证明恢复无需重启。 |
| 57 | +- 取消或非预期异常仍向上传播。 |
| 58 | +- 运行全部服务测试、全部托管测试和安装器契约测试。 |
| 59 | + |
| 60 | +真实系统验收: |
| 61 | + |
| 62 | +- 不连接串口设备,重启后 `CommMonitorService` 为 Running,SCM 不再产生该服务的 7009/7000。 |
| 63 | +- AI `ping` 和 MCP smoke 在无串口设备时可用。 |
| 64 | +- 若当前有串口设备,则验证插入后端口枚举和捕获;若没有设备,则明确记录硬件验收未执行,不伪造结果。 |
| 65 | + |
| 66 | +## 非目标 |
| 67 | + |
| 68 | +- 不增加设备插拔通知服务或周期轮询。 |
| 69 | +- 不改变捕获协议、会话格式、AI 工具契约或公开产品名。 |
| 70 | +- 不把未知驱动状态假定为已停止。 |
0 commit comments