teemow/planscope

GitHub: teemow/planscope

planscope 是一个用于 CAREL pLAN 总线的交互式 TUI 协议终端与离线分析工具,配合 ESP32 网桥实现实时帧捕获、解码、屏幕渲染和按键注入。

Stars: 0 | Forks: 0

# planscope 一个用于 CAREL pLAN 总线的交互式 **pLAN 终端与协议调试器** (pCO/µPC 控制器 + pGD 显示终端)—— 它是 [planterm](https://github.com/teemow/planterm)(开源的 pLAN 终端协议库)的配套工具。 planscope 通过 **PLANCAP**(网桥的经过身份验证的捕获与控制协议,TCP 6054,Noise 加密)与位于 pLAN 上的 ESP32 网桥(运行基于 planterm 的捕获/终端固件的设备)进行通信。它会流式传输总线上的每一帧数据,在 bit9 地址标记(`20'`)处将突发数据拆分为真实的数据帧,验证两种校验和语法(加至 `0xFF` 的经典帧,以及 `0x64/0x65/0x66` 图形/会话帧上的 CRC-16/Modbus 小端序),解码并分类所有内容 —— 并且通过同一连接**回传按键**,因此你的终端会充当一台内置协议分析器的第二个 pGD。 协议本身记录在 [planterm 协议参考](https://github.com/teemow/planterm/blob/main/docs/protocol.md)中; `planscope protocol` 会在实时视图旁边打印其精简版本。 planscope 是从一个私有的热泵逆向工程项目中提取出来的,并已在一个 µPC + pGD 安装环境中通过验证;非常欢迎提供其他 pLAN 硬件的报告。 **使用实时功能你需要什么** —— 一个支持 [PLANCAP](https://github.com/teemow/planterm/blob/main/docs/capture-protocol.md) 的网桥设备: 位于 pLAN 的 RS-485 总线上的 ESP32(或类似设备),运行提供捕获与控制 socket 的固件,详见下文的 [“设备端”](#the-device-side-what-the-firmware-must-provide)。 受支持的固件是 planterm 的 [`plan_bridge` ESPHome 组件](https://github.com/teemow/planterm/tree/main/esphome/components/plan_bridge) (可作为 ESPHome 外部组件使用;如果你想自己构建,planterm 也提供了协议引擎和会话代码)。 **如果没有网桥设备**,planscope 仍然可以作为纯粹的离线分析器处理 hex 捕获文件(即“离线捕获分析”下的所有功能)。 ## 视图 | 按键 | 视图 | 显示内容 | |-----|-----------|-------| | `1` | screen | 实时渲染的 pGD 显示屏(文本行帧) | | `2` | frames | 线路上的每一帧,已解码并附有描述 | | `3` | timeline | 每个类别的每间隔流量计数 + 校验失败次数 | | `4` | addresses | 每个地址的流量、失败情况以及每个地址的身份 | | `5` | errors | 校验失败帧的原始数据 —— 客观的数据干扰检测器 | ## 终端按键 | 按键 | 操作 | |-----|--------| | `↑` `↓` | pGD UP / DOWN | | `Enter` | pGD ENTER | | `Esc` | pGD ESC | | `p` | pGD PRG | | `a` | pGD ALARM | | `w` | arm:写入使能 **+ 注册为终端 31**(PLANCAP 命令) | | `1`-`5` | 切换视图 | | `q` | 退出 | 按键注入是**受控的**:网桥在启动时处于 disarm 状态,在 `w` 对其进行 arm 之前,它会丢弃注入的按键。Arming 还会**将网关注册为 pLAN 终端 31** —— 按键会在终端 31 自己的轮询时隙中注入,该时隙仅在注册时存在。注册后,控制器会将其键盘轮询时隙停靠在 31 上,因此物理 pGD 的键盘将失效,其显示屏也只会偶尔得到服务(偶尔出现的“无链接”闪烁是注册后的固有现象)。 使用 `w` 进行 Disarming 仅会取消写入门控 —— **注册状态会在整个会话期间保持**,并在退出(`q`)时仅释放一次,因为每次离开都会消耗一次网络重建(FF-walk + pGD 重新识别 + 重绘)。**务必使用退出而不是强杀** TUI,以便 pGD 能恢复其轮询时隙。planscope 在每次(重新)连接后都假定处于 disarm 状态,这只会阻止传输 —— 绝不会引发传输,并且会在设备重启时自动重新连接。 ## 安装 ``` go install github.com/teemow/planscope@latest ``` 或克隆并运行 `go build .` ## 用法 只需在 `~/.config/planscope/config` 中放入一次设备地址和 API 密钥: ``` device = 192.0.2.10 key = "" ``` 然后只需运行: ``` planscope ``` `key` 是设备的 `api.encryption.key`(base64 格式,来自设备 YAML)—— 一个密钥可同时保护 ESPHome API 和 PLANCAP socket;对于无密钥设备则省略它。所有内容也都可以通过命令行提供(标志的优先级高于 `PLANSCOPE_KEY` 环境变量,而后者的优先级又高于配置文件): ``` planscope --device 192.0.2.10 --key '' planscope --config /path/to/other/config ``` ## 无头设备控制 ``` planscope log > run.log # tee the capture stream to a file: # bus bytes, device events, diagnostics # (host [HH:MM:SS.mmm] timestamps added) planscope call set_tx_mode 2 # one-shot ESPHome service call planscope call set_turnaround 420 ``` `call` 是唯一**不**使用 PLANCAP 的命令:它会在暴露额外调节参数的设备上,通过 ESPHome 原生 API(TCP 6053)调用任意的用户自定义服务。`true`/`false` 编码为布尔值,其他所有内容编码为整数。调用后的 ping 往返确认了设备已处理该请求。其他所有操作 —— 数据、arm/enroll、按键注入、诊断 —— 均通过 PLANCAP socket 进行。 设备捕获流是**单客户端且最新连接优先**的:并行的 `planscope log`(或第二个 TUI)会抢占流并使正在运行的会话致盲。一次只能运行一个 planscope 实时实例。 菜单导航宏(命名配置值的事务性 get/set、抓取路由、报警历史转储)**不**属于此工具的一部分:它们需要针对具体的控制器*应用*提供路由和字段提取器注册表,而这些是特定于安装环境的数据。它们运行的通用会话引擎(enroll/arm 编排、TX 确认的按键、稳定状态检测、已验证的页面身份)包含在 `internal/device` 中。 ## 离线捕获分析 通过管道输入可分析已保存的捕获文件而不是实时设备,并在 EOF 处打印完整报告 —— 屏幕、时间线、按地址划分的表以及每一个失败帧的原始内容: ``` planscope < capture.log planscope --bucket 60 --from 14:00:00 --to 14:30:00 < capture.log planscope report --bucket 60 capture.log # same report, as a subcommand ``` 促成协议理解的语法工作台作为子命令保留了下来。它们接受原始的 hex 捕获或带有 bit9 标记的捕获行,支持文件或标准输入: ``` planscope analyze idle.log # infer lengths, checksum, length field planscope regroup idle.log # re-split merged frames via checksum planscope correlate idle.log keys.log # diff two captures -> event frames planscope records idle.log # 20 0C field records per selector planscope screen idle.log # reconstruct the pGD text screen planscope bursts navigate.log # find redraw bursts per selector planscope help # all subcommands and their flags ``` ## 设备端(固件必须提供的内容) 网桥在 TCP 6054(ESPHome API 端口 + 1)上提供 **PLANCAP**;规范的传输规范位于 [planterm 的 capture-protocol.md](https://github.com/teemow/planterm/blob/main/docs/capture-protocol.md)。 简而言之: - **单个经过身份验证的连接承载一切。** 在发送 7 字节的 banner `PLANCAP` 之后,客户端 (planscope) 会运行 Noise `NNpsk0_25519_ChaChaPoly_SHA256` 握手 —— 与 ESPHome 原生 API 使用相同的套件、相同的预共享密钥(`api.encryption.key`),但 prologue 不同 —— 其后的每一帧都是一个加密记录。未配置密钥的设备将回退到明文模式。 - **服务端 → 客户端记录:** 原始总线字节(包含序列号、带内 ISR 丢包计数器,以及会话开始时的屏幕快照重放,以便客户端永远不会从空白开始)、类型化的设备事件(状态真值、链路加入、TX 触发 / 按键已接受、walker 保持)以及诊断(网桥中与 plan 相关的说明文本作为类型化记录,与它们所引用的总线字节按顺序排列)。 - **客户端 → 服务端记录:** 命令 —— arm/disarm、enroll/disenroll、注入按键 —— 每一个均由服务端进行 ack 确认。planscope 的实时功能**不需要任何其他通道**;没有任何内容会订阅设备日志流(logger 在负载下可能会丢失日志行,因此没有任何携带数据的传输会通过那里)。 - 该流是单客户端的:新连接会替换旧连接。 客户端将字节记录作为文本捕获行重新输出 (`[#seq| N] 20' 01 01 DD ...` —— 每字节两个 hex 数字,带有撇号后缀表示携带了第 9 位的字节),这也是所有离线子命令使用的格式;设备事件和诊断在同一信息流上变为 `[plan_evt]`/`[plan_diag]` 行。 **安全性:** 双向传输均使用 PSK 进行加密和身份验证 —— 被动观察者只能获知时间和大小,而无法获知内容(显示屏在输入时会显示服务 PIN);没有密钥的主动攻击者无法注入任何内容。除了密钥之外没有其他身份标识。 无密钥的明文模式不具备这些属性:仅限在受信任的网络上运行无密钥网桥,或者更好的是,配置一个密钥。 ## 代码布局 一个精简的 `main.go` 会分发到 `internal/`: | 包 | 职责 | |---------|----------------| | `internal/plan` | 协议核心:行/帧解析、两种校验和语法、屏幕重建、流量分析 | | `internal/esphome` | PLANCAP 捕获与控制客户端(TCP 6054,Noise/明文)+ 用于 `planscope call` 的最小化 ESPHome API 客户端 | | `internal/device` | 有状态的无头会话:arm/enroll 编排、TX 确认的按键、稳定状态检测 | | `internal/decode` | 离线帧语法工作台 | | `internal/tui` | 交互式终端 UI(视图、pGD 渲染、键盘) | | `internal/cli` | 命令表:分发、统一帮助、共享设备标志、配置 | | `internal/style` | ANSI 样式辅助工具 | 依赖关系严格自上而下流动:`cli` → `tui`/`device`/`decode` → `esphome`/`plan` → `style`。每个包都自带测试: ``` go test ./... ``` ## 许可证 MIT —— 见 [LICENSE](LICENSE)。
标签:CAREL, EVTX分析, pLAN协议, RS-485, TUI, 协议调试器, 工业控制, 日志审计, 物联网