API
 
Loading...
Searching...
No Matches
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
28namespace MagAOX
29{
30namespace app
31{
32
33/// Local derivation of tmcController to implement MagAO-X logging
34template<class parentT>
35class tmcCon : public tmcController
36{
37public:
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 */
74class kcubeCtrl : public MagAOXApp<true>
75{
76
77 ///\todo needs telems
78
79 // Give the test harness access.
80 friend class kcubeCtrl_test;
81
82protected:
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
99public:
100
101 /// Default c'tor.
102 kcubeCtrl();
103
104 /// D'tor, declared and defined for 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
194kcubeCtrl::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
206int 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 {
230 return -1;
231 }
232
235 {
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 {
244 return -1;
245 }
246 m_indiP_axis1_voltage["current"]=0;
247 m_indiP_axis1_voltage["target"]=0;
248
251 {
253 return -1;
254 }
255
258 {
260 return -1;
261 }
262
263 createStandardIndiNumber<float>( m_indiP_axis2_voltage, "axis2_voltage",0,150,1.0/32767, "%0.4f");
265 {
267 return -1;
268 }
269 m_indiP_axis2_voltage["current"]=0;
270 m_indiP_axis2_voltage["target"]=0;
271
274 {
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
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
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
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
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
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
1007
1008 return 0;
1009}
1010
1011INDI_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
1028INDI_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
1058INDI_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
1077INDI_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
1094INDI_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
1123INDI_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
1142INDI_NEWCALLBACK_DEFN(kcubeCtrl, m_indiP_set)(const pcf::IndiProperty &ipRecv)
1143{
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.
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.
stateCodes::stateCodeT state()
Get the current state code.
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.
int m_powerState
Current power state, 1=On, 0=Off, -1=Unk.
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.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::mutex m_indiMutex
Mutex for locking INDI communications.
The MagAO-X K-Cube Controller.
Definition kcubeCtrl.hpp:75
kcubeCtrl()
Default c'tor.
pcf::IndiProperty m_indiP_axis2_identify
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis1_enable)
virtual int appShutdown()
Shutdown the app.
pcf::IndiProperty m_indiP_axis1_enable
pcf::IndiProperty m_indiP_axis2_voltage
virtual int appStartup()
Startup function.
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis2_enable)
tmcCon< kcubeCtrl > m_kAxis2
Definition kcubeCtrl.hpp:94
virtual void setupConfig()
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis1_voltage)
~kcubeCtrl() noexcept
D'tor, declared and defined for noexcept.
pcf::IndiProperty m_indiP_axis2_enable
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_set)
pcf::IndiProperty m_indiP_axis1_voltage
tmcCon< kcubeCtrl > m_kAxis1
Definition kcubeCtrl.hpp:91
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis2_voltage)
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis2_identify)
int axis1Voltage(float &v)
pcf::IndiProperty m_indiP_set
friend class kcubeCtrl_test
Definition kcubeCtrl.hpp:80
pcf::IndiProperty m_indiP_axis1_identify
virtual int appLogic()
Implementation of the FSM for kcubeCtrl.
virtual void loadConfig()
INDI_NEWCALLBACK_DECL(kcubeCtrl, m_indiP_axis1_identify)
int axis2Voltage(float &v)
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_DEFN(class, prop)
Define the callback for a new property request.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
@ OPERATING
The device is operating, other than homing.
@ NODEVICE
No device exists for the application to control.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:28
#define INDI_BUSY
Definition indiUtils.hpp:30
#define INDI_OK
Definition indiUtils.hpp:29
std::stringstream msg
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Software ERR log entry.