Line data Source code
1 : /**
2 : * @file dalMethods.cxx
3 : *
4 : * Implementations of Methods defined in confmodel schema classes
5 : *
6 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
7 : * Licensing/copyright details are in the COPYING file that you should have
8 : * received with this code.
9 : */
10 :
11 : #include "confmodel/Application.hpp"
12 : #include "confmodel/DaqApplication.hpp"
13 : #include "confmodel/DaqModule.hpp"
14 : #include "confmodel/DetDataReceiver.hpp"
15 : #include "confmodel/DetDataSender.hpp"
16 : #include "confmodel/DetectorStream.hpp"
17 : #include "confmodel/DetectorToDaqConnection.hpp"
18 : #include "confmodel/ExcludableEntity.hpp"
19 : #include "confmodel/ExcludableEntitySet.hpp"
20 : #include "confmodel/Jsonable.hpp"
21 : #include "confmodel/OpMonURI.hpp"
22 : #include "confmodel/PhysicalHost.hpp"
23 : #include "confmodel/RCApplication.hpp"
24 : #include "confmodel/Segment.hpp"
25 : #include "confmodel/Service.hpp"
26 : #include "confmodel/Session.hpp"
27 : #include "confmodel/VirtualHost.hpp"
28 : #include "confmodel/confmodelIssues.hpp"
29 :
30 : #include "confmodel/test_circular_dependency.hpp"
31 :
32 : #include "conffwk/ConfigObject.hpp"
33 : #include "conffwk/Configuration.hpp"
34 : #include "conffwk/Schema.hpp"
35 : #include "nlohmann/json.hpp"
36 :
37 : #include <iostream>
38 : #include <list>
39 : #include <set>
40 :
41 : using namespace dunedaq::conffwk;
42 :
43 : // Stolen from ATLAS dal package
44 : namespace {
45 : /**
46 : * Static function to calculate list of components
47 : * from the root segment to the lowest component which
48 : * the child object (a segment or a resource) belongs.
49 : */
50 :
51 : void
52 0 : make_parents_list(const ConfigObjectImpl* child,
53 : const dunedaq::confmodel::ExcludableEntitySet* resource_set,
54 : std::vector<const dunedaq::confmodel::ExcludableEntity*>& p_list,
55 : std::list<std::vector<const dunedaq::confmodel::ExcludableEntity*>>& out,
56 : dunedaq::confmodel::TestCircularDependency& cd_fuse)
57 : {
58 0 : dunedaq::confmodel::AddTestOnCircularDependency add_fuse_test(cd_fuse, resource_set);
59 :
60 : // add the resource set to the path
61 0 : p_list.push_back(resource_set);
62 :
63 : // check if the application is in the resource relationship, i.e. is a resource or belongs to resource set(s)
64 0 : for (const auto& i : resource_set->contained_excludable_entities()) {
65 0 : if (i->config_object().implementation() == child) {
66 0 : out.push_back(p_list);
67 0 : } else if (const dunedaq::confmodel::ExcludableEntitySet* rs = i->cast<dunedaq::confmodel::ExcludableEntitySet>()) {
68 0 : make_parents_list(child, rs, p_list, out, cd_fuse);
69 : }
70 0 : }
71 :
72 : // remove the resource set from the path
73 0 : p_list.pop_back();
74 0 : }
75 :
76 : void
77 0 : make_parents_list(const dunedaq::conffwk::ConfigObjectImpl* child,
78 : const dunedaq::confmodel::Segment* segment,
79 : std::vector<const dunedaq::confmodel::ExcludableEntity*>& p_list,
80 : std::list<std::vector<const dunedaq::confmodel::ExcludableEntity*>>& out,
81 : bool is_segment,
82 : dunedaq::confmodel::TestCircularDependency& cd_fuse)
83 : {
84 0 : dunedaq::confmodel::AddTestOnCircularDependency add_fuse_test(cd_fuse, segment);
85 :
86 : // add the segment to the path
87 0 : p_list.push_back(segment);
88 :
89 : // check if the application is in the nested segment
90 0 : for (const auto& seg : segment->get_segments()) {
91 0 : if (seg->config_object().implementation() == child)
92 0 : out.push_back(p_list);
93 : else
94 0 : make_parents_list(child, seg, p_list, out, is_segment, cd_fuse);
95 : }
96 0 : if (!is_segment) {
97 0 : for (const auto& app : segment->get_applications()) {
98 0 : if (app->config_object().implementation() == child)
99 0 : out.push_back(p_list);
100 0 : else if (const auto resource_set = app->cast<dunedaq::confmodel::ExcludableEntitySet>())
101 0 : make_parents_list(child, resource_set, p_list, out, cd_fuse);
102 : }
103 : }
104 :
105 : // remove the segment from the path
106 :
107 0 : p_list.pop_back();
108 0 : }
109 :
110 : void
111 0 : check_segment(std::list<std::vector<const dunedaq::confmodel::ExcludableEntity*>>& out,
112 : const dunedaq::confmodel::Segment* segment,
113 : const dunedaq::conffwk::ConfigObjectImpl* child,
114 : bool is_segment,
115 : dunedaq::confmodel::TestCircularDependency& cd_fuse)
116 : {
117 0 : dunedaq::confmodel::AddTestOnCircularDependency add_fuse_test(cd_fuse, segment);
118 :
119 0 : std::vector<const dunedaq::confmodel::ExcludableEntity*> compList;
120 :
121 0 : if (segment->config_object().implementation() == child) {
122 0 : out.push_back(compList);
123 : }
124 0 : make_parents_list(child, segment, compList, out, is_segment, cd_fuse);
125 0 : }
126 : } // namespace
127 :
128 : namespace dunedaq::confmodel {
129 :
130 : void
131 0 : ExcludableEntity::parents(const Session& session, std::list<std::vector<const ExcludableEntity*>>& parents) const
132 : {
133 0 : const ConfigObjectImpl* obj_impl = config_object().implementation();
134 :
135 0 : const bool is_segment = castable(Segment::s_class_name);
136 :
137 0 : try {
138 0 : TestCircularDependency cd_fuse("component parents", &session);
139 :
140 : // check session's segment
141 0 : check_segment(parents, session.get_segment(), obj_impl, is_segment, cd_fuse);
142 :
143 0 : if (parents.empty()) {
144 0 : TLOG_DEBUG(1) << "cannot find segment/resource path(s) between ExcludableEntity " << this << " and session "
145 0 : << &session << " objects (check this object is linked with the session as a segment or a resource)";
146 : }
147 0 : } catch (ers::Issue& ex) {
148 0 : ers::error(CannotGetParents(ERS_HERE, full_name(), ex));
149 0 : }
150 0 : }
151 :
152 : // ========================================================================
153 :
154 : std::vector<const Application*>
155 32 : Session::getSegmentApps(const Segment* segment, bool included_only) const
156 : {
157 32 : std::vector<const Application*> apps;
158 32 : auto segapps = segment->get_applications();
159 32 : if (included_only) {
160 0 : for (auto app : segapps) {
161 0 : auto comp = app->cast<ExcludableEntity>();
162 0 : if (comp == nullptr || !comp->is_excluded(*this)) {
163 0 : apps.insert(apps.end(), app);
164 : }
165 : }
166 : } else {
167 32 : apps.swap(segapps);
168 : }
169 48 : for (auto seg : segment->get_segments()) {
170 16 : if (!included_only || !seg->is_excluded(*this)) {
171 16 : auto segapps = getSegmentApps(seg, included_only);
172 16 : apps.insert(apps.end(), segapps.begin(), segapps.end());
173 16 : }
174 : }
175 32 : return apps;
176 32 : }
177 :
178 : std::vector<const Application*>
179 16 : Session::all_applications() const
180 : {
181 16 : std::vector<const Application*> apps;
182 16 : auto segapps = getSegmentApps(get_segment(), false);
183 16 : apps.insert(apps.end(), segapps.begin(), segapps.end());
184 16 : return apps;
185 16 : }
186 :
187 : std::vector<const Application*>
188 0 : Session::included_applications() const
189 : {
190 0 : std::vector<const Application*> apps;
191 0 : auto segapps = getSegmentApps(get_segment(), true);
192 0 : apps.insert(apps.end(), segapps.begin(), segapps.end());
193 0 : return apps;
194 0 : }
195 :
196 : // ========================================================================
197 :
198 : std::set<const HostComponent*>
199 0 : DaqApplication::get_used_host_components() const
200 : {
201 0 : std::set<const HostComponent*> res;
202 0 : for (auto module : get_modules()) {
203 0 : for (auto hostresource : module->get_used_host_components()) {
204 0 : res.insert(hostresource);
205 : }
206 : }
207 0 : return res;
208 0 : }
209 :
210 : namespace {
211 : nlohmann::json
212 0 : get_json_config(conffwk::Configuration& confdb,
213 : const std::string& class_name,
214 : const std::string& uid,
215 : bool direct_only,
216 : bool skip_object_name)
217 : {
218 0 : using nlohmann::json;
219 0 : using namespace conffwk;
220 0 : TLOG_DBG(9) << "Getting attributes for " << uid << " of class " << class_name;
221 0 : json attributes;
222 0 : auto class_info = confdb.get_class_info(class_name);
223 0 : ConfigObject obj;
224 0 : confdb.get(class_name, uid, obj);
225 0 : for (auto attr : class_info.p_attributes) {
226 0 : if (attr.p_type == type_t::u8_type) {
227 0 : add_json_value<uint8_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
228 : } else if (attr.p_type == type_t::u16_type) {
229 0 : add_json_value<uint16_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
230 : } else if (attr.p_type == type_t::u32_type) {
231 0 : add_json_value<uint32_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
232 : } else if (attr.p_type == type_t::u64_type) {
233 0 : add_json_value<uint64_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
234 : } else if (attr.p_type == type_t::s8_type) {
235 0 : add_json_value<int8_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
236 : } else if (attr.p_type == type_t::s16_type) {
237 0 : add_json_value<int16_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
238 : } else if (attr.p_type == type_t::s32_type || attr.p_type == type_t::s16_type) {
239 0 : add_json_value<int32_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
240 : } else if (attr.p_type == type_t::s64_type) {
241 0 : add_json_value<int64_t>(obj, attr.p_name, attr.p_is_multi_value, attributes);
242 : } else if (attr.p_type == type_t::float_type) {
243 0 : add_json_value<float>(obj, attr.p_name, attr.p_is_multi_value, attributes);
244 : } else if (attr.p_type == type_t::double_type) {
245 0 : add_json_value<double>(obj, attr.p_name, attr.p_is_multi_value, attributes);
246 : } else if (attr.p_type == type_t::bool_type) {
247 0 : add_json_value<bool>(obj, attr.p_name, attr.p_is_multi_value, attributes);
248 : } else if ((attr.p_type == type_t::string_type) || (attr.p_type == type_t::enum_type) ||
249 : (attr.p_type == type_t::date_type) || (attr.p_type == type_t::time_type)) {
250 0 : add_json_value<std::string>(obj, attr.p_name, attr.p_is_multi_value, attributes);
251 : }
252 0 : }
253 0 : if (!direct_only) {
254 0 : TLOG_DBG(9) << "Processing relationships";
255 0 : for (auto iter : class_info.p_relationships) {
256 0 : std::string rel_name = iter.p_name;
257 0 : if (iter.p_cardinality == cardinality_t::zero_or_one || iter.p_cardinality == cardinality_t::only_one) {
258 0 : ConfigObject rel_obj;
259 0 : obj.get(rel_name, rel_obj);
260 0 : if (!rel_obj.is_null()) {
261 0 : TLOG_DBG(9) << "Getting attibute of relationship " << rel_name;
262 0 : attributes[rel_name] =
263 0 : get_json_config(confdb, rel_obj.class_name(), rel_obj.UID(), direct_only, skip_object_name);
264 : } else {
265 0 : TLOG_DBG(9) << "Relationship " << rel_name << " not set";
266 : }
267 0 : } else {
268 0 : TLOG_DBG(9) << "Relationship " << rel_name << " is multi value. "
269 0 : << "Getting attibutes for relationship.";
270 0 : std::vector<ConfigObject> rel_vec;
271 0 : obj.get(rel_name, rel_vec);
272 0 : std::vector<json> configs;
273 0 : for (auto rel_obj : rel_vec) {
274 0 : TLOG_DBG(9) << "Getting attibute of relationship " << rel_obj.UID();
275 0 : auto rel_conf = get_json_config(confdb, rel_obj.class_name(), rel_obj.UID(), direct_only, skip_object_name);
276 0 : configs.push_back(rel_conf);
277 0 : }
278 0 : attributes[rel_name] = configs;
279 0 : }
280 0 : }
281 : }
282 :
283 0 : if (skip_object_name) {
284 0 : return attributes;
285 : }
286 0 : json json_config;
287 0 : json_config[uid] = attributes;
288 0 : return json_config;
289 0 : }
290 : } // namespace
291 :
292 : nlohmann::json
293 0 : Jsonable::to_json(bool direct_only, bool skip_object_name) const
294 : {
295 0 : return get_json_config(p_registry.configuration(), class_name(), UID(), direct_only, skip_object_name);
296 : }
297 :
298 : const std::vector<std::string>
299 0 : DaqApplication::construct_commandline_parameters(const conffwk::Configuration& confdb,
300 : const dunedaq::confmodel::Session* session) const
301 : {
302 :
303 0 : return construct_commandline_parameters_appfwk<dunedaq::confmodel::DaqApplication>(this, confdb, session);
304 : }
305 :
306 : const std::vector<std::string>
307 0 : RCApplication::construct_commandline_parameters(const conffwk::Configuration& confdb,
308 : const dunedaq::confmodel::Session* session) const
309 : {
310 :
311 0 : const std::string configuration_uri = confdb.get_impl_spec();
312 0 : const dunedaq::confmodel::Service* control_service = nullptr;
313 :
314 0 : const std::string controller_log_level = session->get_controller_log_level();
315 :
316 0 : for (auto const* as : get_exposes_service()) {
317 0 : if (as->UID().ends_with("_control")) {
318 0 : if (control_service)
319 0 : throw DuplicatedControlService(ERS_HERE, as->UID());
320 : control_service = as;
321 : }
322 : }
323 :
324 0 : if (control_service == nullptr)
325 0 : throw NoControlServiceDefined(ERS_HERE, UID());
326 :
327 0 : const std::string control_uri = control_service->get_protocol() + "://" + get_runs_on()->get_runs_on()->UID() + ":" +
328 0 : std::to_string(control_service->get_port());
329 :
330 0 : std::vector<std::string> ret = { "-l", controller_log_level };
331 0 : ret.push_back(configuration_uri);
332 0 : ret.push_back(control_uri);
333 0 : ret.push_back(UID());
334 0 : ret.push_back(session->UID());
335 0 : return ret;
336 0 : }
337 :
338 : std::vector<const confmodel::DetectorStream*>
339 64 : DetectorToDaqConnection::streams() const
340 : {
341 64 : std::vector<const confmodel::DetectorStream*> all_streams;
342 : // Loop over senders
343 128 : for (auto sender : this->senders()) {
344 64 : auto sender_streams = sender->get_streams();
345 64 : all_streams.insert(all_streams.end(), sender_streams.begin(), sender_streams.end());
346 128 : }
347 64 : return all_streams;
348 0 : }
349 :
350 : std::string
351 10 : OpMonURI::get_URI(const std::string& /* app */) const
352 : {
353 :
354 10 : auto type = get_type();
355 10 : if (type == "file") {
356 10 : return type + "://" + get_path();
357 : }
358 :
359 0 : if (type == "stream") {
360 0 : return type + "://" + get_path();
361 : }
362 :
363 0 : return "stdout://";
364 10 : }
365 :
366 : // ========================================================================
367 : void
368 0 : ExcludableEntityTree::exclude(const ExcludableEntity* res)
369 : {
370 0 : auto excluded_vec = get_excluded();
371 0 : for (auto excluded_resource : excluded_vec) {
372 0 : if (excluded_resource == res) {
373 0 : return;
374 : }
375 : }
376 0 : excluded_vec.push_back(res);
377 :
378 0 : set_excluded(excluded_vec);
379 0 : configuration().update<ExcludableEntityTree>({ UID() }, {}, {});
380 :
381 0 : m_excluded_entities.update(root_entity(), excluded_vec);
382 0 : }
383 : void
384 0 : ExcludableEntityTree::include(const ExcludableEntity* res)
385 : {
386 0 : auto excluded_vec = get_excluded();
387 0 : auto count = std::erase(excluded_vec, res);
388 0 : if (count == 0) {
389 0 : return;
390 : }
391 0 : set_excluded(excluded_vec);
392 0 : configuration().update<ExcludableEntityTree>({ UID() }, {}, {});
393 :
394 0 : m_excluded_entities.update(root_entity(), excluded_vec);
395 0 : }
396 :
397 : bool
398 263 : ExcludableEntity::is_excluded(const dunedaq::confmodel::ExcludableEntityTree& holder) const
399 : {
400 263 : return (!holder.excluded_entities().is_included(this));
401 : }
402 : bool
403 236 : ExcludableEntity::compute_excluded_state(const std::set<std::string>& excluded_resources) const
404 : {
405 236 : TLOG_DEBUG(6) << "No compute_excluded_state method defined for ExcludableEntity " << class_name();
406 236 : if (excluded_resources.contains(UID())) {
407 : return true;
408 : }
409 : return false;
410 : }
411 :
412 : std::vector<const ExcludableEntity*>
413 109 : DetDataSender::contained_excludable_entities() const
414 : {
415 109 : return to_resources(get_streams());
416 : }
417 :
418 : std::vector<const ExcludableEntity*>
419 32 : DetectorToDaqConnection::contained_excludable_entities() const
420 : {
421 32 : auto res = to_resources(senders());
422 32 : auto rec = receiver();
423 32 : if (rec)
424 32 : res.push_back(rec);
425 32 : return res;
426 0 : }
427 :
428 : bool
429 35 : DetectorToDaqConnection::compute_excluded_state(const std::set<std::string>& excluded_resources) const
430 : {
431 35 : if (excluded_resources.contains(UID())) {
432 : return true;
433 : }
434 35 : bool send_excluded = true;
435 43 : for (auto sender : senders()) {
436 40 : if (!sender->compute_excluded_state(excluded_resources)) {
437 32 : send_excluded = false;
438 32 : break;
439 : }
440 35 : }
441 35 : TLOG_DBG(6) << "receiver excluded=" << receiver()->compute_excluded_state(excluded_resources)
442 35 : << " senders excluded=" << send_excluded;
443 35 : auto rec = receiver();
444 35 : if (!rec)
445 0 : return send_excluded;
446 :
447 35 : return (rec->compute_excluded_state(excluded_resources) || send_excluded);
448 : }
449 :
450 : std::vector<const ExcludableEntity*>
451 25 : Segment::contained_excludable_entities() const
452 : {
453 : // All our contained segments are resources
454 25 : std::vector<const ExcludableEntity*> resources = to_resources(get_segments());
455 :
456 : // Only a subset of our applications might be resources so check individually
457 64 : for (auto app : get_applications()) {
458 39 : TLOG_DBG(6) << "Checking " << app->UID();
459 39 : auto res = app->cast<const ExcludableEntity>();
460 39 : if (res != nullptr) {
461 33 : TLOG_DBG(6) << "Adding " << app->UID();
462 33 : resources.push_back(res);
463 : }
464 : }
465 25 : TLOG_DBG(6) << "Returning vector of " << resources.size() << " resources";
466 25 : return resources;
467 0 : }
468 :
469 : bool
470 21 : Segment::compute_excluded_state(const std::set<std::string>& excluded) const
471 : {
472 21 : if (excluded.contains(UID())) {
473 : return true;
474 : }
475 36 : for (auto app : get_applications()) {
476 27 : auto res = app->cast<const ExcludableEntity>();
477 27 : if (res == nullptr) {
478 12 : return false;
479 : }
480 : }
481 13 : for (auto res : contained_excludable_entities()) {
482 12 : if (!res->compute_excluded_state(excluded)) {
483 8 : return false;
484 : }
485 9 : }
486 1 : return true;
487 : }
488 :
489 : } // namespace dunedaq::confmodel
|