API
usbtempMon.hpp
Go to the documentation of this file.
1 /** \file usbtempMon.hpp
2  * \brief The MagAO-X XXXXXX header file
3  *
4  * \ingroup usbtempMon_files
5  */
6 
7 #ifndef usbtempMon_hpp
8 #define usbtempMon_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 extern "C"
15 {
16 #include "usbtemp.h"
17 }
18 
19 /** \defgroup usbtempMon
20  * \brief The XXXXXX application to do YYYYYYY
21  *
22  * <a href="../handbook/operating/software/apps/XXXXXX.html">Application Documentation</a>
23  *
24  * \ingroup apps
25  *
26  */
27 
28 /** \defgroup usbtempMon_files
29  * \ingroup usbtempMon
30  */
31 
32 namespace MagAOX
33 {
34 namespace app
35 {
36 
37 /// The MagAO-X xxxxxxxx
38 /**
39  * \ingroup usbtempMon
40  */
41 class usbtempMon : public MagAOXApp<true>, public tty::usbDevice, public dev::telemeter<usbtempMon>
42 {
43 
44  //Give the test harness access.
45  friend class usbtempMon_test;
46 
47  friend class dev::telemeter<usbtempMon>;
48 
49 protected:
50 
51  /** \name Configurable Parameters
52  *@{
53  */
54 
55  //here add parameters which will be config-able at runtime
56 
57  float m_warnTemp {40};
58  float m_alertTemp {50};
59  float m_emergTemp {55};
60 
61  ///@}
62 
63 
64  struct probe
65  {
66  std::string m_location;
67  std::string m_serial;
68  int m_fd {0};
69  std::string m_devName;
70 
72 
73  bool operator<(const probe& p) const
74  {
75  return m_location < p.m_location;
76  }
77  };
78 
79  std::vector<probe> m_probes;
80 
81 
82  pcf::IndiProperty m_indiP_temps;
83 
84 public:
85  /// Default c'tor.
86  usbtempMon();
87 
88  /// D'tor, declared and defined for noexcept.
89  ~usbtempMon() noexcept
90  {}
91 
92  virtual void setupConfig();
93 
94  /// Implementation of loadConfig logic, separated for testing.
95  /** This is called by loadConfig().
96  */
97  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
98 
99  virtual void loadConfig();
100 
101  /// Startup function
102  /**
103  *
104  */
105  virtual int appStartup();
106 
107  /// Implementation of the FSM for usbtempMon.
108  /**
109  * \returns 0 on no critical error
110  * \returns -1 on an error requiring shutdown
111  */
112  virtual int appLogic();
113 
114  /// Shutdown the app.
115  /**
116  *
117  */
118  virtual int appShutdown();
119 
120 
121  int checkConnections();
122 
123  /** \name Telemeter Interface
124  *
125  * @{
126  */
127  int checkRecordTimes();
128 
129  int recordTelem( const telem_temps * );
130 
131 protected:
132  std::vector<float> m_lastTemps;
133 
134  int recordTemps( bool force = false );
135 
136 };
137 
138 usbtempMon::usbtempMon() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
139 {
140 
141  return;
142 }
143 
145 {
146  config.add("temp.warning", "", "temp.warning", argType::Required, "temp", "warning", false, "float", "Temperature at which to issue a warning. Default is 40.");
147  config.add("temp.alert", "", "temp.alert", argType::Required, "temp", "alert", false, "float", "Temperature at which to issue an alert. Default is 50.");
148  config.add("temp.emergency", "", "temp.emergency", argType::Required, "temp", "emergency", false, "float", "Temperature at which to issue an emergency. Default is 55.");
149 
150 
151  config.add("usb.idVendor", "", "usb.idVendor", argType::Required, "usb", "idVendor", false, "string", "USB vendor id, 4 digits");
152  config.add("usb.idProduct", "", "usb.idProduct", argType::Required, "usb", "idProduct", false, "string", "USB product id, 4 digits");
153 
155 }
156 
157 int usbtempMon::loadConfigImpl( mx::app::appConfigurator & _config )
158 {
159 
160  _config(m_warnTemp, "temp.warning");
161  _config(m_alertTemp, "temp.alert");
162  _config(m_emergTemp, "temp.emergency");
163 
164  m_idVendor = "067b";
165  _config(m_idVendor, "usb.idVendor");
166  m_idProduct = "2303";
167  _config(m_idProduct, "usb.idProduct");
168 
169 
170  std::vector<std::string> sections;
171 
172  _config.unusedSections(sections);
173 
174  if( sections.size() == 0 )
175  {
176  log<text_log>("No temperature probes found in config.", logPrio::LOG_CRITICAL);
177  m_shutdown = 1;
178  return -1;
179  }
180 
181  for(size_t i=0; i< sections.size(); ++i)
182  {
183  if(config.isSetUnused(mx::app::iniFile::makeKey(sections[i], "serial" )))
184  {
185  m_probes.emplace_back();
186  config.configUnused(m_probes.back().m_serial, mx::app::iniFile::makeKey(sections[i], "serial" ));
187  m_probes.back().m_serial = mx::ioutils::toLower(m_probes.back().m_serial);
188 
189  m_probes.back().m_location = sections[i];
190  config.configUnused(m_probes.back().m_location, mx::app::iniFile::makeKey(sections[i], "location" ));
191  }
192  }
193 
194  std::sort( m_probes.begin(), m_probes.end());
195 
197 
198  return 0;
199 }
200 
202 {
203  loadConfigImpl(config);
204 }
205 
207 {
208  createROIndiNumber( m_indiP_temps, "temperature", "Temperature [C]");
209  for(size_t n =0; n < m_probes.size(); ++n)
210  {
211  indi::addNumberElement<double>( m_indiP_temps, m_probes[n].m_location, -20., 120., 0, "%0.2f");
212  m_indiP_temps[m_probes[n].m_location] = -999;
213  }
215 
216 
218  {
219  return log<software_error,-1>({__FILE__,__LINE__});
220  }
221 
222  return checkConnections();
223 }
224 
226 {
227  bool checkConn = false;
228  for(size_t n =0; n < m_probes.size(); ++n)
229  {
230  if(m_probes[n].m_fd > 0)
231  {
232  float temperature = -1e37;
233  if (DS18B20_acquire(m_probes[n].m_fd, &temperature) < 0)
234  {
235  log<software_error>({__FILE__, __LINE__, "Did not get temp from " + m_probes[n].m_location + ". Error: " + DS18B20_errmsg()});
236  DS18B20_close(m_probes[n].m_fd);
237  m_probes[n].m_fd = 0;
238  checkConn = true;
239  continue;
240  }
241 
242  m_probes[n].m_temperature = temperature;
243  updateIfChanged(m_indiP_temps, m_probes[n].m_location, temperature);
244 
245  if(temperature >= m_warnTemp)
246  {
247  log<text_log>(m_probes[n].m_location + " temp = " + std::to_string(temperature), logPrio::LOG_WARNING);
248  }
249  else if(temperature >= m_alertTemp)
250  {
251  log<text_log>(m_probes[n].m_location + " temp = " + std::to_string(temperature), logPrio::LOG_ALERT);
252  }
253  else if(temperature >= m_emergTemp)
254  {
255  log<text_log>(m_probes[n].m_location + " temp = " + std::to_string(temperature), logPrio::LOG_EMERGENCY);
256  }
257 
258  recordTemps(); //log it in telemeter
259 
260 
261  if (DS18B20_measure(m_probes[n].m_fd) < 0)
262  {
263  log<software_error>({__FILE__, __LINE__, "Error from " + m_probes[n].m_location + ". Error: " + DS18B20_errmsg()});
264  DS18B20_close(m_probes[n].m_fd);
265  m_probes[n].m_fd = 0;
266  updateIfChanged(m_indiP_temps, m_probes[n].m_location, -999);
267  checkConn = true;
268  continue;
269  }
270  }
271  else
272  {
273  updateIfChanged(m_indiP_temps, m_probes[n].m_location, -999);
274  checkConn = true;
275  }
276 
277 
278  }
279 
280  if(checkConn)
281  {
283  }
284 
286  {
288  }
289 
291  {
292  log<software_error>({__FILE__, __LINE__});
293  return 0;
294  }
295 
296  return 0;
297 }
298 
300 {
301  return 0;
302 }
303 
305 {
306  std::vector<std::string> devNames;
307 
309 
310  int nconn = 0;
311  for(size_t i=0; i< devNames.size(); ++i)
312  {
313  elevatedPrivileges ep(this);
314 
315  int fd = DS18B20_open(devNames[i].c_str());
316  ep.restore();
317 
318  if (!is_fd_valid(fd))
319  {
320  log<software_error>({__FILE__,__LINE__, std::string(DS18B20_errmsg())});
321  DS18B20_close(fd);
322  continue;
323  }
324 
325 
326 
327  unsigned char rom[DS18X20_ROM_SIZE];
328  char romstr[2*DS18X20_ROM_SIZE+1];
329  if (DS18B20_rom(fd, rom) < 0)
330  {
331  log<software_error>({__FILE__,__LINE__, std::string(DS18B20_errmsg())});
332  DS18B20_close(fd);
333  continue;
334  }
335 
336  for (size_t i = 0; i < DS18X20_ROM_SIZE; i++)
337  {
338  snprintf(romstr + 2*i, 3, "%02x", rom[i]);
339  }
340 
341  for(size_t j=0;j< m_probes.size();++j)
342  {
343  if( m_probes[j].m_serial == romstr)
344  {
345  if(m_probes[j].m_fd > 0 ) //Check if we're already connected to this one
346  {
347  if( m_probes[j].m_devName == devNames[i]) //This means we are still good
348  {
349  ++nconn;
350  DS18B20_close(fd);
351  fd = 0;
352  break;
353  }
354 
355  //If here, something changed, and so we close the existing fd and reconnect.
356  DS18B20_close(m_probes[j].m_fd);
357  }
358 
359  m_probes[j].m_fd = fd;
360  fd = 0;
361  m_probes[j].m_devName=devNames[i];
362 
363  log<text_log>("Found " + m_probes[j].m_location + " [" + m_probes[j].m_serial + "] as " + m_probes[j].m_devName);
364 
365  ++nconn;
366  if (DS18B20_measure(m_probes[j].m_fd) < 0)
367  {
368  log<software_error>({__FILE__, __LINE__, "Error from " + m_probes[j].m_location + ". Error: " + DS18B20_errmsg()});
369 
370  DS18B20_close(m_probes[j].m_fd);
371  m_probes[j].m_fd = 0;
372  }
373  break;
374  }
375  }
376 
377  if(fd != 0)
378  {
379  log<text_log>("no match for " + devNames[i]);
380  DS18B20_close(fd);
381  }
382  }
383 
386 
387  return 0;
388 }
389 
391 {
393 }
394 
396 {
397  return recordTemps(true);
398 }
399 
400 inline
401 int usbtempMon::recordTemps(bool force)
402 {
403  if(m_lastTemps.size() != m_probes.size())
404  {
405  m_lastTemps.resize(m_probes.size());
406  for(size_t n=0; n<m_lastTemps.size(); ++n) m_lastTemps[n] = -1e30;
407  }
408 
409  bool log = false;
410  std::vector<float> temps;
411  temps.resize(m_probes.size());
412  for(size_t n=0; n<temps.size(); ++n)
413  {
414  temps[n] = m_probes[n].m_temperature;
415  if(temps[n] != m_lastTemps[n]) log = true;
416  }
417 
418  if(force || log)
419  {
420  telem<telem_temps>(temps);
421  for(size_t n=0; n<m_lastTemps.size(); ++n) m_lastTemps[n] = temps[n];
422  }
423 
424  return 0;
425 
426 }
427 
428 } //namespace app
429 } //namespace MagAOX
430 
431 #endif //usbtempMon_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
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:3120
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:100
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
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:2517
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2655
The MagAO-X xxxxxxxx.
Definition: usbtempMon.hpp:42
pcf::IndiProperty m_indiP_temps
Definition: usbtempMon.hpp:82
virtual int appStartup()
Startup function.
Definition: usbtempMon.hpp:206
virtual int appShutdown()
Shutdown the app.
Definition: usbtempMon.hpp:299
virtual void loadConfig()
Definition: usbtempMon.hpp:201
~usbtempMon() noexcept
D'tor, declared and defined for noexcept.
Definition: usbtempMon.hpp:89
std::vector< probe > m_probes
Definition: usbtempMon.hpp:79
friend class usbtempMon_test
Definition: usbtempMon.hpp:45
std::vector< float > m_lastTemps
Definition: usbtempMon.hpp:132
int recordTelem(const telem_temps *)
Definition: usbtempMon.hpp:395
virtual int appLogic()
Implementation of the FSM for usbtempMon.
Definition: usbtempMon.hpp:225
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: usbtempMon.hpp:157
int recordTemps(bool force=false)
Definition: usbtempMon.hpp:401
virtual void setupConfig()
Definition: usbtempMon.hpp:144
usbtempMon()
Default c'tor.
Definition: usbtempMon.hpp:138
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
@ CONNECTED
The application has connected to the device or service.
Definition: stateCodes.hpp:50
@ NOTCONNECTED
The application is not connected to the device or service.
Definition: stateCodes.hpp:49
int ttyUSBDevNames(std::vector< std::string > &devNames, const std::string &vendor, const std::string &product)
Get the ttyUSB device name for a set of devices specified by their vendor and product ids.
Definition: ttyUSB.cpp:137
std::string toLower(std::string const &s)
Definition: dm.hpp:24
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
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
A device base class which saves telemetry.
Definition: telemeter.hpp:69
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:223
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:268
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:211
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:281
bool operator<(const probe &p) const
Definition: usbtempMon.hpp:73
Software ERR log entry.
Log entry recording electronics rack temperature.
Definition: telem_temps.hpp:26
A USB device as a TTY device.
Definition: usbDevice.hpp:33
std::string m_idProduct
The product id 4-digit code.
Definition: usbDevice.hpp:35
std::string m_serial
The serial number.
Definition: usbDevice.hpp:36
std::string m_idVendor
The vendor id 4-digit code.
Definition: usbDevice.hpp:34
char * DS18B20_errmsg(void)
Definition: usbtemp.c:68
int DS18B20_acquire(HANDLE fd, float *temperature)
Definition: usbtemp.c:172
int DS18B20_measure(HANDLE fd)
Definition: usbtemp.c:115
HANDLE DS18B20_open(const char *serial_port)
Definition: usbtemp.c:220
void DS18B20_close(HANDLE fd)
Definition: usbtemp.c:225
int is_fd_valid(HANDLE fd)
Definition: usbtemp.c:25
int DS18B20_rom(HANDLE fd, unsigned char *rom)
Definition: usbtemp.c:194
#define DS18X20_ROM_SIZE
Definition: usbtemp.h:13