Skip to content

RPC 接口使用说明

概述

在插件开发过程中,可能会遇到一些特殊场景,这些场景超出了目前的 AuboCaps 插件系统的能力范围。用户可能希望在本地实现这些接口,或者已经完成了本地接口的实现,并希望 ARCS 软件直接调用这些本地接口,以满足各种需求。为了解决这种情况,AUBO 研发团队在 AuboCaps 插件系统中增加了 RPC 接口。通过 RPC 接口,ARCS 脚本系统可以调用 AuboCaps 插件系统以外的各种本地服务,极大地丰富了 AuboCaps 插件系统的功能。

示例

本节示例工程的代码可以通过下方链接下载使用,该示例服务端的端口号为 “8988”,也可通过【设置】界面,自定义端口号。

示例工程:https://github.com/AuboRobot/plugin_template/tree/main/example/example4

在示例工程中,【src】文件夹下的【user_service】文件夹内为用户服务相关的代码,用户服务名称为【user_service】;【installation】、【program】文件夹内为 AuboCaps 插件系统相关的代码, 插件名称为【json_rpc_test】。在插件开发的过程中,为了使代码结构清晰且易于维护,建议将用户服务通过子工程的方式链接到插件系统,即将用户服务与插件系统的代码分开管理。

调整代码结构

  1. 在插件工程根目录的【src】目录下,新建用于存放用户服务的文件夹,本示例中命名为【user_service】。

  2. 在【user_service】目录下创建【CMakeLilsts.txt】,该文件用于管理用户服务代码,并方便插件系统进行链接。

  3. 打开【CMakeLilsts.txt】文件,在文件中写入以下代码。

    1. 其中被注释的部分是之后使用 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}")
  4. 打开插件系统的【CMakeLists.txt】,输入以下命令,将用户服务作为子工程加入到插件系统中,编写时请注意路径。

    cmake
    add_subdirectory(src/user_service)
  5. 重新【执行 CMake(Run CMake)】整个工程,完成后工程结构就会变成想要的结构了。

用户服务配置 RPC

本示例通过 http 的方式使用 json_rpc_Cxx 库文件,用户服务【user_service】需要引入【httplib.h】、【cpphttplibconnector.hpp】文件,【httplib.h】、【cpphttplibconnector.hpp】文件已放至示例工程中,用户使用时可直接拷贝复用。

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

在本操作中,【json_rpc_test】插件暂时不需要 json_rpc 服务,而用户服务【user_service】需要使用 json_rpc 服务。

  1. 打开插件系统的【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"
        )
  2. 打开用户服务【user_service】的【CMakeLists.txt】文件,输入以下代码。

    cmake
    target_link_libraries(user_service PUBLIC json-rpc-cxx nlohmann_json)
  3. 将【httplib.h】、【cpphttplibconnector.hpp】文件拷贝至【user_service】目录下。

  4. 打开用户服务【user_service】的【CMakeLists.txt】文件,输入以下代码,将【httplib.h】、【cpphttplibconnector.hpp】文件链接到用户服务系统中。

    cmake
      httplib.h
      cpphttplibconnector.hpp

本地工程引入用户服务

本示例工程内,使用【test.h】抽象类派生出【user_service.h】类,在【user_service.h】类中通过实现 testFunction() 接口来模拟用户已实现的各类服务接口。

在本操作中,将测试用例链接到用户服务【user_service】。

  1. 打开用户服务【user_service】的【CMakeLists.txt】文件,输入以下代码。

    cmake
    user_service.h
    user_service.cpp
    test.h
  2. 打开【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
  3. 打开【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
  4. 打开【user_service.cpp】,输入以下代码。

    c++
    #include "user_service.h"
    
    UserService::UserService()
    {
    }
    
    int UserService::testFunction()
    {
     int test_result{12345};
     return test_result;
    }

本地工程配置 RPC

  1. 打开用户服务【user_service】的【CMakeLists.txt】文件,输入以下代码。

    cmake
       user_service_rpc_service.cpp
       user_service_rpc_service.h
  2. 打开【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
  3. 打开【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();
    }

配置用户服务作为服务端

将用户服务【user_service】作为服务端启动的方法有两种,一是手动启动该文件,二是在插件系统中启动可执行文件。

在上述一系列操作中,已在【CMakeLists.txt】文件中编写了 add_executable 命令,生成了【user_service】的可执行程序,因此可以通过下面的操作,在安装节点程序中通过启用外部程序的方式启动该服务端,实现在插件系统中启动可执行文件的目的。配置完成后,用户一旦进入插件系统便启动该服务端。

  1. 在安装节点添加外部程序接口。

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

    1. 打开【user_service】的【CMakeLists.txt】文件,输入以下代码。

      cmake
      install(TARGETS user_service DESTINATION .)
    2. 将该插件打包, 【arcs_ws/estensions/json_rpc_test/】目录下添加一个【user_service】可执行程序。

插件系统启动本地工程

在插件系统【json_rpc_test】安装节点中启动

  1. 读取设定【user_service】路径。

    1. 激活函数中将工程路径传入安装节点。

    2. aubo_caps 工作路径保存到 service 类中。

    3. aubo_caps 工作路径传入 node 节点,方便使用。

  2. 构造安装节点时,启动用户服务端程序。

    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 脚本中使用已添加到服务端的服务(接口)。

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