API
kcubeCtrl.hpp
Go to the documentation of this file.
1 /** \file kcubeCtrl.hpp
2  * \brief The MagAO-X K-Cube Controller header file
3  *
4  * \ingroup kcubeCtrl_files
5  */
6 
7 #ifndef kcubeCtrl_hpp
8 #define kcubeCtrl_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 #include "tmcController.hpp"
14 
15 /** \defgroup kcubeCtrl
16  * \brief The K-Cube Controller application
17  *
18  * <a href="../handbook/operating/software/apps/kcubeCtrl.html">Application Documentation</a>
19  *
20  * \ingroup apps
21  *
22  */
23 
24 /** \defgroup kcubeCtrl_files
25  * \ingroup kcubeCtrl
26  */
27 
28 namespace MagAOX
29 {
30 namespace app
31 {
32 
33 /// Local derivation of tmcController to implement MagAO-X logging
34 template<class parentT>
35 class tmcCon : public tmcController
36 {
37 public:
38 
39  /// Print a message to MagAO-X logs describing an error from an \libftdi1 function
40  /** Intended to be overriden in a derived class to provide custom error messaging.
41  */
42  virtual void ftdiErrmsg( const std::string & src, ///< [in] The source of the error (the tmcController function)
43  const std::string & msg, ///< [in] The message describing the error
44  int rv, ///< [in] The return value of the \libftdi1 function
45  const std::string & file, ///< [in] The file name of this file
46  int line ///< [in] The line number at which the error was recorded
47  )
48  {
49  std::stringstream logs;
50  logs << src << ": " << msg << " [ libftdi1: " << ftdi_get_error_string(m_ftdi) << " ] ";
51  uint32_t ln = line; //avoid narrowing warning
52  parentT::template log<software_error>({file.c_str(), ln, 0, rv, logs.str()});
53  }
54 
55  /// Print a message to MagAO-X logs describing an error
56  /** Intended to be overriden in a derived class to provide custom error messaging.
57  */
58  virtual void otherErrmsg( const std::string & src, ///< [in] The source of the error (the tmcController function)
59  const std::string & msg, ///< [in] The message describing the error
60  const std::string & file, ///< [in] The file name of this file
61  int line ///< [in] The line number at which the error was recorded
62  )
63  {
64  uint32_t ln = line; //avoid narrowing warning
65  parentT::template log<software_error>({file.c_str(), ln, src + ": " + msg});
66  }
67 
68 };
69 
70 /// The MagAO-X K-Cube Controller
71 /**
72  * \ingroup kcubeCtrl
73  */
74 class kcubeCtrl : public MagAOXApp<true>
75 {
76 
77  ///\todo needs telems
78 
79  // Give the test harness access.
80  friend class kcubeCtrl_test;
81 
82 protected:
83  /** \name Configurable Parameters
84  *@{
85  */
86 
87  // here add parameters which will be config-able at runtime
88 
89  ///@}
90 
92  bool m_axis1Enabled {false};
93 
95  bool m_axis2Enabled {false};
96 
97  bool m_isSet {false};
98 
99 public:
100 
101  /// Default c'tor.
102  kcubeCtrl();
103 
104  /// D'tor, declared and defined for noexcept.
105  ~kcubeCtrl() noexcept
106  {
107  }
108 
109  virtual void setupConfig();
110 
111  /// Implementation of loadConfig logic, separated for testing.
112  /** This is called by loadConfig().
113  */
114  int loadConfigImpl(mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/);
115 
116  virtual void loadConfig();
117 
118  /// Startup function
119  /**
120  *
121  */
122  virtual int appStartup();
123 
124  /// Implementation of the FSM for kcubeCtrl.
125  /**
126  * \returns 0 on no critical error
127  * \returns -1 on an error requiring shutdown
128  */
129  virtual int appLogic();
130 
131  /// Shutdown the app.
132  /**
133  *
134  */
135  virtual int appShutdown();
136 
137  /** \name K-cube Interface
138  *
139  * @{
140  */
141  int modIdentify(){return 0;}
142 
143  ///@}
144 
145  int axis1Initialize();
146 
147  int axis1Enable();
148 
149  int axis1Disable();
150 
151  int axis1Voltage(float & v);
152 
153  int axis2Initialize();
154 
155  int axis2Enable();
156 
157  int axis2Disable();
158 
159  int axis2Voltage(float & v);
160 
161  int set();
162 
163  int rest();
164 
165  /** \name INDI
166  *
167  * @{
168  */
169 
170  pcf::IndiProperty m_indiP_axis1_identify;
172 
173  pcf::IndiProperty m_indiP_axis1_enable;
175 
176  pcf::IndiProperty m_indiP_axis1_voltage;
178 
179  pcf::IndiProperty m_indiP_axis2_identify;
181 
182  pcf::IndiProperty m_indiP_axis2_enable;
184 
185  pcf::IndiProperty m_indiP_axis2_voltage;
187 
188  pcf::IndiProperty m_indiP_set;
190 
191  ///@}
192 };
193 
194 kcubeCtrl::kcubeCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
195 {
196  m_powerMgtEnabled = true;
197  return;
198 }
199 
201 {
202  config.add("axis1.serial", "", "axis1.serial", argType::Required, "axis1", "serial", false, "string", "USB serial number");
203  config.add("axis2.serial", "", "axis2.serial", argType::Required, "axis2", "serial", false, "string", "USB serial number");
204 }
205 
206 int kcubeCtrl::loadConfigImpl(mx::app::appConfigurator &_config)
207 {
208  std::string ser = m_kAxis1.serial();
209  _config(ser, "axis1.serial");
210  m_kAxis1.serial(ser);
211 
212  ser = m_kAxis2.serial();
213  _config(ser, "axis2.serial");
214  m_kAxis2.serial(ser);
215 
216  return 0;
217 }
218 
220 {
221  loadConfigImpl(config);
222 }
223 
225 {
228  {
229  log<software_error>({__FILE__,__LINE__ - 2});
230  return -1;
231  }
232 
235  {
236  log<software_error>({__FILE__,__LINE__ - 2});
237  return -1;
238  }
239  ///\todo if format is "" this crashes INDI startup... wtf
240  createStandardIndiNumber<float>( m_indiP_axis1_voltage, "axis1_voltage", 0, 150, 1.0/32767, "%0.4f");
242  {
243  log<software_error>({__FILE__,__LINE__ - 2});
244  return -1;
245  }
246  m_indiP_axis1_voltage["current"]=0;
247  m_indiP_axis1_voltage["target"]=0;
248 
251  {
252  log<software_error>({__FILE__,__LINE__ - 2});
253  return -1;
254  }
255 
258  {
259  log<software_error>({__FILE__,__LINE__ - 2});
260  return -1;
261  }
262 
263  createStandardIndiNumber<float>( m_indiP_axis2_voltage, "axis2_voltage",0,150,1.0/32767, "%0.4f");
265  {
266  log<software_error>({__FILE__,__LINE__ - 2});
267  return -1;
268  }
269  m_indiP_axis2_voltage["current"]=0;
270  m_indiP_axis2_voltage["target"]=0;
271 
274  {
275  log<software_error>({__FILE__,__LINE__ - 2});
276  return -1;
277  }
278 
280 
281  return 0;
282 }
283 
285 {
287  {
288  int rv1;
289  {
290  elevatedPrivileges elPriv(this);
291  rv1 = m_kAxis1.open(false);
292  }
293 
294  int rv2;
295  {
296  elevatedPrivileges elPriv(this);
297  rv2 = m_kAxis2.open(false);
298  }
299 
300  if(rv1 == 0 && rv2 == 0)
301  {
302  if(!stateLogged())
303  {
304  std::stringstream logs1;
305  logs1 << "Axis-1 USB Device " << m_kAxis1.vendor() << ":" << m_kAxis1.product() << ":";
306  logs1 << m_kAxis1.serial() << " found";
307  log<text_log>(logs1.str());
308 
309  std::stringstream logs2;
310  logs2 << "Axis-2 USB Device " << m_kAxis2.vendor() << ":" << m_kAxis2.product() << ":";
311  logs2 << m_kAxis2.serial() << " found";
312  log<text_log>(logs2.str());
313  }
314 
316  }
317  else if(rv1 == -3 && rv2 == -3)
318  {
320  return 0;
321  }
322  else
323  {
324  if(rv1 == 0)
325  {
326  if(!stateLogged())
327  {
328  std::stringstream logs1;
329  logs1 << "Axis-1 USB Device " << m_kAxis1.vendor() << ":" << m_kAxis1.product() << ":";
330  logs1 << m_kAxis1.serial() << " found";
331  log<text_log>(logs1.str());
332  }
333  }
334 
335  if(rv1 != 0)
336  {
337  log<software_error>({__FILE__, __LINE__, 0, rv1, "axis1 tmcController::open failed. "});
338  }
339 
340  if(rv2 == 0)
341  {
342  if(!stateLogged())
343  {
344  std::stringstream logs2;
345  logs2 << "Axis-2 USB Device " << m_kAxis2.vendor() << ":" << m_kAxis2.product() << ":";
346  logs2 << m_kAxis2.serial() << " found";
347  log<text_log>(logs2.str());
348  }
349  }
350 
351  if(rv2 != 0)
352  {
353  log<software_error>({__FILE__, __LINE__, 0, rv1, "axis 2 tmcController::open failed. "});
354  }
355 
357  return 0;
358  }
359  }
360 
362  {
363  std::lock_guard<std::mutex> guard(m_indiMutex);
364 
365  int rv;
366  {
367  elevatedPrivileges elPriv(this);
368  rv = m_kAxis1.connect();
369  }
370 
371  if(rv < 0)
372  {
373  //if connect failed, and there is a device, then we have some other problem.
374  sleep(1); //wait to see if power state updates
375  if(m_powerState == 0) return -1;
376 
377  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::connect failed. "});
378  std::cerr << "tmcController::connectFailed\n";
380  return 0;
381  }
382 
383  {
384  elevatedPrivileges elPriv(this);
385  rv = m_kAxis2.connect();
386  }
387 
388  if(rv < 0)
389  {
390  //if connect failed, and there is a device, then we have some other problem.
391  sleep(1); //wait to see if power state updates
392  if(m_powerState == 0) return -1;
393 
394  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::connect failed. "});
395 
397  return 0;
398  }
399 
401  }
402 
404  {
405  std::lock_guard<std::mutex> guard(m_indiMutex);
406 
407  if(axis1Initialize() < 0)
408  {
409  return log<software_error, -1>({__FILE__,__LINE__, "error during axis 1 initialization"});
410  }
411 
412  if(axis2Initialize() < 0)
413  {
414  return log<software_error, -1>({__FILE__,__LINE__, "error during axis 2 initialization"});
415  }
416 
418  }
419 
421  {
422  //We try_to_lock at each step, to let it go in case an actual command is waiting
423 
424  //Update Voltage Axis 1
425  {
426  std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
427  if(!lock.owns_lock()) return 0;
428 
429  float ov;
430  int rv = m_kAxis1.pz_req_outputvolts(ov);
431  if(rv < 0)
432  {
433  sleep(1); //wait to see if power state updates
434  if(m_powerState == 0) return -1;
435 
436  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::pz_req_outputvolts failed. "});
438  return 0;
439  }
440 
441  ov *= 150.0;
442 
443  if(m_axis1Enabled)
444  {
446  }
447  else
448  {
450  }
451 
452  }
453 
454  //Update Voltage Axis 2
455  {
456  std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
457 
458  //but don't wait for it, just go back around.
459  if(!lock.owns_lock()) return 0;
460 
461  float ov;
462  int rv = m_kAxis2.pz_req_outputvolts(ov);
463  if(rv < 0)
464  {
465  sleep(1); //wait to see if power state updates
466  if(m_powerState == 0) return -1;
467 
468  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::pz_req_outputvolts failed. "});
470  return 0;
471  }
472 
473  ov *= 150.0;
474 
475  if(m_axis2Enabled)
476  {
478  }
479  else
480  {
482  }
483  }
484 
485 
486  //Update the Properties that don't need to talk to device here
487  {
488  std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
489  if(!lock.owns_lock()) return 0;
490 
491  if(m_axis1Enabled)
492  {
493  updateSwitchIfChanged(m_indiP_axis1_enable, "toggle", pcf::IndiElement::On, INDI_OK);
494  }
495  else
496  {
497  updateSwitchIfChanged(m_indiP_axis1_enable, "toggle", pcf::IndiElement::Off, INDI_IDLE);
498  }
499 
500  if(m_axis2Enabled)
501  {
502  updateSwitchIfChanged(m_indiP_axis2_enable, "toggle", pcf::IndiElement::On, INDI_OK);
503  }
504  else
505  {
506  updateSwitchIfChanged(m_indiP_axis2_enable, "toggle", pcf::IndiElement::Off, INDI_IDLE);
507  }
508 
510  {
512  }
513  else
514  {
516  }
517 
518  if(m_isSet)
519  {
520  updateSwitchIfChanged(m_indiP_set, "toggle", pcf::IndiElement::On, INDI_OK);
521  }
522  else
523  {
524  updateSwitchIfChanged(m_indiP_set, "toggle", pcf::IndiElement::Off, INDI_IDLE);
525  }
526 
527  updateSwitchIfChanged(m_indiP_axis1_identify, "request", pcf::IndiElement::Off, INDI_IDLE);
528  updateSwitchIfChanged(m_indiP_axis2_identify, "request", pcf::IndiElement::Off, INDI_IDLE);
529  }
530 
531  } //READY || OPERATING
532 
533  return 0;
534 }
535 
537 {
538  return 0;
539 }
540 
542 {
543  int rv;
544 
545  tmcController::HWInfo hwi;
546 
547  rv = m_kAxis1.hw_req_info(hwi);
548  if( rv < 0)
549  {
550  sleep(1); //wait to see if power state updates
551  if(m_powerState == 0) return -1;
552 
553  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::hw_req_info failed. "});
554  return -1;
555  }
556 
557  std::stringstream logs1;
558  logs1 << "Axis-1 ";
559  hwi.dump(logs1);
560  log<text_log>(logs1.str());
561 
562  rv = m_kAxis1.mod_set_chanenablestate(0x01, tmcController::EnableState::disabled);
563  if(rv < 0)
564  {
565  sleep(1); //wait to see if power state updates
566  if(m_powerState == 0) return -1;
567 
568  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::mod_set_chanenablestate failed. "});
569  return -1;
570  }
571  m_axis1Enabled = false;
572  m_isSet = false;
573 
574  //Setup the user interface
575  rv = m_kAxis1.hw_stop_updatemsgs();
576  if(rv < 0)
577  {
578  sleep(1); //wait to see if power state updates
579  if(m_powerState == 0) return -1;
580 
581  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::hw_stop_updatemsgs failed. "});
582  return -1;
583  }
584 
585  tmcController::KMMIParams par;
586  rv = m_kAxis1.kpz_req_kcubemmiparams(par);
587  if(rv < 0)
588  {
589  sleep(1); //wait to see if power state updates
590  if(m_powerState == 0) return -1;
591 
592  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::kpz_req_kcubemmiparams failed. "});
593  return -1;
594  }
595 
596  par.DispBrightness = 0;
597 
598  rv = m_kAxis1.kpz_set_kcubemmiparams(par);
599  if(rv < 0)
600  {
601  sleep(1); //wait to see if power state updates
602  if(m_powerState == 0) return -1;
603 
604  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::kpz_set_kcubemmiparams failed. "});
605  return -1;
606  }
607 
608  rv = m_kAxis1.kpz_req_kcubemmiparams(par);
609  if(rv < 0)
610  {
611  sleep(1); //wait to see if power state updates
612  if(m_powerState == 0) return -1;
613 
614  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::kpz_req_kcubemmiparams failed. "});
615  return -1;
616  }
617 
618  logs1.str("");
619  logs1 << "Axis-1 ";
620  par.dump(logs1);
621  log<text_log>(logs1.str());
622 
623  //Get and set TPZ IO Settings, setting limit to 150 V
624  //First reads current settings, and only updates the 150 V limit.
625  tmcController::TPZIOSettings tios;
626  rv = m_kAxis1.pz_req_tpz_iosettings(tios);
627  if(rv < 0)
628  {
629  sleep(1); //wait to see if power state updates
630  if(m_powerState == 0) return -1;
631 
632  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::pz_req_tpz_iosettings failed. "});
633  return -1;
634  }
635  tios.VoltageLimit = tmcController::VoltLimit::V150;
636 
637  rv = m_kAxis1.pz_set_tpz_iosettings(tios);
638  if(rv < 0)
639  {
640  sleep(1); //wait to see if power state updates
641  if(m_powerState == 0) return -1;
642 
643  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::pz_set_tpz_iosettings failed. "});
644  return -1;
645  }
646 
647  rv = m_kAxis1.pz_req_tpz_iosettings(tios);
648  if(rv < 0)
649  {
650  sleep(1); //wait to see if power state updates
651  if(m_powerState == 0) return -1;
652 
653  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::pz_req_tpz_iosettings failed. "});
654  return -1;
655  }
656 
657  logs1.str("");
658  logs1 << "Axis-1 ";
659  tios.dump(logs1);
660  log<text_log>(logs1.str());
661 
662  return 0;
663 }
664 
666 {
667  int rv = m_kAxis1.mod_set_chanenablestate(0x01, tmcController::EnableState::enabled);
668  if(rv < 0)
669  {
670  sleep(1); //wait to see if power state updates
671  if(m_powerState == 0) return -1;
672 
673  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::mod_set_chanenablestate failed. "});
674  return -1;
675  }
676 
677  m_axis1Enabled = true;
678 
679  log<text_log>("enabled axis 1 piezo", logPrio::LOG_NOTICE);
680 
681  return 0;
682 }
683 
685 {
686  int rv = m_kAxis1.mod_set_chanenablestate(0x01, tmcController::EnableState::disabled);
687  if(rv < 0)
688  {
689  sleep(1); //wait to see if power state updates
690  if(m_powerState == 0) return -1;
691 
692  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::mod_set_chanenablestate failed. "});
693  return -1;
694  }
695 
696  m_axis1Enabled = false;
697  m_isSet = false;
698 
699  log<text_log>("disabled axis 1 piezo", logPrio::LOG_NOTICE);
700 
701  return 0;
702 }
703 
705 {
706  if(v < 0)
707  {
708  log<text_log>("axis 1 voltage clamped at 0 (" + std::to_string(v) + ")", logPrio::LOG_WARNING);
709  v = 0;
710  }
711  else if(v > 150)
712  {
713  log<text_log>("axis 1 voltage clamped at 150 (" + std::to_string(v) + ")", logPrio::LOG_WARNING);
714  v = 150;
715  }
716 
717  int rv = m_kAxis1.pz_set_outputvolts(v/150.0);
718  if(rv < 0)
719  {
720  sleep(1); //wait to see if power state updates
721  if(m_powerState == 0) return -1;
722 
723  log<software_error>({__FILE__, __LINE__, 0, rv, "axis1 tmcController::pz_set_outputvolts failed. "});
724  return -1;
725  }
726 
727  return 0;
728 
729 }
730 
732 {
733  int rv;
734 
735  tmcController::HWInfo hwi;
736 
737  rv = m_kAxis2.hw_req_info(hwi);
738  if( rv < 0)
739  {
740  sleep(1); //wait to see if power state updates
741  if(m_powerState == 0) return -1;
742 
743  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::hw_req_info failed. "});
744  return -1;
745  }
746 
747  std::stringstream logs1;
748  logs1 << "Axis-2 ";
749  hwi.dump(logs1);
750  log<text_log>(logs1.str());
751 
752  rv = m_kAxis2.mod_set_chanenablestate(0x01, tmcController::EnableState::disabled);
753  if(rv < 0)
754  {
755  sleep(1); //wait to see if power state updates
756  if(m_powerState == 0) return -1;
757 
758  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::mod_set_chanenablestate failed. "});
759  return -1;
760  }
761  m_axis2Enabled = false;
762  m_isSet = false;
763 
764  //Setup the user interface
765  rv = m_kAxis2.hw_stop_updatemsgs();
766  if(rv < 0)
767  {
768  sleep(1); //wait to see if power state updates
769  if(m_powerState == 0) return -1;
770 
771  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::hw_stop_updatemsgs failed. "});
772  return -1;
773  }
774 
775  tmcController::KMMIParams par;
776  rv = m_kAxis2.kpz_req_kcubemmiparams(par);
777  if(rv < 0)
778  {
779  sleep(1); //wait to see if power state updates
780  if(m_powerState == 0) return -1;
781 
782  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::kpz_req_kcubemmiparams failed. "});
783  return -1;
784  }
785 
786  par.DispBrightness = 0;
787 
788  rv = m_kAxis2.kpz_set_kcubemmiparams(par);
789  if(rv < 0)
790  {
791  sleep(1); //wait to see if power state updates
792  if(m_powerState == 0) return -1;
793 
794 
795  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::kpz_set_kcubemmiparams failed. "});
796  return -1;
797  }
798 
799  rv = m_kAxis2.kpz_req_kcubemmiparams(par);
800  if(rv < 0)
801  {
802  sleep(1); //wait to see if power state updates
803  if(m_powerState == 0) return -1;
804 
805  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::kpz_req_kcubemmiparams failed. "});
806  return -1;
807  }
808 
809  logs1.str("");
810  logs1 << "Axis-2 ";
811  par.dump(logs1);
812  log<text_log>(logs1.str());
813 
814  //Get and set TPZ IO Settings, setting limit to 150 V
815  //First reads current settings, and only updates the 150 V limit.
816  tmcController::TPZIOSettings tios;
817  rv = m_kAxis2.pz_req_tpz_iosettings(tios);
818  if(rv < 0)
819  {
820  sleep(1); //wait to see if power state updates
821  if(m_powerState == 0) return -1;
822 
823  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::pz_req_tpz_iosettings failed. "});
824  return -1;
825  }
826  tios.VoltageLimit = tmcController::VoltLimit::V150;
827 
828  rv = m_kAxis2.pz_set_tpz_iosettings(tios);
829  if(rv < 0)
830  {
831  sleep(1); //wait to see if power state updates
832  if(m_powerState == 0) return -1;
833 
834  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::pz_set_tpz_iosettings failed. "});
835  return -1;
836  }
837 
838  rv = m_kAxis2.pz_req_tpz_iosettings(tios);
839  if(rv < 0)
840  {
841  sleep(1); //wait to see if power state updates
842  if(m_powerState == 0) return -1;
843 
844  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::pz_req_tpz_iosettings failed. "});
845  return -1;
846  }
847 
848  logs1.str("");
849  logs1 << "Axis-2 ";
850  tios.dump(logs1);
851  log<text_log>(logs1.str());
852 
853  return 0;
854 }
855 
857 {
858  int rv = m_kAxis2.mod_set_chanenablestate(0x01, tmcController::EnableState::enabled);
859  if(rv < 0)
860  {
861  sleep(1); //wait to see if power state updates
862  if(m_powerState == 0) return -1;
863 
864  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::mod_set_chanenablestate failed. "});
865  return -1;
866  }
867 
868  m_axis2Enabled = true;
869 
870  log<text_log>("enabled axis 2 piezo", logPrio::LOG_NOTICE);
871 
872  return 0;
873 }
874 
876 {
877  int rv = m_kAxis2.mod_set_chanenablestate(0x01, tmcController::EnableState::disabled);
878  if(rv < 0)
879  {
880  sleep(1); //wait to see if power state updates
881  if(m_powerState == 0) return -1;
882 
883  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::mod_set_chanenablestate failed. "});
884  return -1;
885  }
886 
887  m_axis2Enabled = false;
888  m_isSet = false;
889 
890  log<text_log>("disabled axis 2 piezo", logPrio::LOG_NOTICE);
891 
892  return 0;
893 }
894 
896 {
897  if(v < 0)
898  {
899  log<text_log>("axis 2 voltage clamped at 0 (" + std::to_string(v) + ")", logPrio::LOG_WARNING);
900  v = 0;
901  }
902  else if(v > 150)
903  {
904  log<text_log>("axis 2 voltage clamped at 150 (" + std::to_string(v) + ")", logPrio::LOG_WARNING);
905  v = 150;
906  }
907 
908  int rv = m_kAxis2.pz_set_outputvolts(v/150.0);
909  if(rv < 0)
910  {
911  sleep(1); //wait to see if power state updates
912  if(m_powerState == 0) return -1;
913 
914  log<software_error>({__FILE__, __LINE__, 0, rv, "axis 2 tmcController::pz_set_outputvolts failed. "});
915  return -1;
916  }
917 
918  return 0;
919 }
920 
922 {
923  if(m_isSet) return 0;
924 
925  float v = 75.0;
926 
927  if(axis1Enable() < 0)
928  {
929  sleep(1); //wait to see if power state updates
930  if(m_powerState == 0) return -1;
931 
932  return log<software_error,-1>({__FILE__, __LINE__, "axis 1 enable error in set"});
933  }
934 
935  if(axis1Voltage(v) < 0)
936  {
937  sleep(1); //wait to see if power state updates
938  if(m_powerState == 0) return -1;
939 
940  return log<software_error,-1>({__FILE__, __LINE__, "axis 1 enable voltage in set"});
941  }
942 
943  if(axis2Enable() < 0)
944  {
945  sleep(1); //wait to see if power state updates
946  if(m_powerState == 0) return -1;
947 
948  return log<software_error,-1>({__FILE__, __LINE__, "axis 2 enable error in set"});
949  }
950 
951  if(axis2Voltage(v) < 0)
952  {
953  sleep(1); //wait to see if power state updates
954  if(m_powerState == 0) return -1;
955 
956  return log<software_error,-1>({__FILE__, __LINE__, "axis 2 enable voltage in set"});
957  }
958 
959  m_isSet = true;
960 
961  log<text_log>("set", logPrio::LOG_NOTICE);
962 
963  return 0;
964 }
965 
967 {
968  if(!m_isSet) return 0;
969 
970  float v = 0.0;
971 
972  if(axis1Voltage(v) < 0)
973  {
974  sleep(1); //wait to see if power state updates
975  if(m_powerState == 0) return -1;
976 
977  return log<software_error,-1>({__FILE__, __LINE__, "axis 1 enable voltage in rest"});
978  }
979 
980  if(axis1Disable() < 0)
981  {
982  sleep(1); //wait to see if power state updates
983  if(m_powerState == 0) return -1;
984 
985  return log<software_error,-1>({__FILE__, __LINE__, "axis 1 disable error in rest"});
986  }
987 
988  if(axis2Voltage(v) < 0)
989  {
990  sleep(1); //wait to see if power state updates
991  if(m_powerState == 0) return -1;
992 
993  return log<software_error,-1>({__FILE__, __LINE__, "axis 2 enable voltage in rest"});
994  }
995 
996  if(axis2Disable() < 0)
997  {
998  sleep(1); //wait to see if power state updates
999  if(m_powerState == 0) return -1;
1000 
1001  return log<software_error,-1>({__FILE__, __LINE__, "axis 2 disable error in rest"});
1002  }
1003 
1004  m_isSet = false;
1005 
1006  log<text_log>("rested", logPrio::LOG_NOTICE);
1007 
1008  return 0;
1009 }
1010 
1011 INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_axis1_identify)(const pcf::IndiProperty &ipRecv)
1012 {
1013  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_axis1_identify, ipRecv);
1014 
1015  if(state() != stateCodes::READY) return 0;
1016 
1017  //switch is toggled to on
1018  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1019  {
1020  std::lock_guard<std::mutex> guard(m_indiMutex);
1021  updateSwitchIfChanged(m_indiP_axis1_identify, "request", pcf::IndiElement::On, INDI_BUSY);
1022  return m_kAxis1.mod_identify();
1023  }
1024 
1025  return 0;
1026 }
1027 
1028 INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_axis1_enable)(const pcf::IndiProperty &ipRecv)
1029 {
1030  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_axis1_enable, ipRecv);
1031 
1032  if(!(state() == stateCodes::READY || state() == stateCodes::OPERATING) ) return 0;
1033 
1034  //switch is toggled to on
1035  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
1036  {
1037  std::lock_guard<std::mutex> guard(m_indiMutex);
1038  if(axis1Enable() < 0)
1039  {
1040  if(m_powerState == 0) return 0;
1041  return log<software_error,-1>({__FILE__, __LINE__, "axis 1 enable error in INDI callback"});
1042  }
1043  }
1044  else
1045  {
1046  std::lock_guard<std::mutex> guard(m_indiMutex);
1047  if(axis1Disable() < 0)
1048  {
1049  if(m_powerState == 0) return 0;
1050  log<software_error,-1>({__FILE__, __LINE__, "axis 1 disable error in INDI callback"});
1051  }
1052  }
1053 
1054  return 0;
1055 }
1056 
1057 
1058 INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_axis1_voltage)(const pcf::IndiProperty &ipRecv)
1059 {
1060  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_axis1_voltage, ipRecv);
1061 
1062  if(!(state() == stateCodes::READY || state() == stateCodes::OPERATING) ) return 0;
1063 
1064  float target;
1065  indiTargetUpdate(m_indiP_axis1_voltage, target, ipRecv, true);
1066 
1067  std::lock_guard<std::mutex> guard(m_indiMutex);
1068  if(axis1Voltage(target) < 0)
1069  {
1070  if(m_powerState == 0) return 0;
1071  return log<software_error,-1>({__FILE__, __LINE__, "axis 1 voltage error in INDI callback"});
1072  }
1073 
1074  return 0;
1075 }
1076 
1077 INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_axis2_identify)(const pcf::IndiProperty &ipRecv)
1078 {
1079  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_axis2_identify, ipRecv);
1080 
1081  if(state() != stateCodes::READY) return 0;
1082 
1083  //switch is toggled to on
1084  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1085  {
1086  std::lock_guard<std::mutex> guard(m_indiMutex);
1087  updateSwitchIfChanged(m_indiP_axis2_identify, "request", pcf::IndiElement::On, INDI_BUSY);
1088  return m_kAxis2.mod_identify();
1089  }
1090 
1091  return 0;
1092 }
1093 
1094 INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_axis2_enable)(const pcf::IndiProperty &ipRecv)
1095 {
1096  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_axis2_enable, ipRecv);
1097 
1098  if(!(state() == stateCodes::READY || state() == stateCodes::OPERATING) ) return 0;
1099 
1100  //switch is toggled to on
1101  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
1102  {
1103  std::lock_guard<std::mutex> guard(m_indiMutex);
1104  if(axis2Enable() < 0)
1105  {
1106  if(m_powerState == 0) return 0;
1107  return log<software_error,-1>({__FILE__, __LINE__, "axis 2 enable error in INDI callback"});
1108  }
1109  }
1110  else
1111  {
1112  std::lock_guard<std::mutex> guard(m_indiMutex);
1113  if(axis2Disable() < 0)
1114  {
1115  if(m_powerState == 0) return 0;
1116  return log<software_error,-1>({__FILE__, __LINE__, "axis 2 disable error in INDI callback"});
1117  }
1118  }
1119 
1120  return 0;
1121 }
1122 
1123 INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_axis2_voltage)(const pcf::IndiProperty &ipRecv)
1124 {
1125  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_axis2_voltage, ipRecv);
1126 
1127  if(!(state() == stateCodes::READY || state() == stateCodes::OPERATING) ) return 0;
1128 
1129  float target;
1130  indiTargetUpdate(m_indiP_axis2_voltage, target, ipRecv, true);
1131 
1132  std::lock_guard<std::mutex> guard(m_indiMutex);
1133  if(axis2Voltage(target) < 0)
1134  {
1135  if(m_powerState == 0) return 0;
1136  return log<software_error,-1>({__FILE__, __LINE__, "axis 2 voltage error in INDI callback"});
1137  }
1138 
1139  return 0;
1140 }
1141 
1142 INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_set)(const pcf::IndiProperty &ipRecv)
1143 {
1144  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_set, ipRecv);
1145 
1146  if(!(state() == stateCodes::READY || state() == stateCodes::OPERATING) ) return 0;
1147 
1148  //switch is toggled to on
1149  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
1150  {
1151  std::lock_guard<std::mutex> guard(m_indiMutex);
1152  if(!m_isSet)
1153  {
1154  //Set it to busy if we think this is a state change
1155  updateSwitchIfChanged(m_indiP_set, "toggle", pcf::IndiElement::On, INDI_BUSY);
1156  }
1157  //--else: if already set we probably don't need to call set(), but do it anyway to be sure
1158 
1159  if(set() < 0)
1160  {
1161  if(m_powerState == 0) return 0;
1162  return log<software_error,-1>({__FILE__, __LINE__, "set error in INDI callback"});
1163  }
1164  }
1165  else
1166  {
1167  std::lock_guard<std::mutex> guard(m_indiMutex);
1168  if(m_isSet)
1169  {
1170  //Set it to busy if we think this is a state change
1171  updateSwitchIfChanged(m_indiP_set, "toggle", pcf::IndiElement::Off, INDI_BUSY);
1172  }
1173  //--else: if already rested we probably don't need to call rest(), but do it anyway to be sure
1174 
1175  if(rest() < 0)
1176  {
1177  if(m_powerState == 0) return 0;
1178  return log<software_error,-1>({__FILE__, __LINE__, "rest error in INDI callback"});
1179  }
1180  }
1181 
1182  return 0;
1183 }
1184 
1185 } // namespace app
1186 } // namespace MagAOX
1187 
1188 #endif // kcubeCtrl_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
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
Definition: MagAOXApp.hpp:2573
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
Definition: MagAOXApp.hpp:2543
int m_powerState
Current power state, 1=On, 0=Off, -1=Unk.
Definition: MagAOXApp.hpp:1033
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
Definition: MagAOXApp.hpp:3144
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2361
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:545
The MagAO-X K-Cube Controller.
Definition: kcubeCtrl.hpp:75
kcubeCtrl()
Default c'tor.
Definition: kcubeCtrl.hpp:194
pcf::IndiProperty m_indiP_axis2_identify
Definition: kcubeCtrl.hpp:179
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis1_enable)
virtual int appShutdown()
Shutdown the app.
Definition: kcubeCtrl.hpp:536
pcf::IndiProperty m_indiP_axis1_enable
Definition: kcubeCtrl.hpp:173
pcf::IndiProperty m_indiP_axis2_voltage
Definition: kcubeCtrl.hpp:185
virtual int appStartup()
Startup function.
Definition: kcubeCtrl.hpp:224
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis2_enable)
tmcCon< kcubeCtrl > m_kAxis2
Definition: kcubeCtrl.hpp:94
virtual void setupConfig()
Definition: kcubeCtrl.hpp:200
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis1_voltage)
~kcubeCtrl() noexcept
D'tor, declared and defined for noexcept.
Definition: kcubeCtrl.hpp:105
pcf::IndiProperty m_indiP_axis2_enable
Definition: kcubeCtrl.hpp:182
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_set)
pcf::IndiProperty m_indiP_axis1_voltage
Definition: kcubeCtrl.hpp:176
tmcCon< kcubeCtrl > m_kAxis1
Definition: kcubeCtrl.hpp:91
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: kcubeCtrl.hpp:206
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis2_voltage)
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis2_identify)
int axis1Voltage(float &v)
Definition: kcubeCtrl.hpp:704
pcf::IndiProperty m_indiP_set
Definition: kcubeCtrl.hpp:188
friend class kcubeCtrl_test
Definition: kcubeCtrl.hpp:80
pcf::IndiProperty m_indiP_axis1_identify
Definition: kcubeCtrl.hpp:170
virtual int appLogic()
Implementation of the FSM for kcubeCtrl.
Definition: kcubeCtrl.hpp:284
virtual void loadConfig()
Definition: kcubeCtrl.hpp:219
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis1_identify)
int axis2Voltage(float &v)
Definition: kcubeCtrl.hpp:895
Local derivation of tmcController to implement MagAO-X logging.
Definition: kcubeCtrl.hpp:36
virtual void otherErrmsg(const std::string &src, const std::string &msg, const std::string &file, int line)
Print a message to MagAO-X logs describing an error.
Definition: kcubeCtrl.hpp:58
virtual void ftdiErrmsg(const std::string &src, const std::string &msg, int rv, const std::string &file, int line)
Print a message to MagAO-X logs describing an error from an \libftdi1 function.
Definition: kcubeCtrl.hpp:42
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:208
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
@ NODEVICE
No device exists for the application to control.
Definition: stateCodes.hpp:46
@ 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
@ 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
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_BUSY
Definition: indiUtils.hpp:30
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &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:212
std::stringstream msg
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition: dm.hpp:24
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.