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