ifw-daq  3.0.0-pre2
IFW Data Acquisition modules
config.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @ingroup server
4  * @copyright ESO - European Southern Observatory
5  * @author
6  *
7  * @brief Config class source file.
8  */
9 
10 #include "config.hpp"
11 
12 #include <iostream>
13 #include <optional>
14 
15 #include <rad/assert.hpp>
16 #include <rad/config.hpp>
17 #include <rad/exceptions.hpp>
18 #include <rad/helper.hpp>
19 
20 #include <boost/program_options.hpp>
21 #include <fmt/chrono.h>
22 #include <fmt/format.h>
23 
24 #include <daq/config/manager.hpp>
25 
26 #include "logger.hpp"
27 
28 namespace bpo = boost::program_options;
29 
30 template <>
31 struct daq::config::Formatter<std::filesystem::path> {
32  static auto Format(std::filesystem::path const &t) {
33  return t.c_str();
34  }
35 };
36 
37 namespace server {
38 
40  : m_mgr(GetLogger())
41  , m_proc_name(CONFIG_DEFAULT_PROCNAME)
42  , m_log_level(CONFIG_DEFAULT_LOG_LEVEL)
43  , m_log_properties()
44  , m_config_filename(CONFIG_DEFAULT_FILENAME)
45  , m_scxml_filename(CONFIG_DEFAULT_SCXML_FILENAME)
46  , m_db_prefix(CONFIG_DEFAULT_OLDB_URI_PREFIX + "/ocm")
47  , m_db_timeout_sec(CONFIG_DEFAULT_DB_TIMEOUT_SEC)
48  , m_req_endpoint(CONFIG_DEFAULT_REQ_ENDPOINT)
49  , m_pub_endpoint(CONFIG_DEFAULT_PUB_ENDPOINT)
50  , m_out_path()
51  , m_workspace(CONFIG_DEFAULT_WORKSPACE) {
52  RAD_TRACE(GetLogger());
53  m_mgr.Register(&m_instrument_id, {"cfg/instrument_id", "Instrument id"});
54  m_mgr.Register(&m_proc_name, {rad::KEY_CONFIG_PROCNAME, "Component instance ID"});
55  m_mgr.Register(&m_log_level, {rad::KEY_CONFIG_LOG_LEVEL, "Log verbosity level"});
56  m_mgr.Register(&m_log_properties, {rad::KEY_CONFIG_LOG_PROPERTIES, "Log configuration file"});
57  m_mgr.Register(&m_config_filename, {rad::KEY_CONFIG_FILENAME, "YAML configuration file path"});
58 
60  {rad::KEY_CONFIG_SM_SCXML, "State machine SCXML model file path"});
61  m_mgr.Register(&m_db_prefix, {rad::KEY_CONFIG_OLDB_URI_PREFIX, "CII OLDB URI prefix"});
63  {rad::KEY_CONFIG_OLDB_CONN_TIMEOUT, "CII OLDB timeout (sec)"});
64  m_mgr.Register(&m_out_path, {KEY_CONFIG_DATAROOT, "ICS standard DATAROOT directory"});
65  m_mgr.Register(&m_req_endpoint, {rad::KEY_CONFIG_REQ_ENDPOINT, "Request/reply URI"});
66  m_mgr.Register(&m_pub_endpoint, {"cfg/pub_endpoint", "Publish/subscribe URI"});
68  {"cfg/daq/workspace", "OCM DAQ workspace"});
69 
70  m_mgr.Register(&m_dpm_params.rr_uri, {"cfg/dpm/req_endpoint", "DPM request/reply URI"});
71  m_mgr.Register(&m_dpm_params.ps_uri, {"cfg/dpm/pub_endpoint", "DPM publish/subscribe URI"});
73  {"cfg/dpm/timeout_sec", "DPM timeout (sec)"});
74 
77  {"cfg/daq/stale_acquiring_hours", "Age when DAQ acquisitions are considered stale (hours)"});
80  {"cfg/daq/stale_merging_hours", "Age when DAQ merges are considered stale (hours)"});
81 
82  /*
83  * Read environment variables.
84  */
85  if (auto val = rad::Helper::GetEnvVar(CONFIG_ENVVAR_OUT_PATH); !val.empty()) {
87  }
88 }
89 
90 bool Config::ParseOptions(int argc, char *argv[]) {
91  RAD_TRACE(GetLogger());
92 
93  /*
94  * Define command line options.
95  */
96  bpo::options_description options_desc("Options");
97  // clang-format off
98  options_desc.add_options()
99  ("help,h", "Print help messages")
100  ("proc-name,n",
101  bpo::value<std::string>(),
102  "Process name")
103  ("log-level,l",
104  bpo::value<std::string>(),
105  "Log level: ERROR, WARNING, STATE, EVENT, ACTION, INFO, DEBUG, TRACE")
106  ("config,c",
107  bpo::value<std::string>(),
108  "Configuration filename")
109  ("db-host,d",
110  bpo::value<std::string>(),
111  "*deprecated* In-memory DB host (ipaddr:port)");
112  // clang-format on
113 
114  // Parse the options.
115  try {
116  bpo::variables_map options_map;
117  bpo::store(bpo::parse_command_line(argc, argv, options_desc), options_map);
118  if (options_map.count("help")) {
119  std::cout << options_desc << std::endl;
120  return false;
121  }
122 
123  /*
124  * Throws on error, so do after help in case
125  * there are any problems.
126  */
127  bpo::notify(options_map);
128 
130 
131  if (options_map.count("log-level")) {
132  // Note we cannot use log4cplus::LogLevel as it's an alias to int and we want to parse
133  // a string using ostream, so we use the daq helper daq::LogLevel instead.
134  auto ll_str = options_map["log-level"].as<std::string>();
135  m_mgr.Update(&m_log_level, ll_str, si);
136  }
137 
138  if (options_map.count("proc-name")) {
139  auto value = options_map["proc-name"].as<std::string>();
140  if (m_mgr.Update(&m_proc_name, value, si)) {
141  // Since proc-name updated we also update default for db-prefix
145  }
146  }
147 
148  if (options_map.count("config")) {
149  auto value = options_map["config"].as<std::string>();
150  m_mgr.Update(&m_config_filename, value, si);
151  }
152  if (options_map.count("db-host")) {
153  LOG4CPLUS_WARN(GetLogger(),
154  "Command line parameter --db-host|-d is deprecated and only provided "
155  "for compatibility");
156  }
157  } catch (bpo::error &e) {
158  std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
159  std::cerr << options_desc << std::endl;
160  throw std::invalid_argument(e.what());
161  }
162 
163  return true;
164 }
165 
166 void Config::LoadConfig(const std::string &filename) {
167  RAD_TRACE(GetLogger());
168  m_config.emplace();
169 
170  std::string config_filename = filename;
171  if (config_filename == "") {
172  config_filename = m_config_filename;
173  }
174 
175  // resolve filename
176  std::string resolved_config_filename = rad::Helper::FindFile(config_filename);
177  if (resolved_config_filename.size() == 0) {
178  LOG4CPLUS_ERROR(GetLogger(), "Cannot find <" << config_filename << ">");
179  throw rad::Exception("Cannot find " + config_filename, resolved_config_filename);
180  } else {
181  LOG4CPLUS_DEBUG(GetLogger(),
182  "Loading configuration from <" << resolved_config_filename << ">");
183  }
184  try {
186  auto ifs = std::ifstream(resolved_config_filename);
187  ifs.exceptions(std::ios_base::badbit);
188 
189  m_config->UpdateFromStream(ifs);
190  auto const &root = m_config->GetInstanceRootNode();
191  daq::config::OriginInfo si{daq::config::Origin::Configuration, resolved_config_filename};
192 
193  if (auto value = GetParamAs<std::string>(rad::KEY_CONFIG_LOG_PROPERTIES, root); value) {
194  m_mgr.Update(&m_log_properties, *value, si);
195  }
196 
197  if (auto value = GetParamAs<std::string>(rad::KEY_CONFIG_REQ_ENDPOINT, root); value) {
198  auto updated = m_mgr.Update(&m_req_endpoint, *value, si);
199  if (updated && m_req_endpoint.empty()) {
200  throw rad::Exception("Invalid configuration",
201  fmt::format("invalid configuration for '{}': '{}' request "
202  "endpoint must be non-empty and have a trailing "
203  "path separator '/'.",
204  rad::KEY_CONFIG_REQ_ENDPOINT,
205  m_req_endpoint));
206  }
207  }
208  if (auto value = GetParamAs<std::string>(KEY_CONFIG_PUB_ENDPOINT, root); value) {
209  auto updated = m_mgr.Update(&m_pub_endpoint, *value, si);
210  if (updated && m_pub_endpoint.empty()) {
211  throw rad::Exception("Invalid configuration",
212  fmt::format("invalid configuration for '{}': '{}' publish "
213  "endpoint must be non-empty and have a trailing "
214  "path separator '/'.",
216  m_pub_endpoint));
217  }
218  }
219 
220  if (auto value = GetParamAs<std::string>(rad::KEY_CONFIG_OLDB_URI_PREFIX, root); value) {
221  m_mgr.Update(&m_db_prefix, *value, si);
222  }
223 
224  if (auto value = GetParamAs<int>(rad::KEY_CONFIG_OLDB_CONN_TIMEOUT, root); value) {
225  m_mgr.Update(&m_db_timeout_sec, *value, si);
226  }
227 
228  if (auto value = GetParamAs<std::string>(rad::KEY_CONFIG_SM_SCXML, root); value) {
229  m_mgr.Update(&m_scxml_filename, *value, si);
230  }
231 
232  if (auto value = GetParamAs<std::string>(KEY_CONFIG_INSTRUMENT_ID, root); value) {
233  m_mgr.Update(&m_instrument_id, *value, si);
234  } else {
235  LOG4CPLUS_ERROR(
236  GetLogger(),
237  "Required configuration parameter <" << KEY_CONFIG_INSTRUMENT_ID << "> missing");
238  throw rad::Exception(
239  "Invalid configuration: Required parameter empty: " + KEY_CONFIG_INSTRUMENT_ID,
240  filename);
241  }
242  if (auto value = GetParamAs<std::string>(KEY_CONFIG_DATAROOT, root); value) {
243  m_mgr.Update(&m_out_path, *value, si);
244  }
245  // Workspace
246  if (auto value = GetParamAs<std::string>(KEY_CONFIG_WORKSPACE, root); value) {
247  m_mgr.Update(&m_workspace, *value, si);
248  }
249  LOG4CPLUS_DEBUG(GetLogger(), "Resolved workspace = <" << GetWorkspace() << ">");
250  // Stale daqs from workspace
251  if (auto value = GetParamAs<int>(KEY_CONFIG_STALE_DAQ_ACQUIRING, root); value) {
252  m_mgr.Update(&m_stale_acquiring, std::chrono::hours(*value), si);
253  }
254  if (auto value = GetParamAs<int>(KEY_CONFIG_STALE_DAQ_MERGING, root)) {
255  m_mgr.Update(&m_stale_merging, std::chrono::hours(*value), si);
256  }
257  // DPM
258  if (auto value = GetParamAs<std::string>(KEY_CONFIG_DPM_REQ_ENDPOINT, root); value) {
259  m_mgr.Update(&m_dpm_params.rr_uri, *value, si);
260  } else {
261  LOG4CPLUS_ERROR(
262  GetLogger(),
263  "Required configuration parameter <" << KEY_CONFIG_DPM_REQ_ENDPOINT << "> missing");
264  throw rad::Exception(
265  "Invalid configuration: Required parameter empty: " + KEY_CONFIG_DPM_REQ_ENDPOINT,
266  filename);
267  }
268  if (auto value = GetParamAs<std::string>(KEY_CONFIG_DPM_PUB_ENDPOINT, root); value) {
269  m_mgr.Update(&m_dpm_params.ps_uri, *value, si);
270  } else {
271  LOG4CPLUS_ERROR(
272  GetLogger(),
273  "Required configuration parameter <" << KEY_CONFIG_DPM_PUB_ENDPOINT << "> missing");
274  throw rad::Exception(
275  "Invalid configuration: Required parameter empty: " + KEY_CONFIG_DPM_PUB_ENDPOINT,
276  filename);
277  }
278  if (auto value = GetParamAs<int>(KEY_CONFIG_DPM_TIMEOUT_SEC, root); value) {
279  m_mgr.Update(&m_dpm_params.timeout, std::chrono::seconds(*value), si);
280  }
281  } catch (std::exception const &e) {
282  LOG4CPLUS_ERROR(GetLogger(), e.what());
283  throw rad::Exception("Failed to load configuration", filename);
284  }
285 
286  LOG4CPLUS_DEBUG(GetLogger(), "Loaded configuration file <" << m_config_filename << ">");
287 }
288 
289 const std::string &Config::GetMsgReplierEndpoint() const {
290  RAD_TRACE(GetLogger());
291  return m_req_endpoint;
292 }
293 
294 const std::string &Config::GetPubEndpoint() const {
295  RAD_TRACE(GetLogger());
296  return m_pub_endpoint;
297 }
298 
299 const std::string &Config::GetDbPrefix() const {
300  RAD_TRACE(GetLogger());
301  if (m_db_prefix.empty()) {
302  return m_proc_name;
303  } else {
304  return m_db_prefix;
305  }
306 }
307 
308 std::chrono::seconds Config::GetDbTimeout() const {
309  return std::chrono::seconds(m_db_timeout_sec);
310 }
311 
312 const std::string &Config::GetSmScxmlFilename() const {
313  RAD_TRACE(GetLogger());
314  return m_scxml_filename;
315 }
316 
317 const std::string &Config::GetConfigFilename() const {
318  RAD_TRACE(GetLogger());
319  return m_config_filename;
320 }
321 
322 const std::string &Config::GetProcName() const {
323  RAD_TRACE(GetLogger());
324  return m_proc_name;
325 }
326 
327 const std::string &Config::GetLogLevel() const {
328  RAD_TRACE(GetLogger());
329  return m_log_level;
330 }
331 
332 const std::string &Config::GetLogProperties() const {
333  RAD_TRACE(GetLogger());
334  return m_log_properties;
335 }
336 
338  return m_dpm_params;
339 }
340 
341 } // namespace server
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
void Register(AttrType *ptr, Metadata const &meta)
Registers a configuration parameter/attribute.
Definition: manager.hpp:238
const std::string & GetLogLevel() const
Definition: config.cpp:327
std::filesystem::path GetWorkspace() const
Definition: config.hpp:145
std::string m_pub_endpoint
Definition: config.hpp:170
daq::DpmClientParams m_dpm_params
Definition: config.hpp:173
std::string m_out_path
Definition: config.hpp:171
const std::string & GetSmScxmlFilename() const
Definition: config.cpp:312
std::string m_log_properties
Definition: config.hpp:164
bool ParseOptions(int argc, char *argv[])
Disable assignment operator.
Definition: config.cpp:90
Config()
Default constructor.
Definition: config.cpp:39
daq::DpmClientParams const & GetDpmClientParams() const
Definition: config.cpp:337
const std::string & GetMsgReplierEndpoint() const
Definition: config.cpp:289
const std::string & GetConfigFilename() const
Definition: config.cpp:317
int m_db_timeout_sec
Definition: config.hpp:168
void LoadConfig(const std::string &filename="")
This method load from a configuration file the application configuration overriding the initializatio...
Definition: config.cpp:166
std::chrono::seconds GetDbTimeout() const
Definition: config.cpp:308
std::string m_proc_name
Definition: config.hpp:161
std::optional< elt::configng::CiiConfigDocument > m_config
Definition: config.hpp:159
std::chrono::hours m_stale_merging
Definition: config.hpp:175
std::string m_log_level
Definition: config.hpp:163
std::chrono::hours m_stale_acquiring
Definition: config.hpp:174
std::string m_instrument_id
Definition: config.hpp:162
const std::string & GetLogProperties() const
Definition: config.cpp:332
std::string m_req_endpoint
Definition: config.hpp:169
std::string m_config_filename
Definition: config.hpp:165
daq::config::Manager m_mgr
Definition: config.hpp:156
std::string m_db_prefix
Definition: config.hpp:167
std::filesystem::path m_workspace
Definition: config.hpp:172
const std::string & GetProcName() const
Definition: config.cpp:322
const std::string & GetPubEndpoint() const
Definition: config.cpp:294
std::string m_scxml_filename
Definition: config.hpp:166
const std::string & GetDbPrefix() const
Definition: config.cpp:299
daq::config::Manager and associated types.
Default logger name.
@ Configuration
Configuration file.
@ Default
Built-in default value.
@ CommandLine
Command line argument.
@ EnvironmentVariable
Environment variable.
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
Mutable metadata about a configuration attribute that describes where a value comes from.
Definition: manager.hpp:116
std::chrono::seconds timeout
Definition: dpmClient.hpp:77
std::string rr_uri
Definition: dpmClient.hpp:75
std::string ps_uri
Definition: dpmClient.hpp:76
Connection parameters for DPM.
Definition: dpmClient.hpp:74
const std::string CONFIG_DEFAULT_WORKSPACE
Definition: config.hpp:42
const std::string KEY_CONFIG_PUB_ENDPOINT
Rad configuration keys.
Definition: config.hpp:26
const std::string KEY_CONFIG_DPM_TIMEOUT_SEC
Definition: config.hpp:35
const std::string KEY_CONFIG_STALE_DAQ_ACQUIRING
Definition: config.hpp:30
const std::string CONFIG_DEFAULT_SCXML_FILENAME
Definition: config.hpp:43
const std::string KEY_CONFIG_DATAROOT
Definition: config.hpp:28
const std::string CONFIG_DEFAULT_REQ_ENDPOINT
Definition: config.hpp:46
const std::string CONFIG_DEFAULT_OLDB_URI_PREFIX
Definition: config.hpp:48
const std::string KEY_CONFIG_DPM_PUB_ENDPOINT
Definition: config.hpp:34
const std::string KEY_CONFIG_WORKSPACE
Definition: config.hpp:29
const int CONFIG_DEFAULT_DB_TIMEOUT_SEC
Definition: config.hpp:45
const std::string CONFIG_DEFAULT_PUB_ENDPOINT
Definition: config.hpp:47
const std::string CONFIG_DEFAULT_LOG_LEVEL
Definition: config.hpp:44
const std::string KEY_CONFIG_INSTRUMENT_ID
Definition: config.hpp:27
const std::string CONFIG_ENVVAR_OUT_PATH
Application configuration environment variables.
Definition: config.hpp:54
const std::string CONFIG_DEFAULT_PROCNAME
Default application configuration values.
Definition: config.hpp:40
const std::string CONFIG_DEFAULT_FILENAME
Definition: config.hpp:41
const std::string KEY_CONFIG_STALE_DAQ_MERGING
Definition: config.hpp:31
const std::string KEY_CONFIG_DPM_REQ_ENDPOINT
Definition: config.hpp:33
log4cplus::Logger & GetLogger()
Definition: logger.cpp:14
Config class header file.
static auto Format(std::filesystem::path const &t)
Definition: config.cpp:32
Extension point to allow adaptations from non-formattable configurations types.
Definition: manager.hpp:79