From 70f675a48bde95ce51a21096085d4be1915db425 Mon Sep 17 00:00:00 2001 From: flowerstonezl <125468358+flowerstonezl@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:50:28 +0800 Subject: [PATCH] first commit --- .gitignore | 105 +++++++++++ CMakeLists.txt | 28 +++ cmake/SoftbusPackage.cmake | 23 +++ cmake/SoftbusPlatform.cmake | 106 +++++++++++ cmake/SoftbusThirdParty.cmake | 79 ++++++++ config/daemon_profile.json | 19 ++ config/softbus_model.json | 18 ++ config/ua_model.xml | 11 ++ src/plugins/protocol_canopen/CMakeLists.txt | 11 ++ .../include/canopen/CanopenFramer.h | 13 ++ .../include/canopen/CanopenMapper.h | 15 ++ .../include/canopen/CanopenNodeManager.h | 19 ++ .../include/canopen/CanopenObjectDictionary.h | 25 +++ .../include/canopen/CanopenParser.h | 14 ++ .../include/canopen/EdsDcfLoader.h | 14 ++ .../protocol_canopen/src/canopen_plugin.cpp | 57 ++++++ src/plugins/protocol_modbus/CMakeLists.txt | 13 ++ .../include/modbus/ModbusFramer.h | 13 ++ .../include/modbus/ModbusParser.h | 21 +++ .../protocol_modbus/src/ModbusFramer.cpp | 25 +++ .../protocol_modbus/src/ModbusParser.cpp | 41 +++++ .../protocol_modbus/src/modbus_plugin.cpp | 22 +++ src/softbus_api/CMakeLists.txt | 9 + .../include/softbus_api/ExecuteCommand.h | 19 ++ src/softbus_api/src/ExecuteCommand.cpp | 51 ++++++ src/softbus_core/CMakeLists.txt | 17 ++ .../softbus_core/alignment/TimeAligner.h | 55 ++++++ .../softbus_core/config/ProfileLoader.h | 38 ++++ .../softbus_core/identity/DeviceIdentity.h | 19 ++ .../include/softbus_core/logging/Logger.h | 87 +++++++++ .../softbus_core/memory/LockFreeQueue.h | 3 + .../include/softbus_core/models/DOMMessage.h | 61 +++++++ .../include/softbus_core/models/MetadataDef.h | 19 ++ .../include/softbus_core/models/ObjectModel.h | 50 +++++ .../softbus_core/models/RawBusMessage.h | 9 + .../include/softbus_core/models/RawPacket.h | 31 ++++ .../softbus_core/platform/PlatformSignal.h | 20 ++ .../include/softbus_core/plugin/IFramer.h | 36 ++++ .../include/softbus_core/plugin/IParser.h | 23 +++ .../softbus_core/plugin/PluginLoader.h | 41 +++++ .../softbus_core/queue/LockFreeQueue.h | 87 +++++++++ .../include/softbus_core/softbus_core.h | 16 ++ .../include/softbus_core/time/HwClock.h | 13 ++ .../src/alignment/TimeAligner.cpp | 107 +++++++++++ src/softbus_core/src/config/ProfileLoader.cpp | 37 ++++ .../src/identity/DeviceIdentity.cpp | 61 +++++++ src/softbus_core/src/logging/Logger.cpp | 62 +++++++ src/softbus_core/src/models/ObjectModel.cpp | 84 +++++++++ .../src/platform/PlatformSignal.cpp | 48 +++++ src/softbus_core/src/plugin/PluginLoader.cpp | 86 +++++++++ src/softbus_core/src/time/HwClock.cpp | 37 ++++ src/softbus_daemon/CMakeLists.txt | 26 +++ .../softbus_daemon/api/IpcOptionalDBus.h | 11 ++ .../command/CommandDispatcher.h | 21 +++ .../softbus_daemon/pipeline/PipelineEngine.h | 23 +++ .../registry/MetadataRegistry.h | 20 ++ .../registry/ObjectModelRegistry.h | 20 ++ src/softbus_daemon/src/CommandDispatcher.cpp | 32 ++++ src/softbus_daemon/src/CoreService.cpp | 171 ++++++++++++++++++ src/softbus_daemon/src/IpcOptionalDBus.cpp | 21 +++ src/softbus_daemon/src/MetadataRegistry.cpp | 19 ++ .../src/ObjectModelRegistry.cpp | 25 +++ src/softbus_daemon/src/PipelineEngine.cpp | 48 +++++ src/softbus_daemon/src/main.cpp | 30 +++ src/softbus_edge/CMakeLists.txt | 14 ++ .../softbus_edge/discovery/DeviceDiscovery.h | 18 ++ .../softbus_edge/egress/UplinkClient.h | 28 +++ .../softbus_edge/factory/DeviceFactory.h | 12 ++ .../softbus_edge/ingress/IIngressPort.h | 17 ++ .../softbus_edge/ingress/MockModbusIngress.h | 27 +++ src/softbus_edge/src/DeviceDiscovery.cpp | 10 + src/softbus_edge/src/DeviceFactory.cpp | 12 ++ src/softbus_edge/src/MockModbusIngress.cpp | 54 ++++++ src/softbus_edge/src/UplinkClient.cpp | 40 ++++ src/softbus_edge/src/main.cpp | 85 +++++++++ src/softbus_opcua/CMakeLists.txt | 15 ++ .../softbus_opcua/UaAddressSpaceBuilder.h | 27 +++ .../include/softbus_opcua/UaEventBridge.h | 22 +++ .../include/softbus_opcua/UaMethodBinder.h | 26 +++ .../include/softbus_opcua/UaModelBinder.h | 28 +++ .../include/softbus_opcua/UaServerEngine.h | 40 ++++ .../src/UaAddressSpaceBuilder.cpp | 119 ++++++++++++ src/softbus_opcua/src/UaEventBridge.cpp | 36 ++++ src/softbus_opcua/src/UaMethodBinder.cpp | 157 ++++++++++++++++ src/softbus_opcua/src/UaModelBinder.cpp | 71 ++++++++ src/softbus_opcua/src/UaServerEngine.cpp | 80 ++++++++ src/softbus_transport/CMakeLists.txt | 12 ++ .../softbus_transport/TcpUplinkClient.h | 46 +++++ .../softbus_transport/TcpUplinkServer.h | 43 +++++ .../include/softbus_transport/UplinkWire.h | 33 ++++ src/softbus_transport/src/TcpUplinkClient.cpp | 120 ++++++++++++ src/softbus_transport/src/TcpUplinkServer.cpp | 115 ++++++++++++ src/softbus_transport/src/UplinkWire.cpp | 110 +++++++++++ 93 files changed, 3802 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 cmake/SoftbusPackage.cmake create mode 100644 cmake/SoftbusPlatform.cmake create mode 100644 cmake/SoftbusThirdParty.cmake create mode 100644 config/daemon_profile.json create mode 100644 config/softbus_model.json create mode 100644 config/ua_model.xml create mode 100644 src/plugins/protocol_canopen/CMakeLists.txt create mode 100644 src/plugins/protocol_canopen/include/canopen/CanopenFramer.h create mode 100644 src/plugins/protocol_canopen/include/canopen/CanopenMapper.h create mode 100644 src/plugins/protocol_canopen/include/canopen/CanopenNodeManager.h create mode 100644 src/plugins/protocol_canopen/include/canopen/CanopenObjectDictionary.h create mode 100644 src/plugins/protocol_canopen/include/canopen/CanopenParser.h create mode 100644 src/plugins/protocol_canopen/include/canopen/EdsDcfLoader.h create mode 100644 src/plugins/protocol_canopen/src/canopen_plugin.cpp create mode 100644 src/plugins/protocol_modbus/CMakeLists.txt create mode 100644 src/plugins/protocol_modbus/include/modbus/ModbusFramer.h create mode 100644 src/plugins/protocol_modbus/include/modbus/ModbusParser.h create mode 100644 src/plugins/protocol_modbus/src/ModbusFramer.cpp create mode 100644 src/plugins/protocol_modbus/src/ModbusParser.cpp create mode 100644 src/plugins/protocol_modbus/src/modbus_plugin.cpp create mode 100644 src/softbus_api/CMakeLists.txt create mode 100644 src/softbus_api/include/softbus_api/ExecuteCommand.h create mode 100644 src/softbus_api/src/ExecuteCommand.cpp create mode 100644 src/softbus_core/CMakeLists.txt create mode 100644 src/softbus_core/include/softbus_core/alignment/TimeAligner.h create mode 100644 src/softbus_core/include/softbus_core/config/ProfileLoader.h create mode 100644 src/softbus_core/include/softbus_core/identity/DeviceIdentity.h create mode 100644 src/softbus_core/include/softbus_core/logging/Logger.h create mode 100644 src/softbus_core/include/softbus_core/memory/LockFreeQueue.h create mode 100644 src/softbus_core/include/softbus_core/models/DOMMessage.h create mode 100644 src/softbus_core/include/softbus_core/models/MetadataDef.h create mode 100644 src/softbus_core/include/softbus_core/models/ObjectModel.h create mode 100644 src/softbus_core/include/softbus_core/models/RawBusMessage.h create mode 100644 src/softbus_core/include/softbus_core/models/RawPacket.h create mode 100644 src/softbus_core/include/softbus_core/platform/PlatformSignal.h create mode 100644 src/softbus_core/include/softbus_core/plugin/IFramer.h create mode 100644 src/softbus_core/include/softbus_core/plugin/IParser.h create mode 100644 src/softbus_core/include/softbus_core/plugin/PluginLoader.h create mode 100644 src/softbus_core/include/softbus_core/queue/LockFreeQueue.h create mode 100644 src/softbus_core/include/softbus_core/softbus_core.h create mode 100644 src/softbus_core/include/softbus_core/time/HwClock.h create mode 100644 src/softbus_core/src/alignment/TimeAligner.cpp create mode 100644 src/softbus_core/src/config/ProfileLoader.cpp create mode 100644 src/softbus_core/src/identity/DeviceIdentity.cpp create mode 100644 src/softbus_core/src/logging/Logger.cpp create mode 100644 src/softbus_core/src/models/ObjectModel.cpp create mode 100644 src/softbus_core/src/platform/PlatformSignal.cpp create mode 100644 src/softbus_core/src/plugin/PluginLoader.cpp create mode 100644 src/softbus_core/src/time/HwClock.cpp create mode 100644 src/softbus_daemon/CMakeLists.txt create mode 100644 src/softbus_daemon/include/softbus_daemon/api/IpcOptionalDBus.h create mode 100644 src/softbus_daemon/include/softbus_daemon/command/CommandDispatcher.h create mode 100644 src/softbus_daemon/include/softbus_daemon/pipeline/PipelineEngine.h create mode 100644 src/softbus_daemon/include/softbus_daemon/registry/MetadataRegistry.h create mode 100644 src/softbus_daemon/include/softbus_daemon/registry/ObjectModelRegistry.h create mode 100644 src/softbus_daemon/src/CommandDispatcher.cpp create mode 100644 src/softbus_daemon/src/CoreService.cpp create mode 100644 src/softbus_daemon/src/IpcOptionalDBus.cpp create mode 100644 src/softbus_daemon/src/MetadataRegistry.cpp create mode 100644 src/softbus_daemon/src/ObjectModelRegistry.cpp create mode 100644 src/softbus_daemon/src/PipelineEngine.cpp create mode 100644 src/softbus_daemon/src/main.cpp create mode 100644 src/softbus_edge/CMakeLists.txt create mode 100644 src/softbus_edge/include/softbus_edge/discovery/DeviceDiscovery.h create mode 100644 src/softbus_edge/include/softbus_edge/egress/UplinkClient.h create mode 100644 src/softbus_edge/include/softbus_edge/factory/DeviceFactory.h create mode 100644 src/softbus_edge/include/softbus_edge/ingress/IIngressPort.h create mode 100644 src/softbus_edge/include/softbus_edge/ingress/MockModbusIngress.h create mode 100644 src/softbus_edge/src/DeviceDiscovery.cpp create mode 100644 src/softbus_edge/src/DeviceFactory.cpp create mode 100644 src/softbus_edge/src/MockModbusIngress.cpp create mode 100644 src/softbus_edge/src/UplinkClient.cpp create mode 100644 src/softbus_edge/src/main.cpp create mode 100644 src/softbus_opcua/CMakeLists.txt create mode 100644 src/softbus_opcua/include/softbus_opcua/UaAddressSpaceBuilder.h create mode 100644 src/softbus_opcua/include/softbus_opcua/UaEventBridge.h create mode 100644 src/softbus_opcua/include/softbus_opcua/UaMethodBinder.h create mode 100644 src/softbus_opcua/include/softbus_opcua/UaModelBinder.h create mode 100644 src/softbus_opcua/include/softbus_opcua/UaServerEngine.h create mode 100644 src/softbus_opcua/src/UaAddressSpaceBuilder.cpp create mode 100644 src/softbus_opcua/src/UaEventBridge.cpp create mode 100644 src/softbus_opcua/src/UaMethodBinder.cpp create mode 100644 src/softbus_opcua/src/UaModelBinder.cpp create mode 100644 src/softbus_opcua/src/UaServerEngine.cpp create mode 100644 src/softbus_transport/CMakeLists.txt create mode 100644 src/softbus_transport/include/softbus_transport/TcpUplinkClient.h create mode 100644 src/softbus_transport/include/softbus_transport/TcpUplinkServer.h create mode 100644 src/softbus_transport/include/softbus_transport/UplinkWire.h create mode 100644 src/softbus_transport/src/TcpUplinkClient.cpp create mode 100644 src/softbus_transport/src/TcpUplinkServer.cpp create mode 100644 src/softbus_transport/src/UplinkWire.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac9da89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,105 @@ +# SoftBus OPC workspace — generated / local files + +# ------------------------------------------------------------------------------ +# Editor & IDE +# ------------------------------------------------------------------------------ +*~ +*.autosave +*.swp +.*.swp +.vscode/ +.idea/ +.cursor/ +*.pro.user* +*.qbs.user* +CMakeLists.txt.user* + +# ------------------------------------------------------------------------------ +# OS +# ------------------------------------------------------------------------------ +.DS_Store +Thumbs.db +.directory + +# ------------------------------------------------------------------------------ +# CMake / build +# ------------------------------------------------------------------------------ +/build/ +/out/ +/cmake-build-*/ +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +install_manifest.txt +CTestTestfile.cmake +Testing/ +compile_commands.json +*.cmake.user + +# FetchContent / third-party checkout (under build tree) +/_deps/ + +# ------------------------------------------------------------------------------ +# Compiled objects & libraries +# ------------------------------------------------------------------------------ +*.o +*.obj +*.lo +*.a +*.lib +*.so +*.so.* +*.dll +*.dylib +*.exe +*.pdb +*.ilk +*.idb +*.exp +*.manifest +*.res +*.rc + +# MinGW / MSVC extras +*.Debug +*.Release +*.i +*.s + +# ------------------------------------------------------------------------------ +# Qt / moc (if later used) +# ------------------------------------------------------------------------------ +*.moc +moc_*.cpp +ui_*.h +qrc_*.cpp +*.qm +*.prl +*.app +/.qmake.cache +/.qmake.stash + +# ------------------------------------------------------------------------------ +# Python / scripts +# ------------------------------------------------------------------------------ +*.pyc +__pycache__/ +.cache/ + +# ------------------------------------------------------------------------------ +# Logs & temp +# ------------------------------------------------------------------------------ +*.log +*.tmp +*.temp +core +*.core + +# ------------------------------------------------------------------------------ +# Local secrets (never commit) +# ------------------------------------------------------------------------------ +.env +.env.* +*.pem +*.key +credentials.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..99bb146 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.16) +project(softbus_workspace LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(SoftbusPlatform) +include(SoftbusThirdParty) +include(SoftbusPackage) + +add_subdirectory(src/softbus_core) +add_subdirectory(src/softbus_transport) +add_subdirectory(src/softbus_api) +add_subdirectory(src/softbus_opcua) +add_subdirectory(src/plugins/protocol_modbus) +add_subdirectory(src/plugins/protocol_canopen) +add_subdirectory(src/softbus_edge) +add_subdirectory(src/softbus_daemon) + +message(STATUS "Softbus workspace configured for ${SOFTBUS_PLATFORM_NAME}") diff --git a/cmake/SoftbusPackage.cmake b/cmake/SoftbusPackage.cmake new file mode 100644 index 0000000..07f6ec5 --- /dev/null +++ b/cmake/SoftbusPackage.cmake @@ -0,0 +1,23 @@ +# ROS-like package registration helper + +macro(softbus_package name) + set(SOFTBUS_CURRENT_PACKAGE ${name}) +endmacro() + +function(softbus_export_include target include_dir) + target_include_directories(${target} + PUBLIC + $ + $ + ) +endfunction() + +function(softbus_add_plugin target) + set_target_properties(${target} PROPERTIES + PREFIX "" + OUTPUT_NAME ${target} + ) + if(WIN32) + target_compile_definitions(${target} PRIVATE SOFTBUS_PLUGIN_EXPORTS) + endif() +endfunction() diff --git a/cmake/SoftbusPlatform.cmake b/cmake/SoftbusPlatform.cmake new file mode 100644 index 0000000..5af3b6c --- /dev/null +++ b/cmake/SoftbusPlatform.cmake @@ -0,0 +1,106 @@ +# Platform detection and conditional dependencies + +if(WIN32) + set(SOFTBUS_PLATFORM_NAME "Windows") + add_compile_definitions(SOFTBUS_PLATFORM_WINDOWS NOMINMAX WIN32_LEAN_AND_MEAN) +else() + set(SOFTBUS_PLATFORM_NAME "Linux") + add_compile_definitions(SOFTBUS_PLATFORM_LINUX) +endif() + +find_package(Threads REQUIRED) + +if(WIN32) + set(SOFTBUS_HAS_DBUS OFF) + set(SOFTBUS_HAS_UDEV OFF) +else() + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(DBUS QUIET dbus-1) + if(DBUS_FOUND) + set(SOFTBUS_HAS_DBUS ON) + else() + set(SOFTBUS_HAS_DBUS OFF) + message(WARNING "libdbus-1 not found; DBus IPC will be disabled on Linux") + endif() + else() + set(SOFTBUS_HAS_DBUS OFF) + endif() + + find_library(UDEV_LIB udev) + if(UDEV_LIB) + set(SOFTBUS_HAS_UDEV ON) + else() + set(SOFTBUS_HAS_UDEV OFF) + message(WARNING "libudev not found; udev device monitor will use stub on Linux") + endif() +endif() + +# ZeroMQ +option(SOFTBUS_WITH_ZMQ "Enable ZeroMQ transport" OFF) +if(SOFTBUS_WITH_ZMQ) + if(PkgConfig_FOUND) + pkg_check_modules(ZMQ QUIET libzmq) + endif() + if(NOT ZMQ_FOUND) + find_library(ZMQ_LIBRARY NAMES zmq libzmq) + find_path(ZMQ_INCLUDE_DIR zmq.h) + if(ZMQ_LIBRARY AND ZMQ_INCLUDE_DIR) + set(ZMQ_FOUND TRUE) + set(ZMQ_LIBRARIES ${ZMQ_LIBRARY}) + set(ZMQ_INCLUDE_DIRS ${ZMQ_INCLUDE_DIR}) + endif() + endif() + if(ZMQ_FOUND) + set(SOFTBUS_HAS_ZMQ ON) + message(STATUS "ZeroMQ found: ${ZMQ_LIBRARIES}") + else() + set(SOFTBUS_HAS_ZMQ OFF) + message(WARNING "ZeroMQ not found; set SOFTBUS_WITH_ZMQ=OFF or install libzmq") + endif() +else() + set(SOFTBUS_HAS_ZMQ OFF) +endif() + +# open62541 (optional until daemon links it) +option(SOFTBUS_WITH_OPCUA "Enable OPC UA server in daemon" ON) + +function(softbus_apply_platform_libs target) + target_link_libraries(${target} PRIVATE Threads::Threads) + if(WIN32) + target_link_libraries(${target} PRIVATE ws2_32 wsock32) + endif() + if(MINGW) + target_compile_options(${target} PRIVATE -fno-stack-protector) + target_link_options(${target} PRIVATE -fno-stack-protector) + endif() +endfunction() + +function(softbus_apply_zmq target) + if(SOFTBUS_HAS_ZMQ) + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_ZMQ=1) + target_include_directories(${target} PRIVATE ${ZMQ_INCLUDE_DIRS}) + target_link_libraries(${target} PRIVATE ${ZMQ_LIBRARIES}) + else() + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_ZMQ=0) + endif() +endfunction() + +function(softbus_apply_dbus target) + if(SOFTBUS_HAS_DBUS) + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_DBUS=1) + target_include_directories(${target} PRIVATE ${DBUS_INCLUDE_DIRS}) + target_link_libraries(${target} PRIVATE ${DBUS_LIBRARIES}) + else() + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_DBUS=0) + endif() +endfunction() + +function(softbus_apply_udev target) + if(SOFTBUS_HAS_UDEV) + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_UDEV=1) + target_link_libraries(${target} PRIVATE ${UDEV_LIB}) + else() + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_UDEV=0) + endif() +endfunction() diff --git a/cmake/SoftbusThirdParty.cmake b/cmake/SoftbusThirdParty.cmake new file mode 100644 index 0000000..c402111 --- /dev/null +++ b/cmake/SoftbusThirdParty.cmake @@ -0,0 +1,79 @@ +include(FetchContent) + +# nlohmann_json +FetchContent_Declare( + nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.11.3 + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(nlohmann_json) + +# standalone asio +FetchContent_Declare( + asio + GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git + GIT_TAG asio-1-30-2 + GIT_SHALLOW TRUE +) +FetchContent_GetProperties(asio) +if(NOT asio_POPULATED) + FetchContent_Populate(asio) + add_library(asio INTERFACE) + target_include_directories(asio INTERFACE ${asio_SOURCE_DIR}/asio/include) + target_compile_definitions(asio INTERFACE ASIO_STANDALONE ASIO_NO_DEPRECATED) + if(WIN32) + target_compile_definitions(asio INTERFACE _WIN32_WINNT=0x0601) + endif() +endif() + +# open62541 +if(SOFTBUS_WITH_OPCUA) + set(UA_ENABLE_AMALGAMATION OFF CACHE BOOL "" FORCE) + + if(MINGW) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-stack-protector") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-stack-protector") + endif() + + set(_softbus_saved_build_shared ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + + set(UA_ENABLE_SUBSCRIPTIONS ON CACHE BOOL "" FORCE) + set(UA_ENABLE_METHODCALLS ON CACHE BOOL "" FORCE) + set(UA_ENABLE_DISCOVERY ON CACHE BOOL "" FORCE) + set(UA_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(UA_BUILD_UNIT_TESTS OFF CACHE BOOL "" FORCE) + set(UA_ENABLE_ENCRYPTION OFF CACHE BOOL "" FORCE) + + FetchContent_Declare( + open62541 + GIT_REPOSITORY https://github.com/open62541/open62541.git + GIT_TAG v1.4.6 + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(open62541) + set(BUILD_SHARED_LIBS ${_softbus_saved_build_shared} CACHE BOOL "" FORCE) + + if(MINGW AND TARGET open62541) + set_target_properties(open62541 PROPERTIES + INTERFACE_LINK_LIBRARIES "ws2_32;iphlpapi" + ) + target_compile_options(open62541 PRIVATE -fno-stack-protector) + target_link_options(open62541 PRIVATE -fno-stack-protector) + endif() + + set(SOFTBUS_HAS_OPCUA ON) +else() + set(SOFTBUS_HAS_OPCUA OFF) +endif() + +function(softbus_apply_open62541 target) + if(SOFTBUS_HAS_OPCUA) + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_OPCUA=1) + target_link_libraries(${target} PRIVATE open62541) + else() + target_compile_definitions(${target} PRIVATE SOFTBUS_HAS_OPCUA=0) + endif() +endfunction() diff --git a/config/daemon_profile.json b/config/daemon_profile.json new file mode 100644 index 0000000..7d2942d --- /dev/null +++ b/config/daemon_profile.json @@ -0,0 +1,19 @@ +{ + "opcua": { + "port": 4840, + "applicationName": "SoftBusDaemon" + }, + "uplink": { + "listenHost": "127.0.0.1", + "listenPort": 9000 + }, + "plugins": { + "modbus": "protocol_modbus", + "canopen": "protocol_canopen" + }, + "security": { + "tlsEnabled": false, + "certPath": "", + "keyPath": "" + } +} diff --git a/config/softbus_model.json b/config/softbus_model.json new file mode 100644 index 0000000..b5e4b6d --- /dev/null +++ b/config/softbus_model.json @@ -0,0 +1,18 @@ +{ + "site": "DemoSite", + "system": "DemoSystem", + "asset": "DemoAsset", + "devices": [ + { + "stableDeviceKey": "SimModbusDevice", + "protocol": "modbus", + "points": [ + { + "name": "Temperature", + "register": 40001, + "domain": "Process" + } + ] + } + ] +} diff --git a/config/ua_model.xml b/config/ua_model.xml new file mode 100644 index 0000000..9219889 --- /dev/null +++ b/config/ua_model.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/plugins/protocol_canopen/CMakeLists.txt b/src/plugins/protocol_canopen/CMakeLists.txt new file mode 100644 index 0000000..77db819 --- /dev/null +++ b/src/plugins/protocol_canopen/CMakeLists.txt @@ -0,0 +1,11 @@ +softbus_package(protocol_canopen) + +add_library(protocol_canopen SHARED + src/canopen_plugin.cpp +) + +softbus_export_include(protocol_canopen "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(protocol_canopen PRIVATE softbus_core) +softbus_add_plugin(protocol_canopen) +softbus_apply_platform_libs(protocol_canopen) diff --git a/src/plugins/protocol_canopen/include/canopen/CanopenFramer.h b/src/plugins/protocol_canopen/include/canopen/CanopenFramer.h new file mode 100644 index 0000000..aca5cf7 --- /dev/null +++ b/src/plugins/protocol_canopen/include/canopen/CanopenFramer.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace canopen { + +class CanopenFramer : public softbus::core::IFramer { +public: + softbus::core::FrameResult extract(const uint8_t* data, std::size_t size) override; + const char* protocolName() const override { return "canopen"; } +}; + +} // namespace canopen diff --git a/src/plugins/protocol_canopen/include/canopen/CanopenMapper.h b/src/plugins/protocol_canopen/include/canopen/CanopenMapper.h new file mode 100644 index 0000000..711505f --- /dev/null +++ b/src/plugins/protocol_canopen/include/canopen/CanopenMapper.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include +#include + +namespace canopen { + +class CanopenMapper { +public: + bool mapToDom(uint16_t index, uint8_t subIndex, softbus::core::DOMMessage* message); +}; + +} // namespace canopen diff --git a/src/plugins/protocol_canopen/include/canopen/CanopenNodeManager.h b/src/plugins/protocol_canopen/include/canopen/CanopenNodeManager.h new file mode 100644 index 0000000..75a4fa6 --- /dev/null +++ b/src/plugins/protocol_canopen/include/canopen/CanopenNodeManager.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace canopen { + +class CanopenNodeManager { +public: + bool registerNode(uint8_t nodeId); + bool setState(uint8_t nodeId, const std::string& state); + std::string state(uint8_t nodeId) const; + +private: + std::map states_; +}; + +} // namespace canopen diff --git a/src/plugins/protocol_canopen/include/canopen/CanopenObjectDictionary.h b/src/plugins/protocol_canopen/include/canopen/CanopenObjectDictionary.h new file mode 100644 index 0000000..b4b1e51 --- /dev/null +++ b/src/plugins/protocol_canopen/include/canopen/CanopenObjectDictionary.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include +#include +#include + +namespace canopen { + +class CanopenObjectDictionary { +public: + bool loadEds(const std::string& path); + bool loadDcf(const std::string& path); + bool hasEntry(uint16_t index, uint8_t subIndex) const; + +private: + struct Entry { + std::string name; + std::string dataType; + }; + std::map entries_; +}; + +} // namespace canopen diff --git a/src/plugins/protocol_canopen/include/canopen/CanopenParser.h b/src/plugins/protocol_canopen/include/canopen/CanopenParser.h new file mode 100644 index 0000000..f7e9e0a --- /dev/null +++ b/src/plugins/protocol_canopen/include/canopen/CanopenParser.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace canopen { + +class CanopenParser : public softbus::core::IParser { +public: + softbus::core::ParseResult parseFrame(const uint8_t* data, std::size_t size, + const std::string& sourceId) override; + const char* protocolName() const override { return "canopen"; } +}; + +} // namespace canopen diff --git a/src/plugins/protocol_canopen/include/canopen/EdsDcfLoader.h b/src/plugins/protocol_canopen/include/canopen/EdsDcfLoader.h new file mode 100644 index 0000000..5a682d4 --- /dev/null +++ b/src/plugins/protocol_canopen/include/canopen/EdsDcfLoader.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include + +namespace canopen { + +class EdsDcfLoader { +public: + bool load(const std::string& path, CanopenObjectDictionary* dictionary); +}; + +} // namespace canopen diff --git a/src/plugins/protocol_canopen/src/canopen_plugin.cpp b/src/plugins/protocol_canopen/src/canopen_plugin.cpp new file mode 100644 index 0000000..3efc15a --- /dev/null +++ b/src/plugins/protocol_canopen/src/canopen_plugin.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace canopen { + +softbus::core::FrameResult CanopenFramer::extract(const uint8_t*, std::size_t) +{ + return {}; +} + +softbus::core::ParseResult CanopenParser::parseFrame(const uint8_t*, std::size_t, const std::string&) +{ + return {}; +} + +bool CanopenObjectDictionary::loadEds(const std::string&) { return false; } +bool CanopenObjectDictionary::loadDcf(const std::string&) { return false; } +bool CanopenObjectDictionary::hasEntry(uint16_t, uint8_t) const { return false; } + +bool EdsDcfLoader::load(const std::string&, CanopenObjectDictionary*) { return false; } + +bool CanopenNodeManager::registerNode(uint8_t) { return false; } +bool CanopenNodeManager::setState(uint8_t, const std::string&) { return false; } +std::string CanopenNodeManager::state(uint8_t nodeId) const +{ + const auto it = states_.find(nodeId); + return it == states_.end() ? "Unknown" : it->second; +} + +bool CanopenMapper::mapToDom(uint16_t, uint8_t, softbus::core::DOMMessage*) { return false; } + +} // namespace canopen + +extern "C" SOFTBUS_PLUGIN_API softbus::core::IFramer* create_framer() +{ + return new canopen::CanopenFramer(); +} + +extern "C" SOFTBUS_PLUGIN_API void destroy_framer(softbus::core::IFramer* framer) +{ + delete framer; +} + +extern "C" SOFTBUS_PLUGIN_API softbus::core::IParser* create_parser() +{ + return new canopen::CanopenParser(); +} + +extern "C" SOFTBUS_PLUGIN_API void destroy_parser(softbus::core::IParser* parser) +{ + delete parser; +} diff --git a/src/plugins/protocol_modbus/CMakeLists.txt b/src/plugins/protocol_modbus/CMakeLists.txt new file mode 100644 index 0000000..3278c96 --- /dev/null +++ b/src/plugins/protocol_modbus/CMakeLists.txt @@ -0,0 +1,13 @@ +softbus_package(protocol_modbus) + +add_library(protocol_modbus SHARED + src/ModbusFramer.cpp + src/ModbusParser.cpp + src/modbus_plugin.cpp +) + +softbus_export_include(protocol_modbus "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(protocol_modbus PRIVATE softbus_core) +softbus_add_plugin(protocol_modbus) +softbus_apply_platform_libs(protocol_modbus) diff --git a/src/plugins/protocol_modbus/include/modbus/ModbusFramer.h b/src/plugins/protocol_modbus/include/modbus/ModbusFramer.h new file mode 100644 index 0000000..504a4f5 --- /dev/null +++ b/src/plugins/protocol_modbus/include/modbus/ModbusFramer.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace modbus { + +class ModbusFramer : public softbus::core::IFramer { +public: + softbus::core::FrameResult extract(const uint8_t* data, std::size_t size) override; + const char* protocolName() const override { return "modbus"; } +}; + +} // namespace modbus diff --git a/src/plugins/protocol_modbus/include/modbus/ModbusParser.h b/src/plugins/protocol_modbus/include/modbus/ModbusParser.h new file mode 100644 index 0000000..78f1b97 --- /dev/null +++ b/src/plugins/protocol_modbus/include/modbus/ModbusParser.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +namespace modbus { + +class ModbusParser : public softbus::core::IParser { +public: + explicit ModbusParser(std::string defaultObjectRef = {}); + + softbus::core::ParseResult parseFrame(const uint8_t* data, std::size_t size, + const std::string& sourceId) override; + const char* protocolName() const override { return "modbus"; } + +private: + std::string defaultObjectRef_; +}; + +} // namespace modbus diff --git a/src/plugins/protocol_modbus/src/ModbusFramer.cpp b/src/plugins/protocol_modbus/src/ModbusFramer.cpp new file mode 100644 index 0000000..570367d --- /dev/null +++ b/src/plugins/protocol_modbus/src/ModbusFramer.cpp @@ -0,0 +1,25 @@ +#include + +namespace modbus { + +softbus::core::FrameResult ModbusFramer::extract(const uint8_t* data, std::size_t size) +{ + softbus::core::FrameResult result; + if (size < 7 || data[1] != 0x03) { + return result; + } + + const std::size_t byteCount = data[2]; + const std::size_t frameSize = 3 + byteCount + 2; + if (size < frameSize) { + return result; + } + + result.success = true; + result.frameStartOffset = 0; + result.consumedBytes = frameSize; + result.frame.assign(data, data + frameSize); + return result; +} + +} // namespace modbus diff --git a/src/plugins/protocol_modbus/src/ModbusParser.cpp b/src/plugins/protocol_modbus/src/ModbusParser.cpp new file mode 100644 index 0000000..55375e5 --- /dev/null +++ b/src/plugins/protocol_modbus/src/ModbusParser.cpp @@ -0,0 +1,41 @@ +#include + +namespace modbus { + +ModbusParser::ModbusParser(std::string defaultObjectRef) + : defaultObjectRef_(std::move(defaultObjectRef)) +{ +} + +softbus::core::ParseResult ModbusParser::parseFrame(const uint8_t* data, std::size_t size, + const std::string& sourceId) +{ + softbus::core::ParseResult result; + if (size < 7 || data[1] != 0x03) { + return result; + } + + const std::size_t byteCount = data[2]; + if (byteCount < 2 || size < 3 + byteCount + 2) { + return result; + } + + const uint16_t registerValue = + (static_cast(data[3]) << 8) | static_cast(data[4]); + + result.success = true; + result.message.sourceId = sourceId; + result.message.id = sourceId.empty() ? defaultObjectRef_ : sourceId; + result.message.domain = softbus::core::DomDomain::Process; + result.message.quality = softbus::core::QualityCode::Good; + result.message.value = { + {"protocol", "modbus"}, + {"function", 0x03}, + {"register", 40001}, + {"registerValue", registerValue}, + {"value", static_cast(registerValue) * 0.1} + }; + return result; +} + +} // namespace modbus diff --git a/src/plugins/protocol_modbus/src/modbus_plugin.cpp b/src/plugins/protocol_modbus/src/modbus_plugin.cpp new file mode 100644 index 0000000..c3ebb2b --- /dev/null +++ b/src/plugins/protocol_modbus/src/modbus_plugin.cpp @@ -0,0 +1,22 @@ +#include +#include + +extern "C" SOFTBUS_PLUGIN_API softbus::core::IFramer* create_framer() +{ + return new modbus::ModbusFramer(); +} + +extern "C" SOFTBUS_PLUGIN_API void destroy_framer(softbus::core::IFramer* framer) +{ + delete framer; +} + +extern "C" SOFTBUS_PLUGIN_API softbus::core::IParser* create_parser() +{ + return new modbus::ModbusParser(); +} + +extern "C" SOFTBUS_PLUGIN_API void destroy_parser(softbus::core::IParser* parser) +{ + delete parser; +} diff --git a/src/softbus_api/CMakeLists.txt b/src/softbus_api/CMakeLists.txt new file mode 100644 index 0000000..158b8f8 --- /dev/null +++ b/src/softbus_api/CMakeLists.txt @@ -0,0 +1,9 @@ +softbus_package(softbus_api) + +add_library(softbus_api STATIC + src/ExecuteCommand.cpp +) + +softbus_export_include(softbus_api "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(softbus_api PUBLIC softbus_core nlohmann_json::nlohmann_json) diff --git a/src/softbus_api/include/softbus_api/ExecuteCommand.h b/src/softbus_api/include/softbus_api/ExecuteCommand.h new file mode 100644 index 0000000..1befa97 --- /dev/null +++ b/src/softbus_api/include/softbus_api/ExecuteCommand.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace softbus::api { + +struct ExecuteCommand { + std::string traceId; + std::string objectRef; + std::string command; + nlohmann::json params; + + static ExecuteCommand fromJson(const nlohmann::json& j); + nlohmann::json toJson() const; + bool validate(std::string* error) const; +}; + +} // namespace softbus::api diff --git a/src/softbus_api/src/ExecuteCommand.cpp b/src/softbus_api/src/ExecuteCommand.cpp new file mode 100644 index 0000000..e28bbab --- /dev/null +++ b/src/softbus_api/src/ExecuteCommand.cpp @@ -0,0 +1,51 @@ +#include + +namespace softbus::api { + +ExecuteCommand ExecuteCommand::fromJson(const nlohmann::json& j) +{ + ExecuteCommand cmd; + cmd.traceId = j.value("traceId", ""); + cmd.objectRef = j.value("objectRef", ""); + cmd.command = j.value("command", ""); + if (j.contains("params")) { + cmd.params = j["params"]; + } + return cmd; +} + +nlohmann::json ExecuteCommand::toJson() const +{ + return nlohmann::json{ + {"type", "executeCommand"}, + {"traceId", traceId}, + {"objectRef", objectRef}, + {"command", command}, + {"params", params} + }; +} + +bool ExecuteCommand::validate(std::string* error) const +{ + if (traceId.empty()) { + if (error) { + *error = "traceId is required"; + } + return false; + } + if (objectRef.empty()) { + if (error) { + *error = "objectRef is required"; + } + return false; + } + if (command.empty()) { + if (error) { + *error = "command is required"; + } + return false; + } + return true; +} + +} // namespace softbus::api diff --git a/src/softbus_core/CMakeLists.txt b/src/softbus_core/CMakeLists.txt new file mode 100644 index 0000000..40ea486 --- /dev/null +++ b/src/softbus_core/CMakeLists.txt @@ -0,0 +1,17 @@ +softbus_package(softbus_core) + +add_library(softbus_core STATIC + src/time/HwClock.cpp + src/logging/Logger.cpp + src/alignment/TimeAligner.cpp + src/plugin/PluginLoader.cpp + src/config/ProfileLoader.cpp + src/platform/PlatformSignal.cpp + src/identity/DeviceIdentity.cpp + src/models/ObjectModel.cpp +) + +softbus_export_include(softbus_core "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(softbus_core PUBLIC nlohmann_json::nlohmann_json) +softbus_apply_platform_libs(softbus_core) diff --git a/src/softbus_core/include/softbus_core/alignment/TimeAligner.h b/src/softbus_core/include/softbus_core/alignment/TimeAligner.h new file mode 100644 index 0000000..d0281ef --- /dev/null +++ b/src/softbus_core/include/softbus_core/alignment/TimeAligner.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace softbus::core { + +enum class AlignStrategy { + Nearest, + Linear +}; + +struct TimeAlignerConfig { + int64_t windowNs{50'000'000}; + AlignStrategy strategy{AlignStrategy::Nearest}; +}; + +struct AlignedSample { + int64_t anchorTimestampNs{0}; + std::map channels; +}; + +class TimeAligner { +public: + explicit TimeAligner(TimeAlignerConfig config = {}); + + void ingest(const RawPacket& raw); + void ingestDom(const DOMMessage& dom); + std::vector drainAligned(); + + void setConfig(const TimeAlignerConfig& config); + TimeAlignerConfig config() const; + +private: + struct ChannelBuffer { + std::deque samples; + }; + + DOMMessage rawToDom(const RawPacket& raw) const; + AlignedSample alignAt(int64_t anchorNs); + + mutable std::mutex mutex_; + TimeAlignerConfig config_; + std::map channels_; + int64_t lastAnchorNs_{0}; +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/config/ProfileLoader.h b/src/softbus_core/include/softbus_core/config/ProfileLoader.h new file mode 100644 index 0000000..2908811 --- /dev/null +++ b/src/softbus_core/include/softbus_core/config/ProfileLoader.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace softbus::core { + +struct OpcUaProfile { + uint16_t port{4840}; + std::string applicationName{"SoftBusDaemon"}; +}; + +struct UplinkProfile { + std::string listenHost{"127.0.0.1"}; + uint16_t listenPort{9000}; +}; + +struct PluginProfile { + std::string modbus{"protocol_modbus"}; + std::string canopen{"protocol_canopen"}; +}; + +struct SecurityProfile { + bool tlsEnabled{false}; + std::string certPath; + std::string keyPath; +}; + +struct DaemonProfile { + OpcUaProfile opcua; + UplinkProfile uplink; + PluginProfile plugins; + SecurityProfile security; + + bool loadFromFile(const std::string& path); +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/identity/DeviceIdentity.h b/src/softbus_core/include/softbus_core/identity/DeviceIdentity.h new file mode 100644 index 0000000..60f628e --- /dev/null +++ b/src/softbus_core/include/softbus_core/identity/DeviceIdentity.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace softbus::core { + +class DeviceIdentity { +public: + static std::string generateRuntimeDeviceId(); + static std::string generateTraceId(); + static bool isValidRuntimeDeviceId(const std::string& id); + static bool isValidStableDeviceKey(const std::string& key); + static ObjectRef buildObjectRef(const ObjectModelTree& tree, const std::string& stableDeviceKey, + const std::string& pointName); +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/logging/Logger.h b/src/softbus_core/include/softbus_core/logging/Logger.h new file mode 100644 index 0000000..e76a667 --- /dev/null +++ b/src/softbus_core/include/softbus_core/logging/Logger.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include + +namespace softbus::core { + +enum class LogLevel { Debug, Info, Warn, Error }; + +class Logger { +public: + static Logger& instance(); + + void setLevel(LogLevel level); + void log(LogLevel level, const std::string& tag, const std::string& message); + void log(LogLevel level, const std::string& tag, const std::string& traceId, + const std::string& message); + + template + void info(const std::string& tag, Args&&... args) + { + log(LogLevel::Info, tag, concat(std::forward(args)...)); + } + + template + void infoTrace(const std::string& tag, const std::string& traceId, Args&&... args) + { + log(LogLevel::Info, tag, traceId, concat(std::forward(args)...)); + } + + template + void warn(const std::string& tag, Args&&... args) + { + log(LogLevel::Warn, tag, concat(std::forward(args)...)); + } + + template + void warnTrace(const std::string& tag, const std::string& traceId, Args&&... args) + { + log(LogLevel::Warn, tag, traceId, concat(std::forward(args)...)); + } + + template + void error(const std::string& tag, Args&&... args) + { + log(LogLevel::Error, tag, concat(std::forward(args)...)); + } + + template + void errorTrace(const std::string& tag, const std::string& traceId, Args&&... args) + { + log(LogLevel::Error, tag, traceId, concat(std::forward(args)...)); + } + + template + void debug(const std::string& tag, Args&&... args) + { + log(LogLevel::Debug, tag, concat(std::forward(args)...)); + } + +private: + Logger() = default; + + template + static std::string concat(T&& value) + { + std::ostringstream oss; + oss << std::forward(value); + return oss.str(); + } + + template + static std::string concat(T&& first, Rest&&... rest) + { + std::ostringstream oss; + oss << std::forward(first); + ((oss << std::forward(rest)), ...); + return oss.str(); + } + + LogLevel level_{LogLevel::Info}; + std::mutex mutex_; +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/memory/LockFreeQueue.h b/src/softbus_core/include/softbus_core/memory/LockFreeQueue.h new file mode 100644 index 0000000..41488a0 --- /dev/null +++ b/src/softbus_core/include/softbus_core/memory/LockFreeQueue.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/src/softbus_core/include/softbus_core/models/DOMMessage.h b/src/softbus_core/include/softbus_core/models/DOMMessage.h new file mode 100644 index 0000000..f6d8c0c --- /dev/null +++ b/src/softbus_core/include/softbus_core/models/DOMMessage.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +namespace softbus::core { + +enum class DomDomain : uint8_t { + Process = 0, + Control = 1, + Monitor = 2, + Management = 3 +}; + +enum class QualityCode : int { + Good = 0, + Bad = 1, + Timeout = 2, + Overflow = 3 +}; + +struct DOMMessage { + std::string traceId; + std::string id; + std::string sourceId; + int64_t timestampNs{0}; + DomDomain domain{DomDomain::Process}; + QualityCode quality{QualityCode::Good}; + nlohmann::json value; + + nlohmann::json toJson() const + { + return nlohmann::json{ + {"traceId", traceId}, + {"id", id}, + {"sourceId", sourceId}, + {"timestampNs", timestampNs}, + {"domain", static_cast(domain)}, + {"quality", static_cast(quality)}, + {"value", value} + }; + } + + static DOMMessage fromJson(const nlohmann::json& j) + { + DOMMessage msg; + msg.traceId = j.value("traceId", ""); + msg.id = j.value("id", ""); + msg.sourceId = j.value("sourceId", ""); + msg.timestampNs = j.value("timestampNs", int64_t{0}); + msg.domain = static_cast(j.value("domain", 0)); + msg.quality = static_cast(j.value("quality", 0)); + if (j.contains("value")) { + msg.value = j["value"]; + } + return msg; + } +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/models/MetadataDef.h b/src/softbus_core/include/softbus_core/models/MetadataDef.h new file mode 100644 index 0000000..3b60782 --- /dev/null +++ b/src/softbus_core/include/softbus_core/models/MetadataDef.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include +#include + +namespace softbus::core { + +struct MetadataDef { + std::string objectRef; + std::string dataType{"double"}; + std::string unit; + double scale{1.0}; + DomDomain domain{DomDomain::Process}; + int registerAddress{0}; +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/models/ObjectModel.h b/src/softbus_core/include/softbus_core/models/ObjectModel.h new file mode 100644 index 0000000..1f7933d --- /dev/null +++ b/src/softbus_core/include/softbus_core/models/ObjectModel.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +namespace softbus::core { + +enum class ObjectLayer { + Site = 0, + System = 1, + Asset = 2, + Device = 3, + Point = 4 +}; + +struct ObjectRef { + std::string site; + std::string system; + std::string asset; + std::string device; + std::string point; + + std::string toPath() const; + static ObjectRef fromPath(const std::string& path); + bool isValid() const; +}; + +struct PointDef { + std::string name; + int registerAddress{0}; + std::string domain{"Process"}; +}; + +struct DeviceDef { + std::string stableDeviceKey; + std::string protocol; + std::vector points; +}; + +struct ObjectModelTree { + std::string site; + std::string system; + std::string asset; + std::vector devices; + + bool loadFromFile(const std::string& path); + bool isValid() const; +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/models/RawBusMessage.h b/src/softbus_core/include/softbus_core/models/RawBusMessage.h new file mode 100644 index 0000000..f8e80a0 --- /dev/null +++ b/src/softbus_core/include/softbus_core/models/RawBusMessage.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace softbus::core { + +using RawBusMessage = RawPacket; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/models/RawPacket.h b/src/softbus_core/include/softbus_core/models/RawPacket.h new file mode 100644 index 0000000..74947e9 --- /dev/null +++ b/src/softbus_core/include/softbus_core/models/RawPacket.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace softbus::core { + +struct RawPacket { + std::string traceId; + int64_t timestampNs{0}; + std::string runtimeDeviceId; + std::string sourceId; + std::string protocolTag; + std::vector payload; + + RawPacket() = default; + + RawPacket(std::string trace, int64_t ts, std::string runtimeId, std::string source, + std::string protocol, std::vector data) + : traceId(std::move(trace)) + , timestampNs(ts) + , runtimeDeviceId(std::move(runtimeId)) + , sourceId(std::move(source)) + , protocolTag(std::move(protocol)) + , payload(std::move(data)) + { + } +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/platform/PlatformSignal.h b/src/softbus_core/include/softbus_core/platform/PlatformSignal.h new file mode 100644 index 0000000..f32f538 --- /dev/null +++ b/src/softbus_core/include/softbus_core/platform/PlatformSignal.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace softbus::core { + +class PlatformSignal { +public: + using Handler = std::function; + + static void install(Handler handler); + static bool shouldStop(); + static void requestStop(); + +private: + static std::atomic stopRequested_; +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/plugin/IFramer.h b/src/softbus_core/include/softbus_core/plugin/IFramer.h new file mode 100644 index 0000000..f2dbe94 --- /dev/null +++ b/src/softbus_core/include/softbus_core/plugin/IFramer.h @@ -0,0 +1,36 @@ +#pragma once + +#ifdef _WIN32 +#ifdef SOFTBUS_PLUGIN_EXPORTS +#define SOFTBUS_PLUGIN_API __declspec(dllexport) +#else +#define SOFTBUS_PLUGIN_API __declspec(dllimport) +#endif +#else +#define SOFTBUS_PLUGIN_API __attribute__((visibility("default"))) +#endif + +#include +#include +#include + +namespace softbus::core { + +struct FrameResult { + bool success{false}; + std::size_t consumedBytes{0}; + std::size_t frameStartOffset{0}; + std::vector frame; +}; + +class IFramer { +public: + virtual ~IFramer() = default; + virtual FrameResult extract(const uint8_t* data, std::size_t size) = 0; + virtual const char* protocolName() const = 0; +}; + +extern "C" SOFTBUS_PLUGIN_API IFramer* create_framer(); +extern "C" SOFTBUS_PLUGIN_API void destroy_framer(IFramer* framer); + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/plugin/IParser.h b/src/softbus_core/include/softbus_core/plugin/IParser.h new file mode 100644 index 0000000..00ed545 --- /dev/null +++ b/src/softbus_core/include/softbus_core/plugin/IParser.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace softbus::core { + +struct ParseResult { + bool success{false}; + DOMMessage message; +}; + +class IParser { +public: + virtual ~IParser() = default; + virtual ParseResult parseFrame(const uint8_t* data, std::size_t size, const std::string& sourceId) = 0; + virtual const char* protocolName() const = 0; +}; + +extern "C" SOFTBUS_PLUGIN_API IParser* create_parser(); +extern "C" SOFTBUS_PLUGIN_API void destroy_parser(IParser* parser); + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/plugin/PluginLoader.h b/src/softbus_core/include/softbus_core/plugin/PluginLoader.h new file mode 100644 index 0000000..9d0659a --- /dev/null +++ b/src/softbus_core/include/softbus_core/plugin/PluginLoader.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace softbus::core { + +class PluginLoader { +public: + PluginLoader() = default; + ~PluginLoader(); + + PluginLoader(const PluginLoader&) = delete; + PluginLoader& operator=(const PluginLoader&) = delete; + + bool load(const std::string& pluginPath); + void unload(); + + IFramer* framer() const { return framer_; } + IParser* parser() const { return parser_; } + bool isLoaded() const { return handle_ != nullptr; } + +private: + void* handle_{nullptr}; + IFramer* framer_{nullptr}; + IParser* parser_{nullptr}; + + using CreateFramerFn = IFramer* (*)(); + using DestroyFramerFn = void (*)(IFramer*); + using CreateParserFn = IParser* (*)(); + using DestroyParserFn = void (*)(IParser*); + + DestroyFramerFn destroyFramer_{nullptr}; + DestroyParserFn destroyParser_{nullptr}; +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/queue/LockFreeQueue.h b/src/softbus_core/include/softbus_core/queue/LockFreeQueue.h new file mode 100644 index 0000000..4f5eaa8 --- /dev/null +++ b/src/softbus_core/include/softbus_core/queue/LockFreeQueue.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include + +namespace softbus::core { + +template +class LockFreeQueue { +public: + explicit LockFreeQueue(std::size_t capacity) + : capacity_(capacity) + , buffer_(std::make_unique(capacity)) + , head_(0) + , tail_(0) + { + for (std::size_t i = 0; i < capacity_; ++i) { + buffer_[i].sequence.store(i, std::memory_order_relaxed); + } + } + + bool push(T item) + { + std::size_t pos = tail_.load(std::memory_order_relaxed); + for (;;) { + Slot& slot = buffer_[pos % capacity_]; + const std::size_t seq = slot.sequence.load(std::memory_order_acquire); + const intptr_t diff = static_cast(seq) - static_cast(pos); + if (diff == 0) { + if (tail_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) { + slot.data = std::move(item); + slot.sequence.store(pos + 1, std::memory_order_release); + return true; + } + } else if (diff < 0) { + return false; + } else { + pos = tail_.load(std::memory_order_relaxed); + } + } + } + + std::optional pop() + { + std::size_t pos = head_.load(std::memory_order_relaxed); + for (;;) { + Slot& slot = buffer_[pos % capacity_]; + const std::size_t seq = slot.sequence.load(std::memory_order_acquire); + const intptr_t diff = static_cast(seq) - static_cast(pos + 1); + if (diff == 0) { + if (head_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) { + T item = std::move(slot.data); + slot.sequence.store(pos + capacity_, std::memory_order_release); + return item; + } + } else if (diff < 0) { + return std::nullopt; + } else { + pos = head_.load(std::memory_order_relaxed); + } + } + } + + bool empty() const + { + const std::size_t h = head_.load(std::memory_order_acquire); + const std::size_t t = tail_.load(std::memory_order_acquire); + return h >= t; + } + + std::size_t capacity() const { return capacity_; } + +private: + struct Slot { + std::atomic sequence{0}; + T data{}; + }; + + const std::size_t capacity_; + std::unique_ptr buffer_; + std::atomic head_; + std::atomic tail_; +}; + +} // namespace softbus::core diff --git a/src/softbus_core/include/softbus_core/softbus_core.h b/src/softbus_core/include/softbus_core/softbus_core.h new file mode 100644 index 0000000..e987471 --- /dev/null +++ b/src/softbus_core/include/softbus_core/softbus_core.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/src/softbus_core/include/softbus_core/time/HwClock.h b/src/softbus_core/include/softbus_core/time/HwClock.h new file mode 100644 index 0000000..258b937 --- /dev/null +++ b/src/softbus_core/include/softbus_core/time/HwClock.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace softbus::core { + +class HwClock { +public: + static int64_t nowNs(); + static double nsToMs(int64_t ns); +}; + +} // namespace softbus::core diff --git a/src/softbus_core/src/alignment/TimeAligner.cpp b/src/softbus_core/src/alignment/TimeAligner.cpp new file mode 100644 index 0000000..3de2296 --- /dev/null +++ b/src/softbus_core/src/alignment/TimeAligner.cpp @@ -0,0 +1,107 @@ +#include + +#include +#include +#include + +namespace softbus::core { + +TimeAligner::TimeAligner(TimeAlignerConfig config) + : config_(config) +{ +} + +void TimeAligner::setConfig(const TimeAlignerConfig& config) +{ + std::lock_guard lock(mutex_); + config_ = config; +} + +TimeAlignerConfig TimeAligner::config() const +{ + std::lock_guard lock(mutex_); + return config_; +} + +DOMMessage TimeAligner::rawToDom(const RawPacket& raw) const +{ + DOMMessage dom; + dom.traceId = raw.traceId; + dom.id = raw.sourceId; + dom.sourceId = raw.sourceId; + dom.timestampNs = raw.timestampNs; + dom.domain = DomDomain::Process; + dom.quality = QualityCode::Good; + dom.value = { + {"protocol", raw.protocolTag}, + {"runtimeDeviceId", raw.runtimeDeviceId}, + {"payloadSize", raw.payload.size()}, + {"payloadHex", nlohmann::json::array()} + }; + for (const auto byte : raw.payload) { + dom.value["payloadHex"].push_back(byte); + } + return dom; +} + +void TimeAligner::ingest(const RawPacket& raw) +{ + ingestDom(rawToDom(raw)); +} + +void TimeAligner::ingestDom(const DOMMessage& dom) +{ + std::lock_guard lock(mutex_); + auto& buffer = channels_[dom.sourceId].samples; + buffer.push_back(dom); + while (buffer.size() > 256) { + buffer.pop_front(); + } + lastAnchorNs_ = std::max(lastAnchorNs_, dom.timestampNs); +} + +AlignedSample TimeAligner::alignAt(int64_t anchorNs) +{ + AlignedSample sample; + sample.anchorTimestampNs = anchorNs; + + for (auto& [sourceId, channel] : channels_) { + auto& samples = channel.samples; + if (samples.empty()) { + continue; + } + + DOMMessage best; + int64_t bestDelta = std::numeric_limits::max(); + bool found = false; + + for (const auto& candidate : samples) { + const int64_t delta = std::llabs(candidate.timestampNs - anchorNs); + if (delta <= config_.windowNs && delta < bestDelta) { + bestDelta = delta; + best = candidate; + found = true; + } + } + + if (found) { + sample.channels[sourceId] = best; + } + } + + return sample; +} + +std::vector TimeAligner::drainAligned() +{ + std::lock_guard lock(mutex_); + if (lastAnchorNs_ == 0 || channels_.empty()) { + return {}; + } + + std::vector result; + result.push_back(alignAt(lastAnchorNs_)); + return result; +} + +} // namespace softbus::core diff --git a/src/softbus_core/src/config/ProfileLoader.cpp b/src/softbus_core/src/config/ProfileLoader.cpp new file mode 100644 index 0000000..b5efc20 --- /dev/null +++ b/src/softbus_core/src/config/ProfileLoader.cpp @@ -0,0 +1,37 @@ +#include + +#include +#include + +namespace softbus::core { + +bool DaemonProfile::loadFromFile(const std::string& path) +{ + std::ifstream ifs(path); + if (!ifs) { + return false; + } + nlohmann::json j; + ifs >> j; + + if (j.contains("opcua")) { + opcua.port = static_cast(j["opcua"].value("port", 4840)); + opcua.applicationName = j["opcua"].value("applicationName", "SoftBusDaemon"); + } + if (j.contains("uplink")) { + uplink.listenHost = j["uplink"].value("listenHost", "127.0.0.1"); + uplink.listenPort = static_cast(j["uplink"].value("listenPort", 9000)); + } + if (j.contains("plugins")) { + plugins.modbus = j["plugins"].value("modbus", "protocol_modbus"); + plugins.canopen = j["plugins"].value("canopen", "protocol_canopen"); + } + if (j.contains("security")) { + security.tlsEnabled = j["security"].value("tlsEnabled", false); + security.certPath = j["security"].value("certPath", ""); + security.keyPath = j["security"].value("keyPath", ""); + } + return true; +} + +} // namespace softbus::core diff --git a/src/softbus_core/src/identity/DeviceIdentity.cpp b/src/softbus_core/src/identity/DeviceIdentity.cpp new file mode 100644 index 0000000..4599e38 --- /dev/null +++ b/src/softbus_core/src/identity/DeviceIdentity.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#include +#include +#include +#include + +namespace softbus::core { + +namespace { + +std::string randomHex(std::size_t bytes) +{ + static thread_local std::mt19937_64 rng{ + static_cast( + std::chrono::steady_clock::now().time_since_epoch().count())}; + std::uniform_int_distribution dist(0, 255); + std::ostringstream oss; + for (std::size_t i = 0; i < bytes; ++i) { + oss << std::hex << std::setw(2) << std::setfill('0') << dist(rng); + } + return oss.str(); +} + +} // namespace + +std::string DeviceIdentity::generateRuntimeDeviceId() +{ + return "runtime-" + randomHex(8); +} + +std::string DeviceIdentity::generateTraceId() +{ + return "trace-" + randomHex(8); +} + +bool DeviceIdentity::isValidRuntimeDeviceId(const std::string& id) +{ + return !id.empty() && id.rfind("runtime-", 0) == 0; +} + +bool DeviceIdentity::isValidStableDeviceKey(const std::string& key) +{ + return !key.empty() && key.find('/') == std::string::npos; +} + +ObjectRef DeviceIdentity::buildObjectRef(const ObjectModelTree& tree, const std::string& stableDeviceKey, + const std::string& pointName) +{ + ObjectRef ref; + ref.site = tree.site; + ref.system = tree.system; + ref.asset = tree.asset; + ref.device = stableDeviceKey; + ref.point = pointName; + return ref; +} + +} // namespace softbus::core diff --git a/src/softbus_core/src/logging/Logger.cpp b/src/softbus_core/src/logging/Logger.cpp new file mode 100644 index 0000000..bc1e18e --- /dev/null +++ b/src/softbus_core/src/logging/Logger.cpp @@ -0,0 +1,62 @@ +#include + +#include +#include +#include + +namespace softbus::core { + +Logger& Logger::instance() +{ + static Logger logger; + return logger; +} + +void Logger::setLevel(LogLevel level) +{ + std::lock_guard lock(mutex_); + level_ = level; +} + +void Logger::log(LogLevel level, const std::string& tag, const std::string& message) +{ + log(level, tag, "", message); +} + +void Logger::log(LogLevel level, const std::string& tag, const std::string& traceId, + const std::string& message) +{ + if (static_cast(level) < static_cast(level_)) { + return; + } + + const char* levelStr = "INFO"; + switch (level) { + case LogLevel::Debug: levelStr = "DEBUG"; break; + case LogLevel::Info: levelStr = "INFO"; break; + case LogLevel::Warn: levelStr = "WARN"; break; + case LogLevel::Error: levelStr = "ERROR"; break; + } + + const auto now = std::chrono::system_clock::now(); + const auto time = std::chrono::system_clock::to_time_t(now); + std::tm tm{}; +#ifdef SOFTBUS_PLATFORM_WINDOWS + localtime_s(&tm, &time); +#else + localtime_r(&time, &tm); +#endif + + std::ostringstream oss; + oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") + << " [" << levelStr << "] [" << tag << "]"; + if (!traceId.empty()) { + oss << "[trace=" << traceId << "]"; + } + oss << " " << message; + + std::lock_guard lock(mutex_); + std::cerr << oss.str() << std::endl; +} + +} // namespace softbus::core diff --git a/src/softbus_core/src/models/ObjectModel.cpp b/src/softbus_core/src/models/ObjectModel.cpp new file mode 100644 index 0000000..eae4699 --- /dev/null +++ b/src/softbus_core/src/models/ObjectModel.cpp @@ -0,0 +1,84 @@ +#include + +#include +#include +#include + +namespace softbus::core { + +std::string ObjectRef::toPath() const +{ + std::ostringstream oss; + oss << site << '/' << system << '/' << asset << '/' << device; + if (!point.empty()) { + oss << '/' << point; + } + return oss.str(); +} + +ObjectRef ObjectRef::fromPath(const std::string& path) +{ + ObjectRef ref; + std::istringstream iss(path); + std::string token; + std::string parts[5]; + int idx = 0; + while (std::getline(iss, token, '/') && idx < 5) { + parts[idx++] = token; + } + if (idx >= 4) { + ref.site = parts[0]; + ref.system = parts[1]; + ref.asset = parts[2]; + ref.device = parts[3]; + if (idx >= 5) { + ref.point = parts[4]; + } + } + return ref; +} + +bool ObjectRef::isValid() const +{ + return !site.empty() && !system.empty() && !asset.empty() && !device.empty(); +} + +bool ObjectModelTree::loadFromFile(const std::string& path) +{ + std::ifstream ifs(path); + if (!ifs) { + return false; + } + nlohmann::json j; + ifs >> j; + site = j.value("site", ""); + system = j.value("system", ""); + asset = j.value("asset", ""); + devices.clear(); + if (!j.contains("devices")) { + return isValid(); + } + for (const auto& dev : j["devices"]) { + DeviceDef device; + device.stableDeviceKey = dev.value("stableDeviceKey", ""); + device.protocol = dev.value("protocol", ""); + if (dev.contains("points")) { + for (const auto& pt : dev["points"]) { + PointDef point; + point.name = pt.value("name", ""); + point.registerAddress = pt.value("register", 0); + point.domain = pt.value("domain", "Process"); + device.points.push_back(std::move(point)); + } + } + devices.push_back(std::move(device)); + } + return isValid(); +} + +bool ObjectModelTree::isValid() const +{ + return !site.empty() && !system.empty() && !asset.empty(); +} + +} // namespace softbus::core diff --git a/src/softbus_core/src/platform/PlatformSignal.cpp b/src/softbus_core/src/platform/PlatformSignal.cpp new file mode 100644 index 0000000..da91cd4 --- /dev/null +++ b/src/softbus_core/src/platform/PlatformSignal.cpp @@ -0,0 +1,48 @@ +#include + +#ifdef SOFTBUS_PLATFORM_WINDOWS +#include +#else +#include +#endif + +namespace softbus::core { + +std::atomic PlatformSignal::stopRequested_{false}; + +#ifdef SOFTBUS_PLATFORM_WINDOWS +namespace { + BOOL WINAPI consoleHandler(DWORD signal) + { + if (signal == CTRL_C_EVENT || signal == CTRL_BREAK_EVENT || signal == CTRL_CLOSE_EVENT) { + PlatformSignal::requestStop(); + return TRUE; + } + return FALSE; + } +} +#endif + +void PlatformSignal::install(Handler handler) +{ + stopRequested_.store(false); +#ifdef SOFTBUS_PLATFORM_WINDOWS + SetConsoleCtrlHandler(consoleHandler, TRUE); +#else + std::signal(SIGINT, [](int) { requestStop(); }); + std::signal(SIGTERM, [](int) { requestStop(); }); +#endif + (void)handler; +} + +bool PlatformSignal::shouldStop() +{ + return stopRequested_.load(); +} + +void PlatformSignal::requestStop() +{ + stopRequested_.store(true); +} + +} // namespace softbus::core diff --git a/src/softbus_core/src/plugin/PluginLoader.cpp b/src/softbus_core/src/plugin/PluginLoader.cpp new file mode 100644 index 0000000..598a8e8 --- /dev/null +++ b/src/softbus_core/src/plugin/PluginLoader.cpp @@ -0,0 +1,86 @@ +#include + +#include + +#ifdef SOFTBUS_PLATFORM_WINDOWS +#include +#else +#include +#endif + +namespace softbus::core { + +PluginLoader::~PluginLoader() +{ + unload(); +} + +bool PluginLoader::load(const std::string& pluginPath) +{ + unload(); + +#ifdef SOFTBUS_PLATFORM_WINDOWS + handle_ = LoadLibraryA(pluginPath.c_str()); + if (!handle_) { + Logger::instance().error("PluginLoader", "Failed to load plugin: ", pluginPath); + return false; + } + auto createFramer = reinterpret_cast(GetProcAddress(static_cast(handle_), "create_framer")); + auto destroyFramer = reinterpret_cast(GetProcAddress(static_cast(handle_), "destroy_framer")); + auto createParser = reinterpret_cast(GetProcAddress(static_cast(handle_), "create_parser")); + auto destroyParser = reinterpret_cast(GetProcAddress(static_cast(handle_), "destroy_parser")); +#else + handle_ = dlopen(pluginPath.c_str(), RTLD_NOW | RTLD_LOCAL); + if (!handle_) { + Logger::instance().error("PluginLoader", "Failed to load plugin: ", pluginPath, " err=", dlerror()); + return false; + } + auto createFramer = reinterpret_cast(dlsym(handle_, "create_framer")); + auto destroyFramer = reinterpret_cast(dlsym(handle_, "destroy_framer")); + auto createParser = reinterpret_cast(dlsym(handle_, "create_parser")); + auto destroyParser = reinterpret_cast(dlsym(handle_, "destroy_parser")); +#endif + + if (!createFramer || !destroyFramer || !createParser || !destroyParser) { + Logger::instance().error("PluginLoader", "Plugin missing required exports: ", pluginPath); + unload(); + return false; + } + + destroyFramer_ = destroyFramer; + destroyParser_ = destroyParser; + framer_ = createFramer(); + parser_ = createParser(); + if (!framer_ || !parser_) { + unload(); + return false; + } + + Logger::instance().info("PluginLoader", "Loaded plugin: ", pluginPath); + return true; +} + +void PluginLoader::unload() +{ + if (framer_ && destroyFramer_) { + destroyFramer_(framer_); + } + if (parser_ && destroyParser_) { + destroyParser_(parser_); + } + framer_ = nullptr; + parser_ = nullptr; + destroyFramer_ = nullptr; + destroyParser_ = nullptr; + + if (handle_) { +#ifdef SOFTBUS_PLATFORM_WINDOWS + FreeLibrary(static_cast(handle_)); +#else + dlclose(handle_); +#endif + handle_ = nullptr; + } +} + +} // namespace softbus::core diff --git a/src/softbus_core/src/time/HwClock.cpp b/src/softbus_core/src/time/HwClock.cpp new file mode 100644 index 0000000..65bf8dc --- /dev/null +++ b/src/softbus_core/src/time/HwClock.cpp @@ -0,0 +1,37 @@ +#include + +#include + +#ifdef SOFTBUS_PLATFORM_WINDOWS +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif + +namespace softbus::core { + +int64_t HwClock::nowNs() +{ +#ifdef SOFTBUS_PLATFORM_WINDOWS + static LARGE_INTEGER frequency = [] { + LARGE_INTEGER f{}; + QueryPerformanceFrequency(&f); + return f; + }(); + LARGE_INTEGER counter{}; + QueryPerformanceCounter(&counter); + return static_cast((counter.QuadPart * 1'000'000'000LL) / frequency.QuadPart); +#else + timespec ts{}; + clock_gettime(CLOCK_REALTIME, &ts); + return static_cast(ts.tv_sec) * 1'000'000'000LL + ts.tv_nsec; +#endif +} + +double HwClock::nsToMs(int64_t ns) +{ + return static_cast(ns) / 1'000'000.0; +} + +} // namespace softbus::core diff --git a/src/softbus_daemon/CMakeLists.txt b/src/softbus_daemon/CMakeLists.txt new file mode 100644 index 0000000..f068d17 --- /dev/null +++ b/src/softbus_daemon/CMakeLists.txt @@ -0,0 +1,26 @@ +softbus_package(softbus_daemon) + +add_executable(softbus_daemon + src/main.cpp + src/CoreService.cpp + src/PipelineEngine.cpp + src/MetadataRegistry.cpp + src/ObjectModelRegistry.cpp + src/CommandDispatcher.cpp + src/IpcOptionalDBus.cpp +) + +softbus_export_include(softbus_daemon "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(softbus_daemon PRIVATE + softbus_core + softbus_transport + softbus_api + softbus_opcua + asio +) +softbus_apply_platform_libs(softbus_daemon) +softbus_apply_open62541(softbus_daemon) +softbus_apply_dbus(softbus_daemon) + +add_dependencies(softbus_daemon protocol_modbus) diff --git a/src/softbus_daemon/include/softbus_daemon/api/IpcOptionalDBus.h b/src/softbus_daemon/include/softbus_daemon/api/IpcOptionalDBus.h new file mode 100644 index 0000000..46f996e --- /dev/null +++ b/src/softbus_daemon/include/softbus_daemon/api/IpcOptionalDBus.h @@ -0,0 +1,11 @@ +#pragma once + +namespace softbus::daemon { + +class IpcOptionalDBus { +public: + bool initialize(); + void shutdown(); +}; + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/include/softbus_daemon/command/CommandDispatcher.h b/src/softbus_daemon/include/softbus_daemon/command/CommandDispatcher.h new file mode 100644 index 0000000..b88d460 --- /dev/null +++ b/src/softbus_daemon/include/softbus_daemon/command/CommandDispatcher.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include +#include + +namespace softbus::daemon { + +class CommandDispatcher { +public: + explicit CommandDispatcher(softbus::transport::TcpUplinkServer* uplink); + + bool dispatch(const softbus::api::ExecuteCommand& command); + +private: + softbus::transport::TcpUplinkServer* uplink_{nullptr}; +}; + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/include/softbus_daemon/pipeline/PipelineEngine.h b/src/softbus_daemon/include/softbus_daemon/pipeline/PipelineEngine.h new file mode 100644 index 0000000..ebd8864 --- /dev/null +++ b/src/softbus_daemon/include/softbus_daemon/pipeline/PipelineEngine.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace softbus::daemon { + +class PipelineEngine { +public: + PipelineEngine(softbus::core::PluginLoader* plugin, softbus::opcua::UaModelBinder* binder); + + void process(const softbus::core::RawPacket& packet); + +private: + softbus::core::PluginLoader* plugin_{nullptr}; + softbus::opcua::UaModelBinder* binder_{nullptr}; +}; + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/include/softbus_daemon/registry/MetadataRegistry.h b/src/softbus_daemon/include/softbus_daemon/registry/MetadataRegistry.h new file mode 100644 index 0000000..52aed28 --- /dev/null +++ b/src/softbus_daemon/include/softbus_daemon/registry/MetadataRegistry.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include +#include +#include + +namespace softbus::daemon { + +class MetadataRegistry { +public: + void registerMetadata(const softbus::core::MetadataDef& metadata); + std::optional findByObjectRef(const std::string& objectRef) const; + +private: + std::map entries_; +}; + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/include/softbus_daemon/registry/ObjectModelRegistry.h b/src/softbus_daemon/include/softbus_daemon/registry/ObjectModelRegistry.h new file mode 100644 index 0000000..c4d899c --- /dev/null +++ b/src/softbus_daemon/include/softbus_daemon/registry/ObjectModelRegistry.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include +#include + +namespace softbus::daemon { + +class ObjectModelRegistry { +public: + bool loadFromFile(const std::string& path); + const softbus::core::ObjectModelTree& tree() const { return tree_; } + std::optional findPointByRegister(int registerAddress) const; + +private: + softbus::core::ObjectModelTree tree_; +}; + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/src/CommandDispatcher.cpp b/src/softbus_daemon/src/CommandDispatcher.cpp new file mode 100644 index 0000000..5947134 --- /dev/null +++ b/src/softbus_daemon/src/CommandDispatcher.cpp @@ -0,0 +1,32 @@ +#include + +#include + +namespace softbus::daemon { + +CommandDispatcher::CommandDispatcher(softbus::transport::TcpUplinkServer* uplink) + : uplink_(uplink) +{ +} + +bool CommandDispatcher::dispatch(const softbus::api::ExecuteCommand& command) +{ + std::string error; + if (!command.validate(&error)) { + softbus::core::Logger::instance().errorTrace("CommandDispatcher", command.traceId, + "Invalid executeCommand: ", error); + return false; + } + + if (!uplink_) { + return false; + } + + const bool ok = uplink_->sendCommand(command); + softbus::core::Logger::instance().infoTrace("CommandDispatcher", command.traceId, + "executeCommand dispatched to Edge objectRef=", command.objectRef, + " command=", command.command, " success=", ok ? "true" : "false"); + return ok; +} + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/src/CoreService.cpp b/src/softbus_daemon/src/CoreService.cpp new file mode 100644 index 0000000..e014039 --- /dev/null +++ b/src/softbus_daemon/src/CoreService.cpp @@ -0,0 +1,171 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef SOFTBUS_PLATFORM_WINDOWS +#include +#endif + +#include +#include + +namespace softbus::daemon { + +namespace fs = std::filesystem; + +CoreService::CoreService() = default; + +CoreService::~CoreService() +{ + stop(); +} + +std::string CoreService::resolvePluginPath(const std::string& pluginName) const +{ +#ifdef SOFTBUS_PLATFORM_WINDOWS + const std::string fileName = pluginName + ".dll"; + char exePath[MAX_PATH]{}; + GetModuleFileNameA(nullptr, exePath, MAX_PATH); + const fs::path exeDir = fs::path(exePath).parent_path(); +#else + const std::string fileName = "lib" + pluginName + ".so"; + fs::path exeDir = fs::current_path(); +#endif + + const fs::path candidates[] = { + exeDir / fileName, + exeDir / ".." / "lib" / fileName, + fs::path("build") / "bin" / fileName, + fs::path("lib") / fileName, + fs::path("bin") / fileName, + fs::current_path() / "build" / "bin" / fileName, + fs::current_path() / "lib" / fileName, + fs::current_path() / fileName + }; + + for (const auto& candidate : candidates) { + if (fs::exists(candidate)) { + return fs::absolute(candidate).string(); + } + } + return (exeDir / fileName).string(); +} + +bool CoreService::start(const std::string& profilePath, const std::string& modelPath) +{ + if (!profile_.loadFromFile(profilePath)) { + softbus::core::Logger::instance().error("CoreService", "Failed to load profile: ", profilePath); + return false; + } + + objectRegistry_ = std::make_unique(); + if (!objectRegistry_->loadFromFile(modelPath)) { + softbus::core::Logger::instance().error("CoreService", "Failed to load model: ", modelPath); + return false; + } + model_ = objectRegistry_->tree(); + + const std::string pluginPath = resolvePluginPath(profile_.plugins.modbus); + if (!modbusPlugin_.load(pluginPath)) { + softbus::core::Logger::instance().error("CoreService", "Failed to load modbus plugin: ", pluginPath); + return false; + } + + softbus::opcua::SecurityConfig security; + security.enableTls = profile_.security.tlsEnabled; + security.certPath = profile_.security.certPath; + security.keyPath = profile_.security.keyPath; + + uaEngine_ = std::make_unique(); + if (!uaEngine_->start(profile_.opcua.port, profile_.opcua.applicationName, security)) { + return false; + } + + uaBinder_ = std::make_unique(uaEngine_->nativeServer()); + uaMethodBinder_ = std::make_unique(uaEngine_->nativeServer()); + uaEventBridge_ = std::make_unique(uaEngine_->nativeServer()); + + softbus::opcua::UaAddressSpaceBuilder builder(uaEngine_->nativeServer()); + if (!builder.buildFromModel(model_, uaBinder_.get())) { + softbus::core::Logger::instance().error("CoreService", "Failed to build OPC UA address space"); + return false; + } + + pipeline_ = std::make_unique(&modbusPlugin_, uaBinder_.get()); + uplink_ = std::make_unique( + io_, profile_.uplink.listenHost, profile_.uplink.listenPort); + commandDispatcher_ = std::make_unique(uplink_.get()); + + uplink_->setRawPacketHandler([this](const softbus::core::RawPacket& packet) { + softbus::core::Logger::instance().infoTrace("CoreService", packet.traceId, "RawPacket received"); + pipeline_->process(packet); + }); + + uaMethodBinder_->bindExecuteCommand([this](const softbus::api::ExecuteCommand& cmd) { + return commandDispatcher_->dispatch(cmd); + }); + + uplink_->start(); + ioThread_ = std::thread([this]() { io_.run(); }); + running_ = true; + softbus::core::Logger::instance().info("CoreService", "Daemon started"); + return true; +} + +void CoreService::stop() +{ + if (!running_) { + return; + } + running_ = false; + if (uplink_) { + uplink_->stop(); + } + io_.stop(); + if (ioThread_.joinable()) { + ioThread_.join(); + } + if (uaEngine_) { + uaEngine_->stop(); + } + modbusPlugin_.unload(); + softbus::core::Logger::instance().info("CoreService", "Daemon stopped"); +} + +void CoreService::run(bool selfTest) +{ + softbus::core::PlatformSignal::install(nullptr); + + bool selfTestDone = false; + + while (!softbus::core::PlatformSignal::shouldStop()) { + if (uaEngine_) { + uaEngine_->iterate(); + } + if (selfTest && !selfTestDone && commandDispatcher_) { + softbus::api::ExecuteCommand cmd; + cmd.traceId = softbus::core::DeviceIdentity::generateTraceId(); + cmd.objectRef = "DemoSite/DemoSystem/DemoAsset/SimModbusDevice/Temperature"; + cmd.command = "write"; + cmd.params = {{"value", 42.5}}; + commandDispatcher_->dispatch(cmd); + selfTestDone = true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + stop(); +} + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/src/IpcOptionalDBus.cpp b/src/softbus_daemon/src/IpcOptionalDBus.cpp new file mode 100644 index 0000000..14870e6 --- /dev/null +++ b/src/softbus_daemon/src/IpcOptionalDBus.cpp @@ -0,0 +1,21 @@ +#include + +#include + +namespace softbus::daemon { + +bool IpcOptionalDBus::initialize() +{ +#if SOFTBUS_HAS_DBUS + softbus::core::Logger::instance().info("IpcOptionalDBus", "DBus IPC stub initialized"); +#else + softbus::core::Logger::instance().info("IpcOptionalDBus", "DBus IPC disabled on this platform"); +#endif + return true; +} + +void IpcOptionalDBus::shutdown() +{ +} + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/src/MetadataRegistry.cpp b/src/softbus_daemon/src/MetadataRegistry.cpp new file mode 100644 index 0000000..c7e982f --- /dev/null +++ b/src/softbus_daemon/src/MetadataRegistry.cpp @@ -0,0 +1,19 @@ +#include + +namespace softbus::daemon { + +void MetadataRegistry::registerMetadata(const softbus::core::MetadataDef& metadata) +{ + entries_[metadata.objectRef] = metadata; +} + +std::optional MetadataRegistry::findByObjectRef(const std::string& objectRef) const +{ + const auto it = entries_.find(objectRef); + if (it == entries_.end()) { + return std::nullopt; + } + return it->second; +} + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/src/ObjectModelRegistry.cpp b/src/softbus_daemon/src/ObjectModelRegistry.cpp new file mode 100644 index 0000000..dd9e325 --- /dev/null +++ b/src/softbus_daemon/src/ObjectModelRegistry.cpp @@ -0,0 +1,25 @@ +#include + +#include + +namespace softbus::daemon { + +bool ObjectModelRegistry::loadFromFile(const std::string& path) +{ + return tree_.loadFromFile(path); +} + +std::optional ObjectModelRegistry::findPointByRegister(int registerAddress) const +{ + for (const auto& device : tree_.devices) { + for (const auto& point : device.points) { + if (point.registerAddress == registerAddress) { + return softbus::core::DeviceIdentity::buildObjectRef( + tree_, device.stableDeviceKey, point.name); + } + } + } + return std::nullopt; +} + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/src/PipelineEngine.cpp b/src/softbus_daemon/src/PipelineEngine.cpp new file mode 100644 index 0000000..b023f74 --- /dev/null +++ b/src/softbus_daemon/src/PipelineEngine.cpp @@ -0,0 +1,48 @@ +#include + +#include + +#include + +namespace softbus::daemon { + +PipelineEngine::PipelineEngine(softbus::core::PluginLoader* plugin, softbus::opcua::UaModelBinder* binder) + : plugin_(plugin) + , binder_(binder) +{ +} + +void PipelineEngine::process(const softbus::core::RawPacket& packet) +{ + if (!plugin_ || !plugin_->parser() || !binder_) { + return; + } + + const uint8_t* data = packet.payload.data(); + std::size_t size = packet.payload.size(); + std::vector frame = packet.payload; + + if (plugin_->framer()) { + const auto framed = plugin_->framer()->extract(data, size); + if (framed.success) { + frame = framed.frame; + data = frame.data(); + size = frame.size(); + } + } + + auto parsed = plugin_->parser()->parseFrame(data, size, packet.sourceId); + if (!parsed.success) { + softbus::core::Logger::instance().warnTrace("PipelineEngine", packet.traceId, + "Failed to parse RawPacket"); + return; + } + + parsed.message.traceId = packet.traceId; + parsed.message.timestampNs = packet.timestampNs; + softbus::core::Logger::instance().infoTrace("PipelineEngine", packet.traceId, + "RawPacket converted to DOMMessage id=", parsed.message.id); + binder_->bind(parsed.message); +} + +} // namespace softbus::daemon diff --git a/src/softbus_daemon/src/main.cpp b/src/softbus_daemon/src/main.cpp new file mode 100644 index 0000000..57d6d0d --- /dev/null +++ b/src/softbus_daemon/src/main.cpp @@ -0,0 +1,30 @@ +#include + +#include +#include + +int main(int argc, char* argv[]) +{ + std::string profilePath = "config/daemon_profile.json"; + std::string modelPath = "config/softbus_model.json"; + bool selfTest = false; + + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--config" && i + 1 < argc) { + profilePath = argv[++i]; + } else if (arg == "--model" && i + 1 < argc) { + modelPath = argv[++i]; + } else if (arg == "--self-test") { + selfTest = true; + } + } + + softbus::daemon::CoreService service; + if (!service.start(profilePath, modelPath)) { + std::cerr << "Failed to start softbus_daemon" << std::endl; + return 1; + } + service.run(selfTest); + return 0; +} diff --git a/src/softbus_edge/CMakeLists.txt b/src/softbus_edge/CMakeLists.txt new file mode 100644 index 0000000..d4b8ac7 --- /dev/null +++ b/src/softbus_edge/CMakeLists.txt @@ -0,0 +1,14 @@ +softbus_package(softbus_edge) + +add_executable(softbus_edge + src/main.cpp + src/MockModbusIngress.cpp + src/UplinkClient.cpp + src/DeviceFactory.cpp + src/DeviceDiscovery.cpp +) + +softbus_export_include(softbus_edge "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(softbus_edge PRIVATE softbus_core softbus_transport softbus_api asio) +softbus_apply_platform_libs(softbus_edge) diff --git a/src/softbus_edge/include/softbus_edge/discovery/DeviceDiscovery.h b/src/softbus_edge/include/softbus_edge/discovery/DeviceDiscovery.h new file mode 100644 index 0000000..971cc88 --- /dev/null +++ b/src/softbus_edge/include/softbus_edge/discovery/DeviceDiscovery.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace softbus::edge { + +struct DiscoveredDevice { + std::string portName; + std::string protocol; +}; + +class DeviceDiscovery { +public: + std::vector discover(); +}; + +} // namespace softbus::edge diff --git a/src/softbus_edge/include/softbus_edge/egress/UplinkClient.h b/src/softbus_edge/include/softbus_edge/egress/UplinkClient.h new file mode 100644 index 0000000..47f6dc5 --- /dev/null +++ b/src/softbus_edge/include/softbus_edge/egress/UplinkClient.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace softbus::edge { + +class UplinkClient { +public: + UplinkClient(std::string host, uint16_t port); + ~UplinkClient(); + + bool connect(); + void disconnect(); + bool sendRawPacket(const softbus::core::RawPacket& packet); + void setCommandHandler(std::function handler); + void poll(); + +private: + std::unique_ptr client_; +}; + +} // namespace softbus::edge diff --git a/src/softbus_edge/include/softbus_edge/factory/DeviceFactory.h b/src/softbus_edge/include/softbus_edge/factory/DeviceFactory.h new file mode 100644 index 0000000..68a644e --- /dev/null +++ b/src/softbus_edge/include/softbus_edge/factory/DeviceFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace softbus::edge { + +class DeviceFactory { +public: + std::string createRuntimeDeviceId(); +}; + +} // namespace softbus::edge diff --git a/src/softbus_edge/include/softbus_edge/ingress/IIngressPort.h b/src/softbus_edge/include/softbus_edge/ingress/IIngressPort.h new file mode 100644 index 0000000..c73760f --- /dev/null +++ b/src/softbus_edge/include/softbus_edge/ingress/IIngressPort.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +namespace softbus::edge { + +class IIngressPort { +public: + virtual ~IIngressPort() = default; + virtual bool open() = 0; + virtual void close() = 0; + virtual std::vector poll() = 0; +}; + +} // namespace softbus::edge diff --git a/src/softbus_edge/include/softbus_edge/ingress/MockModbusIngress.h b/src/softbus_edge/include/softbus_edge/ingress/MockModbusIngress.h new file mode 100644 index 0000000..b5d7896 --- /dev/null +++ b/src/softbus_edge/include/softbus_edge/ingress/MockModbusIngress.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include + +namespace softbus::edge { + +class MockModbusIngress : public IIngressPort { +public: + explicit MockModbusIngress(std::string objectRef, std::string runtimeDeviceId); + + bool open() override; + void close() override; + std::vector poll() override; + +private: + std::vector buildFrame(uint16_t registerValue) const; + + std::string objectRef_; + std::string runtimeDeviceId_; + bool open_{false}; + uint16_t counter_{100}; +}; + +} // namespace softbus::edge diff --git a/src/softbus_edge/src/DeviceDiscovery.cpp b/src/softbus_edge/src/DeviceDiscovery.cpp new file mode 100644 index 0000000..65ec654 --- /dev/null +++ b/src/softbus_edge/src/DeviceDiscovery.cpp @@ -0,0 +1,10 @@ +#include + +namespace softbus::edge { + +std::vector DeviceDiscovery::discover() +{ + return {}; +} + +} // namespace softbus::edge diff --git a/src/softbus_edge/src/DeviceFactory.cpp b/src/softbus_edge/src/DeviceFactory.cpp new file mode 100644 index 0000000..f53e65c --- /dev/null +++ b/src/softbus_edge/src/DeviceFactory.cpp @@ -0,0 +1,12 @@ +#include + +#include + +namespace softbus::edge { + +std::string DeviceFactory::createRuntimeDeviceId() +{ + return softbus::core::DeviceIdentity::generateRuntimeDeviceId(); +} + +} // namespace softbus::edge diff --git a/src/softbus_edge/src/MockModbusIngress.cpp b/src/softbus_edge/src/MockModbusIngress.cpp new file mode 100644 index 0000000..8eca06a --- /dev/null +++ b/src/softbus_edge/src/MockModbusIngress.cpp @@ -0,0 +1,54 @@ +#include + +#include +#include + +namespace softbus::edge { + +MockModbusIngress::MockModbusIngress(std::string objectRef, std::string runtimeDeviceId) + : objectRef_(std::move(objectRef)) + , runtimeDeviceId_(std::move(runtimeDeviceId)) +{ +} + +bool MockModbusIngress::open() +{ + open_ = true; + return true; +} + +void MockModbusIngress::close() +{ + open_ = false; +} + +std::vector MockModbusIngress::buildFrame(uint16_t registerValue) const +{ + // slave=1, func=0x03, byteCount=2, value hi/lo, crc lo/hi (placeholder) + return { + 0x01, 0x03, 0x02, + static_cast((registerValue >> 8) & 0xFF), + static_cast(registerValue & 0xFF), + 0x00, 0x00 + }; +} + +std::vector MockModbusIngress::poll() +{ + if (!open_) { + return {}; + } + + const auto traceId = softbus::core::DeviceIdentity::generateTraceId(); + softbus::core::RawPacket packet; + packet.traceId = traceId; + packet.timestampNs = softbus::core::HwClock::nowNs(); + packet.runtimeDeviceId = runtimeDeviceId_; + packet.sourceId = objectRef_; + packet.protocolTag = "modbus"; + packet.payload = buildFrame(counter_); + counter_ = static_cast(counter_ + 1); + return {packet}; +} + +} // namespace softbus::edge diff --git a/src/softbus_edge/src/UplinkClient.cpp b/src/softbus_edge/src/UplinkClient.cpp new file mode 100644 index 0000000..a3a2d7b --- /dev/null +++ b/src/softbus_edge/src/UplinkClient.cpp @@ -0,0 +1,40 @@ +#include + +namespace softbus::edge { + +UplinkClient::UplinkClient(std::string host, uint16_t port) + : client_(std::make_unique(std::move(host), port)) +{ +} + +UplinkClient::~UplinkClient() +{ + disconnect(); +} + +bool UplinkClient::connect() +{ + return client_->connect(); +} + +void UplinkClient::disconnect() +{ + client_->disconnect(); +} + +bool UplinkClient::sendRawPacket(const softbus::core::RawPacket& packet) +{ + return client_->sendRawPacket(packet); +} + +void UplinkClient::setCommandHandler(std::function handler) +{ + client_->setCommandHandler(std::move(handler)); +} + +void UplinkClient::poll() +{ + client_->poll(); +} + +} // namespace softbus::edge diff --git a/src/softbus_edge/src/main.cpp b/src/softbus_edge/src/main.cpp new file mode 100644 index 0000000..e70dca9 --- /dev/null +++ b/src/softbus_edge/src/main.cpp @@ -0,0 +1,85 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace { + +struct EdgeOptions { + std::string daemonHost{"127.0.0.1"}; + uint16_t daemonPort{9000}; + std::string objectRef{"DemoSite/DemoSystem/DemoAsset/SimModbusDevice/Temperature"}; +}; + +EdgeOptions parseArgs(int argc, char* argv[]) +{ + EdgeOptions options; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--daemon" && i + 1 < argc) { + const std::string endpoint = argv[++i]; + const auto pos = endpoint.find(':'); + if (pos != std::string::npos) { + options.daemonHost = endpoint.substr(0, pos); + options.daemonPort = static_cast(std::stoi(endpoint.substr(pos + 1))); + } else { + options.daemonHost = endpoint; + } + } else if (arg == "--object-ref" && i + 1 < argc) { + options.objectRef = argv[++i]; + } + } + return options; +} + +} // namespace + +int main(int argc, char* argv[]) +{ + const EdgeOptions options = parseArgs(argc, argv); + + softbus::edge::DeviceFactory factory; + const std::string runtimeDeviceId = factory.createRuntimeDeviceId(); + softbus::core::Logger::instance().info("Edge", "runtimeDeviceId=", runtimeDeviceId, + " (local only, not persisted)"); + + softbus::edge::UplinkClient uplink(options.daemonHost, options.daemonPort); + uplink.setCommandHandler([](const softbus::api::ExecuteCommand& cmd) { + softbus::core::Logger::instance().infoTrace("Edge", cmd.traceId, + "executeCommand received: ", cmd.toJson().dump()); + }); + + if (!uplink.connect()) { + std::cerr << "Failed to connect to daemon at " + << options.daemonHost << ":" << options.daemonPort << std::endl; + return 1; + } + + softbus::edge::MockModbusIngress ingress(options.objectRef, runtimeDeviceId); + ingress.open(); + + softbus::core::PlatformSignal::install([]() { + softbus::core::Logger::instance().info("Edge", "Shutdown requested"); + }); + + while (!softbus::core::PlatformSignal::shouldStop()) { + uplink.poll(); + for (const auto& packet : ingress.poll()) { + softbus::core::Logger::instance().infoTrace("Edge", packet.traceId, + "Uplink RawPacket objectRef=", packet.sourceId); + uplink.sendRawPacket(packet); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + ingress.close(); + uplink.disconnect(); + return 0; +} diff --git a/src/softbus_opcua/CMakeLists.txt b/src/softbus_opcua/CMakeLists.txt new file mode 100644 index 0000000..38f89c7 --- /dev/null +++ b/src/softbus_opcua/CMakeLists.txt @@ -0,0 +1,15 @@ +softbus_package(softbus_opcua) + +add_library(softbus_opcua STATIC + src/UaServerEngine.cpp + src/UaAddressSpaceBuilder.cpp + src/UaModelBinder.cpp + src/UaMethodBinder.cpp + src/UaEventBridge.cpp +) + +softbus_export_include(softbus_opcua "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(softbus_opcua PUBLIC softbus_core softbus_api) +softbus_apply_open62541(softbus_opcua) +softbus_apply_platform_libs(softbus_opcua) diff --git a/src/softbus_opcua/include/softbus_opcua/UaAddressSpaceBuilder.h b/src/softbus_opcua/include/softbus_opcua/UaAddressSpaceBuilder.h new file mode 100644 index 0000000..77086ad --- /dev/null +++ b/src/softbus_opcua/include/softbus_opcua/UaAddressSpaceBuilder.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#if SOFTBUS_HAS_OPCUA +#include +#else +typedef struct UA_Server UA_Server; +#endif + +struct UA_Server; + +namespace softbus::opcua { + +class UaModelBinder; + +class UaAddressSpaceBuilder { +public: + explicit UaAddressSpaceBuilder(UA_Server* server); + + bool buildFromModel(const softbus::core::ObjectModelTree& tree, UaModelBinder* binder = nullptr); + +private: + UA_Server* server_{nullptr}; +}; + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/include/softbus_opcua/UaEventBridge.h b/src/softbus_opcua/include/softbus_opcua/UaEventBridge.h new file mode 100644 index 0000000..7133a9c --- /dev/null +++ b/src/softbus_opcua/include/softbus_opcua/UaEventBridge.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +struct UA_Server; + +namespace softbus::opcua { + +class UaEventBridge { +public: + explicit UaEventBridge(UA_Server* server); + + void emitHeartbeat(const std::string& objectRef); + void emitDeviceOnline(const std::string& stableDeviceKey); + void emitDeviceOffline(const std::string& stableDeviceKey); + void emitParseError(const std::string& objectRef, const std::string& detail); + +private: + UA_Server* server_{nullptr}; +}; + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/include/softbus_opcua/UaMethodBinder.h b/src/softbus_opcua/include/softbus_opcua/UaMethodBinder.h new file mode 100644 index 0000000..dc7daa9 --- /dev/null +++ b/src/softbus_opcua/include/softbus_opcua/UaMethodBinder.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include +#include + +struct UA_Server; + +namespace softbus::opcua { + +using ExecuteCommandHandler = std::function; + +class UaMethodBinder { +public: + explicit UaMethodBinder(UA_Server* server); + + bool bindExecuteCommand(ExecuteCommandHandler handler); + bool dispatchCommand(const softbus::api::ExecuteCommand& command); + +private: + UA_Server* server_{nullptr}; + ExecuteCommandHandler handler_; +}; + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/include/softbus_opcua/UaModelBinder.h b/src/softbus_opcua/include/softbus_opcua/UaModelBinder.h new file mode 100644 index 0000000..e7f712a --- /dev/null +++ b/src/softbus_opcua/include/softbus_opcua/UaModelBinder.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include +#include + +#if SOFTBUS_HAS_OPCUA +#include +#else +typedef struct UA_NodeId UA_NodeId; +#endif + +namespace softbus::opcua { + +class UaModelBinder { +public: + explicit UaModelBinder(UA_Server* server); + + void registerPointNode(const std::string& objectRef, const UA_NodeId& nodeId); + bool bind(const softbus::core::DOMMessage& message); + +private: + UA_Server* server_{nullptr}; + std::map pointNodes_; +}; + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/include/softbus_opcua/UaServerEngine.h b/src/softbus_opcua/include/softbus_opcua/UaServerEngine.h new file mode 100644 index 0000000..820423a --- /dev/null +++ b/src/softbus_opcua/include/softbus_opcua/UaServerEngine.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include + +struct UA_Server; + +namespace softbus::opcua { + +struct SecurityConfig { + bool enableTls{false}; + std::string certPath; + std::string keyPath; + std::string trustListPath; +}; + +class UaServerEngine { +public: + UaServerEngine(); + ~UaServerEngine(); + + UaServerEngine(const UaServerEngine&) = delete; + UaServerEngine& operator=(const UaServerEngine&) = delete; + + bool start(uint16_t port, const std::string& applicationName, const SecurityConfig& security); + void stop(); + void iterate(); + bool isRunning() const { return running_; } + + UA_Server* nativeServer() { return server_; } + +private: + UA_Server* server_{nullptr}; + bool running_{false}; + SecurityConfig security_; +}; + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/src/UaAddressSpaceBuilder.cpp b/src/softbus_opcua/src/UaAddressSpaceBuilder.cpp new file mode 100644 index 0000000..d16f2bf --- /dev/null +++ b/src/softbus_opcua/src/UaAddressSpaceBuilder.cpp @@ -0,0 +1,119 @@ +#include + +#include +#include +#include + +#if SOFTBUS_HAS_OPCUA +#include +#endif + +namespace softbus::opcua { + +UaAddressSpaceBuilder::UaAddressSpaceBuilder(UA_Server* server) + : server_(server) +{ +} + +#if SOFTBUS_HAS_OPCUA +namespace { + +UA_NodeId makeStringNodeId(const std::string& nsAndId) +{ + return UA_NODEID_STRING_ALLOC(1, nsAndId.c_str()); +} + +bool addObject(UA_Server* server, const UA_NodeId& parent, const std::string& browseName, + UA_NodeId* outNodeId) +{ + UA_ObjectAttributes attr = UA_ObjectAttributes_default; + attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", browseName.c_str()); + const UA_NodeId nodeId = makeStringNodeId(browseName); + const UA_StatusCode status = UA_Server_addObjectNode( + server, + nodeId, + parent, + UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), + UA_QUALIFIEDNAME_ALLOC(1, browseName.c_str()), + UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), + attr, + nullptr, + outNodeId); + UA_ObjectAttributes_clear(&attr); + return status == UA_STATUSCODE_GOOD; +} + +bool addVariable(UA_Server* server, const UA_NodeId& parent, const std::string& browseName, + const std::string& nodeKey, UA_NodeId* outNodeId) +{ + UA_VariableAttributes attr = UA_VariableAttributes_default; + attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", browseName.c_str()); + attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId; + UA_Double initial = 0.0; + UA_Variant_setScalarCopy(&attr.value, &initial, &UA_TYPES[UA_TYPES_DOUBLE]); + attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE; + + const UA_NodeId nodeId = makeStringNodeId(nodeKey); + const UA_StatusCode status = UA_Server_addVariableNode( + server, + nodeId, + parent, + UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), + UA_QUALIFIEDNAME_ALLOC(1, browseName.c_str()), + UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), + attr, + nullptr, + outNodeId); + UA_VariableAttributes_clear(&attr); + return status == UA_STATUSCODE_GOOD; +} + +} // namespace +#endif + +bool UaAddressSpaceBuilder::buildFromModel(const softbus::core::ObjectModelTree& tree, UaModelBinder* binder) +{ +#if !SOFTBUS_HAS_OPCUA + (void)tree; + return false; +#else + if (!server_) { + return false; + } + + UA_NodeId siteNode; + if (!addObject(server_, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), tree.site, &siteNode)) { + return false; + } + + UA_NodeId systemNode; + if (!addObject(server_, siteNode, tree.system, &systemNode)) { + return false; + } + + UA_NodeId assetNode; + if (!addObject(server_, systemNode, tree.asset, &assetNode)) { + return false; + } + + for (const auto& device : tree.devices) { + UA_NodeId deviceNode; + if (!addObject(server_, assetNode, device.stableDeviceKey, &deviceNode)) { + continue; + } + for (const auto& point : device.points) { + const auto objectRef = softbus::core::DeviceIdentity::buildObjectRef( + tree, device.stableDeviceKey, point.name); + UA_NodeId pointNode; + if (addVariable(server_, deviceNode, point.name, objectRef.toPath(), &pointNode) && binder) { + binder->registerPointNode(objectRef.toPath(), pointNode); + } + } + } + + softbus::core::Logger::instance().info("UaAddressSpaceBuilder", "Address space built for ", tree.site); + return true; +#endif +} + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/src/UaEventBridge.cpp b/src/softbus_opcua/src/UaEventBridge.cpp new file mode 100644 index 0000000..306503e --- /dev/null +++ b/src/softbus_opcua/src/UaEventBridge.cpp @@ -0,0 +1,36 @@ +#include + +#include + +namespace softbus::opcua { + +UaEventBridge::UaEventBridge(UA_Server* server) + : server_(server) +{ +} + +void UaEventBridge::emitHeartbeat(const std::string& objectRef) +{ + softbus::core::Logger::instance().debug("UaEventBridge", "Heartbeat stub for ", objectRef); + (void)server_; +} + +void UaEventBridge::emitDeviceOnline(const std::string& stableDeviceKey) +{ + softbus::core::Logger::instance().info("UaEventBridge", "Device online stub: ", stableDeviceKey); + (void)server_; +} + +void UaEventBridge::emitDeviceOffline(const std::string& stableDeviceKey) +{ + softbus::core::Logger::instance().info("UaEventBridge", "Device offline stub: ", stableDeviceKey); + (void)server_; +} + +void UaEventBridge::emitParseError(const std::string& objectRef, const std::string& detail) +{ + softbus::core::Logger::instance().warn("UaEventBridge", "Parse error at ", objectRef, ": ", detail); + (void)server_; +} + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/src/UaMethodBinder.cpp b/src/softbus_opcua/src/UaMethodBinder.cpp new file mode 100644 index 0000000..3ddc7ed --- /dev/null +++ b/src/softbus_opcua/src/UaMethodBinder.cpp @@ -0,0 +1,157 @@ +#include + +#include + +#include + +#if SOFTBUS_HAS_OPCUA +#include +#endif + +namespace softbus::opcua { + +#if SOFTBUS_HAS_OPCUA +namespace { +UaMethodBinder* g_methodBinder = nullptr; + +UA_StatusCode executeCommandMethod(UA_Server* server, + const UA_NodeId* sessionId, + void* sessionContext, + const UA_NodeId* methodId, + void* methodContext, + const UA_NodeId* objectId, + void* objectContext, + size_t inputSize, + const UA_Variant* input, + size_t outputSize, + UA_Variant* output) +{ + (void)server; + (void)sessionId; + (void)sessionContext; + (void)methodId; + (void)methodContext; + (void)objectId; + (void)objectContext; + (void)outputSize; + + if (!g_methodBinder || inputSize < 1) { + return UA_STATUSCODE_BADINVALIDARGUMENT; + } + + const UA_String* jsonInput = static_cast(input[0].data); + if (!jsonInput) { + return UA_STATUSCODE_BADTYPEMISMATCH; + } + + const std::string jsonStr(reinterpret_cast(jsonInput->data), jsonInput->length); + const auto j = nlohmann::json::parse(jsonStr, nullptr, false); + if (j.is_discarded()) { + return UA_STATUSCODE_BADINVALIDARGUMENT; + } + + softbus::api::ExecuteCommand cmd = softbus::api::ExecuteCommand::fromJson(j); + std::string error; + if (!cmd.validate(&error)) { + const std::string result = std::string(R"({"success":false,"error":")") + error + "\"}"; + UA_String out = UA_STRING_ALLOC(result.c_str()); + UA_Variant_setScalarCopy(output, &out, &UA_TYPES[UA_TYPES_STRING]); + UA_String_clear(&out); + return UA_STATUSCODE_GOOD; + } + + const bool ok = g_methodBinder->dispatchCommand(cmd); + const std::string result = ok ? R"({"success":true})" : R"({"success":false})"; + UA_String out = UA_STRING_ALLOC(result.c_str()); + UA_Variant_setScalarCopy(output, &out, &UA_TYPES[UA_TYPES_STRING]); + UA_String_clear(&out); + return UA_STATUSCODE_GOOD; +} +} // namespace +#endif + +UaMethodBinder::UaMethodBinder(UA_Server* server) + : server_(server) +{ +} + +bool UaMethodBinder::dispatchCommand(const softbus::api::ExecuteCommand& command) +{ + if (!handler_) { + return false; + } + softbus::core::Logger::instance().infoTrace("UaMethodBinder", command.traceId, + "ExecuteCommand invoked via OPC UA method"); + return handler_(command); +} + +bool UaMethodBinder::bindExecuteCommand(ExecuteCommandHandler handler) +{ +#if !SOFTBUS_HAS_OPCUA + (void)handler; + return false; +#else + handler_ = std::move(handler); + g_methodBinder = this; + + UA_ObjectAttributes objAttr = UA_ObjectAttributes_default; + objAttr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", "SoftBusControl"); + UA_NodeId controlObjectId = UA_NODEID_STRING_ALLOC(1, "SoftBusControl"); + UA_NodeId createdObjectId; + UA_StatusCode status = UA_Server_addObjectNode( + server_, + controlObjectId, + UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), + UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), + UA_QUALIFIEDNAME_ALLOC(1, "SoftBusControl"), + UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), + objAttr, + nullptr, + &createdObjectId); + UA_ObjectAttributes_clear(&objAttr); + if (status != UA_STATUSCODE_GOOD) { + return false; + } + + UA_Argument inputArgument; + UA_Argument_init(&inputArgument); + inputArgument.description = UA_LOCALIZEDTEXT_ALLOC("en-US", "Command JSON"); + inputArgument.name = UA_STRING_ALLOC("commandJson"); + inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId; + inputArgument.valueRank = UA_VALUERANK_SCALAR; + + UA_Argument outputArgument; + UA_Argument_init(&outputArgument); + outputArgument.description = UA_LOCALIZEDTEXT_ALLOC("en-US", "Result JSON"); + outputArgument.name = UA_STRING_ALLOC("resultJson"); + outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId; + outputArgument.valueRank = UA_VALUERANK_SCALAR; + + UA_MethodAttributes methodAttr = UA_MethodAttributes_default; + methodAttr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", "ExecuteCommand"); + methodAttr.executable = true; + methodAttr.userExecutable = true; + + status = UA_Server_addMethodNode( + server_, + UA_NODEID_STRING_ALLOC(1, "ExecuteCommand"), + createdObjectId, + UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), + UA_QUALIFIEDNAME_ALLOC(1, "ExecuteCommand"), + methodAttr, + &executeCommandMethod, + 1, + &inputArgument, + 1, + &outputArgument, + nullptr, + nullptr); + + UA_Argument_clear(&inputArgument); + UA_Argument_clear(&outputArgument); + UA_MethodAttributes_clear(&methodAttr); + return status == UA_STATUSCODE_GOOD; +#endif +} + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/src/UaModelBinder.cpp b/src/softbus_opcua/src/UaModelBinder.cpp new file mode 100644 index 0000000..fd6cbde --- /dev/null +++ b/src/softbus_opcua/src/UaModelBinder.cpp @@ -0,0 +1,71 @@ +#include + +#include + +#if SOFTBUS_HAS_OPCUA +#include +#endif + +namespace softbus::opcua { + +UaModelBinder::UaModelBinder(UA_Server* server) + : server_(server) +{ +} + +void UaModelBinder::registerPointNode(const std::string& objectRef, const UA_NodeId& nodeId) +{ +#if SOFTBUS_HAS_OPCUA + UA_NodeId copy{}; + UA_NodeId_copy(&nodeId, ©); + pointNodes_[objectRef] = copy; +#else + (void)objectRef; + (void)nodeId; +#endif +} + +bool UaModelBinder::bind(const softbus::core::DOMMessage& message) +{ +#if !SOFTBUS_HAS_OPCUA + (void)message; + return false; +#else + if (!server_) { + return false; + } + + const std::string key = message.id.empty() ? message.sourceId : message.id; + const auto it = pointNodes_.find(key); + if (it == pointNodes_.end()) { + softbus::core::Logger::instance().warnTrace("UaModelBinder", message.traceId, + "No OPC UA node registered for ", key); + return false; + } + + double numericValue = 0.0; + if (message.value.contains("value")) { + numericValue = message.value["value"].get(); + } else if (message.value.contains("registerValue")) { + numericValue = message.value["registerValue"].get(); + } + + UA_Variant variant; + UA_Variant_init(&variant); + UA_Variant_setScalarCopy(&variant, &numericValue, &UA_TYPES[UA_TYPES_DOUBLE]); + const UA_StatusCode status = UA_Server_writeValue(server_, it->second, variant); + UA_Variant_clear(&variant); + + if (status == UA_STATUSCODE_GOOD) { + softbus::core::Logger::instance().infoTrace("UaModelBinder", message.traceId, + "Bound DOMMessage to OPC UA node ", key, " value=", numericValue); + return true; + } + + softbus::core::Logger::instance().errorTrace("UaModelBinder", message.traceId, + "Failed to write OPC UA node ", key); + return false; +#endif +} + +} // namespace softbus::opcua diff --git a/src/softbus_opcua/src/UaServerEngine.cpp b/src/softbus_opcua/src/UaServerEngine.cpp new file mode 100644 index 0000000..14f7373 --- /dev/null +++ b/src/softbus_opcua/src/UaServerEngine.cpp @@ -0,0 +1,80 @@ +#include + +#include + +#if SOFTBUS_HAS_OPCUA +#include +#include +#endif + +namespace softbus::opcua { + +UaServerEngine::UaServerEngine() = default; + +UaServerEngine::~UaServerEngine() +{ + stop(); +} + +bool UaServerEngine::start(uint16_t port, const std::string& applicationName, const SecurityConfig& security) +{ +#if !SOFTBUS_HAS_OPCUA + (void)port; + (void)applicationName; + (void)security; + softbus::core::Logger::instance().error("UaServerEngine", "OPC UA support not built in"); + return false; +#else + security_ = security; + if (security.enableTls) { + softbus::core::Logger::instance().warn("UaServerEngine", + "TLS/mTLS requested but not enabled in Phase1 build"); + } + + server_ = UA_Server_new(); + UA_ServerConfig* config = UA_Server_getConfig(server_); + UA_StatusCode status = UA_ServerConfig_setMinimal(config, port, nullptr); + if (status != UA_STATUSCODE_GOOD) { + softbus::core::Logger::instance().error("UaServerEngine", "Failed to configure server"); + UA_Server_delete(server_); + server_ = nullptr; + return false; + } + + config->applicationDescription.applicationName = UA_LOCALIZEDTEXT_ALLOC("en-US", applicationName.c_str()); + status = UA_Server_run_startup(server_); + if (status != UA_STATUSCODE_GOOD) { + softbus::core::Logger::instance().error("UaServerEngine", "Failed to start server"); + UA_Server_delete(server_); + server_ = nullptr; + return false; + } + + running_ = true; + softbus::core::Logger::instance().info("UaServerEngine", "OPC UA server started on port ", port); + return true; +#endif +} + +void UaServerEngine::stop() +{ +#if SOFTBUS_HAS_OPCUA + if (server_) { + UA_Server_run_shutdown(server_); + UA_Server_delete(server_); + server_ = nullptr; + } +#endif + running_ = false; +} + +void UaServerEngine::iterate() +{ +#if SOFTBUS_HAS_OPCUA + if (server_ && running_) { + UA_Server_run_iterate(server_, false); + } +#endif +} + +} // namespace softbus::opcua diff --git a/src/softbus_transport/CMakeLists.txt b/src/softbus_transport/CMakeLists.txt new file mode 100644 index 0000000..04bdbb2 --- /dev/null +++ b/src/softbus_transport/CMakeLists.txt @@ -0,0 +1,12 @@ +softbus_package(softbus_transport) + +add_library(softbus_transport STATIC + src/UplinkWire.cpp + src/TcpUplinkServer.cpp + src/TcpUplinkClient.cpp +) + +softbus_export_include(softbus_transport "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_link_libraries(softbus_transport PUBLIC softbus_core softbus_api asio) +softbus_apply_platform_libs(softbus_transport) diff --git a/src/softbus_transport/include/softbus_transport/TcpUplinkClient.h b/src/softbus_transport/include/softbus_transport/TcpUplinkClient.h new file mode 100644 index 0000000..117337f --- /dev/null +++ b/src/softbus_transport/include/softbus_transport/TcpUplinkClient.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +namespace softbus::transport { + +using CommandHandler = std::function; + +class TcpUplinkClient { +public: + TcpUplinkClient(std::string host, uint16_t port); + ~TcpUplinkClient(); + + void setCommandHandler(CommandHandler handler); + bool connect(); + void disconnect(); + bool sendRawPacket(const softbus::core::RawPacket& packet); + void poll(); + + bool isConnected() const { return connected_; } + +private: + void doRead(); + void handleLine(const std::string& line); + + std::string host_; + uint16_t port_; + asio::io_context io_; + std::unique_ptr socket_; + std::unique_ptr> workGuard_; + std::thread ioThread_; + CommandHandler commandHandler_; + std::string readBuffer_; + std::mutex sendMutex_; + bool connected_{false}; +}; + +} // namespace softbus::transport diff --git a/src/softbus_transport/include/softbus_transport/TcpUplinkServer.h b/src/softbus_transport/include/softbus_transport/TcpUplinkServer.h new file mode 100644 index 0000000..d60653e --- /dev/null +++ b/src/softbus_transport/include/softbus_transport/TcpUplinkServer.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace softbus::transport { + +using RawPacketHandler = std::function; +using CommandSender = std::function; + +class TcpUplinkServer { +public: + TcpUplinkServer(asio::io_context& io, std::string host, uint16_t port); + ~TcpUplinkServer(); + + void setRawPacketHandler(RawPacketHandler handler); + void start(); + void stop(); + bool sendCommand(const softbus::api::ExecuteCommand& command); + +private: + void doAccept(); + void doRead(std::shared_ptr socket); + void handleLine(const std::string& line); + + asio::io_context& io_; + asio::ip::tcp::acceptor acceptor_; + RawPacketHandler rawHandler_; + std::shared_ptr clientSocket_; + std::string readBuffer_; + std::mutex sendMutex_; + bool running_{false}; +}; + +} // namespace softbus::transport diff --git a/src/softbus_transport/include/softbus_transport/UplinkWire.h b/src/softbus_transport/include/softbus_transport/UplinkWire.h new file mode 100644 index 0000000..fb06b0d --- /dev/null +++ b/src/softbus_transport/include/softbus_transport/UplinkWire.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace softbus::transport { + +enum class WireMessageType { + RawPacket, + ExecuteCommand, + Unknown +}; + +struct WireMessage { + WireMessageType type{WireMessageType::Unknown}; + softbus::core::RawPacket rawPacket; + softbus::api::ExecuteCommand command; +}; + +std::string encodeWireMessage(const softbus::core::RawPacket& packet); +std::string encodeWireMessage(const softbus::api::ExecuteCommand& command); +std::optional decodeWireMessage(const std::string& line); + +std::string base64Encode(const uint8_t* data, std::size_t size); +std::vector base64Decode(const std::string& encoded); + +} // namespace softbus::transport diff --git a/src/softbus_transport/src/TcpUplinkClient.cpp b/src/softbus_transport/src/TcpUplinkClient.cpp new file mode 100644 index 0000000..c990521 --- /dev/null +++ b/src/softbus_transport/src/TcpUplinkClient.cpp @@ -0,0 +1,120 @@ +#include + +#include + +#include + +namespace softbus::transport { + +TcpUplinkClient::TcpUplinkClient(std::string host, uint16_t port) + : host_(std::move(host)) + , port_(port) + , workGuard_(std::make_unique>( + asio::make_work_guard(io_))) +{ +} + +TcpUplinkClient::~TcpUplinkClient() +{ + disconnect(); +} + +void TcpUplinkClient::setCommandHandler(CommandHandler handler) +{ + commandHandler_ = std::move(handler); +} + +bool TcpUplinkClient::connect() +{ + if (connected_) { + return true; + } + socket_ = std::make_unique(io_); + std::error_code ec; + socket_->connect(asio::ip::tcp::endpoint(asio::ip::make_address(host_), port_), ec); + if (ec) { + softbus::core::Logger::instance().error("TcpUplinkClient", "Connect failed: ", ec.message()); + socket_.reset(); + return false; + } + connected_ = true; + ioThread_ = std::thread([this]() { io_.run(); }); + doRead(); + softbus::core::Logger::instance().info("TcpUplinkClient", "Connected to ", host_, ":", port_); + return true; +} + +void TcpUplinkClient::disconnect() +{ + connected_ = false; + if (socket_) { + std::error_code ec; + socket_->close(ec); + socket_.reset(); + } + workGuard_.reset(); + io_.stop(); + if (ioThread_.joinable()) { + ioThread_.join(); + } + io_.restart(); + workGuard_ = std::make_unique>( + asio::make_work_guard(io_)); +} + +bool TcpUplinkClient::sendRawPacket(const softbus::core::RawPacket& packet) +{ + if (!socket_ || !socket_->is_open()) { + return false; + } + const auto line = encodeWireMessage(packet); + std::lock_guard lock(sendMutex_); + std::error_code ec; + asio::write(*socket_, asio::buffer(line), ec); + return !ec; +} + +void TcpUplinkClient::poll() +{ + io_.poll(); +} + +void TcpUplinkClient::doRead() +{ + if (!socket_ || !socket_->is_open()) { + return; + } + auto buffer = std::make_shared>(); + socket_->async_read_some(asio::buffer(*buffer), + [this, buffer](const std::error_code& ec, std::size_t bytes) { + if (ec) { + connected_ = false; + softbus::core::Logger::instance().warn("TcpUplinkClient", "Disconnected from daemon"); + return; + } + readBuffer_.append(buffer->data(), bytes); + for (;;) { + const auto pos = readBuffer_.find('\n'); + if (pos == std::string::npos) { + break; + } + const std::string line = readBuffer_.substr(0, pos); + readBuffer_.erase(0, pos + 1); + handleLine(line); + } + doRead(); + }); +} + +void TcpUplinkClient::handleLine(const std::string& line) +{ + const auto msg = decodeWireMessage(line); + if (!msg) { + return; + } + if (msg->type == WireMessageType::ExecuteCommand && commandHandler_) { + commandHandler_(msg->command); + } +} + +} // namespace softbus::transport diff --git a/src/softbus_transport/src/TcpUplinkServer.cpp b/src/softbus_transport/src/TcpUplinkServer.cpp new file mode 100644 index 0000000..c8f1f43 --- /dev/null +++ b/src/softbus_transport/src/TcpUplinkServer.cpp @@ -0,0 +1,115 @@ +#include + +#include + +#include + +namespace softbus::transport { + +TcpUplinkServer::TcpUplinkServer(asio::io_context& io, std::string host, uint16_t port) + : io_(io) + , acceptor_(io, asio::ip::tcp::endpoint(asio::ip::make_address(host), port)) +{ +} + +TcpUplinkServer::~TcpUplinkServer() +{ + stop(); +} + +void TcpUplinkServer::setRawPacketHandler(RawPacketHandler handler) +{ + rawHandler_ = std::move(handler); +} + +void TcpUplinkServer::start() +{ + running_ = true; + doAccept(); + softbus::core::Logger::instance().info("TcpUplinkServer", "Listening on ", + acceptor_.local_endpoint().address().to_string(), ":", + acceptor_.local_endpoint().port()); +} + +void TcpUplinkServer::stop() +{ + running_ = false; + std::error_code ec; + acceptor_.close(ec); + if (clientSocket_) { + clientSocket_->close(ec); + clientSocket_.reset(); + } +} + +bool TcpUplinkServer::sendCommand(const softbus::api::ExecuteCommand& command) +{ + if (!clientSocket_ || !clientSocket_->is_open()) { + return false; + } + const auto line = encodeWireMessage(command); + std::lock_guard lock(sendMutex_); + std::error_code ec; + asio::write(*clientSocket_, asio::buffer(line), ec); + return !ec; +} + +void TcpUplinkServer::doAccept() +{ + if (!running_) { + return; + } + auto socket = std::make_shared(io_); + acceptor_.async_accept(*socket, [this, socket](const std::error_code& ec) { + if (ec) { + if (running_) { + doAccept(); + } + return; + } + clientSocket_ = socket; + readBuffer_.clear(); + softbus::core::Logger::instance().info("TcpUplinkServer", "Edge connected"); + doRead(socket); + doAccept(); + }); +} + +void TcpUplinkServer::doRead(const std::shared_ptr socket) +{ + auto buffer = std::make_shared>(); + socket->async_read_some(asio::buffer(*buffer), + [this, socket, buffer](const std::error_code& ec, std::size_t bytes) { + if (ec) { + softbus::core::Logger::instance().warn("TcpUplinkServer", "Edge disconnected"); + if (clientSocket_ == socket) { + clientSocket_.reset(); + } + return; + } + readBuffer_.append(buffer->data(), bytes); + for (;;) { + const auto pos = readBuffer_.find('\n'); + if (pos == std::string::npos) { + break; + } + const std::string line = readBuffer_.substr(0, pos); + readBuffer_.erase(0, pos + 1); + handleLine(line); + } + doRead(socket); + }); +} + +void TcpUplinkServer::handleLine(const std::string& line) +{ + const auto msg = decodeWireMessage(line); + if (!msg) { + return; + } + if (msg->type == WireMessageType::RawPacket && rawHandler_) { + rawHandler_(msg->rawPacket); + } +} + +} // namespace softbus::transport diff --git a/src/softbus_transport/src/UplinkWire.cpp b/src/softbus_transport/src/UplinkWire.cpp new file mode 100644 index 0000000..20482e1 --- /dev/null +++ b/src/softbus_transport/src/UplinkWire.cpp @@ -0,0 +1,110 @@ +#include + +#include + +namespace softbus::transport { + +namespace { + +static const char kBase64Table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +} // namespace + +std::string base64Encode(const uint8_t* data, std::size_t size) +{ + std::string out; + out.reserve(((size + 2) / 3) * 4); + for (std::size_t i = 0; i < size; i += 3) { + const uint32_t block = (static_cast(data[i]) << 16) + | ((i + 1 < size ? data[i + 1] : 0) << 8) + | (i + 2 < size ? data[i + 2] : 0); + out.push_back(kBase64Table[(block >> 18) & 0x3F]); + out.push_back(kBase64Table[(block >> 12) & 0x3F]); + out.push_back(i + 1 < size ? kBase64Table[(block >> 6) & 0x3F] : '='); + out.push_back(i + 2 < size ? kBase64Table[block & 0x3F] : '='); + } + return out; +} + +std::vector base64Decode(const std::string& encoded) +{ + auto val = [](char c) -> int { + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 'a' + 26; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '+') return 62; + if (c == '/') return 63; + return -1; + }; + + std::vector out; + int buffer = 0; + int bits = 0; + for (char c : encoded) { + if (c == '=' || c == '\n' || c == '\r') { + continue; + } + const int v = val(c); + if (v < 0) { + continue; + } + buffer = (buffer << 6) | v; + bits += 6; + if (bits >= 8) { + bits -= 8; + out.push_back(static_cast((buffer >> bits) & 0xFF)); + } + } + return out; +} + +std::string encodeWireMessage(const softbus::core::RawPacket& packet) +{ + nlohmann::json j{ + {"type", "rawPacket"}, + {"traceId", packet.traceId}, + {"timestampNs", packet.timestampNs}, + {"runtimeDeviceId", packet.runtimeDeviceId}, + {"sourceId", packet.sourceId}, + {"protocolTag", packet.protocolTag}, + {"payloadBase64", base64Encode(packet.payload.data(), packet.payload.size())} + }; + return j.dump() + "\n"; +} + +std::string encodeWireMessage(const softbus::api::ExecuteCommand& command) +{ + return command.toJson().dump() + "\n"; +} + +std::optional decodeWireMessage(const std::string& line) +{ + if (line.empty()) { + return std::nullopt; + } + const auto j = nlohmann::json::parse(line, nullptr, false); + if (j.is_discarded()) { + return std::nullopt; + } + const std::string type = j.value("type", ""); + WireMessage msg; + if (type == "rawPacket") { + msg.type = WireMessageType::RawPacket; + msg.rawPacket.traceId = j.value("traceId", ""); + msg.rawPacket.timestampNs = j.value("timestampNs", int64_t{0}); + msg.rawPacket.runtimeDeviceId = j.value("runtimeDeviceId", ""); + msg.rawPacket.sourceId = j.value("sourceId", ""); + msg.rawPacket.protocolTag = j.value("protocolTag", ""); + msg.rawPacket.payload = base64Decode(j.value("payloadBase64", "")); + return msg; + } + if (type == "executeCommand") { + msg.type = WireMessageType::ExecuteCommand; + msg.command = softbus::api::ExecuteCommand::fromJson(j); + return msg; + } + return std::nullopt; +} + +} // namespace softbus::transport