Skip to content

RPC 接口使用说明


概述:在插件开发过程中

  1. 在插件 sdk 中可能并没有满足用户使用需求的一些特殊的服务接口。这时候我们可以在本地实现这种接口,然后通过 RPC 接口实现在aubo_scop脚本系统中也可以使用该接口服务。
  2. 可能我们已经在本地完成了需要的各类服务的接口实现,如果使用aubo_caps系统重新实现会耗费大量的人力物力精力,这时候我们便可以使用aubo_scope脚本系统提供的 RPC 接口来远程调用我们在aubo_caps插件系统外部实现的各类服务。
  3. 注:本事例的服务端的端口号为 8988,在代码里给出的,如果有需要可以设置界面,让用户设置端口号,然后再相应的地方读取用户设好的值即可。

示例工程

示例工程的使用:使用README中的命令将其打包,然后参考 https://docs.aubo-robotics.cn/application_notes/07-plugin_load_unload/ 将其加载到aubo_scope

下面,我将使用建立两个工程来演示整个过程,其中user_service是我们的用户服务,json_rpc_test是我们的插件系统。

  • 调整代码结构

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

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

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

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

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

      cmake
      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中写下以下命令,注意路径)

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

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

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

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

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

httplib.h 文件链接:src/httplib.h cpphttplibconnector.hpp 文件链接: src/cpphttplibconnector.hpp

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

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

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

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

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

    cmake
    user_service.h
    user_service.cpp
    test.h

    test.h:

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

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

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

    user_service_rpc_service.h文件:

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

    C++
    #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. 首先,在安装节点添加外部程序接口:

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

    CMake
    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节点,方便使用

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

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

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

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

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

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