A simplified API for passing messages between DAQModules
- Main point-of-contact for Unified API
- Methods to retrieve
Sender/Receiverinstances usingConnectionRefs oruid(defined below) - Configures
Queues andNetworkManagerautomatically inconfigure
dunedaq::get_iomanager()will return a pointer to the IOManager singletonIOManager::get_sender<DataType>(std::string uid)andIOManager::get_receiver<DataType>(std::string uid)should be used to get Sender and Receiver objects connected to the appropriate connections. Note thatConnectionIdobjects are not required, as they will be constructed from the provided DataType and uid arguments.- Subscribers interested in multiple connections for a single DataType should use a Regular Expression to match the desired connections; this can be anywhere from a full wildcard (
".*") to a specific connection UID, depending on the desired scope of the subscription. Topics are automatically assigned by IOManager as the string representation of DataType. appfwkwill give DAQModules a list ofappfwk::app::ConnectionReferenceobjects, which associate a "name" to connection UIDs. Methods inDAQModuleHelper.hpptake DAQModule configuration objects and extract specific UIDs for given names.
auto mandatory_connections =
appfwk::connection_index(init_data, { "token_connection", "td_connection", "busy_connection" });
m_token_connection = mandatory_connections["token_connection"];
m_td_connection = mandatory_connections["td_connection"];
auto busy_connection = mandatory_connections["busy_connection"];
m_busy_sender = iom->get_sender<dfmessages::TriggerInhibit>(busy_connection);- Upon agreement from both endpoints, a connection can use a generated UID string (e.g. from SourceID::to_string()).
- The
serializationlibrary provides a new macroDUNE_DAQ_TYPESTRING(Type, string)which is included in the standardDUNE_DAQ_SERIALIZABLEandDUNE_DAQ_SERIALIZE_NON_INTRUSIVEmacros (called from thedunedaqnamespace only). These macros define the functiondatatype_to_string<T>which is used by IOManager to translate a datatype to the appropriate string. This template function must be visible in every compilation unit sending or receiving a given type!- If it is not available, an error message will be produced at runtime that IOManager was unable to find connection "uid" of type Unknown
- In
daqconf, all connections and queues must have a declared data type that matches a call toDUNE_DAQ_TYPESTRING.add_endpointandconnect_moduleshave changed their API to accomodate this.
ConnectionIduniquely identifies a network connection or queueuid: String identifier for connectiondata_type: String representation of data type
Connectiondefines a network connection, with required initializationid:ConnectionIdconnection_type: Describes what kind of connection (kSendReceive, kPubSub)uri: Field is used by lower-level code to configure the connection- Standard ZMQ URI should be used, e.g.
tcp://localhost:1234(name translation is provided by IPM)
- Standard ZMQ URI should be used, e.g.
QueueConfigrepresents an app-internal queueid:ConectionIdqueue_type: Type of the queue implementation (e.g. kStdDeQueue, kFollySPSCQueue, kFollyMPMCQueue)capacity: Capacity of the queue
Receiveris base type without template (for use inIOManager::m_receivers)ReceiverConceptintroduces template and serves as class given byIOManager::get_receiverQueueReceiverModelandNetworkReceiverModelimplement receives and callback loop for queues and networkNetworkReceiverModel::read_networkdetermines if type is serializable using template metaprogramming
- Similar design as for
Receivers QueueSenderModelandNetworkSenderModelimplement sends for queues and networkNetworkReceiverModel::write_networkdetermines if type is serializable using template metaprogramming
// Int sender
std::string uid = "bar";
int msg = 5;
std::chrono::milliseconds timeout(100);
auto isender = IOManager::get()->get_sender<int>(uid);
isender->send(msg, timeout);
isender->send(msg, timeout);
// One line send
IOManager::get()->get_sender<int>(uid)->send(msg, timeout);
// Send when timeouts may occur
bool sent = isender->try_send(msg, timeout);
// String receiver
std::string uid = "bar";
auto receiver = IOManager::get()->get_receiver<std::string>(uid);
std::string got;
try {
got = receiver->receive(timeout);
} catch (dunedaq::appfwk::QueueTimeoutExpired&) {
// Deal with exception
}
// Alternate API for when timeouts may be allowed
std::optional<std::string> ret = receiver->try_receive(timeout);
if(ret) TLOG() << "Received " << *ret;
// Callback receiver
std::string uid = "zyx";
// CB function
std::function<void(std::string)> str_receiver_cb = [&](std::string data) {
std::cout << "Str receiver callback called with data: " << data << '\n';
};
auto cbrec = IOManager::get()->get_receiver<std::string>(uid);
cbrec->add_callback(str_receiver_cb);
try {
got = cbrec->receive(timeout);
} catch (dunedaq::iomanager::ReceiveCallbackConflict&) {
// This is expected
}
IOManager::get()->remove_callback(uid);
The standard send() and receive() methods will throw an ERS exception if they time out. This is ideal for cases where timeouts are an exceptional condition (this applies to most, if not all send calls, for example). In cases where the timeout condition can be safely ignored (such as the callback-driving methods which are retrying the receive in a tight loop), the try_send and try_receive methods may be used. Note that these methods are not noexcept, any non-timeout issues will result in an ERS exception.
The API used for queues is documented here. Network connections use IPM and NetworkManager