LCOV - code coverage report
Current view: top level - otsdaq-mu2e-tracker/otsdaq-mu2e-tracker/IteratorPlugins - TrackerNoiseRun.h (source / functions) Coverage Total Hit
Test: mu2edaq.info.cleaned Lines: 0.0 % 158 0
Test Date: 2026-10-29 01:38:59 Functions: 0.0 % 11 0

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : // Transport-independent NoiseRun scan logic. Only TrackerNoiseRunPlugin accesses OTS.
       4              : #include <algorithm>
       5              : #include <chrono>
       6              : #include <cmath>
       7              : #include <fstream>
       8              : #include <functional>
       9              : #include <future>
      10              : #include <iomanip>
      11              : #include <locale>
      12              : #include <map>
      13              : #include <regex>
      14              : #include <set>
      15              : #include <sstream>
      16              : #include <stdexcept>
      17              : #include <string>
      18              : #include <vector>
      19              : 
      20              : namespace ots
      21              : {
      22              : namespace tracker_noise_run
      23              : {
      24            0 : inline void require(bool value, const std::string& message)
      25              : {
      26            0 :         if(!value)
      27            0 :                 throw std::runtime_error(message);
      28            0 : }
      29              : struct Target
      30              : {
      31              :         std::string              uid;
      32              :         unsigned int             supervisor;
      33              :         std::vector<int>         links;
      34              :         std::vector<std::string> inputs, outputs, publisherInputs, publisherOutputs;
      35              : };
      36              : using Outputs = std::map<std::string, std::string>;
      37            0 : inline std::vector<std::string> persistedIdentity(const std::string& path)
      38              : {
      39            0 :         std::ifstream f(path);
      40            0 :         require(bool(f), "Cannot read persisted ActiveTableGroups.cfg");
      41            0 :         std::vector<std::string> lines;
      42            0 :         std::string              line;
      43            0 :         while(std::getline(f, line))
      44              :         {
      45            0 :                 if(!line.empty() && line.back() == '\r')
      46            0 :                         line.pop_back();
      47            0 :                 lines.push_back(line);
      48              :         }
      49            0 :         require(lines.size() == 8 && !lines[0].empty() && !lines[6].empty(),
      50              :                 "Malformed persisted active groups");
      51            0 :         for(auto i : {1, 7})
      52            0 :                 require(std::regex_match(lines[i], std::regex("[0-9]+")),
      53              :                         "Invalid persisted group key");
      54            0 :         return {lines[0],
      55            0 :                 lines[1],
      56            0 :                 lines[6],
      57            0 :                 lines[7]};  // Context and Configuration, not editor state.
      58            0 : }
      59            0 : inline unsigned long long scalar(const std::string& text)
      60              : {
      61            0 :         std::smatch m;
      62            0 :         require(
      63            0 :             std::regex_match(
      64            0 :                 text, m, std::regex("\\s*([0-9]+)(?:\\s+\\(0[xX]([0-9a-fA-F]+)\\))?\\s*")),
      65              :             "Malformed numeric macro output");
      66            0 :         auto n = std::stoull(m[1]);
      67            0 :         if(m[2].matched)
      68            0 :                 require(n == std::stoull(m[2], nullptr, 16),
      69              :                         "Mismatched decimal/hex macro output");
      70            0 :         return n;
      71            0 : }
      72            0 : inline std::vector<int> numbers(const std::string& text)
      73              : {
      74            0 :         if(text.find('[') == std::string::npos)
      75              :         {
      76            0 :                 auto n = scalar(text);
      77            0 :                 require(n <= 65535, "Macro number outside range");
      78            0 :                 return {static_cast<int>(n)};
      79              :         }
      80            0 :         require(std::regex_match(
      81            0 :                     text, std::regex("\\s*\\[\\s*[0-9]+(?:\\s*,\\s*[0-9]+)*\\s*\\]\\s*")),
      82              :                 "Malformed macro output list");
      83            0 :         std::vector<int> result;
      84            0 :         const std::regex digit("[0-9]+");
      85            0 :         for(auto i = std::sregex_iterator(text.begin(), text.end(), digit);
      86            0 :             i != std::sregex_iterator();
      87            0 :             ++i)
      88              :         {
      89            0 :                 auto n = std::stoul(i->str());
      90            0 :                 require(n <= 65535, "Macro number outside range");
      91            0 :                 result.push_back(n);
      92            0 :         }
      93            0 :         return result;
      94            0 : }
      95            0 : inline void validateTargets(const std::vector<Target>& targets)
      96              : {
      97            0 :         require(!targets.empty() && targets.size() <= 36, "Invalid NoiseRun DTC coverage");
      98            0 :         std::set<std::string> seen;
      99            0 :         for(const auto& t : targets)
     100              :         {
     101            0 :                 require(std::regex_match(t.uid, std::regex("Trk(0[1-9]|1[0-8])_DTC[01]")) &&
     102            0 :                             seen.insert(t.uid).second,
     103              :                         "Invalid/duplicate NoiseRun target");
     104            0 :                 const std::set<int> links(t.links.begin(), t.links.end());
     105            0 :                 require(!links.empty() && links.size() == t.links.size() && *links.begin() >= 0 &&
     106            0 :                             *links.rbegin() <= 5,
     107              :                         "Invalid/duplicate ROC links");
     108            0 :         }
     109            0 : }
     110            0 : inline double totalHz(const Target& target, const Outputs& out)
     111              : {
     112            0 :         auto links = numbers(out.at("Target ROC")), codes = numbers(out.at("Return Code"));
     113            0 :         require(links.size() == target.links.size() &&
     114            0 :                     std::set<int>(links.begin(), links.end()) ==
     115            0 :                         std::set<int>(target.links.begin(), target.links.end()),
     116            0 :                 "Incomplete ROC coverage: " + target.uid);
     117            0 :         require(codes.size() == links.size() &&
     118            0 :                     std::all_of(codes.begin(), codes.end(), [](int n) { return n == 0; }),
     119            0 :                 "Rate/mask restoration failed: " + target.uid);
     120            0 :         const auto&      masks = out.at("Saved hardware masks");
     121              :         const std::regex mask(
     122              :             "ROC\\s+([0-5])\\s*:\\s*CAL\\s+(0x[0-9a-fA-F]+)\\s+(0x[0-9a-fA-F]+)\\s+(0x[0-9a-"
     123            0 :             "fA-F]+)\\s*;\\s*HV\\s+(0x[0-9a-fA-F]+)\\s+(0x[0-9a-fA-F]+)\\s+(0x[0-9a-fA-F]+)");
     124            0 :         std::set<int> maskLinks;
     125            0 :         for(auto i = std::sregex_iterator(masks.begin(), masks.end(), mask);
     126            0 :             i != std::sregex_iterator();
     127            0 :             ++i)
     128              :         {
     129            0 :                 require(maskLinks.insert(std::stoi((*i)[1])).second, "Duplicate saved mask");
     130            0 :                 for(int k = 2; k <= 7; ++k)
     131            0 :                         require(std::stoul((*i)[k], nullptr, 16) <= 65535, "Invalid saved mask word");
     132            0 :         }
     133            0 :         require(maskLinks == std::set<int>(links.begin(), links.end()),
     134            0 :                 "Missing saved mask coverage: " + target.uid);
     135            0 :         std::istringstream input(out.at("Rates"));
     136            0 :         input.imbue(std::locale::classic());
     137            0 :         std::string      line;
     138            0 :         int              channels = 0, tables = 0;
     139            0 :         double           sum = 0;
     140            0 :         const std::regex row("\\s*([0-9]+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s*");
     141            0 :         while(std::getline(input, line))
     142              :         {
     143            0 :                 std::smatch m;
     144            0 :                 if(!std::regex_match(line, m, row))
     145            0 :                         continue;
     146            0 :                 int ch = std::stoi(m[1]);
     147            0 :                 if(ch == 0)
     148              :                 {
     149            0 :                         require(channels == 0 || channels == 96, "Incomplete rate table");
     150            0 :                         channels = 0;
     151            0 :                         ++tables;
     152              :                 }
     153            0 :                 require(tables > 0 && ch == channels && ch < 96, "Nonsequential rate channels");
     154            0 :                 ++channels;
     155            0 :                 for(int k = 2; k <= 4; ++k)
     156              :                 {
     157            0 :                         size_t n = 0;
     158            0 :                         double v = std::stod(m[k], &n);
     159            0 :                         require(n == m[k].str().size() && std::isfinite(v) && v >= 0,
     160              :                                 "Invalid rate value");
     161            0 :                         if(k == 4)
     162            0 :                                 sum += v * 1000;  // kHz Total/coincidence column -> Hz, all 96 channels.
     163              :                 }
     164            0 :         }
     165            0 :         require(
     166            0 :             tables == static_cast<int>(links.size()) && channels == 96 && std::isfinite(sum),
     167              :             "Incomplete rate tables");
     168            0 :         return sum;
     169            0 : }
     170              : struct Summary
     171              : {
     172              :         std::vector<double> rates;
     173              :         double              total = 0, seconds = 0;
     174              :         unsigned int        channels = 0, rocs = 0;
     175            0 :         std::string         json(const std::vector<Target>& targets) const
     176              :         {
     177            0 :                 std::ostringstream s;
     178            0 :                 s.imbue(std::locale::classic());
     179            0 :                 s << std::setprecision(17);
     180            0 :                 s << "{\"schema_version\":1,\"total_hz\":" << total
     181            0 :                   << ",\"channels\":" << channels << ",\"scan_seconds\":" << seconds
     182            0 :                   << ",\"dtcs\":[";
     183            0 :                 for(size_t i = 0; i < targets.size(); ++i)
     184            0 :                         s << (i ? "," : "") << "{\"uid\":\"" << targets[i].uid
     185            0 :                           << "\",\"total_hz\":" << rates.at(i)
     186            0 :                           << ",\"channels\":" << 96 * targets[i].links.size()
     187            0 :                           << ",\"rocs\":" << targets[i].links.size() << "}";
     188            0 :                 return s.str() + "]}";
     189            0 :         }
     190              : };
     191              : // Always settle every submitted operation before reporting any failure. Never retry
     192              : // a measurement or publish whose outcome might be uncertain.
     193            0 : inline Summary scan(const std::vector<Target>&                   targets,
     194              :                     const std::function<Outputs(const Target&)>& measure,
     195              :                     const std::function<void()>&                 guard,
     196              :                     const std::function<void(const Summary&)>&   publish)
     197              : {
     198            0 :         validateTargets(targets);
     199            0 :         guard();
     200            0 :         const auto                        start = std::chrono::steady_clock::now();
     201            0 :         std::vector<std::future<Outputs>> jobs;
     202            0 :         std::string                       errors;
     203              :         try
     204              :         {
     205            0 :                 for(const auto& t : targets)
     206            0 :                         jobs.push_back(std::async(std::launch::async, [&, t] { return measure(t); }));
     207              :         }
     208            0 :         catch(const std::exception& e)
     209              :         {
     210            0 :                 errors = e.what();
     211            0 :         }
     212            0 :         Summary summary;
     213            0 :         for(size_t i = 0; i < jobs.size(); ++i)
     214              :         {
     215              :                 try
     216              :                 {
     217            0 :                         double v = totalHz(targets[i], jobs[i].get());
     218            0 :                         summary.rates.push_back(v);
     219            0 :                         summary.total += v;
     220            0 :                         summary.rocs += targets[i].links.size();
     221              :                 }
     222            0 :                 catch(const std::exception& e)
     223              :                 {
     224            0 :                         errors += "; " + targets[i].uid + ": " + e.what();
     225            0 :                 }
     226            0 :                 catch(...)
     227              :                 {
     228            0 :                         errors += "; " + targets[i].uid + ": unknown scan failure";
     229            0 :                 }
     230              :         }
     231            0 :         require(errors.empty(),
     232            0 :                 "NoiseRun scan failed; all submitted requests settled" + errors);
     233            0 :         require(summary.rates.size() == targets.size() && std::isfinite(summary.total),
     234              :                 "Incomplete/invalid scan total");
     235            0 :         summary.channels = 96 * summary.rocs;
     236            0 :         summary.seconds =
     237            0 :             std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
     238            0 :         guard();
     239            0 :         publish(summary);
     240            0 :         return summary;
     241            0 : }
     242            0 : inline std::chrono::steady_clock::time_point nextScan(
     243              :     std::chrono::steady_clock::time_point start,
     244              :     std::chrono::steady_clock::time_point end)
     245              : {
     246            0 :         return std::max(start + std::chrono::seconds(3), end);
     247              : }
     248              : }  // namespace tracker_noise_run
     249              : }  // namespace ots
        

Generated by: LCOV version 2.0-1