# 协议架构说明

## 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 文件调用业务逻辑。
