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