API
stateRuleEngine.hpp
Go to the documentation of this file.
1 /** \file stateRuleEngine.hpp
2  * \brief The MagAO-X stateRuleEngine application header file
3  *
4  * \ingroup stateRuleEngine_files
5  */
6 
7 #ifndef stateRuleEngine_hpp
8 #define stateRuleEngine_hpp
9 
10 
11 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
12 #include "../../magaox_git_version.h"
13 
14 #include "indiCompRuleConfig.hpp"
15 
16 /** \defgroup stateRuleEngine
17  * \brief The MagAO-X stateRuleEngine application
18  *
19  * <a href="../handbook/operating/software/apps/stateRuleEngine.html">Application Documentation</a>
20  *
21  * \ingroup apps
22  *
23  */
24 
25 /** \defgroup stateRuleEngine_files
26  * \ingroup stateRuleEngine
27  */
28 
29 namespace MagAOX
30 {
31 namespace app
32 {
33 
34 /// The MagAO-X stateRuleEngine
35 /**
36  * \ingroup stateRuleEngine
37  */
38 class stateRuleEngine : public MagAOXApp<true>
39 {
40 
41  //Give the test harness access.
42  friend class stateRuleEngine_test;
43 
44 protected:
45 
46  /** \name Configurable Parameters
47  *@{
48  */
49 
50  std::string m_ruleDir; /**< Directory containing config files containing rules to load. Relative to config directory. If this is
51  set, then rules in the device config file are ignored*/
52 
54 
55  ///@}
56 
57 public:
58  /// Default c'tor.
60 
61  /// D'tor, declared and defined for noexcept.
62  ~stateRuleEngine() noexcept
63  {}
64 
65  virtual void setupConfig();
66 
67  /// Implementation of loadConfig logic, separated for testing.
68  /** This is called by loadConfig().
69  */
70  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
71 
72  virtual void loadConfig();
73 
74  /// Startup function
75  /**
76  *
77  */
78  virtual int appStartup();
79 
80  /// Implementation of the FSM for stateRuleEngine.
81  /**
82  * \returns 0 on no critical error
83  * \returns -1 on an error requiring shutdown
84  */
85  virtual int appLogic();
86 
87  /// Shutdown the app.
88  /**
89  *
90  */
91  virtual int appShutdown();
92 
93 
94  /// The static callback function to be registered for rule properties
95  /**
96  *
97  * \returns 0 on success.
98  * \returns -1 on error.
99  */
100  static int st_newCallBack_ruleProp( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
101  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
102  );
103 
104  /// Callback to process a NEW preset position request
105  /**
106  * \returns 0 on success.
107  * \returns -1 on error.
108  */
109  int newCallBack_ruleProp( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
110 
111 
112  pcf::IndiProperty m_indiP_info;
113  pcf::IndiProperty m_indiP_caution;
114  pcf::IndiProperty m_indiP_warning;
115  pcf::IndiProperty m_indiP_alert;
116 
117 };
118 
119 stateRuleEngine::stateRuleEngine() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
120 {
121  return;
122 }
123 
125 {
126  config.add( "rules.dir",
127  "",
128  "rules.dir",
129  argType::Required,
130  "rules",
131  "dir",
132  false,
133  "string",
134  "Directory containing config files containing rules to load. Relative to config directory. If this is "
135  "set, then rules in the device config file are ignored" );
136 }
137 
138 int stateRuleEngine::loadConfigImpl( mx::app::appConfigurator & _config )
139 {
140  _config(m_ruleDir, "rules.dir");
141 
142  std::map<std::string, ruleRuleKeys> rrkMap;
143 
144  if(m_ruleDir == "")
145  {
146  try
147  {
148  loadRuleConfig(m_ruleMaps, rrkMap, _config);
150  }
151  catch(mx::err::mxException & e)
152  {
153  return log<software_critical,-1>({__FILE__,__LINE__, std::string("Rule config exception caught:\n") + e.what()});
154  }
155  }
156  else
157  {
158  std::vector<std::string> conffiles = mx::ioutils::getFileNames(m_configDir + "/" + m_ruleDir, "", "", ".conf");
159 
160  for(auto & cnf : conffiles)
161  {
162  //Create a configurator and set it up to log
163  mx::app::appConfigurator fcfg;
164 
165  fcfg.m_sources = true;
166  fcfg.configLog = configLog;
167 
168  //now process the config file
169  if( fcfg.readConfig(cnf) < 0 )
170  {
171  return log<software_critical,-1>({__FILE__,__LINE__, "error reading rule config file: " + cnf});
172  }
173 
174  try
175  {
176  //and finally add to our rule map
177  loadRuleConfig(m_ruleMaps, rrkMap, fcfg);
178  }
179  catch(mx::err::mxException & e)
180  {
181  return log<software_critical,-1>({__FILE__,__LINE__, std::string("Rule config exception caught from ") + cnf + ":\n" + e.what()});
182  }
183  }
184 
185  try
186  {
188  }
189  catch(const std::exception& e)
190  {
191  return log<software_critical,-1>({__FILE__,__LINE__, std::string("Error finalizing rules:\n") + e.what()});
192  }
193 
194  }
195 
196  return 0;
197 }
198 
200 {
201  if(loadConfigImpl(config) < 0)
202  {
203  log<software_critical>({__FILE__,__LINE__,"error in configuration"});
204  m_shutdown = true;
205  }
206 }
207 
209 {
210  for(auto it = m_ruleMaps.rules.begin(); it != m_ruleMaps.rules.end(); ++it)
211  {
212  if(it->second->priority() == rulePriority::info)
213  {
214  if(m_indiP_info.getDevice() != m_configName)
215  {
216  if(registerIndiPropertyNew( m_indiP_info, "info", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
217  pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
218  {
219  return log<software_critical,-1>({__FILE__, __LINE__});
220  }
221  }
222 
223  pcf::IndiElement elem = pcf::IndiElement(it->first, pcf::IndiElement::Off);
224  m_indiP_info.add(elem);
225  }
226 
227  if(it->second->priority() == rulePriority::caution)
228  {
229  if(m_indiP_caution.getDevice() != m_configName)
230  {
231  if(registerIndiPropertyNew( m_indiP_caution, "caution", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
232  pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
233  {
234  return log<software_critical,-1>({__FILE__, __LINE__});
235  }
236  }
237 
238  pcf::IndiElement elem = pcf::IndiElement(it->first, pcf::IndiElement::Off);
239  m_indiP_caution.add(elem);
240  }
241 
242  if(it->second->priority() == rulePriority::warning)
243  {
244  if(m_indiP_warning.getDevice() != m_configName)
245  {
246  if(registerIndiPropertyNew( m_indiP_warning, "warning", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
247  pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
248  {
249  return log<software_critical,-1>({__FILE__, __LINE__});
250  }
251  }
252 
253  pcf::IndiElement elem = pcf::IndiElement(it->first, pcf::IndiElement::Off);
254  m_indiP_warning.add(elem);
255  }
256 
257  if(it->second->priority() == rulePriority::alert)
258  {
259  if(m_indiP_alert.getDevice() != m_configName)
260  {
261  if(registerIndiPropertyNew( m_indiP_alert, "alert", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
262  pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
263  {
264  return log<software_critical,-1>({__FILE__, __LINE__});
265  }
266  }
267 
268  pcf::IndiElement elem = pcf::IndiElement(it->first, pcf::IndiElement::Off);
269  m_indiP_alert.add(elem);
270  }
271  }
272 
273  for(auto it = m_ruleMaps.props.begin(); it != m_ruleMaps.props.end(); ++it)
274  {
275  if(it->second == nullptr) continue;
276 
277  std::string devName, propName;
278 
279  int rv = indi::parseIndiKey(devName, propName, it->first);
280  if(rv != 0)
281  {
282  log<software_error>({__FILE__, __LINE__, 0, rv, "error parsing INDI key: " + it->first});
283  return -1;
284  }
285 
286  registerIndiPropertySet( *it->second, devName, propName, st_newCallBack_ruleProp);
287  }
288 
290 
291  return 0;
292 }
293 
295 {
296  for(auto it = m_ruleMaps.rules.begin(); it != m_ruleMaps.rules.end(); ++it)
297  {
298  #if 0
299  try
300  {
301  bool val = it->second->value();
302  std::cerr << it->first << " " << val << "\n";
303  }
304  catch(...){}
305  #endif
306 
307  if(it->second->priority() != rulePriority::none)
308  {
309  try
310  {
311  bool val = it->second->value();
312 
313  pcf::IndiElement::SwitchStateType onoff = pcf::IndiElement::Off;
314  if(val) onoff = pcf::IndiElement::On;
315 
316  if(it->second->priority() == rulePriority::info)
317  {
318  updateSwitchIfChanged(m_indiP_info, it->first, onoff);
319  }
320  else if(it->second->priority() == rulePriority::caution)
321  {
322  updateSwitchIfChanged(m_indiP_caution, it->first, onoff);
323  }
324  else if(it->second->priority() == rulePriority::warning)
325  {
326  updateSwitchIfChanged(m_indiP_warning, it->first, onoff);
327  }
328  else
329  {
330  updateSwitchIfChanged(m_indiP_alert, it->first, onoff);
331  }
332 
333  }
334  catch(const std::exception & e)
335  {
336  ///\todo how to handle startup vs misconfiguration
337 
338  /*
339  if(it->second->priority() == rulePriority::none)
340  {
341  updateSwitchIfChanged(m_indiP_info, it->first, pcf::IndiElement::Off);
342  }*/
343  }
344  }
345  }
346 
347 
348  return 0;
349 }
350 
352 {
353  return 0;
354 }
355 
357  const pcf::IndiProperty &ipRecv
358  )
359 {
360  stateRuleEngine * sre = static_cast<stateRuleEngine *>(app);
361 
363 
364  return 0;
365 }
366 
367 int stateRuleEngine::newCallBack_ruleProp( const pcf::IndiProperty &ipRecv)
368 {
369  std::string key = ipRecv.createUniqueKey();
370 
371  if(m_ruleMaps.props.count(key) == 0)
372  {
373  return 0;
374  }
375 
376  if(m_ruleMaps.props[key] == nullptr) //
377  {
378  return 0;
379  }
380 
381  *m_ruleMaps.props[key] = ipRecv;
382 
383  return 0;
384 }
385 
386 } //namespace app
387 } //namespace MagAOX
388 
389 #endif //stateRuleEngine_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
std::string m_configName
The name of the configuration file (minus .conf).
Definition: MagAOXApp.hpp:83
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
static void configLog(const std::string &name, const int &code, const std::string &value, const std::string &source)
Callback for config system logging.
Definition: MagAOXApp.hpp:1863
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:100
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
Definition: MagAOXApp.hpp:3144
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
std::string m_configDir
The path to configuration files for MagAOX.
Definition: MagAOXApp.hpp:85
int registerIndiPropertySet(pcf::IndiProperty &prop, const std::string &devName, const std::string &propName, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is monitored for updates from others.
Definition: MagAOXApp.hpp:2792
The MagAO-X stateRuleEngine.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
virtual int appStartup()
Startup function.
~stateRuleEngine() noexcept
D'tor, declared and defined for noexcept.
virtual int appShutdown()
Shutdown the app.
static int st_newCallBack_ruleProp(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for rule properties.
int newCallBack_ruleProp(const pcf::IndiProperty &ipRecv)
Callback to process a NEW preset position request.
virtual int appLogic()
Implementation of the FSM for stateRuleEngine.
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:56
Configuration of rules for the MagAO-X stateRuleEngine.
void loadRuleConfig(indiRuleMaps &maps, std::map< std::string, ruleRuleKeys > &rrkMap, mx::app::appConfigurator &config)
Load the rule and properties maps for a rule engine from a configuration file.
void finalizeRuleValRules(indiRuleMaps &maps, std::map< std::string, ruleRuleKeys > &rrkMap)
Finalize ruleVal rules.
@ none
Don't publish.
@ caution
Caution – make sure you know what you're doing.
@ warning
Warning – something is probably wrong, you should check.
@ alert
Alert – something is definitely wrong, you should take action.
@ info
For information only.
std::ostream & cerr()
int parseIndiKey(std::string &devName, std::string &propName, const std::string &key)
Parse an INDI key into the device and property names.
Definition: indiUtils.hpp:317
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
Definition: dm.hpp:24
Software CRITICAL log entry.
Structure to provide management of the rule and property maps.