wenxiangchao
4 天以前 2e889843b4c171f2d6ad8e0d53084040703d11f6
docs: CAN 总线级应答器 + 硬件调试页多标签多会话 设计规格
1个文件已添加
93 ■■■■■ 已修改文件
docs/superpowers/specs/2026-11-09-can-responder-multidebug-design.md 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/superpowers/specs/2026-11-09-can-responder-multidebug-design.md
New file
@@ -0,0 +1,93 @@
# CAN 全局应答器 + 硬件调试页多设备并发 — 设计规格
日期:2026-11-09
状态:已与用户逐节确认
关联基线:CAN 模拟器 TCP 服务化 + 统一 `CanDeviceDriver`(已完成,47 单测通过)
## 1. 需求
两个独立特性:
- **特性一(应答器)**:当前"自动响应"仅在"有第二个节点且该节点开了自动响应"时才回复。
  用户需要:客户端单独发一条请求,模拟器就能自动回复,无需第二节点。
- **特性二(多设备并发调试)**:硬件调试页(DeviceDebug)当前单设备单会话,
  用户需要同时在多个 Tab 里调试多个 CAN 设备,各自独立会话与维护租约。
## 2. 已确认决策
| 点 | 结论 |
|---|---|
| 应答器语义 | 总线级开关,把发送帧回显给发送者(回波),无需第二节点 |
| 应答内容 | 回显同帧(相同帧ID+数据),来源标记 `RESPONDER` |
| 应答器开关入口 | 服务窗口每总线一个复选框;并支持命令 `BUS_ECHO:ON/OFF` |
| 与节点自动响应关系 | 二者独立并存,互不触发、互不干扰,用户按需开启 |
| 多设备形态 | DeviceDebug 改为 TabControl,每 Tab 一个 `DeviceDebugTab`,各自独立会话 |
| 一会一设备 | 设备下拉只列未打开设备;已打开则聚焦其 Tab,不重复开会话 |
| 会话生命周期 | 加入→OpenSession(ReadOnly);关 Tab→CloseSession(经 token 缓存复用正常释放维护租约) |
| 设备范围 | 通用(CAN/电源/PLC 同结构),主场景多个 CAN;复用 `LoadPanel(kind)` |
## 3. 特性一设计:总线级应答器
### 3.1 CanBusSimulator
- 新增 `public bool EchoResponderToSender { get; set; }`(默认 false)。
- `OnNodeFrameSent` 路由末尾:若 `EchoResponderToSender` 且源节点存在,
  复制该帧投递回**源节点自身**(`sourceNode.ReceiveFrame(echoCopy)`,直接调用绕过"不回源"),
  回显帧 `SourceNodeId = "RESPONDER"`,`Timestamp = now`,`FrameId/Data` 同原帧。
- 与现有 `SendAutoResponse`(接收节点回复别人)互不影响:应答器面向"发送者回环",
  节点自动响应面向"接收者答复"。
### 3.2 命令与 UI
- `CanCommandExecutor` 增加 `BUS_ECHO` 查询:`BUS_ECHO:ON|OFF` 设置 `_bus.EchoResponderToSender`;
  裸 `BUS_ECHO` 查询当前状态,返回 `OK:BUS_ECHO:ON|OFF`。
- 服务窗口 `MainWindow` 每总线节点行/工具区加"应答器"复选框,绑定 `CanBusSimulator.EchoResponderToSender`。
- 传输/驱动层零改动:命令走既有 `QUERY:` 通道(Local 直发 / Broker 经 RPC 到宿主再转 TCP)。
### 3.3 数据流
```
客户端 SEND:0x100:AA → 服务端路由 → (应答器开) 回显帧 EV:RECV:0x100:AA:...:RESPONDER
   → 同一 TCP 连接推送回发送节点 → 客户端 DriverFrameReceived 上屏(来源 RESPONDER)
```
无额外轮询;与节点自动响应同用一路事件模型,不产生重复行(来源不同)。
## 4. 特性二设计:DeviceDebug 多标签多会话
### 4.1 结构
```
DeviceDebug(壳)
 ├─ 顶栏:设备下拉(仅未打开设备) + [加入调试] + [关闭当前页]
 └─ TabControl
     └─ DeviceDebugTab × N(新 UserControl,自带独立会话)
          · 绑定自身 CurrentDeviceId / ConnectionState / SessionState / Reservation / CanEnter|ExitMaintenance
          · panelContainer:LoadPanel(kind)(CAN→CanDebugPanel 壳;PowerSupply;PLC)
          · 自身操作日志
          · 进入/退出维护只作用于本设备会话(sessionId 复用 token,逻辑沿用 DebugSessionManager)
```
### 4.2 会话与租约
- `DebugSessionManager` **不改**:`_activeSessions` 已按 sessionId、`GetActiveSessionByDevice`
  按设备支持多设备并发会话。
- 每 `DeviceDebugTab` 持有一个 sessionId,调用 `OpenSession/RequestMaintenance/ActivateMaintenance/
  CloseSession`,各自资源键为自身 deviceId → 不同设备租约天然独立、无冲突。
- 关闭 Tab 必须 `CloseSession`(正常释放维护租约,杜绝 Token 泄漏型"第二次进维护失败")。
- 同设备不可能出现两个 Tab(下拉过滤 + 聚焦既有 Tab 拦截)。
### 4.3 迁移与影响面
- `DeviceDebug.xaml`:把现"信息栏+维护按钮+panelContainer+操作日志"整体移入 `DeviceDebugTab`;
  DeviceDebug 顶层改为 顶栏 + TabControl。
- `DeviceDebug.xaml.cs`:拆分——壳负责设备列表/加入/关闭 Tab 路由;
  原单会话状态机方法迁入 `DeviceDebugTab.xaml.cs`(每个 Tab 一个实例)。
- 对外契约(插件入口 `InitFinish`、`ForceRefreshForTest`)保留在壳上,行为不变。
- 现有 CAN 通道事件接收、应答器、Broker 代理均复用,无回退。
## 5. 测试策略
- 单测(应答器):`SimulatorServerCoreTests` 增例——开 `BUS_ECHO:ON` 后单节点 `SEND` 收到
  来源 `RESPONDER` 的回显;关后无回显;与节点自动响应并存时来源区分正确、无重复帧。
- 单测(命令):`BUS_ECHO:ON/OFF/查询` 走通 `CanCommandExecutor`;Broker 经 RPC 亦生效。
- DeviceDebug 多会话为 UI 重构,以 MSBuild 编译 + 手动验收覆盖:
  同时开 CAN-001/002/003 三个 Tab,各自进入维护互不阻塞;关闭某 Tab 后其设备可再次进入维护
  (证明租约正确释放);应答器开启后任一 Tab 单独发帧即收到回显 RX。
- 回归:现有 47 单测保持全绿。
## 6. 构建与约束
- 沿用 VS 完整版 MSBuild 编译 WPF 链路(不加 /p:Platform=x64);
  服务端核心可用 dotnet。
- 不引入新外部依赖;应答器仅在 CanSimulator 库与执行器内扩展,Broker/驱动层不动。