API
zaberLowLevel.hpp
Go to the documentation of this file.
1 /** \file zaberLowLevel.hpp
2  * \brief The MagAO-X Low-Level Zaber Controller
3  *
4  * \ingroup zaberLowLevel_files
5  */
6 
7 #ifndef zaberLowLevel_hpp
8 #define zaberLowLevel_hpp
9 
10 #include <iostream>
11 
12 
13 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
14 #include "../../magaox_git_version.h"
15 
16 typedef MagAOX::app::MagAOXApp<true> MagAOXAppT; //This needs to be before zaberStage.hpp for logging to work.
17 
18 #include "zaberUtils.hpp"
19 #include "zaberStage.hpp"
20 #include "za_serial.h"
21 
22 #define ZC_CONNECTED (0)
23 #define ZC_ERROR (-1)
24 #define ZC_NOT_CONNECTED (10)
25 
26 /** \defgroup zaberLowLevel low-level zaber controller
27  * \brief The low-level interface to a set of chained Zaber stages
28  *
29  * <a href="../handbook/operating/software/apps/zaberLowLevel.html">Application Documentation</a>
30  *
31  * \ingroup apps
32  *
33  */
34 
35 /** \defgroup zaberLowLevel_files zaber low-level files
36  * \ingroup zaberLowLevel
37  */
38 
39 namespace MagAOX
40 {
41 namespace app
42 {
43 
44 class zaberLowLevel : public MagAOXAppT, public tty::usbDevice
45 {
46 
47  //Give the test harness access.
48  friend class zaberLowLevel_test;
49 
50 
51 protected:
52 
53  int m_numStages {0};
54 
55  z_port m_port {0};
56 
57  std::vector<zaberStage<zaberLowLevel>> m_stages;
58 
59  std::unordered_map<int, size_t> m_stageAddress;
60  std::unordered_map<std::string, size_t> m_stageSerial;
61  std::unordered_map<std::string, size_t> m_stageName;
62 
63 public:
64  /// Default c'tor.
65  zaberLowLevel();
66 
67  /// D'tor, declared and defined for noexcept.
68  ~zaberLowLevel() noexcept
69  {}
70 
71  virtual void setupConfig();
72 
73  virtual void loadConfig();
74 
75  int connect();
76 
77  int loadStages( std:: string & serialRes );
78 
79  /// Startup functions
80  /** Sets up the INDI vars.
81  *
82  */
83  virtual int appStartup();
84 
85  /// Implementation of the FSM for zaberLowLevel.
86  virtual int appLogic();
87 
88  /// Implementation of the on-power-off FSM logic
89  virtual int onPowerOff();
90 
91  /// Implementation of the while-powered-off FSM
92  virtual int whilePowerOff();
93 
94  /// Do any needed shutdown tasks. Currently nothing in this app.
95  virtual int appShutdown();
96 
97 protected:
98  ///Current state of the stage.
99  pcf::IndiProperty m_indiP_curr_state;
100 
101  ///Maximum raw position of the stage.
102  pcf::IndiProperty m_indiP_max_pos;
103 
104  ///Current raw position of the stage.
105  pcf::IndiProperty m_indiP_curr_pos;
106 
107  ///Current temperature of the stage.
108  pcf::IndiProperty m_indiP_temp;
109 
110  ///Target raw position of the stage.
111  pcf::IndiProperty m_indiP_tgt_pos;
112 
113  ///Target relative position of the stage.
114  pcf::IndiProperty m_indiP_tgt_relpos;
115 
116  ///Command a stage to home.
117  pcf::IndiProperty m_indiP_req_home;
118 
119  ///Command a stage to safely halt.
120  pcf::IndiProperty m_indiP_req_halt;
121 
122  ///Command a stage to safely immediately halt.
123  pcf::IndiProperty m_indiP_req_ehalt;
124 
125 public:
131 
132 };
133 
134 zaberLowLevel::zaberLowLevel() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
135 {
136  m_powerMgtEnabled = true;
137 
138  return;
139 }
140 
142 {
144 
145 
146 }
147 
149 {
150 
151  this->m_baudRate = B115200; //default for Zaber stages. Will be overridden by any config setting.
152 
153  int rv = tty::usbDevice::loadConfig(config);
154 
155  if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
156  {
157  log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
158  }
159 
160  std::vector<std::string> sections;
161 
162  config.unusedSections(sections);
163 
164  if(sections.size() == 0)
165  {
166  log<software_error>({__FILE__, __LINE__, "No stages found"});
167  return;
168  }
169 
170  for(size_t n=0; n<sections.size(); ++n)
171  {
172  if(config.isSetUnused(mx::app::iniFile::makeKey(sections[n], "serial" )))
173  {
174  m_stages.push_back(zaberStage<zaberLowLevel>(this));
175 
176  size_t idx = m_stages.size()-1;
177 
178  m_stages[idx].name(sections[n]);
179 
180 
181  //Get serial number from config.
182  std::string tmp = m_stages[idx].serial(); //get default
183  config.configUnused( tmp , mx::app::iniFile::makeKey(sections[n], "serial" ) );
184  m_stages[idx].serial(tmp);
185 
186  m_stageName.insert( {m_stages[idx].name(), idx});
187  m_stageSerial.insert( {m_stages[idx].serial(), idx});
188  }
189  }
190 }
191 
193 {
194  if(m_port > 0)
195  {
196  int rv = za_disconnect(m_port);
197  if(rv < 0)
198  {
199  log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
200  }
201  m_port = 0;
202  }
203 
204 
205  if(m_port <= 0)
206  {
207 
208  int zrv;
209 
210  {//scope for elPriv
211  elevatedPrivileges elPriv(this);
212  zrv = za_connect(&m_port, m_deviceName.c_str());
213  }
214 
215  if(zrv != Z_SUCCESS)
216  {
217  if(m_port > 0)
218  {
220  m_port = 0;
221  }
222 
223  if(!stateLogged())
224  {
225  log<software_error>({__FILE__, __LINE__, "can not connect to zaber stage(s)"});
226  }
227 
228  return ZC_NOT_CONNECTED; //We aren't connected.
229  }
230  }
231 
232  if(m_port <= 0)
233  {
234  //state(stateCodes::ERROR); //Should not get this here. Probably means no device.
235  log<text_log>("can not connect to zaber stage(s): no port", logPrio::LOG_WARNING);
236  return ZC_NOT_CONNECTED; //We aren't connected.
237  }
238 
239  log<text_log>("DRAINING", logPrio::LOG_DEBUG);
240 
241  int rv = za_drain(m_port);
242 
243  if(rv != Z_SUCCESS)
244  {
245  log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
247  return ZC_ERROR;
248  }
249 
250  char buffer[256];
251 
252  //===== First renumber so they are unique.
253  log<text_log>("Sending: / renumber", logPrio::LOG_DEBUG);
254  int nwr = za_send(m_port, "/ renumber");
255 
256  if(nwr == Z_ERROR_SYSTEM_ERROR)
257  {
258  log<text_log>("Error sending renumber query to stages", logPrio::LOG_ERROR);
260  return ZC_ERROR;
261  }
262 
263  //===== Drain the result
264  log<text_log>("DRAINING", logPrio::LOG_DEBUG);
265 
266  rv = za_drain(m_port);
267 
268  if(rv != Z_SUCCESS)
269  {
270  log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
272  return ZC_ERROR;
273  }
274 
275  //======= Now find the stages
276  log<text_log>("Sending: / get system.serial", logPrio::LOG_DEBUG);
277  nwr = za_send(m_port, "/ get system.serial");
278 
279  if(nwr == Z_ERROR_SYSTEM_ERROR)
280  {
281  log<text_log>("Error sending system.serial query to stages", logPrio::LOG_ERROR);
283  return ZC_ERROR;
284  }
285 
286  std::string serialRes;
287  while(1)
288  {
289  int nrd = za_receive(m_port, buffer, sizeof(buffer));
290  if(nrd >= 0 )
291  {
292  buffer[nrd] = '\0';
293  log<text_log>(std::string("Received: ")+buffer, logPrio::LOG_DEBUG);
294  serialRes += buffer;
295  }
296  else if( nrd != Z_ERROR_TIMEOUT)
297  {
298  log<text_log>("Error receiving from stages", logPrio::LOG_ERROR);
300  return ZC_ERROR;
301  }
302  else
303  {
304  log<text_log>("TIMEOUT", logPrio::LOG_DEBUG);
305  break; //Timeout ok.
306  }
307  }
308 
309  return loadStages( serialRes );
310 }
311 
312 int zaberLowLevel::loadStages( std::string & serialRes )
313 {
314  std::vector<int> addresses;
315  std::vector<std::string> serials;
316 
317  int rv = parseSystemSerial( addresses, serials, serialRes );
318  if( rv < 0)
319  {
320  log<software_error>({__FILE__, __LINE__, errno, rv, "error in parseSystemSerial"});
322  return ZC_ERROR;
323  }
324  else
325  {
326  log<text_log>("Found " + std::to_string(addresses.size()) + " stages.");
327  m_stageAddress.clear(); //We clear this map before re-populating.
328  for(size_t n=0;n<addresses.size(); ++n)
329  {
330  if( m_stageSerial.count( serials[n] ) == 1)
331  {
332  m_stages[m_stageSerial[serials[n]]].deviceAddress(addresses[n]);
333 
334  m_stageAddress.insert({ addresses[n], m_stageSerial[serials[n]]});
335  log<text_log>("stage @" + std::to_string(addresses[n]) + " with s/n " + serials[n] + " corresponds to " + m_stages[m_stageSerial[serials[n]]].name());
336  }
337  else
338  {
339  log<text_log>("Unkown stage @" + std::to_string(addresses[n]) + " with s/n " + serials[n], logPrio::LOG_WARNING);
340  }
341  }
342 
343  for(size_t n=0; n < m_stages.size(); ++n)
344  {
345  if(m_stages[n].deviceAddress() < 1)
346  {
347  log<text_log>("stage " + m_stages[n].name() + " with with s/n " + serials[n] + " not found in system.", logPrio::LOG_ERROR);
348  state(state(), true);
349  }
350  }
351  }
352 
353  return ZC_CONNECTED;
354 }
355 
356 
358 {
360  {
361  log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
362  return -1;
363  }
364 
365  if( m_stages.size() == 0 )
366  {
367  log<text_log>( "No stages configured.", logPrio::LOG_CRITICAL);
368  return -1;
369  }
370 
371  REG_INDI_NEWPROP_NOCB(m_indiP_curr_state, "curr_state", pcf::IndiProperty::Text);
372  for(size_t n=0; n< m_stages.size(); ++n)
373  {
374  m_indiP_curr_state.add (pcf::IndiElement(m_stages[n].name()));
375  }
376 
377  REG_INDI_NEWPROP_NOCB(m_indiP_max_pos, "max_pos", pcf::IndiProperty::Text);
378  for(size_t n=0; n< m_stages.size(); ++n)
379  {
380  m_indiP_max_pos.add (pcf::IndiElement(m_stages[n].name()));
381  m_indiP_max_pos[m_stages[n].name()] = -1;
382  }
383 
384  REG_INDI_NEWPROP_NOCB(m_indiP_curr_pos, "curr_pos", pcf::IndiProperty::Number);
385  for(size_t n=0; n< m_stages.size(); ++n)
386  {
387  m_indiP_curr_pos.add (pcf::IndiElement(m_stages[n].name()));
388  }
389 
390  REG_INDI_NEWPROP_NOCB(m_indiP_temp, "temp", pcf::IndiProperty::Number);
391  for(size_t n=0; n< m_stages.size(); ++n)
392  {
393  m_indiP_temp.add (pcf::IndiElement(m_stages[n].name()));
394  }
395 
396  REG_INDI_NEWPROP(m_indiP_tgt_pos, "tgt_pos", pcf::IndiProperty::Number);
397  for(size_t n=0; n< m_stages.size(); ++n)
398  {
399  m_indiP_tgt_pos.add (pcf::IndiElement(m_stages[n].name()));
400  }
401 
402  /*--> Kill this */
403  REG_INDI_NEWPROP(m_indiP_tgt_relpos, "tgt_relpos", pcf::IndiProperty::Number);
404  for(size_t n=0; n< m_stages.size(); ++n)
405  {
406  m_indiP_tgt_relpos.add (pcf::IndiElement(m_stages[n].name()));
407  }
408 
409  /*--> Make a switch */
410  REG_INDI_NEWPROP(m_indiP_req_home, "req_home", pcf::IndiProperty::Number);
411  for(size_t n=0; n< m_stages.size(); ++n)
412  {
413  m_indiP_req_home.add (pcf::IndiElement(m_stages[n].name()));
414  }
415 
416  /*--> Make a switch */
417  REG_INDI_NEWPROP(m_indiP_req_halt, "req_halt", pcf::IndiProperty::Number);
418  for(size_t n=0; n< m_stages.size(); ++n)
419  {
420  m_indiP_req_halt.add (pcf::IndiElement(m_stages[n].name()));
421  }
422 
423  /*--> Make a switch */
424  REG_INDI_NEWPROP(m_indiP_req_ehalt, "req_ehalt", pcf::IndiProperty::Number);
425  for(size_t n=0; n< m_stages.size(); ++n)
426  {
427  m_indiP_req_ehalt.add (pcf::IndiElement(m_stages[n].name()));
428  }
429 
430  return 0;
431 }
432 
434 {
435  if( state() == stateCodes::INITIALIZED )
436  {
437  log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
438  return -1;
439  }
440 
441  if( state() == stateCodes::POWERON)
442  {
444  for(size_t i=0; i < m_stages.size();++i)
445  {
446  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
447  }
448  }
449 
450  if( state() == stateCodes::NODEVICE )
451  {
453  if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
454  {
455  if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
456 
458  if(!stateLogged())
459  {
460  log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
461  }
462  return -1;
463  }
464 
465  if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
466  {
468 
469  if(!stateLogged())
470  {
471  std::stringstream logs;
472  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
473  log<text_log>(logs.str());
474  }
475  return 0;
476  }
477  else
478  {
479  std::stringstream logs;
480  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
481  log<text_log>(logs.str());
482 
484 
485  for(size_t i=0; i < m_stages.size();++i)
486  {
487  if(m_stages[i].deviceAddress() < 1) continue;
488  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTCONNECTED"));
489  }
490 
491  return 0; //we return to give the stage time to initialize the connection if this is a USB-FTDI power on/plug-in event.
492  }
493 
494  }
495 
497  {
498  std::lock_guard<std::mutex> guard(m_indiMutex);
499 
500  int rv = connect();
501 
502  if( rv == ZC_CONNECTED)
503  {
505  for(size_t i=0; i < m_stages.size();++i)
506  {
507  if(m_stages[i].deviceAddress() < 1) continue;
508  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("CONNECTED"));
509  }
510 
511  if(!stateLogged())
512  {
513  log<text_log>("Connected to stage(s) on " + m_deviceName);
514  }
515  }
516  else if(rv == ZC_NOT_CONNECTED)
517  {
518  return 0;
519  }
520  else
521  {
522 
523  }
524  }
525 
526  if( state() == stateCodes::CONNECTED )
527  {
528  for(size_t i=0; i < m_stages.size();++i)
529  {
530  if(m_stages[i].deviceAddress() < 1)
531  {
532  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
533  continue; //Skip configured but not found stage
534  }
535  std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
536 
537  m_stages[i].getMaxPos(m_port);
538  std::cerr << i << " " << m_stages[i].name() << " " << m_stages[i].maxPos() << "\n";
539  updateIfChanged(m_indiP_max_pos, m_stages[i].name(), m_stages[i].maxPos());
540 
541  //Get warnings so first pass through has correct state for home/not-homed
542  if(m_stages[i].getWarnings(m_port) < 0)
543  {
544  if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
545  log<software_error>({__FILE__, __LINE__});
547  return 0;
548  }
549 
550  }
552 
553  return 0;
554  }
555 
556  if( state() == stateCodes::READY )
557  {
558 
559  //Here we check complete stage state.
560  for(size_t i=0; i < m_stages.size();++i)
561  {
562  if(m_stages[i].deviceAddress() < 1) continue; //Skip configured but not found stage
563 
564  std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
565 
566  m_stages[i].updatePos(m_port);
567 
568  updateIfChanged(m_indiP_curr_pos, m_stages[i].name(), m_stages[i].rawPos());
569 
570  if(m_stages[i].rawPos() == m_stages[i].tgtPos())
571  {
572  updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
573  }
574  else
575  {
576  updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), m_stages[i].tgtPos());
577  }
578 
579 
580  if(m_stages[i].deviceStatus() == 'B')
581  {
582  if(m_stages[i].homing())
583  {
584  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("HOMING"));
585  }
586  else
587  {
588  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("OPERATING"));
589  }
590  }
591  else if(m_stages[i].deviceStatus() == 'I')
592  {
593  if(m_stages[i].homing())
594  {
595  std::cerr << __FILE__ << " " << __LINE__ << "\n";
596  return 0;
597  }
598  if(m_stages[i].warnWR())
599  {
600  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTHOMED"));
601  }
602  else
603  {
604  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("READY"));
605  }
606  }
607  else updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
608 
609  m_stages[i].updateTemp(m_port);
610  updateIfChanged(m_indiP_temp, m_stages[i].name(), m_stages[i].temp());
611 
612  if(m_stages[i].getWarnings(m_port) < 0)
613  {
614  if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
615  log<software_error>({__FILE__, __LINE__});
617  return 0;
618  }
619 
620  }
621  }
622 
623  if( state() == stateCodes::ERROR )
624  {
626  if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
627  {
628  if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
630  for(size_t i=0; i < m_stages.size();++i)
631  {
632  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
633  }
634  if(!stateLogged())
635  {
636  log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
637  }
638  return rv;
639  }
640 
641  if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
642  {
643  if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
645  for(size_t i=0; i < m_stages.size();++i)
646  {
647  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
648  }
649 
650  if(!stateLogged())
651  {
652  std::stringstream logs;
653  logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
654  log<text_log>(logs.str());
655  }
656  return 0;
657  }
658 
659  if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
661  for(size_t i=0; i < m_stages.size();++i)
662  {
663  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
664  }
665 
666  log<software_critical>({__FILE__, __LINE__});
667  log<text_log>("Error NOT due to loss of USB connection. I can't fix it myself.", logPrio::LOG_CRITICAL);
668  }
669 
670 
671 
672  if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
673 
674  if( state() == stateCodes::FAILURE )
675  {
676  return -1;
677  }
678 
679  return 0;
680 }
681 
682 inline
684 {
685  int rv = za_disconnect(m_port);
686  if(rv < 0)
687  {
688  log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
689  }
690 
691 
692  m_port = 0;
693 
694  std::lock_guard<std::mutex> lock(m_indiMutex);
695 
696  for(size_t i=0; i < m_stages.size();++i)
697  {
698  updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
699  updateIfChanged(m_indiP_tgt_relpos, m_stages[i].name(), std::string(""));
700  updateIfChanged(m_indiP_temp, m_stages[i].name(), std::string(""));
701 
702  m_stages[i].onPowerOff();
703 
704  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("POWEROFF"));
705 
706 
707  }
708  return 0;
709 }
710 
711 inline
713 {
714  return 0;
715 }
716 
717 inline
719 {
720  for(size_t i=0; i < m_stages.size();++i)
721  {
722  if(m_stages[i].deviceAddress() < 1) continue;
723  updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
724  }
725 
726  return 0;
727 }
728 
729 INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_pos)(const pcf::IndiProperty &ipRecv)
730 {
731  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_pos, ipRecv);
732 
733  for(size_t n=0; n < m_stages.size(); ++n)
734  {
735  if( ipRecv.find(m_stages[n].name()) )
736  {
737  long tgt = ipRecv[m_stages[n].name()].get<long>();
738  if(tgt >= 0)
739  {
740  if(m_stages[n].deviceAddress() < 1)
741  {
742  return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
743  }
744 
745  std::lock_guard<std::mutex> guard(m_indiMutex);
746  updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
747  return m_stages[n].moveAbs(m_port, tgt);
748  }
749  }
750  }
751 
752  return 0;
753 }
754 
755 INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_relpos)(const pcf::IndiProperty &ipRecv)
756 {
757  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_relpos, ipRecv);
758 
759  for(size_t n=0; n < m_stages.size(); ++n)
760  {
761  if( ipRecv.find(m_stages[n].name()) )
762  {
763  long tgt = ipRecv[m_stages[n].name()].get<long>();
764  tgt += m_stages[n].rawPos();
765  if(tgt >= 0)
766  {
767  if(m_stages[n].deviceAddress() < 1)
768  {
769  return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
770  }
771  std::lock_guard<std::mutex> guard(m_indiMutex);
772 
773  updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
774  return m_stages[n].moveAbs(m_port, tgt);
775  }
776  }
777  }
778 
779  return 0;
780 }
781 
782 INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_home)(const pcf::IndiProperty &ipRecv)
783 {
784  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_home, ipRecv);
785 
786  for(size_t n=0; n < m_stages.size(); ++n)
787  {
788  if( ipRecv.find(m_stages[n].name()) )
789  {
790  int tgt = ipRecv[m_stages[n].name()].get<int>();
791  if(tgt > 0)
792  {
793  if(m_stages[n].deviceAddress() < 1)
794  {
795  return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
796  }
797  std::lock_guard<std::mutex> guard(m_indiMutex);
798  return m_stages[n].home(m_port);
799  }
800  }
801  }
802 
803  return 0;
804 }
805 
806 INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_halt)(const pcf::IndiProperty &ipRecv)
807 {
808  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_halt, ipRecv);
809 
810 
811  for(size_t n=0; n < m_stages.size(); ++n)
812  {
813  if( ipRecv.find(m_stages[n].name()) )
814  {
815  int tgt = ipRecv[m_stages[n].name()].get<int>();
816  if(tgt > 0)
817  {
818  if(m_stages[n].deviceAddress() < 1)
819  {
820  return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
821  }
822 
823  std::lock_guard<std::mutex> guard(m_indiMutex);
824  return m_stages[n].stop(m_port);
825  }
826  }
827  }
828 
829  return 0;
830 }
831 
832 INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_ehalt)(const pcf::IndiProperty &ipRecv)
833 {
834  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_ehalt, ipRecv);
835 
836  for(size_t n=0; n < m_stages.size(); ++n)
837  {
838  if( ipRecv.find(m_stages[n].name()) )
839  {
840  int tgt = ipRecv[m_stages[n].name()].get<int>();
841  if(tgt > 0)
842  {
843  if(m_stages[n].deviceAddress() < 1)
844  {
845  return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
846  }
847 
848  std::lock_guard<std::mutex> guard(m_indiMutex);
849  return m_stages[n].estop(m_port);
850  }
851  }
852  }
853 
854  return 0;
855 }
856 
857 } //namespace app
858 } //namespace MagAOX
859 
860 #endif //zaberLowLevel_hpp
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 powerState()
Returns the current power state.
Definition: MagAOXApp.hpp:3179
int powerStateTarget()
Returns the target power state.
Definition: MagAOXApp.hpp:3187
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2140
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
bool m_powerMgtEnabled
Flag controls whether power mgt is used. Set this in the constructor of a derived app....
Definition: MagAOXApp.hpp:981
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_ehalt)
pcf::IndiProperty m_indiP_req_home
Command a stage to home.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_halt)
virtual int appLogic()
Implementation of the FSM for zaberLowLevel.
pcf::IndiProperty m_indiP_max_pos
Maximum raw position of the stage.
pcf::IndiProperty m_indiP_req_halt
Command a stage to safely halt.
virtual int onPowerOff()
Implementation of the on-power-off FSM logic.
pcf::IndiProperty m_indiP_tgt_pos
Target raw position of the stage.
pcf::IndiProperty m_indiP_temp
Current temperature of the stage.
std::unordered_map< std::string, size_t > m_stageSerial
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_tgt_relpos)
std::unordered_map< int, size_t > m_stageAddress
pcf::IndiProperty m_indiP_curr_state
Current state of the stage.
int loadStages(std::string &serialRes)
virtual int whilePowerOff()
Implementation of the while-powered-off FSM.
pcf::IndiProperty m_indiP_curr_pos
Current raw position of the stage.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_tgt_pos)
virtual int appStartup()
Startup functions.
virtual int appShutdown()
Do any needed shutdown tasks. Currently nothing in this app.
std::vector< zaberStage< zaberLowLevel > > m_stages
pcf::IndiProperty m_indiP_tgt_relpos
Target relative position of the stage.
~zaberLowLevel() noexcept
D'tor, declared and defined for noexcept.
std::unordered_map< std::string, size_t > m_stageName
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_home)
pcf::IndiProperty m_indiP_req_ehalt
Command a stage to safely immediately halt.
A class to manage the details of one stage in a Zaber system.
Definition: zaberStage.hpp:29
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
Definition: indiMacros.hpp:247
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:229
@ NODEVICE
No device exists for the application to control.
Definition: stateCodes.hpp:41
@ 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
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
Definition: ttyErrors.cpp:15
int parseSystemSerial(std::vector< int > &address, std::vector< std::string > &serial, const std::string &response)
Parse the system.serial query.
Definition: zaberUtils.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_DEBUG
Used for debugging.
Definition: logPriority.hpp:52
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
Software ERR log entry.
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 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
@ Z_ERROR_SYSTEM_ERROR
Definition: z_common.h:67
@ Z_SUCCESS
Definition: z_common.h:65
@ Z_ERROR_TIMEOUT
Definition: z_common.h:78
int za_connect(z_port *port, const char *port_name)
Definition: za_serial.c:53
int za_receive(z_port port, char *destination, int length)
Definition: za_serial.c:148
int za_send(z_port port, const char *command)
Definition: za_serial.c:112
int za_drain(z_port port)
Definition: za_serial.c:232
int za_disconnect(z_port port)
Definition: za_serial.c:99
Provides a set of functions for interacting with Zaber devices in the ASCII protocol.
#define ZC_NOT_CONNECTED
#define ZC_ERROR
#define ZC_CONNECTED
MagAOX::app::MagAOXApp< true > MagAOXAppT
A class with details of a single zaber stage.
utilties for working with zaber stages