Line data Source code
1 : /**
2 : * @file DFO.cpp
3 : *
4 : * Implementation of WIECApplication's generate_modules dal method
5 : *
6 : * This is part of the DUNE DAQ Software Suite, copyright 2023.
7 : * Licensing/copyright details are in the COPYING file that you should have
8 : * received with this code.
9 : */
10 :
11 : #include "ConfigObjectFactory.hpp"
12 : #include "conffwk/Configuration.hpp"
13 : #include "logging/Logging.hpp"
14 :
15 : #include "appmodel/NWDetDataReceiver.hpp"
16 : #include "confmodel/DetectorStream.hpp"
17 : #include "confmodel/GeoId.hpp"
18 : #include "confmodel/NetworkInterface.hpp"
19 :
20 : #include "appmodel/WIECApplication.hpp"
21 : #include "appmodel/appmodelIssues.hpp"
22 :
23 : #include "appmodel/HermesDataSender.hpp"
24 : #include "appmodel/HermesModule.hpp"
25 : #include "appmodel/HermesModuleConf.hpp"
26 : #include "appmodel/IpbusAddressTable.hpp"
27 : #include "appmodel/WIBModule.hpp"
28 : #include "appmodel/WIBModuleConf.hpp"
29 : #include "appmodel/WIBSettings.hpp"
30 : #include "confmodel/DetectorToDaqConnection.hpp"
31 :
32 : #include <fmt/core.h>
33 : #include <iostream>
34 : #include <string>
35 : #include <vector>
36 :
37 : namespace dunedaq {
38 : namespace appmodel {
39 :
40 : //-----------------------------------------------------------------------------
41 :
42 : std::vector<const confmodel::ExcludableEntity*>
43 0 : WIECApplication::contained_excludable_entities() const
44 : {
45 0 : return to_resources(get_detector_connections());
46 : }
47 :
48 : void
49 0 : WIECApplication::generate_modules(std::shared_ptr<appmodel::ConfigurationHelper> helper) const
50 : {
51 0 : ConfigObjectFactory obj_fac(this);
52 0 : conffwk::Configuration* config = &this->configuration();
53 0 : const std::string& dbfile = this->config_object().contained_in();
54 :
55 0 : std::vector<const confmodel::DaqModule*> modules;
56 :
57 0 : std::map<std::string, std::vector<const appmodel::HermesDataSender*>> ctrlhost_sender_map;
58 :
59 : // uint16_t conn_idx = 0;
60 0 : for (auto d2d_conn : get_detector_connections()) {
61 :
62 : // Are we sure?
63 0 : if (helper->is_excluded(d2d_conn)) {
64 0 : TLOG_DEBUG(7) << "Ignoring excluded DetectorToDaqConnection " << d2d_conn->UID();
65 0 : continue;
66 0 : }
67 :
68 0 : TLOG_DEBUG(6) << "Processing DetectorToDaqConnection " << d2d_conn->UID();
69 :
70 : // Is this check necessary?
71 0 : if (d2d_conn->contained_excludable_entities().empty()) {
72 0 : throw(BadConf(ERS_HERE, "DetectorToDaqConnection does not contain senders or receivers"));
73 : }
74 :
75 0 : auto det_senders = d2d_conn->senders();
76 0 : auto det_receiver = d2d_conn->receiver();
77 :
78 : // Ensure that receiver is a nw_receiver
79 0 : const auto* nw_receiver = det_receiver->cast<appmodel::NWDetDataReceiver>();
80 :
81 0 : if (!nw_receiver) {
82 0 : throw(BadConf(ERS_HERE,
83 0 : fmt::format("WEICApplication requires NWDetDataReceiver, found {} of class {}",
84 0 : det_receiver->UID(),
85 0 : det_receiver->class_name())));
86 : }
87 :
88 : // Note on how to exclude the senders.
89 : // The hardware interface requires that all the physical links be configured, even if not used for a run.
90 : // Consequently, at the configuration level, we should never remove senders from a detector to daq connection.
91 : // At most, we should exclude them at the level of the session.
92 : // For the same reason, in this code, we create a map of control hosts to senders, without checking if the senders
93 : // are excluded. Once the map is created, if all the senders associated to a control hosts are excluded, then we
94 : // skip the creation of all related modules.
95 :
96 : // Loop over senders to create the map of control hosts to senders.
97 0 : for (const auto* sender : det_senders) {
98 :
99 : // Check the sender type, must be a HermesSender
100 0 : const auto* hrms_sender = sender->cast<appmodel::HermesDataSender>();
101 0 : if (!hrms_sender) {
102 0 : throw(BadConf(ERS_HERE, fmt::format("DataSender {} is not a appmodel::HermesDataSender", sender->UID())));
103 : }
104 :
105 0 : ctrlhost_sender_map[hrms_sender->get_control_host()].push_back(hrms_sender);
106 : }
107 :
108 0 : for (const auto& [ctrlhost, senders] : ctrlhost_sender_map) {
109 :
110 : // If all senders for this control host are excluded, skip creating related modules.
111 : // Of course the opposite logic is faster: check if any sender is included.
112 0 : bool any_included = false;
113 0 : for (const auto* sender : senders) {
114 0 : if (helper->is_included(sender)) {
115 : any_included = true;
116 : break;
117 : }
118 : }
119 0 : if (!any_included) {
120 0 : TLOG_DEBUG(6) << "Skipping control host " << ctrlhost << " whose senders are all excluded.";
121 0 : continue;
122 0 : }
123 :
124 : // Create WIBModule
125 0 : if (this->get_wib_module_conf()) {
126 :
127 0 : bool enable_fembs[4] = { false, false, false, false };
128 :
129 0 : for (const auto* sender : senders) {
130 0 : for (const auto* det_stream : sender->get_streams()) {
131 : // Loop over streams for this sender
132 : // Retrieve stream_id and calculate the femb_id
133 0 : uint32_t stream_id = det_stream->get_geo_id()->get_stream_id();
134 0 : uint32_t femb_id = (stream_id & 0xf) / 2 + 2 * ((stream_id >> 6) & 0xf);
135 :
136 : // std::cout << std::format("stream {} -> femb {}", stream_id, femb_id) << std::endl;
137 :
138 : // Enable the femb if any of the associated streams is enabled
139 0 : enable_fembs[femb_id] |= helper->is_included(det_stream);
140 : } // loop over streams
141 : } // loop over senders for this control host
142 :
143 0 : std::string wib_uid = fmt::format("wib-ctrl-{}-{}", this->UID(), ctrlhost);
144 0 : conffwk::ConfigObject wib_obj = obj_fac.create("WIBModule", wib_uid);
145 0 : wib_obj.set_by_val<std::string>("wib_addr",
146 0 : fmt::format("{}://{}:{}",
147 0 : this->get_wib_module_conf()->get_communication_type(),
148 : ctrlhost,
149 0 : this->get_wib_module_conf()->get_communication_port()));
150 0 : for (int i = 0; i < 4; ++i) {
151 0 : wib_obj.set_by_val<bool>(fmt::format("enabled_femb{}", i), enable_fembs[i]);
152 : }
153 0 : wib_obj.set_obj("conf", &this->get_wib_module_conf()->get_settings()->config_object());
154 0 : modules.push_back(config->get<appmodel::WIBModule>(wib_obj));
155 0 : } // if we have a module configuration for the WIB module
156 :
157 : // Create Hermes Modules
158 0 : if (this->get_hermes_module_conf()) {
159 0 : std::string hermes_uid = fmt::format("hermes-ctrl-{}-{}", this->UID(), ctrlhost);
160 0 : conffwk::ConfigObject hermes_obj = obj_fac.create("HermesModule", hermes_uid);
161 0 : hermes_obj.set_obj("address_table", &this->get_hermes_module_conf()->get_address_table()->config_object());
162 0 : hermes_obj.set_by_val<std::string>("uri",
163 0 : fmt::format("{}://{}:{}",
164 0 : this->get_hermes_module_conf()->get_ipbus_type(),
165 : ctrlhost,
166 0 : this->get_hermes_module_conf()->get_ipbus_port()));
167 0 : hermes_obj.set_by_val<uint32_t>("timeout_ms", this->get_hermes_module_conf()->get_ipbus_timeout_ms());
168 0 : hermes_obj.set_obj("destination", &nw_receiver->get_uses()->config_object());
169 :
170 0 : std::vector<const conffwk::ConfigObject*> links_obj;
171 0 : for (const auto* sndr : senders) {
172 : // Note that it is OK that some of these senders might be excluded
173 : // The hardware interface in HermesModule requires that all links be configured, even if not used for a run
174 0 : links_obj.push_back(&sndr->config_object());
175 : }
176 0 : hermes_obj.set_objs("links", links_obj);
177 :
178 0 : modules.push_back(config->get<appmodel::HermesModule>(hermes_obj));
179 0 : } // If we have a module configuration for the Hermes module
180 :
181 : } // loop over control hosts
182 :
183 0 : } // loop over detector to daq connections
184 :
185 0 : obj_fac.update_modules(modules);
186 0 : }
187 :
188 : } // namespace appmodel
189 : } // namespace dunedaq
|