| 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 "na64util/uri.hh" | ||
| 20 | #include "na64detID/detectorID.hh" | ||
| 21 | #include "na64calib/dispatcher.hh" | ||
| 22 | #include "na64calib/loader.hh" | ||
| 23 | |||
| 24 | #include <log4cpp/Category.hh> | ||
| 25 | |||
| 26 | #include "na64util/na64/event-id.hh" | ||
| 27 | |||
| 28 | namespace na64dp { | ||
| 29 | namespace calib { | ||
| 30 | |||
| 31 | /**\brief Global singleton class indexing calibration data type aliases | ||
| 32 | * | ||
| 33 | * Provides general match between the string identifier used in runtime | ||
| 34 | * configuration files and internal data type identifier used to uniquely | ||
| 35 | * address particular calibration data type. | ||
| 36 | * | ||
| 37 | * Since aliases dictionary usually instantiated at the very early stage | ||
| 38 | * of application runtime (when constructors get called), logging system | ||
| 39 | * is not available for this isntance. App shall explicitly call the | ||
| 40 | * `init_logging()` afterwards to make this class cope with general | ||
| 41 | * logging system | ||
| 42 | * | ||
| 43 | * \note Dynamically loaded modules uses this singleton before main logging | ||
| 44 | * is initialized. | ||
| 45 | * */ | ||
| 46 | class CIDataAliases { | ||
| 47 | private: | ||
| 48 | static CIDataAliases * _self; | ||
| 49 | public: | ||
| 50 | /// Dictionary type for aliases (name to RTTI data type) | ||
| 51 | typedef std::unordered_map<std::string, Dispatcher::CIDataID> | ||
| 52 | TypeIDByName; | ||
| 53 | /// Dictionary type for aliases (RTTI data type to name) | ||
| 54 | typedef std::unordered_map<Dispatcher::CIDataID, std::string, util::PairHash> | ||
| 55 | NameByTypeID; | ||
| 56 | /// List of calib data type dependencies | ||
| 57 | /// | ||
| 58 | /// Organized as unordered multimap internally (pairs of "product" and | ||
| 59 | /// "requirement"). Required type aliases are not resolved in this | ||
| 60 | /// container. This way user code may mention dependencies before they | ||
| 61 | /// actually appear at a runtime. | ||
| 62 | typedef std::unordered_multimap< Dispatcher::CIDataID | ||
| 63 | , std::string | ||
| 64 | , util::PairHash > Dependencies; | ||
| 65 | protected: | ||
| 66 | /// Aliases dictionary, RTTI data type by str name | ||
| 67 | TypeIDByName _nameToTypeID; | ||
| 68 | /// Reverse aliases dictionary, str name by RTTI data type | ||
| 69 | NameByTypeID _typeIDToName; | ||
| 70 | /// Type's dependencies | ||
| 71 | Dependencies _depends; | ||
| 72 | /// Ptr to logging category | ||
| 73 | log4cpp::Category * _logPtr; | ||
| 74 | |||
| 75 | /// Dependencies cache validity flag | ||
| 76 | mutable bool _depsCacheValid; | ||
| 77 | /// Dependencies cache | ||
| 78 | mutable std::unordered_multimap< Dispatcher::CIDataID | ||
| 79 | , Dispatcher::CIDataID | ||
| 80 | , util::PairHash > _depsCache; | ||
| 81 | |||
| 82 | |||
| 83 | CIDataAliases(); | ||
| 84 | public: | ||
| 85 | CIDataAliases(const CIDataAliases &) = delete; | ||
| 86 | |||
| 87 | ///\brief Adds new alias for calibration data type | ||
| 88 | /// | ||
| 89 | ///\throw Runtime error on type collision. | ||
| 90 | void add_alias( const std::string &, Dispatcher::CIDataID ); | ||
| 91 | /// Adds new alias for calibration data type (CIDataID derived | ||
| 92 | /// automatically) | ||
| 93 | template<typename T> Dispatcher::CIDataID | ||
| 94 | add_alias_of( const std::string aliasName | ||
| 95 | , const std::string strTypeID ) { | ||
| 96 | auto td = Dispatcher::info_id<T>(strTypeID); | ||
| 97 | add_alias(aliasName, td); | ||
| 98 | return td; | ||
| 99 | } | ||
| 100 | /// Returns calibration data type aliases names indexed by type ID | ||
| 101 | 71 | const NameByTypeID & name_by_type_id() const { return _typeIDToName; } | |
| 102 | /// Returns calibration data type IDs indexed by alias | ||
| 103 | ✗ | const TypeIDByName & type_id_by_name() const { return _nameToTypeID; } | |
| 104 | |||
| 105 | /// Defines calibration data dependency | ||
| 106 | void add_dependency( const std::string & product | ||
| 107 | , const std::string & requirement | ||
| 108 | ); | ||
| 109 | /// Returns map of dependencies between types | ||
| 110 | const std::unordered_multimap< Dispatcher::CIDataID | ||
| 111 | , Dispatcher::CIDataID | ||
| 112 | , util::PairHash > & | ||
| 113 | dependency_map() const; | ||
| 114 | |||
| 115 | |||
| 116 | /// Has to be invoked once the logging subsystem is initialized. | ||
| 117 | void init_logging(); | ||
| 118 | |||
| 119 | /// Returns instance | ||
| 120 | static CIDataAliases & self(); | ||
| 121 | }; | ||
| 122 | |||
| 123 | /// Macro for runtime register of the new calibration data type | ||
| 124 | #define REGISTER_CALIB_DATA_TYPE(T, alias, subClass) \ | ||
| 125 | static const na64dp::calib::Dispatcher::CIDataID _calibDataTypeID_ ## alias \ | ||
| 126 | = ::na64dp::calib::CIDataAliases::self().add_alias_of<T>( \ | ||
| 127 | # alias, subClass ); | ||
| 128 | |||
| 129 | /**\brief Provides highest level API to access calibration data of all types | ||
| 130 | * | ||
| 131 | * Manages runtime calibration data fetch, retrieval, storage and | ||
| 132 | * on-change notifications. | ||
| 133 | * | ||
| 134 | * Has multiple instances of run indexes and calibration data loader imposed | ||
| 135 | * at a runtime. The first kind of entities implements interface | ||
| 136 | * `iIndex` that shall define what kind of calibration data must be | ||
| 137 | * updated and from which source. The former (loader) will load it. | ||
| 138 | * | ||
| 139 | * Order of insertion of the run indexes matters in case of collisions: | ||
| 140 | * typically the last inserted will displace updates imposed by previous. This | ||
| 141 | * behaviour is not strict and defined by index: `iIndex` instances | ||
| 142 | * get the reentrant updates dictionary and may abstain from displacing | ||
| 143 | * calibration data, depending on implementation (though, displacing is | ||
| 144 | * implied). | ||
| 145 | * | ||
| 146 | * Manager does not maintain the lifetime run indexes and loaders. | ||
| 147 | * | ||
| 148 | * There must be a single instance of the manager in process. | ||
| 149 | * | ||
| 150 | * \todo Threading synchronization primitives as it can be accessed from | ||
| 151 | * different threads. | ||
| 152 | * */ | ||
| 153 | class Manager : public Dispatcher | ||
| 154 | { | ||
| 155 | protected: | ||
| 156 | /// Ordered sequence of indeces to collect the updates | ||
| 157 | std::vector<std::shared_ptr<iIndex>> _indeces; | ||
| 158 | /// Ordered list of loaders to load the update; names used by some kind | ||
| 159 | /// of indexes | ||
| 160 | std::vector<std::pair<std::string, std::shared_ptr<iLoader>>> _loaders; | ||
| 161 | /// Index of loaders by name (used for aux utils) | ||
| 162 | std::unordered_map<std::string, std::shared_ptr<iLoader>> _loadersByName; | ||
| 163 | |||
| 164 | /// Current event ID | ||
| 165 | EventID _cEventID; | ||
| 166 | /// Date+time of the current event (or 0) | ||
| 167 | std::pair<time_t, uint32_t> _cDatetime; | ||
| 168 | protected: | ||
| 169 | /// Collects updates from runs indexes returning a list of updates that | ||
| 170 | /// have to be applied at certain event ID. Does not modify the state of | ||
| 171 | /// calibration data. | ||
| 172 | void _collect_updates( Updates & upds | ||
| 173 | , EventID eventID | ||
| 174 | , const std::pair<time_t, uint32_t> & eventTime | ||
| 175 | ) const; | ||
| 176 | |||
| 177 | /// Sorts updates taking into account their dependencies | ||
| 178 | /// | ||
| 179 | /// Updates list not necessarily contains all the dependencies of depndee. | ||
| 180 | /// They must be loaded beforehead (in previous invokations), so this | ||
| 181 | /// method does not controll all the dependencies are satisfied, it just | ||
| 182 | /// re-shuffles updates it got in a proper order. | ||
| 183 | void _sort_updates( Updates & ); | ||
| 184 | |||
| 185 | /// Forwards update loading to appropriate loader | ||
| 186 | void _load_updates( const Updates & updates ); | ||
| 187 | public: | ||
| 188 | Manager(log4cpp::Category & L); | ||
| 189 | virtual ~Manager(); | ||
| 190 | |||
| 191 | /// Returns current run number | ||
| 192 | EventID event_id() const { return _cEventID; } | ||
| 193 | /// Returns time structure associated with the current event | ||
| 194 | const std::pair<time_t, uint32_t> time() const { return _cDatetime; } | ||
| 195 | |||
| 196 | /// Sets current event ID | ||
| 197 | /// | ||
| 198 | /// Forwards execution to `collect_updates()` and, if at least one update | ||
| 199 | /// was found, calls `load_updates()`. Then sets the `_cEventID`. | ||
| 200 | void event_id( EventID, const std::pair<time_t, uint32_t> & ); | ||
| 201 | |||
| 202 | |||
| 203 | void add_index( std::shared_ptr<iIndex> iPtr ) { | ||
| 204 | _indeces.push_back( iPtr ); | ||
| 205 | } | ||
| 206 | |||
| 207 | void add_loader( const std::string & name | ||
| 208 | , std::shared_ptr<iLoader> lPtr | ||
| 209 | ) { | ||
| 210 | _loaders.push_back(std::pair<std::string, std::shared_ptr<iLoader>>(name, lPtr)); | ||
| 211 | _loadersByName.emplace(name, lPtr); | ||
| 212 | } | ||
| 213 | |||
| 214 | const std::unordered_map<std::string, std::shared_ptr<iLoader>> & | ||
| 215 | loaders_by_name() const { return _loadersByName; } | ||
| 216 | |||
| 217 | ///\brief Returns registered loader identified by name | ||
| 218 | /// | ||
| 219 | /// Since application usually provides some generalized loaders that have | ||
| 220 | /// to be extended at a runtime, user code must be able to retrieve them | ||
| 221 | /// for modification (yet, the modification of the list of the loaders | ||
| 222 | /// itself is steered by `add_loader()`). | ||
| 223 | /// | ||
| 224 | /// \throw runtime error if loader is not found or wrong type is requested | ||
| 225 | // for cast. | ||
| 226 | template<typename LoaderT> | ||
| 227 | LoaderT & get_loader(const std::string & loaderName) { | ||
| 228 | auto it = _loadersByName.find(loaderName); | ||
| 229 | if( _loadersByName.end() == it ) { | ||
| 230 | NA64DP_RUNTIME_ERROR( "No loader \"%s\" known to calibration data" | ||
| 231 | " dispatcher.", loaderName.c_str() ); | ||
| 232 | } | ||
| 233 | try { | ||
| 234 | return dynamic_cast<LoaderT&>(*it->second); | ||
| 235 | } catch( std::bad_cast & ) { | ||
| 236 | NA64DP_RUNTIME_ERROR( "Bad downcast of loader \"%s\"." | ||
| 237 | , loaderName.c_str() | ||
| 238 | ); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | /// Dumps calibration data hierarchy to YAML node for inspection | ||
| 243 | void to_yaml( YAML::Node & ) const; | ||
| 244 | |||
| 245 | #if 0 | ||
| 246 | /// Adds calibration data loader instance | ||
| 247 | virtual void add_loader( const std::string & loaderName | ||
| 248 | , iLoader * loaderPtr ); | ||
| 249 | /// Adds run index instance | ||
| 250 | virtual void add_run_index( iIndex * runIndex ); | ||
| 251 | |||
| 252 | /// Returns reference to the list of registered loaders | ||
| 253 | const std::unordered_map<std::string, iLoader *> & loaders() const { return _ciLoaders; } | ||
| 254 | #endif | ||
| 255 | }; | ||
| 256 | |||
| 257 | } // namespace ::na64dp::calib | ||
| 258 | } // namespace na64dp | ||
| 259 | |||
| 260 |