Project
Loading...
Searching...
No Matches
scan-hvlv-ccdb.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include <algorithm>
13#include <ctime>
14#include <fstream>
15#include <iterator>
16#include <limits>
17#include <map>
18#include <set>
19#include <sstream>
20#include <string>
21#include <tuple>
22#include <unordered_map>
23#include <utility>
24#include <vector>
25
26#include <boost/program_options.hpp>
27
28#include <fmt/format.h>
29
30#include "TFile.h"
31#include "TCanvas.h"
32#include "TGraph.h"
33#include "TH1F.h"
34#include "TLine.h"
35#include "TMultiGraph.h"
36#include "TStyle.h"
37
39#include "CCDB/CcdbApi.h"
46
47namespace po = boost::program_options;
48
49using namespace o2;
52using DPMAP = std::unordered_map<DPID, std::vector<DPVAL>>;
53using DPMAP2 = std::map<std::string, std::map<uint64_t, double>>;
54using RBMAP = std::map<int, std::pair<uint64_t, uint64_t>>;
55using DPBMAP = std::map<uint64_t, uint64_t>;
56using ISSUE = std::tuple<uint64_t, uint64_t, double, double, std::string>;
57using ISSUELIST = std::vector<ISSUE>;
58using ISSUEMAP = std::map<std::string, ISSUELIST>;
59using ULL = unsigned long long;
60
61//----------------------------------------------------------------------------
62bool containsAKey(std::string data, const std::set<std::string>& Keys)
63{
65
66 auto itKey = std::find_if(Keys.begin(), Keys.end(), [&data](const auto& key) {
67 return data.find(key) != data.npos;
68 });
69
70 return itKey != Keys.end();
71}
72
73//----------------------------------------------------------------------------
74bool isValid(std::string alias)
75{
77
78 static const std::vector<std::string> aliases =
79 mch::dcs::aliases({mch::dcs::MeasurementType::HV_V,
80 mch::dcs::MeasurementType::LV_V_FEE_ANALOG,
81 mch::dcs::MeasurementType::LV_V_FEE_DIGITAL,
82 mch::dcs::MeasurementType::LV_V_SOLAR});
83
84 auto itAlias = std::find_if(aliases.begin(), aliases.end(), [&alias](const auto& a) {
85 return a.find(alias) != a.npos;
86 });
87
88 return itAlias != aliases.end();
89}
90
91//----------------------------------------------------------------------------
92void scanWhat(std::string what, std::string& path, bool& scanHV, bool& scanAll, std::set<std::string>& aliases)
93{
95
96 static const std::set<std::string> hvKeys{"HV", "Quad", "Slat"};
97 static const std::set<std::string> lvKeys{"LV", "Group", "an", "di", "Sol"};
98
99 // HV or LV ?
100 path = "";
101 scanHV = false;
102 if (containsAKey(what, hvKeys)) {
103 path = "MCH/Calib/HV";
104 scanHV = true;
105 }
106 if (containsAKey(what, lvKeys)) {
107 if (scanHV) {
108 printf("error: cannot scan HV and LV channels at the same time\n");
109 exit(1);
110 }
111 path = "MCH/Calib/LV";
112 }
113 if (path.empty()) {
114 printf("error: no valid HV or LV channel to scan\n");
115 exit(1);
116 }
117
118 // everything or specific aliases ?
119 if (what.find(scanHV ? "HV" : "LV") != what.npos) {
120 scanAll = true;
121 aliases.clear();
122 } else {
123 scanAll = false;
124 std::istringstream input(what);
125 for (std::string alias; std::getline(input, alias, ',');) {
126 if (isValid(alias)) {
127 aliases.insert(alias);
128 } else {
129 printf("error: \"%s\" invalid (part of) HV or LV alias\n", alias.c_str());
130 exit(1);
131 }
132 }
133 }
134}
135
136//----------------------------------------------------------------------------
137uint64_t ms2s(uint64_t ts)
138{
140
141 return (ts + 500) / 1000;
142}
143
144//----------------------------------------------------------------------------
145std::string getTime(uint64_t ts)
146{
148
149 time_t t = ms2s(ts);
150
151 std::string time = std::ctime(&t);
152 time.pop_back(); // remove trailing \n
153
154 return time;
155}
156
157//----------------------------------------------------------------------------
158std::string getDuration(uint64_t tStart, uint64_t tStop)
159{
161
162 auto dt = ms2s(tStop - tStart);
163 auto s = dt % 60;
164 auto m = (dt / 60) % 60;
165 auto h = (dt / 3600) % 24;
166 auto d = dt / 86400;
167
168 return fmt::format("{:02}d {:02}:{:02}:{:02}", d, h, m, s);
169}
170
171//----------------------------------------------------------------------------
172std::set<int> getRuns(std::string runList)
173{
175
176 std::set<int> runs{};
177
178 auto isNumber = [](std::string val) { return !val.empty() && val.find_first_not_of("0123456789") == val.npos; };
179
180 if (isNumber(runList)) {
181
182 runs.insert(std::stoi(runList));
183
184 } else if (runList.find(",") != runList.npos) {
185
186 std::istringstream input(runList);
187 for (std::string run; std::getline(input, run, ',');) {
188 if (isNumber(run)) {
189 runs.insert(std::stoi(run));
190 }
191 }
192
193 } else {
194
195 std::ifstream input(runList);
196 if (input.is_open()) {
197 for (std::string run; std::getline(input, run);) {
198 if (isNumber(run)) {
199 runs.insert(std::stoi(run));
200 }
201 }
202 }
203 }
204
205 return runs;
206}
207
208//----------------------------------------------------------------------------
209RBMAP getRunBoundaries(ccdb::CcdbApi const& api, std::string runList)
210{
212
213 RBMAP runBoundaries{};
214
215 auto runs = getRuns(runList);
216
217 for (auto run : runs) {
218 auto boundaries = ccdb::CCDBManagerInstance::getRunDuration(api, run);
219 runBoundaries.emplace(run, boundaries);
220 }
221
222 return runBoundaries;
223}
224
225//----------------------------------------------------------------------------
226void checkRunBoundaries(const RBMAP& runBoundaries)
227{
229
230 if (runBoundaries.empty()) {
231 printf("error: no run found from the list\n");
232 exit(1);
233 }
234
235 bool error = false;
236 int previousRun = 0;
237 uint64_t endOfPreviousRun = 0;
238
239 for (const auto& [run, boundaries] : runBoundaries) {
240 if (boundaries.second <= boundaries.first) {
241 printf("error: run %d EOR <= SOR: %llu - %llu (%s - %s)\n",
242 run, (ULL)boundaries.first, (ULL)boundaries.second,
243 getTime(boundaries.first).c_str(), getTime(boundaries.second).c_str());
244 error = true;
245 }
246 if (boundaries.first <= endOfPreviousRun) {
247 printf("error: SOR run %d <= EOR run %d: %llu (%s) <= %llu (%s)\n",
248 run, previousRun, (ULL)boundaries.first, getTime(boundaries.first).c_str(),
249 (ULL)endOfPreviousRun, getTime(endOfPreviousRun).c_str());
250 error = true;
251 }
252 previousRun = run;
253 endOfPreviousRun = boundaries.second;
254 }
255
256 if (error) {
257 exit(1);
258 }
259}
260
261//----------------------------------------------------------------------------
262void printRunBoundaries(const RBMAP& runBoundaries)
263{
265
266 printf("\nlist of runs with their boundaries:\n");
267 printf("------------------------------------\n");
268
269 for (const auto& [run, boundaries] : runBoundaries) {
270 printf("%d: %llu - %llu (%s - %s)\n", run, (ULL)boundaries.first, (ULL)boundaries.second,
271 getTime(boundaries.first).c_str(), getTime(boundaries.second).c_str());
272 }
273
274 printf("------------------------------------\n");
275}
276
277//----------------------------------------------------------------------------
278void drawRunBoudaries(const RBMAP& runBoundaries, TCanvas* c)
279{
281
282 c->cd();
283
284 for (const auto& [run, boundaries] : runBoundaries) {
285
286 TLine* startRunLine = new TLine(ms2s(boundaries.first), c->GetUymin(), ms2s(boundaries.first), c->GetUymax());
287 startRunLine->SetUniqueID(run);
288 startRunLine->SetLineColor(4);
289 startRunLine->SetLineWidth(1);
290 startRunLine->Draw();
291
292 TLine* endRunLine = new TLine(ms2s(boundaries.second), c->GetUymin(), ms2s(boundaries.second), c->GetUymax());
293 endRunLine->SetUniqueID(run);
294 endRunLine->SetLineColor(2);
295 endRunLine->SetLineWidth(1);
296 endRunLine->Draw();
297 }
298}
299
300//----------------------------------------------------------------------------
301DPBMAP getDPBoundaries(ccdb::CcdbApi const& api, std::string what,
302 uint64_t tStart, uint64_t tStop, uint64_t timeInterval)
303{
305
306 // add an extra margin (ms) of ± 1 min to the creation time,
307 // which corresponds to the end of the time interval covered by the file
308 static const uint64_t timeMarging = 60000;
309
310 std::istringstream fileInfo(api.list(what.c_str(), false, "text/plain",
311 tStop + timeInterval + timeMarging, tStart - timeMarging));
312
313 DPBMAP dpBoundaries{};
314 std::string dummy{};
315 uint64_t begin = 0;
316 uint64_t end = 0;
317
318 for (std::string line; std::getline(fileInfo, line);) {
319 if (line.find("Validity:") == 0) {
320 std::istringstream in(line);
321 in >> dummy >> begin >> dummy >> end;
322 dpBoundaries.emplace(begin, end);
323 }
324 }
325
326 if (dpBoundaries.empty()) {
327 printf("\e[0;31merror: no file found in %s in time range %llu - %llu (%s - %s) --> use the default one\e[0m\n",
328 what.c_str(), (ULL)tStart, (ULL)tStop, getTime(tStart).c_str(), getTime(tStop).c_str());
329 dpBoundaries.emplace(1, 9999999999999);
330 }
331
332 return dpBoundaries;
333}
334
335//----------------------------------------------------------------------------
336void checkDPBoundaries(const DPBMAP& dpBoundaries, bool scanHV, uint64_t tStart, uint64_t tStop)
337{
339
340 bool error = false;
341
342 if (dpBoundaries.begin()->first > tStart) {
343 printf("error: the beginning of the time range is not covered: %llu > %llu (%s > %s)\n",
344 (ULL)dpBoundaries.begin()->first, (ULL)tStart,
345 getTime(dpBoundaries.begin()->first).c_str(), getTime(tStart).c_str());
346 error = true;
347 }
348 if (dpBoundaries.rbegin()->second < tStop) {
349 printf("error: the end of the time range is not covered: %llu < %llu (%s < %s)\n",
350 (ULL)dpBoundaries.rbegin()->second, (ULL)tStop,
351 getTime(dpBoundaries.rbegin()->second).c_str(), getTime(tStop).c_str());
352 error = true;
353 }
354
355 uint64_t previousTStop = dpBoundaries.begin()->first;
356 for (auto [tStart, tStop] : dpBoundaries) {
357 if (tStop <= tStart) {
358 printf("error: EOF <= SOF: %llu - %llu (%s - %s)\n",
359 (ULL)tStart, (ULL)tStop, getTime(tStart).c_str(), getTime(tStop).c_str());
360 error = true;
361 }
362 if (tStart != previousTStop) {
363 printf("error: end of %s file != start of next %s file: %llu (%s) != %llu (%s))\n",
364 scanHV ? "HV" : "LV", scanHV ? "HV" : "LV",
365 (ULL)previousTStop, getTime(previousTStop).c_str(), (ULL)tStart, getTime(tStart).c_str());
366 error = true;
367 }
368 previousTStop = tStop;
369 }
370
371 if (error) {
372 exit(1);
373 }
374}
375
376//----------------------------------------------------------------------------
377void printDPBoundaries(const DPBMAP& dpBoundaries, bool scanHV, uint64_t timeInterval)
378{
380
381 printf("\nlist of %s file time boundaries:\n", scanHV ? "HV" : "LV");
382 printf("------------------------------------\n");
383
384 for (auto [tStart, tStop] : dpBoundaries) {
385 printf("%llu - %llu (%s - %s)", (ULL)tStart, (ULL)tStop, getTime(tStart).c_str(), getTime(tStop).c_str());
386 if (tStop - tStart < 60000 * (timeInterval - 1) || tStop - tStart > 60000 * (timeInterval + 1)) {
387 printf("\e[0;31m ! warning: validity range %s != %llu±1 min\e[0m\n",
388 getDuration(tStart, tStop).c_str(), (ULL)timeInterval);
389 } else {
390 printf("\n");
391 }
392 }
393
394 printf("------------------------------------\n");
395}
396
397//----------------------------------------------------------------------------
398double getLVLimit(std::string alias)
399{
401
402 static const double lvLimits[3] = {1.5, 1.5, 6.}; // FeeAnalog, FeeDigital, Solar
403
404 if (alias.find("an") != alias.npos) {
405 return lvLimits[0];
406 } else if (alias.find("di") != alias.npos) {
407 return lvLimits[1];
408 }
409 return lvLimits[2];
410}
411
412//----------------------------------------------------------------------------
413void drawLimit(double limit, TCanvas* c)
414{
416
417 c->cd();
418
419 TLine* l = new TLine(c->GetUxmin(), limit, c->GetUxmax(), limit);
420 l->SetLineColor(1);
421 l->SetLineWidth(1);
422 l->SetLineStyle(2);
423 l->Draw();
424}
425
426//----------------------------------------------------------------------------
427double getValue(DPVAL dp)
428{
430
431 union Converter {
432 uint64_t raw_data;
433 double value;
434 } converter;
435
436 converter.raw_data = dp.payload_pt1;
437
438 return converter.value;
439}
440
441//----------------------------------------------------------------------------
442std::string getDE(std::string alias)
443{
446
447 auto de = mch::dcs::aliasToDetElemId(alias);
448
449 if (de) {
451 ? fmt::format("DE{}-{}", *de, mch::dcs::aliasToNumber(alias) % 10)
452 : fmt::format("DE{}", *de);
453 }
454
455 return "";
456}
457
458//----------------------------------------------------------------------------
459void fillDataPoints(const std::vector<DPVAL>& dps, std::map<uint64_t, double>& dps2,
460 uint64_t tMin, uint64_t tMax, int warningLevel)
461{
463
464 static const uint64_t tolerance = 5000;
465
466 if (dps.empty()) {
467 printf("error: the file does not contain any data point\n");
468 exit(1);
469 }
470
471 auto itDP = dps.begin();
472 auto ts = itDP->get_epoch_time();
473 std::string header = "warning:";
474 std::string color = (ts + tolerance < tMin || ts > tMin + tolerance) ? "\e[0;31m" : "\e[0;34m";
475 bool printWarning = warningLevel > 1 || (warningLevel == 1 && color == "\e[0;31m");
476
477 // check if the first data point is a copy of the last one from previous file
478 if (!dps2.empty()) {
479 auto previousTS = dps2.rbegin()->first;
480 if (ts != previousTS || getValue(*itDP) != dps2.rbegin()->second) {
481 if (ts <= previousTS) {
482 printf("\e[0;31mwarning: wrong data point order (%llu <= %llu)\e[0m\n", (ULL)ts, (ULL)previousTS);
483 }
484 if (printWarning) {
485 printf("%s%s missing the previous data point (dt = %s%llu ms)", color.c_str(), header.c_str(),
486 (previousTS < tMin) ? "-" : "+",
487 (ULL)((previousTS < tMin) ? tMin - previousTS : previousTS - tMin));
488 if (ts <= tMin) {
489 printf(" but get one at dt = -%llu ms\e[0m\n", (ULL)(tMin - ts));
490 } else {
491 printf("\e[0m\n");
492 }
493 header = " ";
494 }
495 }
496 }
497
498 // add the first data point (should be before the start of validity of the file)
499 if (ts >= tMax) {
500 printf("error: first data point exceeding file validity range (dt = +%llu ms)\n", (ULL)(ts - tMax));
501 exit(1);
502 } else if (ts > tMin && printWarning) {
503 printf("%s%s missing data point prior file start of validity (dt = +%llu ms)\e[0m\n",
504 color.c_str(), header.c_str(), (ULL)(ts - tMin));
505 header = " ";
506 }
507 dps2.emplace(ts, getValue(*itDP));
508
509 // add other data points (should be within the validity range of the file)
510 auto previousTS = ts;
511 for (++itDP; itDP < dps.end(); ++itDP) {
512 ts = itDP->get_epoch_time();
513 if (ts <= previousTS) {
514 printf("\e[0;31mwarning: wrong data point order (%llu <= %llu)\e[0m\n", (ULL)ts, (ULL)previousTS);
515 } else {
516 previousTS = ts;
517 }
518 if (ts < tMin && (warningLevel > 1 || (warningLevel == 1 && ts + tolerance < tMin))) {
519 printf("%s%s data point outside of file validity range (dt = -%llu ms)\e[0m\n",
520 (ts + tolerance < tMin) ? "\e[0;31m" : "\e[0;34m", header.c_str(), (ULL)(tMin - ts));
521 } else if (ts >= tMax && warningLevel >= 1) {
522 printf("\e[0;31m%s data point outside of file validity range (dt = +%llu ms)\e[0m\n",
523 header.c_str(), (ULL)(ts - tMax));
524 }
525 dps2.emplace(ts, getValue(*itDP));
526 }
527}
528
529//----------------------------------------------------------------------------
530void selectDataPoints(DPMAP2 dpsMapsPerCh[10], uint64_t tStart, uint64_t tStop)
531{
534
535 for (int ch = 0; ch < 10; ++ch) {
536 for (auto& [alias, dps] : dpsMapsPerCh[ch]) {
537
538 // get the first data point in the time range, remove the previous ones
539 // and add a data point with value equal to the preceding one if it exits
540 // or to this one otherwise
541 auto itFirst = dps.lower_bound(tStart);
542 if (itFirst != dps.begin()) {
543 double previousVal = std::prev(itFirst)->second;
544 for (auto it = dps.begin(); it != itFirst;) {
545 it = dps.erase(it);
546 }
547 dps.emplace(tStart, previousVal);
548 } else if (itFirst->first != tStart) {
549 if (itFirst->first > tStop) {
550 printf("error (%s): all data points are posterior to the end of the time range\n", alias.c_str());
551 } else {
552 printf("error (%s): first data point is posterior to the beginning of the time range\n", alias.c_str());
553 }
554 dps.emplace(tStart, itFirst->second);
555 }
556
557 // get the first data point exceeding the time range, remove it and the next ones
558 // and add a data point with value equal to the preceding one if needed
559 auto itLast = dps.upper_bound(tStop);
560 double previousVal = std::prev(itLast)->second;
561 for (auto it = itLast; it != dps.end();) {
562 it = dps.erase(it);
563 }
564 dps.emplace(tStop, previousVal);
565 }
566 }
567}
568
569//----------------------------------------------------------------------------
570void printDataPoints(const DPMAP2 dpsMapsPerCh[10], std::string hvlvFormat, bool all)
571{
573
574 const auto format1 = fmt::format(" %llu (%s): {} V\n", hvlvFormat.c_str());
575 const auto format2 = fmt::format(": %llu (%s): {} V -- %llu (%s): {} V\n",
576 hvlvFormat.c_str(), hvlvFormat.c_str());
577
578 for (int ch = 0; ch < 10; ++ch) {
579
580 printf("\n------------ chamber %d ------------\n", ch + 1);
581
582 for (const auto& [alias, dps] : dpsMapsPerCh[ch]) {
583
584 printf("- %s: %zu values", alias.c_str(), dps.size());
585
586 if (all) {
587
588 printf("\n");
589 for (const auto& [ts, val] : dps) {
590 printf(format1.c_str(), (ULL)ts, getTime(ts).c_str(), val);
591 }
592
593 } else if (!dps.empty()) {
594
595 const auto firstdt = dps.begin();
596 const auto lastdt = dps.rbegin();
597 printf(format2.c_str(),
598 (ULL)firstdt->first, getTime(firstdt->first).c_str(), firstdt->second,
599 (ULL)lastdt->first, getTime(lastdt->first).c_str(), lastdt->second);
600
601 } else {
602 printf("\n");
603 }
604 }
605 }
606}
607
608//----------------------------------------------------------------------------
609TGraph* mapToGraph(std::string alias, const std::map<uint64_t, double>& dps)
610{
612
613 TGraph* g = new TGraph(dps.size());
614
615 auto pos = alias.find(".");
616 auto shortAlias = alias.substr(0, pos);
617 auto de = getDE(alias);
618 auto title = de.empty() ? fmt::format("{}", shortAlias.c_str())
619 : fmt::format("{} ({})", de.c_str(), shortAlias.c_str());
620 g->SetNameTitle(alias.c_str(), title.c_str());
621
622 int i(0);
623 for (auto [ts, val] : dps) {
624 g->SetPoint(i, ms2s(ts), val);
625 ++i;
626 }
627
628 g->SetMarkerSize(1.5);
629 g->SetMarkerStyle(2);
630 g->SetLineStyle(2);
631
632 return g;
633}
634
635//----------------------------------------------------------------------------
636TCanvas* drawDataPoints(TMultiGraph* mg, double min, double max)
637{
639
640 TCanvas* c = new TCanvas(mg->GetName(), mg->GetHistogram()->GetTitle(), 1500, 900);
641
642 mg->Draw("A plc pmc");
643 mg->SetMinimum(min);
644 mg->SetMaximum(max);
645 mg->GetXaxis()->SetTimeDisplay(1);
646 mg->GetXaxis()->SetTimeFormat("%d/%m %H:%M");
647 mg->GetXaxis()->SetTimeOffset(0, "local");
648 mg->GetXaxis()->SetNdivisions(21010);
649
650 c->BuildLegend();
651 c->Update();
652
653 return c;
654}
655
656//----------------------------------------------------------------------------
657void findIssues(const std::map<uint64_t, double>& dps, double limit, ISSUELIST& issues)
658{
660
661 uint64_t tStart(0);
662 double min(0.);
663 double mean(0.);
664 uint64_t prevTS(0);
665 double prevVal(-1.);
666
667 for (auto [ts, val] : dps) {
668
669 if (val < limit) {
670
671 if (tStart == 0) {
672
673 // start a new issue...
674 tStart = ts;
675 min = val;
676 mean = 0.;
677 prevTS = ts;
678 prevVal = val;
679
680 } else {
681
682 // ... or complement the current one
683 min = std::min(min, val);
684 mean += prevVal * (ts - prevTS);
685 prevTS = ts;
686 prevVal = val;
687 }
688
689 } else if (tStart > 0) {
690
691 // complete the current issue, if any, and register it
692 mean += prevVal * (ts - prevTS);
693 mean /= (ts - tStart);
694 issues.emplace_back(tStart, ts, min, mean, "");
695 tStart = 0;
696 }
697 }
698
699 // complete the last issue, if any and its duration is != 0, and register it
700 if (tStart > 0 && prevTS != tStart) {
701 mean /= (prevTS - tStart);
702 issues.emplace_back(tStart, prevTS, min, mean, "");
703 }
704}
705
706//----------------------------------------------------------------------------
707void fillO2Issues(const std::vector<mch::HVStatusCreator::TimeRange>& o2issues, ISSUELIST& issues,
708 uint64_t tMin, uint64_t tMax)
709{
711
712 // the list must not be empty
713 if (o2issues.empty()) {
714 printf("error: O2 returns an empty list of issues\n");
715 exit(1);
716 }
717
718 for (auto itIssue = o2issues.begin(); itIssue != o2issues.end(); ++itIssue) {
719
720 // exclude issues fully outside of the DP file boudaries
721 if (itIssue->end <= tMin || itIssue->begin >= tMax) {
722 printf("\e[0;35mwarning: skipping O2 issue outside of file boundaries (%llu - %llu)\e[0m\n",
723 (ULL)itIssue->begin, (ULL)itIssue->end);
724 continue;
725 }
726
727 // only the first issue could in principle extend before the start of the DP file, to O
728 if (itIssue->begin < tMin - mch::StatusMapCreatorParam::Instance().timeMargin &&
729 (itIssue != o2issues.begin() || itIssue->begin != 0)) {
730 printf("\e[0;35mwarning: O2 returns an issue with uncommon start time (%llu < %llu)\e[0m\n",
731 (ULL)itIssue->begin, (ULL)(tMin - mch::StatusMapCreatorParam::Instance().timeMargin));
732 }
733
734 // only the last issue could in principle extend beyond the end of the DP file, to infinity
735 if (itIssue->end >= tMax + mch::StatusMapCreatorParam::Instance().timeMargin &&
736 (itIssue != std::prev(o2issues.end()) || itIssue->end != std::numeric_limits<uint64_t>::max())) {
737 printf("\e[0;35mwarning: O2 returns an issue with uncommon end time (%llu >= %llu)\e[0m\n",
738 (ULL)itIssue->end, (ULL)(tMax + mch::StatusMapCreatorParam::Instance().timeMargin));
739 }
740
741 // extend the last issue in case of continuity accross the DP files or add a new one,
742 // restricting their time range within the DP file boundaries
743 if (itIssue->begin <= tMin && !issues.empty() && std::get<1>(issues.back()) == tMin) {
744 std::get<1>(issues.back()) = std::min(itIssue->end, tMax);
745 } else {
746 issues.emplace_back(std::max(itIssue->begin, tMin), std::min(itIssue->end, tMax), 0., 0., "");
747 }
748 }
749}
750
751//----------------------------------------------------------------------------
752std::string findAffectedRuns(const RBMAP& runBoundaries, uint64_t tStart, uint64_t tStop)
753{
755
756 std::string runs;
757
758 for (const auto& [run, boundaries] : runBoundaries) {
759
760 if (boundaries.second <= tStart) {
761 continue;
762 } else if (boundaries.first >= tStop) {
763 break;
764 }
765
766 runs += fmt::format("{},", run);
767 }
768
769 if (!runs.empty()) {
770 runs.pop_back();
771 }
772
773 return runs;
774}
775
776//----------------------------------------------------------------------------
777void selectIssues(ISSUEMAP issuesPerCh[10], const RBMAP& runBoundaries, uint64_t minDuration)
778{
780
781 for (int ch = 0; ch < 10; ++ch) {
782 for (auto& issues : issuesPerCh[ch]) {
783 for (auto itIssue = issues.second.begin(); itIssue != issues.second.end();) {
784
785 auto tStart = std::get<0>(*itIssue);
786 auto tStop = std::get<1>(*itIssue);
787
788 if (tStop - tStart < minDuration) {
789
790 itIssue = issues.second.erase(itIssue);
791
792 } else {
793
794 auto runs = findAffectedRuns(runBoundaries, tStart, tStop);
795
796 if (runs.empty()) {
797
798 itIssue = issues.second.erase(itIssue);
799
800 } else {
801
802 std::get<4>(*itIssue) = runs;
803 ++itIssue;
804 }
805 }
806 }
807 }
808 }
809}
810
811//----------------------------------------------------------------------------
812void selectO2Issues(ISSUEMAP issuesPerCh[10], const RBMAP& runBoundaries)
813{
816
817 for (int ch = 0; ch < 10; ++ch) {
818 for (auto& issues : issuesPerCh[ch]) {
819 for (auto itIssue = issues.second.begin(); itIssue != issues.second.end();) {
820
821 auto& tStart = std::get<0>(*itIssue);
822 auto& tStop = std::get<1>(*itIssue);
823
824 auto runs = findAffectedRuns(runBoundaries, tStart, tStop);
825
826 if (runs.empty()) {
827
828 itIssue = issues.second.erase(itIssue);
829
830 } else {
831
832 tStart = std::max(tStart, runBoundaries.begin()->second.first);
833 tStop = std::min(tStop, runBoundaries.rbegin()->second.second);
834 std::get<4>(*itIssue) = runs;
835 ++itIssue;
836 }
837 }
838 }
839 }
840}
841
842//----------------------------------------------------------------------------
843bool eraseIssue(const ISSUE& issue, ISSUELIST& issues)
844{
847
848 auto itIssue = std::find_if(issues.begin(), issues.end(), [&issue](const auto& i) {
849 return (std::get<0>(i) == std::get<0>(issue) &&
850 std::get<1>(i) == std::get<1>(issue) &&
851 std::get<4>(i) == std::get<4>(issue));
852 });
853
854 if (itIssue != issues.end()) {
855 issues.erase(itIssue);
856 return true;
857 }
858
859 return false;
860}
861
862//----------------------------------------------------------------------------
863void printIssues(const ISSUEMAP issuesPerCh[10], const ISSUEMAP o2IssuesPerCh[10],
864 bool scanHV, std::string hvlvFormat)
865{
867
868 // copy the issues so that we can modify them (i.e. add empty lists or delete issues after printing)
869 ISSUEMAP issuesPerChCopy[10];
870 ISSUEMAP o2IssuesPerChCopy[10];
871 for (int ch = 0; ch < 10; ++ch) {
872 issuesPerChCopy[ch] = issuesPerCh[ch];
873 o2IssuesPerChCopy[ch] = o2IssuesPerCh[ch];
874 }
875
876 // make sure that all alias keys in the map o2IssuesPerChCopy are also in issuesPerChCopy in order to
877 // simplify the loop over all issues from both algorithms and fix the order in which they are printed
878 for (int ch = 0; ch < 10; ++ch) {
879 for (const auto& [alias, o2Issues] : o2IssuesPerChCopy[ch]) {
880 if (!o2Issues.empty()) {
881 issuesPerChCopy[ch].try_emplace(alias, ISSUELIST{});
882 }
883 }
884 }
885
886 auto printHeader = [](std::string alias) {
887 auto de = getDE(alias);
888 if (de.empty()) {
889 printf("Problem found for %s:\n", alias.c_str());
890 } else {
891 printf("Problem found for %s (%s):\n", alias.c_str(), de.c_str());
892 }
893 };
894
895 const auto format = fmt::format("%llu - %llu: %s (duration = %s, min = {} V, mean = {} V) --> run(s) %s\n",
896 hvlvFormat.c_str(), hvlvFormat.c_str());
897
898 auto printIssue = [&format](ISSUE issue, std::string color) {
899 const auto& [tStart, tStop, min, mean, runs] = issue;
900 printf("%s", color.c_str());
901 printf(format.c_str(), (ULL)tStart, (ULL)tStop,
902 getTime(tStart).c_str(), getDuration(tStart, tStop).c_str(), min, mean, runs.c_str());
903 printf("\e[0m");
904 };
905
906 if (scanHV) {
907 printf("\n------ list of issues from \e[0;31mthis macro only\e[0m, \e[0;35mO2 only\e[0m, or \e[0;32mboth\e[0m ------\n");
908 } else {
909 printf("\n------ list of issues ------\n");
910 }
911
912 bool foundIssues = false;
913
914 for (int ch = 0; ch < 10; ++ch) {
915 for (const auto& [alias, issues] : issuesPerChCopy[ch]) {
916
917 auto& o2Issues = o2IssuesPerChCopy[ch][alias];
918
919 if (!issues.empty() || !o2Issues.empty()) {
920
921 foundIssues = true;
922 printHeader(alias);
923
924 // print all issues found by this macro
925 for (const auto& issue : issues) {
926 // change color if the issue is not found by the O2 algorithm (only for HV)
927 std::string color = (scanHV && !eraseIssue(issue, o2Issues)) ? "\e[0;31m" : "\e[0;32m";
928 printIssue(issue, color);
929 }
930
931 // print other issues found by the O2 algorithm
932 for (const auto& issue : o2Issues) {
933 printIssue(issue, "\e[0;35m");
934 }
935
936 printf("----------------------------\n");
937 }
938 }
939 }
940
941 if (!foundIssues) {
942 printf("----------------------------\n");
943 }
944}
945
946//----------------------------------------------------------------------------
947int main(int argc, char** argv)
948{
950
951 std::string runList = "";
952 std::string what = "";
953 std::string config = "";
954 uint64_t minDuration = 0;
955 uint64_t timeInterval = 30;
956 int warningLevel = 1;
957 int printLevel = 1;
958 std::string outFileName = "";
959
960 po::options_description usage("Usage");
961 // clang-format off
962 usage.add_options()
963 ("help,h", "produce help message")
964 ("runs,r",po::value<std::string>(&runList)->default_value(""),"run(s) to scan (comma separated list of runs or ASCII file with one run per line)")
965 ("channels,c",po::value<std::string>(&what)->default_value(""),R"(channel(s) to scan ("HV" or "LV" or comma separated list of (part of) DCS aliases))")
966 ("configKeyValues",po::value<std::string>(&config)->default_value(""),"Semicolon separated key=value strings to change HV thresholds")
967 ("duration,d",po::value<uint64_t>(&minDuration)->default_value(0),"minimum duration (ms) of HV/LV issues to consider")
968 ("interval,i",po::value<uint64_t>(&timeInterval)->default_value(30),"creation time interval (minutes) between CCDB files")
969 ("warning,w",po::value<int>(&warningLevel)->default_value(1),"warning level (0, 1 or 2)")
970 ("print,p",po::value<int>(&printLevel)->default_value(1),"print level (0, 1, 2 or 3)")
971 ("output,o",po::value<std::string>(&outFileName)->default_value("scan.root"),"output root file name")
972 ;
973 // clang-format on
974
975 po::options_description cmdline;
976 cmdline.add(usage);
977
978 po::variables_map vm;
979 po::store(po::command_line_parser(argc, argv).options(cmdline).run(), vm);
980
981 if (vm.count("help")) {
982 std::cout << "This program scans HV or LV channels looking for issues\n";
983 std::cout << usage << "\n";
984 return 2;
985 }
986
987 try {
988 po::notify(vm);
989 } catch (const po::error& e) {
990 std::cout << "error: " << e.what() << "\n";
991 exit(1);
992 }
993
994 if (runList.empty()) {
995 printf("error: you must provide run(s) to scan\n");
996 exit(1);
997 }
998
999 if (what.empty()) {
1000 printf("error: you must provide channel(s) to scan\n");
1001 exit(1);
1002 }
1003
1004 // setup printout and display
1005 const double hvRange[2] = {-10., 1700.};
1006 const double lvRange[3] = {-1., 4., 8.}; // min, max FeeAnalog/FeeDigital, max Solar
1007 const std::string hvFormat = "%7.2f";
1008 const std::string lvFormat = "%4.2f";
1009 gStyle->SetPalette(kVisibleSpectrum);
1010
1011 // setup algorithms searching for HV issues
1013 conf::ConfigurableParam::setValue("MCHStatusMap.hvMinDuration", std::to_string(minDuration));
1014 conf::ConfigurableParam::setValue("MCHStatusMap.timeMargin", "0"); // must be 0 to compare O2 with this scan
1015
1016 // determine what is scanned
1017 std::string path{};
1018 bool scanHV = false;
1019 bool scanAll = false;
1020 std::set<std::string> aliases{};
1021 scanWhat(what, path, scanHV, scanAll, aliases);
1022
1023 ccdb::CcdbApi api;
1024 api.init("http://alice-ccdb.cern.ch");
1025
1026 // get the SOR/EOR of every runs from the list, ordered in run number
1027 auto runBoundaries = getRunBoundaries(api, runList);
1028 if (printLevel > 0) {
1029 printRunBoundaries(runBoundaries);
1030 }
1031 checkRunBoundaries(runBoundaries);
1032
1033 // extract the time boundaries for each HV/LV file in the full time range
1034 auto dpBoundaries = getDPBoundaries(api, path.c_str(), runBoundaries.begin()->second.first,
1035 runBoundaries.rbegin()->second.second, timeInterval * 60000);
1036 if (printLevel > 0) {
1037 printDPBoundaries(dpBoundaries, scanHV, timeInterval);
1038 }
1039 checkDPBoundaries(dpBoundaries, scanHV, runBoundaries.begin()->second.first,
1040 runBoundaries.rbegin()->second.second);
1041
1042 // loop over the HV/LV files, fill the lists of data points per chamber and find issues using O2 algorithm
1043 DPMAP2 dpsMapsPerCh[10];
1044 mch::HVStatusCreator hvStatusCreator{};
1045 ISSUEMAP o2issuesPerCh[10];
1046 std::map<std::string, std::string> metadata;
1047 for (auto boundaries : dpBoundaries) {
1048
1049 auto* dpMap = api.retrieveFromTFileAny<DPMAP>(path.c_str(), metadata, boundaries.first);
1050
1051 // fill the lists of data points per chamber for requested aliases
1052 for (const auto& [dpid, dps] : *dpMap) {
1053 std::string alias(dpid.get_alias());
1054 if (!mch::dcs::isValid(alias)) {
1055 printf("error: invalid DCS alias: %s\n", alias.c_str());
1056 exit(1);
1057 }
1058 if ((scanAll || containsAKey(alias, aliases)) && (!scanHV || alias.find(".iMon") == alias.npos)) {
1059 int chamber = mch::dcs::toInt(mch::dcs::aliasToChamber(alias));
1060 fillDataPoints(dps, dpsMapsPerCh[chamber][alias], boundaries.first, boundaries.second, warningLevel);
1061 }
1062 }
1063
1064 // find issues for requested aliases using O2 algorithm (only for HV)
1065 if (scanHV) {
1066 hvStatusCreator.findBadHVs(*dpMap);
1067 for (const auto& [alias, issues] : hvStatusCreator.getBadHVs()) {
1068 if (scanAll || containsAKey(alias, aliases)) {
1069 int chamber = mch::dcs::toInt(mch::dcs::aliasToChamber(alias));
1070 fillO2Issues(issues, o2issuesPerCh[chamber][alias], boundaries.first, boundaries.second);
1071 }
1072 }
1073 }
1074 }
1075 if (printLevel > 1) {
1076 printf("\nall data points:");
1077 printDataPoints(dpsMapsPerCh, scanHV ? hvFormat : lvFormat, printLevel > 2);
1078 }
1079
1080 // select the data points in the time range
1081 selectDataPoints(dpsMapsPerCh, runBoundaries.begin()->second.first, runBoundaries.rbegin()->second.second);
1082 if (printLevel > 1) {
1083 printf("\ndata points in the time range covered by runs:");
1084 printDataPoints(dpsMapsPerCh, scanHV ? hvFormat : lvFormat, printLevel > 2);
1085 }
1086
1087 // create and fill the graphs, and find HV/LV issues
1088 ISSUEMAP issuesPerCh[10];
1089 TMultiGraph* mg[10];
1090 std::set<double> limits;
1091 for (int ch = 0; ch < 10; ++ch) {
1092 mg[ch] = new TMultiGraph;
1093 mg[ch]->SetNameTitle(fmt::format("ch{}", ch + 1).c_str(),
1094 fmt::format("chamber {};time;{} (V)", ch + 1, scanHV ? "HV" : "LV").c_str());
1095 for (const auto& [alias, dps] : dpsMapsPerCh[ch]) {
1096 mg[ch]->Add(mapToGraph(alias, dps), "lp");
1097 auto limit = scanHV ? mch::StatusMapCreatorParam::Instance().hvLimits[ch] : getLVLimit(alias);
1098 limits.emplace(limit);
1099 findIssues(dps, limit, issuesPerCh[ch][alias]);
1100 }
1101 }
1102
1103 // select HV/LV issues of a minimum duration (ms) occurring during runs
1104 selectIssues(issuesPerCh, runBoundaries, minDuration);
1105 selectO2Issues(o2issuesPerCh, runBoundaries);
1106 printIssues(issuesPerCh, o2issuesPerCh, scanHV, scanHV ? hvFormat : lvFormat);
1107
1108 // display
1109 TCanvas* c[10];
1110 for (int ch = 0; ch < 10; ++ch) {
1111 if (scanHV) {
1112 c[ch] = drawDataPoints(mg[ch], hvRange[0], hvRange[1]);
1113 drawLimit(mch::StatusMapCreatorParam::Instance().hvLimits[ch], c[ch]);
1114 } else {
1115 auto lvMax = (what.find("LV") != what.npos || what.find("Sol") != what.npos) ? lvRange[2] : lvRange[1];
1116 c[ch] = drawDataPoints(mg[ch], lvRange[0], lvMax);
1117 for (auto limit : limits) {
1118 drawLimit(limit, c[ch]);
1119 }
1120 }
1121 drawRunBoudaries(runBoundaries, c[ch]);
1122 }
1123
1124 // save display
1125 TFile dataFile(outFileName.c_str(), "recreate");
1126 for (int ch = 0; ch < 10; ++ch) {
1127 c[ch]->Write();
1128 }
1129 dataFile.Close();
1130
1131 return 0;
1132}
int16_t time
Definition RawEventData.h:4
std::vector< std::string > header
int32_t i
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to runs
char error[CURL_ERROR_SIZE]
uint16_t pos
Definition RawData.h:3
uint32_t c
Definition RawData.h:2
void printHeader()
StringRef key
Class for time synchronization of RawReader instances.
std::pair< int64_t, int64_t > getRunDuration(int runnumber, bool fatal=true)
std::string list(std::string const &path="", bool latestOnly=false, std::string const &returnFormat="text/plain", long createdNotAfter=-1, long createdNotBefore=-1) const
Definition CcdbApi.cxx:1309
void init(std::string const &hosts)
Definition CcdbApi.cxx:240
std::enable_if<!std::is_base_of< o2::conf::ConfigurableParam, T >::value, T * >::type retrieveFromTFileAny(std::string const &path, std::map< std::string, std::string > const &metadata, long timestamp=-1, std::map< std::string, std::string > *headers=nullptr, std::string const &etag="", const std::string &createdNotAfter="", const std::string &createdNotBefore="") const
Definition CcdbApi.h:662
static void setValue(std::string const &mainkey, std::string const &subkey, T x)
static void updateFromString(std::string const &)
Find HV issues from DCS data points and add them to the status map.
std::unordered_map< DPID, std::vector< DPVAL > > DPMAP
Definition dcs-ccdb.cxx:37
GLboolean GLboolean g
Definition glcorearb.h:1233
const GLfloat * m
Definition glcorearb.h:4066
GLuint color
Definition glcorearb.h:1272
GLuint GLuint end
Definition glcorearb.h:469
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLuint GLfloat * val
Definition glcorearb.h:1582
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLint GLsizei GLint GLenum format
Definition glcorearb.h:275
GLsizeiptr const void GLenum usage
Definition glcorearb.h:659
int toInt(Chamber chamber)
Definition Chamber.cxx:68
Chamber aliasToChamber(std::string_view alias)
Definition Chamber.cxx:100
int aliasToNumber(std::string_view dcsAlias)
Definition Number.cxx:30
std::vector< std::string > aliases(std::vector< MeasurementType > types={ MeasurementType::HV_V, MeasurementType::HV_I, MeasurementType::LV_V_FEE_ANALOG, MeasurementType::LV_V_FEE_DIGITAL, MeasurementType::LV_V_SOLAR})
std::optional< int > aliasToDetElemId(std::string_view dcsAlias)
bool isValid(std::string_view dcsAlias)
bool isQuadrant(Chamber chamber)
Definition Chamber.cxx:44
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
void printDPBoundaries(const DPBMAP &dpBoundaries, bool scanHV, uint64_t timeInterval)
std::string getTime(uint64_t ts)
void printIssues(const ISSUEMAP issuesPerCh[10], const ISSUEMAP o2IssuesPerCh[10], bool scanHV, std::string hvlvFormat)
bool isValid(std::string alias)
std::tuple< uint64_t, uint64_t, double, double, std::string > ISSUE
void selectIssues(ISSUEMAP issuesPerCh[10], const RBMAP &runBoundaries, uint64_t minDuration)
void selectO2Issues(ISSUEMAP issuesPerCh[10], const RBMAP &runBoundaries)
std::string getDE(std::string alias)
void fillO2Issues(const std::vector< mch::HVStatusCreator::TimeRange > &o2issues, ISSUELIST &issues, uint64_t tMin, uint64_t tMax)
bool containsAKey(std::string data, const std::set< std::string > &Keys)
void printDataPoints(const DPMAP2 dpsMapsPerCh[10], std::string hvlvFormat, bool all)
void drawRunBoudaries(const RBMAP &runBoundaries, TCanvas *c)
TGraph * mapToGraph(std::string alias, const std::map< uint64_t, double > &dps)
void checkRunBoundaries(const RBMAP &runBoundaries)
double getValue(DPVAL dp)
void checkDPBoundaries(const DPBMAP &dpBoundaries, bool scanHV, uint64_t tStart, uint64_t tStop)
void fillDataPoints(const std::vector< DPVAL > &dps, std::map< uint64_t, double > &dps2, uint64_t tMin, uint64_t tMax, int warningLevel)
std::map< uint64_t, uint64_t > DPBMAP
DPBMAP getDPBoundaries(ccdb::CcdbApi const &api, std::string what, uint64_t tStart, uint64_t tStop, uint64_t timeInterval)
std::set< int > getRuns(std::string runList)
void drawLimit(double limit, TCanvas *c)
std::string getDuration(uint64_t tStart, uint64_t tStop)
std::map< std::string, std::map< uint64_t, double > > DPMAP2
void findIssues(const std::map< uint64_t, double > &dps, double limit, ISSUELIST &issues)
uint64_t ms2s(uint64_t ts)
void selectDataPoints(DPMAP2 dpsMapsPerCh[10], uint64_t tStart, uint64_t tStop)
RBMAP getRunBoundaries(ccdb::CcdbApi const &api, std::string runList)
bool eraseIssue(const ISSUE &issue, ISSUELIST &issues)
std::string findAffectedRuns(const RBMAP &runBoundaries, uint64_t tStart, uint64_t tStop)
void scanWhat(std::string what, std::string &path, bool &scanHV, bool &scanAll, std::set< std::string > &aliases)
TCanvas * drawDataPoints(TMultiGraph *mg, double min, double max)
std::map< std::string, ISSUELIST > ISSUEMAP
unsigned long long ULL
std::map< int, std::pair< uint64_t, uint64_t > > RBMAP
void printRunBoundaries(const RBMAP &runBoundaries)
double getLVLimit(std::string alias)
std::vector< ISSUE > ISSUELIST
uint16_t de
constexpr size_t min
constexpr size_t max
#define main