2. System Handle

As explained in the Introduction, a single integration-service instance can route any number of topics or services to/from any number of middlewares.

This occurs through the use of System Handles, which are system-specific plugins that allows a certain middleware or communication protocol to speak the same language used by the Integration Service, that is, Extensible and Dynamic Topic Types for DDS (xTypes).

../../_images/SH.png

2.1. Built-in System Handles

This section provides an insight over the existing built-in System Handles provided along with Integration Service for connecting the core with the following middlewares or communication protocols:

Protocol

System Handle overview

Fast DDS

Fast DDS System Handle

FIWARE

FIWARE System Handle

ROS 1

ROS 1 System Handle

ROS 2

ROS 2 System Handle

WebSocket

WebSocket System Handle

Additional System Handles can be implemented by users, in order to have the desired middlewares joining the Integration Service world. Adding a new System Handle automatically allows communication with the rest of the protocols already available in this ecosystem.

2.2. Implementation

This section provides an overview of the architecture of a System Handle, by depicting the class inheritance structure and specifying the methods which need to be implemented in order to create a custom System Handle.

Here you can find a diagram of a System Handle class inheritance structure.

../../_images/sh_hierarchy.png

Each System Handle must inherit, directly or indirectly, from the SystemHandle superclass. Depending on the nature of each protocol, it should implement the derived classes using multiple inheritance from TopicSubscriberSystem, TopicPublisherSystem, ServiceClientSystem, and/or ServiceProviderSystem. To simplify this inheritance, classes TopicSystem, ServiceSystem, and FullSystem are available to inherit from.

In the diagram below, the architecture of a generic “Full” System Handle and its integration into Integration Service is shown.

../../_images/sh_impl.png

To ease the implementation, the new system::SystemHandle will inherit from FullSystem. The following sections will explain the methods to be implemented.

To implement the TopicPublisher, ServiceClient, and ServiceProvider interfaces, the most direct way is to create child classes, respectively system::Publisher, system::Client, and system::Server. An additional class system::Subscriber may be useful to manage the subscribers created. In the example shown in the diagram above, the system::SystemHandle will contain the needed instances of these classes, but any approach may be valid if the interfaces are met.

2.2.1. SystemHandle Class

All System Handles must implement the configure, okay, and spin_once methods that belong to the superclass:

bool configure(
    const RequiredTypes& types,
    const YAML::Node& configuration,
    TypeRegistry& type_registry);

bool okay() const = 0;

bool spin_once();

The configure method is called to setup the System Handle with the associated configuration, defined in the YAML file that is passed to it. The types that the SH needs to manage to implement the communication are passed to this method via the types argument, whereas the new types created by the System Handle are expected to be filled in the type_registry.

The okay method is called by Integration Service to check if the System Handle is working. This method will verify internally if the middleware has any problem.

The spin_once method is called by Integration Service to allow spinning to those middlewares that need it.

2.2.2. TopicSubscriberSystem Class

This kind of system must implement the subscribe method:

using SubscriptionCallback = std::function<void(const xtypes::DynamicData& message)>;

bool subscribe(
    const std::string& topic_name,
    const xtypes::DynamicType& message_type,
    SubscriptionCallback callback,
    const YAML::Node& configuration);

Integration Service will call this method in order to create a new subscriber to the topic topic_name using message_type type, plus an optional configuration. Once the middleware system receives a message from the subscription, the message must be translated into the message_type and the System Handle must invoke the callback with the translated message.

2.2.3. TopicPublisherSystem Class

This kind of system must implement the advertise method:

std::shared_ptr<TopicPublisher> advertise(
    const std::string& topic_name,
    const xtypes::DynamicType& message_type,
    const YAML::Node& configuration);

Integration Service will call this method in order to create a new TopicPublisher to the topic topic_name using message_type type, and optional configuration.

The TopicPublisher is an interface that must be implemented by a Publisher in order to allow Integration Service to publish messages to the target middleware. This interface defines a single method publish:

bool publish(const xtypes::DynamicData& message);

When Integration Service needs to publish to the middleware system it will call the TopicPublisher::publish method, with a message that must be translated from the message_type parameter by the advertise method above.

2.2.4. ServiceClientSystem Class

This kind of system must implement the create_client_proxy method:

using RequestCallback =
    std::function<void(
        const xtypes::DynamicData& request,
        ServiceClient& client,
        std::shared_ptr<void> call_handle)>;

bool create_client_proxy(
    const std::string& service_name,
    const xtypes::DynamicType& service_type,
    RequestCallback callback,
    const YAML::Node& configuration);

Integration Service will call this method in order to create a new ServiceClient to the service service_name using the service_type type, plus an optional configuration. This ServiceClient will be provided as an argument in the callback invocation when a response is received.

The ServiceClient is an interface that must be implemented by a Client in order to allow Integration Service to relate a request with its reply. This is done by providing a call_handle both in the call_service method from ServiceProvider and in the callback from create_client_proxy method. When the reply is received by another System Handle, its ServiceProvider will call the receive_response method from the Client:

void receive_response(
    std::shared_ptr<void> call_handle,
    const xtypes::DynamicData& response);

The receive_response:

  • Translates the response from service_type and relate the call_handle, if needed, to its middleware’s request;

  • Replies to its middleware.

2.2.5. ServiceProviderSystem Class

This kind of system must implement the create_service_proxy method:

std::shared_ptr<ServiceProvider> create_service_proxy(
    const std::string& service_name,
    const xtypes::DynamicType& service_type,
    const YAML::Node& configuration);

Integration Service will call this method in order to create a new ServiceProvider to the service service_name using the service_type type, plus an optional configuration.

The ServiceProvider is and interface that must be implemented by a Server in order to allow Integration Service to request (or call) a service from the target middleware.

void call_service(
    const xtypes::DynamicData& request,
    ServiceClient& client,
    std::shared_ptr<void> call_handle);

This call_service method will translate the request from service_type and will call its middleware service, which stores the related call_handle and client. Once it receives the response from its middleware, it must translate back the response and retrieve the call_handle and client related. Then, it will invoke the receive_response method from the client using the call_handle as argument.

2.3. Sequence diagrams

The following diagrams illustrate the previous sections using a generic System Handle.

2.3.1. TopicPublisher flow

../../_images/topic_publisher.png

2.3.2. TopicSubscriber flow

../../_images/topic_subscriber.png

2.3.3. ServiceClient flow

Note that a ServiceClient acts as a client for Integration Service and as a server for the middleware.

../../_images/service_client.png

2.3.4. ServiceProvider flow

Note that a ServiceProvider acts as a server for Integration Service and as a client for the middleware.

../../_images/service_provider.png