API
pi335Ctrl.hpp
Go to the documentation of this file.
1 /** \file pi335Ctrl.hpp
2  * \brief The MagAO-X XXXXXX header file
3  *
4  * \ingroup pi335Ctrl_files
5  */
6 
7 #ifndef pi335Ctrl_hpp
8 #define pi335Ctrl_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 pi335Ctrl
15  * \brief The XXXXXXX application to do YYYYYYY
16  *
17  * <a href="..//apps_html/page_module_pi335Ctrl.html">Application Documentation</a>
18  *
19  * \ingroup apps
20  *
21  */
22 
23 /** \defgroup pi335Ctrl_files
24  * \ingroup pi335Ctrl
25  */
26 
27 namespace MagAOX
28 {
29 namespace app
30 {
31 
32 /// The MagAO-X xxxxxxxx
33 /**
34  * \ingroup pi335Ctrl
35  */
36 class pi335Ctrl : public MagAOXApp<true> , public tty::usbDevice, public dev::ioDevice, public dev::dm<pi335Ctrl,float>, public dev::shmimMonitor<pi335Ctrl>
37 {
38 
39  //Give the test harness access.
40  friend class pi335Ctrl_test;
41 
42  friend class dev::dm<pi335Ctrl,float>;
43 
44  friend class dev::shmimMonitor<pi335Ctrl>;
45 
46 protected:
47 
48  /** \name Configurable Parameters
49  *@{
50  */
51 
52 
53 
54  float m_homePos1 {17.5}; ///< Home position of axis 1. Default is 17.5
55  float m_homePos2 {17.5}; ///< Home position of axis 2. Default is 17.5
56  float m_homePos3 {0.0}; ///< Home position of axis 2. Default is 17.5
57 
58  ///@}
59 
60 private:
61  std::string m_ctrl; ///< The controller connected.
62  std::string m_stage; ///< The stage connected.
63  int m_naxes{2}; ///< The number of axes, default is 2. Max is 3.
64 
65  bool m_pos_3_sent {false};
66 
67  bool m_actuallyATZ {true};
68 
69 protected:
70  float m_min1 {0}; ///< The minimum value for axis 1
71  float m_max1 {35}; ///< The maximum value for axis 1
72 
73  float m_min2 {0}; ///< The minimum value for axis 2
74  float m_max2 {35}; ///< The maximum value for axis 2
75 
76  float m_min3 {0}; ///< The minimum value for axis 3
77  float m_max3 {0}; ///< The maximum value for axis 3
78 
79  double m_homingStart {0};
80  int m_homingState {0};
81 
82  int m_servoState {0};
83 
84  float m_pos1 {0};
85  float m_pos2 {0};
86  float m_pos3 {0};
87 
88  float m_sva1 {0};
89  float m_sva2 {0};
90  float m_sva3 {0};
91 public:
92  /// Default c'tor.
93  pi335Ctrl();
94 
95  /// D'tor, declared and defined for noexcept.
96  ~pi335Ctrl() noexcept
97  {}
98 
99  virtual void setupConfig();
100 
101  /// Implementation of loadConfig logic, separated for testing.
102  /** This is called by loadConfig().
103  */
104  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
105 
106  virtual void loadConfig();
107 
108  /// Startup function
109  /**
110  *
111  */
112  virtual int appStartup();
113 
114  /// Implementation of the FSM for pi335Ctrl.
115  /**
116  * \returns 0 on no critical error
117  * \returns -1 on an error requiring shutdown
118  */
119  virtual int appLogic();
120 
121  /// Shutdown the app.
122  /**
123  *
124  */
125  virtual int appShutdown();
126 
127  /// Test the connection to the device
128  /** Uses the *IDN? query
129  *
130  * \returns 0 if the E227 is found
131  * \returns -1 on an error
132  */
133  int testConnection();
134 
135  int initDM();
136 
137  ///Start the homing procedure
138  /** Checks that servos are off (the manual says this doesn't matter),
139  * and then begins homing by calling 'home_1()'.
140  * Sets FSM state to homing.
141  *
142  * \returns 0 on success (from 'home_1()')
143  * \returns -1 on error
144  */
145  int home();
146 
147  ///Get the status of homing on an axiz
148  /** Homing is also autozero, ATZ. This uses the ATZ? query to
149  * check if the autozero procedure has completed on the given axis.
150  *
151  * \returns 0 if ATZ not complete
152  * \returns 1 if ATZ complete
153  * \returns -1 on an error
154  */
155  int homeState(int axis /**< [in] the axis to check, 0 or 1 */);
156 
157  ///Begin homing (ATZ) axis 1
158  /** Repeats check that servos are off (the manual says this doesn't matter)
159  * and checks that 'm_homingStatte' is 0. Then sends 'ATZ 1 NaN'.
160  *
161  * \returns 0 on success
162  * \returns -1 on error
163  */
164  int home_1();
165 
166  ///Begin homing (ATZ) axis 2
167  /** Repeats check that servos are off (the manual says this doesn't matter)
168  * and checks that 'm_homingStatte' is 1. Then sends 'ATZ 2 NaN'.
169  *
170  * \returns 0 on success
171  * \returns -1 on error
172  */
173  int home_2();
174 
175  ///Begin homing (ATZ) axis 3
176  /** Repeats check that servos are off (the manual says this doesn't matter)
177  * and checks that 'm_homingStatte' is 1. Then sends 'ATZ 3 NaN'.
178  *
179  * \returns 0 on success
180  * \returns -1 on error
181  */
182  int home_3();
183 
184  int finishInit();
185 
186  /// Zero all commands on the DM
187  /** This does not update the shared memory buffer.
188  *
189  * \returns 0 on success
190  * \returns -1 on error
191  */
192  int zeroDM();
193 
194  /// Send a command to the DM
195  /** This is called by the shmim monitoring thread in response to a semaphore trigger.
196  *
197  * \returns 0 on success
198  * \returns -1 on error
199  */
200  int commandDM(void * curr_src);
201 
202  /// Release the DM, making it safe to turn off power.
203  /** The application will be state READY at the conclusion of this.
204  *
205  * \returns 0 on success
206  * \returns -1 on error
207  */
208  int releaseDM();
209 
210  int setCom( const std::string & com );
211 
212  int setCom( const std::string & com,
213  int axis
214  );
215 
216  int setCom( const std::string & com,
217  int axis,
218  const std::string & arg
219  );
220 
221  int getCom( std::string & resp,
222  const std::string & com,
223  int axis
224  );
225 
226  int getPos( float & pos,
227  int n
228  );
229 
230  ///Get the open loop control value
231  int getSva( float & sva,
232  int n
233  );
234 
235  ///Update the flat command and propagate
236  /** Writes the new desired position to the flat shmim, which is then propagated via the dmcomb
237  */
238  int updateFlat( float absPos1, ///< [in] The new position of axis 1
239  float absPos2, ///< [in] The new position of axis 2
240  float absPos3 ///< [in] The new position of axis 3
241  );
242 
243  /// Send command to device to move axis 1
244  int move_1( float absPos );
245 
246  /// Send command to device to move axis 2
247  int move_2( float absPos );
248 
249  /// Send command to device to move axis 3
250  int move_3( float absPos );
251 
252 protected:
253  //declare our properties
254  pcf::IndiProperty m_indiP_pos1;
255  pcf::IndiProperty m_indiP_pos2;
256  pcf::IndiProperty m_indiP_pos3;
257 
258 public:
262 };
263 
264 pi335Ctrl::pi335Ctrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
265 {
266  m_powerMgtEnabled = true;
267 
268  return;
269 }
270 
272 {
276 
277  config.add("stage.naxes", "", "stage.naxes", argType::Required, "stage", "naxes", false, "int", "Number of axes. Default is 2. Max is 3.");
278 
279  config.add("stage.homePos1", "", "stage.homePos1", argType::Required, "stage", "homePos1", false, "float", "Home position of axis 1. Default is 17.5.");
280  config.add("stage.homePos2", "", "stage.homePos2", argType::Required, "stage", "homePos2", false, "float", "Home position of axis 2. Default is 17.5.");
281  config.add("stage.homePos3", "", "stage.homePos3", argType::Required, "stage", "homePos3", false, "float", "Home position of axis 3. Default is 17.5.");
282 
283  config.add("dm.calibRelDir", "", "dm.calibRelDir", argType::Required, "dm", "calibRelDir", false, "string", "Used to find the default calib directory.");
284 }
285 
286 int pi335Ctrl::loadConfigImpl( mx::app::appConfigurator & _config )
287 {
288 
289  this->m_baudRate = B115200; //default for E727 controller. Will be overridden by any config setting.
290 
291  int rv = tty::usbDevice::loadConfig(_config);
292 
293  if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND )
294  {
295  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
296  }
297 
298  dev::ioDevice::loadConfig(_config);
299 
300  m_calibRelDir = "ttmpupil";
301  config(m_calibRelDir, "dm.calibRelDir");
302 
304 
305  config(m_naxes, "stage.naxes");
306  config(m_homePos1, "stage.homePos1");
307  config(m_homePos2, "stage.homePos2");
308  config(m_homePos3, "stage.homePos3");
309 
310  return 0;
311 }
312 
314 {
315  loadConfigImpl(config);
316 }
317 
319 {
321  {
322  log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
323  return -1;
324  }
325 
326  ///\todo promote usbDevice to dev:: and make this part of its appStartup
327  //Get the USB device if it's in udev
328  if(m_deviceName != "")
329  {
330  log<text_log>(std::string("USB Device ") + m_idVendor + ":" + m_idProduct + ":" + m_serial + " found in udev as " + m_deviceName);
331  }
332 
333  ///\todo error checks here
336 
337  // set up the INDI properties
338  REG_INDI_NEWPROP(m_indiP_pos1, "pos_1", pcf::IndiProperty::Number);
339  m_indiP_pos1.add (pcf::IndiElement("current"));
340  m_indiP_pos1.add (pcf::IndiElement("target"));
341  m_indiP_pos1["current"] = -99999;
342  m_indiP_pos1["target"] = -99999;
343 
344  REG_INDI_NEWPROP(m_indiP_pos2, "pos_2", pcf::IndiProperty::Number);
345  m_indiP_pos2.add (pcf::IndiElement("current"));
346  m_indiP_pos2.add (pcf::IndiElement("target"));
347  m_indiP_pos2["current"] = -99999;
348  m_indiP_pos2["target"] = -99999;
349 
350  //Note: 3rd axis added in testConnection if it's found
351 
352  return 0;
353 }
354 
356 {
359 
360  if(state() == stateCodes::POWERON)
361  {
362  if(!powerOnWaitElapsed())
363  {
364  return 0;
365  }
366  else
367  {
368  ///\todo promote usbDevice to dev:: and make this part of its appStartup
369  //Get the USB device if it's in udev
370  if(m_deviceName == "")
371  {
373  }
374  else
375  {
377  }
378  }
379  }
380 
381  ///\todo promote usbDevice to dev:: and make this part of its appLogic
382  if( state() == stateCodes::NODEVICE )
383  {
385  if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
386  {
388  if(!stateLogged())
389  {
390  log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
391  }
392  return -1;
393  }
394 
395  if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
396  {
398 
399  if(!stateLogged())
400  {
401  log<text_log>(std::string("USB Device ") + m_idVendor + ":" + m_idProduct + ":" + m_serial + " not found in udev");
402  }
403  return 0;
404  }
405  else
406  {
408  if(!stateLogged())
409  {
410  log<text_log>(std::string("USB Device ") + m_idVendor + ":" + m_idProduct + ":" + m_serial + " found in udev as " + m_deviceName);
411  }
412  }
413  }
414 
416  {
417  int rv;
418  { //scope for elPriv
419  elevatedPrivileges elPriv(this);
420  rv = connect();
421  }
422 
423  if(rv < 0)
424  {
425  int nrv = tty::usbDevice::getDeviceName();
426  if(nrv < 0 && nrv != TTY_E_DEVNOTFOUND && nrv != TTY_E_NODEVNAMES)
427  {
429  if(!stateLogged()) log<software_critical>({__FILE__, __LINE__, nrv, tty::ttyErrorString(nrv)});
430  return -1;
431  }
432 
433  if(nrv == TTY_E_DEVNOTFOUND || nrv == TTY_E_NODEVNAMES)
434  {
436 
437  if(!stateLogged())
438  {
439  std::stringstream logs;
440  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " no longer found in udev";
441  log<text_log>(logs.str());
442  }
443  return 0;
444  }
445 
446  //if connect failed, and there is a device, then we have some other problem.
448  if(!stateLogged()) log<software_error>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
449  return -1;
450 
451  }
452 
453  if( testConnection() == 0 )
454  {
456  }
457  else
458  {
459  return 0;
460  }
461  }
462 
464  {
466  }
467 
468  if(state() == stateCodes::HOMING)
469  {
470  //std::cerr << "Homing state: " << m_homingState << " " << mx::sys::get_curr_time() - m_homingStart << "\n";
471  int ax = m_homingState + 1;
472 
473  int atz = homeState(ax);
474 
475  if(! (atz == 0 || atz == 1))
476  {
478  log<software_error,-1>({__FILE__, __LINE__, "error getting ATZ? home state."});
479  }
480 
481  if(atz == 1) //mx::sys::get_curr_time() - m_homingStart > 20)
482  {
483  ++m_homingState;
484 
485  if(m_homingState == 1) //x complete
486  {
487  home_2();
488  }
489  else if(m_homingState == 2 && m_naxes == 2) //y complete, done
490  {
491  finishInit();
492  }
493  else if(m_homingState == 2 && m_naxes == 3) //y complete
494  {
495  home_3();
496  }
497  else if(m_homingState > 2)
498  {
499  finishInit();
500  }
501  }
502  }
503 
505  {
507  else state(stateCodes::READY);
508  }
509 
510  if(state() == stateCodes::READY)
511  {
512  float pos1;
513  float sva1;
514  float pos2;
515  float sva2;
516  float pos3;
517  float sva3;
518 
519  //Get a lock if we can
520  std::unique_lock<std::mutex> lock(m_indiMutex);
521 
522  if(getPos(pos1, 1) < 0)
523  {
524  log<software_error>({__FILE__,__LINE__});
526  return 0;
527  }
528 
529  lock.unlock();
530  mx::sys::milliSleep(1); //Put this thread to sleep to make sure other thread gets a lock
531 
532  if(fabs(m_pos1-pos1) > 0.1)
533  {
535  }
536  else
537  {
539  }
540 
541  lock.lock();
542  if(getSva(sva1, 1) < 0)
543  {
544  log<software_error>({__FILE__,__LINE__});
546  return 0;
547  }
548  m_sva1 = sva1;
549 
550  lock.unlock();
551  mx::sys::milliSleep(1); //Put this thread to sleep to make sure other thread gets a lock
552 
553  lock.lock();
554  if(getPos(pos2, 2) < 0)
555  {
556  log<software_error>({__FILE__,__LINE__});
558  return 0;
559  }
560  lock.unlock();
561  mx::sys::milliSleep(1); //Put this thread to sleep to make sure other thread gets a lock
562 
563  if(fabs(m_pos2 - pos2) > 0.1) //sva2 != m_sva2)
564  {
566  }
567  else
568  {
570  }
571 
572  lock.lock();
573  if(getSva(sva2, 2) < 0)
574  {
575  log<software_error>({__FILE__,__LINE__});
577  return 0;
578  }
579  lock.unlock();
580  mx::sys::milliSleep(1); //Put this thread to sleep to make sure other thread gets a lock
581 
582  m_sva2 = sva2;
583 
584  if(m_naxes == 3)
585  {
586  lock.lock();
587  if(getPos(pos3, 3) < 0)
588  {
589  log<software_error>({__FILE__,__LINE__});
591  return 0;
592  }
593  lock.unlock();
594  mx::sys::milliSleep(1); //Put this thread to sleep to make sure other thread gets a lock
595 
596  if(fabs(m_pos3 - pos3) > 0.1) //sva2 != m_sva2)
597  {
599  }
600  else
601  {
603  }
604 
605  lock.lock();
606  if(getSva(sva3, 2) < 0)
607  {
608  log<software_error>({__FILE__,__LINE__});
610  return 0;
611  }
612  lock.unlock();
613  mx::sys::milliSleep(1); //Put this thread to sleep to make sure other thread gets a lock
614 
615  m_sva3 = sva3;
616  }
617 
618 
619  //std::cerr << m_pos1 << " " << pos1 << " " << m_sva1 << " " << m_pos2 << " " << pos2 << " " << m_sva2;
620  //if(m_naxes == 3) std::cerr << " " << m_pos3 << " " << pos3 << " " << m_sva3;
621  //std::cerr << "\n";
622  }
623  else if(state() == stateCodes::OPERATING)
624  {
629  if(m_naxes == 3)
630  {
633  }
634  }
635  return 0;
636 }
637 
639 {
642 
643  return 0;
644 }
645 
647 {
648  int rv;
649  std::string resp;
650 
651  if( (rv = tty::ttyWriteRead( resp, "*IDN?\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
652  {
653  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
654  }
655 
656  std::cerr << "idn response: " << resp << "\n";
657 
658  size_t st;
659  if( (st = resp.find("E-727.3SDA")) == std::string::npos)
660  {
661  return log<text_log,-1>("Unknown device found: " + resp, logPrio::LOG_CRITICAL);
662  }
663  m_ctrl = mx::ioutils::removeWhiteSpace(resp.substr(st));
664  log<text_log>(std::string("Connected to " + m_ctrl + " on ") + m_deviceName);
665 
666  std::string resp1, resp2, resp3;
667 
668  if( (rv = tty::ttyWriteRead( resp1, "CST? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
669  {
670  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
671  }
672  resp1 = mx::ioutils::removeWhiteSpace(resp1);
673 
674  std::cerr << "CST? 1 response: " << resp1 << "\n";
675 
676  if( (rv = tty::ttyWriteRead( resp2, "CST? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
677  {
678  return log<software_critical,-1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
679  }
680  resp2 = mx::ioutils::removeWhiteSpace(resp2);
681 
682  std::cerr << "CST? 2 response: " << resp2 << "\n";
683 
684  if( (rv = tty::ttyWriteRead( resp3, "CST? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
685  {
686  return log<software_critical,-1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
687  }
688  resp3 = mx::ioutils::removeWhiteSpace(resp3);
689 
690  std::cerr << "CST? 3 response: " << resp3 << "\n";
691 
692  updateIfChanged(m_indiP_pos1, "current", 0.0);
693  updateIfChanged(m_indiP_pos1, "target", 0.0);
694 
695  updateIfChanged(m_indiP_pos2, "current", 0.0);
696  updateIfChanged(m_indiP_pos2, "target", 0.0);
697 
698  if( resp1.find("1=S-335") == 0 && resp2.find("2=S-335") == 0 && resp3.find("3=0") == 0)
699  {
700  m_stage = resp1.substr(2);
701  m_naxes = 2;
702  m_actuallyATZ = true;
703  }
704  else if( resp1.find("1=S-325") == 0 && resp2.find("2=S-325") == 0 && resp3.find("3=S-325") == 0)
705  {
706  m_stage = resp1.substr(2);
707  m_naxes = 3;
708  m_actuallyATZ = false;
709  if(!m_pos_3_sent)
710  {
711  ///\todo this needs to only happen once, and then never again
712  REG_INDI_NEWPROP(m_indiP_pos3, "pos_3", pcf::IndiProperty::Number);
713  m_indiP_pos3.add (pcf::IndiElement("current"));
714  m_indiP_pos3.add (pcf::IndiElement("target"));
715  m_indiP_pos3["current"] = -99999999;
716  m_indiP_pos3["target"] = -99999999;
717  updateIfChanged(m_indiP_pos3, "current", 0.0);
718  updateIfChanged(m_indiP_pos3, "target", 0.0);
719  m_pos_3_sent = true;
720  }
721  }
722  else
723  {
724  return log<text_log,-1>("Unknown stage found: " + resp1 + " " + resp2 + " " + resp3, logPrio::LOG_CRITICAL);
725  }
726 
727  log<text_log>("Found " + m_stage + " with " + std::to_string(m_naxes) + " axes");
728 
729  //-------- now get axis limits
730 
731  // axis 1
732 
733  if( (rv = tty::ttyWriteRead( resp, "TMN? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
734  {
735  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
736  }
737 
738  if((st = resp.find('=')) == std::string::npos)
739  {
740  return log<software_critical, -1>( {__FILE__, __LINE__, "invalid response"});
741  }
742 
743  m_min1 = mx::ioutils::convertFromString<float>(resp.substr(st+1));
744  log<text_log>("axis 1 min: " + std::to_string(m_min1));
745 
746  if( (rv = tty::ttyWriteRead( resp, "TMX? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
747  {
748  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
749  }
750 
751  if((st = resp.find('=')) == std::string::npos)
752  {
753  return log<software_critical, -1>( {__FILE__, __LINE__, "invalid response"});
754  }
755 
756  m_max1 = mx::ioutils::convertFromString<float>(resp.substr(st+1));
757  log<text_log>("axis 1 max: " + std::to_string(m_max1));
758 
759  if( (rv = tty::ttyWriteRead( resp, "TMN? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
760  {
761  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
762  }
763 
764  if((st = resp.find('=')) == std::string::npos)
765  {
766  return log<software_critical, -1>( {__FILE__, __LINE__, "invalid response"});
767  }
768 
769  m_min2 = mx::ioutils::convertFromString<float>(resp.substr(st+1));
770  log<text_log>("axis 2 min: " + std::to_string(m_min2));
771 
772  if( (rv = tty::ttyWriteRead( resp, "TMX? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
773  {
774  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
775  }
776 
777  if((st = resp.find('=')) == std::string::npos)
778  {
779  return log<software_critical, -1>( {__FILE__, __LINE__, "invalid response"});
780  }
781 
782  m_max2 = mx::ioutils::convertFromString<float>(resp.substr(st+1));
783  log<text_log>("axis 2 max: " + std::to_string(m_max2));
784 
785  if(m_naxes == 3)
786  {
787  if( (rv = tty::ttyWriteRead( resp, "TMN? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
788  {
789  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
790  }
791 
792  if((st = resp.find('=')) == std::string::npos)
793  {
794  return log<software_critical, -1>( {__FILE__, __LINE__, "invalid response"});
795  }
796 
797  m_min3 = mx::ioutils::convertFromString<float>(resp.substr(st+1));
798  log<text_log>("axis 3 min: " + std::to_string(m_min3));
799 
800  if( (rv = tty::ttyWriteRead( resp, "TMX? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
801  {
802  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
803  }
804 
805  if((st = resp.find('=')) == std::string::npos)
806  {
807  return log<software_critical, -1>( {__FILE__, __LINE__, "invalid response"});
808  }
809 
810  m_max3 = mx::ioutils::convertFromString<float>(resp.substr(st+1));
811  log<text_log>("axis 3 max: " + std::to_string(m_max3));
812  }
813 
814  std::cerr << "Limits axis-1: " << m_min1 << " " << m_max1 << "\n";
815  std::cerr << "Limits axis-2: " << m_min2 << " " << m_max2 << "\n";
816  if(m_naxes == 3) std::cerr << "Limits axis-3: " << m_min3 << " " << m_max3 << "\n";
817 
818  m_flatCommand.resize(3,1);
819  if(m_naxes == 2)
820  {
821  m_flatCommand(0,0) = m_homePos1;
822  m_flatCommand(1,0) = m_homePos2;
823  m_flatCommand(2,0) = 0.0;
824  }
825  else if(m_naxes == 3)
826  {
827  m_flatCommand(0,0) = m_homePos1;
828  m_flatCommand(1,0) = m_homePos2;
829  m_flatCommand(2,0) = m_homePos3;
830  }
831  m_flatLoaded = true;
832 
833  /*if( (rv = tty::ttyWriteRead( resp, "PUN?\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
834  {
835  return log<software_critical, -1>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
836  }
837 
838  if((st = resp.find('=')) == std::string::npos)
839  {
840  return log<software_critical, -1>( {__FILE__, __LINE__, "invalid response"});
841  }
842 
843  std::cerr << resp << "\n";*/
844 
845  return 0;
846 
847 }
848 
850 {
851  int rv;
852  std::string resp;
853 
854 
855  //get open-loop position of axis 1 (should be zero)
856  //std::cerr << "Sending: SVA? 1\n";
857  rv = tty::ttyWriteRead( resp, "SVA? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
858 
859  if(rv < 0)
860  {
861  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
862  }
863  //std::cerr << "response: " << resp << "\n";
864 
865  //get open-loop position of axis 2 (should be zero)
866  // std::cerr << "Sending: SVA? 2\n";
867  rv = tty::ttyWriteRead( resp, "SVA? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
868 
869  if(rv < 0)
870  {
871  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
872  }
873  //std::cerr << "response: " << resp << "\n";
874 
875  if(m_naxes == 3)
876  {
877  //get open-loop position of axis 2 (should be zero)
878  // std::cerr << "Sending: SVA? 2\n";
879  rv = tty::ttyWriteRead( resp, "SVA? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
880 
881  if(rv < 0)
882  {
883  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
884  }
885  //std::cerr << "response: " << resp << "\n";
886  }
887 
888  //make sure axis 1 has servo off
889  //std::cerr << "Sending: SVO 1 0\n";
890  rv = tty::ttyWrite("SVO 1 0\n", m_fileDescrip, m_writeTimeout);
891 
892  if(rv < 0)
893  {
894  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
895  }
896 
897  //make sure axis 2 has servo off
898  //std::cerr << "Sending: SVO 2 0\n";
899  rv = tty::ttyWrite("SVA 2 0\n", m_fileDescrip, m_writeTimeout);
900 
901  if(rv < 0)
902  {
903  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
904  }
905 
906  if(m_naxes == 0)
907  {
908  //make sure axis 3 has servo off
909  //std::cerr << "Sending: SVO 2 0\n";
910  rv = tty::ttyWrite("SVA 3 0\n", m_fileDescrip, m_writeTimeout);
911 
912  if(rv < 0)
913  {
914  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
915  }
916  }
917 
918 
919  m_servoState = 0;
920 
921  log<text_log>("servos off", logPrio::LOG_NOTICE);
922 
923  return home();
924 }
925 
926 
928 {
929  if(m_servoState != 0)
930  {
931  log<text_log>("home requested but servos are not off", logPrio::LOG_ERROR);
932  return -1;
933  }
934 
935  m_homingStart = 0;
936  m_homingState = 0;
937 
939 
940 
941  return home_1();
942 
943 }
944 
945 int pi335Ctrl::homeState( int axis)
946 {
947  if(!m_actuallyATZ) return 1;
948  std::string resp;
949 
950  if( getCom( resp, "ATZ?", axis) < 0)
951  {
952  log<software_error>( {__FILE__, __LINE__});
953 
954  return -1;
955  }
956 
957  ///\todo this should be a separate unit-tested parser
958  size_t st = resp.find('=');
959  if(st == std::string::npos || st > resp.size()-2)
960  {
961  log<software_error>( {__FILE__, __LINE__, "error parsing response"});
962  return -1;
963  }
964  st += 1;
965 
966  return mx::ioutils::convertFromString<double>(resp.substr(st));
967 
968 
969 }
970 
971 
973 {
974  int rv;
975 
976  if(m_servoState != 0)
977  {
978  log<text_log>("home_1 requested but servos are not off", logPrio::LOG_ERROR);
979  return -1;
980  }
981 
982  if(m_homingState != 0)
983  {
984  log<text_log>("home_1 requested but not in correct homing state", logPrio::LOG_ERROR);
985  return -1;
986  }
987 
988  if(m_actuallyATZ)
989  {
990  //zero range found in axis 1 (NOTE this moves mirror full range) TAKES 1min
991  //std::cerr << "Sending: ATZ 1 NaN\n";
992  rv = tty::ttyWrite("ATZ 1 NaN\n", m_fileDescrip, m_writeTimeout);
993 
994  if(rv < 0)
995  {
996  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
997  }
998  }
999 
1000  m_homingStart = mx::sys::get_curr_time(); ///\todo remmove m_homingStart once ATZ? works.
1001  m_homingState = 0;
1002  log<text_log>("commenced homing x");
1003 
1004  return 0;
1005 }
1006 
1008 {
1009  int rv;
1010 
1011  if(m_servoState != 0)
1012  {
1013  log<text_log>("home_2 requested but servos are not off", logPrio::LOG_ERROR);
1014  return -1;
1015  }
1016 
1017  if(m_homingState != 1)
1018  {
1019  log<text_log>("home_2 requested but not in correct homing state", logPrio::LOG_ERROR);
1020  return -1;
1021  }
1022 
1023  if(m_actuallyATZ)
1024  {
1025  //zero range found in axis 2 (NOTE this moves mirror full range) TAKES 1min
1026  //std::cerr << "Sending: ATZ 2 NaN\n";
1027  rv = tty::ttyWrite("ATZ 2 NaN\n", m_fileDescrip, m_writeTimeout);
1028 
1029  if(rv < 0)
1030  {
1031  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1032  }
1033  }
1034 
1035  m_homingStart = mx::sys::get_curr_time();
1036  log<text_log>("commenced homing y");
1037 
1038  return 0;
1039 }
1040 
1042 {
1043  int rv;
1044 
1045  if(m_servoState != 0)
1046  {
1047  log<text_log>("home_3 requested but servos are not off", logPrio::LOG_ERROR);
1048  return -1;
1049  }
1050 
1051  if(m_homingState != 2)
1052  {
1053  log<text_log>("home_3 requested but not in correct homing state", logPrio::LOG_ERROR);
1054  return -1;
1055  }
1056 
1057  if(m_actuallyATZ)
1058  {
1059  //zero range found in axis 3 (NOTE this moves mirror full range) TAKES 1min
1060  //std::cerr << "Sending: ATZ 3 NaN\n";
1061  rv = tty::ttyWrite("ATZ 3 NaN\n", m_fileDescrip, m_writeTimeout);
1062 
1063  if(rv < 0)
1064  {
1065  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1066  }
1067  }
1068 
1069  m_homingStart = mx::sys::get_curr_time();
1070  log<text_log>("commenced homing z");
1071 
1072  return 0;
1073 }
1074 
1076 {
1077  int rv;
1078  std::string resp;
1079 
1080  if(m_servoState != 0)
1081  {
1082  log<text_log>("finishInit requested but servos are not off", logPrio::LOG_ERROR);
1083  return -1;
1084  }
1085 
1086  if( m_naxes==2 && m_homingState != 2)
1087  {
1088  log<text_log>("finishInit requested but not in correct homing state", logPrio::LOG_ERROR);
1089  return -1;
1090  }
1091  if( m_naxes==3 && m_homingState != 3)
1092  {
1093  log<text_log>("finishInit requested but not in correct homing state", logPrio::LOG_ERROR);
1094  return -1;
1095  }
1096 
1097  //goto openloop pos zero (0 V) axis 1
1098  rv = tty::ttyWrite("SVA 1 0.0\n", m_fileDescrip, m_writeTimeout);
1099 
1100  if(rv < 0)
1101  {
1102  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1103  }
1104 
1105  mx::sys::milliSleep(2000);
1106 
1107  //goto openloop pos zero (0 V) axis 2
1108  rv = tty::ttyWrite("SVA 2 0.0\n", m_fileDescrip, m_writeTimeout);
1109 
1110  if(rv < 0)
1111  {
1112  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1113  }
1114 
1115  mx::sys::milliSleep(2000);
1116 
1117  if(m_naxes == 3)
1118  {
1119  //goto openloop pos zero (0 V) axis 3
1120  rv = tty::ttyWrite("SVA 3 0.0\n", m_fileDescrip, m_writeTimeout);
1121 
1122  if(rv < 0)
1123  {
1124  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1125  }
1126 
1127  mx::sys::milliSleep(2000);
1128  }
1129 
1130  //Get the real position of axis 1 (should be 0mrad st start)
1131  rv = tty::ttyWriteRead( resp, "SVA? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
1132 
1133  if(rv < 0)
1134  {
1135  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1136  }
1137  //std::cerr << "response: " << resp << "\n";
1138 
1139  //Get the real position of axis 2 (should be 0mrad st start)
1140  rv = tty::ttyWriteRead( resp, "SVA? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
1141 
1142  if(rv < 0)
1143  {
1144  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1145  }
1146 
1147  if(m_naxes == 3)
1148  {
1149  //Get the real position of axis 3 (should be 0mrad st start)
1150  rv = tty::ttyWriteRead( resp, "SVA? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
1151 
1152  if(rv < 0)
1153  {
1154  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1155  }
1156  }
1157 
1158  //now safe to engage servos
1159  //(IMPORTANT: NEVER EVER enable servos on axis 3 -- will damage S-335)
1160  //(CAVEAT: for S-325 you CAN enable servors on axis 3)
1161 
1162  //turn on servo to axis 1 (green servo LED goes on 727)
1163  rv = tty::ttyWrite("SVO 1 1\n", m_fileDescrip, m_writeTimeout);
1164 
1165  if(rv < 0)
1166  {
1167  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1168  }
1169 
1170  mx::sys::milliSleep(250);
1171 
1172  //turn on servo to axis 3 (green servo LED goes on 727)
1173  rv = tty::ttyWrite("SVO 2 1\n", m_fileDescrip, m_writeTimeout);
1174 
1175  if(rv < 0)
1176  {
1177  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1178  }
1179 
1180  if(m_naxes == 3)
1181  {
1182  mx::sys::milliSleep(250);
1183 
1184  //turn on servo to axis 3 (green servo LED goes on 727)
1185  rv = tty::ttyWrite("SVO 3 1\n", m_fileDescrip, m_writeTimeout);
1186 
1187  if(rv < 0)
1188  {
1189  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1190  }
1191  }
1192 
1193 
1194  m_servoState = 1;
1195  log<text_log>("servos engaged", logPrio::LOG_NOTICE);
1196 
1197  mx::sys::milliSleep(1000);
1198 
1199 
1200 
1201  //now safe for closed loop moves
1202  //center axis 1 (to configured home position)
1203 
1204  std::string com = "MOV 1 " + std::to_string(m_homePos1) + "\n";
1206 
1207  if(rv < 0)
1208  {
1209  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1210  }
1211 
1212  m_pos1 = m_homePos1;
1214 
1215  //center axis 2 (to configured home position)
1216  com = "MOV 2 " + std::to_string(m_homePos2) + "\n";
1218 
1219  if(rv < 0)
1220  {
1221  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv) } );
1222  }
1223 
1224  m_pos2 = m_homePos2;
1226 
1227  if(m_naxes == 3)
1228  {
1229  //center axis 3 (to configured home position)
1230  com = "MOV 3 " + std::to_string(m_homePos3) + "\n";
1232 
1233  if(rv < 0)
1234  {
1235  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv) } );
1236  }
1237 
1238  m_pos3 = m_homePos3;
1240 
1241  }
1242 
1243  /*
1244  sleep(3);
1245 
1246 
1247  //Now we turn servos off
1248  if( tty::ttyWrite("SVO 1 0\n", m_fileDescrip, m_writeTimeout) < 0)
1249  {
1250  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1251  }
1252 
1253  if( tty::ttyWrite("SVO 2 0\n", m_fileDescrip, m_writeTimeout) < 0)
1254  {
1255  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv) } );
1256  }
1257 
1258  m_servoState = 0;
1259 
1260  log<text_log>("servos off", logPrio::LOG_NOTICE);
1261 
1262 
1263  float sva1;
1264  if(getSva(sva1, 1) < 0)
1265  {
1266  log<software_error>({__FILE__,__LINE__});
1267  state(stateCodes::ERROR);
1268  return 0;
1269  }
1270 
1271  m_sva1 = sva1;
1272 
1273  float sva2;
1274  if(getSva(sva2, 2) < 0)
1275  {
1276  log<software_error>({__FILE__,__LINE__});
1277  state(stateCodes::ERROR);
1278  return 0;
1279  }
1280 
1281  m_sva2 = sva2;
1282  */
1284  //state(stateCodes::OPERATING);
1285  return 0;
1286 }
1287 
1289 {
1290  move_1(0.0);
1291  move_2(0.0);
1292  if(m_naxes == 3) move_3(0.0);
1293 
1294  log<text_log>("DM zeroed");
1295  return 0;
1296 }
1297 
1298 int pi335Ctrl::commandDM(void * curr_src)
1299 {
1300  if(state() != stateCodes::OPERATING) return 0;
1301  float pos1 = ((float *) curr_src)[0];
1302  float pos2 = ((float *) curr_src)[1];
1303 
1304  float pos3 = 0;
1305  if(m_naxes == 3) pos3 = ((float *) curr_src)[2];
1306 
1307  std::unique_lock<std::mutex> lock(m_indiMutex);
1308 
1309  int rv;
1310  if( (rv = move_1(pos1)) < 0) return rv;
1311 
1312  if( (rv = move_2(pos2)) < 0) return rv;
1313 
1314  if(m_naxes == 3) if( (rv = move_3(pos3)) < 0) return rv;
1315 
1316  return 0;
1317 
1318 }
1319 
1321 {
1322  int rv;
1323 
1324  if(m_servoState != 0)
1325  {
1326  if( (rv = tty::ttyWrite("SVO 1 0\n", m_fileDescrip, m_writeTimeout)) < 0)
1327  {
1328  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1329  }
1330 
1331  if( (rv = tty::ttyWrite("SVO 2 0\n", m_fileDescrip, m_writeTimeout)) < 0)
1332  {
1333  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv) } );
1334  }
1335 
1336  m_servoState = 0;
1337 
1338  log<text_log>("servos off", logPrio::LOG_NOTICE);
1339  }
1340 
1341  if( (rv = tty::ttyWrite("SVA 1 0\n", m_fileDescrip, m_writeTimeout)) < 0)
1342  {
1343  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1344  }
1345 
1346  if( (rv = tty::ttyWrite("SVA 2 0\n", m_fileDescrip, m_writeTimeout)) < 0)
1347  {
1348  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv) } );
1349  }
1350 
1351  if(m_naxes == 3)
1352  {
1353  if( (rv = tty::ttyWrite("SVA 2 0\n", m_fileDescrip, m_writeTimeout)) < 0)
1354  {
1355  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv) } );
1356  }
1357  }
1358 
1359  m_flatSet = false;
1361 
1362  return 0;
1363 }
1364 
1365 int pi335Ctrl::getCom( std::string & resp,
1366  const std::string & com,
1367  int axis
1368  )
1369 {
1370  std::string sendcom = com;
1371  if(axis == 1 || axis == 2)
1372  {
1373  sendcom += " ";
1374  sendcom += std::to_string(axis);
1375  }
1376 
1377  sendcom += "\n";
1378 
1379  //std::cerr << "sending: " << sendcom;
1380  int rv = tty::ttyWriteRead( resp, sendcom, "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
1381  if(rv < 0)
1382  {
1383  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1384  return -1;
1385  }
1386 
1387  //std::cerr << "response: " << resp << "\n";
1388 
1389  return 0;
1390 }
1391 
1392 int pi335Ctrl::getPos( float & pos,
1393  int n
1394  )
1395 {
1396  std::string resp;
1397  if(getCom(resp, "POS?", n) < 0)
1398  {
1399  log<software_error>( {__FILE__, __LINE__});
1400  }
1401 
1402 
1403  ///\todo this should be a separate unit-tested parser
1404  size_t st = resp.find('=');
1405  if(st == std::string::npos || st > resp.size()-2)
1406  {
1407  log<software_error>( {__FILE__, __LINE__, "error parsing response"});
1408  return -1;
1409  }
1410  st += 1;
1411  pos = mx::ioutils::convertFromString<double>(resp.substr(st));
1412 
1413  return 0;
1414 }
1415 
1416 int pi335Ctrl::getSva( float & sva,
1417  int n
1418  )
1419 {
1420  std::string resp;
1421  if(getCom(resp, "SVA?", n) < 0)
1422  {
1423  log<software_error>( {__FILE__, __LINE__});
1424  }
1425 
1426  ///\todo this should be a separate unit-tested parser
1427  size_t st = resp.find('=');
1428  if(st == std::string::npos || st > resp.size()-2)
1429  {
1430  log<software_error>( {__FILE__, __LINE__, "error parsing response"});
1431  return -1;
1432  }
1433  st += 1;
1434  sva = mx::ioutils::convertFromString<double>(resp.substr(st));
1435 
1436  return 0;
1437 }
1438 
1439 int pi335Ctrl::updateFlat( float absPos1,
1440  float absPos2,
1441  float absPos3
1442  )
1443 {
1444  m_flatCommand(0,0) = absPos1;
1445  m_flatCommand(1,0) = absPos2;
1446  m_flatCommand(2,0) = absPos3;
1447 
1448  if(state() == stateCodes::OPERATING)
1449  {
1450  return setFlat(true);
1451  }
1452  else
1453  {
1454  return 0;
1455  }
1456 }
1457 
1458 int pi335Ctrl::move_1( float absPos )
1459 {
1460  int rv;
1461 
1462  if(absPos < m_min1 || absPos > m_max1)
1463  {
1464  log<text_log>("request move on azis 1 out of range", logPrio::LOG_ERROR);
1465  return -1;
1466  }
1467 
1468  m_pos1 = absPos;
1469 
1470  std::string com = "MOV 1 " + std::to_string(absPos) + "\n";
1471 
1472  //std::cerr << "Sending: " << com;
1474 
1475  if(rv < 0)
1476  {
1477  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1478  }
1479 
1480  return 0;
1481 }
1482 
1483 int pi335Ctrl::move_2( float absPos )
1484 {
1485  int rv;
1486 
1487  if(absPos < m_min2 || absPos > m_max2)
1488  {
1489  log<text_log>("request move on azis 2 out of range", logPrio::LOG_ERROR);
1490  return -1;
1491  }
1492 
1493  m_pos2 = absPos;
1494  std::string com = "MOV 2 " + std::to_string(absPos) + "\n";
1495 
1496  //std::cerr << "Sending: " << com;
1498 
1499  if(rv < 0)
1500  {
1501  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1502  }
1503 
1504  return 0;
1505 }
1506 
1507 int pi335Ctrl::move_3( float absPos )
1508 {
1509  if(m_naxes < 3)
1510  {
1511  return log<software_error,-1>({__FILE__, __LINE__, "tried to move axis 3 but we don't have that"});
1512  }
1513 
1514  int rv;
1515 
1516  if(absPos < m_min3 || absPos > m_max3)
1517  {
1518  log<text_log>("request move on azis 3 out of range", logPrio::LOG_ERROR);
1519  return -1;
1520  }
1521 
1522  m_pos3 = absPos;
1523  std::string com = "MOV 3 " + std::to_string(absPos) + "\n";
1524 
1525  //std::cerr << "Sending: " << com;
1527 
1528  if(rv < 0)
1529  {
1530  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
1531  }
1532 
1533  return 0;
1534 }
1535 
1536 INDI_NEWCALLBACK_DEFN(pi335Ctrl, m_indiP_pos1)(const pcf::IndiProperty &ipRecv)
1537 {
1538  //if(MagAOXAppT::m_powerState == 0) return 0;
1539 
1540  if (ipRecv.createUniqueKey() == m_indiP_pos1.createUniqueKey())
1541  {
1542  float current = -999999, target = -999999;
1543 
1544  if(ipRecv.find("current"))
1545  {
1546  current = ipRecv["current"].get<float>();
1547  }
1548 
1549  if(ipRecv.find("target"))
1550  {
1551  target = ipRecv["target"].get<float>();
1552  }
1553 
1554  if(target == -999999) target = current;
1555 
1556  if(target == -999999) return 0;
1557 
1558  if(state() == stateCodes::READY)
1559  {
1560  //Lock the mutex, waiting if necessary
1561  std::unique_lock<std::mutex> lock(m_indiMutex);
1562 
1563  updateIfChanged(m_indiP_pos1, "target", target);
1564 
1565  updateFlat(target, m_pos2, m_pos3); //This just changes the values, but doesn't move
1566 
1567  return move_1(target);
1568  }
1569  else if(state() == stateCodes::OPERATING)
1570  {
1571  return updateFlat(target, m_pos2, m_pos3);
1572  }
1573 
1574  }
1575  return -1;
1576 }
1577 
1578 INDI_NEWCALLBACK_DEFN(pi335Ctrl, m_indiP_pos2)(const pcf::IndiProperty &ipRecv)
1579 {
1580  //if(MagAOXAppT::m_powerState == 0) return 0;
1581 
1582  if (ipRecv.createUniqueKey() == m_indiP_pos2.createUniqueKey())
1583  {
1584  float current = -999999, target = -999999;
1585 
1586  if(ipRecv.find("current"))
1587  {
1588  current = ipRecv["current"].get<float>();
1589  }
1590 
1591  if(ipRecv.find("target"))
1592  {
1593  target = ipRecv["target"].get<float>();
1594  }
1595 
1596  std::cerr << current << " " << target << "\n";
1597 
1598  if(target == -999999) target = current;
1599 
1600  if(target == -999999) return 0;
1601 
1602  if(state() == stateCodes::READY)
1603  {
1604  //Lock the mutex, waiting if necessary
1605  std::unique_lock<std::mutex> lock(m_indiMutex);
1606 
1607  updateIfChanged(m_indiP_pos2, "target", target);
1608  updateFlat(m_pos1, target, m_pos3); //This just changes the values, but doesn't move
1609  return move_2(target);
1610  }
1611  else if(state() == stateCodes::OPERATING)
1612  {
1613  return updateFlat(m_pos1, target, m_pos3);
1614  }
1615 
1616  }
1617  return -1;
1618 }
1619 
1620 INDI_NEWCALLBACK_DEFN(pi335Ctrl, m_indiP_pos3)(const pcf::IndiProperty &ipRecv)
1621 {
1622  //if(MagAOXAppT::m_powerState == 0) return 0;
1623 
1624  if (ipRecv.getName() == m_indiP_pos3.getName())
1625  {
1626  float current = -999999, target = -999999;
1627 
1628  if(ipRecv.find("current"))
1629  {
1630  current = ipRecv["current"].get<float>();
1631  }
1632 
1633  if(ipRecv.find("target"))
1634  {
1635  target = ipRecv["target"].get<float>();
1636  }
1637 
1638  if(target == -999999) target = current;
1639 
1640  if(target == -999999) return 0;
1641 
1642  if(state() == stateCodes::READY)
1643  {
1644  //Lock the mutex, waiting if necessary
1645  std::unique_lock<std::mutex> lock(m_indiMutex);
1646 
1647  updateIfChanged(m_indiP_pos3, "target", target);
1648 
1649  updateFlat(m_pos1, m_pos2, target); //This just changes the values, but doesn't move
1650  return move_3(target);
1651  }
1652  else if(state() == stateCodes::OPERATING)
1653  {
1654  return updateFlat(m_pos1, m_pos2, target);
1655  }
1656 
1657  }
1658  return -1;
1659 }
1660 
1661 } //namespace app
1662 } //namespace MagAOX
1663 
1664 #endif //pi335Ctrl_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
bool powerOnWaitElapsed()
This method tests whether the power on wait time has elapsed.
Definition: MagAOXApp.hpp:3163
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
bool m_powerMgtEnabled
Flag controls whether power mgt is used. Set this in the constructor of a derived app....
Definition: MagAOXApp.hpp:981
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
Definition: dm.hpp:569
std::string m_calibRelDir
The directory relative to the calibPath. Set this before calling dm<derivedT,realT>::loadConfig().
Definition: dm.hpp:109
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
Definition: dm.hpp:610
int appShutdown()
DM shutdown.
Definition: dm.hpp:840
bool m_flatSet
Flag indicating whether the flat command has been set.
Definition: dm.hpp:120
bool m_flatLoaded
Flag indicating whether a flat is loaded in memory.
Definition: dm.hpp:117
int setFlat(bool update=false)
Send the current flat command to the DM.
Definition: dm.hpp:1208
mx::improc::eigenImage< float > m_flatCommand
Data storage for the flat command.
Definition: dm.hpp:116
int appStartup()
Startup function.
Definition: dm.hpp:683
int appLogic()
DM application logic.
Definition: dm.hpp:815
int appLogic()
Checks the shmimMonitor thread.
int appShutdown()
Shuts down the shmimMonitor thread.
The MagAO-X xxxxxxxx.
Definition: pi335Ctrl.hpp:37
virtual int appShutdown()
Shutdown the app.
Definition: pi335Ctrl.hpp:638
INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos2)
int testConnection()
Test the connection to the device.
Definition: pi335Ctrl.hpp:646
virtual void loadConfig()
Definition: pi335Ctrl.hpp:313
int commandDM(void *curr_src)
Send a command to the DM.
Definition: pi335Ctrl.hpp:1298
int home()
Start the homing procedure.
Definition: pi335Ctrl.hpp:927
int m_naxes
The number of axes, default is 2. Max is 3.
Definition: pi335Ctrl.hpp:63
int setCom(const std::string &com)
float m_max1
The maximum value for axis 1.
Definition: pi335Ctrl.hpp:71
pcf::IndiProperty m_indiP_pos3
Definition: pi335Ctrl.hpp:256
int getSva(float &sva, int n)
Get the open loop control value.
Definition: pi335Ctrl.hpp:1416
INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos1)
int getPos(float &pos, int n)
Definition: pi335Ctrl.hpp:1392
float m_homePos1
Home position of axis 1. Default is 17.5.
Definition: pi335Ctrl.hpp:54
INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos3)
~pi335Ctrl() noexcept
D'tor, declared and defined for noexcept.
Definition: pi335Ctrl.hpp:96
friend class pi335Ctrl_test
Definition: pi335Ctrl.hpp:40
int home_1()
Begin homing (ATZ) axis 1.
Definition: pi335Ctrl.hpp:972
int move_3(float absPos)
Send command to device to move axis 3.
Definition: pi335Ctrl.hpp:1507
std::string m_stage
The stage connected.
Definition: pi335Ctrl.hpp:62
int getCom(std::string &resp, const std::string &com, int axis)
Definition: pi335Ctrl.hpp:1365
virtual void setupConfig()
Definition: pi335Ctrl.hpp:271
float m_max3
The maximum value for axis 3.
Definition: pi335Ctrl.hpp:77
int setCom(const std::string &com, int axis, const std::string &arg)
int homeState(int axis)
Get the status of homing on an axiz.
Definition: pi335Ctrl.hpp:945
int releaseDM()
Release the DM, making it safe to turn off power.
Definition: pi335Ctrl.hpp:1320
int home_3()
Begin homing (ATZ) axis 3.
Definition: pi335Ctrl.hpp:1041
int updateFlat(float absPos1, float absPos2, float absPos3)
Update the flat command and propagate.
Definition: pi335Ctrl.hpp:1439
float m_min2
The minimum value for axis 2.
Definition: pi335Ctrl.hpp:73
float m_homePos2
Home position of axis 2. Default is 17.5.
Definition: pi335Ctrl.hpp:55
float m_min1
The minimum value for axis 1.
Definition: pi335Ctrl.hpp:70
std::string m_ctrl
The controller connected.
Definition: pi335Ctrl.hpp:61
int home_2()
Begin homing (ATZ) axis 2.
Definition: pi335Ctrl.hpp:1007
pi335Ctrl()
Default c'tor.
Definition: pi335Ctrl.hpp:264
int move_1(float absPos)
Send command to device to move axis 1.
Definition: pi335Ctrl.hpp:1458
float m_min3
The minimum value for axis 3.
Definition: pi335Ctrl.hpp:76
float m_homePos3
Home position of axis 2. Default is 17.5.
Definition: pi335Ctrl.hpp:56
virtual int appLogic()
Implementation of the FSM for pi335Ctrl.
Definition: pi335Ctrl.hpp:355
int zeroDM()
Zero all commands on the DM.
Definition: pi335Ctrl.hpp:1288
float m_max2
The maximum value for axis 2.
Definition: pi335Ctrl.hpp:74
virtual int appStartup()
Startup function.
Definition: pi335Ctrl.hpp:318
pcf::IndiProperty m_indiP_pos2
Definition: pi335Ctrl.hpp:255
int setCom(const std::string &com, int axis)
pcf::IndiProperty m_indiP_pos1
Definition: pi335Ctrl.hpp:254
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: pi335Ctrl.hpp:286
int move_2(float absPos)
Send command to device to move axis 2.
Definition: pi335Ctrl.hpp:1483
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:229
@ 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
@ NOTHOMED
The device has not been homed.
Definition: stateCodes.hpp:48
@ HOMING
The device is homing.
Definition: stateCodes.hpp:49
@ 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
@ 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
@ UNINITIALIZED
The application is unitialized, the default.
Definition: stateCodes.hpp:39
@ 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
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
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_BUSY
Definition: indiUtils.hpp:30
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
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_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
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
Software CRITICAL log entry.
Software ERR log entry.
A simple text log, a string-type log.
Definition: text_log.hpp:24
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