ifw-daq 3.1.0
IFW Data Acquisition modules
Loading...
Searching...
No Matches
manager.hpp
Go to the documentation of this file.
1/**
2 * @file
3 * @ingroup daq_config
4 * @copyright (c) Copyright ESO 2022
5 * All Rights Reserved
6 * ESO (eso.org) is an Intergovernmental Organisation, and therefore special legal conditions apply.
7 *
8 * @brief daq::config::Manager and associated types.
9 */
10#ifndef DAQ_CONFIG_MANAGER_HPP
11#define DAQ_CONFIG_MANAGER_HPP
12
13#include <functional>
14#include <iosfwd>
15#include <optional>
16#include <string>
17#include <unordered_map>
18
19#include <boost/algorithm/string/join.hpp>
20#include <config-ng/ciiConfigApi.hpp>
21#include <fmt/chrono.h>
22#include <fmt/format.h>
23#include <fmt/ostream.h>
24#include <fmt/ranges.h>
25
26#include <log4cplus/logger.h>
27#include <log4cplus/loggingmacros.h>
28#include <rad/cii/oldbTypes.hpp>
29
30namespace daq::config {
31
32/**
33 * Configuration origins in descending priority.
34 *
35 * This is used to indicate from where a configuration parameter originates as well as its
36 * configuration priority. Higher or equal priority (lower number) replace lower or equal priority
37 * origins.
38 */
39enum class Origin {
40 /**
41 * Runtime change via e.g. request/reply command.
42 */
43 Runtime = 0,
44 /**
45 * Command line argument.
46 */
48 /**
49 * Configuration file.
50 */
52 /**
53 * Environment variable.
54 */
56 /**
57 * Built-in default value.
58 */
59 Default,
60};
61
62/**
63 * Format Origin
64 *
65 * @param os output stream.
66 * @param origin Origin to format.
67 */
68std::ostream& operator<<(std::ostream& os, Origin origin);
69
70/**
71 * Immutable information about a configuration attribute.
72 */
73struct Metadata {
74 std::string canonical_name;
75 std::string description;
76};
77
78/**
79 * Extension point to allow adaptations from non-formattable configurations types.
80 */
81template <class T>
82struct Formatter {
83 static T const& Format(T const& t) {
84 return t;
85 }
86};
87
88template <class T>
89struct Converter {
90 static rad::cii::OldbType ToCii(T const& t) {
91 return t;
92 }
93};
94
95template <>
96struct Converter<std::filesystem::path> {
97 static rad::cii::OldbType ToCii(std::filesystem::path const& p) {
98 return p.string();
99 }
100};
101
102template <>
103struct Converter<std::chrono::seconds> {
104 static rad::cii::OldbType ToCii(std::chrono::seconds const& s) {
105 return s.count();
106 }
107};
108
109template <>
110struct Converter<std::chrono::hours> {
111 static rad::cii::OldbType ToCii(std::chrono::hours const& s) {
112 return s.count();
113 }
114};
115
116/**
117 * @note: rad does not support vector of strings at the moment.
118 */
119template <>
120struct Converter<std::vector<std::string>> {
121 static rad::cii::OldbType ToCii(std::vector<std::string> const& s) {
122 return boost::algorithm::join(s, ", ");
123 }
124};
125
126/**
127 * Mutable metadata about a configuration attribute that describes where a value comes from.
128 */
131 /**
132 * May include additional information like which configuration file was used.
133 */
134 std::string description = "";
135};
136
137/**
138 * Format OriginInfo
139 *
140 * @param os output stream.
141 * @param info OriginInfo to format.
142 */
143std::ostream& operator<<(std::ostream& os, OriginInfo const& info);
144
145/**
146 * Maintains the associativity of configuration attributes with metadata and value origin/priority.
147 *
148 * Main responsibilities:
149 * - Maintain associativity between configuration attribute and metadata.
150 * - Implement update logic in a way that configuration can only be overwritten if origin has a
151 * higher priority.
152 *
153 * Outside the scope:
154 * - Implement parsing of various configuration origins.
155 *
156 * Manager works on the following assumptions and principles:
157 *
158 * - There is a 1:1 relationship between configuration class and manager.
159 * - Configuration point identity is the member pointer to configuration class member variable.
160 */
161class Manager {
162public:
163 /**
164 * Constructor
165 *
166 * @param logger Where to log to.
167 */
168 Manager(log4cplus::Logger const& logger) : m_logger(logger) {
169 }
170
171 /**
172 * Registers a configuration parameter/attribute.
173 *
174 * @param ptr Configuration value pointer.
175 * @param meta Metadata associated with attribute.
176 * @throws std::exception if `ptr` has already been registered before or if allocation fails.
177 */
178 template <class AttrType>
179 void Register(AttrType* ptr, Metadata const& meta);
180
181 template <class AttrType, class Converter>
182 void Register(AttrType* ptr, Metadata const& meta, Converter&& c);
183
184 /**
185 * Update configuration value taking into account the origin of the change.
186 *
187 * @param ptr Configuration value pointer.
188 * @param value new configuration value.
189 * @param origin Specifies origin of configuration value.
190 * @returns true if configuration was changed false otherwise.
191 * @throws std::exception if configuration member has not been registered before.
192 */
193 template <class AttrType, class T>
194 bool Update(AttrType* ptr, T const& value, OriginInfo const& origin);
195
196 /**
197 * Describes current value.
198 */
199 template <class AttrType>
201 AttrType const& value;
204 };
205
206 /**
207 * CII representation of real value.
208 */
209 struct CiiValue {
210 rad::cii::OldbType value;
213 };
214
215 /**
216 * Get current configuration value and associated metadata and origin
217 * @param ptr Configuration member pointer.
218 * @throws std::exception if configuration is unknown.
219 */
220 template <class AttrType>
221 CurrentValue<AttrType> Get(AttrType* ptr) const;
222
223 /**
224 * Visit each registered parameter.
225 *
226 * @tparam Func Callable invocable with `func(cii_value)` where `cii_value` is const-reference
227 * CiiValue.
228 */
229 template <class Func>
230 void Visit(Func&& func) {
231 for (auto const& entry : m_update) {
232 auto value =
233 CiiValue{entry.second.as_cii(), entry.second.origin, entry.second.metadata};
234 func(value);
235 }
236 }
237
238private:
239 struct State {
240 Metadata metadata;
241 OriginInfo origin;
242 std::function<rad::cii::OldbType()> as_cii;
243 };
244
245 log4cplus::Logger m_logger;
246 // Type erased registered attributes
247 std::unordered_map<void*, State> m_update;
248};
249
250template <class AttrType>
251void Manager::Register(AttrType* ptr, Metadata const& m) {
252 // Note pointer to member cannot be converted to void*, so we store the actual data pointer to
253 // the instance provided in constructor. This works equally well as we only care about using
254 // &C::member syntax as an identity.
255 if (auto it = m_update.find(ptr); it != m_update.end()) {
256 throw std::invalid_argument("Attribute already registered");
257 }
258 m_update.insert(
259 std::make_pair(static_cast<void*>(ptr),
260 State{m, {}, [ptr]() { return Converter<AttrType>::ToCii(*ptr); }}));
261 LOG4CPLUS_INFO(m_logger,
262 fmt::format("Registering configuration parameter <{}> with default value {}",
265}
266
267template <class AttrType, class Converter>
268void Manager::Register(AttrType* ptr, Metadata const& m, Converter&& converter) {
269 // Note pointer to member cannot be converted to void*, so we store the actual data pointer to
270 // the instance provided in constructor. This works equally well as we only care about using
271 // &C::member syntax as an identity.
272 if (auto it = m_update.find(ptr); it != m_update.end()) {
273 throw std::invalid_argument("Attribute already registered");
274 }
275 m_update.insert(
276 std::make_pair(static_cast<void*>(ptr),
277 State{m, {}, [ptr, c = std::move(converter)]() { return c(*ptr); }}));
278}
279
280template <class AttrType>
281typename Manager::template CurrentValue<AttrType> Manager::Get(AttrType* ptr) const {
282 // Note pointer to member cannot be converted to void*, so we store the actual data pointer to
283 // the instance provided in constructor. This works equally well as we only care about using
284 // &C::member syntax as an identity.
285 if (auto it = m_update.find(ptr); it == m_update.end()) {
286 throw std::invalid_argument("Attribute not previously registered");
287 } else {
288 return CurrentValue<AttrType>{*ptr, it->second.origin, it->second.metadata};
289 }
290}
291
292template <class AttrType, class T>
293bool Manager::Update(AttrType* ptr, T const& value, OriginInfo const& new_origin) {
294 if (auto it = m_update.find(ptr); it == m_update.end()) {
295 throw std::invalid_argument("Attribute not previously registered");
296 } else {
297 // Keep old value and assign new before logging as T may not be the same as AttrType
298 auto const& old_value = *ptr;
299 auto const& new_value = value;
300 auto const& meta = it->second.metadata;
301 auto const& old_origin = it->second.origin;
302
303 if (new_origin.origin <= it->second.origin.origin) {
304 // Same or higher priority origin -> update!
305 LOG4CPLUS_INFO(m_logger,
306 fmt::format("Updating configuration value for <{}> from "
307 "old value {} ({}) -> new value {} ({})",
308 meta.canonical_name,
310 old_origin,
311 Formatter<T>::Format(new_value),
312 new_origin));
313 it->second.origin = new_origin;
314 *ptr = new_value;
315 return true;
316 } else {
317 // Current value came from higher priority origin!
318 LOG4CPLUS_DEBUG(m_logger,
319 fmt::format("Will _not_ update configuration value for <{}> from "
320 "old value {} (origin {}) -> new value {} (origin {}) as "
321 "current origin has higher priority",
322 meta.canonical_name,
324 old_origin,
325 Formatter<T>::Format(new_value),
326 new_origin));
327 return false;
328 }
329 }
330}
331
332/**
333 * Performs lookup of parameters in the form `root/node/leaf` relative to the provided node.
334 *
335 * @param selector Configuration selector in the format `node/node/param`
336 * @param node The configuration node to select from.
337 */
338std::optional<std::reference_wrapper<elt::configng::CiiConfigInstanceNode const>>
339GetParam(std::string const& selector, elt::configng::CiiConfigInstanceNode const& node);
340
341/**
342 * Performs lookup of parameters in the form `root/node/leaf` relative to the provided node.
343 *
344 * @param selector Configuration selector in the format `node/node/param`
345 * @param node The configuration node to select from.
346 * @tparam T Type convertion to perform.
347 */
348template <class T>
349std::optional<T>
350GetParamAs(std::string const& query, elt::configng::CiiConfigInstanceNode const& node) {
351 if (auto opt = GetParam(query, node); opt.has_value()) {
352 return opt->get().As<T>();
353 }
354 return {};
355}
356
357} // namespace daq::config
358
359template <>
360struct fmt::formatter<daq::config::OriginInfo> : ostream_formatter {};
361template <>
362struct fmt::formatter<daq::config::Origin> : ostream_formatter {};
363
364#endif // DAQ_CONFIG_MANAGER_HPP
Maintains the associativity of configuration attributes with metadata and value origin/priority.
Definition: manager.hpp:161
OriginInfo const & origin
Definition: manager.hpp:211
void Visit(Func &&func)
Visit each registered parameter.
Definition: manager.hpp:230
bool Update(AttrType *ptr, T const &value, OriginInfo const &origin)
Update configuration value taking into account the origin of the change.
Definition: manager.hpp:293
rad::cii::OldbType value
Definition: manager.hpp:210
Manager(log4cplus::Logger const &logger)
Constructor.
Definition: manager.hpp:168
void Register(AttrType *ptr, Metadata const &meta)
Registers a configuration parameter/attribute.
Definition: manager.hpp:251
CurrentValue< AttrType > Get(AttrType *ptr) const
Get current configuration value and associated metadata and origin.
Metadata const & metadata
Definition: manager.hpp:212
CII representation of real value.
Definition: manager.hpp:209
Describes current value.
Definition: manager.hpp:200
std::string description
Definition: manager.hpp:75
std::string canonical_name
Definition: manager.hpp:74
std::string description
May include additional information like which configuration file was used.
Definition: manager.hpp:134
Origin
Configuration origins in descending priority.
Definition: manager.hpp:39
@ Configuration
Configuration file.
@ Default
Built-in default value.
@ Runtime
Runtime change via e.g.
@ CommandLine
Command line argument.
@ EnvironmentVariable
Environment variable.
std::optional< std::reference_wrapper< elt::configng::CiiConfigInstanceNode const > > GetParam(std::string const &query, elt::configng::CiiConfigInstanceNode const &node)
Performs lookup of parameters in the form root/node/leaf relative to the provided node.
Definition: manager.cpp:49
std::ostream & operator<<(std::ostream &os, Origin origin)
Format Origin.
Definition: manager.cpp:18
std::optional< T > GetParamAs(std::string const &query, elt::configng::CiiConfigInstanceNode const &node)
Performs lookup of parameters in the form root/node/leaf relative to the provided node.
Definition: manager.hpp:350
Immutable information about a configuration attribute.
Definition: manager.hpp:73
Mutable metadata about a configuration attribute that describes where a value comes from.
Definition: manager.hpp:129
State
Observable states of the data acquisition process.
Definition: state.hpp:41
static rad::cii::OldbType ToCii(std::chrono::hours const &s)
Definition: manager.hpp:111
static rad::cii::OldbType ToCii(std::chrono::seconds const &s)
Definition: manager.hpp:104
static rad::cii::OldbType ToCii(std::filesystem::path const &p)
Definition: manager.hpp:97
static rad::cii::OldbType ToCii(std::vector< std::string > const &s)
Definition: manager.hpp:121
static rad::cii::OldbType ToCii(T const &t)
Definition: manager.hpp:90
Extension point to allow adaptations from non-formattable configurations types.
Definition: manager.hpp:82
static T const & Format(T const &t)
Definition: manager.hpp:83