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 relatively small (few KB) and they should be emitted at relatively low rates (up to 1 Hz).
The main sources of events are Data Tasks and HRTC Supervisors. Such components emit events e.g. after performing computations or when applying new configurations to the HRTC.
The main consumers of events 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 Service comes with an API that allows publishing and subscribing to JSON objects on specified topic names. Additionally, a hierarchy of common event types is provided. Those types can be further customised by instrument RTC developers to fit project specific needs.
Event Service API
To use the event service, include the following headers and namespaces:
#include <rtctk/componentFramework/eventServiceIf.hpp>
#include <rtctk/componentFramework/eventDefinitions.hpp>
using namespace rtctk::componentFramework;
The event service object can be retrieved from the service container using:
EventServiceIf& es = services.Get<EventServiceIf>();
To publish events, use:
// create a publisher object publishing to a specific topic
auto pub = es.MakePublisher("computation_topic");
// define a payload
JsonPayload payload = {
{"origin", "data_task_1"},
{"time", 12345628652},
{"type", "computation_event"},
{"item", "control_matrix_foo"},
{"result", 42},
};
// publish the payload on the event channel
pub->Publish(payload);
To subscribe to events, use:
// create a subscriber object for a specific topic
auto sub = es.MakeSubscriber("computation_topic");
sub->Subscribe(
[](const JsonPayload& payload) {
// dump the entire event as a string
std::cout << payload.dump() << std::endl;
// or access only specific payload members
std::cout << payload.at("item").get<std::string>() << std::endl;
});
// ...
sub->Unsubscribe();
Predefined Event Types
The event service comes with a hierarchy 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 |
It is recommended that instrument RTC teams use these event types wherever applicable, however, for specific needs, custom event types can be created.
Publishing events from predefined types works as follows:
// create a publisher object publishing to computation_topic
auto pub = es.MakePublisher(ComputationEvent::TOPIC_NAME);
// publish the payload on the event channel
pub->Publish(ComputationStartedEvent{"data_task_1", "control_matrix"});
// ...
pub->Publish(ComputationFinishedEvent{"data_task_1", "control_matrix", "42"});
Subscribing to events from predefined types works as follows:
// create a subscriber object for computation_topic
auto pub = es.MakeSubscriber(ComputationEvent::TOPIC_NAME);
sub->Subscribe(
[](const JsonPayload& payload) {
// either use the event as a JSON object
std::cout << payload.dump() << std::endl;
// or convert it back to a specific event type
if(auto ev = ConvertTo<ComputationStartedEvent>(payload); ev.has_value()) {
std::cout << ev.value().item << std::endl;
return;
}
if(auto ev = ConvertTo<ComputationFinishedEvent>(payload); ev.has_value()) {
std::cout << ev.value().item << std::endl;
return;
}
});
Creating Custom Event Types
Custom event types can be created by specialising toolkit-provided event types.
struct CustomComputationEvent : ComputationEvent
{
CustomComputationEvent = default(); // must be default constructible
CustomComputationEvent(std::string origin, std::string item, int attribute)
: ComputationEvent(origin, item)
, custom_attribute(attribute)
{ }
int custom_attribute;
};
void to_json(JsonPayload& j, const CustomComputationEvent& e) {
to_json(j, static_cast<const ComputationEvent&>(e));
j["type"].push_back(PrettyTypeName<std::remove_cvref_t<decltype(e)>>());
j["custom_attribute"] = e.custom_attribute;
}
void from_json(const JsonPayload& j, CustomComputationEvent& e) {
from_json(j, static_cast<ComputationEvent&>(e));
j.at("custom_attribute").get_to(e.custom_attribute);
}
Above, the event type CustomComputationEvent is defined by extending toolkit-provided event
type ComputationEvent with the additional attribute custom_attribte.
For serialisation and deserialisation, methods to_json and from_json must be implemented
in the same namespace. Additionally, constructors to create new events from required input data
may be defined as well.
Limitations and Known Issues
The EventPublisher and EventSubscriber objects should be created well in advance before actually sending and receiving events such that DDS has enough time to perform the connection, otherwise the first samples may be lost.
Keeping DDS history already starts with creation of the EventPublisher and EventSubscriber objects. This means, if a publisher already sends data to a subscriber that has not yet subscribed, the DDS queues may fill up to a point where DDS write errors occur. This may cause problems especially with the DDS history policy KEEP_ALL. Therefore, carefully consider the life-cycle of EventPublisher and EventSubscriber objects for each application.
Avoid the following operations, they may cause deadlocks due to limitations in the underlying FastDDS implementation: - publishing events from within a subscription callback - subscribing or unsubscribing events from within a subscription callback