通过RPC接口连接用户服务
概述:在插件开发过程中
- 在插件sdk中可能并没有满足用户使用需求的一些特殊的服务接口。这时候我们可以在本地实现这种接口,然后通过RPC接口实现在
aubo_scop
脚本系统中也可以使用该接口服务。- 可能我们已经在本地完成了需要的各类服务的接口实现,如果使用
aubo_caps
系统重新实现会耗费大量的人力物力精力,这时候我们便可以使用aubo_scope
脚本系统提供的RPC接口来远程调用我们在aubo_caps
插件系统外部实现的各类服务。- 注: 本事例的服务端的端口号为8988,在代码里给出的,如果有需要可以设置界面,让用户设置端口号,然后再相应的地方读取用户设好的值即可。
示例工程:http://git.aubo-robotics.cn:8001/cwq/comnnect_to_user_services_by_json_rpc
示例工程的使用:使用
README
中的命令将其打包,然后参考https://docs.aubo-robotics.cn/application_notes/07-plugin_load_unload/将其加载到`aubo_scope`
下面,我将使用建立两个工程来演示整个过程,其中user_service
是我们的用户服务,json_rpc_test
是我们的插件系统。
调整代码结构
在插件开发过程中,建议将用户服务与插件系统的代码分开进行管理。这样代码结构清晰易于维护。
如图:我们将用户服务作为子工程的方式链接到插件系统
在插件系统src目录下新建用于存放用户服务的文件夹
在用户服务文件中写下
CMakeLilsts.txt
用于管理用户服务代码,以及方便插件系统进行链接这里被注释掉的部分是后续使用
json_rpc
的时候需要实现的,实现之后需要在这里加上。set(CMAKE_CXX_STANDARD 17) set(CMAKE_VERBOSE_MAKEFILE ON) set(rpc_test_srcs) set(rpc_test_hdrs) set(rpc_test_hpps) file(GLOB_RECURSE rpc_test_srcs "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE rpc_test_hdrs "${CMAKE_CURRENT_SOURCE_DIR}/*.h") file(GLOB_RECURSE rpc_test_hpps "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp") find_package(Thread) # user_service 服务端 add_executable(user_service # httplib.h # user_service_test.h # user_service_test.cpp # user_service_test_server.h # user_service_test_server.cpp # user_service.h # user_service_test_demon.cpp # cpphttplibconnector.hpp ) target_include_directories(user_service PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) # target_link_libraries(user_service PUBLIC json-rpc-cxx nlohmann_json) target_link_libraries(user_service PRIVATE ${CMAKE_THREAD_LIBS_INIT}) set_target_properties(user_service PROPERTIES OUTPUT_NAME user_service PREFIX "" DEBUG_POSTFIX "" INSTALL_RPATH "$\{ORIGIN\}/lib" ) include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
将用户服务作为子工程加入到插件系统中(在插件系统的
CMakeLists.txt
中写下以下命令,注意路径)add_subdirectory(src/user_service)
重新
Cmake
整个工程,这时候我们的工程结构就会编程我们想要的结构了。
下载并链接
json-rpc-cxx
到本地工程首先在插件系统的
CMakeLists.txt
文件中写下如下命令:CPMAddPackage(json-rpc-cxx1 NAME json-rpc-cxx1 VERSION 0.3.0 GIT_REPOSITORY https://gitee.com/aubo-robotics/json-rpc-cxx.git EXCLUDE_FROM_ALL YES OPTIONS "COMPILE_EXAMPLES OFF" "COMPILE_TESTS OFF" ) CPMAddPackage(json1 NAME json1 VERSION 3.9.1 GIT_REPOSITORY https://gitee.com/aubo-robotics/json-v3.9.1.git EXCLUDE_FROM_ALL YES OPTIONS "JSON_BuildTests OFF" "JSON_Install OFF" )
因为暂时插件系统json_rpc_test不需要使用
json_rpc
服务,而在用户服务系统user_service
需要使用json_rpc
服务,因此,将下载下来的包只链接到用户服务系统,需要在用户服务系统CMakeLists.txt
中写下如下命令:target_link_libraries(user_service PUBLIC json-rpc-cxx nlohmann_json)
用户服务系统
user_service
引入httplib.h
cpphttplibconnector.hpp
文件
将这两个文件链接到用户服务系统中,需要在用户服务CMakeLists.txt
文件中写下如下命令:
httplib.h
cpphttplibconnector.hpp
引入用户服务
这里笔者自定义了
test.h
抽象类,并从该类派生了user_service.h
类,在user_service.h
类中实现了testFunction()
接口用于仿造用户已经实现的各类的服务接口。这里,笔者的测试用例结构如下:
将测试用例链接到用户服务系统
user_service
:user_service.h user_service.cpp test.h
test.h:
#ifndef TEST_H #define TEST_H #include <string> #include <vector> #include <map> #include <memory> // 用户服务抽象接口,针对一种型号做一次实现 class Test { public: virtual ~Test() = default; virtual int testFunction() = 0; }; using TestPtr = std::shared_ptr<Test>; #endif // TEST_H
user_service.h
:#ifndef USER_SERVICE_H #define USER_SERVICE_H #include "test.h" class UserService : public Test { public: UserService(); int testFunction() override; }; using UserServicePtr = std::shared_ptr<UserService>; #endif // USER_SERVICE_H
user_service.cpp
:#include "user_service.h" UserService::UserService() { } int UserService::testFunction() { int test_result{12345}; return test_result; }
为测试用例添加rpc服务:(这里需要实现一个启用rpc服务的功能模块,并且用户需要把自己的功能模块中的接口添加到此rpc服务中)
user_service_rpc_service.h
文件:#ifndef USERSERVICERPCSERVICE_H #define USERSERVICERPCSERVICE_H #include "cpphttplibconnector.hpp" #include "user_service.h" class UserServiceRpcService : public jsonrpccxx::JsonRpc2Server { public: UserServiceRpcService(int port = 8988); ~UserServiceRpcService(); bool startListening(); void join(); private: CppHttpLibServerConnectorPtr connector_; UserServicePtr user_service_; }; #endif // USERSERVICERPCSERVICE_H
user_service_rpc_service.cpp
文件:#include "user_service_rpc_service.h" UserServiceRpcService::UserServiceRpcService(int port) { // 设置用户服务系统服务端的ip以及端口号,这部分设置完成之后会在 aubo_scope // 脚本中连接该服务端 connector_ = std::shared_ptr<CppHttpLibServerConnector>( new CppHttpLibServerConnector(*this, port)); // 实例化一个 UserService ,用于将 UserService // 里面需要的接口添加到此rpc服务中去 user_service_ = std::make_shared<UserService>(); // 由 UserService 提供的服务 Add("testFunction", jsonrpccxx::GetHandle(&UserService::testFunction, *user_service_), {}); } UserServiceRpcService::~UserServiceRpcService() { } bool UserServiceRpcService::startListening() { std::cout << "start startListening" << std::endl; connector_->StartListening(); } void UserServiceRpcService::join() { connector_->join(); }
将用户服务作为服务端启动
这里由于我们在cmakeLists.txt文件中写下了add_executable命令生成了user_service可执行程序,所以我们只需要启动该可执行程序就将用户服务user_service作为服务端启动了,这里我们可以有两种方式对其进行启动,一是手动启动该文件,二是在插件系统中启动该文件。
在插件系统启动该文件的思路:在用户一进入插件系统就启动该服务端。具体实现如下:
因为aubo_scope插件系统连接外部设备的功能都放在安装节点,因此我们可以在安装节点程序中启用外部程序的方式启动该服务端。
- 首先,在安装节点添加外部程序接口:
2.需要将生成的服务端文件(生成的用户服务系统可执行文件)打包到插件系统zip文件中,方便插件系统启动该文件。
install(TARGETS user_service DESTINATION .)
效果:将该插件打包后会在arcs_ws/estensions/json_rpc_test/下添加一个
user_service
可执行程序3.最后,在插件系统
json_rpc_test
安装节点中启动该程序:读取设定
user_service
路径:(1)激活函数中将工程路径传入安装节点
(2)将
aubo_caps
工作路径保存到service
类中(3)将
aubo_caps
工作路径传入node
节点,方便使用在安装节点构造的时候,启动用户服务端程序:
user_server_ = new QProcess(); // FIXME: 找到正确的加载路径 auto dir = QDir(location.c_str()); dir.cd(".."); user_server_->setProgram(QString("%1/user_service").arg(dir.path())); user_server_->setArguments(QStringList() << QString::number(8988)); QObject::connect(user_server_, &QProcess::stateChanged, [](QProcess::ProcessState newState) { // }); QObject::connect(user_server_, &QProcess::errorOccurred, [](QProcess::ProcessError error) { // }); // 启动用户服务端 user_server_->start();
将插件与用户服务服务端进行连接
在插件系统安装节点中写下连接脚本:
// 连接到服务端 script_writer->appendLine( QString( "local api = sched.jsonrpc.proxy('http://127.0.0.1:%1/jsonrpc')") .arg(8988) .toStdString());
在用户服务服务端添加需要的服务(接口)
// 由 UserService 提供的服务 Add("testFunction", jsonrpccxx::GetHandle(&UserService::testFunction, *user_service_), {});
在aubo_scope脚本中使用已添加到服务端的服务(接口)
// 测试调用外部接口 script_writer->appendLine("local result = api.testFunction()"); script_writer->appendLine( "print(1111111111111111111111111111111111111111111111)"); // 打印获取到的结果 script_writer->appendLine("print(result)");