# 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/驱动层不动。