Files
softbus_daemon/docs/数据流转.md

155 lines
6.8 KiB
Markdown
Raw Normal View History

2026-04-14 15:39:04 +08:00
# 数据流转
## 1. 概念约定
- **物理设备**:真实硬件端口(当前主要是串口),由 `SerialQtDriver` 直接读写。
- **设备实例**`SerialDevice`,负责单设备生命周期、驱动回调、上下行数据接入。
2026-04-15 20:05:04 +08:00
- **原始总线消息**`RawBusMessage`,包含 `endpointHash``protocol``routingKey``header``payload``traceId``direction` 等基础字段。
2026-04-14 15:39:04 +08:00
- **流水线上下文**`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`
2026-04-15 20:05:04 +08:00
- `endpointHash = makeEndpointHash(串口名)`
- `protocol = protocolTypeFromHint(DeviceInfo.protocol)`
- `routingKey = makeRoutingKey(endpointHash, deviceId)`
- `header = ProtocolHeader{}`(由后续 framer/parser 按协议填充 OOB 元数据)
- `payload = 原始字节数据`(长度由 `payload.length` 表示,不再维护 `payloadSize`
- `traceId = serial:<endpoint>:<timestampMs>`
2026-04-14 15:39:04 +08:00
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**
2026-04-15 20:05:04 +08:00
-`endpointHash` 创建/复用独立 `EndpointFramingState`(每端点一个分帧线程 + chunk 队列)。
2026-04-14 15:39:04 +08:00
- chunk 先进入端点队列,端点线程负责分帧并赋值 `frameSeq`
- 完整帧进入 `m_framedQ`,由解析线程池处理。
- **单线程模式parallelPipeline=false**
- 直接进入 `m_ingressQ`,由单 worker 串行处理。
### 3.2 Stage 1分帧Framer
`dispatchFramer(...)` 的选择策略:
2026-04-15 20:05:04 +08:00
1.`protocol``ProtocolType`)从插件管理器选择协议分帧插件。
2. 按结构化键 `SessionKey{endpointHash, pluginId}` 复用 framer/parser/mapper session保持协议状态机连续性
2026-04-14 15:39:04 +08:00
3. 若没有可用插件,降级到 `PassthroughFramer`(透传/整包策略)。
2026-04-15 20:05:04 +08:00
4. framer session 创建接口已升级为 `createFramerSession(uint32_t endpointHash, ...)`,不再传递 endpoint 字符串。
2026-04-14 15:39:04 +08:00
输出:`frameKind = CompleteFrame` 的帧批次。
2026-04-15 11:16:27 +08:00
### 3.3 Stage 2/3/4解析 -> 过滤 -> 校验(当前返回语义)
2026-04-14 15:39:04 +08:00
`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**
2026-04-15 11:16:27 +08:00
- 仅对通过 filter+validator 的 `DOMMessage` 应用动态规则scale/offset/unit
2026-04-15 20:05:04 +08:00
- 规则作用域按 `endpointHash/deviceId/stableKey` 匹配,不再按 endpoint 字符串匹配。
2026-04-14 15:39:04 +08:00
- 写入 trace补充富化字段。
2026-04-15 11:16:27 +08:00
说明(与代码一致):
- `runStages234``stage2Parser` 返回空时才返回 `false`
- 只要 `stage2Parser` 有消息,哪怕全部在 Stage3/Stage4 被跳过,当前实现仍返回 `true`
- `EnrichedMessage` 在内部生成,但尚未进入独立发布/持久化通道。
2026-04-14 15:39:04 +08:00
### 3.4 出口与有序保证
- 未开启有序出口:解析成功后直接进入 `m_egressQ`
- 开启 `orderedEgress`
- 解析线程先写入 `m_mergeQ`
2026-04-15 20:05:04 +08:00
- `mergerLoop``endpointHash(映射键) + frameSeq` 重排。
2026-04-14 15:39:04 +08:00
- 使用 `nextExpected` 保证同端点有序输出。
- 超过 `maxReorderDepth` 的乱序缓存上限会丢弃并计数。
---
## 4. 错误与背压点
2026-04-15 11:16:27 +08:00
主要失败点及表现(按当前实现):
2026-04-14 15:39:04 +08:00
1. **内存池分配失败**`SerialDevice::onRead` 直接 `reportError(memory pool exhausted)`
2. **入口队列满**`enqueueIngress` 返回 false计入 drop。
2026-04-15 11:16:27 +08:00
3. **分帧/解析/映射插件缺失或失败**`stage2Parser` 返回空,`runStages234=false`,该帧不进入 egress。
4. **过滤/校验失败**:对应 `DOMMessage` 被跳过(不做 enrich但该 `PipelineContext` 仍可能继续进入 egress只要 Stage2 非空)。
2026-04-14 15:39:04 +08:00
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)
2026-04-15 11:16:27 +08:00
-> DeviceBusManager::processEgressLoop
-> EgressRouter::send
-> SerialDevice::writePayload
2026-04-14 15:39:04 +08:00
```
---
2026-04-15 11:16:27 +08:00
## 6. egress 到设备(现状)
2026-04-14 15:39:04 +08:00
2026-04-15 11:16:27 +08:00
- `DeviceBusManager` 已启动 `processEgressLoop`,持续从 `PipelineEngine::tryPopEgress` 取数据并调用 `EgressRouter::send`
2026-04-15 20:05:04 +08:00
- `EgressRouter` 已实现基于 `endpointHash``SerialDevice` 查找和 `writePayload(...)` 发送。
2026-04-15 11:16:27 +08:00
- 当前关键缺口:`SerialDeviceManager::initializeSerialDeviceWithDeps``bindSerialDevice(...)` 仍被注释,导致路由表通常为空,`send` 多数返回失败。
- 因此当前链路是“egress 框架已存在,但默认没有绑定到串口实例”。
2026-04-14 15:39:04 +08:00
---
## 7. 维护建议
1. `traceId` 建议在全链路透传到最终发布侧,便于定位帧级问题。
2. 为 Stage2/Stage3/Stage4 增加细粒度指标成功率、耗时、drop 原因)。
3. 文档后续可新增“协议插件开发约定”章节framer/parser/mapper 三类插件接口对照)。