Concepts#

Understanding on how to develop an application based on the CCF is simple, thus we start with the basic concepts followed a description on how to get started.

Connections

A connection represents the binding to a certain communication protocoll. Currently we already provide MQTT and NNG. It is possible to add new connections based on the Connection -Interface. All connections support the following basic functionalities extended by their protocoll specific capabilities:

// Listen on a specific channel.
virtual bool listen(const std::string &channel) { return true; };

// Initializes the connection.
virtual bool initializeConnection(std::function
	<void(const std::string, const std::string)> callback) = 0;

// Send a message over a channel
virtual bool send(const std::string &channel, const std::string &message) = 0;

The listen method represents the capability to listen on a specific channel and returns false on default. It must not be implemented, but then it is assumed that the connection listen to all channels. Initializes the connection by specifying a callback for received messages. This method must be called exactly once. The initializeConnection method initializes the connection by specifying a callback for received messages. This method must be called exactly once. The send method finally transmits a message. The already existing connection implementations can be found here.

Controllers and their Configuration

Controllers are the central concept of the CCF. They contain the application logic and are thus responsible for controlling a robot, for example. Here we can distinguish between controllers at high and low levels of abstraction. For example, a low-level abstraction controller would directly compute trajectories and send them to a robot. A high abstraction level controller would only call this controller and thus only call a method that encapsulates the computation. By default a controller should inherit from the Controller-Class, which provides the basic methods for adding connections and sending messages throught them.

protected:
    ros::NodeHandle nodeHandle;
    std::vector<std::unique_ptr<Connection>> connections;

    virtual void receive(const std::string &channel,
        const std::string &data) = 0;

public:
    explicit Controller(const ros::NodeHandle &nodeHandle);

    void addConnection(std::unique_ptr<Connection> &&connection);

    void sendToAll(const std::string &channel, const std::string &message);

An example of a low level controller is the MoveItRobotArmController, which provides functionalities to control a Franka Emika Panda robot, by implementing methods for pick-and-place or pick-and-drop operations. Beside this controller, we also provide several other low level controllers. An example for a high level controller is the MoveItCgvController, which configures and runs the MoveItRobotArmController. Firstly this is about configuring the attached connections. Second, the behavior of the MoveItRobotArmController is extend on dedicated points to hook in. These hook in points have to be specifically implemented for individual controllers. The following code example shows how to configure the behaviour of the MoveItRobotArmController by defining what should happen when the CCF receives a selection message.

    auto selectionMessageCallback = 
        [&currentlyPickedBox, &connector, &robot, &selectedBox, &selectedBin, &n](
            const Selection &selection) {

        // forward the selection to the clients
        connector.sendToAll(getParameter<std::string>(n, "topics/selection", "selection"),
                            selection.SerializeAsString());

        // ...
    };
    connector.reactToSelectionMessage(selectionMessageCallback);

This is done by defining a lambda function and passing it to the MoveItRobotArmController and happens similar when configuring the behavior for scene update messages.