Files
softbus_daemon/docs/数据流转.md
flower_linux 4f2ada08c8 modbus rtu 1.2
2026-04-14 15:39:04 +08:00

145 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据流转
## 1. 概念约定
- **物理设备**:真实硬件端口(当前主要是串口),由 `SerialQtDriver` 直接读写。
- **设备实例**`SerialDevice`,负责单设备生命周期、驱动回调、上下行数据接入。
- **原始总线消息**`RawBusMessage`,包含 `endpoint``protocolHint``payload``traceId``direction` 等基础字段。
- **流水线上下文**`PipelineContext`,继承 `RawBusMessage`,增加 `frameKind``frameSeq` 等管道字段。
- **业务消息**:经过协议解析和映射后得到 `DOMMessage`(统一语义数据)。
---
## 2. 上行主链路(串口 -> 消息总线)
### 2.1 设备初始化
`SerialDeviceManager` 完成以下动作:
1. 根据配置创建并配置 `SerialQtDriver`(波特率、校验、停止位、流控)。
2. 创建 `SerialDevice` 并注入 `DeviceInfo`(含协议提示,如 `modbus_rtu`)。
3. 通过 `bindIngress(...)` 绑定:
- `IIngressPort`(当前实现为 `DriverIngressAdapter`
- `MemoryPool`(用于 payload 内存块分配)
4. 调用 `start()` 打开端口并注册读回调/错误回调。
### 2.2 读回调进入 ingress
当串口收到字节流时,`SerialDevice::onRead(...)` 执行:
1.`MemoryPool` 分配内存块并拷贝原始字节。
2. 构造 `RawBusMessage`
- `direction = Upstream`
- `endpoint = 串口名`
- `protocolHint = DeviceInfo.protocol`
- `payload/payloadSize = 原始字节数据`
- `traceId = serial:<endpoint>:<timestamp>`
3. 调用 `m_ingress->push(std::move(ctx))` 进入统一入口。
4. 若内存池不足或 ingress 队列满,通过 `reportError(...)` 记录错误。
### 2.3 ingress 适配为 PipelineContext
`DriverIngressAdapter::push(...)` 负责轻量转换:
1. 新建 `PipelineContext`
2. `pipelineCtx.raw() = std::move(ctx)` 将原始消息搬运进管道上下文。
3. 调用 `PipelineEngine::enqueueIngress(...)` 入总线处理队列。
---
## 3. PipelineEngine 内部流转
### 3.1 入口分流
`enqueueIngress(...)` 分两种模式:
- **并行模式parallelPipeline=true**
-`endpoint` 创建/复用独立 `EndpointFramingState`(每端点一个分帧线程 + chunk 队列)。
- chunk 先进入端点队列,端点线程负责分帧并赋值 `frameSeq`
- 完整帧进入 `m_framedQ`,由解析线程池处理。
- **单线程模式parallelPipeline=false**
- 直接进入 `m_ingressQ`,由单 worker 串行处理。
### 3.2 Stage 1分帧Framer
`dispatchFramer(...)` 的选择策略:
1. 优先根据 `protocolHint` 从插件管理器选择协议分帧插件。
2.`endpoint + pluginId` 复用 framer session保持协议状态机连续性
3. 若没有可用插件,降级到 `PassthroughFramer`(透传/整包策略)。
输出:`frameKind = CompleteFrame` 的帧批次。
### 3.3 Stage 2/3/4解析 -> 过滤 -> 校验
`runStages234(...)` 核心流程:
1. **Stage2 Parser**
- 选择 `IProtocolPlugin`,复用 parser session。
- `parse(rawCtx, envelope)` 产出协议封装 `ProtocolEnvelope`
- 选择 mapper 插件并映射为 `std::vector<DOMMessage>`
2. **Stage3 Filter**
-`DOMMessage` 执行过滤规则(`ProtocolParseFilter::process`)。
3. **Stage4 Validator**
- 最低校验当前为 `messageId` 非空。
4. **Transform/Enrich**
- 应用动态规则scale/offset/unit
- 写入 trace补充富化字段。
说明:当前 `runStages234` 只返回布尔结果,`EnrichedMessage` 由内部生成,后续可接持久化/发布环节。
### 3.4 出口与有序保证
- 未开启有序出口:解析成功后直接进入 `m_egressQ`
- 开启 `orderedEgress`
- 解析线程先写入 `m_mergeQ`
- `mergerLoop``endpoint + frameSeq` 重排。
- 使用 `nextExpected` 保证同端点有序输出。
- 超过 `maxReorderDepth` 的乱序缓存上限会丢弃并计数。
---
## 4. 错误与背压点
主要失败点及表现:
1. **内存池分配失败**`SerialDevice::onRead` 直接 `reportError(memory pool exhausted)`
2. **入口队列满**`enqueueIngress` 返回 false计入 drop。
3. **分帧/解析插件缺失或失败**:对应帧在阶段中被丢弃。
4. **过滤/校验失败**:消息跳过,不进入下游。
5. **重排深度超限**`mergeDrop` 增加并告警。
---
## 5. 当前链路时序(上行)
```text
SerialQtDriver(read callback)
-> SerialDevice::onRead
-> MemoryPool::allocate + RawBusMessage
-> IIngressPort::push (DriverIngressAdapter)
-> PipelineEngine::enqueueIngress
-> [Framer] dispatchFramer/feedChunk
-> [Stage2] parse + map -> DOMMessage
-> [Stage3] filter
-> [Stage4] validate
-> transform/enrich
-> egressQ (有序模式下先 merge 再 egress)
```
---
## 6. 下行(现状)
- `RawBusMessage.direction` 已支持 `Downstream` 语义。
- `SerialDevice` 已提供 `writePayload(...)` 写入能力。
- 当前代码中 egress 路由到物理设备的绑定仍预留(`SerialDeviceManager` 里相关逻辑已注释),后续可补齐:
- egress 消息 -> `EgressRouter` -> endpoint 设备 -> `writePayload`
---
## 7. 维护建议
1. `traceId` 建议在全链路透传到最终发布侧,便于定位帧级问题。
2. 为 Stage2/Stage3/Stage4 增加细粒度指标成功率、耗时、drop 原因)。
3. 文档后续可新增“协议插件开发约定”章节framer/parser/mapper 三类插件接口对照)。