devices modbus rtu 1.0
This commit is contained in:
467
CMakeLists.txt
467
CMakeLists.txt
@@ -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.
BIN
bin/softbus_modbus_rtu_framing_predict_test
Normal file
BIN
bin/softbus_modbus_rtu_framing_predict_test
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
37
plugins/protocols/modbusrtu/README.md
Normal file
37
plugins/protocols/modbusrtu/README.md
Normal 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 (可依赖 types;codec 使用 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`。
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
{
|
||||
173
plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.cpp
Normal file
173
plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.cpp
Normal 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
|
||||
44
plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h
Normal file
44
plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h
Normal 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
|
||||
88
plugins/protocols/modbusrtu/framer/ModbusRtuFraming.cpp
Normal file
88
plugins/protocols/modbusrtu/framer/ModbusRtuFraming.cpp
Normal 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
|
||||
17
plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h
Normal file
17
plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h
Normal 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
|
||||
@@ -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
|
||||
@@ -8,5 +8,6 @@ namespace softbus::core::plugin_system
|
||||
{
|
||||
|
||||
std::shared_ptr<IProtocolPlugin> makeModbusRtuProtocolPlugin();
|
||||
IProtocolPlugin* createModbusRtuProtocolPluginRaw();
|
||||
|
||||
} // namespace softbus::core::plugin_system
|
||||
38
src/core/plugin_system/IProtocolFramerPlugin.h
Normal file
38
src/core/plugin_system/IProtocolFramerPlugin.h
Normal 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
|
||||
125
src/core/plugin_system/PluginHost.cpp
Normal file
125
src/core/plugin_system/PluginHost.cpp
Normal 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
|
||||
39
src/core/plugin_system/PluginHost.h
Normal file
39
src/core/plugin_system/PluginHost.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
// 功能 :分配帧序列号
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
57
tests/modbus_rtu_framing_predict_test.cpp
Normal file
57
tests/modbus_rtu_framing_predict_test.cpp
Normal 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"
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "message_bus/pipeline/protocol/modbusrtu/ModbusRtuCodec.h"
|
||||
#include "plugins/protocols/modbusrtu/codec/ModbusRtuCodec.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user