API
filterWheelCtrl.hpp
Go to the documentation of this file.
1 /** \file filterWheelCtrl.hpp
2  * \brief The MagAO-X Filter Wheel Controller
3  *
4  * \ingroup filterWheelCtrl_files
5  */
6 
7 
8 #ifndef filterWheelCtrl_hpp
9 #define filterWheelCtrl_hpp
10 
11 
12 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
13 #include "../../magaox_git_version.h"
14 
15 /** \defgroup filterWheelCtrl Filter Wheel Control
16  * \brief Control of MagAO-X MCBL-based f/w.
17  *
18  * <a href="../handbook/operating/software/apps/filterWheelCtrl.html">Application Documentation</a>
19  *
20  * \ingroup apps
21  *
22  */
23 
24 /** \defgroup filterWheelCtrl_files Filter Wheel Control Files
25  * \ingroup filterWheelCtrl
26  */
27 
28 namespace MagAOX
29 {
30 namespace app
31 {
32 
33 /** MagAO-X application to control a Faulhaber MCBL controlled filter wheel.
34  *
35  * \todo add temperature monitoring
36  * \todo add INDI props to md doc
37  * \todo should move in least time direction, rather than always in the same direction.
38  * \todo add tests
39  * \todo should be an iodevice
40  *
41  * \ingroup filterWheelCtrl
42  */
43 class filterWheelCtrl : public MagAOXApp<>, public tty::usbDevice, public dev::stdMotionStage<filterWheelCtrl>, public dev::telemeter<filterWheelCtrl>
44 {
45 
47 
48  friend class dev::telemeter<filterWheelCtrl>;
49 
50 protected:
51 
52  /** \name Non-configurable parameters
53  *@{
54  */
55 
56  int m_motorType {2};
57 
58  ///@}
59 
60  /** \name Configurable Parameters
61  * @{
62  */
63 
64  int m_writeTimeOut {1000}; ///< The timeout for writing to the device [msec].
65  int m_readTimeOut {1000}; ///< The timeout for reading from the device [msec].
66 
67  double m_acceleration {100};
68  double m_deceleration {50};
69  double m_motorSpeed {3000};
70 
71  long m_circleSteps {0}; ///< The number of position counts in 1 360-degree revolution.
72  long m_homeOffset {0}; ///< The number of position counts to offset from the home position
73 
74 
75 
76  ///@}
77 
78  /** \name Status
79  * @{
80  */
81 
82  bool m_switch{false}; ///< The home switch status
83 
84  long m_rawPos {0}; ///< The position of the wheel in motor counts.
85 
86  int m_homingState{0}; ///< The homing state, tracks the stages of homing.
87  ///@}
88 
89 
90 public:
91 
92  /// Default c'tor.
94 
95  /// D'tor, declared and defined for noexcept.
96  ~filterWheelCtrl() noexcept
97  {}
98 
99  /// Setup the configuration system (called by MagAOXApp::setup())
100  virtual void setupConfig();
101 
102  /// load the configuration system results (called by MagAOXApp::setup())
103  virtual void loadConfig();
104 
105  /// Startup functions
106  /** Setsup the INDI vars.
107  *
108  * \returns 0 on success
109  * \returns -1 on error.
110  */
111  virtual int appStartup();
112 
113  /// Implementation of the FSM for the TTM Modulator
114  /**
115  * \returns 0 on success
116  * \returns -1 on error.
117  */
118  virtual int appLogic();
119 
120  /// Do any needed shutdown tasks. Currently nothing in this app.
121  /**
122  * \returns 0 on success
123  * \returns -1 on error.
124  */
125  virtual int appShutdown();
126 
127 
128  /// This method is called when the change to poweroff is detected.
129  /**
130  * \returns 0 on success.
131  * \returns -1 on any error which means the app should exit.
132  */
133  virtual int onPowerOff();
134 
135  /// This method is called while the power is off, once per FSM loop.
136  /**
137  * \returns 0 on success.
138  * \returns -1 on any error which means the app should exit.
139  */
140  virtual int whilePowerOff();
141 
142 protected:
143 
144  //declare our properties
145 
146  ///The position of the wheel in counts
147  pcf::IndiProperty m_indiP_counts;
148 
149 public:
151 
152 protected:
153  //Each of these should have m_indiMutex locked before being called.
154 
155  /// Set up the MCBL controller, called after each power-on/connection
156  /**
157  * \returns 0 on success.
158  * \returns -1 on error.
159  */
160  int onPowerOnConnect();
161 
162  /// Get the home switch status, sets m_switch to true or false.
163  /**
164  * \returns 0 on success.
165  * \returns -1 on error.
166  */
167  int getSwitch();
168 
169  /// Get the moving-state of the wheel, sets m_moving to true or false.
170  /**
171  * \returns 0 on success.
172  * \returns -1 on error.
173  */
174  int getMoving();
175 
176  /// Get the current position of the wheel, sets m_rawPos to the current motor counts.
177  /**
178  * \returns 0 on success.
179  * \returns -1 on error.
180  */
181  int getPos();
182 
183  /// Start a high-level homing sequence.
184  /** For this device this includes the homing dither.
185  *
186  * \returns 0 on success.
187  * \returns -1 on error.
188  */
189  int startHoming();
190 
191  int presetNumber();
192 
193  /// Start a low-level homing sequence.
194  /** This initiates the device homing sequence.
195  *
196  * \returns 0 on success.
197  * \returns -1 on error.
198  */
199  int home();
200 
201  /// Stop the wheel motion immediately.
202  /**
203  * \returns 0 on success.
204  * \returns -1 on error.
205  */
206  int stop();
207 
208  /// Move to an absolute position in raw counts.
209  /**
210  * \returns 0 on success.
211  * \returns -1 on error.
212  */
213  int moveToRaw( const long & counts /**< [in] The new position in absolute motor counts*/);
214 
215  /// Move to a new position relative to current, in raw counts.
216  /**
217  * \returns 0 on success.
218  * \returns -1 on error.
219  */
220  int moveToRawRelative( const long & counts /**< [in] The new position in relative motor counts*/ );
221 
222  /// Move to an absolute position in filter units.
223  /**
224  * \returns 0 on success.
225  * \returns -1 on error.
226  */
227  int moveTo( const double & filters /**< [in] The new position in absolute filter units*/ );
228 
229  /** \name Telemeter Interface
230  *
231  * @{
232  */
233  int checkRecordTimes();
234 
235  int recordTelem( const telem_stage * );
236 
237  int recordTelem( const telem_position * );
238 
239  int recordStage( bool force = false );
240 
241  int recordPosition( bool force = false );
242 
243  ///@}
244 };
245 
246 inline
247 filterWheelCtrl::filterWheelCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
248 {
249  m_presetNotation = "filter"; //sets the name of the configs, etc.
250 
251  m_powerMgtEnabled = true;
252 
253  return;
254 }
255 
256 inline
258 {
260 
261  config.add("timeouts.write", "", "timeouts.write", argType::Required, "timeouts", "write", false, "int", "The timeout for writing to the device [msec]. Default = 1000");
262  config.add("timeouts.read", "", "timeouts.read", argType::Required, "timeouts", "read", false, "int", "The timeout for reading the device [msec]. Default = 1000");
263 
264  config.add("motor.acceleration", "", "motor.acceleration", argType::Required, "motor", "acceleration", false, "real", "The motor acceleration parameter. Default=100.");
265  config.add("motor.deceleration", "", "motor.deceleration", argType::Required, "motor", "deceleration", false, "real", "The motor deceleration parameter. Default=50.");
266  config.add("motor.speed", "", "motor.speed", argType::Required, "motor", "speed", false, "real", "The motor speed parameter. Default=3000.");
267  config.add("motor.circleSteps", "", "motor.circleSteps", argType::Required, "motor", "circleSteps", false, "long", "The number of steps in 1 revolution.");
268  config.add("stage.homeOffset", "", "stage.homeOffset", argType::Required, "stage", "homeOffset", false, "long", "The homing offset in motor counts.");
269 
271 
273 }
274 
275 inline
277 {
278  this->m_baudRate = B9600; //default for MCBL controller. Will be overridden by any config setting.
279 
280  int rv = tty::usbDevice::loadConfig(config);
281 
282  if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
283  {
284  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
285  }
286 
287  config(m_writeTimeOut, "timeouts.write");
288  config(m_readTimeOut, "timeouts.read");
289 
290  config(m_acceleration, "motor.acceleration");
291  config(m_deceleration, "motor.deceleration");
292  config(m_motorSpeed, "motor.speed");
293  config(m_circleSteps, "motor.circleSteps");
294  config(m_homeOffset, "stage.homeOffset");
295 
296 
298 
300 }
301 
302 inline
304 {
306  {
307  log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
308  return -1;
309  }
310 
311  // set up the INDI properties
312  createStandardIndiNumber<long>( m_indiP_counts, "counts", std::numeric_limits<long>::lowest(), std::numeric_limits<long>::max(), 0.0, "%ld");
314 
315 
317 
319  {
320  return log<software_error,-1>({__FILE__,__LINE__});
321  }
322 
323  return 0;
324 }
325 
326 inline
328 {
329  if( state() == stateCodes::INITIALIZED )
330  {
331  log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
332  return -1;
333  }
334 
335  if( state() == stateCodes::POWERON )
336  {
338  else
339  {
341  std::stringstream logs;
342  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
343  log<text_log>(logs.str());
344  }
345  }
346 
347  if( state() == stateCodes::NODEVICE )
348  {
350  if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
351  {
353  if(!stateLogged())
354  {
355  log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
356  }
357  return -1;
358  }
359 
360  if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
361  {
363 
364  if(!stateLogged())
365  {
366  std::stringstream logs;
367  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
368  log<text_log>(logs.str());
369  }
370  return 0;
371  }
372  else
373  {
375  if(!stateLogged())
376  {
377  std::stringstream logs;
378  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
379  log<text_log>(logs.str());
380  }
381  }
382  }
383 
385  {
386  int rv;
387  {
388  elevatedPrivileges elPriv(this);
389  rv = connect();
390  }
391 
392  if(rv < 0)
393  {
394  int nrv = tty::usbDevice::getDeviceName();
395  if(nrv < 0 && nrv != TTY_E_DEVNOTFOUND && nrv != TTY_E_NODEVNAMES)
396  {
398  if(!stateLogged()) log<software_critical>({__FILE__, __LINE__, nrv, tty::ttyErrorString(nrv)});
399  return -1;
400  }
401 
402  if(nrv == TTY_E_DEVNOTFOUND || nrv == TTY_E_NODEVNAMES)
403  {
405 
406  if(!stateLogged())
407  {
408  std::stringstream logs;
409  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " no longer found in udev";
410  log<text_log>(logs.str());
411  }
412  return 0;
413  }
414 
415  //if connect failed, and there is a device, then we have some other problem.
416  sleep(1); //wait to see if power state updates
417  if(m_powerState == 0) return 0;
418 
419  //Ok we can't figure this out, die.
421  if(!stateLogged()) log<software_error>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
422  return -1;
423 
424  }
425 
426 
427 
428  if( getPos() == 0 ) state(stateCodes::CONNECTED);
429  else
430  {
431  return 0;
432  }
433 
435  {
436  std::stringstream logs;
437  logs << "Connected to filter wheel on " << m_deviceName;
438  log<text_log>(logs.str());
439  }
440 
441  }
442 
443  if( state() == stateCodes::CONNECTED )
444  {
445  int rv = onPowerOnConnect();
446 
447  if(rv < 0)
448  {
449  log<software_error>({__FILE__,__LINE__});
451 
452  return 0;
453  }
454 
455  std::string landfill;
456  tty::ttyRead(landfill, "\r", m_fileDescrip, m_readTimeOut); //read to timeout to kill any missed chars.
457 
458  if(m_powerOnHome)
459  {
460  std::lock_guard<std::mutex> guard(m_indiMutex);
461 
462  if(startHoming()<0)
463  {
465  log<software_error>({__FILE__,__LINE__});
466  return 0;
467  }
468  }
470 
471 
472  }
473 
475  {
476  { //mutex scope
477  //Make sure we have exclusive attention of the device
478  std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
479 
480  int rv = getSwitch();
481 
482  if(rv != 0 )
483  {
485  return 0;
486  }
487 
488  rv = getMoving();
489 
490  if(rv != 0 )
491  {
493  return 0;
494  }
495 
496  rv = getPos();
497 
498  if(rv != 0 )
499  {
501  return 0;
502  }
503 
504  recordPosition();
505  }
506 
507  if(m_moving)
508  {
509  //Started moving but we don't know yet.
511  }
512  else
513  {
514  if(state() == stateCodes::OPERATING)
515  {
516  if(m_movingState == 1)
517  {
518  std::lock_guard<std::mutex> guard(m_indiMutex);
519  if(moveToRawRelative(-20000) < 0)
520  {
521  sleep(1);
522  if(m_powerState == 0) return 0;
523 
525  return log<software_error,0>({__FILE__,__LINE__});
526  }
527 
528  m_movingState=2;
529  }
530  else if(m_movingState == 2)
531  {
532  std::lock_guard<std::mutex> guard(m_indiMutex);
533  if(moveTo(m_preset_target) < 0)
534  {
535  sleep(1);
536  if(m_powerState == 0) return 0;
537 
539  return log<software_error,0>({__FILE__,__LINE__});
540  }
541 
542  m_movingState=3;
543  }
544  else
545  {
546  m_movingState = 0;
547  state(stateCodes::READY); //stopped moving but was just changing pos
548  }
549 
550  }
551  else if (state() == stateCodes::HOMING) //stopped moving but was in the homing sequence
552  {
553  if(m_homingState == 1)
554  {
555  std::lock_guard<std::mutex> guard(m_indiMutex);
556  if(moveToRawRelative(-50000) < 0)
557  {
558  sleep(1);
559  if(m_powerState == 0) return 0;
560 
562  return log<software_error,0>({__FILE__,__LINE__});
563  }
564 
565  m_homingState=2;
566  }
567  else if (m_homingState == 2)
568  {
569  std::lock_guard<std::mutex> guard(m_indiMutex);
570  if(home()<0)
571  {
572  sleep(1);
573  if(m_powerState == 0) return 0;
574 
576  return log<software_error,0>({__FILE__,__LINE__});
577  }
578  m_homingState = 3;
579  }
580  else if(m_homingState == 3)
581  {
582  std::lock_guard<std::mutex> guard(m_indiMutex);
583  if(moveToRaw(m_homeOffset)<0)
584  {
585  sleep(1);
586  if(m_powerState == 0) return 0;
587 
589  return log<software_error,0>({__FILE__,__LINE__});
590  }
591  m_homingState=4;
592  }
593  else if(m_homingState == 4)
594  {
595  if(m_homePreset >= 0)
596  {
599 
601  }
602  m_homingState = 5;
603  }
604  else
605  {
606  m_homingState = 0;
608 
610  }
611  }
612  }
613 
614  std::lock_guard<std::mutex> guard(m_indiMutex);
615 
616  if(m_moving)
617  {
619  }
620  else
621  {
623  }
624 
625  m_preset = ((double) m_rawPos-m_homeOffset)/m_circleSteps*m_presetNames.size() + 1.0;
626 
628  recordStage();
629 
631  {
632  log<software_error>({__FILE__, __LINE__});
633  return 0;
634  }
635 
636  return 0;
637  }
638 
639  if(state() == stateCodes::ERROR)
640  {
641  sleep(1);
642  if(m_powerState == 0) return 0;
643 
644  return log<software_error,-1>({__FILE__,__LINE__, "In state ERROR but no recovery implemented. Terminating."});
645  }
646 
647  return log<software_error,-1>({__FILE__,__LINE__, "appLogic fell through. Terminating."});
648 
649 }
650 
651 
652 
653 inline
655 {
656  //don't bother
657  return 0;
658 }
659 
660 inline
662 {
664  {
665  log<software_error>({__FILE__,__LINE__});
666  }
667 
668  recordStage(true);
669  recordPosition(true);
670 
671  return 0;
672 }
673 
674 inline
676 {
678  {
679  log<software_error>({__FILE__,__LINE__});
680  }
681 
682  //record telem if it's been longer than 10 sec:
684  {
685  log<software_error>({__FILE__, __LINE__});
686  }
687 
688  return 0;
689 }
690 
691 INDI_NEWCALLBACK_DEFN(filterWheelCtrl, m_indiP_counts)(const pcf::IndiProperty &ipRecv)
692 {
693  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_counts, ipRecv);
694 
695  double counts = -1;
696  double target_abs = -1;
697  if(ipRecv.find("current"))
698  {
699  counts = ipRecv["current"].get<double>();
700  }
701  if(ipRecv.find("target"))
702  {
703  target_abs = ipRecv["target"].get<double>();
704  }
705  if(target_abs == -1) target_abs = counts;
706 
707 
708  m_preset_target = ((double) target_abs - m_homeOffset)/m_circleSteps*m_presetNames.size() + 1.0;
709  std::lock_guard<std::mutex> guard(m_indiMutex);
710  return moveToRaw(target_abs);
711 
712 }
713 
714 
715 
716 
718 {
719  std::string com;
720 
721  int rv;
722 
723  std::lock_guard<std::mutex> guard(m_indiMutex);
724 
725  rv = tty::ttyWrite( "ANSW0\r", m_fileDescrip, m_writeTimeOut); //turn off replies and asynchronous comms.
726  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
727 
728  //Send motor type
729  com = "MOTTYP" + std::to_string(m_motorType) + "\r";
731  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
732 
733  //Set up acceleration and speed.
734  com = "AC" + std::to_string(m_acceleration) + "\r";
736  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
737 
738  //Set up acceleration and speed.
739  com = "DEC" + std::to_string(m_deceleration) + "\r";
741  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
742 
743  com = "SP" + std::to_string(m_motorSpeed) + "\r";
745  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
746 
747  return 0;
748 }
749 
751 {
752  int rv;
753  std::string resp;
754 
755  rv = tty::ttyWriteRead( resp, "GAST\r", "\r\n", false, m_fileDescrip, m_writeTimeOut, m_readTimeOut);
756 
757  if(rv == 0)
758  {
759 
760  if(resp == "1011") m_switch=true;
761  else m_switch=false;
762 
763  return 0;
764  }
765 
766 
767  return rv;
768 
769 }
770 
772 {
773  int rv;
774  std::string resp;
775 
776  rv = tty::ttyWriteRead( resp, "GN\r", "\r\n", false, m_fileDescrip, m_writeTimeOut, m_readTimeOut);
777 
778  if(rv == 0)
779  {
780  int speed;
781  try{ speed = std::stol(resp.c_str());}
782  catch(...){speed=0;}
783 
784  if(fabs(speed) > 0.1*m_motorSpeed)
785  {
786  if(m_homingState) m_moving = 2;
787  else m_moving = 1;
788  }
789  else m_moving = 0;
790 
791  return 0;
792  }
793 
794 
795  return rv;
796 
797 }
798 
800 {
801  int rv;
802  std::string resp;
803 
804  rv = tty::ttyWriteRead( resp, "POS\r", "\r\n", false, m_fileDescrip, m_writeTimeOut, m_readTimeOut);
805 
806  if(rv == 0)
807  {
808  try{ m_rawPos = std::stol(resp.c_str());}
809  catch(...){m_rawPos=0;}
810  }
811 
812 
813  return rv;
814 
815 }
816 
818 {
819  m_homingState = 1;
820  m_moving = 2;
821  recordStage(true);
822  recordPosition(true);
823 
824  updateSwitchIfChanged(m_indiP_home, "request", pcf::IndiElement::Off, INDI_IDLE);
825  return home();
826 }
827 
829 {
830  //First we calculate current preset name
831  int n = floor(m_preset + 0.5) - 1;
832  if(n < 0)
833  {
834  while(n < 0) n += m_presetNames.size();
835  }
836  if( n > (long) m_presetNames.size()-1 )
837  {
838  while( n > (long) m_presetNames.size()-1 ) n -= m_presetNames.size();
839  }
840 
841  if( n < 0)
842  {
843  log<software_error>({__FILE__,__LINE__, "error calculating " + m_presetNotation + " index, n < 0"});
844  return -1;
845  }
846 
847  return n;
848 }
849 
851 {
852 
854 
855  int rv;
856 
858  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
859 
860  rv = tty::ttyWrite( "HA4\r", m_fileDescrip, m_writeTimeOut);
861  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
862 
863  rv = tty::ttyWrite( "HL4\r", m_fileDescrip, m_writeTimeOut);
864  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
865 
866  rv = tty::ttyWrite( "CAHOSEQ\r", m_fileDescrip, m_writeTimeOut);
867  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
868 
869  rv = tty::ttyWrite( "HP0\r", m_fileDescrip, m_writeTimeOut);
870  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
871 
872  std::string com = "HOSP" + std::to_string(m_motorSpeed) + "\r";
874  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
875 
876  rv = tty::ttyWrite( "GOHOSEQ\r", m_fileDescrip, m_writeTimeOut);
877  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
878 
879 
880 
881  return 0;
882 }
883 
885 {
886  m_homingState = 0;
887  //First try without locking
889 
890  //Now make sure it goes through
891  std::lock_guard<std::mutex> guard(m_indiMutex);
892  int rv = tty::ttyWrite( "DI\r", m_fileDescrip, m_writeTimeOut);
893 
894  updateSwitchIfChanged(m_indiP_stop, "request", pcf::IndiElement::Off, INDI_IDLE);
895 
896  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
897 
898  return 0;
899 }
900 
901 int filterWheelCtrl::moveToRaw( const long & counts )
902 {
903 
904  std::string com;
905  int rv;
906 
907  m_moving = 1;
908  recordStage(true);
909  recordPosition(true);
910 
912  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
913 
914  com = "LA" + std::to_string(counts) + "\r";
916  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
917 
919  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
920 
921  updateIfChanged(m_indiP_counts, "target", counts, pcf::IndiProperty::Busy);
922 
923  return 0;
924 }
925 
926 int filterWheelCtrl::moveToRawRelative( const long & counts_relative )
927 {
928 
929  std::string com;
930  int rv;
931 
932  m_moving = 1;
933  recordStage(true);
934  recordPosition(true);
935 
937  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
938 
939  com = "LR" + std::to_string(counts_relative) +"\r";
941  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
942 
944  if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
945 
946 
947 
948  return 0;
949 }
950 
951 int filterWheelCtrl::moveTo( const double & filters )
952 {
953  long counts;
954 
955  if(m_circleSteps ==0 || m_presetNames.size() == 0) counts = filters;
956  else counts = m_homeOffset + m_circleSteps/m_presetNames.size() * (filters-1);
957 
958  return moveToRaw(counts);
959 
960 }
961 
963 {
965 }
966 
968 {
969  return recordStage(true);
970 }
971 
973 {
974  return recordPosition(true);
975 }
976 
978 {
980 }
981 
983 {
984  static long last_pos = 999999999999;
985 
986  if( m_rawPos != last_pos || force )
987  {
988  float fpos = m_rawPos;
989 
990  telem<telem_position>(fpos);
991 
992  last_pos = m_rawPos;
993  }
994 
995  return 0;
996 }
997 
998 } //namespace app
999 } //namespace MagAOX
1000 
1001 #endif //filterWheelCtrl_hpp
Internal class to manage setuid privilege escalation with RAII.
Definition: MagAOXApp.hpp:324
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 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_powerState
Current power state, 1=On, 0=Off, -1=Unk.
Definition: MagAOXApp.hpp:1033
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
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2361
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:545
MagAO-X standard motion stage interface.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
std::vector< std::string > m_presetNames
The names of each position on the stage.
bool m_powerOnHome
If true, then the motor is homed at startup (by this software or actual power on)
std::string m_presetNotation
Notation used to refer to a preset, should be singular, as in "preset" or "filter".
float m_preset_target
The target numerical preset position [1.0 is index 0 in the preset name vector].
pcf::IndiProperty m_indiP_home
Command the stage to home. .
int updateINDI()
Update the INDI properties for this device controller.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int m_homePreset
If >=0, this preset position is moved to after homing.
int8_t m_moving
Whether or not the stage is moving. -2 means powered off, -1 means not homed, 0 means not moving,...
pcf::IndiProperty m_indiP_stop
Command the stage to halt.
pcf::IndiProperty m_indiP_preset
The position of the stage in presets.
float m_preset
The current numerical preset position [1.0 is index 0 in the preset name vector].
int appStartup()
Startup function.
std::vector< float > m_presetPositions
The positions, in arbitrary units, of each preset. If 0, then the integer position number (starting f...
int recordStage(bool force=false)
int8_t m_movingState
Used to track the type of command. If > 1 this is a command to move to a preset. If 0 then it is a mo...
virtual int appLogic()
Implementation of the FSM for the TTM Modulator.
long m_circleSteps
The number of position counts in 1 360-degree revolution.
int home()
Start a low-level homing sequence.
int recordPosition(bool force=false)
virtual void setupConfig()
Setup the configuration system (called by MagAOXApp::setup())
virtual int whilePowerOff()
This method is called while the power is off, once per FSM loop.
virtual int appShutdown()
Do any needed shutdown tasks. Currently nothing in this app.
int getSwitch()
Get the home switch status, sets m_switch to true or false.
pcf::IndiProperty m_indiP_counts
The position of the wheel in counts.
int startHoming()
Start a high-level homing sequence.
int moveToRaw(const long &counts)
Move to an absolute position in raw counts.
long m_homeOffset
The number of position counts to offset from the home position.
INDI_NEWCALLBACK_DECL(filterWheelCtrl, m_indiP_counts)
int onPowerOnConnect()
Set up the MCBL controller, called after each power-on/connection.
int recordStage(bool force=false)
int getPos()
Get the current position of the wheel, sets m_rawPos to the current motor counts.
long m_rawPos
The position of the wheel in motor counts.
int moveTo(const double &filters)
Move to an absolute position in filter units.
virtual void loadConfig()
load the configuration system results (called by MagAOXApp::setup())
virtual int onPowerOff()
This method is called when the change to poweroff is detected.
int m_writeTimeOut
The timeout for writing to the device [msec].
bool m_switch
The home switch status.
int moveToRawRelative(const long &counts)
Move to a new position relative to current, in raw counts.
~filterWheelCtrl() noexcept
D'tor, declared and defined for noexcept.
int m_homingState
The homing state, tracks the stages of homing.
int recordTelem(const telem_stage *)
int getMoving()
Get the moving-state of the wheel, sets m_moving to true or false.
int m_readTimeOut
The timeout for reading from the device [msec].
virtual int appStartup()
Startup functions.
int stop()
Stop the wheel motion immediately.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:208
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
@ NODEVICE
No device exists for the application to control.
Definition: stateCodes.hpp:46
@ NOTHOMED
The device has not been homed.
Definition: stateCodes.hpp:53
@ HOMING
The device is homing.
Definition: stateCodes.hpp:54
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
Definition: stateCodes.hpp:42
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
Definition: stateCodes.hpp:43
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:56
@ CONNECTED
The application has connected to the device or service.
Definition: stateCodes.hpp:50
@ UNINITIALIZED
The application is unitialized, the default.
Definition: stateCodes.hpp:44
@ INITIALIZED
The application has been initialized, set just before calling appStartup().
Definition: stateCodes.hpp:45
@ NOTCONNECTED
The application is not connected to the device or service.
Definition: stateCodes.hpp:49
@ POWERON
The device power is on.
Definition: stateCodes.hpp:48
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
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
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
Definition: dm.hpp:24
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
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
Software ERR log entry.
Log entry recording position stage specific status.
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