Line data Source code
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 :
41 : typedef dev::telemeter<cacaoInterface> telemeterT;
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.
91 : cacaoInterface();
92 :
93 : /// D'tor, declared and defined for noexcept.
94 15 : ~cacaoInterface() noexcept
95 15 : {}
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 :
156 : int getModeBlocks();
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 :
248 0 : INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_loopState);
249 0 : INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_loopZero);
250 0 : INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_loopGain);
251 0 : INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_multCoeff);
252 0 : INDI_NEWCALLBACK_DECL(cacaoInterface, m_indiP_maxLim);
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 45 : cacaoInterface::cacaoInterface() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
270 : {
271 :
272 15 : return;
273 0 : }
274 :
275 0 : void cacaoInterface::setupConfig()
276 : {
277 0 : config.add("loop.number", "", "loop.number", argType::Required, "loop", "number", false, "string", "the loop number");
278 :
279 0 : telemeterT::setupConfig(config);
280 0 : }
281 :
282 0 : int cacaoInterface::loadConfigImpl( mx::app::appConfigurator & _config )
283 : {
284 0 : _config(m_loopNumber, "loop.number");
285 :
286 0 : if(telemeterT::loadConfig(_config) < 0)
287 : {
288 0 : log<text_log>("Error during telemeter config", logPrio::LOG_CRITICAL);
289 0 : m_shutdown = true;
290 : }
291 :
292 0 : return 0;
293 : }
294 :
295 0 : void cacaoInterface::loadConfig()
296 : {
297 0 : loadConfigImpl(config);
298 0 : }
299 :
300 0 : int cacaoInterface::appStartup()
301 : {
302 :
303 0 : if(m_loopNumber == "")
304 : {
305 0 : return log<software_critical, -1>({__FILE__, __LINE__, "loop number not set"});
306 : }
307 :
308 0 : createROIndiText( m_indiP_loop, "loop", "name", "Loop Description", "Loop Controls", "Name");
309 0 : indi::addTextElement(m_indiP_loop, "number", "Number");
310 0 : m_indiP_loop["number"] = m_loopNumber;
311 0 : m_indiP_loop["name"] = m_loopName;
312 0 : registerIndiPropertyReadOnly(m_indiP_loop);
313 :
314 0 : createStandardIndiToggleSw( m_indiP_loopProcesses, "loop_processes", "Loop Processes", "Loop Controls");
315 0 : registerIndiPropertyReadOnly( m_indiP_loopProcesses);
316 :
317 0 : createStandardIndiToggleSw( m_indiP_loopState, "loop_state", "Loop State", "Loop Controls");
318 0 : registerIndiPropertyNew( m_indiP_loopState, INDI_NEWCALLBACK(m_indiP_loopState) );
319 :
320 0 : createStandardIndiRequestSw( m_indiP_loopZero, "loop_zero", "Loop Zero", "Loop Controls");
321 0 : registerIndiPropertyNew( m_indiP_loopZero,INDI_NEWCALLBACK(m_indiP_loopZero) );
322 :
323 0 : createStandardIndiNumber<float>( m_indiP_loopGain, "loop_gain", 0.0, 10.0, 0.01, "%0.3f", "Loop Gain", "Loop Controls");
324 0 : registerIndiPropertyNew( m_indiP_loopGain, INDI_NEWCALLBACK(m_indiP_loopGain) );
325 :
326 0 : createStandardIndiNumber<float>( m_indiP_multCoeff, "loop_multcoeff", 0.0, 1.0, 0.001, "%0.3f", "Mult. Coefficient", "Loop Controls");
327 0 : registerIndiPropertyNew( m_indiP_multCoeff, INDI_NEWCALLBACK(m_indiP_multCoeff) );
328 :
329 0 : createStandardIndiNumber<float>( m_indiP_maxLim, "loop_max_limit", 0.0, 10.0, 0.001, "%0.3f", "Max. Limit", "Loop Controls");
330 0 : registerIndiPropertyNew( m_indiP_maxLim, INDI_NEWCALLBACK(m_indiP_maxLim) );
331 :
332 0 : if(threadStart( m_fmThread, m_fmThreadInit, m_fmThreadID, m_fmThreadProp, m_fmThreadPrio, "", "loopmon", this, fmThreadStart) < 0)
333 : {
334 0 : log<software_error>({__FILE__, __LINE__});
335 0 : return -1;
336 : }
337 :
338 0 : if(telemeterT::appStartup() < 0)
339 : {
340 0 : return log<software_error,-1>({__FILE__,__LINE__});
341 : }
342 :
343 0 : return 0;
344 : }
345 :
346 0 : int cacaoInterface::appLogic()
347 : {
348 : //do a join check to see if other threads have exited.
349 0 : if(pthread_tryjoin_np(m_fmThread.native_handle(),0) == 0)
350 : {
351 0 : log<software_critical>({__FILE__, __LINE__, "cacao file monitoring thread has exited"});
352 :
353 0 : return -1;
354 : }
355 :
356 : //These could change if a new calibration is loaded
357 0 : if(getAOCalib() < 0 )
358 : {
359 0 : state(stateCodes::ERROR, true);
360 0 : if(!stateLogged()) log<text_log>("Could not get AO calib", logPrio::LOG_ERROR);
361 0 : return 0;
362 : }
363 :
364 0 : if(m_loopProcesses == 0 || m_loopState == 0) state(stateCodes::READY);
365 0 : else state(stateCodes::OPERATING);
366 :
367 0 : if(telemeterT::appLogic() < 0)
368 : {
369 0 : log<software_error>({__FILE__, __LINE__});
370 0 : return 0;
371 : }
372 :
373 0 : std::unique_lock<std::mutex> lock(m_indiMutex);
374 :
375 0 : updateIfChanged(m_indiP_loop, std::vector<std::string>({"name", "number"}), std::vector<std::string>({m_loopName, m_loopNumber}));
376 :
377 0 : if(m_loopProcesses)
378 : {
379 0 : updateSwitchIfChanged(m_indiP_loopProcesses, "toggle", pcf::IndiElement::On, INDI_OK);
380 : }
381 : else
382 : {
383 0 : updateSwitchIfChanged(m_indiP_loopProcesses, "toggle", pcf::IndiElement::Off, INDI_IDLE);
384 : }
385 :
386 0 : if(m_loopState == 0)
387 : {
388 0 : updateSwitchIfChanged(m_indiP_loopState, "toggle", pcf::IndiElement::Off, INDI_IDLE);
389 : }
390 0 : else if(m_loopState == 1)
391 : {
392 0 : updateSwitchIfChanged(m_indiP_loopState, "toggle", pcf::IndiElement::On, INDI_IDLE);
393 : }
394 0 : else if(m_loopState == 2)
395 : {
396 0 : updateSwitchIfChanged(m_indiP_loopState, "toggle", pcf::IndiElement::On, INDI_OK);
397 : }
398 :
399 0 : updateIfChanged(m_indiP_loop, "name", m_loopName);
400 0 : updateIfChanged(m_indiP_loop, "number", m_loopNumber);
401 :
402 0 : updateIfChanged(m_indiP_loopGain, "current", m_gain);
403 0 : updateIfChanged(m_indiP_multCoeff, "current", m_multCoeff);
404 0 : updateIfChanged(m_indiP_maxLim, "current", m_maxLim);
405 :
406 0 : return 0;
407 0 : }
408 :
409 0 : int cacaoInterface::appShutdown()
410 : {
411 :
412 0 : if(m_fmThread.joinable())
413 : {
414 : try
415 : {
416 0 : m_fmThread.join(); //this will throw if it was already joined
417 : }
418 0 : catch(...)
419 : {
420 0 : }
421 : }
422 :
423 0 : telemeterT::appShutdown();
424 :
425 0 : return 0;
426 : }
427 :
428 0 : int cacaoInterface::setFPSVal( const std::string & fps,
429 : const std::string & param,
430 : const std::string & val
431 : )
432 : {
433 0 : std::string comout = "setval " + fps + "-" + m_loopNumber + "." + param + " " + val + "\n";
434 :
435 0 : int wfd = open( m_fpsFifo.c_str(), O_WRONLY);
436 0 : if(wfd < 0)
437 : {
438 0 : log<software_error>({__FILE__, __LINE__, errno, "error opening " + m_fpsFifo});
439 0 : return -1;
440 : }
441 :
442 0 : int w = write(wfd, comout.c_str(), comout.size());
443 :
444 0 : if(w != (int) comout.size())
445 : {
446 0 : log<software_error>({__FILE__, __LINE__, errno, "error on write to " + m_fpsFifo});
447 0 : return -1;
448 : }
449 :
450 0 : close(wfd);
451 :
452 0 : return 0;
453 0 : }
454 :
455 :
456 : template<typename T>
457 0 : int cacaoInterface::setFPSVal( const std::string & fps,
458 : const std::string & param,
459 : const T & val
460 : )
461 : {
462 0 : return setFPSVal( fps, param, std::to_string(val));
463 : }
464 :
465 0 : std::string cacaoInterface::getFPSValStr( const std::string & fps,
466 : const std::string & param
467 : )
468 : {
469 0 : std::string outfile = "/dev/shm/" + m_loopName + "_out_" + fps + "-" + m_loopNumber + "." + param;
470 :
471 0 : std::string comout = "fwrval " + fps + "-" + m_loopNumber + "." + param + " " + outfile + "\n";
472 :
473 0 : int wfd = open( m_fpsFifo.c_str(), O_WRONLY);
474 0 : if(wfd < 0)
475 : {
476 0 : log<software_error>({__FILE__, __LINE__, errno, "error opening " + m_fpsFifo});
477 0 : return "";
478 : }
479 :
480 0 : int w = write(wfd, comout.c_str(), comout.size());
481 :
482 0 : if(w != (int) comout.size())
483 : {
484 0 : log<software_error>({__FILE__, __LINE__, errno, "error on write to " + m_fpsFifo});
485 0 : return "";
486 : }
487 :
488 0 : close(wfd);
489 :
490 : char inbuff [4096];
491 :
492 0 : int rfd = -1;
493 0 : int nr =0;
494 0 : while(rfd < 0 && nr < 500)
495 : {
496 0 : rfd = open(outfile.c_str(), O_RDONLY);
497 0 : ++nr;
498 0 : mx::sys::milliSleep(10);
499 : }
500 :
501 0 : if(rfd < 0)
502 : {
503 0 : log<software_error>({__FILE__, __LINE__, "could not get an open fd on " + m_fpsFifo});
504 0 : return "";
505 : }
506 :
507 0 : int r = read(rfd, inbuff, sizeof(inbuff));
508 :
509 0 : if(r < 0)
510 : {
511 0 : log<software_error>({__FILE__, __LINE__, errno, "error on read from " + m_fpsFifo});
512 0 : return "";
513 : }
514 :
515 0 : close(rfd);
516 :
517 0 : remove(outfile.c_str());
518 :
519 0 : inbuff[r] = '\0';
520 :
521 0 : std::string instr = inbuff;
522 :
523 0 : size_t ned = instr.find_last_not_of(" \t\r\n");
524 :
525 0 : if(ned == std::string::npos || ned == 0)
526 : {
527 : //log<software_error>({__FILE__, __LINE__, "got empty result from " + m_fpsFifo});
528 0 : return "";
529 : }
530 :
531 0 : size_t nst = instr.rfind(' ', ned);
532 :
533 0 : if(nst == 0 || nst == std::string::npos || ned <= nst)
534 : {
535 0 : log<software_error>({__FILE__, __LINE__, "bad format in result from " + m_fpsFifo});
536 0 : return "";
537 : }
538 :
539 0 : return instr.substr(nst+1);
540 0 : }
541 :
542 0 : std::string cacaoInterface::getFPSValNum( const std::string & fps,
543 : const std::string & param
544 : )
545 : {
546 0 : std::string outfile = "/dev/shm/" + m_loopName + "_out_" + fps + "-" + m_loopNumber + "." + param;
547 :
548 0 : std::string comout = "fwrval " + fps + "-" + m_loopNumber + "." + param + " " + outfile + "\n";
549 :
550 0 : int wfd = open( m_fpsFifo.c_str(), O_WRONLY);
551 0 : if(wfd < 0)
552 : {
553 0 : log<software_error>({__FILE__, __LINE__, errno, "error opening " + m_fpsFifo});
554 0 : return "";
555 : }
556 :
557 0 : int w = write(wfd, comout.c_str(), comout.size());
558 :
559 0 : if(w != (int) comout.size())
560 : {
561 0 : log<software_error>({__FILE__, __LINE__, errno, "error on write to " + m_fpsFifo});
562 0 : return "";
563 : }
564 0 : close(wfd);
565 :
566 : char inbuff [4096];
567 :
568 0 : int rfd = -1;
569 0 : int nr =0;
570 0 : while(rfd < 0 && nr < 500)
571 : {
572 0 : rfd = open(outfile.c_str(), O_RDONLY);
573 0 : ++nr;
574 0 : mx::sys::milliSleep(10);
575 : }
576 :
577 0 : if(rfd < 0)
578 : {
579 0 : log<software_error>({__FILE__, __LINE__, "could not get an open fd on " + m_fpsFifo});
580 0 : return "";
581 : }
582 :
583 0 : int r = read(rfd, inbuff, sizeof(inbuff));
584 :
585 0 : close(rfd);
586 :
587 0 : if(r < 0)
588 : {
589 0 : log<software_error>({__FILE__, __LINE__, errno, "error on read from " + m_fpsFifo});
590 0 : return "";
591 : }
592 :
593 0 : remove(outfile.c_str());
594 :
595 0 : inbuff[r] = '\0';
596 :
597 0 : std::string instr = inbuff;
598 :
599 0 : size_t ned = instr.find_last_not_of(" \t\r\n");
600 :
601 0 : if(ned == std::string::npos || ned == 0)
602 : {
603 0 : log<software_error>({__FILE__, __LINE__, "got empty result from " + m_fpsFifo});
604 0 : return "";
605 : }
606 :
607 0 : size_t nst = instr.rfind(' ', ned);
608 :
609 0 : if(nst == 0 || nst == std::string::npos || ned <= nst)
610 : {
611 0 : log<software_error>({__FILE__, __LINE__, "bad format in result from " + m_fpsFifo});
612 0 : return "";
613 : }
614 :
615 0 : return instr.substr(nst);
616 0 : }
617 :
618 : inline
619 0 : int cacaoInterface::getAOCalib()
620 : {
621 0 : std::string calsrc = "/milk/shm/aol" + m_loopNumber + "_calib_source.txt";
622 :
623 0 : std::ifstream fin;
624 :
625 : //First read in the milk/shm directory name, which could be to a symlinked directory
626 0 : fin.open(calsrc);
627 0 : if(!fin)
628 : {
629 0 : return 0;
630 : }
631 0 : fin >> calsrc;
632 0 : fin.close();
633 :
634 : //Now read in the actual directory
635 0 : calsrc += "/calib_dir.txt";
636 0 : fin.open(calsrc);
637 0 : if(!fin)
638 : {
639 0 : return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + calsrc});
640 : }
641 0 : std::string aoCalDir;
642 :
643 0 : fin >> aoCalDir;
644 :
645 0 : fin.close();
646 :
647 0 : bool newcal = false;
648 0 : if(aoCalDir != m_aoCalDir)
649 : {
650 0 : m_aoCalDir = aoCalDir;
651 0 : newcal = true;
652 : }
653 :
654 0 : std::string nameFile = m_aoCalDir + "/LOOPNAME";
655 0 : fin.open(nameFile);
656 0 : if(!fin)
657 : {
658 0 : return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + nameFile});
659 : }
660 0 : fin >> m_loopName;
661 0 : fin.close();
662 :
663 0 : m_fpsFifo = "/milk/shm/" + m_loopName + "_fpsCTRL.fifo";
664 :
665 0 : calsrc = "/milk/shm/aol" + m_loopNumber + "_calib_loaded.txt";
666 0 : fin.open(calsrc);
667 0 : if(!fin)
668 : {
669 0 : return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + calsrc});
670 : }
671 0 : std::string aoCalLoadTime;
672 0 : fin >> aoCalLoadTime;
673 :
674 0 : if(aoCalLoadTime != m_aoCalLoadTime)
675 : {
676 0 : newcal = true;
677 0 : m_aoCalLoadTime = aoCalLoadTime;
678 : }
679 :
680 0 : if(newcal)
681 : {
682 0 : log<text_log>("new calibration " + m_aoCalDir + " loaded at: " + m_aoCalLoadTime, logPrio::LOG_INFO);
683 : }
684 :
685 0 : fin.close();
686 :
687 0 : calsrc = m_aoCalDir + "/calib_archived.txt";
688 :
689 0 : fin.open(calsrc);
690 0 : if(!fin)
691 : {
692 0 : return log<software_error, -1>({__FILE__, __LINE__, errno, "cacaoInterface::getAOCalib failed to open: " + calsrc});
693 : }
694 :
695 0 : while(!fin.eof())
696 : {
697 0 : fin >> m_aoCalArchiveTime;
698 : }
699 0 : fin.close();
700 :
701 :
702 0 : return 0;
703 0 : }
704 :
705 0 : int cacaoInterface::checkLoopProcesses()
706 : {
707 : ///\todo look for actual evidence of processes, such as interrogating ps.
708 :
709 0 : m_loopProcesses = m_loopProcesses_stat;
710 :
711 0 : return 0;
712 : }
713 :
714 0 : int cacaoInterface::setGain()
715 : {
716 0 : recordLoopGain(true);
717 0 : return setFPSVal("mfilt", "loopgain", m_gain_target);
718 : }
719 :
720 0 : int cacaoInterface::setMultCoeff()
721 : {
722 0 : recordLoopGain(true);
723 0 : return setFPSVal("mfilt", "loopmult", m_multCoeff_target);
724 : }
725 :
726 0 : int cacaoInterface::setMaxLim()
727 : {
728 0 : recordLoopGain(true);
729 0 : return setFPSVal("mfilt", "looplimit", m_maxLim_target);
730 : }
731 :
732 0 : int cacaoInterface::loopOn()
733 : {
734 0 : recordLoopGain(true);
735 0 : if( setFPSVal("mfilt", "loopON", std::string("ON")) != 0)
736 : {
737 0 : return log<software_error,-1>({__FILE__, __LINE__, "error setting FPS val"});
738 : }
739 :
740 0 : log<loop_closed>();
741 :
742 0 : return 0;
743 :
744 : }
745 :
746 0 : int cacaoInterface::loopOff()
747 : {
748 0 : recordLoopGain(true);
749 0 : if( setFPSVal("mfilt", "loopON", std::string("OFF")) != 0)
750 : {
751 0 : return log<software_error,-1>({__FILE__, __LINE__, "error setting FPS val"});
752 : }
753 :
754 0 : if(m_gain == 0)
755 : {
756 0 : log<loop_open>();
757 : }
758 : else
759 : {
760 0 : log<loop_paused>();
761 : }
762 :
763 0 : return 0;
764 :
765 : }
766 :
767 0 : int cacaoInterface::loopZero()
768 : {
769 0 : if( setFPSVal("mfilt", "loopZERO", std::string("ON")) != 0)
770 : {
771 0 : return log<software_error,-1>({__FILE__, __LINE__, "error setting FPS val"});
772 : }
773 :
774 0 : log<text_log>("loop zeroed", logPrio::LOG_NOTICE);
775 :
776 0 : return 0;
777 :
778 : }
779 :
780 0 : void cacaoInterface::fmThreadStart( cacaoInterface * c )
781 : {
782 0 : c->fmThreadExec();
783 0 : }
784 :
785 :
786 0 : void cacaoInterface::fmThreadExec( )
787 : {
788 0 : m_fmThreadID = syscall(SYS_gettid);
789 :
790 0 : while( m_fmThreadInit == true && shutdown() == 0)
791 : {
792 0 : sleep(1);
793 : }
794 :
795 0 : while(shutdown() == 0)
796 : {
797 0 : if(m_fpsFifo == "")
798 : {
799 0 : sleep(1);
800 0 : continue;
801 : }
802 :
803 0 : std::string ans = getFPSValStr("mfilt", "loopON");
804 :
805 0 : if(ans[1] == 'F')
806 : {
807 0 : m_loopState = 0;
808 : }
809 0 : else m_loopState = 2; //closed
810 :
811 0 : ans = getFPSValNum("mfilt", "loopgain");
812 :
813 : try
814 : {
815 0 : m_gain = std::stof(ans);
816 : }
817 0 : catch(const std::exception& e)
818 : {
819 0 : m_gain = 0;
820 0 : }
821 :
822 0 : ans = getFPSValNum("mfilt", "loopmult");
823 : try
824 : {
825 0 : m_multCoeff = std::stof(ans);
826 : }
827 0 : catch(...)
828 : {
829 0 : m_multCoeff = 0;
830 0 : }
831 0 : ans = getFPSValNum("mfilt", "looplimit");
832 : try
833 : {
834 0 : m_maxLim = std::stof(ans);
835 : }
836 0 : catch(...)
837 : {
838 0 : m_maxLim = 0;
839 0 : }
840 :
841 0 : recordLoopGain();
842 :
843 0 : mx::sys::milliSleep(500);
844 :
845 0 : }
846 :
847 0 : return;
848 : }
849 :
850 3 : INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_loopState )(const pcf::IndiProperty &ipRecv)
851 : {
852 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_loopState, ipRecv);
853 :
854 : if(!ipRecv.find("toggle")) return 0;
855 :
856 : std::unique_lock<std::mutex> lock(m_indiMutex);
857 :
858 : if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
859 : {
860 : return loopOn();
861 : }
862 : else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
863 : {
864 : return loopOff();
865 : }
866 :
867 : log<software_error>({__FILE__,__LINE__, "switch state fall through."});
868 : return -1;
869 : }
870 :
871 3 : INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_loopGain )(const pcf::IndiProperty &ipRecv)
872 : {
873 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_loopGain, ipRecv);
874 :
875 : float current = -1;
876 : float target = -1;
877 :
878 : if(ipRecv.find("current"))
879 : {
880 : current = ipRecv["current"].get<double>();
881 : }
882 :
883 : if(ipRecv.find("target"))
884 : {
885 : target = ipRecv["target"].get<double>();
886 : }
887 :
888 : if(target == -1) target = current;
889 :
890 : if(target == -1)
891 : {
892 : return 0;
893 : }
894 :
895 : std::lock_guard<std::mutex> guard(m_indiMutex);
896 :
897 : m_gain_target = target;
898 :
899 : updateIfChanged(m_indiP_loopGain, "target", m_gain_target);
900 :
901 : return setGain();
902 : }
903 :
904 3 : INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_loopZero )(const pcf::IndiProperty &ipRecv)
905 : {
906 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_loopZero, ipRecv);
907 :
908 : if(!ipRecv.find("request")) return 0;
909 :
910 : std::unique_lock<std::mutex> lock(m_indiMutex);
911 :
912 : if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
913 : {
914 : return loopZero();
915 : }
916 :
917 : return 0;
918 : }
919 :
920 3 : INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_multCoeff )(const pcf::IndiProperty &ipRecv)
921 : {
922 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_multCoeff, ipRecv);
923 :
924 : float current = -1;
925 : float target = -1;
926 :
927 : if(ipRecv.find("current"))
928 : {
929 : current = ipRecv["current"].get<double>();
930 : }
931 :
932 : if(ipRecv.find("target"))
933 : {
934 : target = ipRecv["target"].get<double>();
935 : }
936 :
937 : if(target == -1) target = current;
938 :
939 : if(target == -1)
940 : {
941 : return 0;
942 : }
943 :
944 : std::lock_guard<std::mutex> guard(m_indiMutex);
945 :
946 : m_multCoeff_target = target;
947 : updateIfChanged(m_indiP_multCoeff, "target", target);
948 :
949 : return setMultCoeff();
950 : }
951 :
952 3 : INDI_NEWCALLBACK_DEFN(cacaoInterface, m_indiP_maxLim )(const pcf::IndiProperty &ipRecv)
953 : {
954 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_maxLim, ipRecv);
955 :
956 : float current = -1;
957 : float target = -1;
958 :
959 : if(ipRecv.find("current"))
960 : {
961 : current = ipRecv["current"].get<double>();
962 : }
963 :
964 : if(ipRecv.find("target"))
965 : {
966 : target = ipRecv["target"].get<double>();
967 : }
968 :
969 : if(target == -1) target = current;
970 :
971 : if(target == -1)
972 : {
973 : return 0;
974 : }
975 :
976 : std::lock_guard<std::mutex> guard(m_indiMutex);
977 : m_maxLim_target = target;
978 : updateIfChanged(m_indiP_maxLim, "target", target);
979 :
980 : return setMaxLim();
981 : }
982 :
983 : inline
984 0 : int cacaoInterface::checkRecordTimes()
985 : {
986 0 : return telemeterT::checkRecordTimes(telem_loopgain());
987 : }
988 :
989 : inline
990 0 : int cacaoInterface::recordTelem( const telem_loopgain * )
991 : {
992 0 : return recordLoopGain(true);
993 : }
994 :
995 : inline
996 0 : int cacaoInterface::recordLoopGain( bool force )
997 : {
998 : static uint8_t state {0};
999 : static float gain {-1000};
1000 : static float multcoef {0};
1001 : static float limit {0};
1002 :
1003 0 : if(state != m_loopState || gain != m_gain || multcoef != m_multCoeff || limit != m_maxLim || force)
1004 : {
1005 0 : state = m_loopState;
1006 0 : gain = m_gain;
1007 0 : multcoef = m_multCoeff;
1008 0 : limit = m_maxLim;
1009 :
1010 0 : telem<telem_loopgain>({state, m_gain, m_multCoeff, m_maxLim});
1011 : }
1012 :
1013 0 : return 0;
1014 : }
1015 :
1016 : } //namespace app
1017 : } //namespace MagAOX
1018 :
1019 : #endif //cacaoInterface_hpp
|