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