| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* This file is a part of NA64SW software. | ||
| 2 | * Copyright (C) 2015-2022 NA64 Collaboration, CERN | ||
| 3 | * | ||
| 4 | * NA64SW is free software: you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation, either version 3 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 16 | |||
| 17 | #pragma once | ||
| 18 | |||
| 19 | #include "na64sw-config.h" | ||
| 20 | #include "na64util/na64/event-id.hh" | ||
| 21 | #include "na64calib/manager.hh" | ||
| 22 | #include "na64util/demangle.hh" | ||
| 23 | |||
| 24 | #include <map> | ||
| 25 | #include <cassert> | ||
| 26 | |||
| 27 | #include <yaml-cpp/yaml.h> | ||
| 28 | |||
| 29 | namespace na64dp { | ||
| 30 | namespace errors { | ||
| 31 | |||
| 32 | /// Indexing container has no valid range wrt given value | ||
| 33 | class NoValidForRange : public std::runtime_error { | ||
| 34 | public: | ||
| 35 | ✗ | NoValidForRange() | |
| 36 | ✗ | : std::runtime_error( "No valid entry has been found for event." ) {} | |
| 37 | }; | ||
| 38 | |||
| 39 | } // namespace na64dp::error | ||
| 40 | |||
| 41 | namespace calib { | ||
| 42 | |||
| 43 | namespace aux { | ||
| 44 | /// This auxiliary function is used by RangeOverrideRunIndex::to_yaml() to | ||
| 45 | /// represent a key | ||
| 46 | template<typename KeyT> std::string key_to_str_for_yaml(KeyT k) { | ||
| 47 | std::ostringstream oss; | ||
| 48 | oss << k; | ||
| 49 | return oss.str(); | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Specialization for time structure | ||
| 53 | template<> std::string key_to_str_for_yaml(std::pair<time_t, uint32_t>); | ||
| 54 | |||
| 55 | /// Specialization for run number | ||
| 56 | template<> std::string key_to_str_for_yaml(EventID); | ||
| 57 | |||
| 58 | template<typename KeyT> std::pair<KeyT, KeyT> | ||
| 59 | deduce_key( EventID oldEventID, EventID newEventID | ||
| 60 | , const std::pair<time_t, uint32_t> & oldDatetime | ||
| 61 | , const std::pair<time_t, uint32_t> & newDateTime | ||
| 62 | ); | ||
| 63 | |||
| 64 | template<> std::pair<EventID, EventID> | ||
| 65 | deduce_key( EventID oldEventID, EventID newEventID | ||
| 66 | , const std::pair<time_t, uint32_t> & oldDatetime | ||
| 67 | , const std::pair<time_t, uint32_t> & newDateTime | ||
| 68 | ); | ||
| 69 | |||
| 70 | template<> std::pair<std::pair<time_t, uint32_t>, std::pair<time_t, uint32_t>> | ||
| 71 | deduce_key( EventID oldEventID, EventID newEventID | ||
| 72 | , const std::pair<time_t, uint32_t> & oldDatetime | ||
| 73 | , const std::pair<time_t, uint32_t> & newDateTime | ||
| 74 | ); | ||
| 75 | |||
| 76 | } // namespace ::na64dp::calib::aux | ||
| 77 | |||
| 78 | /**\brief A runs validity range index helper class. | ||
| 79 | * | ||
| 80 | * Maps objects that are valid _from_ certain run number. This helper class | ||
| 81 | * incapsulates `std::upper_bound()` over sorted associative array to provide | ||
| 82 | * a bit more intuitive way of retreiving the data associated with range. | ||
| 83 | * | ||
| 84 | * Ususal application is to index calibration data wrt runs ranges. */ | ||
| 85 | template<typename KeyT, typename T, typename Compare = std::less<KeyT>> | ||
| 86 | class RangeIndex : public std::map<KeyT, T, Compare> { | ||
| 87 | public: | ||
| 88 | typedef std::map<KeyT, T, Compare> Parent; | ||
| 89 | public: | ||
| 90 | typename Parent::iterator | ||
| 91 | 11 | get_valid_entry_for(KeyT rn) { | |
| 92 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | assert( !Parent::empty() ); |
| 93 | // it->first > rn, i.e. get iterator to the next element | ||
| 94 |
1/1✓ Branch 1 taken 11 times.
|
11 | auto it = Parent::upper_bound( rn ); |
| 95 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
|
11 | if( it == Parent::begin() ) { |
| 96 | 1 | return Parent::end(); | |
| 97 | } | ||
| 98 | // Return element previous to upper bound | ||
| 99 | 10 | return --it; | |
| 100 | } | ||
| 101 | }; | ||
| 102 | |||
| 103 | /**\brief A basic externally-configured calibration index class. | ||
| 104 | * | ||
| 105 | * Indexes calibration entries by key. Each entry of certain | ||
| 106 | * calibration type considered valid *from* certain key value *until* the | ||
| 107 | * next key value. The `validTo` attribute to the update recipe is considered | ||
| 108 | * only on the update loading and may result in a runtime expression. | ||
| 109 | */ | ||
| 110 | template<typename KeyT, typename T, typename Compare = std::less<KeyT>> | ||
| 111 | class RangeOverrideRunIndex : public virtual iIndex { | ||
| 112 | public: | ||
| 113 | struct UpdateRecipe : public iSpecificUpdate<T> { | ||
| 114 | Dispatcher::CIDataID subjectDataType; | ||
| 115 | KeyT validTo; | ||
| 116 | std::shared_ptr<iLoader> recommendedLoader; | ||
| 117 | |||
| 118 | ✗ | UpdateRecipe( Dispatcher::CIDataID dt | |
| 119 | , KeyT validTo_ | ||
| 120 | , std::shared_ptr<iLoader> lPtr | ||
| 121 | , const T pl | ||
| 122 | ) : iSpecificUpdate<T>(pl) | ||
| 123 | ✗ | , subjectDataType(dt) | |
| 124 | ✗ | , validTo(validTo_) | |
| 125 | ✗ | , recommendedLoader(lPtr) | |
| 126 | ✗ | {} | |
| 127 | |||
| 128 | ✗ | Dispatcher::CIDataID subject_data_type() const override | |
| 129 | ✗ | { return subjectDataType; } | |
| 130 | ✗ | iLoader * recommended_loader() const override | |
| 131 | ✗ | { return recommendedLoader.get(); } | |
| 132 | }; | ||
| 133 | private: | ||
| 134 | /// Collection of run range sub-indexes, grouped by calibration data type | ||
| 135 | std::unordered_map< std::string | ||
| 136 | , RangeIndex< KeyT, UpdateRecipe, Compare > | ||
| 137 | > _types; | ||
| 138 | public: | ||
| 139 | ✗ | virtual ~RangeOverrideRunIndex() {} | |
| 140 | /// Collects calibration information differences between two time | ||
| 141 | /// moments/runs | ||
| 142 | ✗ | void append_updates( EventID oldEventID, EventID newEventID | |
| 143 | , const std::pair<time_t, uint32_t> & oldDatetime | ||
| 144 | , const std::pair<time_t, uint32_t> & newDateTime | ||
| 145 | , Updates & ul ) override { | ||
| 146 | ✗ | auto keys = aux::deduce_key<KeyT>( oldEventID, newEventID | |
| 147 | , oldDatetime, newDateTime | ||
| 148 | ); | ||
| 149 | ✗ | for( auto it = _types.begin() | |
| 150 | ✗ | ; _types.end() != it | |
| 151 | ✗ | ; ++it ) { | |
| 152 | ✗ | auto cidID = it->first; | |
| 153 | ✗ | auto & runIdx = it->second; | |
| 154 | ✗ | auto oldEntryIt = runIdx.get_valid_entry_for( keys.first ) | |
| 155 | ✗ | , newEntryIt = runIdx.get_valid_entry_for( keys.second ) | |
| 156 | ; | ||
| 157 | ✗ | if( oldEntryIt == newEntryIt ) continue; | |
| 158 | ✗ | if( runIdx.end() == newEntryIt ) { | |
| 159 | ✗ | throw errors::NoValidForRange(); | |
| 160 | } | ||
| 161 | ✗ | ul.push_back(&(newEntryIt->second)); | |
| 162 | ✗ | log4cpp::Category::getInstance("calibrations.indeces.rangeOverride") | |
| 163 | ✗ | << log4cpp::Priority::DEBUG | |
| 164 | ✗ | << "Update of (aliased) type \"" | |
| 165 | ✗ | << it->first | |
| 166 | ✗ | << "\" with validity (" | |
| 167 | ✗ | << aux::key_to_str_for_yaml(newEntryIt->first) | |
| 168 | ✗ | << ") has been pushed into updates list for " | |
| 169 | ✗ | << "/(" | |
| 170 | ✗ | << aux::key_to_str_for_yaml(oldEventID) << "), (" | |
| 171 | ✗ | << aux::key_to_str_for_yaml(newEventID) << ")/ -> /(" | |
| 172 | ✗ | << aux::key_to_str_for_yaml(oldDatetime) << ", " | |
| 173 | ✗ | << aux::key_to_str_for_yaml(newDateTime) << ")/" | |
| 174 | ✗ | << "." | |
| 175 | ; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | /// Adds new calibration data entry into index | ||
| 180 | ✗ | void add_entry( KeyT k | |
| 181 | , const std::string & typeKey | ||
| 182 | , const UpdateRecipe & updRecipe | ||
| 183 | ) { | ||
| 184 | // find or insert typed entry (ranges index) | ||
| 185 | ✗ | auto tIt = _types.find( typeKey ); | |
| 186 | ✗ | if( _types.end() == tIt ) { | |
| 187 | ✗ | auto ir = _types.emplace( typeKey | |
| 188 | ✗ | , RangeIndex<KeyT, UpdateRecipe, Compare>() ); | |
| 189 | ✗ | tIt = ir.first; | |
| 190 | } | ||
| 191 | // emplace new entry | ||
| 192 | ✗ | RangeIndex< KeyT, UpdateRecipe, Compare> & rangeIndex = tIt->second; | |
| 193 | ✗ | auto ir = rangeIndex.emplace(k, updRecipe); | |
| 194 | ✗ | if( ! ir.second ) { | |
| 195 | ✗ | log4cpp::Category::getInstance("calibrations.indeces.rangeOverride").warn( | |
| 196 | "Doubling specification for calibration" | ||
| 197 | " data %s." | ||
| 198 | , typeKey.c_str() | ||
| 199 | //, std::to_string(k.to_str()) | ||
| 200 | ); | ||
| 201 | } else { | ||
| 202 | ✗ | log4cpp::Category::getInstance("calibrations.indeces.rangeOverride") | |
| 203 | ✗ | << log4cpp::Priority::DEBUG | |
| 204 | ✗ | << "RangeOverrideRunIndex::add_entry(" | |
| 205 | ✗ | << aux::key_to_str_for_yaml(k) << ", " | |
| 206 | ✗ | << typeKey << ", (" | |
| 207 | ✗ | << aux::key_to_str_for_yaml(updRecipe.validTo) | |
| 208 | ✗ | << ", ...)" | |
| 209 | ; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | /// Dumps content of the index index to YAML node for debugging | ||
| 213 | ✗ | void to_yaml(YAML::Node & outNode) const override { | |
| 214 | outNode["keyType"] | ||
| 215 | ✗ | = std::string(util::demangle_cpp(typeid(KeyT).name()).get()); | |
| 216 | outNode["valueType"] | ||
| 217 | ✗ | = std::string(util::demangle_cpp(typeid(T).name()).get()); | |
| 218 | ✗ | YAML::Node typesNode; | |
| 219 | ✗ | for( auto subIdxPair : _types ) { | |
| 220 | char bf[64]; | ||
| 221 | // NOTE: subIdxPair.second is always of UpdateRecipe | ||
| 222 | ✗ | YAML::Node typedNode; | |
| 223 | ✗ | size_t nEntry = 0; | |
| 224 | ✗ | for( auto updPair : subIdxPair.second ) { | |
| 225 | ✗ | YAML::Node entryNode; | |
| 226 | ✗ | entryNode["validFrom"] = aux::key_to_str_for_yaml(updPair.first); | |
| 227 | ✗ | entryNode["validTo"] = aux::key_to_str_for_yaml(updPair.second.validTo); | |
| 228 | ✗ | entryNode["subjectType"] = util::calib_id_to_str(updPair.second.subjectDataType); | |
| 229 | ✗ | snprintf(bf, sizeof(bf), "%p", updPair.second.recommendedLoader.get()); | |
| 230 | ✗ | typedNode["recommendedLoader"] = bf; | |
| 231 | ✗ | updPair.second.to_yaml(entryNode); | |
| 232 | ✗ | typedNode[nEntry++] = entryNode; | |
| 233 | } | ||
| 234 | ✗ | typesNode[subIdxPair.first] = typedNode; | |
| 235 | } | ||
| 236 | ✗ | outNode["byTypeAlias"] = typesNode; | |
| 237 | } | ||
| 238 | }; | ||
| 239 | |||
| 240 | } // namespace calib | ||
| 241 | } // namespace na64dp | ||
| 242 | |||
| 243 |