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