API
cacaoInterface.hpp
Go to the documentation of this file.
1 /** \file cacaoInterface.hpp
2  * \brief The MagAO-X CACAO Interface header file
3  *
4  * \ingroup cacaoInterface_files
5  */
6 
7 #ifndef cacaoInterface_hpp
8 #define cacaoInterface_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 cacaoInterface
14  * \brief The CACAO Interface to provide loop status
15  *
16  * <a href="../handbook/apps/cacaoInterface.html">Application Documentation</a>
17  *
18  * \ingroup apps
19  *
20  */
21 
22 /** \defgroup cacaoInterface_files
23  * \ingroup cacaoInterface
24  */
25 
26 namespace MagAOX
27 {
28 namespace app
29 {
30 
31 /// The MagAO-X CACAO Interface
32 /**
33  * \ingroup cacaoInterface
34  */
35 class cacaoInterface : public MagAOXApp<true>, public dev::telemeter<cacaoInterface>
36 {
37 
38  //Give the test harness access.
39  friend class cacaoInterface_test;
40 
42 
43  friend class dev::telemeter<cacaoInterface>;
44 
45 protected:
46 
47  /** \name Configurable Parameters
48  *@{
49  */
50  std::string m_loopNumber; ///< The loop number, X in aolX. We keep it a string because that's how it gets used.
51 
52  ///@}
53 
54  std::string m_aoCalDir;
55  std::string m_aoCalArchiveTime;
56  std::string m_aoCalLoadTime;
57 
58  std::string m_loopName; ///< the loop name
59 
60 
61  std::string m_fpsName;
62  std::string m_fpsFifo;
63 
64 
65  int m_loopState {0}; ///< The loop state. 0 = off, 1 = paused (on, 0 gain), 2 = on
66 
67  bool m_loopProcesses {false}; ///< Status of the loop processes.
68  bool m_loopProcesses_stat {false}; ///< What the cacao status file says the state of loop processes is.
69 
70  float m_gain {0.0}; ///< The current loop gain.
71  float m_gain_target {0.0}; ///< The target loop gain.
72 
73  float m_multCoeff {0.0}; ///< The current multiplicative coefficient (1-leak)
74  float m_multCoeff_target {0.0}; ///< The target multiplicative coefficient (1-leak)
75 
76  std::vector<int> m_modeBlockStart;
77  std::vector<int> m_modeBlockN;
78 
79  std::vector<float> m_modeBlockGains;
80  std::vector<float> m_modeBlockMCs;
81  std::vector<float> m_modeBlockLims;
82 
83  std::mutex m_modeBlockMutex;
84 
85 
86  float m_maxLim {0.0}; ///< The current max limit
87  float m_maxLim_target {0.0}; ///< The target max limit
88 
89 public:
90  /// Default c'tor.
92 
93  /// D'tor, declared and defined for noexcept.
94  ~cacaoInterface() noexcept
95  {}
96 
97  virtual void setupConfig();
98 
99  /// Implementation of loadConfig logic, separated for testing.
100  /** This is called by loadConfig().
101  */
102  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
103 
104  virtual void loadConfig();
105 
106  /// Startup function
107  /**
108  *
109  */
110  virtual int appStartup();
111 
112  /// Implementation of the FSM for cacaoInterface.
113  /**
114  * \returns 0 on no critical error
115  * \returns -1 on an error requiring shutdown
116  */
117  virtual int appLogic();
118 
119  /// Shutdown the app.
120  /**
121  *
122  */
123  virtual int appShutdown();
124 
125 
126  /** \name CACAO Interface Functions
127  * @{
128  */
129 
130  int setFPSVal( const std::string & fps,
131  const std::string & param,
132  const std::string & val
133  );
134 
135 
136  template<typename T>
137  int setFPSVal( const std::string & fps,
138  const std::string & param,
139  const T & val
140  );
141 
142  std::string getFPSValStr(const std::string & fps,
143  const std::string & param );
144 
145  std::string getFPSValNum(const std::string & fps,
146  const std::string & param );
147 
148  /// Get the calibration details
149  /** This is done each loop
150  *
151  * \returns 0 on success
152  * \returns -1 on an error
153  */
154  int getAOCalib();
155 
157 
158  /// Check if the loop processes are running
159  /** sets m_loopProcesses to true or false depending on what it finds out.
160  *
161  * \returns 0 on success
162  * \returns -1 on an error
163  */
164  int checkLoopProcesses();
165 
166  /// Set loop gain to the value of m_gain_target;
167  /**
168  *
169  * \returns 0 on success
170  * \returns -1 on an error
171  */
172  int setGain();
173 
174  /// Set loop multiplication coefficient to the value of m_multCoeff_target;
175  /**
176  *
177  * \returns 0 on success
178  * \returns -1 on an error
179  */
180  int setMultCoeff();
181 
182  /// Set loop max lim to the value of m_maxLim_target;
183  /**
184  *
185  * \returns 0 on success
186  * \returns -1 on an error
187  */
188  int setMaxLim();
189 
190  /// Turn the loop on
191  /**
192  * \returns 0 on success
193  * \returns -1 on an error
194  */
195  int loopOn();
196 
197  /// Turn the loop off
198  /**
199  * \returns 0 on success
200  * \returns -1 on an error
201  */
202  int loopOff();
203 
204  /// Zero the loop control channel
205  /**
206  * \returns 0 on success
207  * \returns -1 on an error
208  */
209  int loopZero();
210 
211  /// @}
212 
213  /** \name File Monitoring Thread
214  * Handling of offloads from the average woofer shape
215  * @{
216  */
217  int m_fmThreadPrio {0}; ///< Priority of the filemonitoring thread.
218 
219  std::thread m_fmThread; ///< The file monitoring thread.
220 
221  bool m_fmThreadInit {true}; ///< Initialization flag for the file monitoring thread.
222 
223  pid_t m_fmThreadID {0}; ///< File monitor thread PID.
224 
225  pcf::IndiProperty m_fmThreadProp; ///< The property to hold the f.m. thread details.
226 
227  /// File monitoring thread starter function
228  static void fmThreadStart( cacaoInterface * c /**< [in] pointer to this */);
229 
230 
231  /// File monitoring thread function
232  /** Runs until m_shutdown is true.
233  */
234  void fmThreadExec();
235 
236 
237  ///@}
238 
239  pcf::IndiProperty m_indiP_loop;
240  pcf::IndiProperty m_indiP_loopProcesses;
241 
242  pcf::IndiProperty m_indiP_loopState;
243  pcf::IndiProperty m_indiP_loopZero;
244  pcf::IndiProperty m_indiP_loopGain;
245  pcf::IndiProperty m_indiP_multCoeff;
246  pcf::IndiProperty m_indiP_maxLim;
247 
253 
254  /** \name Telemeter Interface
255  *
256  * @{
257  */
258  int checkRecordTimes();
259 
260  int recordTelem( const telem_loopgain * );
261 
262  int recordLoopGain( bool force = false );
263 
264  ///@}
265 
266 
267 };
268 
269 cacaoInterface::cacaoInterface() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
270 {
271 
272  return;
273 }
274 
276 {
277  config.add("loop.number", "", "loop.number", argType::Required, "loop", "number", false, "string", "the loop number");
278 
279  telemeterT::setupConfig(config);
280 }
281 
282 int cacaoInterface::loadConfigImpl( mx::app::appConfigurator & _config )
283 {
284  _config(m_loopNumber, "loop.number");
285 
286  if(telemeterT::loadConfig(_config) < 0)
287  {
288  log<text_log>("Error during telemeter config", logPrio::LOG_CRITICAL);
289  m_shutdown = true;
290  }
291 
292  return 0;
293 }
294 
296 {
297  loadConfigImpl(config);
298 }
299 
301 {
302 
303  if(m_loopNumber == "")
304  {
305  return log<software_critical, -1>({__FILE__, __LINE__, "loop number not set"});
306  }
307 
308  createROIndiText( m_indiP_loop, "loop", "name", "Loop Description", "Loop Controls", "Name");
309  indi::addTextElement(m_indiP_loop, "number", "Number");
310  m_indiP_loop["number"] = m_loopNumber;
311  m_indiP_loop["name"] = m_loopName;
313 
314  createStandardIndiToggleSw( m_indiP_loopProcesses, "loop_processes", "Loop Processes", "Loop Controls");
316 
317  createStandardIndiToggleSw( m_indiP_loopState, "loop_state", "Loop State", "Loop Controls");
319 
320  createStandardIndiRequestSw( m_indiP_loopZero, "loop_zero", "Loop Zero", "Loop Controls");
322 
323  createStandardIndiNumber<float>( m_indiP_loopGain, "loop_gain", 0.0, 10.0, 0.01, "%0.3f", "Loop Gain", "Loop Controls");
325 
326  createStandardIndiNumber<float>( m_indiP_multCoeff, "loop_multcoeff", 0.0, 1.0, 0.001, "%0.3f", "Mult. Coefficient", "Loop Controls");
328 
329  createStandardIndiNumber<float>( m_indiP_maxLim, "loop_max_limit", 0.0, 10.0, 0.001, "%0.3f", "Max. Limit", "Loop Controls");
331 
333  {
334  log<software_error>({__FILE__, __LINE__});
335  return -1;
336  }
337 
338  if(telemeterT::appStartup() < 0)
339  {
340  return log<software_error,-1>({__FILE__,__LINE__});
341  }
342 
343  return 0;
344 }
345 
347 {
348  //do a join check to see if other threads have exited.
349  if(pthread_tryjoin_np(m_fmThread.native_handle(),0) == 0)
350  {
351  log<software_critical>({__FILE__, __LINE__, "cacao file monitoring thread has exited"});
352 
353  return -1;
354  }
355 
356  //These could change if a new calibration is loaded
357  if(getAOCalib() < 0 )
358  {
359  state(stateCodes::ERROR, true);
360  if(!stateLogged()) log<text_log>("Could not get AO calib", logPrio::LOG_ERROR);
361  return 0;
362  }
363 
366 
367  if(telemeterT::appLogic() < 0)
368  {
369  log<software_error>({__FILE__, __LINE__});
370  return 0;
371  }
372 
373  std::unique_lock<std::mutex> lock(m_indiMutex);
374 
375  updateIfChanged(m_indiP_loop, std::vector<std::string>({"name", "number"}), std::vector<std::string>({m_loopName, m_loopNumber}));
376 
377  if(m_loopProcesses)
378  {
379  updateSwitchIfChanged(m_indiP_loopProcesses, "toggle", pcf::IndiElement::On, INDI_OK);
380  }
381  else
382  {
383  updateSwitchIfChanged(m_indiP_loopProcesses, "toggle", pcf::IndiElement::Off, INDI_IDLE);
384  }
385 
386  if(m_loopState == 0)
387  {
388  updateSwitchIfChanged(m_indiP_loopState, "toggle", pcf::IndiElement::Off, INDI_IDLE);
389  }
390  else if(m_loopState == 1)
391  {
392  updateSwitchIfChanged(m_indiP_loopState, "toggle", pcf::IndiElement::On, INDI_IDLE);
393  }
394  else if(m_loopState == 2)
395  {
396  updateSwitchIfChanged(m_indiP_loopState, "toggle", pcf::IndiElement::On, INDI_OK);
397  }
398 
401 
405 
406  return 0;
407 }
408 
410 {
411 
412  if(m_fmThread.joinable())
413  {
414  try
415  {
416  m_fmThread.join(); //this will throw if it was already joined
417  }
418  catch(...)
419  {
420  }
421  }
422 
424 
425  return 0;
426 }
427 
428 int cacaoInterface::setFPSVal( const std::string & fps,
429  const std::string & param,
430  const std::string & val
431  )
432 {
433  std::string comout = "setval " + fps + "-" + m_loopNumber + "." + param + " " + val + "\n";
434 
435  int wfd = open( m_fpsFifo.c_str(), O_WRONLY);
436  if(wfd < 0)
437  {
438  log<software_error>({__FILE__, __LINE__, errno, "error opening " + m_fpsFifo});
439  return -1;
440  }
441 
442  int w = write(wfd, comout.c_str(), comout.size());
443 
444  if(w != (int) comout.size())
445  {
446  log<software_error>({__FILE__, __LINE__, errno, "error on write to " + m_fpsFifo});
447  return -1;
448  }
449 
450  close(wfd);
451 
452  return 0;
453 }
454 
455 
456 template<typename T>
457 int cacaoInterface::setFPSVal( const std::string & fps,
458  const std::string & param,
459  const T & val
460  )
461 {
462  return setFPSVal( fps, param, std::to_string(val));
463 }
464 
465 std::string cacaoInterface::getFPSValStr( const std::string & fps,
466  const std::string & param
467  )
468 {
469  std::string outfile = "/dev/shm/" + m_loopName + "_out_" + fps + "-" + m_loopNumber + "." + param;
470 
471  std::string comout = "fwrval " + fps + "-" + m_loopNumber + "." + param + " " + outfile + "\n";
472 
473  int wfd = open( m_fpsFifo.c_str(), O_WRONLY);
474  if(wfd < 0)
475  {
476  log<software_error>({__FILE__, __LINE__, errno, "error opening " + m_fpsFifo});
477  return "";
478  }
479 
480  int w = write(wfd, comout.c_str(), comout.size());
481 
482  if(w != (int) comout.size())
483  {
484  log<software_error>({__FILE__, __LINE__, errno, "error on write to " + m_fpsFifo});
485  return "";
486  }
487 
488  close(wfd);
489 
490  char inbuff [4096];
491 
492  int rfd = -1;
493  int nr =0;
494  while(rfd < 0 && nr < 20)
495  {
496  rfd = open(outfile.c_str(), O_RDONLY);
497  ++nr;
498  mx::sys::milliSleep(10);
499  }
500 
501  int r = read(rfd, inbuff, sizeof(inbuff));
502 
503  if(r < 0)
504  {
505  log<software_error>({__FILE__, __LINE__, errno, "error on read from " + m_fpsFifo});
506  return "";
507  }
508 
509  close(rfd);
510 
511  remove(outfile.c_str());
512 
513  inbuff[r] = '\0';
514 
515  std::string instr = inbuff;
516 
517  size_t ned = instr.find_last_not_of(" \t\r\n");
518 
519  if(ned == std::string::npos || ned == 0)
520  {
521  log<software_error>({__FILE__, __LINE__, "got empty result from " + m_fpsFifo});
522  return "";
523  }
524 
525  size_t nst = instr.rfind(' ', ned);
526 
527  if(nst == 0 || nst == std::string::npos || ned <= nst)
528  {
529  log<software_error>({__FILE__, __LINE__, "bad format in result from " + m_fpsFifo});
530  return "";
531  }
532 
533  return instr.substr(nst+1);
534 }
535 
536 std::string cacaoInterface::getFPSValNum( const std::string & fps,
537  const std::string & param
538  )
539 {
540  std::string outfile = "/dev/shm/" + m_loopName + "_out_" + fps + "-" + m_loopNumber + "." + param;
541 
542  std::string comout = "fwrval " + fps + "-" + m_loopNumber + "." + param + " " + outfile + "\n";
543 
544  int wfd = open( m_fpsFifo.c_str(), O_WRONLY);
545  if(wfd < 0)
546  {
547  log<software_error>({__FILE__, __LINE__, errno, "error opening " + m_fpsFifo});
548  return "";
549  }
550 
551  int w = write(wfd, comout.c_str(), comout.size());
552 
553  if(w != (int) comout.size())
554  {
555  log<software_error>({__FILE__, __LINE__, errno, "error on write to " + m_fpsFifo});
556  return "";
557  }
558  close(wfd);
559 
560  char inbuff [4096];
561 
562  int rfd = -1;
563  int nr =0;
564  while(rfd < 0 && nr < 20)
565  {
566  rfd = open(outfile.c_str(), O_RDONLY);
567  ++nr;
568  mx::sys::milliSleep(10);
569  }
570 
571  int r = read(rfd, inbuff, sizeof(inbuff));
572 
573  close(rfd);
574 
575  if(r < 0)
576  {
577  log<software_error>({__FILE__, __LINE__, errno, "error on read from " + m_fpsFifo});
578  return "";
579  }
580 
581  remove(outfile.c_str());
582 
583  inbuff[r] = '\0';
584 
585  std::string instr = inbuff;
586 
587  size_t ned = instr.find_last_not_of(" \t\r\n");
588 
589  if(ned == std::string::npos || ned == 0)
590  {
591  log<software_error>({__FILE__, __LINE__, "got empty result from " + m_fpsFifo});
592  return "";
593  }
594 
595  size_t nst = instr.rfind(' ', ned);
596 
597  if(nst == 0 || nst == std::string::npos || ned <= nst)
598  {
599  log<software_error>({__FILE__, __LINE__, "bad format in result from " + m_fpsFifo});
600  return "";
601  }
602 
603  return instr.substr(nst);
604 }
605 
606 inline
608 {
609  std::string calsrc = "/milk/shm/aol" + m_loopNumber + "_calib_source.txt";
610 
611  std::ifstream fin;
612 
613  //First read in the milk/shm directory name, which could be to a symlinked directory
614  fin.open(calsrc);
615  if(!fin)
616  {
617  return 0;
618  }
619  fin >> calsrc;
620  fin.close();
621 
622  //Now read in the actual directory
623  calsrc += "/calib_dir.txt";
624  fin.open(calsrc);
625  if(!fin)
626  {
627  return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + calsrc});
628  }
629  std::string aoCalDir;
630 
631  fin >> aoCalDir;
632 
633  fin.close();
634 
635  bool newcal = false;
636  if(aoCalDir != m_aoCalDir)
637  {
638  m_aoCalDir = aoCalDir;
639  newcal = true;
640  }
641 
642  std::string nameFile = m_aoCalDir + "/LOOPNAME";
643  fin.open(nameFile);
644  if(!fin)
645  {
646  return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + nameFile});
647  }
648  fin >> m_loopName;
649  fin.close();
650 
651  m_fpsFifo = "/milk/shm/" + m_loopName + "_fpsCTRL.fifo";
652 
653  calsrc = "/milk/shm/aol" + m_loopNumber + "_calib_loaded.txt";
654  fin.open(calsrc);
655  if(!fin)
656  {
657  return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + calsrc});
658  }
659  std::string aoCalLoadTime;
660  fin >> aoCalLoadTime;
661 
662  if(aoCalLoadTime != m_aoCalLoadTime)
663  {
664  newcal = true;
665  m_aoCalLoadTime = aoCalLoadTime;
666  }
667 
668  if(newcal)
669  {
670  log<text_log>("new calibration " + m_aoCalDir + " loaded at: " + m_aoCalLoadTime, logPrio::LOG_INFO);
671  }
672 
673  fin.close();
674 
675  calsrc = m_aoCalDir + "/calib_archived.txt";
676 
677  fin.open(calsrc);
678  if(!fin)
679  {
680  return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + calsrc});
681  }
682 
683  while(!fin.eof())
684  {
685  fin >> m_aoCalArchiveTime;
686  }
687  fin.close();
688 
689 
690  return 0;
691 }
692 
694 {
695  ///\todo look for actual evidence of processes, such as interrogating ps.
696 
698 
699  return 0;
700 }
701 
703 {
704  recordLoopGain(true);
705  return setFPSVal("mfilt", "loopgain", m_gain_target);
706 }
707 
709 {
710  recordLoopGain(true);
711  return setFPSVal("mfilt", "loopmult", m_multCoeff_target);
712 }
713 
715 {
716  recordLoopGain(true);
717  return setFPSVal("mfilt", "looplimit", m_maxLim_target);
718 }
719 
721 {
722  recordLoopGain(true);
723  if( setFPSVal("mfilt", "loopON", std::string("ON")) != 0)
724  {
725  return log<software_error,-1>({__FILE__, __LINE__, "error setting FPS val"});
726  }
727 
728  log<loop_closed>();
729 
730  return 0;
731 
732 }
733 
735 {
736  recordLoopGain(true);
737  if( setFPSVal("mfilt", "loopON", std::string("OFF")) != 0)
738  {
739  return log<software_error,-1>({__FILE__, __LINE__, "error setting FPS val"});
740  }
741 
742  if(m_gain == 0)
743  {
744  log<loop_open>();
745  }
746  else
747  {
748  log<loop_paused>();
749  }
750 
751  return 0;
752 
753 }
754 
756 {
757  if( setFPSVal("mfilt", "loopZERO", std::string("ON")) != 0)
758  {
759  return log<software_error,-1>({__FILE__, __LINE__, "error setting FPS val"});
760  }
761 
762  log<text_log>("loop zeroed", logPrio::LOG_NOTICE);
763 
764  return 0;
765 
766 }
767 
769 {
770  c->fmThreadExec();
771 }
772 
773 
775 {
776  m_fmThreadID = syscall(SYS_gettid);
777 
778  while( m_fmThreadInit == true && shutdown() == 0)
779  {
780  sleep(1);
781  }
782 
783  while(shutdown() == 0)
784  {
785  if(m_fpsFifo == "")
786  {
787  sleep(1);
788  continue;
789  }
790 
791  std::string ans = getFPSValStr("mfilt", "loopON");
792 
793  if(ans[1] == 'F')
794  {
795  m_loopState = 0;
796  }
797  else m_loopState = 2; //closed
798 
799  ans = getFPSValNum("mfilt", "loopgain");
800 
801  try
802  {
803  m_gain = std::stof(ans);
804  }
805  catch(const std::exception& e)
806  {
807  m_gain = 0;
808  }
809 
810  ans = getFPSValNum("mfilt", "loopmult");
811  try
812  {
813  m_multCoeff = std::stof(ans);
814  }
815  catch(...)
816  {
817  m_multCoeff = 0;
818  }
819  ans = getFPSValNum("mfilt", "looplimit");
820  try
821  {
822  m_maxLim = std::stof(ans);
823  }
824  catch(...)
825  {
826  m_maxLim = 0;
827  }
828 
829  recordLoopGain();
830 
831  mx::sys::milliSleep(250);
832 
833  }
834 
835  return;
836 }
837 
838 INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_loopState )(const pcf::IndiProperty &ipRecv)
839 {
840  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_loopState, ipRecv);
841 
842  if(!ipRecv.find("toggle")) return 0;
843 
844  std::unique_lock<std::mutex> lock(m_indiMutex);
845 
846  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
847  {
848  return loopOn();
849  }
850  else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
851  {
852  return loopOff();
853  }
854 
855  log<software_error>({__FILE__,__LINE__, "switch state fall through."});
856  return -1;
857 }
858 
859 INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_loopGain )(const pcf::IndiProperty &ipRecv)
860 {
861  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_loopGain, ipRecv);
862 
863  float current = -1;
864  float target = -1;
865 
866  if(ipRecv.find("current"))
867  {
868  current = ipRecv["current"].get<double>();
869  }
870 
871  if(ipRecv.find("target"))
872  {
873  target = ipRecv["target"].get<double>();
874  }
875 
876  if(target == -1) target = current;
877 
878  if(target == -1)
879  {
880  return 0;
881  }
882 
883  std::lock_guard<std::mutex> guard(m_indiMutex);
884 
885  m_gain_target = target;
886 
887  updateIfChanged(m_indiP_loopGain, "target", m_gain_target);
888 
889  return setGain();
890 }
891 
892 INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_loopZero )(const pcf::IndiProperty &ipRecv)
893 {
894  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_loopZero, ipRecv);
895 
896  if(!ipRecv.find("request")) return 0;
897 
898  std::unique_lock<std::mutex> lock(m_indiMutex);
899 
900  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
901  {
902  return loopZero();
903  }
904 
905  return 0;
906 }
907 
908 INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_multCoeff )(const pcf::IndiProperty &ipRecv)
909 {
910  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_multCoeff, ipRecv);
911 
912  float current = -1;
913  float target = -1;
914 
915  if(ipRecv.find("current"))
916  {
917  current = ipRecv["current"].get<double>();
918  }
919 
920  if(ipRecv.find("target"))
921  {
922  target = ipRecv["target"].get<double>();
923  }
924 
925  if(target == -1) target = current;
926 
927  if(target == -1)
928  {
929  return 0;
930  }
931 
932  std::lock_guard<std::mutex> guard(m_indiMutex);
933 
934  m_multCoeff_target = target;
935  updateIfChanged(m_indiP_multCoeff, "target", target);
936 
937  return setMultCoeff();
938 }
939 
940 INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_maxLim )(const pcf::IndiProperty &ipRecv)
941 {
942  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_maxLim, ipRecv);
943 
944  float current = -1;
945  float target = -1;
946 
947  if(ipRecv.find("current"))
948  {
949  current = ipRecv["current"].get<double>();
950  }
951 
952  if(ipRecv.find("target"))
953  {
954  target = ipRecv["target"].get<double>();
955  }
956 
957  if(target == -1) target = current;
958 
959  if(target == -1)
960  {
961  return 0;
962  }
963 
964  std::lock_guard<std::mutex> guard(m_indiMutex);
965  m_maxLim_target = target;
966  updateIfChanged(m_indiP_maxLim, "target", target);
967 
968  return setMaxLim();
969 }
970 
971 inline
973 {
975 }
976 
977 inline
979 {
980  return recordLoopGain(true);
981 }
982 
983 inline
985 {
986  static uint8_t state {0};
987  static float gain {-1000};
988  static float multcoef {0};
989  static float limit {0};
990 
991  if(state != m_loopState || gain != m_gain || multcoef != m_multCoeff || limit != m_maxLim || force)
992  {
993  state = m_loopState;
994  gain = m_gain;
995  multcoef = m_multCoeff;
996  limit = m_maxLim;
997 
998  telem<telem_loopgain>({state, m_gain, m_multCoeff, m_maxLim});
999  }
1000 
1001  return 0;
1002 }
1003 
1004 } //namespace app
1005 } //namespace MagAOX
1006 
1007 #endif //cacaoInterface_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
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:2352
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int 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:2321
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:102
int shutdown()
Get the value of the shutdown flag.
Definition: MagAOXApp.hpp:1146
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
Definition: MagAOXApp.hpp:2901
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2140
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2437
int createROIndiText(pcf::IndiProperty &prop, const std::string &propName, const std::string &elName, const std::string &propLabel="", const std::string &propGroup="", const std::string &elLabel="")
Create a standard ReadOnly INDI Text property, with at least one element.
Definition: MagAOXApp.hpp:2209
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
Definition: MagAOXApp.hpp:1950
The MagAO-X CACAO Interface.
int setMultCoeff()
Set loop multiplication coefficient to the value of m_multCoeff_target;.
pcf::IndiProperty m_indiP_loopZero
int m_loopState
The loop state. 0 = off, 1 = paused (on, 0 gain), 2 = on.
int checkLoopProcesses()
Check if the loop processes are running.
INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_maxLim)
bool m_fmThreadInit
Initialization flag for the file monitoring thread.
virtual int appLogic()
Implementation of the FSM for cacaoInterface.
std::string getFPSValNum(const std::string &fps, const std::string &param)
void fmThreadExec()
File monitoring thread function.
float m_gain_target
The target loop gain.
static void fmThreadStart(cacaoInterface *c)
File monitoring thread starter function.
INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_loopZero)
pcf::IndiProperty m_indiP_loop
int loopOn()
Turn the loop on.
int setFPSVal(const std::string &fps, const std::string &param, const std::string &val)
std::vector< float > m_modeBlockLims
pcf::IndiProperty m_indiP_loopState
bool m_loopProcesses
Status of the loop processes.
float m_maxLim
The current max limit.
INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_multCoeff)
pcf::IndiProperty m_fmThreadProp
The property to hold the f.m. thread details.
std::vector< float > m_modeBlockGains
int m_fmThreadPrio
Priority of the filemonitoring thread.
std::vector< float > m_modeBlockMCs
int setGain()
Set loop gain to the value of m_gain_target;.
float m_gain
The current loop gain.
std::string m_loopName
the loop name
~cacaoInterface() noexcept
D'tor, declared and defined for noexcept.
std::string getFPSValStr(const std::string &fps, const std::string &param)
int recordLoopGain(bool force=false)
std::vector< int > m_modeBlockStart
std::thread m_fmThread
The file monitoring thread.
virtual int appStartup()
Startup function.
int getAOCalib()
Get the calibration details.
int setMaxLim()
Set loop max lim to the value of m_maxLim_target;.
virtual int appShutdown()
Shutdown the app.
float m_maxLim_target
The target max limit.
float m_multCoeff
The current multiplicative coefficient (1-leak)
dev::telemeter< cacaoInterface > telemeterT
int recordTelem(const telem_loopgain *)
pcf::IndiProperty m_indiP_maxLim
INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_loopState)
std::string m_loopNumber
The loop number, X in aolX. We keep it a string because that's how it gets used.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
pid_t m_fmThreadID
File monitor thread PID.
std::vector< int > m_modeBlockN
int loopOff()
Turn the loop off.
pcf::IndiProperty m_indiP_loopProcesses
pcf::IndiProperty m_indiP_multCoeff
float m_multCoeff_target
The target multiplicative coefficient (1-leak)
pcf::IndiProperty m_indiP_loopGain
int loopZero()
Zero the loop control channel.
bool m_loopProcesses_stat
What the cacao status file says the state of loop processes is.
INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_loopGain)
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:207
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
@ 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
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
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
int addTextElement(pcf::IndiProperty &prop, const std::string &name, const std::string &label="")
Add a standard INDI Text element.
Definition: indiUtils.hpp:40
const pcf::IndiProperty & ipRecv
INDI_VALIDATE_CALLBACK_PROPS(function, 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_INFO
Informational. The info log level is the lowest level recorded during normal operations.
Definition: logPriority.hpp:49
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
A device which saves telemetry.
Definition: telemeter.hpp:52
int appShutdown()
Perform telemeter application shutdown.
Definition: telemeter.hpp:259
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:208
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:253
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:195
int appStartup()
Starts the telemetry log thread.
Definition: telemeter.hpp:226
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:266
Software CRITICAL log entry.
Software ERR log entry.
Log entry recording the build-time git state.