# 协议架构说明 ## 1. 总体边界 小程序按“链路、协议、领域模型、功能服务、页面”分层。页面只做展示和事件转发;协议层只处理帧格式和响应解析;功能服务负责把协议动作组合成业务流程;领域模型负责参数组、结构体、数值编解码等纯数据逻辑。 ```text BLE 透传链路 transport/ble-core.js transport/ble-utils.js transport/ble-logs.js transport/ble-device-registry.js transport/protocol-helper-registry.js 协议层 protocols/modbus-rtu/index.js protocols/storage-access/index.js protocols/bootloader/index.js protocols/transport-helpers.js 领域模型 domain/parameter-groups/ domain/storage-access/code-info-parser.js 功能服务 features/communication/ features/modbus-rtu/service.js features/storage-access/service.js features/parameter-groups/ features/bootloader/ features/tools/ features/settings/ features/home/service.js 页面 pages/home/ pages/communication/ pages/params/ pages/settings/ ``` 维护原则: 1. 一个协议只保留一个协议入口文件,不再拆 `frame.js`、`request.js`、`response.js` 这类细粒度文件。 2. 功能服务按页面或业务域聚合,不为每个按钮、每个弹窗再单独建模块。 3. 领域模型可以按“值类型、编解码、结构体解析、参数组规范化”拆分,因为这些逻辑可测试且复用度高。 4. 页面不直接拼协议帧,不直接读写本地存储格式。 5. 设置页的协议模式只保留当前字段 `protocolMode`,不维护历史字段迁移逻辑。 ## 2. 协议模式 协议模式常量由 `domain/protocol-mode.js` 统一定义,当前设置值由 `store/settings-store.js` 持久化维护: | key | 页面名称 | 通讯页显示 | 参数页数据 | |---|---|---|---| | `none` | 无协议 | 串口发送卡片 + 日志 | 不显示协议参数组 | | `storage-access` | 存储访问 | 同步、CodeInfo、特殊指令、读写卡片 + 日志 | 存储访问结构体组/单变量组 | | `modbus-rtu` | 标准 Modbus | 标准 Modbus 指令卡片 + 日志 | Modbus 寄存器组 | 参数组按协议分别存储,切换协议时 `features/parameter-groups/store.js` 会切换当前参数组集合。自动读取/轮询运行时如果检测到协议切换,会停止当前协议的循环,避免用错误协议继续读写。 ## 3. 传输层 目录:`transport/` 职责: - `ble-core.js`:蓝牙扫描、连接、通知订阅、发送队列、响应等待、收发日志、页面订阅。 - `ble-utils.js`:BLE 设备识别、UUID 判断、包长推断、HEX/ArrayBuffer 转换、错误文案。 - `ble-logs.js`:收发日志构造、裁剪和清空。 - `ble-device-registry.js`:扫描设备合并、排序、连接标记和 RSSI 文案。 - `protocol-helper-registry.js`:协议 helper 的懒加载和响应读取函数归一化。 传输层不知道业务含义,只通过 `protocols/transport-helpers.js` 调用当前协议的响应解析能力。 ## 4. 协议层 ### 4.1 标准 Modbus RTU 入口:`protocols/modbus-rtu/index.js` 职责: - 生成标准 Modbus RTU 请求帧。 - 校验 Modbus CRC。 - 解析标准 Modbus 响应和异常响应。 - 计算单帧包长限制下的读取分片。 - 支持功能码 `0x01`、`0x02`、`0x03`、`0x04`、`0x05`、`0x06`、`0x10`。 - 地址和数量按标准 Modbus 寄存器/线圈语义处理。 - 不直接读取设置页从机地址,不直接调用 BLE 发送。 标准 Modbus 不解析 CodeInfo TLV 信息块,也不使用存储访问协议的 `CMD/AREA`。 ### 4.2 存储访问协议 入口:`protocols/storage-access/index.js` 职责: - 普通内存读写使用 `CMD + ADDR + LEN + DATA + CRC16-CCITT-FALSE`。 - `LEN` 固定 16 位,单位始终为字节。 - `CMD bit0~bit2` 区分地址模式或区域:`0x07` 为 32 位地址,`0x01..0x04` 为 DATA/IDATA/XDATA/CODE 的 16 位地址,`0x00/0x05/0x06` 保留。 - `CMD bit3` 为读写位,`bit4/bit5` 暂时保留,`bit7` 为异常标志。 - 普通区域包括 `DATA`、`IDATA`、`XDATA`、`CODE`,其中 `CODE` 只读。 - 特殊指令使用 `CMD=0x4F`,用于复位、启动、停止、控制参考值和 CodeInfo 描述符读取。 - CodeInfo 同步只使用特殊指令 `OP=0x05` 获取 `CODE_ADDR32 + CODE_LEN16 + ADDR_WIDTH8 + MEMORY_ENDIAN16 + MAX_PACKET16`,再按 bootstrap 声明的地址宽度、目标内存变量字节序和包长读取完整信息块。 - 协议控制字段始终固定大端;结构体字段和单独变量等目标内存值按 bootstrap `MEMORY_ENDIAN` 编解码。 - 不直接调用 BLE 发送,不负责弹窗;发包编排由 `features/storage-access/service.js` 处理。 完整帧格式和从机实现参考见 `存储访问协议.md`。 ### 4.3 Bootloader 协议入口:`protocols/bootloader/index.js` 功能服务:`features/bootloader/` 职责: - 保持独立升级协议,不依赖标准 Modbus 或存储访问协议。 - `protocols/bootloader/index.js` 负责 Bootloader 帧构建、CRC、ACK/NAK 和响应解析。 - `features/bootloader/service.js` 负责设置页升级状态、固件加载、握手、擦除、编程、校验流程。 - `features/bootloader/firmware.js` 负责芯片型号识别、Flash 容量推断、固件大小校验和升级地址布局。 - `features/bootloader/transport.js` 负责 Bootloader 原始帧发送、响应等待、断连中止和超时处理。 ## 5. 领域模型 ### 5.1 参数组模型 目录:`domain/parameter-groups/` 职责: - 定义参数组、寄存器、结构体字段、单变量的标准数据结构。 - 规范化地址、数量、读写状态、显示值和来源元数据。 - 支持标准 Modbus 寄存器/线圈组。 - 支持存储访问字节地址组、结构体字段、单变量、数组、bit field。 - 支持原始值到实际值的转换公式。 当前模块: | 文件 | 职责 | |---|---| | `constants.js` | 寄存器类型、数据类型、布局类型、地址上限和导入导出字段白名单 | | `model.js` | 参数组/寄存器规范化、地址布局、显示文案、导入克隆和领域 API | | `register-io.js` | 读缓存解码、写入编码、组级字节/字编码和分片辅助 | | `value-types.js` | 数据类型元数据、字节/字长度、bit field 长度、类型分类 | | `value-number.js` | 整数、HEX、float 的解析、范围校验和格式化 | | `value-text.js` | ASCII/UTF-8 文本字段的编码和解码 | | `value-codec.js` | 寄存器值编解码、显示值和输入校验 | | `value-formula.js` | 读回标幺值到实际值的转换公式求值 | | `struct-parser.js` | C 结构体与 enum 定义解析入口 | | `struct-c-syntax.js` | C 注释剥离、typedef、struct、enum 和 declarator 解析 | | `struct-layout.js` | 结构体字段布局、数组展开、ASCII 字段、enum 映射和 bit field 展开 | ### 5.2 CodeInfo 解析 入口:`domain/storage-access/code-info-parser.js` 职责: - 解析 CodeInfo 纯 TLV 信息块,未知 TLV 类型跳过。 - 使用同步 bootstrap 返回的 `CODE_LEN`、`ADDR_WIDTH`、`MEMORY_ENDIAN` 和 `MAX_PACKET`;`CODE_LEN` 决定 CodeInfo 总长度,`ADDR_WIDTH` 只决定读取 CodeInfo 信息块本体时的地址宽度,内存入口地址宽度由 TLV `TYPE` 自描述,`MEMORY_ENDIAN` 进入参数组上下文用于变量值编解码。 - 解析 UTF-8/ASCII 电机型号、芯片型号和转换相关可选 TLV 参数。 - 按 `mem_type` 映射真实存储区域。 - 按 TLV `TYPE=0x20/0x21/0x28/0x29` 区分 16/32 位地址结构体实例和单独变量。 - 生成参数组初始结构,结构体未导入定义时按字节占位,单独变量按 1/2/4 字节推断为 `uint8_t`、`uint16_t`、`uint32_t`。 ## 6. 功能服务 ### 6.1 通讯页服务 目录:`features/communication/` 当前模块: | 文件 | 职责 | |---|---| | `index.js` | 通讯页功能聚合入口,显式导出页面需要的 API | | `manual-rtu.js` | 标准 Modbus 指令表单、写多个寄存器输入、帧生成、发送和响应文案 | | `service.js` | 串口原始发送、存储访问普通读写、特殊指令下发 | | `view-model.js` | 日志展示、串口/存储访问表单状态、协议模式展示状态 | 无协议模式只显示串口发送和日志;标准 Modbus 模式只显示 Modbus 指令;存储访问模式只显示存储访问卡片。 ### 6.2 存储访问服务 入口:`features/storage-access/service.js` 职责: - 包装 `protocols/storage-access/index.js` 的读写、特殊指令和 CodeInfo 同步能力。 - 根据设置的最大包长决定分片长度。 - 执行 `OP=0x05` 描述符读取,再用 `MODE=0x07` 或 `MODE=0x04 CODE` 完整读取 CodeInfo。 - 将解析结果转换为参数组。 - 提供复位、启动、停止、控制参考值等特殊指令。 ### 6.3 标准 Modbus 服务 入口:`features/modbus-rtu/service.js` 职责: - 包装 `protocols/modbus-rtu/index.js` 的帧生成、响应期望和分片能力。 - 读取设置页从机地址,并把地址错误统一转成浮层警告。 - 通过 BLE 发送标准 Modbus 读写命令。 - 聚合分片读取结果,供参数组读取、写入和自动读取复用。 ### 6.4 参数组服务 目录:`features/parameter-groups/` 当前模块: | 文件 | 职责 | |---|---| | `index.js` | 参数页功能聚合入口,显式导出页面需要的 API | | `service.js` | 参数组页面主 API、导入导出、结构体补全、读写调度 | | `store.js` | 参数组状态容器、协议切换、本地持久化、CodeInfo 卡片状态 | | `persistence.js` | 参数组 JSON 导入、导出和按协议本地存储 | | `imports.js` | 导入合并、重复项识别、结构体/enum 定义补全和已有结构保留 | | `io.js` | 标准 Modbus 和存储访问协议的参数组读写实现 | | `poller.js` | 按设置自动读取当前协议的可读参数组 | | `view-model.js` | 参数页展示态和弹窗状态 | | `drag-view-model.js` | 参数页寄存器拖拽排序状态计算 | | `dialog-handlers.js` | 新建/编辑参数组、寄存器信息弹窗、结构体解析交互 | ### 6.5 设置与工具服务 目录:`features/settings/`、`features/tools/` 职责: - 设置页协议模式、主题、蓝牙状态、Bootloader 状态和工具页面状态聚合。 - `protocol-implementation.js` 提供存储访问协议实现说明卡片和文件占位;从机源码暂未提供。 - `features/tools/index.js` 聚合工具入口、工具导航、CRC/哈希、滤波、阻抗、贴片码、制冷、三相功率等工具状态和事件处理器。 - 工具内部按工具域拆分,避免设置页脚本膨胀。 ## 7. 页面职责 ### 7.1 首页 目录:`pages/home/` 职责:蓝牙扫描、连接、断开、设备列表展示。 ### 7.2 通讯页 目录:`pages/communication/` 职责:根据协议模式显示对应通讯卡片和收发日志。 ### 7.3 参数页 目录:`pages/params/` 职责:展示参数组、读取、写入、导入导出、结构体补全、自动读取和拖拽排序。 ### 7.4 设置页 目录:`pages/settings/` 职责:协议模式、从机地址、自动读取间隔、最大包长、协议实现说明、Bootloader 和工具入口。 ## 8. 数据流 ### 8.1 存储访问同步 ```text 用户点击同步 -> pages/communication/communication.js -> features/parameter-groups/service.syncFromStorageAccessCodeInfo -> features/storage-access/service.syncCodeInfo -> features/storage-access/service.readCodeInfoBlock -> protocols/storage-access/index.js 构建特殊指令和普通读帧 -> 特殊指令 OP=0x05 读取 CODE_ADDR32 + CODE_LEN16 + ADDR_WIDTH8 + MEMORY_ENDIAN16 + MAX_PACKET16 -> 按 ADDR_WIDTH/MAX_PACKET 分片读取 CodeInfo TLV 信息块,保存 MEMORY_ENDIAN 到参数组上下文 -> domain/storage-access/code-info-parser.parseCodeInfo -> domain/storage-access/code-info-parser.createGroupsFromCodeInfo -> features/parameter-groups/imports.mergeImportedGroups -> features/parameter-groups/store.setGroups ``` ### 8.2 参数组读取 ```text 手动读取或自动读取 -> pages/params/params.js -> features/parameter-groups/service.readGroup -> features/parameter-groups/io.readGroup -> 标准 Modbus: protocols/modbus-rtu/index.js -> 存储访问: features/storage-access/service.readMemory -> 读缓存映射回参数组显示态 ``` ### 8.3 参数组写入 ```text 输入完成或点击写入 -> pages/params/params.js -> features/parameter-groups/service.writeRegister/writeGroup -> features/parameter-groups/io.writeRegister/writeGroup -> 标准 Modbus: 单寄存器/多寄存器写入 -> 存储访问: 字节地址写入,bit field 使用读改写 -> 写入快照映射回参数组显示态 ``` ## 9. 维护约束 1. 不再按单个按钮、单个协议动作、单个导入步骤拆零碎模块,优先收敛到当前 feature service 和领域模型文件。 2. 新增协议能力优先放入对应 `protocols/*/index.js` 和对应 feature service,不直接写进页面。 3. 新增参数值类型时,同时更新 `constants.js`、`value-types.js`、`value-codec.js` 和必要的导入导出字段。 4. 新增 CodeInfo 字段时,同时更新 `domain/storage-access/code-info-parser.js` 和 `存储访问协议.md`。 5. 页面只通过 feature 入口或明确 service/view-model 文件调用业务逻辑。