API
flipperCtrl.hpp
Go to the documentation of this file.
1 /** \file flipperCtrl.hpp
2  * \brief The MagAO-X XXXXXX header file
3  *
4  * \ingroup flipperCtrl_files
5  */
6 
7 #ifndef flipperCtrl_hpp
8 #define flipperCtrl_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 /** \defgroup flipperCtrl
15  * \brief The XXXXXX application to do YYYYYYY
16  *
17  * <a href="../handbook/operating/software/apps/XXXXXX.html">Application Documentation</a>
18  *
19  * \ingroup apps
20  *
21  */
22 
23 /** \defgroup flipperCtrl_files
24  * \ingroup flipperCtrl
25  */
26 
27 namespace MagAOX
28 {
29 namespace app
30 {
31 
32 /// The MagAO-X xxxxxxxx
33 /**
34  * \ingroup flipperCtrl
35  */
36 class flipperCtrl : public MagAOXApp<true>, public tty::usbDevice, public dev::ioDevice, public dev::telemeter<flipperCtrl>
37 {
38 
39  //Give the test harness access.
40  friend class flipperCtrl_test;
41 
42  friend class dev::telemeter<flipperCtrl>;
43 
45 
46 protected:
47 
48  /** \name Configurable Parameters
49  *@{
50  */
51 
52  //here add parameters which will be config-able at runtime
53  int m_inPos {1};
54  int m_outPos {2};
55 
56  ///@}
57 
58 
59  int m_pos {1};
60  int m_tgt {0};
61 
62 public:
63  /// Default c'tor.
64  flipperCtrl();
65 
66  /// D'tor, declared and defined for noexcept.
67  ~flipperCtrl() noexcept
68  {}
69 
70  virtual void setupConfig();
71 
72  /// Implementation of loadConfig logic, separated for testing.
73  /** This is called by loadConfig().
74  */
75  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
76 
77  virtual void loadConfig();
78 
79  /// Startup function
80  /**
81  *
82  */
83  virtual int appStartup();
84 
85  /// Implementation of the FSM for flipperCtrl.
86  /**
87  * \returns 0 on no critical error
88  * \returns -1 on an error requiring shutdown
89  */
90  virtual int appLogic();
91 
92  /// Shutdown the app.
93  /**
94  *
95  */
96  virtual int appShutdown();
97 
98 
99  int getPos();
100 
101  int moveTo(int pos);
102 
103  pcf::IndiProperty m_indiP_position;
104 
106 
107 
108  /* Telemetry */
109  int checkRecordTimes();
110 
111  int recordTelem( const telem_stage *);
112 
113  int recordStage( bool force = false);
114 
115 };
116 
117 flipperCtrl::flipperCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
118 {
119  m_powerMgtEnabled = true;
120  return;
121 }
122 
124 {
127 
128  config.add("flipper.reverse", "", "flipper.reverse", argType::Required, "flipper", "reverse", false, "bool", "If true, reverse the positions for in and out.");
129 
130  telemeterT::setupConfig(config);
131 }
132 
133 int flipperCtrl::loadConfigImpl( mx::app::appConfigurator & _config )
134 {
135  this->m_baudRate = B115200; //default for MCBL controller. Will be overridden by any config setting.
136 
137  int rv = tty::usbDevice::loadConfig(_config);
138 
139  if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
140  {
141  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
142  }
143 
144  dev::ioDevice::loadConfig(_config);
145 
146  bool rev = false;
147  _config(rev, "flipper.reverse");
148 
149  if(rev)
150  {
151  m_inPos = 2;
152  m_outPos = 1;
153  }
154 
155  telemeterT::loadConfig(_config);
156 
157  return 0;
158 }
159 
161 {
162  if(loadConfigImpl(config)<0)
163  {
164  log<software_critical>({__FILE__, __LINE__});
165  m_shutdown = 1;
166  return;
167  }
168 }
169 
171 {
172  createStandardIndiSelectionSw( m_indiP_position, "presetName", {"in", "out"});
173 
175  {
176  log<software_error>({__FILE__,__LINE__});
177  return -1;
178  }
179 
180  if(telemeterT::appStartup() < 0)
181  {
182  return log<software_error,-1>({__FILE__,__LINE__});
183  }
184 
185  return 0;
186 }
187 
189 {
190  if(state() == stateCodes::POWERON)
191  {
193  }
194 
195  if( state() == stateCodes::NODEVICE )
196  {
198  if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
199  {
201  if(!stateLogged())
202  {
203  log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
204  }
205  return -1;
206  }
207 
208  if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
209  {
211  if(!stateLogged())
212  {
213  std::stringstream logs;
214  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
215  log<text_log>(logs.str());
216  }
217  return 0;
218  }
219  else
220  {
222  if(!stateLogged())
223  {
224  std::stringstream logs;
225  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
226  log<text_log>(logs.str());
227  }
228  }
229  }
230 
232  {
233  elevatedPrivileges ep(this);
234  int rv = connect();
235  ep.restore();
236 
237  if(rv < 0)
238  {
239  int nrv = tty::usbDevice::getDeviceName();
240  if(nrv < 0 && nrv != TTY_E_DEVNOTFOUND && nrv != TTY_E_NODEVNAMES)
241  {
243  if(!stateLogged()) log<software_critical>({__FILE__, __LINE__, nrv, tty::ttyErrorString(nrv)});
244  return -1;
245  }
246 
247  if(nrv == TTY_E_DEVNOTFOUND || nrv == TTY_E_NODEVNAMES)
248  {
250 
251  if(!stateLogged())
252  {
253  std::stringstream logs;
254  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " no longer found in udev";
255  log<text_log>(logs.str());
256  }
257  return 0;
258  }
259  }
260 
262  }
263 
264  if( state() == stateCodes::CONNECTED )
265  {
266  std::unique_lock<std::mutex> lock(m_indiMutex);
267  getPos();
268  m_tgt = m_pos;
269 
271  }
272 
274  {
275  std::unique_lock<std::mutex> lock(m_indiMutex);
276 
277  getPos();
278 
279  if(m_pos == m_inPos)
280  {
281  if(m_pos == m_tgt)
282  {
283  updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::On, INDI_IDLE);
284  updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::Off, INDI_IDLE);
286  }
287  else
288  {
289  updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::On, INDI_BUSY);
290  updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::Off, INDI_BUSY);
292  }
293  }
294  else
295  {
296  if(m_pos == m_tgt)
297  {
298  updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::Off, INDI_IDLE);
299  updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::On, INDI_IDLE);
301  }
302  else
303  {
304  updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::Off, INDI_BUSY);
305  updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::On, INDI_BUSY);
307  }
308  }
309 
310  recordStage();
311 
312  if(telemeterT::appLogic() < 0)
313  {
314  log<software_error>({__FILE__, __LINE__});
315  return 0;
316  }
317  /* */
318 
319  //sleep(2);
320  }
321 
322  return 0;
323 }
324 
326 {
327  return 0;
328 }
329 
330 
332 {
333  std::string header(6,'\0');
334 
335  header[0] = 0x80;
336  header[1] = 0x04;
337  header[2] = 0x00;
338  header[3] = 0x00;
339  header[4] = 0x50;
340  header[5] = 0x01;
341 
343 
344  std::string response;
345  if(tty::ttyRead(response, 20, m_fileDescrip, m_readTimeout) < 0)
346  {
347  log<software_error>({__FILE__,__LINE__, "error getting response from flipper"});
348  }
349 
350  if(response[16] == 1)
351  {
352  m_pos = 1;
353  }
354  else
355  {
356  m_pos = 2;
357  }
358 
359  return 0;
360 }
361 
363 {
364  std::string header(6,'\0');
365 
366  header[0] = 0x6A;
367  header[1] = 0x04;
368  header[2] = 0x00;
369  if(pos == 1)
370  {
371  header[3] = 0x01;
372  }
373  else if(pos == 2)
374  {
375  header[3] = 0x02;
376  }
377  else
378  {
379  return log<software_error,-1>({__FILE__,__LINE__, "invalid position"});
380  }
381  header[4] = 0x50;
382  header[5] = 0x01;
383 
385 
386  return 0;
387 }
388 
389 INDI_NEWCALLBACK_DEFN(flipperCtrl, m_indiP_position )(const pcf::IndiProperty &ipRecv)
390 {
391  if(ipRecv.getName() != m_indiP_position.getName())
392  {
393  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
394  return -1;
395  }
396 
397 
398  int newpos = 0;
399 
400  if(ipRecv.find("in"))
401  {
402  if(ipRecv["in"].getSwitchState() == pcf::IndiElement::On)
403  {
404  newpos = m_inPos;
405  }
406  }
407 
408  if(ipRecv.find("out"))
409  {
410  if(ipRecv["out"].getSwitchState() == pcf::IndiElement::On)
411  {
412  if(newpos)
413  {
414  log<text_log>("can not set position to both in and out", logPrio::LOG_ERROR);
415  }
416  else newpos = m_outPos;
417  }
418  }
419 
420  if(newpos)
421  {
422  m_tgt = newpos;
423 
424  std::unique_lock<std::mutex> lock(m_indiMutex);
425 
426  m_indiP_position.setState (INDI_BUSY);
427  m_indiDriver->sendSetProperty (m_indiP_position);
428 
429  recordStage(true);
430  state(stateCodes::OPERATING);
431 
432  if(moveTo(m_tgt) < 0)
433  {
434  return log<software_error,-1>({__FILE__, __LINE__});
435  }
436 
437  recordStage(true);
438 
439  return 0;
440  }
441 
442 
443 
444  return 0;
445 }
446 
447 
449 {
451 }
452 
454 {
455  return recordStage(true);
456 }
457 
458 inline
459 int flipperCtrl::recordStage( bool force )
460 {
461  static int last_pos = -1;
462  static int last_moving = -1;
463 
464  int moving = (m_tgt != m_pos);
465 
466  if(last_pos != m_pos || last_moving != moving || force)
467  {
468  std::string ps = "in";
469  if(m_pos == m_outPos) ps = "out";
470 
471  telem<telem_stage>({ (int8_t) moving, (double) m_pos, ps});
472 
473  last_pos = m_pos;
474  last_moving = moving;
475  }
476 
477 
478  return 0;
479 }
480 
481 
482 } //namespace app
483 } //namespace MagAOX
484 
485 #endif //flipperCtrl_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
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.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:102
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:2901
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
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
int createStandardIndiSelectionSw(pcf::IndiProperty &prop, const std::string &name, const std::vector< std::string > &elements, const std::vector< std::string > &elementLabels, const std::string &label="", const std::string &group="")
Create a standard R/W INDI selection (one of many) switch with vector of elements and element labels.
Definition: MagAOXApp.hpp:2383
bool m_powerMgtEnabled
Flag controls whether power mgt is used. Set this in the constructor of a derived app....
Definition: MagAOXApp.hpp:981
The MagAO-X xxxxxxxx.
Definition: flipperCtrl.hpp:37
friend class flipperCtrl_test
Definition: flipperCtrl.hpp:40
flipperCtrl()
Default c'tor.
pcf::IndiProperty m_indiP_position
int recordTelem(const telem_stage *)
~flipperCtrl() noexcept
D'tor, declared and defined for noexcept.
Definition: flipperCtrl.hpp:67
INDI_NEWCALLBACK_DECL(flipperCtrl, m_indiP_position)
virtual void setupConfig()
virtual int appLogic()
Implementation of the FSM for flipperCtrl.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
virtual int appStartup()
Startup function.
virtual void loadConfig()
int recordStage(bool force=false)
virtual int appShutdown()
Shutdown the app.
dev::telemeter< flipperCtrl > telemeterT
Definition: flipperCtrl.hpp:44
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:207
@ 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
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:51
@ 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
@ POWERON
The device power is on.
Definition: stateCodes.hpp:43
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
Definition: ttyErrors.cpp:15
int ttyWrite(const std::string &buffWrite, int fd, int timeoutWrite)
Write to the tty console indicated by a file descriptor.
Definition: ttyIOUtils.cpp:132
int ttyRead(std::string &strRead, int bytes, int fd, int timeoutRead)
Read from a tty console indicated by a file-descriptor, until a given number of bytes are read.
Definition: ttyIOUtils.cpp:206
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_BUSY
Definition: indiUtils.hpp:30
const pcf::IndiProperty & ipRecv
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
Definition: dm.hpp:24
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
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 appStartup()
Starts the telemetry log thread.
Definition: telemeter.hpp:226
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 ERR log entry.
Log entry recording stdMotionStage status.
Definition: telem_stage.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
speed_t m_baudRate
The baud rate specification.
Definition: usbDevice.hpp:38
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