first commit
This commit is contained in:
105
.gitignore
vendored
Normal file
105
.gitignore
vendored
Normal file
@@ -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
|
||||
28
CMakeLists.txt
Normal file
28
CMakeLists.txt
Normal file
@@ -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}")
|
||||
23
cmake/SoftbusPackage.cmake
Normal file
23
cmake/SoftbusPackage.cmake
Normal file
@@ -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
|
||||
$<BUILD_INTERFACE:${include_dir}>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
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()
|
||||
106
cmake/SoftbusPlatform.cmake
Normal file
106
cmake/SoftbusPlatform.cmake
Normal file
@@ -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()
|
||||
79
cmake/SoftbusThirdParty.cmake
Normal file
79
cmake/SoftbusThirdParty.cmake
Normal file
@@ -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()
|
||||
19
config/daemon_profile.json
Normal file
19
config/daemon_profile.json
Normal file
@@ -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": ""
|
||||
}
|
||||
}
|
||||
18
config/softbus_model.json
Normal file
18
config/softbus_model.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"site": "DemoSite",
|
||||
"system": "DemoSystem",
|
||||
"asset": "DemoAsset",
|
||||
"devices": [
|
||||
{
|
||||
"stableDeviceKey": "SimModbusDevice",
|
||||
"protocol": "modbus",
|
||||
"points": [
|
||||
{
|
||||
"name": "Temperature",
|
||||
"register": 40001,
|
||||
"domain": "Process"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
11
config/ua_model.xml
Normal file
11
config/ua_model.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Phase1 placeholder UA model file.
|
||||
The daemon builds the address space dynamically from config/softbus_model.json.
|
||||
Phase2 may import a static NodeSet2 XML generated from this template.
|
||||
-->
|
||||
<UANodeSet xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
|
||||
<Models>
|
||||
<Model ModelUri="urn:softbus:demo" Version="1.0.0"/>
|
||||
</Models>
|
||||
</UANodeSet>
|
||||
11
src/plugins/protocol_canopen/CMakeLists.txt
Normal file
11
src/plugins/protocol_canopen/CMakeLists.txt
Normal file
@@ -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)
|
||||
13
src/plugins/protocol_canopen/include/canopen/CanopenFramer.h
Normal file
13
src/plugins/protocol_canopen/include/canopen/CanopenFramer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/plugin/IFramer.h>
|
||||
|
||||
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
|
||||
15
src/plugins/protocol_canopen/include/canopen/CanopenMapper.h
Normal file
15
src/plugins/protocol_canopen/include/canopen/CanopenMapper.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/DOMMessage.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace canopen {
|
||||
|
||||
class CanopenMapper {
|
||||
public:
|
||||
bool mapToDom(uint16_t index, uint8_t subIndex, softbus::core::DOMMessage* message);
|
||||
};
|
||||
|
||||
} // namespace canopen
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
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<uint8_t, std::string> states_;
|
||||
};
|
||||
|
||||
} // namespace canopen
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/DOMMessage.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
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<uint32_t, Entry> entries_;
|
||||
};
|
||||
|
||||
} // namespace canopen
|
||||
14
src/plugins/protocol_canopen/include/canopen/CanopenParser.h
Normal file
14
src/plugins/protocol_canopen/include/canopen/CanopenParser.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/plugin/IParser.h>
|
||||
|
||||
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
|
||||
14
src/plugins/protocol_canopen/include/canopen/EdsDcfLoader.h
Normal file
14
src/plugins/protocol_canopen/include/canopen/EdsDcfLoader.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <canopen/CanopenObjectDictionary.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace canopen {
|
||||
|
||||
class EdsDcfLoader {
|
||||
public:
|
||||
bool load(const std::string& path, CanopenObjectDictionary* dictionary);
|
||||
};
|
||||
|
||||
} // namespace canopen
|
||||
57
src/plugins/protocol_canopen/src/canopen_plugin.cpp
Normal file
57
src/plugins/protocol_canopen/src/canopen_plugin.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <canopen/CanopenFramer.h>
|
||||
#include <canopen/CanopenParser.h>
|
||||
#include <softbus_core/plugin/IParser.h>
|
||||
#include <canopen/CanopenObjectDictionary.h>
|
||||
#include <canopen/EdsDcfLoader.h>
|
||||
#include <canopen/CanopenNodeManager.h>
|
||||
#include <canopen/CanopenMapper.h>
|
||||
|
||||
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;
|
||||
}
|
||||
13
src/plugins/protocol_modbus/CMakeLists.txt
Normal file
13
src/plugins/protocol_modbus/CMakeLists.txt
Normal file
@@ -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)
|
||||
13
src/plugins/protocol_modbus/include/modbus/ModbusFramer.h
Normal file
13
src/plugins/protocol_modbus/include/modbus/ModbusFramer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/plugin/IFramer.h>
|
||||
|
||||
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
|
||||
21
src/plugins/protocol_modbus/include/modbus/ModbusParser.h
Normal file
21
src/plugins/protocol_modbus/include/modbus/ModbusParser.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/plugin/IParser.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
||||
25
src/plugins/protocol_modbus/src/ModbusFramer.cpp
Normal file
25
src/plugins/protocol_modbus/src/ModbusFramer.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <modbus/ModbusFramer.h>
|
||||
|
||||
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
|
||||
41
src/plugins/protocol_modbus/src/ModbusParser.cpp
Normal file
41
src/plugins/protocol_modbus/src/ModbusParser.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <modbus/ModbusParser.h>
|
||||
|
||||
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<uint16_t>(data[3]) << 8) | static_cast<uint16_t>(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<double>(registerValue) * 0.1}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace modbus
|
||||
22
src/plugins/protocol_modbus/src/modbus_plugin.cpp
Normal file
22
src/plugins/protocol_modbus/src/modbus_plugin.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <modbus/ModbusFramer.h>
|
||||
#include <modbus/ModbusParser.h>
|
||||
|
||||
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;
|
||||
}
|
||||
9
src/softbus_api/CMakeLists.txt
Normal file
9
src/softbus_api/CMakeLists.txt
Normal file
@@ -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)
|
||||
19
src/softbus_api/include/softbus_api/ExecuteCommand.h
Normal file
19
src/softbus_api/include/softbus_api/ExecuteCommand.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
51
src/softbus_api/src/ExecuteCommand.cpp
Normal file
51
src/softbus_api/src/ExecuteCommand.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <softbus_api/ExecuteCommand.h>
|
||||
|
||||
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
|
||||
17
src/softbus_core/CMakeLists.txt
Normal file
17
src/softbus_core/CMakeLists.txt
Normal file
@@ -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)
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/DOMMessage.h>
|
||||
#include <softbus_core/models/RawPacket.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::string, DOMMessage> channels;
|
||||
};
|
||||
|
||||
class TimeAligner {
|
||||
public:
|
||||
explicit TimeAligner(TimeAlignerConfig config = {});
|
||||
|
||||
void ingest(const RawPacket& raw);
|
||||
void ingestDom(const DOMMessage& dom);
|
||||
std::vector<AlignedSample> drainAligned();
|
||||
|
||||
void setConfig(const TimeAlignerConfig& config);
|
||||
TimeAlignerConfig config() const;
|
||||
|
||||
private:
|
||||
struct ChannelBuffer {
|
||||
std::deque<DOMMessage> samples;
|
||||
};
|
||||
|
||||
DOMMessage rawToDom(const RawPacket& raw) const;
|
||||
AlignedSample alignAt(int64_t anchorNs);
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
TimeAlignerConfig config_;
|
||||
std::map<std::string, ChannelBuffer> channels_;
|
||||
int64_t lastAnchorNs_{0};
|
||||
};
|
||||
|
||||
} // namespace softbus::core
|
||||
38
src/softbus_core/include/softbus_core/config/ProfileLoader.h
Normal file
38
src/softbus_core/include/softbus_core/config/ProfileLoader.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/ObjectModel.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
||||
87
src/softbus_core/include/softbus_core/logging/Logger.h
Normal file
87
src/softbus_core/include/softbus_core/logging/Logger.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
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 <typename... Args>
|
||||
void info(const std::string& tag, Args&&... args)
|
||||
{
|
||||
log(LogLevel::Info, tag, concat(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void infoTrace(const std::string& tag, const std::string& traceId, Args&&... args)
|
||||
{
|
||||
log(LogLevel::Info, tag, traceId, concat(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void warn(const std::string& tag, Args&&... args)
|
||||
{
|
||||
log(LogLevel::Warn, tag, concat(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void warnTrace(const std::string& tag, const std::string& traceId, Args&&... args)
|
||||
{
|
||||
log(LogLevel::Warn, tag, traceId, concat(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void error(const std::string& tag, Args&&... args)
|
||||
{
|
||||
log(LogLevel::Error, tag, concat(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void errorTrace(const std::string& tag, const std::string& traceId, Args&&... args)
|
||||
{
|
||||
log(LogLevel::Error, tag, traceId, concat(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void debug(const std::string& tag, Args&&... args)
|
||||
{
|
||||
log(LogLevel::Debug, tag, concat(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
Logger() = default;
|
||||
|
||||
template <typename T>
|
||||
static std::string concat(T&& value)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::forward<T>(value);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
template <typename T, typename... Rest>
|
||||
static std::string concat(T&& first, Rest&&... rest)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::forward<T>(first);
|
||||
((oss << std::forward<Rest>(rest)), ...);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
LogLevel level_{LogLevel::Info};
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace softbus::core
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/queue/LockFreeQueue.h>
|
||||
61
src/softbus_core/include/softbus_core/models/DOMMessage.h
Normal file
61
src/softbus_core/include/softbus_core/models/DOMMessage.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
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<int>(domain)},
|
||||
{"quality", static_cast<int>(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<DomDomain>(j.value("domain", 0));
|
||||
msg.quality = static_cast<QualityCode>(j.value("quality", 0));
|
||||
if (j.contains("value")) {
|
||||
msg.value = j["value"];
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace softbus::core
|
||||
19
src/softbus_core/include/softbus_core/models/MetadataDef.h
Normal file
19
src/softbus_core/include/softbus_core/models/MetadataDef.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/DOMMessage.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
50
src/softbus_core/include/softbus_core/models/ObjectModel.h
Normal file
50
src/softbus_core/include/softbus_core/models/ObjectModel.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<PointDef> points;
|
||||
};
|
||||
|
||||
struct ObjectModelTree {
|
||||
std::string site;
|
||||
std::string system;
|
||||
std::string asset;
|
||||
std::vector<DeviceDef> devices;
|
||||
|
||||
bool loadFromFile(const std::string& path);
|
||||
bool isValid() const;
|
||||
};
|
||||
|
||||
} // namespace softbus::core
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/RawPacket.h>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
using RawBusMessage = RawPacket;
|
||||
|
||||
} // namespace softbus::core
|
||||
31
src/softbus_core/include/softbus_core/models/RawPacket.h
Normal file
31
src/softbus_core/include/softbus_core/models/RawPacket.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
struct RawPacket {
|
||||
std::string traceId;
|
||||
int64_t timestampNs{0};
|
||||
std::string runtimeDeviceId;
|
||||
std::string sourceId;
|
||||
std::string protocolTag;
|
||||
std::vector<uint8_t> payload;
|
||||
|
||||
RawPacket() = default;
|
||||
|
||||
RawPacket(std::string trace, int64_t ts, std::string runtimeId, std::string source,
|
||||
std::string protocol, std::vector<uint8_t> 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
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
class PlatformSignal {
|
||||
public:
|
||||
using Handler = std::function<void()>;
|
||||
|
||||
static void install(Handler handler);
|
||||
static bool shouldStop();
|
||||
static void requestStop();
|
||||
|
||||
private:
|
||||
static std::atomic<bool> stopRequested_;
|
||||
};
|
||||
|
||||
} // namespace softbus::core
|
||||
36
src/softbus_core/include/softbus_core/plugin/IFramer.h
Normal file
36
src/softbus_core/include/softbus_core/plugin/IFramer.h
Normal file
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
struct FrameResult {
|
||||
bool success{false};
|
||||
std::size_t consumedBytes{0};
|
||||
std::size_t frameStartOffset{0};
|
||||
std::vector<uint8_t> 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
|
||||
23
src/softbus_core/include/softbus_core/plugin/IParser.h
Normal file
23
src/softbus_core/include/softbus_core/plugin/IParser.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/DOMMessage.h>
|
||||
#include <softbus_core/plugin/IFramer.h>
|
||||
|
||||
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
|
||||
41
src/softbus_core/include/softbus_core/plugin/PluginLoader.h
Normal file
41
src/softbus_core/include/softbus_core/plugin/PluginLoader.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <softbus_core/plugin/IFramer.h>
|
||||
#include <softbus_core/plugin/IParser.h>
|
||||
|
||||
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
|
||||
87
src/softbus_core/include/softbus_core/queue/LockFreeQueue.h
Normal file
87
src/softbus_core/include/softbus_core/queue/LockFreeQueue.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
template <typename T>
|
||||
class LockFreeQueue {
|
||||
public:
|
||||
explicit LockFreeQueue(std::size_t capacity)
|
||||
: capacity_(capacity)
|
||||
, buffer_(std::make_unique<Slot[]>(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<intptr_t>(seq) - static_cast<intptr_t>(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<T> 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<intptr_t>(seq) - static_cast<intptr_t>(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<std::size_t> sequence{0};
|
||||
T data{};
|
||||
};
|
||||
|
||||
const std::size_t capacity_;
|
||||
std::unique_ptr<Slot[]> buffer_;
|
||||
std::atomic<std::size_t> head_;
|
||||
std::atomic<std::size_t> tail_;
|
||||
};
|
||||
|
||||
} // namespace softbus::core
|
||||
16
src/softbus_core/include/softbus_core/softbus_core.h
Normal file
16
src/softbus_core/include/softbus_core/softbus_core.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/RawPacket.h>
|
||||
#include <softbus_core/models/DOMMessage.h>
|
||||
#include <softbus_core/models/MetadataDef.h>
|
||||
#include <softbus_core/models/ObjectModel.h>
|
||||
#include <softbus_core/identity/DeviceIdentity.h>
|
||||
#include <softbus_core/queue/LockFreeQueue.h>
|
||||
#include <softbus_core/time/HwClock.h>
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
#include <softbus_core/alignment/TimeAligner.h>
|
||||
#include <softbus_core/plugin/IFramer.h>
|
||||
#include <softbus_core/plugin/IParser.h>
|
||||
#include <softbus_core/plugin/PluginLoader.h>
|
||||
#include <softbus_core/platform/PlatformSignal.h>
|
||||
#include <softbus_core/config/ProfileLoader.h>
|
||||
13
src/softbus_core/include/softbus_core/time/HwClock.h
Normal file
13
src/softbus_core/include/softbus_core/time/HwClock.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
class HwClock {
|
||||
public:
|
||||
static int64_t nowNs();
|
||||
static double nsToMs(int64_t ns);
|
||||
};
|
||||
|
||||
} // namespace softbus::core
|
||||
107
src/softbus_core/src/alignment/TimeAligner.cpp
Normal file
107
src/softbus_core/src/alignment/TimeAligner.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include <softbus_core/alignment/TimeAligner.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
TimeAligner::TimeAligner(TimeAlignerConfig config)
|
||||
: config_(config)
|
||||
{
|
||||
}
|
||||
|
||||
void TimeAligner::setConfig(const TimeAlignerConfig& config)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
config_ = config;
|
||||
}
|
||||
|
||||
TimeAlignerConfig TimeAligner::config() const
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<int64_t>::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<AlignedSample> TimeAligner::drainAligned()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (lastAnchorNs_ == 0 || channels_.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<AlignedSample> result;
|
||||
result.push_back(alignAt(lastAnchorNs_));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace softbus::core
|
||||
37
src/softbus_core/src/config/ProfileLoader.cpp
Normal file
37
src/softbus_core/src/config/ProfileLoader.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <softbus_core/config/ProfileLoader.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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<uint16_t>(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<uint16_t>(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
|
||||
61
src/softbus_core/src/identity/DeviceIdentity.cpp
Normal file
61
src/softbus_core/src/identity/DeviceIdentity.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <softbus_core/identity/DeviceIdentity.h>
|
||||
|
||||
#include <softbus_core/time/HwClock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string randomHex(std::size_t bytes)
|
||||
{
|
||||
static thread_local std::mt19937_64 rng{
|
||||
static_cast<std::mt19937_64::result_type>(
|
||||
std::chrono::steady_clock::now().time_since_epoch().count())};
|
||||
std::uniform_int_distribution<int> 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
|
||||
62
src/softbus_core/src/logging/Logger.cpp
Normal file
62
src/softbus_core/src/logging/Logger.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
Logger& Logger::instance()
|
||||
{
|
||||
static Logger logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void Logger::setLevel(LogLevel level)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<int>(level) < static_cast<int>(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<std::mutex> lock(mutex_);
|
||||
std::cerr << oss.str() << std::endl;
|
||||
}
|
||||
|
||||
} // namespace softbus::core
|
||||
84
src/softbus_core/src/models/ObjectModel.cpp
Normal file
84
src/softbus_core/src/models/ObjectModel.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <softbus_core/models/ObjectModel.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sstream>
|
||||
|
||||
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
|
||||
48
src/softbus_core/src/platform/PlatformSignal.cpp
Normal file
48
src/softbus_core/src/platform/PlatformSignal.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <softbus_core/platform/PlatformSignal.h>
|
||||
|
||||
#ifdef SOFTBUS_PLATFORM_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <csignal>
|
||||
#endif
|
||||
|
||||
namespace softbus::core {
|
||||
|
||||
std::atomic<bool> 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
|
||||
86
src/softbus_core/src/plugin/PluginLoader.cpp
Normal file
86
src/softbus_core/src/plugin/PluginLoader.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <softbus_core/plugin/PluginLoader.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#ifdef SOFTBUS_PLATFORM_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#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<CreateFramerFn>(GetProcAddress(static_cast<HMODULE>(handle_), "create_framer"));
|
||||
auto destroyFramer = reinterpret_cast<DestroyFramerFn>(GetProcAddress(static_cast<HMODULE>(handle_), "destroy_framer"));
|
||||
auto createParser = reinterpret_cast<CreateParserFn>(GetProcAddress(static_cast<HMODULE>(handle_), "create_parser"));
|
||||
auto destroyParser = reinterpret_cast<DestroyParserFn>(GetProcAddress(static_cast<HMODULE>(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<CreateFramerFn>(dlsym(handle_, "create_framer"));
|
||||
auto destroyFramer = reinterpret_cast<DestroyFramerFn>(dlsym(handle_, "destroy_framer"));
|
||||
auto createParser = reinterpret_cast<CreateParserFn>(dlsym(handle_, "create_parser"));
|
||||
auto destroyParser = reinterpret_cast<DestroyParserFn>(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<HMODULE>(handle_));
|
||||
#else
|
||||
dlclose(handle_);
|
||||
#endif
|
||||
handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace softbus::core
|
||||
37
src/softbus_core/src/time/HwClock.cpp
Normal file
37
src/softbus_core/src/time/HwClock.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <softbus_core/time/HwClock.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#ifdef SOFTBUS_PLATFORM_WINDOWS
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#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<int64_t>((counter.QuadPart * 1'000'000'000LL) / frequency.QuadPart);
|
||||
#else
|
||||
timespec ts{};
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return static_cast<int64_t>(ts.tv_sec) * 1'000'000'000LL + ts.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
double HwClock::nsToMs(int64_t ns)
|
||||
{
|
||||
return static_cast<double>(ns) / 1'000'000.0;
|
||||
}
|
||||
|
||||
} // namespace softbus::core
|
||||
26
src/softbus_daemon/CMakeLists.txt
Normal file
26
src/softbus_daemon/CMakeLists.txt
Normal file
@@ -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)
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace softbus::daemon {
|
||||
|
||||
class IpcOptionalDBus {
|
||||
public:
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
};
|
||||
|
||||
} // namespace softbus::daemon
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_api/ExecuteCommand.h>
|
||||
#include <softbus_transport/TcpUplinkServer.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/RawPacket.h>
|
||||
#include <softbus_core/plugin/PluginLoader.h>
|
||||
|
||||
#include <softbus_opcua/UaModelBinder.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/MetadataDef.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace softbus::daemon {
|
||||
|
||||
class MetadataRegistry {
|
||||
public:
|
||||
void registerMetadata(const softbus::core::MetadataDef& metadata);
|
||||
std::optional<softbus::core::MetadataDef> findByObjectRef(const std::string& objectRef) const;
|
||||
|
||||
private:
|
||||
std::map<std::string, softbus::core::MetadataDef> entries_;
|
||||
};
|
||||
|
||||
} // namespace softbus::daemon
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/ObjectModel.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace softbus::daemon {
|
||||
|
||||
class ObjectModelRegistry {
|
||||
public:
|
||||
bool loadFromFile(const std::string& path);
|
||||
const softbus::core::ObjectModelTree& tree() const { return tree_; }
|
||||
std::optional<softbus::core::ObjectRef> findPointByRegister(int registerAddress) const;
|
||||
|
||||
private:
|
||||
softbus::core::ObjectModelTree tree_;
|
||||
};
|
||||
|
||||
} // namespace softbus::daemon
|
||||
32
src/softbus_daemon/src/CommandDispatcher.cpp
Normal file
32
src/softbus_daemon/src/CommandDispatcher.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <softbus_daemon/command/CommandDispatcher.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
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
|
||||
171
src/softbus_daemon/src/CoreService.cpp
Normal file
171
src/softbus_daemon/src/CoreService.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <softbus_daemon/core/CoreService.h>
|
||||
|
||||
#include <softbus_daemon/command/CommandDispatcher.h>
|
||||
#include <softbus_daemon/pipeline/PipelineEngine.h>
|
||||
|
||||
#include <softbus_opcua/UaAddressSpaceBuilder.h>
|
||||
#include <softbus_opcua/UaEventBridge.h>
|
||||
#include <softbus_opcua/UaMethodBinder.h>
|
||||
#include <softbus_opcua/UaModelBinder.h>
|
||||
#include <softbus_opcua/UaServerEngine.h>
|
||||
#include <softbus_daemon/registry/ObjectModelRegistry.h>
|
||||
|
||||
#include <softbus_core/identity/DeviceIdentity.h>
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
#include <softbus_core/platform/PlatformSignal.h>
|
||||
|
||||
#ifdef SOFTBUS_PLATFORM_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
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<ObjectModelRegistry>();
|
||||
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<softbus::opcua::UaServerEngine>();
|
||||
if (!uaEngine_->start(profile_.opcua.port, profile_.opcua.applicationName, security)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uaBinder_ = std::make_unique<softbus::opcua::UaModelBinder>(uaEngine_->nativeServer());
|
||||
uaMethodBinder_ = std::make_unique<softbus::opcua::UaMethodBinder>(uaEngine_->nativeServer());
|
||||
uaEventBridge_ = std::make_unique<softbus::opcua::UaEventBridge>(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<PipelineEngine>(&modbusPlugin_, uaBinder_.get());
|
||||
uplink_ = std::make_unique<softbus::transport::TcpUplinkServer>(
|
||||
io_, profile_.uplink.listenHost, profile_.uplink.listenPort);
|
||||
commandDispatcher_ = std::make_unique<CommandDispatcher>(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
|
||||
21
src/softbus_daemon/src/IpcOptionalDBus.cpp
Normal file
21
src/softbus_daemon/src/IpcOptionalDBus.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <softbus_daemon/api/IpcOptionalDBus.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
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
|
||||
19
src/softbus_daemon/src/MetadataRegistry.cpp
Normal file
19
src/softbus_daemon/src/MetadataRegistry.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <softbus_daemon/registry/MetadataRegistry.h>
|
||||
|
||||
namespace softbus::daemon {
|
||||
|
||||
void MetadataRegistry::registerMetadata(const softbus::core::MetadataDef& metadata)
|
||||
{
|
||||
entries_[metadata.objectRef] = metadata;
|
||||
}
|
||||
|
||||
std::optional<softbus::core::MetadataDef> 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
|
||||
25
src/softbus_daemon/src/ObjectModelRegistry.cpp
Normal file
25
src/softbus_daemon/src/ObjectModelRegistry.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <softbus_daemon/registry/ObjectModelRegistry.h>
|
||||
|
||||
#include <softbus_core/identity/DeviceIdentity.h>
|
||||
|
||||
namespace softbus::daemon {
|
||||
|
||||
bool ObjectModelRegistry::loadFromFile(const std::string& path)
|
||||
{
|
||||
return tree_.loadFromFile(path);
|
||||
}
|
||||
|
||||
std::optional<softbus::core::ObjectRef> 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
|
||||
48
src/softbus_daemon/src/PipelineEngine.cpp
Normal file
48
src/softbus_daemon/src/PipelineEngine.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <softbus_daemon/pipeline/PipelineEngine.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t> 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
|
||||
30
src/softbus_daemon/src/main.cpp
Normal file
30
src/softbus_daemon/src/main.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <softbus_daemon/core/CoreService.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
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;
|
||||
}
|
||||
14
src/softbus_edge/CMakeLists.txt
Normal file
14
src/softbus_edge/CMakeLists.txt
Normal file
@@ -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)
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace softbus::edge {
|
||||
|
||||
struct DiscoveredDevice {
|
||||
std::string portName;
|
||||
std::string protocol;
|
||||
};
|
||||
|
||||
class DeviceDiscovery {
|
||||
public:
|
||||
std::vector<DiscoveredDevice> discover();
|
||||
};
|
||||
|
||||
} // namespace softbus::edge
|
||||
28
src/softbus_edge/include/softbus_edge/egress/UplinkClient.h
Normal file
28
src/softbus_edge/include/softbus_edge/egress/UplinkClient.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_api/ExecuteCommand.h>
|
||||
#include <softbus_core/models/RawPacket.h>
|
||||
#include <softbus_transport/TcpUplinkClient.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
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<void(const softbus::api::ExecuteCommand&)> handler);
|
||||
void poll();
|
||||
|
||||
private:
|
||||
std::unique_ptr<softbus::transport::TcpUplinkClient> client_;
|
||||
};
|
||||
|
||||
} // namespace softbus::edge
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace softbus::edge {
|
||||
|
||||
class DeviceFactory {
|
||||
public:
|
||||
std::string createRuntimeDeviceId();
|
||||
};
|
||||
|
||||
} // namespace softbus::edge
|
||||
17
src/softbus_edge/include/softbus_edge/ingress/IIngressPort.h
Normal file
17
src/softbus_edge/include/softbus_edge/ingress/IIngressPort.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/RawPacket.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace softbus::edge {
|
||||
|
||||
class IIngressPort {
|
||||
public:
|
||||
virtual ~IIngressPort() = default;
|
||||
virtual bool open() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual std::vector<softbus::core::RawPacket> poll() = 0;
|
||||
};
|
||||
|
||||
} // namespace softbus::edge
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_edge/ingress/IIngressPort.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace softbus::edge {
|
||||
|
||||
class MockModbusIngress : public IIngressPort {
|
||||
public:
|
||||
explicit MockModbusIngress(std::string objectRef, std::string runtimeDeviceId);
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
std::vector<softbus::core::RawPacket> poll() override;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> buildFrame(uint16_t registerValue) const;
|
||||
|
||||
std::string objectRef_;
|
||||
std::string runtimeDeviceId_;
|
||||
bool open_{false};
|
||||
uint16_t counter_{100};
|
||||
};
|
||||
|
||||
} // namespace softbus::edge
|
||||
10
src/softbus_edge/src/DeviceDiscovery.cpp
Normal file
10
src/softbus_edge/src/DeviceDiscovery.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <softbus_edge/discovery/DeviceDiscovery.h>
|
||||
|
||||
namespace softbus::edge {
|
||||
|
||||
std::vector<DiscoveredDevice> DeviceDiscovery::discover()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace softbus::edge
|
||||
12
src/softbus_edge/src/DeviceFactory.cpp
Normal file
12
src/softbus_edge/src/DeviceFactory.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <softbus_edge/factory/DeviceFactory.h>
|
||||
|
||||
#include <softbus_core/identity/DeviceIdentity.h>
|
||||
|
||||
namespace softbus::edge {
|
||||
|
||||
std::string DeviceFactory::createRuntimeDeviceId()
|
||||
{
|
||||
return softbus::core::DeviceIdentity::generateRuntimeDeviceId();
|
||||
}
|
||||
|
||||
} // namespace softbus::edge
|
||||
54
src/softbus_edge/src/MockModbusIngress.cpp
Normal file
54
src/softbus_edge/src/MockModbusIngress.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <softbus_edge/ingress/MockModbusIngress.h>
|
||||
|
||||
#include <softbus_core/identity/DeviceIdentity.h>
|
||||
#include <softbus_core/time/HwClock.h>
|
||||
|
||||
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<uint8_t> 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<uint8_t>((registerValue >> 8) & 0xFF),
|
||||
static_cast<uint8_t>(registerValue & 0xFF),
|
||||
0x00, 0x00
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<softbus::core::RawPacket> 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<uint16_t>(counter_ + 1);
|
||||
return {packet};
|
||||
}
|
||||
|
||||
} // namespace softbus::edge
|
||||
40
src/softbus_edge/src/UplinkClient.cpp
Normal file
40
src/softbus_edge/src/UplinkClient.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <softbus_edge/egress/UplinkClient.h>
|
||||
|
||||
namespace softbus::edge {
|
||||
|
||||
UplinkClient::UplinkClient(std::string host, uint16_t port)
|
||||
: client_(std::make_unique<softbus::transport::TcpUplinkClient>(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<void(const softbus::api::ExecuteCommand&)> handler)
|
||||
{
|
||||
client_->setCommandHandler(std::move(handler));
|
||||
}
|
||||
|
||||
void UplinkClient::poll()
|
||||
{
|
||||
client_->poll();
|
||||
}
|
||||
|
||||
} // namespace softbus::edge
|
||||
85
src/softbus_edge/src/main.cpp
Normal file
85
src/softbus_edge/src/main.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <softbus_edge/egress/UplinkClient.h>
|
||||
#include <softbus_edge/factory/DeviceFactory.h>
|
||||
#include <softbus_edge/ingress/MockModbusIngress.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
#include <softbus_core/platform/PlatformSignal.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
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<uint16_t>(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;
|
||||
}
|
||||
15
src/softbus_opcua/CMakeLists.txt
Normal file
15
src/softbus_opcua/CMakeLists.txt
Normal file
@@ -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)
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/ObjectModel.h>
|
||||
|
||||
#if SOFTBUS_HAS_OPCUA
|
||||
#include <open62541/types.h>
|
||||
#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
|
||||
22
src/softbus_opcua/include/softbus_opcua/UaEventBridge.h
Normal file
22
src/softbus_opcua/include/softbus_opcua/UaEventBridge.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
||||
26
src/softbus_opcua/include/softbus_opcua/UaMethodBinder.h
Normal file
26
src/softbus_opcua/include/softbus_opcua/UaMethodBinder.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_api/ExecuteCommand.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
struct UA_Server;
|
||||
|
||||
namespace softbus::opcua {
|
||||
|
||||
using ExecuteCommandHandler = std::function<bool(const softbus::api::ExecuteCommand&)>;
|
||||
|
||||
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
|
||||
28
src/softbus_opcua/include/softbus_opcua/UaModelBinder.h
Normal file
28
src/softbus_opcua/include/softbus_opcua/UaModelBinder.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/DOMMessage.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#if SOFTBUS_HAS_OPCUA
|
||||
#include <open62541/types.h>
|
||||
#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<std::string, UA_NodeId> pointNodes_;
|
||||
};
|
||||
|
||||
} // namespace softbus::opcua
|
||||
40
src/softbus_opcua/include/softbus_opcua/UaServerEngine.h
Normal file
40
src/softbus_opcua/include/softbus_opcua/UaServerEngine.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
119
src/softbus_opcua/src/UaAddressSpaceBuilder.cpp
Normal file
119
src/softbus_opcua/src/UaAddressSpaceBuilder.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include <softbus_opcua/UaAddressSpaceBuilder.h>
|
||||
|
||||
#include <softbus_core/identity/DeviceIdentity.h>
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
#include <softbus_opcua/UaModelBinder.h>
|
||||
|
||||
#if SOFTBUS_HAS_OPCUA
|
||||
#include <open62541/server.h>
|
||||
#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
|
||||
36
src/softbus_opcua/src/UaEventBridge.cpp
Normal file
36
src/softbus_opcua/src/UaEventBridge.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <softbus_opcua/UaEventBridge.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
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
|
||||
157
src/softbus_opcua/src/UaMethodBinder.cpp
Normal file
157
src/softbus_opcua/src/UaMethodBinder.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include <softbus_opcua/UaMethodBinder.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#if SOFTBUS_HAS_OPCUA
|
||||
#include <open62541/server.h>
|
||||
#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<const UA_String*>(input[0].data);
|
||||
if (!jsonInput) {
|
||||
return UA_STATUSCODE_BADTYPEMISMATCH;
|
||||
}
|
||||
|
||||
const std::string jsonStr(reinterpret_cast<const char*>(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
|
||||
71
src/softbus_opcua/src/UaModelBinder.cpp
Normal file
71
src/softbus_opcua/src/UaModelBinder.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <softbus_opcua/UaModelBinder.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#if SOFTBUS_HAS_OPCUA
|
||||
#include <open62541/server.h>
|
||||
#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<double>();
|
||||
} else if (message.value.contains("registerValue")) {
|
||||
numericValue = message.value["registerValue"].get<double>();
|
||||
}
|
||||
|
||||
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
|
||||
80
src/softbus_opcua/src/UaServerEngine.cpp
Normal file
80
src/softbus_opcua/src/UaServerEngine.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include <softbus_opcua/UaServerEngine.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#if SOFTBUS_HAS_OPCUA
|
||||
#include <open62541/server.h>
|
||||
#include <open62541/server_config_default.h>
|
||||
#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
|
||||
12
src/softbus_transport/CMakeLists.txt
Normal file
12
src/softbus_transport/CMakeLists.txt
Normal file
@@ -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)
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_transport/UplinkWire.h>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace softbus::transport {
|
||||
|
||||
using CommandHandler = std::function<void(const softbus::api::ExecuteCommand&)>;
|
||||
|
||||
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<asio::ip::tcp::socket> socket_;
|
||||
std::unique_ptr<asio::executor_work_guard<asio::io_context::executor_type>> workGuard_;
|
||||
std::thread ioThread_;
|
||||
CommandHandler commandHandler_;
|
||||
std::string readBuffer_;
|
||||
std::mutex sendMutex_;
|
||||
bool connected_{false};
|
||||
};
|
||||
|
||||
} // namespace softbus::transport
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_transport/UplinkWire.h>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace softbus::transport {
|
||||
|
||||
using RawPacketHandler = std::function<void(const softbus::core::RawPacket&)>;
|
||||
using CommandSender = std::function<bool(const softbus::api::ExecuteCommand&)>;
|
||||
|
||||
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<asio::ip::tcp::socket> socket);
|
||||
void handleLine(const std::string& line);
|
||||
|
||||
asio::io_context& io_;
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
RawPacketHandler rawHandler_;
|
||||
std::shared_ptr<asio::ip::tcp::socket> clientSocket_;
|
||||
std::string readBuffer_;
|
||||
std::mutex sendMutex_;
|
||||
bool running_{false};
|
||||
};
|
||||
|
||||
} // namespace softbus::transport
|
||||
33
src/softbus_transport/include/softbus_transport/UplinkWire.h
Normal file
33
src/softbus_transport/include/softbus_transport/UplinkWire.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <softbus_core/models/RawPacket.h>
|
||||
|
||||
#include <softbus_api/ExecuteCommand.h>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<WireMessage> decodeWireMessage(const std::string& line);
|
||||
|
||||
std::string base64Encode(const uint8_t* data, std::size_t size);
|
||||
std::vector<uint8_t> base64Decode(const std::string& encoded);
|
||||
|
||||
} // namespace softbus::transport
|
||||
120
src/softbus_transport/src/TcpUplinkClient.cpp
Normal file
120
src/softbus_transport/src/TcpUplinkClient.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <softbus_transport/TcpUplinkClient.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace softbus::transport {
|
||||
|
||||
TcpUplinkClient::TcpUplinkClient(std::string host, uint16_t port)
|
||||
: host_(std::move(host))
|
||||
, port_(port)
|
||||
, workGuard_(std::make_unique<asio::executor_work_guard<asio::io_context::executor_type>>(
|
||||
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<asio::ip::tcp::socket>(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::executor_work_guard<asio::io_context::executor_type>>(
|
||||
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<std::mutex> 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<std::array<char, 4096>>();
|
||||
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
|
||||
115
src/softbus_transport/src/TcpUplinkServer.cpp
Normal file
115
src/softbus_transport/src/TcpUplinkServer.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <softbus_transport/TcpUplinkServer.h>
|
||||
|
||||
#include <softbus_core/logging/Logger.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
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<std::mutex> 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<asio::ip::tcp::socket>(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<asio::ip::tcp::socket> socket)
|
||||
{
|
||||
auto buffer = std::make_shared<std::array<char, 4096>>();
|
||||
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
|
||||
110
src/softbus_transport/src/UplinkWire.cpp
Normal file
110
src/softbus_transport/src/UplinkWire.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <softbus_transport/UplinkWire.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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<uint32_t>(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<uint8_t> 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<uint8_t> 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<uint8_t>((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<WireMessage> 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
|
||||
Reference in New Issue
Block a user