API
rhusbMon.hpp
Go to the documentation of this file.
1 /** \file rhusbMon.hpp
2  * \brief The MagAO-X RH USB monitor
3  *
4  * \ingroup rhusbMon_files
5  */
6 
7 #ifndef rhusbMon_hpp
8 #define rhusbMon_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 "rhusbMonParsers.hpp"
15 
16 /** \defgroup rhusbMon
17  * \brief Application to monitor an Omega RH USB probe.
18  *
19  * <a href="../handbook/operating/software/apps/rhusbMon.html">Application Documentation</a>
20  *
21  * \ingroup apps
22  *
23  */
24 
25 /** \defgroup rhusbMon_files
26  * \ingroup rhusbMon
27  */
28 
29 namespace MagAOX
30 {
31 namespace app
32 {
33 
34 /// The MagAO-X RH-USB monitoring class
35 /** Interacts with the Omega RH-USB probe used for DM chamber humidity monitoring.
36  *
37  * \todo need a test mode (compile-time) which adds a way (INDI?) to initiate testing of parameter limits.
38  *
39  * \ingroup rhusbMon
40  */
41 class rhusbMon : public MagAOXApp<true>, public tty::usbDevice, public dev::ioDevice, public dev::telemeter<rhusbMon>
42 {
43 
44  //Give the test harness access.
45  friend class rhusbMon_test;
46 
47  //Let telemeter work.
48  friend class dev::telemeter<rhusbMon>;
49 
50 protected:
51 
52  /** \name Configurable Parameters
53  *@{
54  */
55  float m_warnTemp {30}; ///< This is abnormally high if the system is working, but still safe.
56  float m_alertTemp {35}; ///< This is the actual limit, shut down should occur.
57  float m_emergTemp {40}; ///< Must shutdown immediately.
58 
59  float m_warnHumid {18}; ///< This is abnormally high if the system is working, but still safe.
60  float m_alertHumid {20}; ///< This is the actual limit, shut down should occur.
61  float m_emergHumid {22}; ///< Must shutdown immediately.
62 
63  ///@}
64 
65  float m_temp {-999};
66  float m_rh {-999};
67 
68  pcf::IndiProperty m_indiP_temp;
69  pcf::IndiProperty m_indiP_rh;
70 
71 
72 public:
73  /// Default c'tor.
74  rhusbMon();
75 
76  /// D'tor, declared and defined for noexcept.
77  ~rhusbMon() noexcept
78  {}
79 
80  virtual void setupConfig();
81 
82  /// Implementation of loadConfig logic, separated for testing.
83  /** This is called by loadConfig().
84  */
85  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
86 
87  virtual void loadConfig();
88 
89  /// Startup function
90  /**
91  *
92  */
93  virtual int appStartup();
94 
95  /// Implementation of the FSM for rhusbMon.
96  /**
97  * \returns 0 on no critical error
98  * \returns -1 on an error requiring shutdown
99  */
100  virtual int appLogic();
101 
102  /// Shutdown the app.
103  /**
104  *
105  */
106  virtual int appShutdown();
107 
108  /// Connect to the probe
109  /** Search for the USB device in udev and attempt ot open it.
110  * The result is reported via the FSM state (NODEVICE, NOTCONNECTED, CONNECTED).
111  *
112  * \returns -1 on an error attempting to read udev
113  * \returns 0 if device not found, or connection does not work, or if connected.
114  */
115  int connect();
116 
117  /// Read current values from the RH-USB probe
118  /** Issues the 'C' and 'H' commands to get temperature and humidity.
119  *
120  * \returns -1 on error writing or reading, or on a parsing error
121  *
122  * \see \parseC( float &, const std::string)
123  * \see \parseH( float &, const std::string)
124  */
125  int readProbe();
126 
127  /** \name Telemeter Interface
128  *
129  * @{
130  */
131  int checkRecordTimes();
132 
133  int recordTelem( const telem_rhusb * );
134 
135 protected:
136 
137  int recordRH( bool force = false );
138 
139  ///@}
140 
141 };
142 
143 rhusbMon::rhusbMon() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
144 {
147 
148  return;
149 }
150 
152 {
153  config.add("temp.warning", "", "temp.warning", argType::Required, "temp", "warning", false, "float", "Temperature at which to issue a warning. Default is 30.");
154  config.add("temp.alert", "", "temp.alert", argType::Required, "temp", "alert", false, "float", "Temperature at which to issue an alert. Default is 35.");
155  config.add("temp.emergency", "", "temp.emergency", argType::Required, "temp", "emergency", false, "float", "Temperature at which to issue an emergency. Default is 40.");
156 
157  config.add("humid.warning", "", "humid.warning", argType::Required, "humid", "warning", false, "float", "Humidity at which to issue a warning. Default is 18.");
158  config.add("humid.alert", "", "humid.alert", argType::Required, "humid", "alert", false, "float", "Humidity at which to issue an alert. Default is 20.");
159  config.add("humid.emergency", "", "humid.emergency", argType::Required, "humid", "emergency", false, "float", "Humidity at which to issue an emergency. Default is 22.");
160 
163 
165 }
166 
167 int rhusbMon::loadConfigImpl( mx::app::appConfigurator & _config )
168 {
169 
170  _config(m_warnTemp, "temp.warning");
171  _config(m_alertTemp, "temp.alert");
172  _config(m_emergTemp, "temp.emergency");
173 
174  _config(m_warnHumid, "humid.warning");
175  _config(m_alertHumid, "humid.alert");
176  _config(m_emergHumid, "humid.emergency");
177 
179  dev::ioDevice::loadConfig(_config);
180 
182 
183  return 0;
184 }
185 
187 {
188  loadConfigImpl(config);
189 }
190 
192 {
193  createROIndiNumber( m_indiP_temp, "temperature", "Temperature [C]");
194  indi::addNumberElement<float>( m_indiP_temp, "current", -20., 120., 0, "%0.1f");
195  m_indiP_temp["current"] = -999;
197 
198  createROIndiNumber( m_indiP_rh, "humidity", "Relative Humidity [%]");
199  indi::addNumberElement<float>( m_indiP_rh, "current", 0., 100., 0, "%0.1f");
200  m_indiP_rh["current"] = -999;
202 
203 
205  {
206  return log<software_error,-1>({__FILE__,__LINE__});
207  }
208 
209  return connect();
210 }
211 
212 
213 
215 {
217  {
218  int rv = connect();
219  if(rv < 0) return log<software_error,-1>({__FILE__, __LINE__});
220  }
221 
223  {
224  int rv = readProbe();
225  if(rv == 0)
226  {
228  }
229  else
230  {
232  return log<software_error,0>({__FILE__, __LINE__});
233  }
234  }
235 
236  pcf::IndiProperty::PropertyStateType rhState = pcf::IndiProperty::Ok;
237  //Check warning and alert values
238  if(m_rh > m_emergHumid)
239  {
240  log<text_log>("RH > " + std::to_string(m_emergHumid) + "% : " + std::to_string(m_rh) + "%! Shutdown immediately!", logPrio::LOG_EMERGENCY);
241  rhState = pcf::IndiProperty::Alert;
242  }
243  else if(m_rh > m_alertHumid)
244  {
245  log<text_log>("RH > " + std::to_string(m_alertHumid) + "% : " + std::to_string(m_rh) + "%. Fix or shutdown.", logPrio::LOG_ALERT);
246  rhState = pcf::IndiProperty::Alert;
247  }
248  else if(m_rh > m_warnHumid)
249  {
250  log<text_log>("RH > " + std::to_string(m_warnHumid) + "% : " + std::to_string(m_rh) + "%.", logPrio::LOG_WARNING);
251  rhState = pcf::IndiProperty::Alert;
252  }
253 
254  pcf::IndiProperty::PropertyStateType tState = pcf::IndiProperty::Ok;
255  //Check warning and alert values
256  if(m_temp > m_emergTemp)
257  {
258  log<text_log>("Temp > " + std::to_string(m_emergTemp) + "C : " + std::to_string(m_temp) + "C! Shutdown immediately!", logPrio::LOG_EMERGENCY);
259  tState = pcf::IndiProperty::Alert;
260  }
261  else if(m_temp > m_alertTemp)
262  {
263  log<text_log>("Temp > " + std::to_string(m_alertTemp) + "C : " + std::to_string(m_temp) + "C. Fix or shutdown.", logPrio::LOG_ALERT);
264  tState = pcf::IndiProperty::Alert;
265  }
266  else if(m_temp > m_warnTemp)
267  {
268  log<text_log>("Temp > " + std::to_string(m_warnTemp) + "C : " + std::to_string(m_temp) + "C.", logPrio::LOG_WARNING);
269  tState = pcf::IndiProperty::Alert;
270  }
271 
272  //Scope for mutex
273  {
274  std::unique_lock<std::mutex> lock(m_indiMutex);
275  updateIfChanged(m_indiP_temp, "current", m_temp, tState);
276 
277  m_indiP_rh["current"].set<float>(-999); //Force the update to get a new timestamp
278  updateIfChanged(m_indiP_rh, "current", m_rh, rhState); ///\todo updateIfChanged should have a force flag
279  }
280 
282  {
283  return log<software_error,0>({__FILE__, __LINE__});
284  }
285 
286  return 0;
287 }
288 
290 {
291  return 0;
292 }
293 
295 {
297  if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
298  {
299  //There is no device reason for this to error. Something is wrong.
301  return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
302  }
303 
304  if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
305  {
307 
308  if(!stateLogged())
309  {
310  std::stringstream logs;
311  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
312  log<text_log>(logs.str());
313  }
314  return 0;
315  }
316  else
317  {
319  if(!stateLogged())
320  {
321  std::stringstream logs;
322  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
323  log<text_log>(logs.str());
324  }
325 
326  //scope for elevated priv
327  {
328  elevatedPrivileges elPriv(this);
330  }
331 
332  if(rv == TTY_E_NOERROR)
333  {
335  if(!stateLogged())
336  {
337  std::stringstream logs;
338  logs << "Connected to " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " @ " << m_deviceName;
339  log<text_log>(logs.str());
340  }
341  }
342  else
343  {
344  //There is no power or other reason this should happen. It means something is wrong and needs to be corrected.
346  return log<software_critical, -1>({__FILE__,__LINE__, errno, rv, "Error opening connection: " + tty::ttyErrorString(rv)});
347  }
348  }
349 
350  return 0;
351 }
352 
354 {
355  std::string strRead;
356 
358  if(rv != TTY_E_NOERROR)
359  {
360  return log<software_error,-1>({__FILE__, __LINE__, 0, rv, "Error reading temp: " + tty::ttyErrorString(rv)});
361  }
362 
363  rv = RH::parseC(m_temp, strRead);
364  if(rv != 0)
365  {
366  if( rv == -1 )
367  {
368  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, no EOT"});
369  }
370  else if (rv == -2 )
371  {
372  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, no value"});
373  }
374  else if (rv == -3)
375  {
376  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, does not begin with digit"});
377  }
378  else
379  {
380  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp."});
381  }
382  }
383 
384  std::cout << m_temp << "\n";
385 
387  if(rv != TTY_E_NOERROR)
388  {
389  return log<software_error,-1>({__FILE__, __LINE__, 0, rv, "Error reading RH: " + tty::ttyErrorString(rv)});
390  }
391 
392  rv = RH::parseH(m_rh, strRead);
393  if(rv != 0)
394  {
395  if( rv == -1 )
396  {
397  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing humid, no EOT"});
398  }
399  else if (rv == -2 )
400  {
401  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing humid, no value"});
402  }
403  else if (rv == -3)
404  {
405  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, does not begin with digit"});
406  }
407  else
408  {
409  return log<software_error, -1>({__FILE__, __LINE__, "Error parsing humid."});
410  }
411  }
412 
413  std::cout << m_rh << "\n";
414 
415  return 0;
416 }
417 
419 {
421 }
422 
424 {
425  return recordRH(true);
426 }
427 
428 inline
429 int rhusbMon::recordRH(bool force)
430 {
431  static float lastTemp = -99;
432  static float lastRH = -99;
433 
434  if(force || m_temp != lastTemp || m_rh != lastRH)
435  {
436  telem<telem_rhusb>({m_temp, m_rh});
437  }
438 
439  lastTemp = m_temp;
440  lastRH = m_rh;
441 
442  return 0;
443 
444 }
445 
446 } //namespace app
447 } //namespace MagAOX
448 
449 #endif //rhusbMon_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
Definition: MagAOXApp.hpp:2877
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2140
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
Definition: MagAOXApp.hpp:2294
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2437
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
The MagAO-X RH-USB monitoring class.
Definition: rhusbMon.hpp:42
float m_warnTemp
This is abnormally high if the system is working, but still safe.
Definition: rhusbMon.hpp:55
virtual int appLogic()
Implementation of the FSM for rhusbMon.
Definition: rhusbMon.hpp:214
rhusbMon()
Default c'tor.
Definition: rhusbMon.hpp:143
int recordRH(bool force=false)
Definition: rhusbMon.hpp:429
float m_emergTemp
Must shutdown immediately.
Definition: rhusbMon.hpp:57
virtual int appStartup()
Startup function.
Definition: rhusbMon.hpp:191
virtual void loadConfig()
Definition: rhusbMon.hpp:186
virtual int appShutdown()
Shutdown the app.
Definition: rhusbMon.hpp:289
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: rhusbMon.hpp:167
int recordTelem(const telem_rhusb *)
Definition: rhusbMon.hpp:423
int readProbe()
Read current values from the RH-USB probe.
Definition: rhusbMon.hpp:353
friend class rhusbMon_test
Definition: rhusbMon.hpp:45
float m_alertTemp
This is the actual limit, shut down should occur.
Definition: rhusbMon.hpp:56
pcf::IndiProperty m_indiP_temp
Definition: rhusbMon.hpp:68
pcf::IndiProperty m_indiP_rh
Definition: rhusbMon.hpp:69
int connect()
Connect to the probe.
Definition: rhusbMon.hpp:294
float m_warnHumid
This is abnormally high if the system is working, but still safe.
Definition: rhusbMon.hpp:59
virtual void setupConfig()
Definition: rhusbMon.hpp:151
~rhusbMon() noexcept
D'tor, declared and defined for noexcept.
Definition: rhusbMon.hpp:77
float m_emergHumid
Must shutdown immediately.
Definition: rhusbMon.hpp:61
float m_alertHumid
This is the actual limit, shut down should occur.
Definition: rhusbMon.hpp:60
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
@ NODEVICE
No device exists for the application to control.
Definition: stateCodes.hpp:41
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
Definition: stateCodes.hpp:37
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
Definition: stateCodes.hpp:38
@ CONNECTED
The application has connected to the device or service.
Definition: stateCodes.hpp:45
@ NOTCONNECTED
The application is not connected to the device or service.
Definition: stateCodes.hpp:44
int ttyWriteRead(std::string &strRead, const std::string &strWrite, const std::string &eot, bool swallowEcho, int fd, int timeoutWrite, int timeoutRead)
Write to a tty on an open file descriptor, then get the result.
Definition: ttyIOUtils.cpp:332
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
Definition: ttyErrors.cpp:15
std::ostream & cout()
int parseH(float &humid, const std::string &str)
Parse the RH probe H humidity command.
int parseC(float &temp, const std::string &str)
Parse the RH probe C temp command.
Definition: dm.hpp:24
constexpr static logPrioT LOG_ALERT
This should only be used if some action is required by operators to keep the system safe.
Definition: logPriority.hpp:34
constexpr static logPrioT LOG_EMERGENCY
Normal operations of the entire system should be shut down immediately.
Definition: logPriority.hpp:31
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
Parsers for the MagAO-X RH USB monitor.
An input/output capable device.
Definition: ioDevice.hpp:27
unsigned m_writeTimeout
The write timeout [msec].
Definition: ioDevice.hpp:29
int loadConfig(mx::app::appConfigurator &config)
Load the device section from an application configurator.
Definition: ioDevice.cpp:28
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the device section.
Definition: ioDevice.cpp:20
unsigned m_readTimeout
The read timeout [msec].
Definition: ioDevice.hpp:28
A device which saves telemetry.
Definition: telemeter.hpp:52
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:208
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:253
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:195
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:266
Software CRITICAL log entry.
Software ERR log entry.
Log entry recording the build-time git state.
Definition: telem_rhusb.hpp:26
A USB device as a TTY device.
Definition: usbDevice.hpp:33
std::string m_deviceName
The device path name, e.g. /dev/ttyUSB0.
Definition: usbDevice.hpp:40
int m_fileDescrip
The file descriptor.
Definition: usbDevice.hpp:42
int connect()
Connect to the device.
Definition: usbDevice.cpp:108
int getDeviceName()
Get the device name from udev using the vendor, product, and serial number.
Definition: usbDevice.cpp:103
std::string m_idProduct
The product id 4-digit code.
Definition: usbDevice.hpp:35
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the USB section.
Definition: usbDevice.cpp:24
std::string m_serial
The serial number.
Definition: usbDevice.hpp:36
int loadConfig(mx::app::appConfigurator &config)
Load the USB section from an application configurator.
Definition: usbDevice.cpp:34
std::string m_idVendor
The vendor id 4-digit code.
Definition: usbDevice.hpp:34
#define TTY_E_NODEVNAMES
Definition: ttyErrors.hpp:28
#define TTY_E_DEVNOTFOUND
Definition: ttyErrors.hpp:30
#define TTY_E_NOERROR
Definition: ttyErrors.hpp:15