Event Service

The RTC Toolkit provides an Event Service library that enables RTC Components to publish and subscribe customisable events over the non-deterministic control network (CND). The Event Service is based on CII MAL using FastDDS. It makes use of the pre-defined MAL topic type rtctkif::GenericEvent that carries a BLOB with a customisable JSON payload.

Design Assumptions

The event service is used to exchange notification events within the SRTC via a publish and subscribe messaging pattern. Events should be rather small (few KB) and they should be emitted at relatively low rates (up to 1 Hz).

Main event sources are Data Tasks and HRTC Supervisors. Such components emit events e.g. after performing computations or when applying new configurations to the HRTC.

Main event sinks are GUIs, Telemetry Recorders, Metadata Collectors as well as Data Tasks and HRTC Supervisors. While GUIs visualise emitted events (e.g. in an event screen), Recorders and Collectors dump interesting events to FITS files. Data Tasks and HRTC Supervisors ‘react’ on events (e.g. start computations or apply configurations to the HRTC).

To provide enough flexibility the Event Services comes with two APIs: a low-level API and a high-level API. While the low-level API allows publishing and subscribing JSON objects on specified topic names, the high-level API is aware of different event types. Additionally, a set of common event types are already defined and they can be further customised by instrument RTC developers in order to create a hierarchy of event types.

Low-level API

To use the low-level event service include the follow header:

#include <rtctk/componentFramework/eventServiceIf.hpp>

The low-level event service object can be retrieved from the service container using:

EventServiceIf& es = services.Get<EventServiceIf>();

To publish low-level events use:

JsonPayload payload = {
    {"origin", "data_task_1"},
    {"time", 12345628652},
    {"type", "computation_event"},
    {"item", "control_matrix_foo"},
    {"result", 42},
};

auto pub = es.MakePublisher("computation_topic");

pub->Publish(payload);

To subscribe to low-level events use:

auto sub = es.MakeSubscriber("computation_topic");

sub->Subscribe("computation_topic",
    [](JsonPayload const& payload) {
        std::cout << payload.dump() << std::endl;;
    });

// ...

sub->Unsubscribe();

High-level API

To use the high-level event service include the follow header:

#include <rtctk/componentFramework/typedEventService.hpp>

The high-level event service object can be constructed with the low-level event service using:

TypedEventService tes{services.Get<EventServiceIf>()};

To publish high-level events use:

auto pub = tes.MakePublisher<ComputationStartedEvent>();

pub->Publish({"data_task_1", "control_matrix_foo"});

To subscribe to high-level events use:

auto sub = tes.MakeSubscriber<ComputationStartedEvent>();

sub->Subscribe<ComputationStartedEvent>(
    [](ComputationStartedEvent const& ev) {
        std::cout << ev.ToJson().dump() << std::endl;;
    });

// ...

sub->Unsubscribe();

Predefined Event Types

The high-level API comes with a set of predefined event types:

Event Type

Topic Name

Description

AbstractEvent

none

abstract, common base for all event types

ComputationEvent

computation_topic

abstract, defines common event attributes

ComputationStartedEvent

computation_topic

signals that computation has started

ComputationFinishedEvent

computation_topic

signals that computation has finished

ConfigurationEvent

configuration_topic

abstract, defines common event attributes

ConfigurationUpdatedEvent

configuration_topic

signals that a configuration item was updated

ConfigurationRetrievedEvent

configuration_topic

signals that a configuration item was retrieved

HrtcConfigurationScheduledEvent

configuration_topic

signals that a configuration item was scheduled to be applied in HRTC

HrtcConfigurationAppliedEvent

configuration_topic

signals that a configuration item was applied in HRTC

CoordinationEvent

coordination_topic

abstract, defines common event attributes

StateChangedEvent

coordination_topic

signals that an entity changed its state

HrtcStateChangedEvent

coordination_topic

signals that an entity in HRTC changed its state

AlertStatusEvent

alert_status_topic

event published when alert status is changed

The general idea is that instrument RTC teams use these event types as-is or specialise them according to their needs.

Creating Custom Event Types

Custom event types can be created by specialising toolkit-provided event types.

struct CustomComputationEvent : ComputationEvent
{
    CustomComputationEvent(std::string origin, std::string item, int attribute)
    : ComputationEvent(origin, item)
    , custom_attribute(attribute)
    { }

    CustomComputationEvent(JsonPayload const& payload)
    : ComputationEvent(payload)
    , custom_attribute(payload["custom_attribute"])
    { }

    JsonPayload ToJson() const {
        auto payload = ComputationEvent::ToJson();
        payload["type"].push_back(PrettyTypeName<CustomComputationEvent>);
        payload["custom_attribute"] = custom_attribute;
        return payload;
    }

    int custom_attribute;

    // optional: override the topic_name of the parent event type
    // inline static const std::string topic_name = "test_topic";
};

In the example above, the event type CustomComputationEvent is defined by extending toolkit-provided event type ComputationEvent event with additional attribute custom_attribte. For serialisation and deserialisation purpose a constructor taking a JsonPayload and method ToJson need to be implemented. Additionally, constructors to create new events from required input data need to be defined as well.

Note that the high-level API allows to publish specialised events like CustomComputationEvent and subscribe to them using the base event type e.g. ComputationEvent, but this is only possible if the same topic name is used.

Limitations and Known Issues

While the low-level API is more flexible, the high-level API is more compact and simpler to use. It is recommended not to mix both API types within the same component.

Do not subscribe or unsubscribe events from within the subscription callback, this causes deadlocks!