Skip to content

RPC Interface

Overview

During plugin development, developers may encounter special scenarios that exceed the current capabilities of the AuboCaps plugin system. Users may wish to implement these interfaces locally, or they may have already implemented local interfaces and want the ARCS software to directly call these local interfaces to meet various requirements. To address this situation, the AUBO development team has added RPC interfaces to the AuboCaps plugin system. Through the RPC interface, the ARCS script system can call various local services outside the AuboCaps plugin system, greatly enriching the functionality of the AuboCaps plugin system.

Example

The code for the example project in this section can be downloaded and used via the link below. The port number for the example server is "8988," but it can also be customized through the [Setting] interface.

Example project: https://github.com/AuboRobot/plugin_template/tree/main/example/example4

In the example project, the user_service folder under the src directory contains code related to user services, with the user service named "user_service". The installation and program folders contain code related to the AuboCaps plugin system, with the plugin named "json_rpc_test". During the plugin development process, to ensure a clear and maintainable code structure, it is recommended to link the user service to the plugin system as a subproject. This approach allows the user service and the plugin system code to be managed separately.

Adjusting Code Structure

  1. In the root directory of the plugin project, under the src directory, create a new folder to store the user service. In this example, name it "user_service".

  2. Inside the user_service directory, create a CMakeLists.txt file. This file is used to manage the user service code and facilitate linking within the plugin system.

  3. Open the CMakeLists.txt file and write the following code into it.

    1. The commented sections are for implementing the json_rpc service, which will be required later. Once implemented, uncomment these sections.
    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. Open the plugin system's CMakeLists.txt and enter the following commands to add the user service as a subproject to the plugin system. Pay attention to the paths when writing.

    cmake
    add_subdirectory(src/user_service)
  5. Re-execute [Run CMake] for the entire project. After completion, the project structure will be transformed into the desired configuration.

User Service Configuration RPC

This example uses the json_rpc_Cxx library via HTTP. The user service "user_service" needs to include the httplib.h and cpphttplibconnector.hpp files. These files have been placed in the example project and can be directly copied and reused.

httplib.h file link: src/httplib.hcpphttplibconnector.hpp file link: src/cpphttplibconnector.hpp

In this operation, the "json_rpc_test" plugin temporarily does not require the json_rpc service, while the user service "user_service" needs to use the json_rpc service.

  1. Open the plugin system's CMakeLists.txt file and enter the following code:

    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. Open the user service "user_service" CMakeLists.txt file and enter the following code:

    cmake
    target_link_libraries(user_service PUBLIC json-rpc-cxx nlohmann_json)
  3. Copy the httplib.h and cpphttplibconnector.hpp files to the user_service directory.

  4. Open the user service "user_service" CMakeLists.txt file and enter the following code to link the httplib.h and cpphttplibconnector.hpp files to the user service system.

cmake
  httplib.h
  cpphttplibconnector.hpp

Importing User Services into Local Project

In this example project, the test.h abstract class is derived to create the user_service.h class. The user_service.h class simulates various user-implemented service interfaces by implementing the testFunction() interface.

In this operation, we will link the test cases to the user service "user_service".

  1. Open the CMakeLists.txt file of the user service "user_service" and enter the following code.

    cmake
    user_service.h
    user_service.cpp
    test.h
  2. Open test.h and enter the following code.

    c++
    #ifndef TEST_H
    #define TEST_H
    
    #include <string>
    #include <vector>
    #include <map>
    #include <memory>
    
    // Abstract interface for user services, implement once for each model type
    class Test
    {
    public:
     virtual ~Test() = default;
    
     virtual int testFunction() = 0;
    };
    using TestPtr = std::shared_ptr<Test>;
    
    #endif // TEST_H
  3. Open user_service.h and enter the following code.

    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. Open user_service.cpp and enter the following code.

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

Local Project RPC Configuration

  1. Open the CMakeLists.txt file in the user service "user_service" and enter the following code.

    cmake
       user_service_rpc_service.cpp
       user_service_rpc_service.h
  2. Open user_service_rpc_service.h and enter the following code.

    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. Open user_service_rpc_service.cpp and enter the following code.

    c++
    #include "user_service_rpc_service.h"
    
    UserServiceRpcService::UserServiceRpcService(int port)
    {
     // Set the IP address and port number of the user service system server.
     // After this configuration is complete, it will connect to this server in the aubo_scope script
     connector_ = std::shared_ptr<CppHttpLibServerConnector>(
         new CppHttpLibServerConnector(*this, port));
    
     // Instantiate a UserService to add the required interfaces
     // from UserService to this RPC service
     user_service_ = std::make_shared<UserService>();
    
     // Services provided by 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();
    }

Configure User Service as Server

There are two methods to start the user service "user_service" as a server: one is to manually start the file, and the other is to launch the executable file within the plugin system.

In the series of operations mentioned above, the add_executable command has been written in the CMakeLists.txt file, generating the executable program for "user_service". Therefore, through the following operations, the server can be started by enabling external programs in the installation node, achieving the purpose of launching executable files within the plugin system. Once configured, the server will start automatically when users enter the plugin system.

  1. Add external program interface in the installation node.

  2. Package the generated server file (the user service system executable file) into the plugin system zip file to facilitate its launch within the plugin system.

    1. Open the CMakeLists.txt file of "user_service" and input the following code.

      cmake
      install(TARGETS user_service DESTINATION .)
    2. Package the plugin, adding a user_service executable program to the arcs_ws/estensions/json_rpc_test/ directory.

Plugin System Starts Local Project

In the plugin system json_rpc_test installation node:

  1. Read the configured user_service path.

    1. Pass the project path to the installation node in the activation function.

    2. Save the aubo_caps working path to the service class.

    3. Pass the aubo_caps working path to the node for convenient use.

  2. When constructing the installation node, start the user server program.

    c++
     user_server_ = new QProcess();
    
        // FIXME: Find the correct loading path
        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) {
                             //
                         });
    
        // tart user server
        user_server_->start();

Plugin System Connects to User Service Server

Write connection script in the plugin system installation node.

c++
// Server Adds Services
 script_writer->appendLine(
     QString(
         "local api = sched.jsonrpc.proxy('http://127.0.0.1:%1/jsonrpc')")
         .arg(8988)
         .toStdString());

Server Adds Services

Add required services (interfaces) to the user service server.

c++
// Services provided by UserService
 Add("testFunction",
     jsonrpccxx::GetHandle(&UserService::testFunction, *user_service_), {});

Script Uses Services

Use the services (interfaces) that have been added to the server in aubo_scope script.

cmake
// Test calling external interface
 script_writer->appendLine("local result = api.testFunction()");
 script_writer->appendLine(
     "print(1111111111111111111111111111111111111111111111)");
 // Print the obtained result
 script_writer->appendLine("print(result)");