API
 
Loading...
Searching...
No Matches
smc100ccCtrl.hpp
Go to the documentation of this file.
1/** \file smc100ccCtrl.hpp
2 * \brief The smc controller communicator
3 * \author Chris Bohlman (cbohlman@pm.me)
4 *
5 * \ingroup smc100ccCtrl_files
6 *
7 * History:
8 * - 2019-01-10 created by CJB
9 *
10 */
11#ifndef smc100ccCtrl_hpp
12#define smc100ccCtrl_hpp
13
14#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
15#include "../../magaox_git_version.h"
16
17#include <iostream>
18#include <string>
19#include <fstream>
20#include <vector>
21#include <sstream>
22#include <algorithm>
23#include <iterator>
24#include <bitset>
25
26namespace MagAOX
27{
28namespace app
29{
30
31/** TS command: Checks if there were any errors during initialization
32 * Solid orange LED: everything is okay, TS should return 1TS00000A
33 * PW command: change all stage and motor configuration parameters
34 * OR command: gets controller to ready state (must go through homing first)
35 * In ready state, can move relative and move absolute
36 * RS command: TO get from ready to not referenced
37
38 Change to stateCodes::OPERATING and stateCodes::READY
39
40 */
41class smc100ccCtrl : public MagAOXApp<>, public tty::usbDevice, public dev::ioDevice,
42 public dev::stdMotionStage<smc100ccCtrl>, public dev::telemeter<smc100ccCtrl>
43{
44
46
47 friend class dev::telemeter<smc100ccCtrl>;
48
49protected:
50
51 /** \name Configurable Parameters
52 *
53 *@{
54 */
55 double m_homingOffset {0};
56
57 double m_opDelta {0}; ///< The threshold for switching to OPERATING
58
59 ///@}
60
61
62 pcf::IndiProperty m_indiP_position; ///< Indi variable for reporting the stage position.
63
64 std::vector<std::string> validStateCodes{};
65
66 double m_position {0};
67
68 double m_target {0};
69
70 bool m_wasHoming {0};
71
72 bool m_powerOnHomed{false};
73
74 bool m_moveOp {true}; ///< Flag indicating that OPERATING should not be set for a move, because it's less than m_opDelta.
75public:
76
78
79
80 /// Default c'tor.
82
86
87 /// Setup the configuration system (called by MagAOXApp::setup())
88 virtual void setupConfig();
89
90 /// Load the configuration system results (called by MagAOXApp::setup())
91 virtual void loadConfig();
92
93 /// Checks if the device was found during loadConfig.
94 virtual int appStartup();
95
96 /// Changes device state based on testing connection and device status
97 virtual int appLogic();
98
99 /// Do any needed shutdown tasks. Currently nothing in this app.
100 virtual int appShutdown();
101
102 virtual int onPowerOff()
103 {
104 m_powerOnHomed=false;
105 recordStage(true);
106 recordPosition(true);
107 return 0;
108 }
109
110 int makeCom( std::string &str,
111 const std::string & com
112 );
113
114 int splitResponse( int &axis,
115 std::string &com,
116 std::string &val,
117 std::string &resp
118 );
119
120
121 int getCtrlState( std::string &state );
122
123 /// Tests if device is cabale of recieving/executing IO commands
124 /** Sends command for device to return serial number, and compares to device serial number indi property
125 *
126 * \returns -1 on serial numbers being different, thus ensuring connection test was sucsessful
127 * \returns 0 on serial numbers being equal
128 */
129 int testConnection();
130
131 /// Verifies current status of controller
132 /** Checks if controller is moving or has moved to correct position
133 *
134 * \returns 0 if controller is currently moving or has moved correctly.
135 * \returns -1 on error with sending commands or if current position does not match target position.
136 */
137 int getPosition( double & pos /**< [out] on output, the current position*/);
138
139 /// Returns any error controller has
140 /** Called after every command is sent
141 *
142 * \returns 0 if no error is reported
143 * \returns -1 if an error is reported and error string is set in reference
144 */
145 int getLastError( std::string& errStr /** [out] the last error string */);
146
147 /** \name Standard Motion Stage Interface
148 * @{
149 *
150 */
151
152 int stop();
153
154 int startHoming();
155
156 double presetNumber();
157
158 int moveTo(double position);
159
160
161 ///@}
162
163
164 /** \name Telemeter Interface
165 *
166 * @{
167 */
168 int checkRecordTimes();
169
170 int recordTelem( const telem_stage * );
171
172 int recordTelem( const telem_position * );
173
174 int recordStage( bool force = false );
175
176 int recordPosition( bool force = false);
177
178 ///@}
179};
180
181inline smc100ccCtrl::smc100ccCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
182{
183 m_powerMgtEnabled = true;
184 m_powerOnWait = 5; // default to 5 seconds for controller boot up.
185
186 m_defaultPositions = false;
187
188 return;
189}
190
192{
193 config.add("stage.homingOffset", "", "stage.homingOffset", argType::Required, "stage", "homingOffset", false, "float", "Homing offset, a.k.a. default starting position.");
194 config.add("stage.opDelta", "", "stage.opDelta", argType::Required, "stage", "opDelta", false, "float", "Threshold move size for switching to OPERATING.");
195
200}
201
203{
204
205 config(m_homingOffset, "stage.homingOffset");
206
207 config(m_opDelta, "stage.opDelta");
208 this->m_baudRate = B57600; //default for SMC100CC controller. Will be overridden by any config setting.
209
210 int rv = tty::usbDevice::loadConfig(config);
211
212 if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
213 {
214 log<software_error>({ __FILE__, __LINE__, "error loading USB device configs"});
216 m_shutdown = 1;
217 }
218
220 if(rv != 0)
221 {
222 log<software_error>({ __FILE__, __LINE__, "error loading io device configs"});
223 m_shutdown = 1;
224 }
225
227 if(rv != 0)
228 {
229 log<software_error>({ __FILE__, __LINE__, "error loading stdMotionStage configs"});
230 m_shutdown = 1;
231 }
232
234 if(rv != 0)
235 {
236 log<software_error>({ __FILE__, __LINE__, "error loading telemeter configs"});
237 m_shutdown = 1;
238 }
239}
240
242{
243
244 REG_INDI_NEWPROP(m_indiP_position, "position", pcf::IndiProperty::Number);
245 m_indiP_position.add (pcf::IndiElement("current"));
246 m_indiP_position["current"].set(0);
247 m_indiP_position.add (pcf::IndiElement("target"));
248
249
251 {
252 log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
253 return -1;
254 }
255
256 if(m_presetNames.size() != m_presetPositions.size())
257 {
258 return log<text_log,-1>("must set a position for each preset", logPrio::LOG_CRITICAL);
259 }
260
261 m_presetNames.insert(m_presetNames.begin(), "none");
262 m_presetPositions.insert(m_presetPositions.begin(), -1);
263
265 if(rv != 0)
266 {
267 log<software_error>({ __FILE__, __LINE__, "error starting up stdMotionStage"});
268 m_shutdown = 1;
269 }
270
272 if(rv != 0)
273 {
274 log<software_error>({ __FILE__, __LINE__, "error starting up telemeter"});
275 m_shutdown = 1;
276 }
277
278 return 0;
279}
280
282{
284 {
285 log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
286 return -1;
287 }
288
290 {
292 return -1;
293 }
294
295 if( state() == stateCodes::POWERON)
296 {
297 if(!powerOnWaitElapsed()) return 0;
298
299 if(m_deviceName == "")
300 {
302 }
303 else
304 {
306 if(!stateLogged())
307 {
308 std::stringstream logs;
309 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
310 log<text_log>(logs.str());
311 }
312 }
313
314 }
315
316 if( state() == stateCodes::NODEVICE )
317 {
318 #ifdef XWC_SIM_MODE
319 m_deviceName = "/dev/simTTY";
320 int rv = TTY_E_NOERROR;
321 #else
323 #endif
324
325 if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
326 {
328 if(!stateLogged())
329 {
331 }
332 return -1;
333 }
334
336 {
338 if(!stateLogged())
339 {
340 std::stringstream logs;
341 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
342 log<text_log>(logs.str());
343 }
344 return 0;
345 }
346 else
347 {
349 if(!stateLogged())
350 {
351 std::stringstream logs;
352 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
353 log<text_log>(logs.str());
354 }
355 }
356 }
357
359 {
360 int rv;
361 #ifdef XWC_SIM_MODE
362 rv = 0;
363 #else
364 {//scope for elPriv
366 rv = connect();
367 }
368 #endif
369
370 if(rv < 0)
371 {
373 if(nrv < 0 && nrv != TTY_E_DEVNOTFOUND && nrv != TTY_E_NODEVNAMES)
374 {
377 return -1;
378 }
379
381 {
383
384 if(!stateLogged())
385 {
386 std::stringstream logs;
387 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " no longer found in udev";
388 log<text_log>(logs.str());
389 }
390 return 0;
391 }
392
393 //if connect failed, and there is a device, then we have some other problem.
396 return -1;
397
398 }
399
400 if( testConnection() == 0 )
401 {
403 }
404 else
405 {
406 std::string errorString;
407 if (getLastError(errorString) != 0)
408 {
410 }
411
412 return 0;
413 }
414
415
417 {
418 std::stringstream logs;
419 logs << "Connected to stage(s) on " << m_deviceName;
420 log<text_log>(logs.str());
421 }
422 }
423
424
426 {
428 {
430 return -1;
431 }
432 }
433
434 //If we're here, we can get state from controller...
435 std::string axState;
436 //mutex scope
437 {
438 std::unique_lock<std::mutex> lock(m_indiMutex);
439 if(getCtrlState(axState) < 0)
440 {
441 if(m_powerTargetState == 0) return 0;
443 }
444 }
445
446
447 if(axState[0] == '0')
448 {
449 state(stateCodes::NOTHOMED); //This always means this.
450 }
451 else if (axState[0] == '1' && axState[1] == '0')
452 {
453 //Need to download stage info
454 log<text_log>("getting stage information");
455 std::string com;
456 if(makeCom(com, "PW1") < 0)
457 {
458 log<software_error>({__FILE__, __LINE__,"Error making command PW1" });
459 return 0;
460 }
461
463 if (rv != TTY_E_NOERROR)
464 {
465 if(m_powerTargetState == 0) return 0;
467 return -1;
468 }
469
470 sleep(5);
471 if(makeCom(com, "ZX2") < 0)
472 {
473 log<software_error>({__FILE__, __LINE__,"Error making command ZX2" });
474 return 0;
475 }
476
478 if (rv != TTY_E_NOERROR)
479 {
480 if(m_powerTargetState == 0) return 0;
482 return -1;
483 }
484
485 sleep(5);
486 if(makeCom(com, "PW0") < 0)
487 {
488 log<software_error>({__FILE__, __LINE__,"Error making command PW0" });
489 return 0;
490 }
491
493 if (rv != TTY_E_NOERROR)
494 {
495 if(m_powerTargetState == 0) return 0;
497 return -1;
498 }
499
500 sleep(5);
501 log<text_log>("stage information loaded");
502 return 0;
503
504 }
505 else if (axState[0] == '1' && (axState[1] == 'E' || axState[1] == 'F'))
506 {
508 m_moving = 1;
509 m_wasHoming = 1;
510 }
511 else if (axState[0] == '2')
512 {
513 m_moving = 1;
514 if(m_moveOp)
515 {
517 }
518 }
519 else if (axState[0] == '3' && isdigit(axState[1]))
520 {
521 if(m_wasHoming)
522 {
523 std::unique_lock<std::mutex> lock(m_indiMutex);
525 m_wasHoming = 0;
526 }
527 else
528 {
529 m_moving = 0;
531 m_moveOp = true;
532 }
533 }
534 else if (axState[0] == '3')
535 {
536 log<text_log>("Stage disabled. Enabling");
537 std::string com;
538 if(makeCom(com, "MM1") < 0)
539 {
540 log<software_error>({__FILE__, __LINE__,"Error making command PW1" });
541 return 0;
542 }
544 if (rv != TTY_E_NOERROR)
545 {
546 if(m_powerTargetState == 0) return 0;
548 return -1;
549 }
550 }
551 else if( axState[0] == '\0' && axState[1] == '\0' )
552 {
553 //a non-response, this means we should go back around and ask again
554 //this doesn't seem to be an error, but isn't documented.
555 //Occurs after a power off, but also sometimes after homing completes.
556 return 0;
557 }
558 else
559 {
560 sleep(1);
561 if(m_powerState == 0) return 0;
562
563 log<software_error>({__FILE__,__LINE__, "Invalid state: |" + std::to_string(axState[0]) + "|" + std::to_string(axState[1]) + "|"});
565 }
566
568 {
570 {
571 std::unique_lock<std::mutex> lock(m_indiMutex);
572 startHoming();
573 m_powerOnHomed = true;
574 }
575 return 0;
576 }
577
579 {
580 std::unique_lock<std::mutex> lock(m_indiMutex);
581
582
584
585 if(rv < 0)
586 {
587 sleep(1);
588 if(m_powerState == 0) return 0;
589
590 std::string errorString;
591
592 if (getLastError(errorString) != 0 && errorString.size() != 0)
593 {
595 }
596
597 log<software_error>({__FILE__, __LINE__,"There's been an error with getting current controller position."});
598 }
599
601
603
604 static int last_moving = -1;
605
606 bool changed = false;
607 if(last_moving != m_moving)
608 {
609 changed = true;
611 }
612
613 if(changed)
614 {
615 if(m_moving)
616 {
617 m_indiP_position.setState(INDI_BUSY);
618 }
619 else
620 {
621 m_indiP_position.setState(INDI_IDLE);
622 m_indiP_position["target"] = m_position;
623 }
624 m_indiDriver->sendSetProperty(m_indiP_position);
625 }
626
627
628 int n = presetNumber();
629 if(n == -1)
630 {
631 m_preset = 0;
632 m_preset_target = 0;
633 }
634 else
635 {
636 m_preset = n;
638 }
639
641 recordStage();
642
643 return 0;
644 }
645
646 if( state() == stateCodes::ERROR )
647 {
648 if(m_powerTargetState == 0) return 0;
649 sleep(1);
650 if(m_powerState == 0) return 0;
651
653 if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
654 {
656 if(!stateLogged())
657 {
659 }
660 return rv;
661 }
662
664 {
666
667 if(!stateLogged())
668 {
669 std::stringstream logs;
670 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
671 log<text_log>(logs.str());
672 }
673 return 0;
674 }
675
676 sleep(1);
677 if(m_powerState == 0) return 0;
678
680 if(!stateLogged())
681 {
682 log<text_log>("Error NOT due to loss of USB connection. I can't fix it myself.", logPrio::LOG_CRITICAL);
683 }
684 return -1;
685 }
686
687 return 0;
688}
689
691{
692 #ifdef XWC_SIM_MODE
693 return 0;
694 #endif
695
696 std::string buffer{"1TS\r\n"};
697 std::string output;
698
700
701 if (rv != TTY_E_NOERROR)
702 {
703 if(m_powerTargetState == 0) return -1;
705 return -1;
706 }
707
708 /*int axis;
709 std::string com;
710 std::string val;
711
712 splitResponse( axis, com, val, output);*/
713
714 return 0;
715}
716
718{
719 return 0;
720}
721
722int smc100ccCtrl::makeCom( std::string &str,
723 const std::string & com
724 )
725{
726 char tmp[10];
727
728 int axis = 1;
729
730 snprintf(tmp, 10, "%i", axis);
731
732 str = tmp;
733
734 str += com;
735
736 str += "\r\n";
737
738 return 0;
739}
740int smc100ccCtrl::splitResponse(int &axis, std::string &com, std::string &val, std::string &resp)
741{
742 if(resp.length() < 3)
743 {
744 log<software_error>({__FILE__,__LINE__, "Invalid response"});
745 return -1;
746 }
747
748 if(isalpha(resp[0]))
749 {
750 log<software_error>({__FILE__,__LINE__, "Invalid response"});
751 axis = 0;
752 com = "";
753 val = resp;
754 return 0;
755 }
756
757 if(isalpha(resp[1]))
758 {
759 axis = resp[0] - '0';
760 }
761 else
762 {
763 axis = atoi(resp.substr(0,2).c_str());
764 }
765
766 if(axis < 10)
767 {
768
769 com = resp.substr(1,2);
770 if(resp.length() < 4 ) val = "";
771 else val = resp.substr(3, resp.length()-3);
772 if(val.size() > 1)
773 {
774 while(val[val.size()-1] == '\r' || val[val.size()-1] == '\n')
775 {
776 val.erase(val.size()-1);
777 if(val.size() < 1) break;
778 }
779 }
780 }
781 else
782 {
783 if(resp.length() < 4)
784 {
785 log<software_error>({__FILE__,__LINE__, "Invalid response"});
786 com = "";
787 val = "";
788 return -1;
789 }
790 com = resp.substr(2,2);
791 if(resp.length() < 5) val = "";
792 else val = resp.substr(4, resp.length()-4);
793
794 if(val.size() > 1)
795 {
796 while(val[val.size()-1] == '\r' || val[val.size()-1] == '\n')
797 {
798 val.erase(val.size()-1);
799 if(val.size() < 1) break;
800 }
801 }
802 }
803
804 return 0;
805}
806
807int smc100ccCtrl::getCtrlState( std::string &state )
808{
809 std::string com, resp;
810
811 if(makeCom(com, "TS") < 0)
812 {
813 log<software_error>({__FILE__, __LINE__,"Error making command TS" });
814 return 0;
815 }
816
818 if (rv != TTY_E_NOERROR)
819 {
820 if(m_powerTargetState == 0) return -1;
822 return -1;
823 }
824
825 //std::cerr << "TS Response: " << resp << "\n";
826 int raxis;
827 std::string rcom, rval;
828
830
831 if(rcom == "")
832 {
833 log<software_error>({__FILE__, __LINE__, "An Error occurred"});
834 return -1;
835 }
836
837 if(raxis != 1)
838 {
839 log<software_error>({__FILE__, __LINE__, "Wrong axis returned"});
840 return -1;
841 }
842
843 if(rcom != "TS")
844 {
845 log<software_error>({__FILE__, __LINE__, "Wrong command returned"});
846 return -1;
847 }
848
849 if(rval.length() != 6)
850 {
851 log<software_error>({__FILE__, __LINE__,"Incorrect response length" });
852 return -1;
853 }
854
855 state = rval.substr(4, 2);
856
857 return 0;
858}
859
860
861int smc100ccCtrl::getPosition(double& current)
862{
863 std::string buffer{"1TP\r\n"};
864 std::string output;
866
867 if (rv != TTY_E_NOERROR)
868 {
869 if(m_powerTargetState == 0) return -1;
871 return -1;
872 }
873
874 // Parse current and place into argument
875 try
876 {
877 current = std::stod(output.substr(3));
878 }
879 catch (...)
880 {
881 log<software_error>({__FILE__, __LINE__,"Error occured: Unexpected output in getPosition()"});
882 return -1;
883 }
884 return 0;
885}
886
887int smc100ccCtrl::getLastError( std::string& errorString)
888{
889 std::string buffer{"1TE\r\n"};
890 std::string output;
892
893 if (rv != TTY_E_NOERROR)
894 {
895 if(m_powerTargetState == 0) return -1;
897 std::cerr << __FILE__ << " " << __LINE__ << " " << rv << "\n";
898
900 return -1;
901 }
902
903 char status;
904 try
905 {
906 status = output.at(3);
907 }
908 catch (const std::out_of_range& oor)
909 {
911 errorString = "Unknown output; controller not responding correctly.";
912 return -1;
913 }
914
915 if (status == '@')
916 {
917 return 0;
918 }
919 else
920 {
921 switch(status)
922 {
923 case 'A':
924 errorString = "Unknown message code or floating point controller address.";
925 break;
926 case 'B':
927 errorString = "Controller address not correct.";
928 break;
929 case 'C':
930 errorString = "Parameter missing or out of range.";
931 break;
932 case 'D':
933 errorString = "Command not allowed.";
934 break;
935 case 'E':
936 errorString = "Home sequence already started.";
937 break;
938 case 'F':
939 errorString = "ESP stage name unknown.";
940 break;
941 case 'G':
942 errorString = "Displacement out of limits.";
943 break;
944 case 'H':
945 errorString = "Command not allowed in NOT REFERENCED state.";
946 break;
947 case 'I':
948 errorString = "Command not allowed in CONFIGURATION state.";
949 break;
950 case 'J':
951 errorString = "Command not allowed in DISABLE state.";
952 break;
953 case 'K':
954 errorString = "Command not allowed in READY state.";
955 break;
956 case 'L':
957 errorString = "Command not allowed in HOMING state.";
958 break;
959 case 'M':
960 errorString = "UCommand not allowed in MOVING state.";
961 break;
962 case 'N':
963 errorString = "Current position out of software limit.";
964 break;
965 case 'S':
966 errorString = "Communication Time Out.";
967 break;
968 case 'U':
969 errorString = "Error during EEPROM access.";
970 break;
971 case 'V':
972 errorString = "Error during command execution.";
973 break;
974 case 'W':
975 errorString = "Command not allowed for PP version.";
976 break;
977 case 'X':
978 errorString = "Command not allowed for CC version.";
979 break;
980 default:
981 errorString = "unknown status";
982 std::cerr << "unkown status: " << status << "\n";
983 }
984
986 return -1;
987 }
988}
989
990INDI_NEWCALLBACK_DEFN(smc100ccCtrl, m_indiP_position)(const pcf::IndiProperty &ipRecv)
991{
992 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_position, ipRecv);
993
994 if(!( state() == stateCodes::READY || state() == stateCodes::OPERATING))
995 {
996 log<text_log>("can not command position in current state");
997 return 0;
998 }
999
1000
1001 float current = -1e55, target = -1e55;
1002
1003 try
1004 {
1005 current = ipRecv["current"].get<float>();
1006 }
1007 catch(...){}
1008
1009 try
1010 {
1011 target = ipRecv["target"].get<float>();
1012 }
1013 catch(...){}
1014
1015 if(target == -1e55) target = current;
1016
1017 if(target == -1e55) return 0;
1018
1019 //Lock the mutex, waiting if necessary
1020 std::unique_lock<std::mutex> lock(m_indiMutex);
1021
1022 updateIfChanged(m_indiP_position, "target", target, INDI_BUSY);
1023 m_target = target;
1024
1025
1026 return moveTo(target);
1027
1028}
1029
1031{
1032 recordStage(true);
1033 recordPosition(true);
1034
1035 //don't lock mutex -- this must go through
1036
1037 std::string buffer{"1ST\r\n"};
1039
1040 updateSwitchIfChanged(m_indiP_stop, "request", pcf::IndiElement::Off, INDI_IDLE);
1041
1042 if (rv != TTY_E_NOERROR)
1043 {
1044 if(m_powerTargetState == 0) return -1;
1046 return -1;
1047 }
1048 return 0;
1049}
1050
1052{
1053 updateSwitchIfChanged(m_indiP_home, "request", pcf::IndiElement::Off, INDI_IDLE);
1054
1055 recordStage(true);
1056 recordPosition(true);
1057
1058 std::string buffer{"1OR\r\n"};
1060
1061 if (rv != TTY_E_NOERROR)
1062 {
1063 if(m_powerTargetState == 0) return -1;
1065 return -1;
1066 }
1067 return 0;
1068}
1069
1071{
1072 for( size_t n=1; n < m_presetPositions.size(); ++n)
1073 {
1074 if( fabs(m_position-m_presetPositions[n]) < 1e-3) return n;
1075 }
1076
1077 return 0;
1078}
1079
1080int smc100ccCtrl::moveTo(double position)
1081{
1082 recordStage(true);
1083 recordPosition(true);
1084
1086 {
1087 m_moveOp = true;
1088 }
1089 else
1090 {
1091 m_moveOp = false;
1092 }
1093
1094 std::string buffer{"1PA"};
1095 buffer = buffer + std::to_string(position) + "\r\n";
1096
1098
1099 if (rv != TTY_E_NOERROR)
1100 {
1101 if(m_powerTargetState == 0) return -1;
1103 return -1;
1104 }
1105
1106 std::string errorString;
1107 if (getLastError(errorString) == 0)
1108 {
1109 if(m_moveOp)
1110 {
1112 }
1114 return 0;
1115 }
1116 else
1117 {
1119 return -1;
1120 }
1121}
1122
1127
1129{
1130 return recordStage(true);
1131}
1132
1134{
1135 return recordPosition(true);
1136}
1137
1142
1144{
1145 static float last_position = 1e30;
1146
1147 float fpos = m_position;
1148
1149 if( fpos != last_position || force )
1150 {
1153 }
1154
1155 return 0;
1156}
1157
1158} //namespace app
1159} //namespace MagAOX
1160
1161#endif //smc100ccCtrl_hpp
Internal class to manage setuid privilege escalation with RAII.
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.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
int m_powerState
Current power state, 1=On, 0=Off, -1=Unk.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
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.
bool powerOnWaitElapsed()
This method tests whether the power on wait time has elapsed.
unsigned long m_powerOnWait
Time in sec to wait for device to boot after power on.
std::mutex m_indiMutex
Mutex for locking INDI communications.
int m_powerTargetState
Current target power state, 1=On, 0=Off, -1=Unk.
MagAO-X standard motion stage interface.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
std::vector< std::string > m_presetNames
The names of each position on the stage.
bool m_powerOnHome
If true, then the motor is homed at startup (by this software or actual power on)
float m_preset_target
The target numerical preset position [1.0 is index 0 in the preset name vector].
pcf::IndiProperty m_indiP_home
Command the stage to home. .
bool m_defaultPositions
Flag controlling whether the default preset positions (the vector index) are set in loadConfig.
int updateINDI()
Update the INDI properties for this device controller.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int8_t m_moving
Whether or not the stage is moving. -2 means powered off, -1 means not homed, 0 means not moving,...
pcf::IndiProperty m_indiP_stop
Command the stage to halt.
float m_preset
The current numerical preset position [1.0 is index 0 in the preset name vector].
int appStartup()
Startup function.
std::vector< float > m_presetPositions
The positions, in arbitrary units, of each preset. If 0, then the integer position number (starting f...
int getPosition(double &pos)
Verifies current status of controller.
int recordTelem(const telem_stage *)
std::vector< std::string > validStateCodes
int getLastError(std::string &errStr)
Returns any error controller has.
virtual int appLogic()
Changes device state based on testing connection and device status.
bool m_moveOp
Flag indicating that OPERATING should not be set for a move, because it's less than m_opDelta.
int testConnection()
Tests if device is cabale of recieving/executing IO commands.
INDI_NEWCALLBACK_DECL(smc100ccCtrl, m_indiP_position)
double m_opDelta
The threshold for switching to OPERATING.
virtual void setupConfig()
Setup the configuration system (called by MagAOXApp::setup())
virtual void loadConfig()
Load the configuration system results (called by MagAOXApp::setup())
int moveTo(double position)
int recordStage(bool force=false)
virtual int appShutdown()
Do any needed shutdown tasks. Currently nothing in this app.
int recordPosition(bool force=false)
pcf::IndiProperty m_indiP_position
Indi variable for reporting the stage position.
int getCtrlState(std::string &state)
virtual int appStartup()
Checks if the device was found during loadConfig.
int makeCom(std::string &str, const std::string &com)
int splitResponse(int &axis, std::string &com, std::string &val, std::string &resp)
virtual int onPowerOff()
This method is called when the change to poweroff is detected.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
@ OPERATING
The device is operating, other than homing.
@ NODEVICE
No device exists for the application to control.
@ NOTHOMED
The device has not been homed.
@ HOMING
The device is homing.
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
@ 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.
@ UNINITIALIZED
The application is unitialized, the default.
@ INITIALIZED
The application has been initialized, set just before calling appStartup().
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
int ttyWriteRead(std::string &strRead, const std::string &strWrite, const std::string &eot, bool swallowEcho, int fd, int timeoutWrite, int timeoutRead)
Write to a tty on an open file descriptor, then get the result.
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
Definition ttyErrors.cpp:15
int ttyWrite(const std::string &buffWrite, int fd, int timeoutWrite)
Write to the tty console indicated by a file descriptor.
#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
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
An input/output capable device.
Definition ioDevice.hpp:27
unsigned m_writeTimeout
The write timeout [msec].
Definition ioDevice.hpp:29
int loadConfig(mx::app::appConfigurator &config)
Load the device section from an application configurator.
Definition ioDevice.cpp:28
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the device section.
Definition ioDevice.cpp:20
unsigned m_readTimeout
The read timeout [msec].
Definition ioDevice.hpp:28
A device base class which saves telemetry.
Definition telemeter.hpp:69
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
int appStartup()
Starts the telemetry log thread.
Log entry recording position stage specific status.
Log entry recording stdMotionStage status.
A simple text log, a string-type log.
Definition text_log.hpp:24
A USB device as a TTY device.
Definition usbDevice.hpp:33
std::string m_deviceName
The device path name, e.g. /dev/ttyUSB0.
Definition usbDevice.hpp:40
int m_fileDescrip
The file descriptor.
Definition usbDevice.hpp:42
int connect()
Connect to the device.
int getDeviceName()
Get the device name from udev using the vendor, product, and serial number.
std::string m_idProduct
The product id 4-digit code.
Definition usbDevice.hpp:35
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the USB section.
Definition usbDevice.cpp:24
std::string m_serial
The serial number.
Definition usbDevice.hpp:36
int loadConfig(mx::app::appConfigurator &config)
Load the USB section from an application configurator.
Definition usbDevice.cpp:34
speed_t m_baudRate
The baud rate specification.
Definition usbDevice.hpp:38
std::string m_idVendor
The vendor id 4-digit code.
Definition usbDevice.hpp:34
#define TTY_E_NODEVNAMES
Definition ttyErrors.hpp:28
#define TTY_E_DEVNOTFOUND
Definition ttyErrors.hpp:30
#define TTY_E_NOERROR
Definition ttyErrors.hpp:15