From faf1b97d93030583562195692443c0ad38d50f9a Mon Sep 17 00:00:00 2001 From: zhaoxi <535394140@qq.com> Date: Mon, 4 Mar 2024 16:13:13 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=AE=8C=E5=96=84=20Findxxx.cmake=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdparty/readme.txt | 6 +++++- cmake/FindHikSDK.cmake | 19 ++++++++----------- cmake/FindMvSDK.cmake | 11 ++++------- cmake/FindOPTCameraSDK.cmake | 11 ++++------- cmake/FindOPTLightCtrl.cmake | 11 ++++------- cmake/FindOrt.cmake | 9 +++------ cmake/RMVLModule.cmake | 10 +++++----- cmake/RMVLUtils.cmake | 3 ++- doc/tutorials/modules/core/runge_kutta.md | 16 +++++++--------- modules/ml/CMakeLists.txt | 2 +- 10 files changed, 43 insertions(+), 55 deletions(-) diff --git a/3rdparty/readme.txt b/3rdparty/readme.txt index 5b81d9b8..45a199d5 100644 --- a/3rdparty/readme.txt +++ b/3rdparty/readme.txt @@ -12,4 +12,8 @@ apriltag 描述 用于定位的视觉基准系统 CMake 选项 BUILD_APRILTAG 开启后可构建此模块(默认开启) WITH_APRILTAG 启用该选项来为 tag_detector 模块提供 apriltag 支持 - +open62541 描述 OPC UA 的开源 C++ 实现 + 许可与版权 open62541 由 MPL-2.0 license 许可证覆盖,参见 open62541/LICENSE + 官网主页 open62541.org + CMake 选项 BUILD_OPEN62541 开启后可下载并构建此模块(默认关闭) + WITH_OPEN62541 启用该选项来为 opcua 模块提供 open62541 支持 diff --git a/cmake/FindHikSDK.cmake b/cmake/FindHikSDK.cmake index 03400abd..55c5b1d9 100644 --- a/cmake/FindHikSDK.cmake +++ b/cmake/FindHikSDK.cmake @@ -1,14 +1,14 @@ -set(MVCAM_SDK_PATH "$ENV{MVCAM_SDK_PATH}") -if(MVCAM_SDK_PATH STREQUAL "") +set(mvcam_sdk_path "$ENV{MVCAM_SDK_PATH}") +if(mvcam_sdk_path STREQUAL "") set(HikSDK_FOUND FALSE) return() endif() # add the include directories path find_path( - HikSDK_INCLUDE_DIR + HikSDK_INCLUDE_DIRS NAMES "MvCameraControl.h" - PATHS "${MVCAM_SDK_PATH}/include" + PATHS "${mvcam_sdk_path}/include" NO_DEFAULT_PATH ) @@ -28,20 +28,17 @@ else() endif() find_library( - HikSDK_LIB + HikSDK_LIBS NAMES "libMvCameraControl.so" - PATHS "${MVCAM_SDK_PATH}/lib/${ARCH_HIKLIB}" + PATHS "${mvcam_sdk_path}/lib/${ARCH_HIKLIB}" NO_DEFAULT_PATH ) -mark_as_advanced(ARCH_HIKLIB HikSDK_INCLUDE_DIR HikSDK_LIB) - -set(HikSDK_INCLUDE_DIRS "${HikSDK_INCLUDE_DIR}") -set(HikSDK_LIBS "${HikSDK_LIB}") +mark_as_advanced(ARCH_HIKLIB HikSDK_INCLUDE_DIRS HikSDK_LIBS) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( HikSDK - REQUIRED_VARS HikSDK_LIB HikSDK_INCLUDE_DIR + REQUIRED_VARS HikSDK_LIBS HikSDK_INCLUDE_DIRS ) \ No newline at end of file diff --git a/cmake/FindMvSDK.cmake b/cmake/FindMvSDK.cmake index 661303d6..6c7c2878 100644 --- a/cmake/FindMvSDK.cmake +++ b/cmake/FindMvSDK.cmake @@ -1,6 +1,6 @@ # add the include directories path find_path( - MvSDK_INCLUDE_DIR + MvSDK_INCLUDE_DIRS NAMES "CameraApi.h" PATHS "/usr/include" NO_DEFAULT_PATH @@ -8,20 +8,17 @@ find_path( # add libraries find_library( - MvSDK_LIB + MvSDK_LIBS PATHS "/lib" NAMES "libMVSDK.so" NO_DEFAULT_PATH ) -mark_as_advanced(MvSDK_INCLUDE_DIR MvSDK_LIB) - -set(MvSDK_INCLUDE_DIRS "${MvSDK_INCLUDE_DIR}") -set(MvSDK_LIBS "${MvSDK_LIB}") +mark_as_advanced(MvSDK_INCLUDE_DIRS MvSDK_LIBS) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( MvSDK - REQUIRED_VARS MvSDK_LIB MvSDK_INCLUDE_DIR + REQUIRED_VARS MvSDK_LIBS MvSDK_INCLUDE_DIRS ) diff --git a/cmake/FindOPTCameraSDK.cmake b/cmake/FindOPTCameraSDK.cmake index 0c852c67..c3fe91c1 100644 --- a/cmake/FindOPTCameraSDK.cmake +++ b/cmake/FindOPTCameraSDK.cmake @@ -2,7 +2,7 @@ set(OPTCameraSDK_root_path "/opt/OPT/OPTCameraDemo") # add the include directories path find_path( - OPTCameraSDK_INCLUDE_DIR + OPTCameraSDK_INCLUDE_DIRS NAMES "OPTApi.h" PATHS "${OPTCameraSDK_root_path}/include" NO_DEFAULT_PATH @@ -10,20 +10,17 @@ find_path( # add libraries find_library( - OPTCameraSDK_LIB + OPTCameraSDK_LIBS NAMES "libOPTSDK.so" PATHS "${OPTCameraSDK_root_path}/lib" NO_DEFAULT_PATH ) -mark_as_advanced(OPTCameraSDK_INCLUDE_DIR OPTCameraSDK_LIB) - -set(OPTCameraSDK_INCLUDE_DIRS "${OPTCameraSDK_INCLUDE_DIR}") -set(OPTCameraSDK_LIBS "${OPTCameraSDK_LIB}") +mark_as_advanced(OPTCameraSDK_INCLUDE_DIRS OPTCameraSDK_LIBS) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( OPTCameraSDK - REQUIRED_VARS OPTCameraSDK_LIB OPTCameraSDK_INCLUDE_DIR + REQUIRED_VARS OPTCameraSDK_LIBS OPTCameraSDK_INCLUDE_DIRS ) \ No newline at end of file diff --git a/cmake/FindOPTLightCtrl.cmake b/cmake/FindOPTLightCtrl.cmake index 299369f4..d825a9d6 100644 --- a/cmake/FindOPTLightCtrl.cmake +++ b/cmake/FindOPTLightCtrl.cmake @@ -2,7 +2,7 @@ set(OPTLightCtrl_root_path "/opt/OPT/OPTController") # add the include directories path find_path( - OPTLightCtrl_INCLUDE_DIR + OPTLightCtrl_INCLUDE_DIRS NAMES "OPTController.h" PATHS "${OPTLightCtrl_root_path}/include" NO_DEFAULT_PATH @@ -10,20 +10,17 @@ find_path( # add libraries find_library( - OPTLightCtrl_LIB + OPTLightCtrl_LIBS NAMES "libOPTController.so" PATHS "${OPTLightCtrl_root_path}/lib" NO_DEFAULT_PATH ) -mark_as_advanced(OPTLightCtrl_INCLUDE_DIR OPTLightCtrl_LIB) - -set(OPTLightCtrl_INCLUDE_DIRS "${OPTLightCtrl_INCLUDE_DIR}") -set(OPTLightCtrl_LIBS "${OPTLightCtrl_LIB}") +mark_as_advanced(OPTLightCtrl_INCLUDE_DIRS OPTLightCtrl_LIBS) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( OPTLightCtrl - REQUIRED_VARS OPTLightCtrl_LIB OPTLightCtrl_INCLUDE_DIR + REQUIRED_VARS OPTLightCtrl_LIBS OPTLightCtrl_INCLUDE_DIRS ) \ No newline at end of file diff --git a/cmake/FindOrt.cmake b/cmake/FindOrt.cmake index 551b5b51..897e4046 100755 --- a/cmake/FindOrt.cmake +++ b/cmake/FindOrt.cmake @@ -4,7 +4,7 @@ endif() # add the include directories path find_path( - Ort_INCLUDE_DIR + Ort_INCLUDE_DIRS PATHS "${ort_root_path}/include/onnxruntime" NAMES "onnxruntime_cxx_api.h" NO_DEFAULT_PATH @@ -12,18 +12,15 @@ find_path( # add libraries find_library( - Ort_LIB + Ort_LIBS NAMES "libonnxruntime.so" PATHS "${ort_root_path}/lib" NO_DEFAULT_PATH ) -set(Ort_INCLUDE_DIRS "${Ort_INCLUDE_DIR}") -set(Ort_LIBS "${Ort_LIB}") - include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Ort - REQUIRED_VARS Ort_LIB + REQUIRED_VARS Ort_LIBS Ort_INCLUDE_DIRS ) diff --git a/cmake/RMVLModule.cmake b/cmake/RMVLModule.cmake index 4b7e985f..fd775a03 100644 --- a/cmake/RMVLModule.cmake +++ b/cmake/RMVLModule.cmake @@ -37,7 +37,7 @@ endmacro(rmvl_compile_definitions _target) # ---------------------------------------------------------------------------- # 将指定路径下的所有文件安装至特定目标 # 用法: -# rmvl_install_directories( [DST_LIB]) +# rmvl_install_directories( [DESTINATION]) # 示例: # rmvl_install_directories(include/rmvl) # ---------------------------------------------------------------------------- @@ -82,8 +82,8 @@ endfunction(rmvl_install_directories) # 示例: # rmvl_add_module( # my_module # 需要生成的模块 (文件夹名) -# EXTRA_HEADER xxx_h # 参与构建的其余头文件目录 -# EXTRA_SOURCE xxx_src # 参与构建的其余头文件目录 +# EXTRA_HEADER xxx_h # 参与构建的除 include 文件夹以外的其余头文件目录 +# EXTRA_SOURCE xxx_src # 参与构建的除 src 文件夹以外的其余源文件目录 # DEPENDS core # 依赖的 RMVL 模块 (文件夹名) # EXTERNAL ${OpenCV_LIBS} # 依赖的第三方目标库 # ) @@ -95,7 +95,6 @@ macro(rmvl_add_module _name) cmake_parse_arguments(MD "${options}" "" "${multi_args}" ${ARGN}) # Module information - unset(the_module) set(the_module rmvl_${_name}) set( RMVL_MODULE_${the_module}_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}" @@ -202,7 +201,8 @@ macro(rmvl_add_module _name) set(RMVL_MODULES_INTERFACE ${RMVL_MODULES_INTERFACE} "${the_module}" CACHE INTERNAL "List of RMVL interface modules marked for export" FORCE) endif(NOT MD_INTERFACE) set(RMVL_MODULES_BUILD ${RMVL_MODULES_BUILD} "${the_module}" CACHE INTERNAL "List of RMVL modules included into the build" FORCE) - endif(BUILD_${the_module}) + endif() + unset(the_module) endmacro(rmvl_add_module _name) # ---------------------------------------------------------------------------- diff --git a/cmake/RMVLUtils.cmake b/cmake/RMVLUtils.cmake index bca57002..bfe86c2a 100644 --- a/cmake/RMVLUtils.cmake +++ b/cmake/RMVLUtils.cmake @@ -14,7 +14,7 @@ macro(rmvl_check_cxx result src standard) try_compile( ${result} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/cpp${standard} - SOURCES ${CMAKE_CURRENT_LIST_DIR}/check/${src} + SOURCES ${PROJECT_SOURCE_DIR}/cmake/check/${src} COMPILE_DEFINITIONS "${standard}" ) @@ -25,6 +25,7 @@ macro(rmvl_check_cxx result src standard) set(${result} "" CACHE INTERNAL "Test ${result}") message(STATUS "Performing Test ${result} - Failed") endif() + unset(build_args) endmacro() set(RMVL_BUILD_INFO_STR "" CACHE INTERNAL "") diff --git a/doc/tutorials/modules/core/runge_kutta.md b/doc/tutorials/modules/core/runge_kutta.md index 7a496db2..e900dd80 100644 --- a/doc/tutorials/modules/core/runge_kutta.md +++ b/doc/tutorials/modules/core/runge_kutta.md @@ -24,8 +24,8 @@ 可写成一阶方程组的形式,令\f$x_1=x,\quad x_2=\dot x\f$,有 -\f[\left\{\begin{align} -\dot x_1&=x_2\\ +\f[\def\mat#1#2{\begin{bmatrix}#1\\#2\end{bmatrix}} +\left\{\begin{align}\dot x_1&=x_2\\ \dot x_2&=-\frac kmx_1-\frac cmx_2+\frac1mp(t) \end{align}\right.\tag{1-2}\f] @@ -206,7 +206,7 @@ k_2&=f\left(t_n+\frac h2,x_n+\frac h2k_1\right)\end{align}\tag{3-7}\f] \f[\left\{\begin{align}x_{n+1}&=x_n+h(\lambda_1k_1+\lambda_2k_2)\\k_1&=f(t_n+p_1h,x_n+h(a_{11} k_1+a_{12}k_2))\\k_2&=f(t_n+p_2h,x_n+h(a_{21}k_1+a_{22}k_2))\end{align}\right.\tag{3-8}\f] -令\f$\pmb p=(p_1,p_2)T,\quad\pmb\lambda=(\lambda_1,\lambda_2),\quad +令\f$\pmb p=\mat{p_1}{p_2},\quad\pmb\lambda=(\lambda_1,\lambda_2),\quad R=\begin{bmatrix}a_{11}&a_{12}\\a_{21}&a_{22}\end{bmatrix}\f$,则 \f[\begin{array}{c|c}\pmb p&R\\\hline&\pmb\lambda\end{array}=\begin{array}{c|cc}p_1& @@ -272,7 +272,7 @@ RMVL 的相关类请参考 rm::RungeKutta **解答** -① 数值解 +**① 数值解** 使用中点公式: @@ -395,12 +395,11 @@ l_2&=f_2(t_n+\frac h2,x_{1_n}+\frac h2k_1,x_{2_n}+\frac h2l_1)\end{align}\f] 因此,在\f$t=1\f$时,\f$x\approx(0.587286,-0.219401)^T\f$ -② 精确解 +**② 精确解** 对待求的初值问题写成矩阵形式 -\f[\dot X=\begin{bmatrix}0&2\\-1&-3\end{bmatrix}X+\begin{bmatrix}1\\0 -\end{bmatrix}t,\quad X(0)=\begin{bmatrix}1\\-1\end{bmatrix}\f] +\f[\dot X=\begin{bmatrix}0&2\\-1&-3\end{bmatrix}X+\mat10t,\quad X(0)=\mat1{-1}\f] 由矩阵分析的知识,可以知道,对于形如\f[\dot X=AX+B(t)\tag{a}\f]的矩阵微分方程,其解为 @@ -430,8 +429,7 @@ l_2&=f_2(t_n+\frac h2,x_{1_n}+\frac h2k_1,x_{2_n}+\frac h2l_1)\end{align}\f] 可以算出 -\f[\def\mat#1#2{\begin{bmatrix}#1\\#2\end{bmatrix}} -e^{At}X(0)=\mat00e^{-t}+\mat1{-1}e^{-2t}=\mat1{-1}e^{-2t}\tag{f}\f] +\f[e^{At}X(0)=\mat00e^{-t}+\mat1{-1}e^{-2t}=\mat1{-1}e^{-2t}\tag{f}\f] 同理有 diff --git a/modules/ml/CMakeLists.txt b/modules/ml/CMakeLists.txt index 52705e97..e33e74b3 100644 --- a/modules/ml/CMakeLists.txt +++ b/modules/ml/CMakeLists.txt @@ -10,7 +10,7 @@ rmvl_add_module( if(WITH_ONNXRUNTIME) find_package(Ort) endif() - + set(BUILD_rmvl_ort_INIT ${WITH_ONNXRUNTIME}) rmvl_add_module( From 0cd47dea7488ca33452f7f7a5f5479df103a613e Mon Sep 17 00:00:00 2001 From: zhaoxi <535394140@qq.com> Date: Thu, 7 Mar 2024 13:45:46 +0800 Subject: [PATCH 2/3] =?UTF-8?q?rmvlpara=20=E7=94=9F=E6=88=90=E7=9A=84?= =?UTF-8?q?=E5=A4=B4=E6=96=87=E4=BB=B6=E7=A7=BB=E9=99=A4=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E9=9A=94=E6=97=A5=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmake/templates/para_generator_header.in | 6 ++---- cmake/templates/para_generator_module.in | 6 ++---- cmake/templates/para_generator_source.in | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/cmake/templates/para_generator_header.in b/cmake/templates/para_generator_header.in index 34394ea8..79b4a7bf 100644 --- a/cmake/templates/para_generator_header.in +++ b/cmake/templates/para_generator_header.in @@ -1,11 +1,9 @@ /** * @file @target_name@.@header_ext@ - * @author RoboMaster Vision Community + * @author RMVL Community * @brief @class_name@ module header file (Generated by CMake automatically) - * @version 1.0 - * @date @year@-@month@-@day@ * - * @copyright Copyright @year@ (c), RoboMaster Vision Community + * @copyright Copyright @year@ (c), RMVL Community * */ diff --git a/cmake/templates/para_generator_module.in b/cmake/templates/para_generator_module.in index c6e4fdfb..830c64f3 100644 --- a/cmake/templates/para_generator_module.in +++ b/cmake/templates/para_generator_module.in @@ -1,11 +1,9 @@ /** * @file @module_name@.hpp - * @author RoboMaster Vision Community + * @author RMVL Community * @brief @module_name@ header file (Generated by CMake automatically) - * @version 1.0 - * @date @year@-@month@-@day@ * - * @copyright Copyright @year@ (c), RoboMaster Vision Community + * @copyright Copyright @year@ (c), RMVL Community * */ diff --git a/cmake/templates/para_generator_source.in b/cmake/templates/para_generator_source.in index 25fe3c97..bca9d6fe 100644 --- a/cmake/templates/para_generator_source.in +++ b/cmake/templates/para_generator_source.in @@ -1,11 +1,9 @@ /** * @file @target_name@.cpp - * @author RoboMaster Vision Community + * @author RMVL Community * @brief @class_name@ module source file (Generated by CMake automatically) - * @version 1.0 - * @date @year@-@month@-@day@ * - * @copyright Copyright @year@ (c), RoboMaster Vision Community + * @copyright Copyright @year@ (c), RMVL Community * */ From 56497997126955d2de22a86d09f4145a4554eef5 Mon Sep 17 00:00:00 2001 From: zhaoxi <535394140@qq.com> Date: Thu, 7 Mar 2024 19:32:20 +0800 Subject: [PATCH 3/3] =?UTF-8?q?opcua-2.1=201.=20=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=89=80=E6=9C=89=20C/S=E3=80=81Pub/Sub=20=E7=9A=84=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=B3=84=E6=BC=8F=E9=97=AE=E9=A2=98=202.=20=E6=9E=81?= =?UTF-8?q?=E5=A4=A7=E7=BC=A9=E7=9F=AD=E5=9B=A0=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=AF=BC=E8=87=B4=E7=9A=84=E9=80=9A=E4=BF=A1?= =?UTF-8?q?=E5=BB=B6=E8=BF=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/camera/src/hik_camera/hik_camera.cpp | 11 +- modules/opcua/include/rmvl/opcua/client.hpp | 7 +- .../opcua/include/rmvl/opcua/publisher.hpp | 8 +- modules/opcua/include/rmvl/opcua/server.hpp | 9 +- .../opcua/include/rmvl/opcua/subscriber.hpp | 8 +- modules/opcua/include/rmvl/opcua/variable.hpp | 121 +++++++----------- modules/opcua/param/opcua.para | 6 +- modules/opcua/src/client.cpp | 23 ++-- modules/opcua/src/cvt.hpp | 46 +++++++ modules/opcua/src/data.cpp | 76 ++++++++++- modules/opcua/src/helper.cpp | 16 ++- modules/opcua/src/publisher.cpp | 23 ++-- modules/opcua/src/server.cpp | 32 +++-- modules/opcua/src/subscriber.cpp | 17 ++- modules/opcua/test/test_opcua_client.cpp | 3 +- 15 files changed, 254 insertions(+), 152 deletions(-) create mode 100644 modules/opcua/src/cvt.hpp diff --git a/modules/camera/src/hik_camera/hik_camera.cpp b/modules/camera/src/hik_camera/hik_camera.cpp index cf9c6c69..c315e4ec 100644 --- a/modules/camera/src/hik_camera/hik_camera.cpp +++ b/modules/camera/src/hik_camera/hik_camera.cpp @@ -28,7 +28,8 @@ bool HikCamera::isOpened() const { return _impl->isOpened(); } bool HikCamera::reconnect() { return _impl->reconnect(); } //! MV_CC_PIXEL_CONVERT_PARAM 的初始化变量 -static MV_CC_PIXEL_CONVERT_PARAM MV_CC_PIXEL_CONVERT_PARAM_Init = {0, 0, PixelType_Gvsp_Undefined, nullptr, 0, PixelType_Gvsp_Undefined, nullptr, 0, 0, {0}}; +static const MV_CC_PIXEL_CONVERT_PARAM MV_CC_PIXEL_CONVERT_PARAM_Init = + {0, 0, PixelType_Gvsp_Undefined, nullptr, 0, PixelType_Gvsp_Undefined, nullptr, 0, 0, {0}}; HikCamera::Impl::Impl(CameraConfig init_mode, std::string_view serial) noexcept : _grab_mode(init_mode.grab_mode), _retrieve_mode(init_mode.retrieve_mode), _serial(serial) { _opened = open(); } @@ -145,11 +146,11 @@ static inline cv::ColorConversionCodes pixelType2CVType(MvGvspPixelType pixel_ty bool HikCamera::Impl::retrieve(cv::OutputArray image, RetrieveMode flag) noexcept { // --------------------- 前置信息准备 --------------------- - auto &frame_info = _p_out.stFrameInfo; + const auto &frame_info = _p_out.stFrameInfo; // 当前格式 auto pixel_type = frame_info.enPixelType; // 单通道标志位集合 - std::unordered_set mono_set = + static std::unordered_set mono_set = {PixelType_Gvsp_Mono1p, PixelType_Gvsp_Mono2p, PixelType_Gvsp_Mono4p, PixelType_Gvsp_Mono8, PixelType_Gvsp_Mono8_Signed, PixelType_Gvsp_Mono10, PixelType_Gvsp_Mono10_Packed, @@ -171,7 +172,7 @@ bool HikCamera::Impl::retrieve(cv::OutputArray image, RetrieveMode flag) noexcep // MV_CC_ConvertPixelType if (flag == RetrieveMode::SDK) { - MV_CC_PIXEL_CONVERT_PARAM cvt_param = MV_CC_PIXEL_CONVERT_PARAM_Init; + MV_CC_PIXEL_CONVERT_PARAM cvt_param{MV_CC_PIXEL_CONVERT_PARAM_Init}; cvt_param.nWidth = frame_info.nWidth; // 图像宽 cvt_param.nHeight = frame_info.nHeight; // 图像高 cvt_param.pSrcData = _p_out.pBufAddr; // 输入数据缓存 @@ -221,7 +222,7 @@ bool HikCamera::Impl::read(cv::OutputArray image) noexcept retrieve(image, _retrieve_mode); else { - WARNING_("No data in getting image buffer"); + WARNING_("hik - No data in getting image buffer"); reconnect(); } // 释放图像缓存 diff --git a/modules/opcua/include/rmvl/opcua/client.hpp b/modules/opcua/include/rmvl/opcua/client.hpp index 641c2f64..76cba64e 100644 --- a/modules/opcua/include/rmvl/opcua/client.hpp +++ b/modules/opcua/include/rmvl/opcua/client.hpp @@ -2,8 +2,8 @@ * @file client.hpp * @author zhaoxi (535394140@qq.com) * @brief OPC UA 客户端 - * @version 1.0 - * @date 2023-10-29 + * @version 2.1 + * @date 2024-03-07 * * @copyright Copyright 2023 (c), zhaoxi * @@ -60,6 +60,9 @@ class Client /****************************** 功能配置 ******************************/ + //! 是否成功创建客户端 + inline bool ok() const { return _client != nullptr; } + /** * @brief 在网络上监听并处理到达的异步响应,同时进行内部维护、安全通道的更新和订阅管理 * @brief diff --git a/modules/opcua/include/rmvl/opcua/publisher.hpp b/modules/opcua/include/rmvl/opcua/publisher.hpp index cf5958ce..8343af19 100644 --- a/modules/opcua/include/rmvl/opcua/publisher.hpp +++ b/modules/opcua/include/rmvl/opcua/publisher.hpp @@ -2,8 +2,8 @@ * @file publisher.hpp * @author zhaoxi (535394140@qq.com) * @brief OPC UA 发布者 - * @version 1.0 - * @date 2023-11-30 + * @version 2.1 + * @date 2024-03-07 * * @copyright Copyright 2023 (c), zhaoxi * @@ -24,11 +24,11 @@ namespace rm /** * @brief 待发布的数据集 (PDS) * @brief - * - 包含变量节点发布的名称及其对应的 ID + * - 包含变量节点发布的字段名称及其对应的 ID */ struct PublishedDataSet { - std::string name; //!< 变量节点发布的名称 + std::string name; //!< 变量节点发布的字段名称 UA_NodeId node_id; //!< 变量节点 ID }; diff --git a/modules/opcua/include/rmvl/opcua/server.hpp b/modules/opcua/include/rmvl/opcua/server.hpp index 509a60cc..7d78be8f 100644 --- a/modules/opcua/include/rmvl/opcua/server.hpp +++ b/modules/opcua/include/rmvl/opcua/server.hpp @@ -2,8 +2,8 @@ * @file server.hpp * @author zhaoxi (535394140@qq.com) * @brief OPC UA 服务器 - * @version 1.0 - * @date 2023-10-21 + * @version 2.1 + * @date 2024-03-07 * * @copyright Copyright 2023 (c), zhaoxi * @@ -84,7 +84,7 @@ class Server /** * @brief 阻塞 * @brief - * - 调用方线程阻塞,直到服务器停止后才继续运行 + * - 调用方线程阻塞,直到服务器执行 `stop()` 或以外停止后才继续运行 */ inline void join() { _run.join(); } @@ -144,7 +144,8 @@ class Server * @brief * - 数据源变量节点会把每次 IO 都绑定到各自的回调函数中,即可以重定向到一个实际的物理过程中,从而跟服务器本身的数据读写脱离关系 * - * @param[in] val `rm::Variable` 表示的变量 + * @param[in] val `rm::Variable` 表示的变量,仅取 `browse_name`、`description`、`display_name`、`access_level` + * 字段,以及对应的变量类型节点 * @param[in] on_read 重定向的读取回调函数 * @param[in] on_write 重定向的写入回调函数 * @param[in] parent_id 指定父节点的 `UA_NodeId`,默认为 `rm::nodeObjectsFolder` diff --git a/modules/opcua/include/rmvl/opcua/subscriber.hpp b/modules/opcua/include/rmvl/opcua/subscriber.hpp index 1432752d..c8e0a95b 100644 --- a/modules/opcua/include/rmvl/opcua/subscriber.hpp +++ b/modules/opcua/include/rmvl/opcua/subscriber.hpp @@ -2,8 +2,8 @@ * @file subscriber.hpp * @author zhaoxi (535394140@qq.com) * @brief OPC UA 订阅者 - * @version 1.0 - * @date 2023-11-30 + * @version 2.1 + * @date 2024-03-07 * * @copyright Copyright 2023 (c), zhaoxi * @@ -54,12 +54,12 @@ struct FieldMetaData final * @brief * - 变量的 `Variable::getDataType()` 用于设置字段类型 * @brief - * - 变量的 `Variable::getValueRank()` 用于设置字段 ValueRank + * - 变量的 `Variable::size()` 用于辅助设置字段 ValueRank * * @param[in] val 变量,可参考 @ref rm::Variable * @return FieldMetaData 字段元数据 */ - FieldMetaData(const Variable &val) : name(val.browse_name), type(val.getDataType()), value_rank(val.getValueRank()) {} + FieldMetaData(const Variable &val) : name(val.browse_name), type(val.getDataType()), value_rank(val.size() == 1 ? UA_VALUERANK_SCALAR : 1) {} }; /** diff --git a/modules/opcua/include/rmvl/opcua/variable.hpp b/modules/opcua/include/rmvl/opcua/variable.hpp index dc1d91b1..cecb561f 100644 --- a/modules/opcua/include/rmvl/opcua/variable.hpp +++ b/modules/opcua/include/rmvl/opcua/variable.hpp @@ -2,8 +2,8 @@ * @file variable.hpp * @author zhaoxi (535394140@qq.com) * @brief 变量(类型) - * @version 1.0 - * @date 2023-10-20 + * @version 2.1 + * @date 2024-03-07 * * @copyright Copyright 2023 (c), zhaoxi * @@ -55,8 +55,8 @@ class VariableType final std::any _value; //! 数据类型 UA_TypeFlag _data_type{}; - //! 维数 - UA_UInt32 _dims{}; + //! 数据大小 + UA_UInt32 _size{}; public: /** @@ -65,7 +65,7 @@ class VariableType final * @param[in] str 字面量字符串 */ template - VariableType(const char (&str)[N]) : _value(str), _data_type(typeflag.at(typeid(const char *))), _dims(1) {} + VariableType(const char (&str)[N]) : _value(str), _data_type(typeflag.at(typeid(const char *))), _size(1) {} /** * @brief 单值构造,设置默认值 @@ -74,7 +74,7 @@ class VariableType final * @param[in] val 标量、数量值 */ template || std::is_same_v>> - VariableType(Tp &&val) : _value(val), _data_type(typeflag.at(typeid(Tp))), _dims(1) {} + VariableType(Tp &&val) : _value(val), _data_type(typeflag.at(typeid(Tp))), _size(1) {} /** * @brief 列表构造,设置默认值 @@ -83,13 +83,14 @@ class VariableType final * @param[in] arr 列表、数组 */ template && !std::is_same_v>> - VariableType(const std::vector &arr) : _value(arr), _data_type(typeflag.at(typeid(Tp))), _dims(arr.size()) {} + VariableType(const std::vector &arr) : _value(arr), _data_type(typeflag.at(typeid(Tp))), _size(arr.size()) {} VariableType(const VariableType &val) : browse_name(val.browse_name), display_name(val.display_name), description(val.description), - _value(val._value), _data_type(val._data_type), _dims(val._dims) {} + _value(val._value), _data_type(val._data_type), _size(val._size) {} - VariableType(VariableType &&val) : browse_name(std::move(val.browse_name)), display_name(std::move(val.display_name)), description(std::move(val.description)), - _value(std::move(val._value)), _data_type(std::exchange(val._data_type, 0)), _dims(std::exchange(val._dims, 0)) {} + VariableType(VariableType &&val) : browse_name(std::move(val.browse_name)), display_name(std::move(val.display_name)), + description(std::move(val.description)), _value(std::move(val._value)), + _data_type(std::exchange(val._data_type, 0)), _size(std::exchange(val._size, 0)) {} VariableType &operator=(const VariableType &val); @@ -111,16 +112,8 @@ class VariableType final //! 获取数据类型 inline UA_TypeFlag getDataType() const { return _data_type; } - //! 获取数组维度 @note 单独的数则返回 `1`,未初始化则返回 `0` - inline const UA_UInt32 &size() const { return _dims; } - - /** - * @brief 获取默认数据的阶数、秩 - * - * @return 阶数 - * @retval `UA_VALUERANK_SCALAR` 或 `1` - */ - inline int getValueRank() const { return _dims == 1 ? UA_VALUERANK_SCALAR : 1; } + //! 获取大小 @note 未初始化则返回 `0` + inline UA_UInt32 size() const { return _size; } }; //! OPC UA 变量 @@ -160,8 +153,8 @@ class Variable final std::any _value; //! 数据类型 UA_TypeFlag _data_type{}; - //! 维数 - UA_UInt32 _dims{}; + //! 数据大小 + UA_UInt32 _size{}; //! 访问性 uint8_t _access_level{}; @@ -174,7 +167,7 @@ class Variable final * @param[in] str 字面量字符串 */ template - Variable(const char (&str)[N]) : _value(str), _data_type(typeflag.at(typeid(const char *))), _dims(1), _access_level(3U) {} + Variable(const char (&str)[N]) : _value(str), _data_type(typeflag.at(typeid(const char *))), _size(1), _access_level(3U) {} /** * @brief 单值构造 @@ -183,7 +176,7 @@ class Variable final * @param[in] val 标量、数量值 */ template || std::is_same_v>> - Variable(const Tp &val) : _value(val), _data_type(typeflag.at(typeid(Tp))), _dims(1), _access_level(3U) {} + Variable(const Tp &val) : _value(val), _data_type(typeflag.at(typeid(Tp))), _size(1), _access_level(3U) {} /** * @brief 列表构造 @@ -192,7 +185,7 @@ class Variable final * @param[in] arr 列表、数组 */ template && !std::is_same_v>> - Variable(const std::vector &arr) : _value(arr), _data_type(typeflag.at(typeid(Tp))), _dims(arr.size()), _access_level(3U) {} + Variable(const std::vector &arr) : _value(arr), _data_type(typeflag.at(typeid(Tp))), _size(arr.size()), _access_level(3U) {} /** * @brief 从变量类型构造新的变量节点 @@ -200,20 +193,39 @@ class Variable final * @param[in] vtype 既存的待作为变量节点类型信息的使用 `rm::VariableType` 表示的变量类型 */ explicit Variable(VariableType &vtype) : _type(&vtype), _value(vtype.data()), _data_type(vtype.getDataType()), - _dims(vtype.size()), _access_level(3U) {} + _size(vtype.size()), _access_level(3U) {} Variable(const Variable &val) : browse_name(val.browse_name), display_name(val.display_name), description(val.description), _type(val._type), - _value(val._value), _data_type(val._data_type), _dims(val._dims), _access_level(val._access_level) {} + _value(val._value), _data_type(val._data_type), _size(val._size), _access_level(val._access_level) {} - Variable(Variable &&val) : browse_name(std::move(val.browse_name)), display_name(std::move(val.display_name)), description(std::move(val.description)), _type(std::exchange(val._type, nullptr)), - _value(std::move(val._value)), _data_type(std::exchange(val._data_type, 0)), _dims(std::exchange(val._dims, 0)), _access_level(std::exchange(val._access_level, 0)) {} + Variable(Variable &&val) : browse_name(std::move(val.browse_name)), display_name(std::move(val.display_name)), description(std::move(val.description)), + _type(std::exchange(val._type, nullptr)), _value(std::move(val._value)), _data_type(std::exchange(val._data_type, 0)), + _size(std::exchange(val._size, 0)), _access_level(std::exchange(val._access_level, 0)) {} Variable &operator=(const Variable &val); Variable &operator=(Variable &&val); + /** + * @brief 比较两个变量是否相等,当且仅当两个变量的数据类型、维数、数据值均相等时返回 + * `true`,而不考虑变量的名称、描述等信息 + * + * @param[in] val 另一个变量 + * @return 是否相等 + */ + bool operator==(const Variable &val) const; + + /** + * @brief 比较两个变量是否不等 + * @see `rm::Variable::operator==` + * + * @param[in] val 另一个变量 + * @return 是否不等 + */ + inline bool operator!=(const Variable &val) const { return !(*this == val); } + //! 判断变量节点是否为空 - constexpr bool empty() const { return _dims == 0; } + constexpr bool empty() const { return _size == 0; } /** * @brief 将变量节点转化为指定类型的数据 @@ -248,8 +260,8 @@ class Variable final //! 获取形如 `UA_TYPES_` 的数据类型 inline UA_TypeFlag getDataType() const { return _data_type; } - //! 获取数组维度指针 @note 单独的数则返回 `1`,未初始化则返回 `0` - inline const UA_UInt32 &size() const { return _dims; } + //! 获取大小 @note 未初始化则返回 `0` + inline UA_UInt32 size() const { return _size; } /** * @brief 设置访问性 @@ -260,53 +272,8 @@ class Variable final //! 获取访问性 inline uint8_t getAccessLevel() const { return _access_level; } - - /** - * @brief 获取数据阶数、秩 - * - * @return 数据阶数 - * @retval `UA_VALUERANK_SCALAR` 或 `1` - */ - inline int getValueRank() const { return _dims == 1 ? UA_VALUERANK_SCALAR : 1; } }; -//! @} opcua - -namespace helper -{ - -/** - * @brief `rm::Variable` 转化为 `UA_Variant` - * - * @warning 此方法一般不直接使用 - * @param[in] val `rm::Variable` 表示的变量 - * @return `UA_Variant` 表示变量节点的内置数据 - */ -UA_Variant cvtVariable(const Variable &val); - -/** - * @brief `UA_Variant` 转化为 `rm::Variable` - * - * @warning 此方法一般不直接使用 - * @param[in] p_val `UA_Variant` 表示的变量 - * @return 用 `rm::Variable` 表示的变量节点 - */ -Variable cvtVariable(const UA_Variant &p_val); - -/** - * @brief `rm::VariableType` 转化为 `UA_Variant` - * - * @warning 此方法一般不直接使用 - * @param[in] vtype `rm::VariableType` 表示的变量类型 - * @return 用 `UA_Variant` 表示的变量类型节点的内置数据 - */ -UA_Variant cvtVariable(const VariableType &vtype); - -} // namespace helper - -//! @addtogroup opcua -//! @{ - /** * @brief 创建变量类型,BrowseName、DisplayName、Description 均为变量类型的名称 * diff --git a/modules/opcua/param/opcua.para b/modules/opcua/param/opcua.para index 028fc8f1..3f36ff08 100644 --- a/modules/opcua/param/opcua.para +++ b/modules/opcua/param/opcua.para @@ -1,9 +1,9 @@ uint32_t SPIN_TIMEOUT = 500 # 服务器超时响应的时间,单位 (ms) -double SAMPLING_INTERVAL = 50 # 服务器监视变量的采样速度,单位 (ms) -double PUBLISHING_INTERVAL = 100 # 服务器尝试发布数据变更的期望时间间隔,若数据未变更则不会发布,单位 (ms) +double SAMPLING_INTERVAL = 2 # 服务器监视变量的采样速度,单位 (ms),不得小于 2ms +double PUBLISHING_INTERVAL = 2 # 服务器尝试发布数据变更的期望时间间隔,若数据未变更则不会发布,单位 (ms),不得小于 2ms uint32_t LIFETIME_COUNT = 100 # 在没有发布任何消息的情况下,订阅请求所期望的能够保持活动状态的最大发布周期数 -uint32_t MAX_KEEPALIVE_COUNT = 10 # 在没有任何通知的情况下,订阅请求所期望的服务器应该发送的最大 “保活” 消息数 +uint32_t MAX_KEEPALIVE_COUNT = 50 # 在没有任何通知的情况下,订阅请求所期望的服务器应该发送的最大 “保活” 消息数 uint32_t MAX_NOTIFICATIONS = 100 # 服务器应该发送的期望的最大通知数(通知是服务器向客户端报告订阅的变化的方式) uint8_t PRIORITY = 0 # 订阅请求的优先级 diff --git a/modules/opcua/src/client.cpp b/modules/opcua/src/client.cpp index 45442e54..b76a61d3 100644 --- a/modules/opcua/src/client.cpp +++ b/modules/opcua/src/client.cpp @@ -17,6 +17,8 @@ #include "rmvl/opcua/client.hpp" #include "rmvlpara/opcua.hpp" +#include "cvt.hpp" + namespace rm { @@ -46,6 +48,8 @@ Client::Client(std::string_view address, UserConfig usr) Client::~Client() { + if (_client == nullptr) + return; auto status = UA_Client_disconnect(_client); if (status != UA_STATUSCODE_GOOD) UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Failed to disconnect the client"); @@ -75,22 +79,21 @@ void Client::spinOnce() { UA_Client_run_iterate(_client, para::opcua_param.SPIN_ Variable Client::read(const UA_NodeId &node) { - UA_Variant variant; - - UA_StatusCode status = UA_Client_readValueAttribute(_client, node, &variant); + UA_Variant p_val; + UA_Variant_init(&p_val); + UA_StatusCode status = UA_Client_readValueAttribute(_client, node, &p_val); if (status != UA_STATUSCODE_GOOD) - { - UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Failed to read value from the specific node, error: %s", UA_StatusCode_name(status)); return {}; - } - // 变量节点信息 - return helper::cvtVariable(variant); + Variable retval = helper::cvtVariable(p_val); + UA_Variant_clear(&p_val); + return retval; } bool Client::write(const UA_NodeId &node, const Variable &val) { UA_Variant new_variant = helper::cvtVariable(val); auto status = UA_Client_writeValueAttribute(_client, node, &new_variant); + UA_Variant_clear(&new_variant); if (status != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Failed to write value to the specific node, error: %s", UA_StatusCode_name(status)); @@ -118,6 +121,8 @@ bool Client::call(const UA_NodeId &obj_node, const std::string &name, const std: // 调用方法 UA_StatusCode status = UA_Client_call(_client, obj_node, method_node, input_variants.size(), input_variants.data(), &output_size, &output_variants); + for (auto &input_variant : input_variants) + UA_Variant_clear(&input_variant); if (status != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Failed to call the method, node id: %d, error code: %s", @@ -127,6 +132,8 @@ bool Client::call(const UA_NodeId &obj_node, const std::string &name, const std: outputs.reserve(output_size); for (size_t i = 0; i < output_size; ++i) outputs.push_back(helper::cvtVariable(output_variants[i])); + for (size_t i = 0; i < output_size; ++i) + UA_Variant_clear(&output_variants[i]); return true; } diff --git a/modules/opcua/src/cvt.hpp b/modules/opcua/src/cvt.hpp new file mode 100644 index 00000000..dc62e650 --- /dev/null +++ b/modules/opcua/src/cvt.hpp @@ -0,0 +1,46 @@ +/** + * @file cvt.hpp + * @author zhaoxi (535394140@qq.com) + * @brief 变量、变量类型与 UA_Variant 之间的转换 + * @version 1.0 + * @date 2024-03-07 + * + * @copyright Copyright 2023 (c), zhaoxi + * + */ + +#pragma once + +#include "rmvl/opcua/variable.hpp" + +namespace rm::helper +{ + +/** + * @brief `rm::Variable` 转化为 `UA_Variant` + * + * @warning 此方法一般不直接使用 + * @param[in] val `rm::Variable` 表示的变量 + * @return `UA_Variant` 表示变量节点的内置数据 + */ +UA_Variant cvtVariable(const Variable &val); + +/** + * @brief `UA_Variant` 转化为 `rm::Variable` + * + * @warning 此方法一般不直接使用 + * @param[in] p_val `UA_Variant` 表示的变量 + * @return 用 `rm::Variable` 表示的变量节点 + */ +Variable cvtVariable(const UA_Variant &p_val); + +/** + * @brief `rm::VariableType` 转化为 `UA_Variant` + * + * @warning 此方法一般不直接使用 + * @param[in] vtype `rm::VariableType` 表示的变量类型 + * @return 用 `UA_Variant` 表示的变量类型节点的内置数据 + */ +UA_Variant cvtVariable(const VariableType &vtype); + +} // namespace rm::helper diff --git a/modules/opcua/src/data.cpp b/modules/opcua/src/data.cpp index c9bc0a39..9825b242 100644 --- a/modules/opcua/src/data.cpp +++ b/modules/opcua/src/data.cpp @@ -23,7 +23,7 @@ VariableType &VariableType::operator=(const VariableType &val) description = val.description; _value = val._value; _data_type = val._data_type; - _dims = val._dims; + _size = val._size; return *this; } @@ -34,7 +34,7 @@ VariableType &VariableType::operator=(VariableType &&val) description = std::move(val.description); _value = std::move(val._value); _data_type = std::exchange(val._data_type, 0); - _dims = std::exchange(val._dims, 0); + _size = std::exchange(val._size, 0); return *this; } @@ -46,7 +46,7 @@ Variable &Variable::operator=(const Variable &val) _type = val._type; _value = val._value; _data_type = val._data_type; - _dims = val._dims; + _size = val._size; _access_level = val._access_level; return *this; } @@ -59,11 +59,79 @@ Variable &Variable::operator=(Variable &&val) _type = std::exchange(val._type, nullptr); _value = std::move(val._value); _data_type = std::exchange(val._data_type, 0); - _dims = std::exchange(val._dims, 0); + _size = std::exchange(val._size, 0); _access_level = std::exchange(val._access_level, 0); return *this; } +bool Variable::operator==(const Variable &val) const +{ + if (_data_type != val._data_type) + return false; + if (_size != val._size) + return false; + if (_size == 1) + { + switch (_data_type) + { + case UA_TYPES_BOOLEAN: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_SBYTE: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_BYTE: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_INT16: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_UINT16: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_INT32: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_UINT32: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_INT64: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_UINT64: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_FLOAT: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_DOUBLE: + return std::any_cast(_value) == std::any_cast(val._value); + case UA_TYPES_STRING: + return std::any_cast(_value) == std::any_cast(val._value); + default: + return false; + } + } + else + { + switch (_data_type) + { + case UA_TYPES_SBYTE: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_BYTE: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_INT16: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_UINT16: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_INT32: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_UINT32: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_INT64: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_UINT64: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_FLOAT: + return std::any_cast>(_value) == std::any_cast>(val._value); + case UA_TYPES_DOUBLE: + return std::any_cast>(_value) == std::any_cast>(val._value); + default: + return false; + } + } +} + Argument &Argument::operator=(const Argument &val) { name = val.name; diff --git a/modules/opcua/src/helper.cpp b/modules/opcua/src/helper.cpp index 418b8c65..a6652f23 100644 --- a/modules/opcua/src/helper.cpp +++ b/modules/opcua/src/helper.cpp @@ -14,7 +14,8 @@ #include #include "rmvl/opcua/method.hpp" -#include "rmvl/opcua/variable.hpp" + +#include "cvt.hpp" UA_NodeId operator|(UA_NodeId origin, rm::FindNodeInServer &&fnis) { @@ -66,13 +67,12 @@ UA_Variant cvtVariable(const Variable &val) const std::any &data = val.data(); UA_Variant p_val; - UA_Variant_init(&p_val); if (val.size() == 1) { switch (val.getDataType()) { case UA_TYPES_STRING: { - UA_String str = UA_STRING_ALLOC(std::any_cast(data)); + UA_String str = UA_STRING(const_cast(std::any_cast(data))); UA_Variant_setScalarCopy(&p_val, &str, &UA_TYPES[UA_TYPES_STRING]); } break; @@ -199,7 +199,8 @@ UA_Variant cvtVariable(const Variable &val) } p_val.arrayLength = val.size(); p_val.arrayDimensionsSize = 1; - p_val.arrayDimensions = &const_cast(val.size()); + p_val.arrayDimensions = reinterpret_cast(UA_malloc(sizeof(UA_UInt32))); + *p_val.arrayDimensions = val.size(); } return p_val; } @@ -410,7 +411,8 @@ UA_Variant cvtVariable(const VariableType &vtype) } p_val.arrayLength = vtype.size(); p_val.arrayDimensionsSize = 1; - p_val.arrayDimensions = &const_cast(vtype.size()); + p_val.arrayDimensions = reinterpret_cast(UA_malloc(sizeof(UA_UInt32))); + *p_val.arrayDimensions = vtype.size(); } return p_val; } @@ -419,8 +421,8 @@ UA_Argument cvtArgument(const Argument &arg) { UA_Argument argument; UA_Argument_init(&argument); - argument.name = UA_STRING_ALLOC(arg.name.c_str()); - argument.description = UA_LOCALIZEDTEXT_ALLOC(zh_CN(), arg.name.c_str()); + argument.name = UA_STRING(to_char(arg.name)); + argument.description = UA_LOCALIZEDTEXT(zh_CN(), to_char(arg.name)); argument.dataType = UA_TYPES[arg.data_type].typeId; RMVL_Assert(arg.dims); if (arg.dims == 1) diff --git a/modules/opcua/src/publisher.cpp b/modules/opcua/src/publisher.cpp index 08a2645f..fdd23db3 100644 --- a/modules/opcua/src/publisher.cpp +++ b/modules/opcua/src/publisher.cpp @@ -38,11 +38,13 @@ Publisher::Publisher(const std::string &pub_name, const s //////////////////// 添加连接配置 //////////////////// UA_ServerConfig_addPubSubTransportLayer(UA_Server_getConfig(_server), UA_PubSubTransportLayerUDPMP()); UA_PubSubConnectionConfig connect_config{}; - connect_config.name = UA_String_fromChars((_name + "Connection").c_str()); - connect_config.transportProfileUri = UA_String_fromChars("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"); + std::string cn_name_str = _name + "Connection"; + connect_config.name = UA_STRING(helper::to_char(cn_name_str)); + connect_config.transportProfileUri = UA_STRING(const_cast("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp")); connect_config.enabled = UA_TRUE; - UA_NetworkAddressUrlDataType address_url{UA_STRING_NULL, UA_String_fromChars((address + ":" + std::to_string(port)).c_str())}; - UA_Variant_setScalarCopy(&connect_config.address, &address_url, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); + std::string url_str = address + ":" + std::to_string(port); + UA_NetworkAddressUrlDataType address_url{UA_STRING_NULL, UA_STRING(helper::to_char(url_str))}; + UA_Variant_setScalar(&connect_config.address, &address_url, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); // 用哈希值作为发布者 ID connect_config.publisherId.numeric = _strhash(_name + "Connection") % 0x8000000u; auto status = UA_Server_addPubSubConnection(_server, &connect_config, &_connection_id); @@ -54,7 +56,8 @@ Publisher::Publisher(const std::string &pub_name, const s //////////// 添加 PublishedDataSet (PDS) ///////////// UA_PublishedDataSetConfig pds_config{}; pds_config.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS; - pds_config.name = UA_String_fromChars((_name + "PublishedDataSet").c_str()); + std::string pds_name_str = _name + "PublishedDataSet"; + pds_config.name = UA_STRING(helper::to_char(pds_name_str)); auto pds_status = UA_Server_addPublishedDataSet(_server, &pds_config, &_pds_id); if (pds_status.addResult != UA_STATUSCODE_GOOD) { @@ -64,11 +67,11 @@ Publisher::Publisher(const std::string &pub_name, const s } } -static UA_DataSetFieldConfig getPDS(const PublishedDataSet &pd) +static inline UA_DataSetFieldConfig getPDS(const PublishedDataSet &pd) { UA_DataSetFieldConfig dsf_config{}; dsf_config.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE; - dsf_config.field.variable.fieldNameAlias = UA_String_fromChars(pd.name.c_str()); + dsf_config.field.variable.fieldNameAlias = UA_STRING(helper::to_char(pd.name)); dsf_config.field.variable.promotedField = false; dsf_config.field.variable.publishParameters.publishedVariable = pd.node_id; dsf_config.field.variable.publishParameters.attributeId = UA_ATTRIBUTEID_VALUE; @@ -99,7 +102,8 @@ bool Publisher::publish(const std::vector::publish(const std::vector &users) UA_ServerConfig *config = UA_Server_getConfig(_server); UA_ServerConfig_setMinimal(config, port, nullptr); - + config->samplingIntervalLimits.min = 2.0; + config->publishingIntervalLimits.min = 2.0; if (!users.empty()) { std::vector usr_passwd; @@ -53,8 +56,7 @@ Server::Server(uint16_t port, const std::vector &users) } } -Server::Server(ServerUserConfig on_config, uint16_t port, const std::vector &users) - : Server(port, users) // 委托构造 +Server::Server(ServerUserConfig on_config, uint16_t port, const std::vector &users) : Server(port, users) { if (on_config != nullptr) on_config(_server); @@ -84,7 +86,7 @@ UA_NodeId Server::addVariableTypeNode(const VariableType &vtype) // 设置属性 attr.value = variant; attr.dataType = variant.type->typeId; - attr.valueRank = vtype.getValueRank(); + attr.valueRank = vtype.size() == 1 ? UA_VALUERANK_SCALAR : 1; if (attr.valueRank != UA_VALUERANK_SCALAR) { attr.arrayDimensionsSize = variant.arrayDimensionsSize; @@ -97,6 +99,7 @@ UA_NodeId Server::addVariableTypeNode(const VariableType &vtype) _server, UA_NODEID_NULL, nodeBaseDataVariableType, nodeHasSubtype, UA_QUALIFIEDNAME(1, helper::to_char(vtype.browse_name)), UA_NODEID_NULL, attr, nullptr, &retval); + UA_Variant_clear(&variant); if (status != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to add variable type node: %s", UA_StatusCode_name(status)); @@ -114,7 +117,7 @@ UA_NodeId Server::addVariableNode(const Variable &val, const UA_NodeId &parent_i attr.value = variant; attr.dataType = variant.type->typeId; attr.accessLevel = val.getAccessLevel(); - attr.valueRank = val.getValueRank(); + attr.valueRank = val.size() == 1 ? UA_VALUERANK_SCALAR : 1; if (attr.valueRank != UA_VALUERANK_SCALAR) { attr.arrayDimensionsSize = variant.arrayDimensionsSize; @@ -146,25 +149,27 @@ UA_NodeId Server::addVariableNode(const Variable &val, const UA_NodeId &parent_i UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to add variable node: %s", UA_StatusCode_name(status)); return UA_NODEID_NULL; } + UA_Variant_clear(&variant); return retval; } Variable Server::read(const UA_NodeId &node) { UA_Variant p_val; + UA_Variant_init(&p_val); auto status = UA_Server_readValue(_server, node, &p_val); if (status != UA_STATUSCODE_GOOD) - { - UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to read variable: %s", UA_StatusCode_name(status)); return {}; - } - return helper::cvtVariable(p_val); + Variable retval = helper::cvtVariable(p_val); + UA_Variant_clear(&p_val); + return retval; } bool Server::write(const UA_NodeId &node, const Variable &val) { auto variant = helper::cvtVariable(val); auto status = UA_Server_writeValue(_server, node, variant); + UA_Variant_clear(&variant); if (status != UA_STATUSCODE_GOOD) UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to write variable, error code: %s", UA_StatusCode_name(status)); return status == UA_STATUSCODE_GOOD; @@ -186,15 +191,7 @@ UA_NodeId Server::addDataSourceVariableNode(const Variable &val, DataSourceRead UA_VariableAttributes attr = UA_VariableAttributes_default; UA_Variant variant = helper::cvtVariable(val); // 设置属性 - attr.value = variant; - attr.dataType = variant.type->typeId; attr.accessLevel = val.getAccessLevel(); - attr.valueRank = val.getValueRank(); - if (attr.valueRank != UA_VALUERANK_SCALAR) - { - attr.arrayDimensionsSize = variant.arrayDimensionsSize; - attr.arrayDimensions = variant.arrayDimensions; - } attr.displayName = UA_LOCALIZEDTEXT(helper::en_US(), helper::to_char(val.display_name)); attr.description = UA_LOCALIZEDTEXT(helper::zh_CN(), helper::to_char(val.description)); // 获取变量节点的变量类型节点 @@ -218,6 +215,7 @@ UA_NodeId Server::addDataSourceVariableNode(const Variable &val, DataSourceRead auto status = UA_Server_addDataSourceVariableNode( _server, UA_NODEID_NULL, parent_id, nodeOrganizes, UA_QUALIFIEDNAME(1, helper::to_char(val.browse_name)), type_id, attr, data_source, nullptr, &retval); + UA_Variant_clear(&variant); if (status != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to add data source variable node: %s", UA_StatusCode_name(status)); diff --git a/modules/opcua/src/subscriber.cpp b/modules/opcua/src/subscriber.cpp index 9775ca1a..6475221c 100644 --- a/modules/opcua/src/subscriber.cpp +++ b/modules/opcua/src/subscriber.cpp @@ -35,10 +35,11 @@ Subscriber::Subscriber(const std::string &sub_name, const //////////////////// 添加连接配置 //////////////////// UA_ServerConfig_addPubSubTransportLayer(UA_Server_getConfig(_server), UA_PubSubTransportLayerUDPMP()); UA_PubSubConnectionConfig connect_config{}; - connect_config.name = UA_String_fromChars((_name + "Connection").c_str()); - connect_config.transportProfileUri = UA_String_fromChars("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"); + std::string cn_name_str = _name + "Connection"; + connect_config.name = UA_STRING(helper::to_char(cn_name_str)); + connect_config.transportProfileUri = UA_STRING(const_cast("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp")); connect_config.enabled = UA_TRUE; - UA_NetworkAddressUrlDataType address_url{UA_STRING_NULL, UA_String_fromChars(address.c_str())}; + UA_NetworkAddressUrlDataType address_url{UA_STRING_NULL, UA_STRING(helper::to_char(address))}; UA_Variant_setScalarCopy(&connect_config.address, &address_url, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); connect_config.publisherId.numeric = UA_UInt32_random(); auto status = UA_Server_addPubSubConnection(_server, &connect_config, &_connection_id); @@ -53,7 +54,8 @@ std::vector Subscriber::subscribe(const std::s { //////////////// 添加 ReaderGroup (RG) /////////////// UA_ReaderGroupConfig rg_config{}; - rg_config.name = UA_String_fromChars((pub_name + "ReaderGroup").c_str()); + std::string pub_name_str = pub_name + "ReaderGroup"; + rg_config.name = UA_STRING(helper::to_char(pub_name_str)); auto status = UA_Server_addReaderGroup(_server, _connection_id, &rg_config, &_rg_id); if (status != UA_STATUSCODE_GOOD) { @@ -70,7 +72,8 @@ std::vector Subscriber::subscribe(const std::s ////////////// 添加 DataSetReader (DSR) ////////////// UA_DataSetReaderConfig dsr_config{}; - dsr_config.name = UA_String_fromChars((pub_name + "DataSetWriter").c_str()); + std::string dsr_name = pub_name + "DataSetReader"; + dsr_config.name = UA_STRING(helper::to_char(dsr_name)); UA_UInt32 publisher_id = _strhash(pub_name + "Connection") % 0x8000000u; UA_Variant_setScalar(&dsr_config.publisherId, &publisher_id, &UA_TYPES[UA_TYPES_UINT16]); dsr_config.writerGroupId = _strhash(pub_name + "WriterGroup") % 0x8000u; @@ -78,13 +81,13 @@ std::vector Subscriber::subscribe(const std::s // 设置数 DSR 中的元数据配置 std::string dataset_name = _name + "DataSetMetaData"; - dsr_config.dataSetMetaData.name = UA_String_fromChars(dataset_name.c_str()); + dsr_config.dataSetMetaData.name = UA_STRING(helper::to_char(dataset_name)); std::vector raw_fields(fields.size()); for (size_t i = 0; i < fields.size(); i++) { UA_NodeId_copy(&UA_TYPES[fields[i].type].typeId, &raw_fields[i].dataType); raw_fields[i].builtInType = typeflag_ns0[fields[i].type]; - raw_fields[i].name = UA_String_fromChars(fields[i].name.c_str()); + raw_fields[i].name = UA_STRING(helper::to_char(fields[i].name)); raw_fields[i].description = UA_LOCALIZEDTEXT(helper::zh_CN(), helper::to_char(fields[i].name)); raw_fields[i].valueRank = fields[i].value_rank; } diff --git a/modules/opcua/test/test_opcua_client.cpp b/modules/opcua/test/test_opcua_client.cpp index 40ee311c..40b46f6c 100644 --- a/modules/opcua/test/test_opcua_client.cpp +++ b/modules/opcua/test/test_opcua_client.cpp @@ -50,7 +50,7 @@ void setSvr(rm::Server &svr) svr.addMethodNode(method); // 添加对象节点,包含字符串变量和乘法方法 svr.start(); - std::this_thread::sleep_for(std::chrono::milliseconds(5)); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); } // 路径搜索 @@ -122,6 +122,7 @@ TEST(OPC_UA_ClientTest, variable_monitor) EXPECT_TRUE(client.monitor(node_id, onChange, 5)); // 数据更新 client.write(node_id, 66); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); client.spinOnce(); EXPECT_EQ(type_name, "Int32"); EXPECT_EQ(receive_data, 66);