通过RPC接口连接用户服务

概述:在插件开发过程中

  1. 在插件sdk中可能并没有满足用户使用需求的一些特殊的服务接口。这时候我们可以在本地实现这种接口,然后通过RPC接口实现在aubo_scop脚本系统中也可以使用该接口服务。
  2. 可能我们已经在本地完成了需要的各类服务的接口实现,如果使用aubo_caps系统重新实现会耗费大量的人力物力精力,这时候我们便可以使用aubo_scope脚本系统提供的RPC接口来远程调用我们在aubo_caps插件系统外部实现的各类服务。
  3. 注: 本事例的服务端的端口号为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是我们的插件系统。

  • 调整代码结构

    在插件开发过程中,建议将用户服务与插件系统的代码分开进行管理。这样代码结构清晰易于维护。

    如图:我们将用户服务作为子工程的方式链接到插件系统

    image-20230329105445455

    1. 在插件系统src目录下新建用于存放用户服务的文件夹

      image-20230329103518815

    2. 在用户服务文件中写下CMakeLilsts.txt用于管理用户服务代码,以及方便插件系统进行链接

      image-20230329103413786

      这里被注释掉的部分是后续使用json_rpc的时候需要实现的,实现之后需要在这里加上。

      image-20230329104925952

      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}")
      
    3. 将用户服务作为子工程加入到插件系统中(在插件系统的CMakeLists.txt中写下以下命令,注意路径)

      image-20230329105043314

      add_subdirectory(src/user_service)
      
    4. 重新Cmake整个工程,这时候我们的工程结构就会编程我们想要的结构了。

  • 下载并链接json-rpc-cxx到本地工程

    首先在插件系统的CMakeLists.txt文件中写下如下命令:

    image-20230329110147348

    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中写下如下命令:

    image-20230329110604943

    target_link_libraries(user_service PUBLIC json-rpc-cxx nlohmann_json)
    
  • 用户服务系统user_service引入 httplib.h cpphttplibconnector.hpp 文件

    image-20230329111319383

将这两个文件链接到用户服务系统中,需要在用户服务CMakeLists.txt文件中写下如下命令:

image-20230329112406295

  httplib.h
  cpphttplibconnector.hpp
  • 引入用户服务

    这里笔者自定义了test.h抽象类,并从该类派生了user_service.h类,在user_service.h类中实现了testFunction()接口用于仿造用户已经实现的各类的服务接口。

    这里,笔者的测试用例结构如下:

    将测试用例链接到用户服务系统user_service

    image-20230329132146133

    user_service.h
    user_service.cpp
    test.h
    

    test.h:

    image-20230329130504693

    #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:

    image-20230329131736853

    #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:

    image-20230329131948463

    #include "user_service.h"
    
    UserService::UserService()
    {
    }
    
    int UserService::testFunction()
    {
        int test_result{12345};
        return test_result;
    }
    
  • 为测试用例添加rpc服务:(这里需要实现一个启用rpc服务的功能模块,并且用户需要把自己的功能模块中的接口添加到此rpc服务中)

    image-20230329135123022

    user_service_rpc_service.h文件:

    image-20230329134700398

    #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文件:

    image-20230329142223412

    #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插件系统连接外部设备的功能都放在安装节点,因此我们可以在安装节点程序中启用外部程序的方式启动该服务端。

    1. 首先,在安装节点添加外部程序接口:

    image-20230329140035198

    2.需要将生成的服务端文件(生成的用户服务系统可执行文件)打包到插件系统zip文件中,方便插件系统启动该文件。

    image-20230329143531921

    install(TARGETS user_service DESTINATION .)
    

    效果:将该插件打包后会在arcs_ws/estensions/json_rpc_test/下添加一个 user_service可执行程序

    image-20230329154603467

    3.最后,在插件系统json_rpc_test安装节点中启动该程序:

    读取设定user_service路径:

    (1)激活函数中将工程路径传入安装节点image-20230329154739513

    (2)将aubo_caps工作路径保存到service类中

    image-20230329155025622

    image-20230329155117761

    (3)将aubo_caps工作路径传入node节点,方便使用

    image-20230329155240534

    image-20230329155323142

    在安装节点构造的时候,启动用户服务端程序:

    image-20230329155445292

     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();
    
  • 将插件与用户服务服务端进行连接

    在插件系统安装节点中写下连接脚本:

    image-20230329151901460

     // 连接到服务端
        script_writer->appendLine(
            QString(
                "local api = sched.jsonrpc.proxy('http://127.0.0.1:%1/jsonrpc')")
                .arg(8988)
                .toStdString());
    
  • 在用户服务服务端添加需要的服务(接口)

    image-20230329151948571

    // 由 UserService 提供的服务
        Add("testFunction",
            jsonrpccxx::GetHandle(&UserService::testFunction, *user_service_), {});
    
  • 在aubo_scope脚本中使用已添加到服务端的服务(接口)

    image-20230329152101471

    // 测试调用外部接口
        script_writer->appendLine("local result = api.testFunction()");
        script_writer->appendLine(
            "print(1111111111111111111111111111111111111111111111)");
        // 打印获取到的结果
        script_writer->appendLine("print(result)");
    

results matching ""

    No results matching ""