Line data Source code
1 : /**
2 : * @file DaphneApplication.cpp
3 : *
4 : * Implementation of DaphneApplication'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 "conffwk/Configuration.hpp"
12 : #include "oks/kernel.hpp"
13 : #include "logging/Logging.hpp"
14 :
15 : #include "confmodel/GeoId.hpp"
16 : #include "confmodel/DetectorStream.hpp"
17 : #include "confmodel/NetworkInterface.hpp"
18 :
19 : #include "ConfigObjectFactory.hpp"
20 : #include "appmodel/appmodelIssues.hpp"
21 : #include "appmodel/FelixDataSender.hpp"
22 : #include "appmodel/DaphneConf.hpp"
23 : #include "appmodel/DaphneMapEntry.hpp"
24 : #include "appmodel/DaphneV2BoardConf.hpp"
25 : #include "appmodel/DaphneV2Channel.hpp"
26 : #include "appmodel/DaphneV2AFE.hpp"
27 : #include "appmodel/DaphneV2ADC.hpp"
28 : #include "appmodel/DaphneV2PGA.hpp"
29 : #include "appmodel/DaphneV2LNA.hpp"
30 : #include "appmodel/DaphneV2ControllerModule.hpp"
31 : #include "appmodel/DaphneV3ControllerModule.hpp"
32 : #include "appmodel/DaphneApplication.hpp"
33 : #include "appmodel/FelixDetectorToDaqConnection.hpp"
34 : #include "appmodel/NetworkDetectorToDaqConnection.hpp"
35 : #include "appmodel/NWDetDataSender.hpp"
36 : #include "appmodel/NWDetDataReceiver.hpp"
37 : #include "appmodel/HermesDataSender.hpp"
38 : #include "appmodel/HermesModuleConf.hpp"
39 : #include "appmodel/HermesModule.hpp"
40 : #include "appmodel/IpbusAddressTable.hpp"
41 :
42 : #include <string>
43 : #include <vector>
44 : #include <bitset>
45 : #include <iostream>
46 : #include <fmt/core.h>
47 : #include <set>
48 : #include <map>
49 :
50 : namespace dunedaq::appmodel {
51 :
52 : std::vector<const confmodel::Resource*>
53 0 : DaphneApplication::contained_resources() const {
54 0 : return to_resources(get_detector_connections());
55 : }
56 :
57 : void
58 0 : DaphneApplication::generate_modules(std::shared_ptr<appmodel::ConfigurationHelper> helper) const
59 : {
60 0 : ConfigObjectFactory obj_fac(this);
61 :
62 0 : std::vector<const confmodel::DaqModule*> modules;
63 :
64 0 : auto daphne_conf = get_configuration();
65 :
66 0 : std::map<std::string, const DaphneV2BoardConf*> conf_map;
67 0 : auto confs = daphne_conf->get_boards();
68 0 : for ( const auto & c : confs ) {
69 0 : conf_map[c->get_key()] = c->get_conf();
70 : }
71 :
72 : // these maps are all indexed on the board id {detector].{crate}.{slot}
73 0 : std::map<std::string, bool> v3_map;
74 0 : std::map<std::string, const confmodel::NetworkInterface*> interfaces;
75 0 : std::map<std::string, std::string> ctrl_hosts;
76 :
77 : // map from ctrl_host to senders
78 0 : std::map<std::string, std::vector<const appmodel::HermesDataSender*> > hermes_senders;
79 :
80 0 : for (auto d2d_conn : get_detector_connections()) {
81 :
82 : // A Resource can be disabled and still its application can be enabled because the application can have multile resources, so we need to check which resources are enabled
83 0 : if (helper->is_disabled(d2d_conn)) {
84 0 : TLOG_DEBUG(7) << "Ignoring disabled DetectorToDaqConnection " << d2d_conn->UID();
85 0 : continue;
86 0 : }
87 :
88 0 : TLOG_DEBUG(6) << "Processing DetectorToDaqConnection " << d2d_conn->UID();
89 : // get the readout groups and the interfaces and streams therein; 1 reaout group corresponds to 1 data reader module
90 :
91 : // Redundant? Schema forbids 0 connections
92 0 : if (d2d_conn->contained_resources().empty()) {
93 0 : throw(BadConf(ERS_HERE, "DetectorToDaqConnection does not contain senders or receivers"));
94 : }
95 :
96 0 : auto flx_conn = dynamic_cast<const appmodel::FelixDetectorToDaqConnection *>( d2d_conn ); // NOLINT(runtime/rtti)
97 0 : auto net_conn = dynamic_cast<const appmodel::NetworkDetectorToDaqConnection *>( d2d_conn ); // NOLINT(runtime/rtti)
98 :
99 0 : if ( ! net_conn && ! flx_conn) {
100 0 : throw BadConf(ERS_HERE, d2d_conn->UID() + " is neither felix or eth connection");
101 : }
102 :
103 0 : if ( flx_conn ) {
104 0 : auto det_senders = flx_conn->get_felix_senders();
105 :
106 : // Loop over senders
107 0 : for (const auto* felix_sender : det_senders) {
108 :
109 0 : if ( helper->is_disabled(felix_sender) ) {
110 0 : TLOG() << "Skipping disabled sender: " << felix_sender->UID();
111 0 : continue;
112 0 : }
113 :
114 0 : auto ip = felix_sender -> get_control_host();
115 :
116 : // from the felix sender we get the DetStream and then the GeoID
117 :
118 0 : auto streams = felix_sender -> get_streams();
119 :
120 0 : for ( const auto * det_s : streams ) {
121 :
122 0 : if ( helper->is_disabled(det_s) ) {
123 0 : TLOG() << "Skipping disabled DetStream: " << det_s->UID();
124 0 : continue;
125 0 : }
126 :
127 0 : auto geo_id = det_s->get_geo_id();
128 0 : auto id = fmt::format("{}.{}.{}", geo_id->get_detector_id(), geo_id->get_crate_id(), geo_id->get_slot_id());
129 0 : if (!v3_map.contains(id)) {
130 0 : v3_map[id] = false;
131 : }
132 :
133 0 : } // loop over DetStreams
134 :
135 0 : } // loop over det_senders
136 0 : } // if flx connection
137 :
138 0 : if ( net_conn ) {
139 0 : auto det_senders = net_conn->get_net_senders();
140 :
141 0 : for ( const auto* nw_sender : det_senders ) {
142 0 : if ( helper->is_disabled(nw_sender) ) {
143 0 : TLOG() << "Skipping disabled sender: " << nw_sender->UID();
144 0 : continue;
145 0 : }
146 :
147 : // Check the sender type, must me a HermesSender
148 0 : const auto* hrms_sender = nw_sender->cast<appmodel::HermesDataSender>();
149 0 : if (!hrms_sender ) {
150 0 : throw(BadConf(ERS_HERE, fmt::format("DataSender {} is not a appmodel::HermesDataSender", nw_sender->UID())));
151 : }
152 :
153 0 : hermes_senders[hrms_sender->get_control_host()].push_back(hrms_sender);
154 :
155 0 : auto streams = nw_sender -> get_streams();
156 0 : for ( const auto * det_s : streams ) {
157 :
158 0 : if ( helper->is_disabled(det_s) ) {
159 0 : TLOG() << "Skipping disabled DetStream: " << det_s->UID();
160 0 : continue;
161 0 : }
162 :
163 0 : auto geo_id = det_s->get_geo_id();
164 0 : auto id = fmt::format("{}.{}.{}", geo_id->get_detector_id(), geo_id->get_crate_id(), geo_id->get_slot_id());
165 0 : if (!v3_map.contains(id)) {
166 0 : v3_map[id] = true;
167 0 : interfaces[id] = net_conn->get_net_receiver()->get_uses();
168 0 : ctrl_hosts[id] = hrms_sender->get_control_host();
169 : }
170 :
171 0 : } // loop over streams
172 :
173 0 : } // loop over NW senders
174 :
175 0 : } // if net_connection
176 :
177 : } // loop over det2DAQ Connections
178 :
179 :
180 :
181 :
182 0 : for ( const auto & [id, v3] : v3_map ) {
183 :
184 0 : auto conf_it = conf_map.find(id);
185 0 : if ( conf_it == conf_map.end() ) {
186 0 : throw MissingDaphne(ERS_HERE, id);
187 : }
188 0 : auto conf = conf_it->second;
189 :
190 0 : conffwk::ConfigObject module_obj = obj_fac.create( (v3 ? "DaphneV3ControllerModule" : "DaphneV2ControllerModule"), fmt::format("controller-{}", id) );
191 0 : module_obj.set_obj("daphne_conf", & daphne_conf -> config_object() );
192 0 : module_obj.set_obj("board_conf", & conf -> config_object() );
193 :
194 0 : auto module = obj_fac.get_dal<confmodel::DaqModule>(module_obj);
195 0 : modules.push_back(module);
196 :
197 :
198 : // Create Hermes Modules
199 0 : if (v3) {
200 0 : std::string hermes_uid = fmt::format("daphne-hermes-ctrl-{}", id);
201 0 : conffwk::ConfigObject hermes_obj = obj_fac.create("HermesModule", hermes_uid);
202 0 : hermes_obj.set_obj("address_table", &this->get_hermes_module_conf()->get_address_table()->config_object());
203 0 : hermes_obj.set_by_val<std::string>("uri", fmt::format("{}://{}:{}", this->get_hermes_module_conf()->get_ipbus_type(), ctrl_hosts[id], this->get_hermes_module_conf()->get_ipbus_port()));
204 0 : hermes_obj.set_by_val<uint32_t>("timeout_ms", this->get_hermes_module_conf()->get_ipbus_timeout_ms()); // NOLINT
205 0 : hermes_obj.set_obj("destination", & interfaces[id]->config_object());
206 :
207 0 : std::vector< const conffwk::ConfigObject * > links_obj;
208 0 : const auto & senders = hermes_senders[ctrl_hosts[id]];
209 0 : for ( const auto* sndr : senders ){
210 0 : links_obj.push_back(&sndr->config_object());
211 : }
212 0 : hermes_obj.set_objs("links", links_obj);
213 :
214 0 : modules.push_back(obj_fac.get_dal<appmodel::HermesModule>(hermes_obj));
215 :
216 0 : }
217 :
218 0 : } // ips
219 :
220 0 : obj_fac.update_modules(modules);
221 0 : } // NOLINT
222 :
223 :
224 : bool
225 0 : DaphneV2BoardConf::is_channel_used(size_t ch) const {
226 :
227 0 : for ( auto ch_p : get_active_channels() ) {
228 0 : if ( ch_p->get_channel_id() == ch ) {
229 0 : return true;
230 : }
231 : }
232 :
233 0 : return false;
234 : }
235 :
236 : const DaphneV2Channel &
237 0 : DaphneV2BoardConf::get_channel(size_t ch) const {
238 :
239 0 : for ( auto ch_p : get_active_channels() ) {
240 0 : if ( ch_p->get_channel_id() == ch ) {
241 0 : return *ch_p;
242 : }
243 : }
244 :
245 0 : return *get_default_channel();
246 : }
247 :
248 : bool
249 0 : DaphneV2BoardConf::is_afe_used(size_t afe) const {
250 :
251 0 : auto begin = afe*8;
252 0 : auto end = (afe+1)*8;
253 0 : for ( size_t i = begin; i < end; ++i) {
254 0 : if( is_channel_used(i) ) return true;
255 : }
256 :
257 : return false;
258 : }
259 :
260 : const DaphneV2AFE &
261 0 : DaphneV2BoardConf::get_afe(size_t ch) const {
262 :
263 0 : if ( ! is_afe_used(ch) ) return *get_default_afe();
264 :
265 0 : for ( auto afe_p : get_active_afes() ) {
266 0 : if ( afe_p->get_afe_id() == ch ) {
267 0 : return *afe_p;
268 : }
269 : }
270 :
271 0 : throw appmodel::MissingAFE(ERS_HERE, UID(), ch);
272 : }
273 :
274 :
275 : uint16_t
276 0 : DaphneV2ADC::get_reg4() const {
277 :
278 : // ADC, reg 4 has no parsing as it's all made of booleans
279 0 : std::bitset<5> reg4;
280 : // bits 0 and 2 are reserved
281 0 : reg4[1] = get_low_resolution();
282 0 : reg4[3] = get_output_offset_binary();
283 0 : reg4[4] = get_MSB_first();
284 0 : return reg4.to_ulong();
285 : }
286 :
287 : uint16_t
288 0 : DaphneV2PGA::get_reg51() const {
289 :
290 0 : std::bitset<14> reg51(get_lpf_cut_frequency());
291 0 : reg51 <<= 1;
292 0 : reg51[4] = get_integrator_disable();
293 0 : reg51[7] = true; // clamp is always disabled and we are in low noise mode
294 0 : reg51[13] = get_gain();
295 :
296 0 : return reg51.to_ulong() ;
297 : }
298 :
299 : uint16_t
300 0 : DaphneV2LNA::get_reg52() const {
301 :
302 0 : std::bitset<16> reg52;
303 :
304 0 : decltype(reg52) clamp(get_clamp());
305 0 : clamp <<= 6;
306 :
307 0 : reg52[12] = get_integrator_disable();
308 :
309 0 : decltype(reg52) gain(get_gain());
310 0 : clamp <<= 13;
311 :
312 0 : reg52 |= clamp;
313 0 : reg52 |= gain;
314 :
315 0 : return reg52.to_ulong();
316 : }
317 :
318 : } // namespace dunedaq::appmodel
|