devices modbus rtu 1.0

This commit is contained in:
flower_linux
2026-04-07 14:20:04 +08:00
parent 28b5082742
commit 51175ffd18
38 changed files with 1006 additions and 745 deletions

View File

@@ -1,254 +1,213 @@
cmake_minimum_required(VERSION 3.19)
project(soft_bus_daemon
LANGUAGES CXX
VERSION 1.0.0
)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
# Output layout
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/plugins)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/plugins)
# 启用AddressSanitizer仅在非Windows平台上
if(NOT WIN32)
set(SANITIZE_FLAGS "-fsanitize=address")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZE_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZE_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZE_FLAGS}")
else()
message(STATUS "AddressSanitizer disabled on Windows because the runtime is unavailable in the MinGW kit.")
endif()
# 设置C++标准
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)
# 查找Qt6守护进程只需要 Core/DBus/Network/SerialPort/Sql
find_package(Qt6 6.5 REQUIRED COMPONENTS Core SerialPort Network Sql DBus)
include(CheckIncludeFileCXX)
# 检测编译平台
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
set(PlatformDir "x86")
else()
set(PlatformDir "x64")
endif()
qt_standard_project_setup()
# 添加软总线模板库
# 1. 日志库
qt_add_library(softbus_template
src/utils/logs/logging.h
src/utils/logs/LogStream.h
)
qt_add_executable(softbus_daemon
main.cpp
src/core/CoreService.cpp
# api 接口
src/api/ipc_dbus/IpcDBus.h
src/api/ipc_dbus/IpcDBus.cpp
# device types
src/devices/DeviceTypes.h
src/devices/DeviceTypes.cpp
# hardware interfaces (Level 2)
src/hardware/interfaces/IStreamDriver.h
src/hardware/interfaces/IFrameDriver.h
src/hardware/interfaces/IDeviceWatcher.h
# hardware drivers (Level 2)
src/hardware/drivers/serial/SerialQtDriver.h
src/hardware/drivers/serial/SerialQtDriver.cpp
src/hardware/drivers/serial/SerialCapabilities.h
# device bus: tree & capabilities
src/device_bus/tree/DeviceTreeModel.h
src/device_bus/tree/DeviceTreeModel.cpp
# device bus: monitor
src/device_bus/monitor/DeviceEvent.h
src/device_bus/monitor/IDeviceMonitor.h
src/device_bus/monitor/DeviceMonitorService.h
src/device_bus/monitor/DeviceMonitorService.cpp
src/device_bus/monitor/UdevDeviceMonitor.h
src/device_bus/monitor/UdevDeviceMonitor.cpp
src/device_bus/monitor/NetlinkDeviceMonitor.h
src/device_bus/monitor/NetlinkDeviceMonitor.cpp
src/device_bus/monitor/WindowsDeviceMonitor.h
src/device_bus/monitor/WindowsDeviceMonitor.cpp
# device bus: facade & manager
src/device_bus/DeviceBus.h
src/device_bus/DeviceBus.cpp
src/device_bus/manager/DeviceBusManager.h
src/device_bus/manager/DeviceBusManager.cpp
src/device_bus/manager/SerialDeviceManager.h
src/device_bus/manager/SerialDeviceManager.cpp
# device bus: registry skeleton
src/device_bus/registry/IDeviceRegistry.h
src/device_bus/registry/DeviceRegistry.h
src/device_bus/registry/DeviceRegistry.cpp
# devices: base skeleton
src/devices/base/IDevice.h
src/devices/base/IPhysicalDevice.h
# devices: physical
src/devices/physical/SerialDevice.h
src/devices/physical/SerialDevice.cpp
# core memory/plugin
src/core/memory/MemoryPool.h
src/core/memory/LockFreeQueue.h
src/core/plugin_system/IProtocolPlugin.h
src/core/plugin_system/PluginManager.h
src/core/plugin_system/PluginManager.cpp
src/message_bus/pipeline/protocol/modbusrtu/ModbusRtuProtocolPlugin.h
src/message_bus/pipeline/protocol/modbusrtu/ModbusRtuProtocolPlugin.cpp
src/core/models/Types.h
src/core/models/MetadataDef.h
src/core/models/DataMapping.h
src/core/models/DOMMessage.h
src/core/metadata/MetadataRegistry.h
src/core/metadata/MetadataRegistry.cpp
src/core/metadata/ProfileRegistry.h
src/core/metadata/ProfileRegistry.cpp
# message bus pipeline
src/message_bus/pipeline/PayloadRef.h
src/message_bus/pipeline/PipelineContext.h
src/message_bus/pipeline/PipelineEngine.h
src/message_bus/pipeline/PipelineEngine.cpp
src/message_bus/pipeline/protocol/ProtocolEnvelope.h
src/message_bus/pipeline/protocol/modbusrtu/ModbusRtuPdu.h
src/message_bus/pipeline/protocol/modbusrtu/ModbusRtuCodec.h
src/message_bus/pipeline/protocol/modbusrtu/ModbusRtuCodec.cpp
src/message_bus/pipeline/stages/1_framers/IFramer.h
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/3_filters/IFilterStage.h
src/message_bus/pipeline/stages/3_filters/ProtocolParseFilter.h
src/message_bus/pipeline/stages/3_filters/ProtocolParseFilter.cpp
src/message_bus/pipeline/stages/4_validators/IValidator.h
# message bus ingress/egress
src/message_bus/ingress/IIngressPort.h
src/message_bus/ingress/DriverIngressAdapter.h
src/message_bus/ingress/DriverIngressAdapter.cpp
src/message_bus/egress/IEgressPort.h
src/message_bus/egress/EgressRouter.h
src/message_bus/egress/EgressRouter.cpp
)
target_include_directories(softbus_daemon
PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
)
target_link_libraries(softbus_daemon
PRIVATE
Qt::Core
Qt6::DBus
Qt6::SerialPort
Qt6::Network
)
# Platform-specific deps
if(UNIX AND NOT APPLE)
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(UDEV QUIET libudev)
endif()
if(UDEV_FOUND)
target_include_directories(softbus_daemon PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_directories(softbus_daemon PRIVATE ${UDEV_LIBRARY_DIRS})
target_link_libraries(softbus_daemon PRIVATE ${UDEV_LIBRARIES})
else()
# Fallback: rely on system linker search path
target_link_libraries(softbus_daemon PRIVATE udev)
endif()
endif()
if(WIN32)
target_link_libraries(softbus_daemon PRIVATE setupapi iphlpapi user32 advapi32)
endif()
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
)
qt_add_executable(softbus_protocol_parse_filter_test
tests/protocol_parse_filter_test.cpp
src/core/metadata/MetadataRegistry.cpp
src/core/metadata/ProfileRegistry.cpp
src/message_bus/pipeline/stages/3_filters/ProtocolParseFilter.cpp
)
qt_add_executable(softbus_modbus_rtu_pdu_codec_test
tests/modbus_rtu_pdu_codec_test.cpp
src/message_bus/pipeline/protocol/modbusrtu/ModbusRtuCodec.cpp
src/message_bus/pipeline/stages/1_framers/ModbusRtuFraming.cpp
)
target_include_directories(softbus_modbus_rtu_pdu_codec_test PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
)
target_link_libraries(softbus_modbus_rtu_pdu_codec_test PRIVATE
Qt6::Core
Qt6::Test
)
target_include_directories(softbus_protocol_parse_filter_test PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
)
target_link_libraries(softbus_protocol_parse_filter_test PRIVATE
Qt6::Core
Qt6::Test
)
endif()
install(TARGETS softbus_daemon
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
cmake_minimum_required(VERSION 3.19)
project(soft_bus_daemon
LANGUAGES CXX
VERSION 1.0.0
)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
# Output layout
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/plugins)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/plugins)
# 启用AddressSanitizer仅在非Windows平台上
if(NOT WIN32)
set(SANITIZE_FLAGS "-fsanitize=address")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZE_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZE_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZE_FLAGS}")
else()
message(STATUS "AddressSanitizer disabled on Windows because the runtime is unavailable in the MinGW kit.")
endif()
# 设置C++标准
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)
# 查找Qt6守护进程只需要 Core/DBus/Network/SerialPort/Sql
find_package(Qt6 6.5 REQUIRED COMPONENTS Core SerialPort Network Sql DBus)
include(CheckIncludeFileCXX)
# 检测编译平台
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
set(PlatformDir "x86")
else()
set(PlatformDir "x64")
endif()
qt_standard_project_setup()
# 添加软总线模板库
# 1. 日志库
qt_add_library(softbus_template
src/utils/logs/logging.h
src/utils/logs/LogStream.h
)
qt_add_executable(softbus_daemon
main.cpp
src/core/CoreService.cpp
# api 接口
src/api/ipc_dbus/IpcDBus.h
src/api/ipc_dbus/IpcDBus.cpp
# device types
src/devices/DeviceTypes.h
src/devices/DeviceTypes.cpp
# hardware interfaces (Level 2)
src/hardware/interfaces/IStreamDriver.h
src/hardware/interfaces/IFrameDriver.h
src/hardware/interfaces/IDeviceWatcher.h
# hardware drivers (Level 2)
src/hardware/drivers/serial/SerialQtDriver.h
src/hardware/drivers/serial/SerialQtDriver.cpp
src/hardware/drivers/serial/SerialCapabilities.h
# device bus: tree & capabilities
src/device_bus/tree/DeviceTreeModel.h
src/device_bus/tree/DeviceTreeModel.cpp
# device bus: monitor
src/device_bus/monitor/DeviceEvent.h
src/device_bus/monitor/IDeviceMonitor.h
src/device_bus/monitor/DeviceMonitorService.h
src/device_bus/monitor/DeviceMonitorService.cpp
src/device_bus/monitor/UdevDeviceMonitor.h
src/device_bus/monitor/UdevDeviceMonitor.cpp
src/device_bus/monitor/NetlinkDeviceMonitor.h
src/device_bus/monitor/NetlinkDeviceMonitor.cpp
src/device_bus/monitor/WindowsDeviceMonitor.h
src/device_bus/monitor/WindowsDeviceMonitor.cpp
# device bus: facade & manager
src/device_bus/DeviceBus.h
src/device_bus/DeviceBus.cpp
src/device_bus/manager/DeviceBusManager.h
src/device_bus/manager/DeviceBusManager.cpp
src/device_bus/manager/SerialDeviceManager.h
src/device_bus/manager/SerialDeviceManager.cpp
# device bus: registry skeleton
src/device_bus/registry/IDeviceRegistry.h
src/device_bus/registry/DeviceRegistry.h
src/device_bus/registry/DeviceRegistry.cpp
# devices: base skeleton
src/devices/base/IDevice.h
src/devices/base/IPhysicalDevice.h
# devices: physical
src/devices/physical/SerialDevice.h
src/devices/physical/SerialDevice.cpp
# core memory/plugin
src/core/memory/MemoryPool.h
src/core/memory/LockFreeQueue.h
src/core/plugin_system/IProtocolPlugin.h
src/core/plugin_system/IProtocolFramerPlugin.h
src/core/plugin_system/PluginManager.h
src/core/plugin_system/PluginManager.cpp
src/core/plugin_system/PluginHost.h
src/core/plugin_system/PluginHost.cpp
plugins/protocols/modbusrtu/parser/ModbusRtuProtocolPlugin.h
plugins/protocols/modbusrtu/parser/ModbusRtuProtocolPlugin.cpp
src/core/models/Types.h
src/core/models/MetadataDef.h
src/core/models/DataMapping.h
src/core/models/DOMMessage.h
src/core/metadata/MetadataRegistry.h
src/core/metadata/MetadataRegistry.cpp
src/core/metadata/ProfileRegistry.h
src/core/metadata/ProfileRegistry.cpp
# message bus pipeline
src/message_bus/pipeline/PayloadRef.h
src/message_bus/pipeline/PipelineContext.h
src/message_bus/pipeline/PipelineEngine.h
src/message_bus/pipeline/PipelineEngine.cpp
src/message_bus/pipeline/protocol/ProtocolEnvelope.h
plugins/protocols/modbusrtu/types/ModbusRtuPdu.h
plugins/protocols/modbusrtu/codec/ModbusRtuCodec.h
plugins/protocols/modbusrtu/codec/ModbusRtuCodec.cpp
plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h
plugins/protocols/modbusrtu/framer/ModbusRtuFraming.cpp
plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h
plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.cpp
src/message_bus/pipeline/stages/1_framers/IFramer.h
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/3_filters/IFilterStage.h
src/message_bus/pipeline/stages/3_filters/ProtocolParseFilter.h
src/message_bus/pipeline/stages/3_filters/ProtocolParseFilter.cpp
src/message_bus/pipeline/stages/4_validators/IValidator.h
# message bus ingress/egress
src/message_bus/ingress/IIngressPort.h
src/message_bus/ingress/DriverIngressAdapter.h
src/message_bus/ingress/DriverIngressAdapter.cpp
src/message_bus/egress/IEgressPort.h
src/message_bus/egress/EgressRouter.h
src/message_bus/egress/EgressRouter.cpp
)
target_include_directories(softbus_daemon
PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
)
target_link_libraries(softbus_daemon
PRIVATE
Qt::Core
Qt6::DBus
Qt6::SerialPort
Qt6::Network
)
# Platform-specific deps
if(UNIX AND NOT APPLE)
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(UDEV QUIET libudev)
endif()
if(UDEV_FOUND)
target_include_directories(softbus_daemon PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_directories(softbus_daemon PRIVATE ${UDEV_LIBRARY_DIRS})
target_link_libraries(softbus_daemon PRIVATE ${UDEV_LIBRARIES})
else()
# Fallback: rely on system linker search path
target_link_libraries(softbus_daemon PRIVATE udev)
endif()
target_link_libraries(softbus_daemon PRIVATE dl)
endif()
if(WIN32)
target_link_libraries(softbus_daemon PRIVATE setupapi iphlpapi user32 advapi32)
endif()
add_subdirectory(plugins)
install(TARGETS softbus_daemon
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,6 +9,19 @@
"modbusRtuMaxFrameBytes": 256,
"modbusRtuInterFrameTimeoutMs": 0
},
"plugins": {
"framers": [
{
"path": "bin/plugins/libmodbus_rtu.so",
"protocol": "modbus_rtu",
"abi": 1
}
],
"hotReload": {
"enabled": true,
"pollIntervalMs": 2000
}
},
"device_tree": [
{
"id": "serial_1",

View File

@@ -1,3 +1,14 @@
# Placeholder for modbus_rtu plugin
add_library(modbus_rtu SHARED plugin_stub.cpp)
set_target_properties(modbus_rtu PROPERTIES OUTPUT_NAME "modbus_rtu")
# Runtime-loadable Modbus RTU parser + framer plugin
add_library(modbus_rtu SHARED
plugin_stub.cpp
../modbusrtu/parser/ModbusRtuProtocolPlugin.cpp
../modbusrtu/codec/ModbusRtuCodec.cpp
../modbusrtu/framer/ModbusRtuFramerPlugin.cpp
../modbusrtu/framer/ModbusRtuFraming.cpp
)
target_include_directories(modbus_rtu PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
)
target_link_libraries(modbus_rtu PRIVATE Qt6::Core)
set_target_properties(modbus_rtu PROPERTIES OUTPUT_NAME "modbus_rtu")

View File

@@ -1,2 +1,17 @@
extern "C" void
softbus_modbus_rtu_plugin_stub() {}
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h"
#include "plugins/protocols/modbusrtu/parser/ModbusRtuProtocolPlugin.h"
extern "C" int softbus_plugin_abi_version()
{
return 1;
}
extern "C" softbus::core::plugin_system::IProtocolPlugin* softbus_create_protocol_plugin()
{
return softbus::core::plugin_system::createModbusRtuProtocolPluginRaw();
}
extern "C" softbus::core::plugin_system::IProtocolFramerPlugin* softbus_create_framer_plugin()
{
return softbus::message_bus::pipeline::protocol::modbusrtu::createModbusRtuFramerPluginRaw();
}

View File

@@ -0,0 +1,37 @@
# Modbus RTU 协议模块(`modbusrtu`
本目录实现 **Modbus RTU** 的成帧、编解码与解析插件,供守护进程静态编译与 `plugins/protocols/modbus_rtu` 动态库(`.so`)共用同一套源码。
## 目录结构
| 子目录 | 职责 |
|--------|------|
| `types/` | PDU 数据模型:`ModbusRtuPdu` 及各类请求/响应结构体,供编解码与 `ProtocolEnvelope` 使用。 |
| `codec/` | ADU 与 `ModbusRtuPdu` 的互转:`decodeModbusRtu` / `encodeModbusRtu`(依赖 `types``framer` 中的 CRC 等工具函数)。 |
| `framer/` | 字节流成帧:`ModbusRtuFraming`长度预测、CRC`ModbusRtuFramerPlugin`Stage1 插件会话)。 |
| `parser/` | Stage2 协议插件:`ModbusRtuProtocolPlugin`,将完整帧解析为 `ProtocolEnvelope` / 兼容 JSON。 |
## 依赖方向(建议遵守)
```
types (无本模块内依赖)
codec、framer (可依赖 typescodec 使用 framer 中的 crc16/crcOk
parser (依赖 codec、types不反向依赖 framer 插件类)
```
成帧与解析在流水线中分属不同阶段;**解析侧只应处理 `CompleteFrame`**,与 `PipelineContext::frameKind` 约定一致。
## 与动态库的关系
- 动态库入口:`plugins/protocols/modbus_rtu/plugin_stub.cpp`,导出 `softbus_create_protocol_plugin` / `softbus_create_framer_plugin` 等符号。
- 主工程 `CMakeLists.txt` 同样列出本目录下源文件,用于无 `.so` 时的内置链接;路径与 include 均以 `plugins/protocols/modbusrtu/...` 为准。
## 配置提示
`daemon_config.json``plugins.framers` 中配置 `libmodbus_rtu.so` 路径即可加载成帧插件;解析插件加载方式与 `PluginHost` / 工程内注册逻辑一致。
## 扩展其它协议时的参考
新增协议(如 Modbus TCP、CANopen建议沿用类似分层**types → codec/framer → parser**,并在各自 `plugin_stub` 中导出统一 ABI 工厂函数,由 `PluginHost` 注册到 `PluginManager`

View File

@@ -1,9 +1,9 @@
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuCodec.h"
#include "plugins/protocols/modbusrtu/codec/ModbusRtuCodec.h"
#include <variant>
#include "message_bus/pipeline/PipelineContext.h"
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h"
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
namespace softbus::message_bus::pipeline::protocol
{
@@ -12,12 +12,12 @@ namespace
{
using softbus::message_bus::pipeline::BusDirection;
using softbus::message_bus::pipeline::stages::framers::modbusRtuCrc16;
using softbus::message_bus::pipeline::stages::framers::modbusRtuCrcOk;
using softbus::message_bus::pipeline::protocol::modbusrtu::crc16;
using softbus::message_bus::pipeline::protocol::modbusrtu::crcOk;
void appendCrcLe(QByteArray& adu)
{
const auto crc = modbusRtuCrc16(reinterpret_cast<const std::uint8_t*>(adu.constData()),
const auto crc = crc16(reinterpret_cast<const std::uint8_t*>(adu.constData()),
static_cast<std::size_t>(adu.size()));
adu.append(static_cast<char>(crc & 0xFF));
adu.append(static_cast<char>((crc >> 8) & 0xFF));
@@ -47,7 +47,7 @@ bool decodeModbusRtu(BusDirection dir,
if (n < 5) {
return fail("exception_incomplete");
}
if (!modbusRtuCrcOk(p, 5)) {
if (!crcOk(p, 5)) {
return fail("exception_crc");
}
out = ModbusExceptionPdu{p[0], p[1], p[2]};
@@ -57,7 +57,7 @@ bool decodeModbusRtu(BusDirection dir,
const std::uint8_t unit = p[0];
const std::uint8_t fc = p[1];
auto parseReadReq = [&]() -> bool {
if (n != 8 || !modbusRtuCrcOk(p, 8)) {
if (n != 8 || !crcOk(p, 8)) {
return false;
}
const std::uint16_t addr = static_cast<std::uint16_t>((p[2] << 8) | p[3]);
@@ -81,7 +81,7 @@ bool decodeModbusRtu(BusDirection dir,
};
auto parseWriteSingleReqOrResp = [&]() -> bool {
if (n != 8 || !modbusRtuCrcOk(p, 8)) {
if (n != 8 || !crcOk(p, 8)) {
return false;
}
const std::uint16_t addr = static_cast<std::uint16_t>((p[2] << 8) | p[3]);
@@ -116,7 +116,7 @@ bool decodeModbusRtu(BusDirection dir,
if (n != static_cast<std::size_t>(9) + byteCount) {
return false;
}
if (!modbusRtuCrcOk(p, n)) {
if (!crcOk(p, n)) {
return false;
}
if (fc == 0x0F) {
@@ -141,7 +141,7 @@ bool decodeModbusRtu(BusDirection dir,
};
auto parseWriteMultiResp = [&]() -> bool {
if (n != 8 || !modbusRtuCrcOk(p, 8)) {
if (n != 8 || !crcOk(p, 8)) {
return false;
}
const std::uint16_t start = static_cast<std::uint16_t>((p[2] << 8) | p[3]);
@@ -165,7 +165,7 @@ bool decodeModbusRtu(BusDirection dir,
}
const std::uint8_t bc = p[2];
const std::size_t total = static_cast<std::size_t>(5) + bc;
if (n < total || !modbusRtuCrcOk(p, total)) {
if (n < total || !crcOk(p, total)) {
return false;
}
if (fc == 0x01) {

View File

@@ -6,7 +6,7 @@
#include <QByteArray>
#include <QString>
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuPdu.h"
#include "plugins/protocols/modbusrtu/types/ModbusRtuPdu.h"
namespace softbus::message_bus::pipeline
{

View File

@@ -0,0 +1,173 @@
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h"
#include <algorithm>
#include <cstring>
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
#include "utils/logs/logging.h"
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
namespace
{
class ModbusRtuFramerSession final : public softbus::core::plugin_system::IProtocolFramerSession
{
public:
ModbusRtuFramerSession(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerPluginConfig cfg,
QString endpoint)
: m_pool(std::move(pool))
, m_cfg(cfg)
, m_endpoint(std::move(endpoint))
{
m_buf.resize(m_cfg.maxAdu);
}
void feedChunk(softbus::message_bus::pipeline::PipelineContext& in,
std::vector<softbus::message_bus::pipeline::PipelineContext>& out) override
{
using softbus::message_bus::pipeline::FrameKind;
if (!m_pool || !in.payload.valid()) {
return;
}
const std::uint8_t* p = in.payload.bytes();
const std::size_t n = in.payloadSize ? in.payloadSize : in.payload.length;
if (!p || n == 0) {
return;
}
m_lastRx = std::chrono::steady_clock::now();
if (m_cfg.interFrameTimeoutMs > 0 && m_len > 0) {
const auto age = std::chrono::duration_cast<std::chrono::milliseconds>(m_lastRx - m_lastActivity).count();
if (age >= m_cfg.interFrameTimeoutMs) {
m_len = 0;
}
}
m_lastActivity = m_lastRx;
if (m_len + n > m_cfg.maxAdu) {
LOG_WARNING() << "ModbusRtuFramerPlugin: overflow endpoint=" << m_endpoint;
m_len = 0;
return;
}
std::memcpy(m_buf.data() + m_len, p, n);
m_len += n;
while (m_len > 0) {
auto pred = predictAduLength(m_buf.data(), m_len, m_cfg.maxAdu);
if (!pred || m_len < *pred) {
break;
}
if (!crcOk(m_buf.data(), *pred)) {
if (m_len <= 1) {
m_len = 0;
break;
}
std::memmove(m_buf.data(), m_buf.data() + 1, m_len - 1);
--m_len;
continue;
}
auto blk = m_pool->allocate(*pred);
if (!blk || !blk->data || blk->capacity < *pred) {
in.framerError = QStringLiteral("pool_exhausted");
return;
}
std::memcpy(blk->data, m_buf.data(), *pred);
blk->size = *pred;
softbus::message_bus::pipeline::PipelineContext framed;
framed.deviceId = in.deviceId;
framed.stableKey = in.stableKey;
framed.endpoint = in.endpoint;
framed.timestamp = in.timestamp;
framed.protocolHint = in.protocolHint;
framed.traceId = in.traceId;
framed.direction = in.direction;
framed.frameKind = FrameKind::CompleteFrame;
framed.payload.block = std::move(blk);
framed.payload.offset = 0;
framed.payload.length = *pred;
framed.payloadSize = *pred;
out.push_back(std::move(framed));
const std::size_t rest = m_len - *pred;
if (rest > 0) {
std::memmove(m_buf.data(), m_buf.data() + *pred, rest);
}
m_len = rest;
}
in.payload = {};
in.payloadSize = 0;
in.frameKind = FrameKind::StreamChunk;
}
void reset() override { m_len = 0; }
void shutdown() override { m_len = 0; }
private:
std::shared_ptr<softbus::core::memory::MemoryPool> m_pool;
ModbusRtuFramerPluginConfig m_cfg;
QString m_endpoint;
std::vector<std::uint8_t> m_buf;
std::size_t m_len{0};
std::chrono::steady_clock::time_point m_lastRx{std::chrono::steady_clock::now()};
std::chrono::steady_clock::time_point m_lastActivity{std::chrono::steady_clock::now()};
};
} // namespace
ModbusRtuFramerPlugin::ModbusRtuFramerPlugin(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerPluginConfig cfg)
: m_pool(std::move(pool))
, m_cfg(cfg)
{
}
QString ModbusRtuFramerPlugin::pluginId() const
{
return QStringLiteral("modbus_rtu_framer");
}
bool ModbusRtuFramerPlugin::supports(const QString& protocolHint) const
{
return isModbusRtuHint(protocolHint);
}
void ModbusRtuFramerPlugin::bindMemoryPool(std::shared_ptr<softbus::core::memory::MemoryPool> pool)
{
m_pool = std::move(pool);
}
std::unique_ptr<softbus::core::plugin_system::IProtocolFramerSession>
ModbusRtuFramerPlugin::createFramerSession(const QString& endpoint, const QJsonObject& params)
{
if (!m_pool) {
return {};
}
ModbusRtuFramerPluginConfig cfg = m_cfg;
if (params.contains(QStringLiteral("modbusRtuMaxFrameBytes"))) {
cfg.maxAdu = static_cast<std::size_t>(
std::max(8, params.value(QStringLiteral("modbusRtuMaxFrameBytes")).toInt(static_cast<int>(cfg.maxAdu))));
}
if (params.contains(QStringLiteral("modbusRtuInterFrameTimeoutMs"))) {
cfg.interFrameTimeoutMs = params.value(QStringLiteral("modbusRtuInterFrameTimeoutMs")).toInt(cfg.interFrameTimeoutMs);
}
return std::make_unique<ModbusRtuFramerSession>(m_pool, cfg, endpoint);
}
std::shared_ptr<softbus::core::plugin_system::IProtocolFramerPlugin> makeModbusRtuFramerPlugin(
std::shared_ptr<softbus::core::memory::MemoryPool> pool, ModbusRtuFramerPluginConfig cfg)
{
return std::make_shared<ModbusRtuFramerPlugin>(std::move(pool), cfg);
}
softbus::core::plugin_system::IProtocolFramerPlugin* createModbusRtuFramerPluginRaw()
{
return new ModbusRtuFramerPlugin(nullptr, {});
}
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -0,0 +1,44 @@
#pragma once
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
#include <QHash>
#include <QString>
#include "core/memory/MemoryPool.h"
#include "core/plugin_system/IProtocolFramerPlugin.h"
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
struct ModbusRtuFramerPluginConfig
{
std::size_t maxAdu{256};
int interFrameTimeoutMs{0};
};
class ModbusRtuFramerPlugin final : public softbus::core::plugin_system::IProtocolFramerPlugin
{
public:
explicit ModbusRtuFramerPlugin(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerPluginConfig cfg = {});
QString pluginId() const override;
bool supports(const QString& protocolHint) const override;
void bindMemoryPool(std::shared_ptr<softbus::core::memory::MemoryPool> pool) override;
std::unique_ptr<softbus::core::plugin_system::IProtocolFramerSession> createFramerSession(
const QString& endpoint, const QJsonObject& params) override;
private:
std::shared_ptr<softbus::core::memory::MemoryPool> m_pool;
ModbusRtuFramerPluginConfig m_cfg;
};
std::shared_ptr<softbus::core::plugin_system::IProtocolFramerPlugin> makeModbusRtuFramerPlugin(
std::shared_ptr<softbus::core::memory::MemoryPool> pool, ModbusRtuFramerPluginConfig cfg = {});
softbus::core::plugin_system::IProtocolFramerPlugin* createModbusRtuFramerPluginRaw();
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -0,0 +1,88 @@
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
std::uint16_t crc16(const std::uint8_t* data, std::size_t len)
{
std::uint16_t crc = 0xFFFF;
for (std::size_t i = 0; i < len; ++i) {
crc ^= data[i];
for (int k = 0; k < 8; ++k) {
crc = (crc & 1) ? static_cast<std::uint16_t>((crc >> 1) ^ 0xA001)
: static_cast<std::uint16_t>(crc >> 1);
}
}
return crc;
}
bool crcOk(const std::uint8_t* adu, std::size_t aduLen)
{
if (!adu || aduLen < 4) {
return false;
}
const std::size_t body = aduLen - 2;
const std::uint16_t got =
static_cast<std::uint16_t>(adu[body] | (static_cast<std::uint16_t>(adu[body + 1]) << 8));
return got == crc16(adu, body);
}
static bool isReadFc(std::uint8_t fc)
{
return fc == 0x01 || fc == 0x02 || fc == 0x03 || fc == 0x04;
}
std::optional<std::size_t> predictAduLength(const std::uint8_t* buf, std::size_t len, std::size_t maxAdu)
{
if (!buf || len < 2) {
return std::nullopt;
}
const std::uint8_t fc = buf[1];
if (fc & 0x80) {
return len >= 5 ? std::optional<std::size_t>(5) : std::nullopt;
}
if (isReadFc(fc)) {
if (len >= 8 && crcOk(buf, 8)) {
return std::optional<std::size_t>(8); // read request
}
if (len < 3) {
return std::nullopt;
}
const std::size_t total = static_cast<std::size_t>(5) + buf[2];
if (total > maxAdu || len < total) {
return std::nullopt; // wait more bytes
}
return crcOk(buf, total) ? std::optional<std::size_t>(total) : std::nullopt;
}
if (fc == 0x05 || fc == 0x06) {
return len >= 8 ? std::optional<std::size_t>(8) : std::nullopt;
}
if (fc == 0x16) {
return len >= 10 ? std::optional<std::size_t>(10) : std::nullopt;
}
if (fc == 0x0F || fc == 0x10) {
if (len >= 8 && crcOk(buf, 8)) {
return std::optional<std::size_t>(8); // response
}
if (len < 7) {
return std::nullopt;
}
const std::size_t req = static_cast<std::size_t>(9) + buf[6];
if (req > maxAdu || len < req) {
return std::nullopt;
}
return crcOk(buf, req) ? std::optional<std::size_t>(req) : std::nullopt;
}
return len >= 8 ? std::optional<std::size_t>(8) : std::nullopt;
}
bool isModbusRtuHint(const QString& hint)
{
const QString h = hint.trimmed().toLower();
return h == QStringLiteral("modbus_rtu") || h == QStringLiteral("modbus-rtu")
|| h == QStringLiteral("modbusrtu");
}
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -0,0 +1,17 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <optional>
#include <QString>
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
std::uint16_t crc16(const std::uint8_t* data, std::size_t len);
bool crcOk(const std::uint8_t* adu, std::size_t aduLen);
std::optional<std::size_t> predictAduLength(const std::uint8_t* buf, std::size_t len, std::size_t maxAdu);
bool isModbusRtuHint(const QString& hint);
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -1,10 +1,10 @@
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuProtocolPlugin.h"
#include "plugins/protocols/modbusrtu/parser/ModbusRtuProtocolPlugin.h"
#include "core/plugin_system/IProtocolPlugin.h"
#include "message_bus/pipeline/PipelineContext.h"
#include "message_bus/pipeline/protocol/ProtocolEnvelope.h"
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuCodec.h"
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h"
#include "plugins/protocols/modbusrtu/codec/ModbusRtuCodec.h"
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
#include <QJsonArray>
#include <QJsonObject>
@@ -227,7 +227,7 @@ public:
bool supports(const QString& protocolHint) const override
{
return softbus::message_bus::pipeline::stages::framers::protocolHintIsModbusRtu(protocolHint);
return softbus::message_bus::pipeline::protocol::modbusrtu::isModbusRtuHint(protocolHint);
}
std::unique_ptr<IProtocolSession> createSession() override
@@ -241,4 +241,9 @@ std::shared_ptr<IProtocolPlugin> makeModbusRtuProtocolPlugin()
return std::make_shared<ModbusRtuProtocolPlugin>();
}
IProtocolPlugin* createModbusRtuProtocolPluginRaw()
{
return new ModbusRtuProtocolPlugin();
}
} // namespace softbus::core::plugin_system

View File

@@ -8,5 +8,6 @@ namespace softbus::core::plugin_system
{
std::shared_ptr<IProtocolPlugin> makeModbusRtuProtocolPlugin();
IProtocolPlugin* createModbusRtuProtocolPluginRaw();
} // namespace softbus::core::plugin_system

View File

@@ -0,0 +1,38 @@
#pragma once
#include <memory>
#include <vector>
#include <QJsonObject>
#include <QString>
#include "core/memory/MemoryPool.h"
#include "message_bus/pipeline/PipelineContext.h"
namespace softbus::core::plugin_system
{
class IProtocolFramerSession
{
public:
virtual ~IProtocolFramerSession() = default;
virtual void feedChunk(softbus::message_bus::pipeline::PipelineContext& in,
std::vector<softbus::message_bus::pipeline::PipelineContext>& out) = 0;
virtual void reset() = 0;
virtual void shutdown() = 0;
};
class IProtocolFramerPlugin
{
public:
virtual ~IProtocolFramerPlugin() = default;
virtual QString pluginId() const = 0;
virtual bool supports(const QString& protocolHint) const = 0;
virtual void bindMemoryPool(std::shared_ptr<softbus::core::memory::MemoryPool> pool) = 0;
virtual std::unique_ptr<IProtocolFramerSession> createFramerSession(const QString& endpoint,
const QJsonObject& params) = 0;
};
} // namespace softbus::core::plugin_system

View File

@@ -0,0 +1,125 @@
#include "core/plugin_system/PluginHost.h"
#include <dlfcn.h>
#include "core/plugin_system/IProtocolFramerPlugin.h"
#include "core/plugin_system/IProtocolPlugin.h"
namespace softbus::core::plugin_system
{
namespace
{
using GetAbiVersionFn = int (*)();
using CreateProtocolPluginFn = IProtocolPlugin* (*)();
using CreateFramerPluginFn = IProtocolFramerPlugin* (*)();
} // namespace
PluginHost::PluginHost(std::shared_ptr<PluginManager> manager)
: m_manager(std::move(manager))
{
}
PluginHost::~PluginHost()
{
unloadAll();
}
bool PluginHost::loadPlugin(const QString& path, QString* err)
{
if (!m_manager) {
if (err) *err = QStringLiteral("plugin_manager_null");
return false;
}
if (m_loaded.contains(path)) {
return true;
}
void* handle = dlopen(path.toUtf8().constData(), RTLD_NOW | RTLD_LOCAL);
if (!handle) {
if (err) *err = QString::fromUtf8(dlerror());
return false;
}
auto* getAbi = reinterpret_cast<GetAbiVersionFn>(dlsym(handle, "softbus_plugin_abi_version"));
const int abi = getAbi ? getAbi() : 1;
if (abi != 1) {
if (err) *err = QStringLiteral("abi_mismatch");
dlclose(handle);
return false;
}
auto* createParser = reinterpret_cast<CreateProtocolPluginFn>(dlsym(handle, "softbus_create_protocol_plugin"));
QString protocolPluginId;
if (createParser) {
std::shared_ptr<IProtocolPlugin> p(createParser());
if (p) {
protocolPluginId = p->pluginId();
m_manager->registerProtocolPlugin(p);
}
}
auto* createFramer = reinterpret_cast<CreateFramerPluginFn>(dlsym(handle, "softbus_create_framer_plugin"));
QString framerPluginId;
if (createFramer) {
std::shared_ptr<IProtocolFramerPlugin> p(createFramer());
if (p) {
framerPluginId = p->pluginId();
m_manager->registerProtocolFramerPlugin(p);
}
}
LoadedPlugin lp;
lp.path = path;
lp.protocolPluginId = protocolPluginId;
lp.framerPluginId = framerPluginId;
lp.abiVersion = abi;
lp.handle = handle;
m_loaded.insert(path, lp);
return true;
}
bool PluginHost::reloadPlugin(const QString& path, QString* err)
{
QString localErr;
if (!unloadPlugin(path, &localErr)) {
// allow reload when not loaded
localErr.clear();
}
const bool ok = loadPlugin(path, &localErr);
if (!ok && err) {
*err = localErr;
}
return ok;
}
bool PluginHost::unloadPlugin(const QString& path, QString* err)
{
auto it = m_loaded.find(path);
if (it == m_loaded.end()) {
if (err) *err = QStringLiteral("not_loaded");
return false;
}
if (m_manager) {
if (!it.value().protocolPluginId.isEmpty()) {
m_manager->unregisterProtocolPlugin(it.value().protocolPluginId);
}
if (!it.value().framerPluginId.isEmpty()) {
m_manager->unregisterProtocolFramerPlugin(it.value().framerPluginId);
}
}
if (it.value().handle) {
dlclose(it.value().handle);
}
m_loaded.erase(it);
return true;
}
void PluginHost::unloadAll()
{
const auto keys = m_loaded.keys();
for (const auto& k : keys) {
QString ignore;
(void)unloadPlugin(k, &ignore);
}
}
} // namespace softbus::core::plugin_system

View File

@@ -0,0 +1,39 @@
#pragma once
#include <memory>
#include <QHash>
#include <QString>
#include "core/plugin_system/PluginManager.h"
namespace softbus::core::plugin_system
{
class PluginHost
{
public:
struct LoadedPlugin
{
QString path;
QString protocolPluginId;
QString framerPluginId;
int abiVersion{1};
void* handle{nullptr};
};
explicit PluginHost(std::shared_ptr<PluginManager> manager);
~PluginHost();
bool loadPlugin(const QString& path, QString* err = nullptr);
bool reloadPlugin(const QString& path, QString* err = nullptr);
bool unloadPlugin(const QString& path, QString* err = nullptr);
private:
void unloadAll();
std::shared_ptr<PluginManager> m_manager;
QHash<QString, LoadedPlugin> m_loaded;
};
} // namespace softbus::core::plugin_system

View File

@@ -21,4 +21,32 @@ std::shared_ptr<IProtocolPlugin> PluginManager::selectProtocolPlugin(const QStri
return {};
}
void PluginManager::unregisterProtocolPlugin(const QString& pluginId)
{
m_protocolPlugins.remove(pluginId);
}
void PluginManager::registerProtocolFramerPlugin(std::shared_ptr<IProtocolFramerPlugin> plugin)
{
if (!plugin) {
return;
}
m_protocolFramerPlugins.insert(plugin->pluginId(), std::move(plugin));
}
std::shared_ptr<IProtocolFramerPlugin> PluginManager::selectProtocolFramerPlugin(const QString& protocolHint) const
{
for (auto it = m_protocolFramerPlugins.constBegin(); it != m_protocolFramerPlugins.constEnd(); ++it) {
if (it.value() && it.value()->supports(protocolHint)) {
return it.value();
}
}
return {};
}
void PluginManager::unregisterProtocolFramerPlugin(const QString& pluginId)
{
m_protocolFramerPlugins.remove(pluginId);
}
} // namespace softbus::core::plugin_system

View File

@@ -5,6 +5,7 @@
#include <QHash>
#include <QString>
#include "core/plugin_system/IProtocolFramerPlugin.h"
#include "core/plugin_system/IProtocolPlugin.h"
namespace softbus::core::plugin_system
@@ -17,9 +18,14 @@ public:
void registerProtocolPlugin(std::shared_ptr<IProtocolPlugin> plugin);
std::shared_ptr<IProtocolPlugin> selectProtocolPlugin(const QString& protocolHint) const;
void unregisterProtocolPlugin(const QString& pluginId);
void registerProtocolFramerPlugin(std::shared_ptr<IProtocolFramerPlugin> plugin);
std::shared_ptr<IProtocolFramerPlugin> selectProtocolFramerPlugin(const QString& protocolHint) const;
void unregisterProtocolFramerPlugin(const QString& pluginId);
private:
QHash<QString, std::shared_ptr<IProtocolPlugin>> m_protocolPlugins;
QHash<QString, std::shared_ptr<IProtocolFramerPlugin>> m_protocolFramerPlugins;
};
} // namespace softbus::core::plugin_system

View File

@@ -3,6 +3,7 @@
#include <chrono>
#include <memory>
#include <QJsonArray>
#include <QSet>
// #include <algorithm>
@@ -10,7 +11,7 @@
#include "message_bus/ingress/DriverIngressAdapter.h"
#include "message_bus/pipeline/PipelineEngine.h"
#include "core/plugin_system/PluginManager.h"
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuProtocolPlugin.h"
#include "plugins/protocols/modbusrtu/parser/ModbusRtuProtocolPlugin.h"
#include "device_bus/manager/SerialDeviceManager.h"
#include "devices/DeviceTypes.h"
#include "utils/logs/logging.h"
@@ -57,7 +58,23 @@ bool DeviceBusManager::initialize(const QJsonObject& rootConfig)
if (!m_pluginManager) {
m_pluginManager = std::make_shared<softbus::core::plugin_system::PluginManager>();
}
if (!m_pluginHost) {
m_pluginHost = std::make_unique<softbus::core::plugin_system::PluginHost>(m_pluginManager);
}
m_pluginManager->registerProtocolPlugin(softbus::core::plugin_system::makeModbusRtuProtocolPlugin());
const auto pluginsObj = rootConfig.value(QStringLiteral("plugins")).toObject();
const auto framerArr = pluginsObj.value(QStringLiteral("framers")).toArray();
for (const auto& v : framerArr) {
const auto obj = v.toObject();
const QString path = obj.value(QStringLiteral("path")).toString();
if (path.isEmpty()) {
continue;
}
QString err;
if (!m_pluginHost->loadPlugin(path, &err)) {
LOG_WARNING() << "DeviceBusManager: framer plugin load failed path=" << path << "err=" << err;
}
}
if (m_pipelineEngine) {
m_pipelineEngine->setMemoryPool(m_memoryPool);
m_pipelineEngine->loadRuntimeConfigFromJson(rootConfig.value(QStringLiteral("pipeline")).toObject());

View File

@@ -13,6 +13,7 @@
#include "core/memory/MemoryPool.h"
#include "core/plugin_system/PluginManager.h"
#include "core/plugin_system/PluginHost.h"
#include "device_bus/tree/DeviceTreeModel.h"
#include "device_bus/registry/IDeviceRegistry.h"
#include "devices/DeviceTypes.h"
@@ -57,6 +58,7 @@ private:
softbus::device_bus::registry::IDeviceRegistry* m_registry = nullptr;
std::shared_ptr<softbus::core::memory::MemoryPool> m_memoryPool;
std::shared_ptr<softbus::core::plugin_system::PluginManager> m_pluginManager;
std::unique_ptr<softbus::core::plugin_system::PluginHost> m_pluginHost;
std::unique_ptr<softbus::message_bus::pipeline::PipelineEngine> m_pipelineEngine;
std::unique_ptr<softbus::message_bus::ingress::DriverIngressAdapter> m_ingressAdapter;
std::unique_ptr<softbus::message_bus::egress::EgressRouter> m_egressRouter;

View File

@@ -6,7 +6,8 @@
#include <chrono>
#include <functional>
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h"
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h"
#include "message_bus/pipeline/stages/3_filters/ProtocolParseFilter.h"
#include "utils/logs/logging.h"
@@ -21,8 +22,7 @@ struct EndpointFramingState
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&, std::vector<PipelineContext>&)> feed;
std::function<void(PipelineContext)> pushFramed;
explicit EndpointFramingState(std::size_t cap)
@@ -36,11 +36,6 @@ 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)) {
@@ -48,10 +43,8 @@ void endpointFramerThread(std::shared_ptr<EndpointFramingState> st)
continue;
}
std::vector<PipelineContext> out;
if (protocolHintIsModbusRtu(ch.protocolHint)) {
modbus->feed(ch, out);
} else if (st->pass) {
st->pass->feed(ch, out);
if (st->feed) {
st->feed(ch, out);
}
for (auto& x : out) {
x.frameSeq = st->nextSeq++;
@@ -98,9 +91,9 @@ void PipelineEngine::loadRuntimeConfigFromJson(const QJsonObject& obj)
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>(
m_rtConfig.modbusMaxAdu = static_cast<std::size_t>(
std::max(8, obj.value(QStringLiteral("modbusRtuMaxFrameBytes")).toInt(256)));
m_rtConfig.modbus.interFrameTimeoutMs = obj.value(QStringLiteral("modbusRtuInterFrameTimeoutMs")).toInt(0);
m_rtConfig.modbusInterFrameTimeoutMs = obj.value(QStringLiteral("modbusRtuInterFrameTimeoutMs")).toInt(0);
}
void PipelineEngine::start()
@@ -115,12 +108,15 @@ void PipelineEngine::start()
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_pluginManager) {
softbus::message_bus::pipeline::protocol::modbusrtu::ModbusRtuFramerPluginConfig cfg;
cfg.maxAdu = m_rtConfig.modbusMaxAdu;
cfg.interFrameTimeoutMs = m_rtConfig.modbusInterFrameTimeoutMs;
m_pluginManager->registerProtocolFramerPlugin(
softbus::message_bus::pipeline::protocol::modbusrtu::makeModbusRtuFramerPlugin(m_pool, cfg));
}
// 如果启用并行管道 则创建帧队列和解析线程 如果启用有序出口 则创建合并队列和合并线程
// 并行管道是用来处理多个端点的数据流的
@@ -182,8 +178,11 @@ void PipelineEngine::stop()
m_framedQ.reset();
m_mergeQ.reset();
m_modbusFramer.reset();
m_passthroughFramer.reset();
{
std::lock_guard<std::mutex> lk(m_framerSessionMutex);
m_framerSessions.clear();
}
}
// 功能 :清空 ingress 队列
// 参数 :无
@@ -266,8 +265,9 @@ void PipelineEngine::ensureEndpointFramer(const QString& endpoint)
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->feed = [this](PipelineContext& in, std::vector<PipelineContext>& out) {
this->dispatchFramer(in, out);
};
st->pushFramed = [this](PipelineContext c) {
if (!m_framedQ) {
return;
@@ -286,17 +286,30 @@ void PipelineEngine::ensureEndpointFramer(const QString& endpoint)
// 逻辑 :根据协议提示 选择合适的帧解析器 进行帧解析
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);
if (m_pluginManager) {
auto plugin = m_pluginManager->selectProtocolFramerPlugin(chunkIn.protocolHint);
if (plugin) {
plugin->bindMemoryPool(m_pool);
const QString key = chunkIn.endpoint + QStringLiteral("|") + plugin->pluginId();
std::lock_guard<std::mutex> lk(m_framerSessionMutex);
auto it = m_framerSessions.find(key);
if (it == m_framerSessions.end() || !it.value()) {
QJsonObject params;
params.insert(QStringLiteral("modbusRtuMaxFrameBytes"), static_cast<int>(m_rtConfig.modbusMaxAdu));
params.insert(QStringLiteral("modbusRtuInterFrameTimeoutMs"), m_rtConfig.modbusInterFrameTimeoutMs);
auto s = plugin->createFramerSession(chunkIn.endpoint, params);
m_framerSessions.insert(
key, s ? std::shared_ptr<softbus::core::plugin_system::IProtocolFramerSession>(std::move(s)) : nullptr);
it = m_framerSessions.find(key);
}
if (it != m_framerSessions.end() && it.value()) {
it.value()->feedChunk(chunkIn, completeFramesOut);
return;
}
}
} else if (m_passthroughFramer) {
}
if (m_passthroughFramer) {
m_passthroughFramer->feed(chunkIn, completeFramesOut);
}
}
// 功能 :分配帧序列号

View File

@@ -11,11 +11,11 @@
#include <QString>
#include "core/plugin_system/PluginManager.h"
#include "core/plugin_system/IProtocolFramerPlugin.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/stages/1_framers/ModbusRtuFramer.h"
#include "message_bus/pipeline/stages/1_framers/PassthroughFramer.h"
namespace softbus::message_bus::pipeline
@@ -30,7 +30,8 @@ struct PipelineRuntimeConfig
std::size_t framedQueueMax{4096};
std::size_t maxReorderDepth{256};
softbus::message_bus::pipeline::stages::framers::ModbusRtuFramerConfig modbus;
std::size_t modbusMaxAdu{256};
int modbusInterFrameTimeoutMs{0};
};
struct EndpointFramingState;
@@ -106,8 +107,9 @@ private:
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_framerSessionMutex;
QHash<QString, std::shared_ptr<softbus::core::plugin_system::IProtocolFramerSession>> m_framerSessions;
std::mutex m_seqMutex;
QHash<QString, std::uint64_t> m_nextFrameSeqByEndpoint;

View File

@@ -2,7 +2,7 @@
#include <variant>
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuPdu.h"
#include "plugins/protocols/modbusrtu/types/ModbusRtuPdu.h"
namespace softbus::message_bus::pipeline::protocol
{

View File

@@ -1,201 +0,0 @@
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFramer.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <cstring>
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h"
#include "utils/logs/logging.h"
namespace softbus::message_bus::pipeline::stages::framers
{
ModbusRtuFramer::ModbusRtuFramer(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerConfig cfg,
QString exclusiveEndpoint)
: m_pool(std::move(pool))
, m_cfg(cfg)
, m_exclusiveEndpoint(std::move(exclusiveEndpoint))
{
}
// 功能 :追加数据块 将数据块追加到会话中 通过时间戳记录最后接收时间 时间间隔超过最大帧间隔时间 则清空会话 如果超过最大ADU长度 则记录溢出 并记录溢出数量
void ModbusRtuFramer::appendChunk(Session& session, const std::uint8_t* p, std::size_t n)
{
if (!p || n == 0) {
return;
}
session.lastRx = std::chrono::steady_clock::now();
if (session.buf.size() < m_cfg.maxAdu) {
session.buf.resize(m_cfg.maxAdu);
}
if (session.len + n > m_cfg.maxAdu) {
++m_cnt.overflow;
session.len = 0;
LOG_WARNING() << "ModbusRtuFramer: buffer_overflow maxAdu=" << m_cfg.maxAdu;
return;
}
// `p/n` 来自 chunkIn.payload.bytes(),也就是串口读回的原始字节块(当前函数会再次拷贝进 session.buf
const std::size_t beforeLen = session.len;
std::ostringstream oss;
oss << std::hex << std::setfill('0');
const std::size_t show = std::min(n, static_cast<std::size_t>(16));
for (std::size_t i = 0; i < show; ++i) {
oss << std::setw(2) << static_cast<int>(p[i]);
if (i + 1 < show) {
oss << ' ';
}
}
if (n > show) {
oss << " ...";
}
const QString hexPreview = QString::fromStdString(oss.str());
// LOG_INFO() << "ModbusRtuFramer: appendChunk n=" << n << " session.len(before)=" << beforeLen
// << " p=" << static_cast<const void*>(p) << " bytes(16)=" << hexPreview;
std::memcpy(session.buf.data() + session.len, p, n);
session.len += n;
m_cnt.bytesIn += n;
}
// 功能 :尝试消费缓冲区 如果时间间隔超过最大帧间隔时间 则清空会话 如果超过最大ADU长度 则记录溢出 并记录溢出数量
void ModbusRtuFramer::tryConsumeBuffer(Session& session,
const QString& endpoint,
QString traceId,
int deviceId,
const QString& stableKey,
const QString& protocolHint,
QDateTime ts,
BusDirection dir,
std::vector<softbus::message_bus::pipeline::PipelineContext>& out,
QString* framerErr)
{
(void)framerErr;
if (m_cfg.interFrameTimeoutMs > 0 && session.len > 0) {
const auto now = std::chrono::steady_clock::now();
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - session.lastRx).count();
if (ms >= m_cfg.interFrameTimeoutMs) {
session.len = 0;
}
}
while (session.len > 0) {
const std::uint8_t* buf = session.buf.data();
auto pred =
predictModbusRtuAduLength(buf, session.len, m_cfg.maxAdu);
if (!pred) {
break;
}
if (session.len < *pred) {
break;
}
// std::ostringstream oss;
// oss << std::hex << std::setfill('0');
// for (std::size_t i = 0; i < session.len; ++i) {
// oss << std::setw(2) << static_cast<int>(buf[i]);
// if (i + 1 < session.len) {
// oss << ' ';
// }
// }
// const QString hexPreview = QString::fromStdString(oss.str());
// LOG_INFO() << "ModbusRtuFramer: tryConsumeBuffer buf(16)=" << hexPreview << " session.len=" << session.len << " pred=" << *pred ;
if (modbusRtuCrcOk(buf, *pred)) {
auto blk = m_pool->allocate(*pred);
if (!blk || !blk->data || blk->capacity < *pred) {
if (framerErr) {
*framerErr = QStringLiteral("pool_exhausted");
}
LOG_WARNING() << "ModbusRtuFramer: pool_exhausted endpoint=" << endpoint;
return;
}
std::memcpy(blk->data, buf, *pred);
blk->size = *pred;
softbus::message_bus::pipeline::PipelineContext ctx;
ctx.deviceId = deviceId;
ctx.stableKey = stableKey;
ctx.endpoint = endpoint;
ctx.timestamp = ts;
ctx.protocolHint = protocolHint;
ctx.traceId = traceId;
ctx.direction = dir;
ctx.frameKind = softbus::message_bus::pipeline::FrameKind::CompleteFrame;
ctx.payloadSize = *pred;
ctx.payload.block = std::move(blk);
ctx.payload.offset = 0;
ctx.payload.length = *pred;
out.push_back(std::move(ctx));
++m_cnt.framesEmitted;
const std::size_t rest = session.len - *pred;
if (rest > 0) {
std::memmove(session.buf.data(), buf + *pred, rest);
}
session.len = rest;
// LOG_INFO() << "ModbusRtuFramer success to consume buffer rest=" << rest;
continue;
}
++m_cnt.crcFail;
++m_cnt.resyncBytes;
// LOG_WARNING() << "ModbusRtuFramer: crc_mismatch resync_byte endpoint=" << endpoint
// << "traceId=" << traceId << "buf_len=" << session.len;
if (session.len <= 1) {
session.len = 0;
break;
}
std::memmove(session.buf.data(), buf + 1, session.len - 1);
--session.len;
}
}
void ModbusRtuFramer::feed(softbus::message_bus::pipeline::PipelineContext& chunkIn,
std::vector<softbus::message_bus::pipeline::PipelineContext>& completeFramesOut)
{
if (!m_pool || !chunkIn.payload.valid()) {
return;
}
if (!protocolHintIsModbusRtu(chunkIn.protocolHint)) {
return;
}
const std::uint8_t* p = chunkIn.payload.bytes();
const std::size_t n = chunkIn.payloadSize ? chunkIn.payloadSize : chunkIn.payload.length;
if (!p || n == 0) {
return;
}
Session* session = nullptr;
if (!m_exclusiveEndpoint.isEmpty()) {
session = &m_exclusiveSession;
} else {
session = &m_sessions[chunkIn.endpoint];
}
appendChunk(*session, p, n);
QString err;
tryConsumeBuffer(*session,
chunkIn.endpoint,
chunkIn.traceId,
chunkIn.deviceId,
chunkIn.stableKey,
chunkIn.protocolHint,
chunkIn.timestamp,
chunkIn.direction,
completeFramesOut,
&err);
if (!err.isEmpty()) {
chunkIn.framerError = err;
}
chunkIn.payload = {};
chunkIn.payloadSize = 0;
chunkIn.frameKind = softbus::message_bus::pipeline::FrameKind::StreamChunk;
}
} // namespace softbus::message_bus::pipeline::stages::framers

View File

@@ -1,79 +0,0 @@
#pragma once
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
#include <QHash>
#include <QString>
#include "core/memory/MemoryPool.h"
#include "message_bus/pipeline/PipelineContext.h"
#include "message_bus/pipeline/stages/1_framers/IFramer.h"
namespace softbus::message_bus::pipeline::stages::framers
{
struct ModbusRtuFramerConfig
{
std::size_t maxAdu{256};
int interFrameTimeoutMs{0};
};
struct ModbusRtuFramerCounters
{
std::atomic<std::uint64_t> bytesIn{0}; // 输入字节数
std::atomic<std::uint64_t> framesEmitted{0}; // 输出帧数
std::atomic<std::uint64_t> resyncBytes{0}; // 重同步字节数
std::atomic<std::uint64_t> crcFail{0}; // CRC校验失败数
std::atomic<std::uint64_t> overflow{0}; // 溢出数
};
/// 单 worker、多 endpoint使用内部 QHash单线程访问 Session无锁
/// 并行成帧:构造时传入 `exclusiveEndpoint`,仅重组该串口字节流(每 endpoint 一实例)。
class ModbusRtuFramer final : public IFramer
{
public:
ModbusRtuFramer(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerConfig cfg = {},
QString exclusiveEndpoint = {});
void feed(softbus::message_bus::pipeline::PipelineContext& chunkIn,
std::vector<softbus::message_bus::pipeline::PipelineContext>& completeFramesOut) override;
const ModbusRtuFramerCounters& counters() const { return m_cnt; }
ModbusRtuFramerCounters& counters() { return m_cnt; }
void setConfig(ModbusRtuFramerConfig cfg) { m_cfg = cfg; }
private:
struct Session
{
std::vector<std::uint8_t> buf;
std::size_t len{0};
std::chrono::steady_clock::time_point lastRx{std::chrono::steady_clock::now()};
};
void appendChunk(Session& session, const std::uint8_t* p, std::size_t n);
void tryConsumeBuffer(Session& session,
const QString& endpoint,
QString traceId,
int deviceId,
const QString& stableKey,
const QString& protocolHint,
QDateTime ts,
BusDirection dir,
std::vector<softbus::message_bus::pipeline::PipelineContext>& out,
QString* framerErr);
std::shared_ptr<softbus::core::memory::MemoryPool> m_pool;
ModbusRtuFramerConfig m_cfg;
ModbusRtuFramerCounters m_cnt;
QString m_exclusiveEndpoint;
QHash<QString, Session> m_sessions;
Session m_exclusiveSession;
};
} // namespace softbus::message_bus::pipeline::stages::framers

View File

@@ -1,125 +0,0 @@
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h"
#include <QtGlobal>
namespace softbus::message_bus::pipeline::stages::framers
{
std::uint16_t modbusRtuCrc16(const std::uint8_t* data, std::size_t len)
{
std::uint16_t crc = 0xFFFF;
for (std::size_t i = 0; i < len; ++i) {
crc ^= data[i];
for (int k = 0; k < 8; ++k) {
if (crc & 1) {
crc = static_cast<std::uint16_t>((crc >> 1) ^ 0xA001);
} else {
crc = static_cast<std::uint16_t>(crc >> 1);
}
}
}
return crc;
}
// 功能 校验CRC 如果CRC校验不正确 则返回false 如果CRC校验正确 则返回true
// 参数 adu 数据缓冲区 aduLen 数据长度
// 返回值 如果CRC校验不正确 则返回false 如果CRC校验正确 则返回true
// 逻辑:如果数据缓冲区为空 则返回false 如果数据长度小于4 则返回false 如果数据长度大于等于4 则返回true
// 如果数据长度大于等于4 则返回true
// 如果数据长度大于等于4 则返回true
bool modbusRtuCrcOk(const std::uint8_t* adu, std::size_t aduLen)
{
if (!adu || aduLen < 4) {
return false;
}
const std::size_t body = aduLen - 2;
const std::uint16_t got =
static_cast<std::uint16_t>(adu[body] | (static_cast<std::uint16_t>(adu[body + 1]) << 8));
const std::uint16_t want = modbusRtuCrc16(adu, body);
return got == want;
}
static bool fcIsReadRange(std::uint8_t fc)
{
return fc == 0x01 || fc == 0x02 || fc == 0x03 || fc == 0x04;
}
// 功能 预测Modbus RTU帧长度 如果长度不足 则返回nullopt 如果长度足够 则返回长度
// 参数 buf 数据缓冲区 len 数据长度 maxAdu 最大ADU长度
// 返回值 :如果长度不足 则返回nullopt 如果长度足够 则返回长度
// 逻辑: unit 是设备地址 fc 是功能码 如果fc是异常码 则返回5 如果fc是读指令 则返回8 如果fc是写指令 则返回8 如果fc是其他指令 则返回nullopt
// 如果fc是读指令 则检查buf长度是否大于等于8 并且CRC校验是否正确 如果正确 则返回8
// 如果fc是写指令 则检查buf长度是否大于等于8 并且CRC校验是否正确 如果正确 则返回8
std::optional<std::size_t> predictModbusRtuAduLength(const std::uint8_t* buf,
std::size_t len,
std::size_t maxAdu)
{
if (len < 2 || !buf) {
return std::nullopt;
}
const std::uint8_t unit = buf[0];
(void)unit;
const std::uint8_t fc = buf[1];
if (fc & 0x80) {
return len < 5 ? std::nullopt : std::optional<std::size_t>(5);
}
if (fcIsReadRange(fc)) {
if (len >= 8 && modbusRtuCrcOk(buf, 8)) {
return std::optional<std::size_t>(8);
}
if (len < 3) {
return std::nullopt;
}
const std::size_t byteCount = buf[2];
const std::size_t total = 5 + byteCount;
if (total > maxAdu) {
return std::nullopt;
}
if (len < total) {
return std::nullopt;
}
if (modbusRtuCrcOk(buf, total) ) {
return total;
}
return std::optional<std::size_t>(8);
}
if (fc == 0x05 || fc == 0x06) {
return std::optional<std::size_t>(8);
}
if (fc == 0x16) {
return std::optional<std::size_t>(10);
}
if (fc == 0x0F || fc == 0x10) {
if (len >= 8 && modbusRtuCrcOk(buf, 8)) {
return std::optional<std::size_t>(8);
}
if (len >= 7) {
const std::uint8_t bc = buf[6];
const std::size_t req = 9 + static_cast<std::size_t>(bc);
if (bc <= 242 && req <= maxAdu && req >= 9) {
return req;
}
}
return len >= 2 ? std::optional<std::size_t>(8) : std::nullopt;
}
if (len < 6) {
return std::nullopt;
}
return std::optional<std::size_t>(8);
}
bool protocolHintIsModbusRtu(const QString& hint)
{
const QString h = hint.trimmed().toLower();
return h == QStringLiteral("modbus_rtu") || h == QStringLiteral("modbus-rtu")
|| h == QStringLiteral("modbusrtu");
}
} // namespace softbus::message_bus::pipeline::stages::framers

View File

@@ -1,24 +0,0 @@
#pragma once
// 功能 定义Modbus RTU帧解析器 包含CRC校验、帧预测、协议提示判断
#include <cstddef>
#include <cstdint>
#include <optional>
#include <QString>
namespace softbus::message_bus::pipeline::stages::framers
{
std::uint16_t modbusRtuCrc16(const std::uint8_t* data, std::size_t len);
/// 校验 ADU含尾端 CRC LE`aduLen` 为整帧长度unit..crc_hi
bool modbusRtuCrcOk(const std::uint8_t* adu, std::size_t aduLen);
/// 根据已缓冲前缀预测整帧 ADU 长度(含 2 字节 CRC未知或不足返回 nullopt。
std::optional<std::size_t> predictModbusRtuAduLength(const std::uint8_t* buf,
std::size_t len,
std::size_t maxAdu);
bool protocolHintIsModbusRtu(const QString& hint);
} // namespace softbus::message_bus::pipeline::stages::framers

View File

@@ -16,7 +16,7 @@
#include <variant>
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuPdu.h"
#include "plugins/protocols/modbusrtu/types/ModbusRtuPdu.h"
#include "message_bus/pipeline/protocol/ProtocolEnvelope.h"
#include "utils/logs/logging.h"

View File

@@ -1,5 +1,5 @@
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFramer.h"
#include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h"
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
#include <QtTest/QtTest>
@@ -11,14 +11,14 @@ using softbus::message_bus::pipeline::FrameKind;
using softbus::message_bus::pipeline::PipelineContext;
using softbus::message_bus::pipeline::stages::framers::ModbusRtuFramer;
using softbus::message_bus::pipeline::stages::framers::ModbusRtuFramerConfig;
using softbus::message_bus::pipeline::stages::framers::modbusRtuCrc16;
using softbus::message_bus::pipeline::protocol::modbusrtu::crc16;
namespace
{
void appendCrcLe(std::vector<std::uint8_t>& v)
{
const std::uint16_t c = modbusRtuCrc16(v.data(), v.size());
const std::uint16_t c = crc16(v.data(), v.size());
v.push_back(static_cast<std::uint8_t>(c & 0xFF));
v.push_back(static_cast<std::uint8_t>(c >> 8));
}

View File

@@ -0,0 +1,57 @@
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
#include <QtTest/QtTest>
using softbus::message_bus::pipeline::protocol::modbusrtu::predictAduLength;
using softbus::message_bus::pipeline::protocol::modbusrtu::crc16;
namespace
{
void appendCrc(QByteArray& b)
{
const auto c = crc16(reinterpret_cast<const std::uint8_t*>(b.constData()), static_cast<std::size_t>(b.size()));
b.append(static_cast<char>(c & 0xFF));
b.append(static_cast<char>((c >> 8) & 0xFF));
}
} // namespace
class ModbusRtuFramingPredictTest : public QObject
{
Q_OBJECT
private slots:
void readRequestWaitsUntilEightBytes();
void readResponseNeedsWholeAndCrc();
};
void ModbusRtuFramingPredictTest::readRequestWaitsUntilEightBytes()
{
QByteArray partial;
partial.append('\x01');
partial.append('\x03');
partial.append('\x00');
partial.append('\x00');
partial.append('\x00');
partial.append('\x0A');
partial.append('\xC5'); // missing last crc byte
auto pred = predictAduLength(reinterpret_cast<const std::uint8_t*>(partial.constData()),
static_cast<std::size_t>(partial.size()), 256);
QVERIFY(!pred.has_value());
}
void ModbusRtuFramingPredictTest::readResponseNeedsWholeAndCrc()
{
QByteArray resp;
resp.append('\x01');
resp.append('\x03');
resp.append('\x02');
resp.append('\x12');
resp.append('\x34');
appendCrc(resp);
auto pred = predictAduLength(reinterpret_cast<const std::uint8_t*>(resp.constData()),
static_cast<std::size_t>(resp.size()), 256);
QVERIFY(pred.has_value());
QCOMPARE(static_cast<int>(*pred), resp.size());
}
QTEST_MAIN(ModbusRtuFramingPredictTest)
#include "modbus_rtu_framing_predict_test.moc"

View File

@@ -1,4 +1,4 @@
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuCodec.h"
#include "plugins/protocols/modbusrtu/codec/ModbusRtuCodec.h"
#include <QtTest/QtTest>

View File

@@ -6,7 +6,7 @@
#include <QFile>
#include <variant>
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuPdu.h"
#include "plugins/protocols/modbusrtu/types/ModbusRtuPdu.h"
#include "message_bus/pipeline/protocol/ProtocolEnvelope.h"
using softbus::message_bus::pipeline::BusDirection;