Files
softbus_daemon/docs/数据流转.md
flower_linux 94eec643de rawbusmessage
2026-04-15 11:16:27 +08:00

151 lines
6.2 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**
- 仅对通过 filter+validator 的 `DOMMessage` 应用动态规则scale/offset/unit
- 写入 trace补充富化字段。
说明(与代码一致):
- `runStages234``stage2Parser` 返回空时才返回 `false`
- 只要 `stage2Parser` 有消息,哪怕全部在 Stage3/Stage4 被跳过,当前实现仍返回 `true`
- `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. **分帧/解析/映射插件缺失或失败**`stage2Parser` 返回空,`runStages234=false`,该帧不进入 egress。
4. **过滤/校验失败**:对应 `DOMMessage` 被跳过(不做 enrich但该 `PipelineContext` 仍可能继续进入 egress只要 Stage2 非空)。
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)
-> DeviceBusManager::processEgressLoop
-> EgressRouter::send
-> SerialDevice::writePayload
```
---
## 6. egress 到设备(现状)
- `DeviceBusManager` 已启动 `processEgressLoop`,持续从 `PipelineEngine::tryPopEgress` 取数据并调用 `EgressRouter::send`
- `EgressRouter` 已实现 `endpoint -> SerialDevice` 查找和 `writePayload(...)` 发送。
- 当前关键缺口:`SerialDeviceManager::initializeSerialDeviceWithDeps``bindSerialDevice(...)` 仍被注释,导致路由表通常为空,`send` 多数返回失败。
- 因此当前链路是“egress 框架已存在,但默认没有绑定到串口实例”。
---
## 7. 维护建议
1. `traceId` 建议在全链路透传到最终发布侧,便于定位帧级问题。
2. 为 Stage2/Stage3/Stage4 增加细粒度指标成功率、耗时、drop 原因)。
3. 文档后续可新增“协议插件开发约定”章节framer/parser/mapper 三类插件接口对照)。