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