devices pipelinecontext
This commit is contained in:
@@ -119,6 +119,8 @@ qt_add_executable(softbus_daemon
|
|||||||
src/core/plugin_system/IProtocolPlugin.h
|
src/core/plugin_system/IProtocolPlugin.h
|
||||||
src/core/plugin_system/PluginManager.h
|
src/core/plugin_system/PluginManager.h
|
||||||
src/core/plugin_system/PluginManager.cpp
|
src/core/plugin_system/PluginManager.cpp
|
||||||
|
src/core/plugin_system/builtins/ModbusRtuProtocolPlugin.h
|
||||||
|
src/core/plugin_system/builtins/ModbusRtuProtocolPlugin.cpp
|
||||||
|
|
||||||
# message bus pipeline
|
# message bus pipeline
|
||||||
src/message_bus/pipeline/PayloadRef.h
|
src/message_bus/pipeline/PayloadRef.h
|
||||||
@@ -126,7 +128,12 @@ qt_add_executable(softbus_daemon
|
|||||||
src/message_bus/pipeline/PipelineEngine.h
|
src/message_bus/pipeline/PipelineEngine.h
|
||||||
src/message_bus/pipeline/PipelineEngine.cpp
|
src/message_bus/pipeline/PipelineEngine.cpp
|
||||||
src/message_bus/pipeline/stages/1_framers/IFramer.h
|
src/message_bus/pipeline/stages/1_framers/IFramer.h
|
||||||
src/message_bus/pipeline/stages/1_framers/DefaultFramer.cpp
|
src/message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h
|
||||||
|
src/message_bus/pipeline/stages/1_framers/ModbusRtuFraming.cpp
|
||||||
|
src/message_bus/pipeline/stages/1_framers/ModbusRtuFramer.h
|
||||||
|
src/message_bus/pipeline/stages/1_framers/ModbusRtuFramer.cpp
|
||||||
|
src/message_bus/pipeline/stages/1_framers/PassthroughFramer.h
|
||||||
|
src/message_bus/pipeline/stages/1_framers/PassthroughFramer.cpp
|
||||||
src/message_bus/pipeline/stages/2_parsers/IParserStage.h
|
src/message_bus/pipeline/stages/2_parsers/IParserStage.h
|
||||||
src/message_bus/pipeline/stages/3_filters/IFilterStage.h
|
src/message_bus/pipeline/stages/3_filters/IFilterStage.h
|
||||||
src/message_bus/pipeline/stages/4_validators/IValidator.h
|
src/message_bus/pipeline/stages/4_validators/IValidator.h
|
||||||
@@ -179,6 +186,23 @@ endif()
|
|||||||
|
|
||||||
add_subdirectory(plugins)
|
add_subdirectory(plugins)
|
||||||
|
|
||||||
|
option(SOFTBUS_BUILD_TESTS "Build unit tests" OFF)
|
||||||
|
if(SOFTBUS_BUILD_TESTS)
|
||||||
|
find_package(Qt6 6.5 REQUIRED COMPONENTS Test)
|
||||||
|
qt_add_executable(softbus_modbus_rtu_framer_test
|
||||||
|
tests/modbus_rtu_framer_test.cpp
|
||||||
|
src/message_bus/pipeline/stages/1_framers/ModbusRtuFraming.cpp
|
||||||
|
src/message_bus/pipeline/stages/1_framers/ModbusRtuFramer.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(softbus_modbus_rtu_framer_test PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}/src
|
||||||
|
)
|
||||||
|
target_link_libraries(softbus_modbus_rtu_framer_test PRIVATE
|
||||||
|
Qt6::Core
|
||||||
|
Qt6::Test
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
install(TARGETS softbus_daemon
|
install(TARGETS softbus_daemon
|
||||||
BUNDLE DESTINATION .
|
BUNDLE DESTINATION .
|
||||||
|
|||||||
Binary file not shown.
@@ -1,5 +1,14 @@
|
|||||||
{
|
{
|
||||||
"plugin_dir": "bin/plugins",
|
"plugin_dir": "bin/plugins",
|
||||||
|
"pipeline": {
|
||||||
|
"parallelPipeline": false,
|
||||||
|
"parseWorkerCount": 2,
|
||||||
|
"orderedEgress": true,
|
||||||
|
"framedQueueMax": 4096,
|
||||||
|
"maxReorderDepth": 256,
|
||||||
|
"modbusRtuMaxFrameBytes": 256,
|
||||||
|
"modbusRtuInterFrameTimeoutMs": 0
|
||||||
|
},
|
||||||
"device_tree": [
|
"device_tree": [
|
||||||
{
|
{
|
||||||
"id": "serial_1",
|
"id": "serial_1",
|
||||||
@@ -9,6 +18,7 @@
|
|||||||
"driver": "hardware/drivers/serial/SerialQtDriver",
|
"driver": "hardware/drivers/serial/SerialQtDriver",
|
||||||
"capabilitiesRef": "src/hardware/drivers/serial/parameters.json",
|
"capabilitiesRef": "src/hardware/drivers/serial/parameters.json",
|
||||||
"params": {
|
"params": {
|
||||||
|
"protocol": "modbus_rtu",
|
||||||
"baudRate": 115200,
|
"baudRate": 115200,
|
||||||
"dataBits": 8,
|
"dataBits": 8,
|
||||||
"parity": "none",
|
"parity": "none",
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
extern "C" void softbus_modbus_rtu_plugin_stub() {}
|
extern "C" void
|
||||||
|
softbus_modbus_rtu_plugin_stub() {}
|
||||||
|
|||||||
@@ -59,36 +59,8 @@ public:
|
|||||||
this->release(*b);
|
this->release(*b);
|
||||||
delete b;
|
delete b;
|
||||||
});
|
});
|
||||||
BlockPtr allocate(std::size_t requiredSize)
|
|
||||||
{
|
|
||||||
if (requiredSize > m_blockSize) {
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t slot = 0;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(m_mutex);
|
|
||||||
if (m_freeSlots.empty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
slot = m_freeSlots.front();
|
|
||||||
m_freeSlots.pop();
|
|
||||||
m_inUse[slot] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* raw = new PoolBlock{
|
|
||||||
m_storage.data() + slot * m_blockSize,
|
|
||||||
m_blockSize,
|
|
||||||
requiredSize,
|
|
||||||
slot,
|
|
||||||
};
|
|
||||||
return BlockPtr(raw, [this](PoolBlock* b) {
|
|
||||||
if (!b) return;
|
|
||||||
this->release(*b);
|
|
||||||
delete b;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
void release(const PoolBlock& block)
|
void release(const PoolBlock& block)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(m_mutex);
|
std::lock_guard<std::mutex> lk(m_mutex);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "message_bus/ingress/DriverIngressAdapter.h"
|
#include "message_bus/ingress/DriverIngressAdapter.h"
|
||||||
#include "message_bus/pipeline/PipelineEngine.h"
|
#include "message_bus/pipeline/PipelineEngine.h"
|
||||||
#include "core/plugin_system/PluginManager.h"
|
#include "core/plugin_system/PluginManager.h"
|
||||||
|
#include "core/plugin_system/builtins/ModbusRtuProtocolPlugin.h"
|
||||||
#include "device_bus/manager/SerialDeviceManager.h"
|
#include "device_bus/manager/SerialDeviceManager.h"
|
||||||
#include "devices/DeviceTypes.h"
|
#include "devices/DeviceTypes.h"
|
||||||
#include "utils/logs/logging.h"
|
#include "utils/logs/logging.h"
|
||||||
@@ -56,7 +57,10 @@ bool DeviceBusManager::initialize(const QJsonObject& rootConfig)
|
|||||||
if (!m_pluginManager) {
|
if (!m_pluginManager) {
|
||||||
m_pluginManager = std::make_shared<softbus::core::plugin_system::PluginManager>();
|
m_pluginManager = std::make_shared<softbus::core::plugin_system::PluginManager>();
|
||||||
}
|
}
|
||||||
|
m_pluginManager->registerProtocolPlugin(softbus::core::plugin_system::makeModbusRtuProtocolPlugin());
|
||||||
if (m_pipelineEngine) {
|
if (m_pipelineEngine) {
|
||||||
|
m_pipelineEngine->setMemoryPool(m_memoryPool);
|
||||||
|
m_pipelineEngine->loadRuntimeConfigFromJson(rootConfig.value(QStringLiteral("pipeline")).toObject());
|
||||||
m_pipelineEngine->setPluginManager(m_pluginManager);
|
m_pipelineEngine->setPluginManager(m_pluginManager);
|
||||||
m_pipelineEngine->start();
|
m_pipelineEngine->start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ bool initializeSerialDeviceWithDeps(const QString& endpoint, const QJsonObject&
|
|||||||
info.endpoint = endpoint;
|
info.endpoint = endpoint;
|
||||||
info.type = QStringLiteral("serial");
|
info.type = QStringLiteral("serial");
|
||||||
info.kind = softbus::devices::DeviceKind::Serial;
|
info.kind = softbus::devices::DeviceKind::Serial;
|
||||||
|
info.protocol = params.value(QStringLiteral("protocol")).toString(QStringLiteral("modbus_rtu"));
|
||||||
|
|
||||||
softbus::devices::physical::SerialDevice::Config cfg;
|
softbus::devices::physical::SerialDevice::Config cfg;
|
||||||
cfg.endpoint = endpoint;
|
cfg.endpoint = endpoint;
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ void SerialDevice::onRead(const std::vector<std::uint8_t>& data)
|
|||||||
softbus::message_bus::pipeline::PipelineContext ctx;
|
softbus::message_bus::pipeline::PipelineContext ctx;
|
||||||
ctx.endpoint = m_config.endpoint;
|
ctx.endpoint = m_config.endpoint;
|
||||||
ctx.protocolHint = m_info.protocol;
|
ctx.protocolHint = m_info.protocol;
|
||||||
|
ctx.frameKind = softbus::message_bus::pipeline::FrameKind::StreamChunk;
|
||||||
ctx.payload.block = std::move(block);
|
ctx.payload.block = std::move(block);
|
||||||
ctx.payload.length = data.size();
|
ctx.payload.length = data.size();
|
||||||
ctx.payloadSize = data.size();
|
ctx.payloadSize = data.size();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
@@ -13,6 +14,19 @@
|
|||||||
namespace softbus::message_bus::pipeline
|
namespace softbus::message_bus::pipeline
|
||||||
{
|
{
|
||||||
|
|
||||||
|
enum class FrameKind
|
||||||
|
{
|
||||||
|
StreamChunk,
|
||||||
|
CompleteFrame,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class BusDirection
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Upstream,
|
||||||
|
Downstream,
|
||||||
|
};
|
||||||
|
|
||||||
struct PipelineContext
|
struct PipelineContext
|
||||||
{
|
{
|
||||||
int deviceId{-1}; // 设备ID
|
int deviceId{-1}; // 设备ID
|
||||||
@@ -24,6 +38,13 @@ struct PipelineContext
|
|||||||
|
|
||||||
PayloadRef payload; // 有效载荷
|
PayloadRef payload; // 有效载荷
|
||||||
std::size_t payloadSize{0}; // 有效载荷大小
|
std::size_t payloadSize{0}; // 有效载荷大小
|
||||||
|
|
||||||
|
FrameKind frameKind{FrameKind::StreamChunk};
|
||||||
|
BusDirection direction{BusDirection::Unknown};
|
||||||
|
std::uint64_t frameSeq{0};
|
||||||
|
|
||||||
|
QJsonObject parsed;
|
||||||
|
QString framerError; // 成帧/Pipeline 旁路诊断(可选)
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace softbus::message_bus::pipeline
|
} // namespace softbus::message_bus::pipeline
|
||||||
|
|||||||
@@ -1,14 +1,71 @@
|
|||||||
#include "message_bus/pipeline/PipelineEngine.h"
|
#include "message_bus/pipeline/PipelineEngine.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h"
|
||||||
#include "utils/logs/logging.h"
|
#include "utils/logs/logging.h"
|
||||||
|
|
||||||
namespace softbus::message_bus::pipeline
|
namespace softbus::message_bus::pipeline
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct EndpointFramingState
|
||||||
|
{
|
||||||
|
softbus::core::memory::LockFreeQueue<PipelineContext> chunkQ;
|
||||||
|
std::atomic<bool> running{true};
|
||||||
|
std::thread th;
|
||||||
|
std::uint64_t nextSeq{1};
|
||||||
|
QString endpoint;
|
||||||
|
std::shared_ptr<softbus::core::memory::MemoryPool> pool;
|
||||||
|
softbus::message_bus::pipeline::stages::framers::ModbusRtuFramerConfig modbusCfg;
|
||||||
|
std::shared_ptr<softbus::message_bus::pipeline::stages::framers::PassthroughFramer> pass;
|
||||||
|
std::function<void(PipelineContext)> pushFramed;
|
||||||
|
|
||||||
|
explicit EndpointFramingState(std::size_t cap)
|
||||||
|
: chunkQ(cap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void endpointFramerThread(std::shared_ptr<EndpointFramingState> st)
|
||||||
|
{
|
||||||
|
using softbus::message_bus::pipeline::stages::framers::ModbusRtuFramer;
|
||||||
|
using softbus::message_bus::pipeline::stages::framers::protocolHintIsModbusRtu;
|
||||||
|
|
||||||
|
auto modbus = std::make_unique<ModbusRtuFramer>(st->pool, st->modbusCfg, st->endpoint);
|
||||||
|
|
||||||
|
while (st->running.load(std::memory_order_acquire)) {
|
||||||
|
PipelineContext ch;
|
||||||
|
if (!st->chunkQ.pop(ch)) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::vector<PipelineContext> out;
|
||||||
|
if (protocolHintIsModbusRtu(ch.protocolHint)) {
|
||||||
|
modbus->feed(ch, out);
|
||||||
|
} else if (st->pass) {
|
||||||
|
st->pass->feed(ch, out);
|
||||||
|
}
|
||||||
|
for (auto& x : out) {
|
||||||
|
x.frameSeq = st->nextSeq++;
|
||||||
|
if (st->pushFramed) {
|
||||||
|
st->pushFramed(std::move(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
PipelineEngine::PipelineEngine(std::size_t queueCapacity)
|
PipelineEngine::PipelineEngine(std::size_t queueCapacity)
|
||||||
: m_ingressQ(queueCapacity), m_egressQ(queueCapacity)
|
: m_ingressQ(queueCapacity)
|
||||||
|
, m_egressQ(queueCapacity)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,12 +74,73 @@ PipelineEngine::~PipelineEngine()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PipelineEngine::setMemoryPool(std::shared_ptr<softbus::core::memory::MemoryPool> pool)
|
||||||
|
{
|
||||||
|
m_pool = std::move(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineEngine::setRuntimeConfig(PipelineRuntimeConfig cfg)
|
||||||
|
{
|
||||||
|
m_rtConfig = cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineEngine::loadRuntimeConfigFromJson(const QJsonObject& obj)
|
||||||
|
{
|
||||||
|
if (obj.isEmpty()) {
|
||||||
|
LOG_ERROR() << "PipelineEngine: runtime config is empty";
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
m_rtConfig.parallelPipeline = obj.value(QStringLiteral("parallelPipeline")).toBool(true);
|
||||||
|
m_rtConfig.parseWorkerCount = std::max(1, obj.value(QStringLiteral("parseWorkerCount")).toInt(2));
|
||||||
|
m_rtConfig.orderedEgress = obj.value(QStringLiteral("orderedEgress")).toBool(true);
|
||||||
|
m_rtConfig.framedQueueMax = static_cast<std::size_t>(
|
||||||
|
std::max(64, obj.value(QStringLiteral("framedQueueMax")).toInt(4096)));
|
||||||
|
m_rtConfig.maxReorderDepth = static_cast<std::size_t>(
|
||||||
|
std::max(16, obj.value(QStringLiteral("maxReorderDepth")).toInt(256)));
|
||||||
|
m_rtConfig.modbus.maxAdu = static_cast<std::size_t>(
|
||||||
|
std::max(8, obj.value(QStringLiteral("modbusRtuMaxFrameBytes")).toInt(256)));
|
||||||
|
m_rtConfig.modbus.interFrameTimeoutMs = obj.value(QStringLiteral("modbusRtuInterFrameTimeoutMs")).toInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
void PipelineEngine::start()
|
void PipelineEngine::start()
|
||||||
{
|
{
|
||||||
if (m_running.exchange(true)) {
|
if (m_running.exchange(true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_worker = std::thread([this]() { run(); });
|
|
||||||
|
if (!m_pool) {
|
||||||
|
LOG_ERROR() << "PipelineEngine::start: memory pool not set";
|
||||||
|
m_running = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_modbusFramer =
|
||||||
|
std::make_unique<softbus::message_bus::pipeline::stages::framers::ModbusRtuFramer>(m_pool,
|
||||||
|
m_rtConfig.modbus,
|
||||||
|
QString{});
|
||||||
|
m_passthroughFramer =
|
||||||
|
std::make_shared<softbus::message_bus::pipeline::stages::framers::PassthroughFramer>(m_pool);
|
||||||
|
|
||||||
|
// 如果启用并行管道 则创建帧队列和解析线程 如果启用有序出口 则创建合并队列和合并线程
|
||||||
|
// 并行管道是用来处理多个端点的数据流的
|
||||||
|
// 有序出口是用来保证数据流的有序性的
|
||||||
|
// 帧队列是用来存储帧的
|
||||||
|
// 解析线程是用来处理帧的
|
||||||
|
// 合并队列是用来存储合并的帧的
|
||||||
|
// 合并线程是用来处理合并的帧的
|
||||||
|
if (m_rtConfig.parallelPipeline) {
|
||||||
|
m_framedQ = std::make_unique<softbus::core::threading::MutexQueue<PipelineContext>>(
|
||||||
|
m_rtConfig.framedQueueMax);
|
||||||
|
for (int i = 0; i < m_rtConfig.parseWorkerCount; ++i) {
|
||||||
|
m_parseThreads.emplace_back([this]() { parseWorkerLoop(); });
|
||||||
|
}
|
||||||
|
if (m_rtConfig.orderedEgress) {
|
||||||
|
m_mergeQ = std::make_unique<softbus::core::threading::MutexQueue<MergeItem>>(0);
|
||||||
|
m_mergerThread = std::thread([this]() { mergerLoop(); });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_worker = std::thread([this]() { runSingleWorker(); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineEngine::stop()
|
void PipelineEngine::stop()
|
||||||
@@ -30,21 +148,70 @@ void PipelineEngine::stop()
|
|||||||
if (!m_running.exchange(false)) {
|
if (!m_running.exchange(false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_endpointMutex);
|
||||||
|
for (auto it = m_endpointFramers.constBegin(); it != m_endpointFramers.constEnd(); ++it) {
|
||||||
|
if (it.value()) {
|
||||||
|
it.value()->running.store(false, std::memory_order_release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto it = m_endpointFramers.constBegin(); it != m_endpointFramers.constEnd(); ++it) {
|
||||||
|
if (it.value() && it.value()->th.joinable()) {
|
||||||
|
it.value()->th.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_endpointFramers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_worker.joinable()) {
|
if (m_worker.joinable()) {
|
||||||
m_worker.join();
|
m_worker.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& t : m_parseThreads) {
|
||||||
|
if (t.joinable()) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_parseThreads.clear();
|
||||||
|
|
||||||
|
if (m_mergerThread.joinable()) {
|
||||||
|
m_mergerThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_framedQ.reset();
|
||||||
|
m_mergeQ.reset();
|
||||||
|
m_modbusFramer.reset();
|
||||||
|
m_passthroughFramer.reset();
|
||||||
|
}
|
||||||
|
// 功能 :清空 ingress 队列
|
||||||
|
// 参数 :无
|
||||||
|
// 返回值 :无
|
||||||
|
// 逻辑 :从 ingress 队列中取出数据 直到队列为空
|
||||||
void PipelineEngine::drain()
|
void PipelineEngine::drain()
|
||||||
{
|
{
|
||||||
PipelineContext tmp;
|
PipelineContext tmp;
|
||||||
while (m_ingressQ.pop(tmp)) {
|
while (m_ingressQ.pop(tmp)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 功能 :将数据推送到 ingress 队列
|
||||||
|
// 参数 :ctx 数据
|
||||||
|
// 返回值 :是否成功
|
||||||
|
// 逻辑 :如果并行管道启用 则根据端点确保帧解析器 然后推送到帧队列 如果并行管道未启用 则直接推送到 ingress 队列
|
||||||
bool PipelineEngine::enqueueIngress(PipelineContext ctx)
|
bool PipelineEngine::enqueueIngress(PipelineContext ctx)
|
||||||
{
|
{
|
||||||
if (!m_ingressQ.push(std::move(ctx))) {
|
if (m_rtConfig.parallelPipeline) {
|
||||||
|
if (ctx.endpoint.isEmpty()) {
|
||||||
|
++m_counters.drop;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ensureEndpointFramer(ctx.endpoint);
|
||||||
|
std::shared_ptr<EndpointFramingState> st;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_endpointMutex);
|
||||||
|
st = m_endpointFramers.value(ctx.endpoint);
|
||||||
|
}
|
||||||
|
if (!st || !st->chunkQ.push(std::move(ctx))) {
|
||||||
++m_counters.drop;
|
++m_counters.drop;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -52,40 +219,216 @@ bool PipelineEngine::enqueueIngress(PipelineContext ctx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_ingressQ.push(std::move(ctx))) {
|
||||||
|
++m_counters.drop;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++m_counters.in;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 功能 :将数据推送到 egress 队列
|
||||||
|
// 参数 :ctx 数据
|
||||||
|
// 返回值 :是否成功
|
||||||
|
// 逻辑 :将数据推送到 egress 队列
|
||||||
bool PipelineEngine::enqueueEgress(PipelineContext ctx)
|
bool PipelineEngine::enqueueEgress(PipelineContext ctx)
|
||||||
{
|
{
|
||||||
return m_egressQ.push(std::move(ctx));
|
return m_egressQ.push(std::move(ctx));
|
||||||
}
|
}
|
||||||
|
// 功能 :尝试从 egress 队列中取出数据
|
||||||
|
// 参数 :out 数据
|
||||||
|
// 返回值 :是否成功
|
||||||
|
// 逻辑 :从 egress 队列中取出数据 如果队列为空 则返回false 如果队列不为空 则返回true
|
||||||
bool PipelineEngine::tryPopEgress(PipelineContext& out)
|
bool PipelineEngine::tryPopEgress(PipelineContext& out)
|
||||||
{
|
{
|
||||||
return m_egressQ.pop(out);
|
return m_egressQ.pop(out);
|
||||||
}
|
}
|
||||||
|
// 功能 :设置插件管理器
|
||||||
|
// 参数 :pluginManager 插件管理器
|
||||||
|
// 返回值 :无
|
||||||
|
// 逻辑 :设置插件管理器
|
||||||
void PipelineEngine::setPluginManager(std::shared_ptr<softbus::core::plugin_system::PluginManager> pluginManager)
|
void PipelineEngine::setPluginManager(std::shared_ptr<softbus::core::plugin_system::PluginManager> pluginManager)
|
||||||
{
|
{
|
||||||
m_pluginManager = std::move(pluginManager);
|
m_pluginManager = std::move(pluginManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 管道引擎运行线程 从 ingress 队列中取出数据 进行处理 然后推送到 egress 队列中
|
// 功能 :确保端点帧解析器
|
||||||
void PipelineEngine::run()
|
// 参数 :endpoint 端点
|
||||||
|
// 返回值 :无
|
||||||
|
// 逻辑 :如果端点帧解析器不存在 则创建端点帧解析器
|
||||||
|
void PipelineEngine::ensureEndpointFramer(const QString& endpoint)
|
||||||
{
|
{
|
||||||
while (m_running.load()) {
|
std::lock_guard<std::mutex> lk(m_endpointMutex);
|
||||||
|
if (m_endpointFramers.contains(endpoint)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const std::size_t qcap = m_ingressQ.capacity();
|
||||||
|
auto st = std::make_shared<EndpointFramingState>(qcap > 0 ? qcap : 4096);
|
||||||
|
st->endpoint = endpoint;
|
||||||
|
st->pool = m_pool;
|
||||||
|
st->modbusCfg = m_rtConfig.modbus;
|
||||||
|
st->pass = m_passthroughFramer;
|
||||||
|
st->pushFramed = [this](PipelineContext c) {
|
||||||
|
if (!m_framedQ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_framedQ->tryPush(std::move(c))) {
|
||||||
|
++m_counters.drop;
|
||||||
|
LOG_WARNING() << "PipelineEngine: framed queue full drop";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
st->th = std::thread(endpointFramerThread, st);
|
||||||
|
m_endpointFramers.insert(endpoint, std::move(st));
|
||||||
|
}
|
||||||
|
// 功能 :分发帧解析器
|
||||||
|
// 参数 :chunkIn 数据
|
||||||
|
// 返回值 :无
|
||||||
|
// 逻辑 :根据协议提示 选择合适的帧解析器 进行帧解析
|
||||||
|
void PipelineEngine::dispatchFramer(PipelineContext& chunkIn, std::vector<PipelineContext>& completeFramesOut)
|
||||||
|
{
|
||||||
|
using softbus::message_bus::pipeline::stages::framers::protocolHintIsModbusRtu;
|
||||||
|
// 根据协议提示 选择合适的帧解析器 进行帧解析
|
||||||
|
|
||||||
|
if (protocolHintIsModbusRtu(chunkIn.protocolHint)) {
|
||||||
|
if (m_modbusFramer) {
|
||||||
|
m_modbusFramer->feed(chunkIn, completeFramesOut);
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (m_passthroughFramer) {
|
||||||
|
m_passthroughFramer->feed(chunkIn, completeFramesOut);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 功能 :分配帧序列号
|
||||||
|
void PipelineEngine::assignFrameSequence(PipelineContext& ctx)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_seqMutex);
|
||||||
|
ctx.frameSeq = ++m_nextFrameSeqByEndpoint[ctx.endpoint];
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineEngine::runSingleWorker()
|
||||||
|
{
|
||||||
|
// 单线程工作线程 从 ingress 队列中取出数据 进行处理 然后推送到 egress 队列中
|
||||||
|
while (m_running.load(std::memory_order_acquire)) {
|
||||||
PipelineContext ctx;
|
PipelineContext ctx;
|
||||||
if (!m_ingressQ.pop(ctx)) {
|
if (!m_ingressQ.pop(ctx)) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool framed = stage1Framer(ctx);
|
std::vector<PipelineContext> batch;
|
||||||
// 帧解析
|
dispatchFramer(ctx, batch);
|
||||||
const bool parsed = framed && stage2Parser(ctx);
|
|
||||||
// 过滤
|
for (auto& c : batch) {
|
||||||
const bool filtered = parsed && stage3Filter(ctx);
|
if (c.frameKind != FrameKind::CompleteFrame) {
|
||||||
// 验证
|
continue;
|
||||||
const bool valid = filtered && stage4Validator(ctx);
|
}
|
||||||
// 如果验证失败 则记录错误
|
//
|
||||||
if (!valid) {
|
std::ostringstream oss;
|
||||||
|
oss << std::hex << std::setfill('0');
|
||||||
|
for (std::size_t i = 0; i < c.payload.block->size; ++i) {
|
||||||
|
oss << std::setw(2) << static_cast<int>(c.payload.block->data[i]);
|
||||||
|
if (i + 1 < c.payload.block->size) {
|
||||||
|
oss << ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const QString hexPreview = QString::fromStdString(oss.str());
|
||||||
|
LOG_INFO() << "the data is"<< hexPreview;
|
||||||
|
// 分配帧序列号
|
||||||
|
assignFrameSequence(c);
|
||||||
|
if (!runStages234(c)) {
|
||||||
|
++m_counters.error;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!m_egressQ.push(std::move(c))) {
|
||||||
|
++m_counters.drop;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++m_counters.out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineEngine::parseWorkerLoop()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
PipelineContext ctx;
|
||||||
|
|
||||||
|
const bool have = m_framedQ && m_framedQ->popWaitFor(ctx, m_running, std::chrono::milliseconds(50)); // 从帧队列中取出帧 , 如果帧队列为空 则等待50毫秒
|
||||||
|
if (!have) {
|
||||||
|
if (!m_running.load(std::memory_order_acquire)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << std::hex << std::setfill('0');
|
||||||
|
for (std::size_t i = 0; i < ctx.payload.block->size; ++i) {
|
||||||
|
oss << std::setw(2) << static_cast<int>(ctx.payload.block->data[i]);
|
||||||
|
if (i + 1 < ctx.payload.block->size) {
|
||||||
|
oss << ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const QString hexPreview = QString::fromStdString(oss.str());
|
||||||
|
LOG_INFO() << "the data is"<< hexPreview;
|
||||||
|
|
||||||
|
const bool ok = runStages234(ctx);
|
||||||
|
if (m_rtConfig.orderedEgress && m_mergeQ) {
|
||||||
|
MergeItem it;
|
||||||
|
it.endpoint = ctx.endpoint;
|
||||||
|
it.seq = ctx.frameSeq;
|
||||||
|
it.pipelineOk = ok;
|
||||||
|
it.ctx = std::move(ctx);
|
||||||
|
m_mergeQ->push(std::move(it));
|
||||||
|
} else {
|
||||||
|
if (!ok) {
|
||||||
|
++m_counters.error;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!m_egressQ.push(std::move(ctx))) {
|
||||||
|
++m_counters.drop;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++m_counters.out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineEngine::mergerLoop()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
MergeItem it;
|
||||||
|
const bool have = m_mergeQ && m_mergeQ->popWaitFor(it, m_running, std::chrono::milliseconds(50));
|
||||||
|
if (!have) {
|
||||||
|
if (!m_running.load(std::memory_order_acquire)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ingestMergeItem(std::move(it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 功能 :合并帧
|
||||||
|
// 参数 :item 合并项
|
||||||
|
// 返回值 :无
|
||||||
|
// 逻辑 :将合并项中的数据推送到 egress 队列中
|
||||||
|
void PipelineEngine::ingestMergeItem(MergeItem item)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_mergeStateMutex);
|
||||||
|
auto& st = m_mergeByEndpoint[item.endpoint];
|
||||||
|
if (m_rtConfig.maxReorderDepth > 0
|
||||||
|
&& static_cast<std::size_t>(st.buffer.size()) >= m_rtConfig.maxReorderDepth) {
|
||||||
|
++m_counters.mergeDrop;
|
||||||
|
LOG_WARNING() << "PipelineEngine: merge reorder depth exceeded endpoint=" << item.endpoint;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
st.buffer.insert(item.seq, std::make_pair(std::move(item.ctx), item.pipelineOk));
|
||||||
|
|
||||||
|
while (st.buffer.contains(st.nextExpected)) {
|
||||||
|
auto pr = st.buffer.take(st.nextExpected);
|
||||||
|
++st.nextExpected;
|
||||||
|
const bool pipelineOk = pr.second;
|
||||||
|
PipelineContext ctx = std::move(pr.first);
|
||||||
|
if (!pipelineOk) {
|
||||||
++m_counters.error;
|
++m_counters.error;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -97,17 +440,31 @@ void PipelineEngine::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PipelineEngine::stage1Framer(PipelineContext& ctx)
|
// 功能 :运行阶段234
|
||||||
|
// 参数 :ctx 数据
|
||||||
|
// 返回值 :是否成功
|
||||||
|
// 逻辑 :如果帧类型不是完整帧 则返回false 如果阶段2解析失败 则返回false 如果阶段3过滤失败 则返回false 如果阶段4验证失败 则返回false 如果都成功 则返回true
|
||||||
|
bool PipelineEngine::runStages234(PipelineContext& ctx)
|
||||||
{
|
{
|
||||||
if (!ctx.payload.valid()) {
|
if (ctx.frameKind != FrameKind::CompleteFrame) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ctx.payloadSize == 0) {
|
if (!stage2Parser(ctx)) {
|
||||||
ctx.payloadSize = ctx.payload.length;
|
return false;
|
||||||
|
}
|
||||||
|
if (!stage3Filter(ctx)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!stage4Validator(ctx)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 功能 :验证阶段4
|
||||||
|
// 参数 :ctx 数据
|
||||||
|
// 返回值 :是否成功
|
||||||
|
// 逻辑 :如果有效载荷无效 则返回false 如果都成功 则返回true
|
||||||
bool PipelineEngine::stage4Validator(PipelineContext& ctx)
|
bool PipelineEngine::stage4Validator(PipelineContext& ctx)
|
||||||
{
|
{
|
||||||
if (!ctx.payload.valid()) {
|
if (!ctx.payload.valid()) {
|
||||||
@@ -119,9 +476,16 @@ bool PipelineEngine::stage4Validator(PipelineContext& ctx)
|
|||||||
if (!ctx.timestamp.isValid()) {
|
if (!ctx.timestamp.isValid()) {
|
||||||
ctx.timestamp = QDateTime::currentDateTimeUtc();
|
ctx.timestamp = QDateTime::currentDateTimeUtc();
|
||||||
}
|
}
|
||||||
|
if (ctx.frameKind != FrameKind::CompleteFrame) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 功能 :运行阶段2解析器
|
||||||
|
// 参数 :ctx 数据
|
||||||
|
// 返回值 :是否成功
|
||||||
|
// 逻辑 :如果插件管理器为空 则返回true 如果插件管理器不为空 则选择合适的插件 创建会话 如果会话为空 则返回false 如果会话不为空 则返回true
|
||||||
bool PipelineEngine::stage2Parser(PipelineContext& ctx)
|
bool PipelineEngine::stage2Parser(PipelineContext& ctx)
|
||||||
{
|
{
|
||||||
if (!m_pluginManager) {
|
if (!m_pluginManager) {
|
||||||
|
|||||||
@@ -1,21 +1,40 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// 定义管道引擎 包含队列、运行状态、工作线程、计数器、插件管理器
|
// 定义管道引擎 包含队列、运行状态、工作线程、计数器、插件管理器
|
||||||
// 队列是用来存储管道上下文的
|
|
||||||
// 运行状态是用来控制引擎的运行和停止的
|
|
||||||
// 工作线程是用来处理管道上下文的
|
|
||||||
// 计数器是用来统计管道上下文的数量和错误数量的
|
|
||||||
// 插件管理器是用来管理插件的
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include "core/plugin_system/PluginManager.h"
|
#include "core/plugin_system/PluginManager.h"
|
||||||
#include "core/memory/LockFreeQueue.h"
|
#include "core/memory/LockFreeQueue.h"
|
||||||
|
#include "core/memory/MemoryPool.h"
|
||||||
|
#include "core/threading/MutexQueue.h"
|
||||||
#include "message_bus/pipeline/PipelineContext.h"
|
#include "message_bus/pipeline/PipelineContext.h"
|
||||||
|
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFramer.h"
|
||||||
|
#include "message_bus/pipeline/stages/1_framers/PassthroughFramer.h"
|
||||||
|
|
||||||
namespace softbus::message_bus::pipeline
|
namespace softbus::message_bus::pipeline
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct PipelineRuntimeConfig
|
||||||
|
{
|
||||||
|
bool parallelPipeline{false};
|
||||||
|
int parseWorkerCount{2};
|
||||||
|
bool orderedEgress{true};
|
||||||
|
|
||||||
|
std::size_t framedQueueMax{4096};
|
||||||
|
std::size_t maxReorderDepth{256};
|
||||||
|
|
||||||
|
softbus::message_bus::pipeline::stages::framers::ModbusRtuFramerConfig modbus;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EndpointFramingState;
|
||||||
|
|
||||||
class PipelineEngine
|
class PipelineEngine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -25,6 +44,7 @@ public:
|
|||||||
std::atomic<std::uint64_t> out{0};
|
std::atomic<std::uint64_t> out{0};
|
||||||
std::atomic<std::uint64_t> drop{0};
|
std::atomic<std::uint64_t> drop{0};
|
||||||
std::atomic<std::uint64_t> error{0};
|
std::atomic<std::uint64_t> error{0};
|
||||||
|
std::atomic<std::uint64_t> mergeDrop{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit PipelineEngine(std::size_t queueCapacity = 4096);
|
explicit PipelineEngine(std::size_t queueCapacity = 4096);
|
||||||
@@ -39,12 +59,38 @@ public:
|
|||||||
|
|
||||||
bool tryPopEgress(PipelineContext& out);
|
bool tryPopEgress(PipelineContext& out);
|
||||||
void setPluginManager(std::shared_ptr<softbus::core::plugin_system::PluginManager> pluginManager);
|
void setPluginManager(std::shared_ptr<softbus::core::plugin_system::PluginManager> pluginManager);
|
||||||
|
void setMemoryPool(std::shared_ptr<softbus::core::memory::MemoryPool> pool);
|
||||||
|
void setRuntimeConfig(PipelineRuntimeConfig cfg);
|
||||||
|
void loadRuntimeConfigFromJson(const QJsonObject& obj);
|
||||||
|
|
||||||
const Counters& counters() const { return m_counters; }
|
const Counters& counters() const { return m_counters; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void run();
|
struct MergeItem
|
||||||
bool stage1Framer(PipelineContext& ctx);
|
{
|
||||||
|
QString endpoint;
|
||||||
|
std::uint64_t seq{0};
|
||||||
|
PipelineContext ctx;
|
||||||
|
bool pipelineOk{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MergeState
|
||||||
|
{
|
||||||
|
std::uint64_t nextExpected{1};
|
||||||
|
QHash<std::uint64_t, std::pair<PipelineContext, bool>> buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
void runSingleWorker();
|
||||||
|
void parseWorkerLoop();
|
||||||
|
void mergerLoop();
|
||||||
|
|
||||||
|
void ensureEndpointFramer(const QString& endpoint);
|
||||||
|
|
||||||
|
void dispatchFramer(PipelineContext& chunkIn, std::vector<PipelineContext>& completeFramesOut);
|
||||||
|
void assignFrameSequence(PipelineContext& ctx);
|
||||||
|
bool runStages234(PipelineContext& ctx);
|
||||||
|
void ingestMergeItem(MergeItem item);
|
||||||
|
|
||||||
bool stage2Parser(PipelineContext& ctx);
|
bool stage2Parser(PipelineContext& ctx);
|
||||||
bool stage3Filter(PipelineContext& ctx);
|
bool stage3Filter(PipelineContext& ctx);
|
||||||
bool stage4Validator(PipelineContext& ctx);
|
bool stage4Validator(PipelineContext& ctx);
|
||||||
@@ -56,6 +102,26 @@ private:
|
|||||||
std::thread m_worker;
|
std::thread m_worker;
|
||||||
Counters m_counters;
|
Counters m_counters;
|
||||||
std::shared_ptr<softbus::core::plugin_system::PluginManager> m_pluginManager;
|
std::shared_ptr<softbus::core::plugin_system::PluginManager> m_pluginManager;
|
||||||
|
std::shared_ptr<softbus::core::memory::MemoryPool> m_pool;
|
||||||
|
|
||||||
|
PipelineRuntimeConfig m_rtConfig;
|
||||||
|
|
||||||
|
std::unique_ptr<softbus::message_bus::pipeline::stages::framers::ModbusRtuFramer> m_modbusFramer;
|
||||||
|
std::shared_ptr<softbus::message_bus::pipeline::stages::framers::PassthroughFramer> m_passthroughFramer;
|
||||||
|
|
||||||
|
std::mutex m_seqMutex;
|
||||||
|
QHash<QString, std::uint64_t> m_nextFrameSeqByEndpoint;
|
||||||
|
|
||||||
|
std::unique_ptr<softbus::core::threading::MutexQueue<PipelineContext>> m_framedQ;
|
||||||
|
std::unique_ptr<softbus::core::threading::MutexQueue<MergeItem>> m_mergeQ;
|
||||||
|
std::vector<std::thread> m_parseThreads;
|
||||||
|
std::thread m_mergerThread;
|
||||||
|
|
||||||
|
std::mutex m_endpointMutex;
|
||||||
|
QHash<QString, std::shared_ptr<EndpointFramingState>> m_endpointFramers;
|
||||||
|
|
||||||
|
std::mutex m_mergeStateMutex;
|
||||||
|
QHash<QString, MergeState> m_mergeByEndpoint;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace softbus::message_bus::pipeline
|
} // namespace softbus::message_bus::pipeline
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#include "message_bus/pipeline/stages/1_framers/IFramer.h"
|
|
||||||
|
|
||||||
namespace softbus::message_bus::pipeline::stages::framers
|
|
||||||
{
|
|
||||||
|
|
||||||
// Placeholder TU for stage1 concrete framer implementations.
|
|
||||||
|
|
||||||
} // namespace softbus::message_bus::pipeline::stages::framers
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "message_bus/pipeline/PipelineContext.h"
|
#include "message_bus/pipeline/PipelineContext.h"
|
||||||
|
|
||||||
namespace softbus::message_bus::pipeline::stages::framers
|
namespace softbus::message_bus::pipeline::stages::framers
|
||||||
@@ -9,7 +11,10 @@ class IFramer
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IFramer() = default;
|
virtual ~IFramer() = default;
|
||||||
virtual bool frame(softbus::message_bus::pipeline::PipelineContext& ctx) = 0;
|
|
||||||
|
/// Consumes `chunkIn` (StreamChunk 语义);仅在 `completeFramesOut` 中追加 CompleteFrame。
|
||||||
|
virtual void feed(softbus::message_bus::pipeline::PipelineContext& chunkIn,
|
||||||
|
std::vector<softbus::message_bus::pipeline::PipelineContext>& completeFramesOut) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace softbus::message_bus::pipeline::stages::framers
|
} // namespace softbus::message_bus::pipeline::stages::framers
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
// 定义解析器阶段接口
|
||||||
#include "message_bus/pipeline/PipelineContext.h"
|
#include "message_bus/pipeline/PipelineContext.h"
|
||||||
|
|
||||||
namespace softbus::message_bus::pipeline::stages::parsers
|
namespace softbus::message_bus::pipeline::stages::parsers
|
||||||
|
|||||||
Reference in New Issue
Block a user