API
 
Loading...
Searching...
No Matches
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
16typedef 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
39namespace MagAOX
40{
41namespace app
42{
43
45{
46
47 //Give the test harness access.
48 friend class zaberLowLevel_test;
49
50
51protected:
52
53 int m_numStages {0};
54
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
63public:
64 /// Default c'tor.
66
67 /// D'tor, declared and defined for noexcept.
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
97protected:
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 ///Current temperature of the stage.
111 pcf::IndiProperty m_indiP_warn;
112
113 ///Target raw position of the stage.
114 pcf::IndiProperty m_indiP_tgt_pos;
115
116 ///Target relative position of the stage.
117 pcf::IndiProperty m_indiP_tgt_relpos;
118
119 ///Command a stage to home.
120 pcf::IndiProperty m_indiP_req_home;
121
122 ///Command a stage to safely halt.
123 pcf::IndiProperty m_indiP_req_halt;
124
125 ///Command a stage to safely immediately halt.
126 pcf::IndiProperty m_indiP_req_ehalt;
127
128public:
134
135};
136
137zaberLowLevel::zaberLowLevel() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
138{
139 m_powerMgtEnabled = true;
140
141 return;
142}
143
145{
147
148
149}
150
152{
153
154 this->m_baudRate = B115200; //default for Zaber stages. Will be overridden by any config setting.
155
156 int rv = tty::usbDevice::loadConfig(config);
157
158 if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
159 {
161 }
162
163 std::vector<std::string> sections;
164
165 config.unusedSections(sections);
166
167 if(sections.size() == 0)
168 {
169 log<software_error>({__FILE__, __LINE__, "No stages found"});
170 return;
171 }
172
173 for(size_t n=0; n<sections.size(); ++n)
174 {
175 if(config.isSetUnused(mx::app::iniFile::makeKey(sections[n], "serial" )))
176 {
177 m_stages.push_back(zaberStage<zaberLowLevel>(this));
178
179 size_t idx = m_stages.size()-1;
180
181 m_stages[idx].name(sections[n]);
182
183
184 //Get serial number from config.
185 std::string tmp = m_stages[idx].serial(); //get default
186 config.configUnused( tmp , mx::app::iniFile::makeKey(sections[n], "serial" ) );
187 m_stages[idx].serial(tmp);
188
189 m_stageName.insert( {m_stages[idx].name(), idx});
190 m_stageSerial.insert( {m_stages[idx].serial(), idx});
191 }
192 }
193}
194
196{
197 if(m_port > 0)
198 {
199 int rv = za_disconnect(m_port);
200 if(rv < 0)
201 {
202 log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
203 }
204 m_port = 0;
205 }
206
207
208 if(m_port <= 0)
209 {
210
211 int zrv;
212
213 {//scope for elPriv
214 elevatedPrivileges elPriv(this);
215 zrv = za_connect(&m_port, m_deviceName.c_str());
216 }
217
218 if(zrv != Z_SUCCESS)
219 {
220 if(m_port > 0)
221 {
223 m_port = 0;
224 }
225
226 if(!stateLogged())
227 {
228 log<software_error>({__FILE__, __LINE__, "can not connect to zaber stage(s)"});
229 }
230
231 return ZC_NOT_CONNECTED; //We aren't connected.
232 }
233 }
234
235 if(m_port <= 0)
236 {
237 //state(stateCodes::ERROR); //Should not get this here. Probably means no device.
238 log<text_log>("can not connect to zaber stage(s): no port", logPrio::LOG_WARNING);
239 return ZC_NOT_CONNECTED; //We aren't connected.
240 }
241
243
244 int rv = za_drain(m_port);
245
246 if(rv != Z_SUCCESS)
247 {
248 log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
250 return ZC_ERROR;
251 }
252
253 char buffer[256];
254
255 //===== First renumber so they are unique.
256 log<text_log>("Sending: / renumber", logPrio::LOG_DEBUG);
257 std::string renum = "/ renumber";
258 int nwr = za_send(m_port, renum.c_str(), renum.size());
259
261 {
262 log<text_log>("Error sending renumber query to stages", logPrio::LOG_ERROR);
264 return ZC_ERROR;
265 }
266
267 //===== Drain the result
269
270 rv = za_drain(m_port);
271
272 if(rv != Z_SUCCESS)
273 {
274 log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
276 return ZC_ERROR;
277 }
278
279 //======= Now find the stages
280 log<text_log>("Sending: / get system.serial", logPrio::LOG_DEBUG);
281 std::string gss = "/ get system.serial";
282 nwr = za_send(m_port, gss.c_str(), gss.size() );
283
285 {
286 log<text_log>("Error sending system.serial query to stages", logPrio::LOG_ERROR);
288 return ZC_ERROR;
289 }
290
291 std::string serialRes;
292 while(1)
293 {
294 int nrd = za_receive(m_port, buffer, sizeof(buffer));
295 if(nrd >= 0 )
296 {
297 buffer[nrd] = '\0';
298 log<text_log>(std::string("Received: ")+buffer, logPrio::LOG_DEBUG);
299 serialRes += buffer;
300 }
301 else if( nrd != Z_ERROR_TIMEOUT)
302 {
303 log<text_log>("Error receiving from stages", logPrio::LOG_ERROR);
305 return ZC_ERROR;
306 }
307 else
308 {
310 break; //Timeout ok.
311 }
312 }
313
314 return loadStages( serialRes );
315}
316
317int zaberLowLevel::loadStages( std::string & serialRes )
318{
319 std::vector<int> addresses;
320 std::vector<std::string> serials;
321
323 if( rv < 0)
324 {
325 log<software_error>({__FILE__, __LINE__, errno, rv, "error in parseSystemSerial"});
327 return ZC_ERROR;
328 }
329 else
330 {
331 log<text_log>("Found " + std::to_string(addresses.size()) + " stages.");
332 m_stageAddress.clear(); //We clear this map before re-populating.
333 for(size_t n=0;n<addresses.size(); ++n)
334 {
335 if( m_stageSerial.count( serials[n] ) == 1)
336 {
337 m_stages[m_stageSerial[serials[n]]].deviceAddress(addresses[n]);
338
340 log<text_log>("stage @" + std::to_string(addresses[n]) + " with s/n " + serials[n] + " corresponds to " + m_stages[m_stageSerial[serials[n]]].name());
341 }
342 else
343 {
344 log<text_log>("Unkown stage @" + std::to_string(addresses[n]) + " with s/n " + serials[n], logPrio::LOG_WARNING);
345 }
346 }
347
348 for(size_t n=0; n < m_stages.size(); ++n)
349 {
350 if(m_stages[n].deviceAddress() < 1)
351 {
352 log<text_log>("stage " + m_stages[n].name() + " with with s/n " + serials[n] + " not found in system.", logPrio::LOG_ERROR);
353 state(state(), true);
354 }
355 }
356 }
357
358 return ZC_CONNECTED;
359}
360
361
363{
365 {
366 log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
367 return -1;
368 }
369
370 if( m_stages.size() == 0 )
371 {
372 log<text_log>( "No stages configured.", logPrio::LOG_CRITICAL);
373 return -1;
374 }
375
376 REG_INDI_NEWPROP_NOCB(m_indiP_curr_state, "curr_state", pcf::IndiProperty::Text);
377 for(size_t n=0; n< m_stages.size(); ++n)
378 {
379 m_indiP_curr_state.add (pcf::IndiElement(m_stages[n].name()));
380 }
381
382 REG_INDI_NEWPROP_NOCB(m_indiP_max_pos, "max_pos", pcf::IndiProperty::Text);
383 for(size_t n=0; n< m_stages.size(); ++n)
384 {
385 m_indiP_max_pos.add (pcf::IndiElement(m_stages[n].name()));
386 m_indiP_max_pos[m_stages[n].name()] = -1;
387 }
388
389 REG_INDI_NEWPROP_NOCB(m_indiP_curr_pos, "curr_pos", pcf::IndiProperty::Number);
390 for(size_t n=0; n< m_stages.size(); ++n)
391 {
392 m_indiP_curr_pos.add (pcf::IndiElement(m_stages[n].name()));
393 }
394
395 REG_INDI_NEWPROP_NOCB(m_indiP_temp, "temp", pcf::IndiProperty::Number);
396 for(size_t n=0; n< m_stages.size(); ++n)
397 {
398 m_indiP_temp.add (pcf::IndiElement(m_stages[n].name()));
399 }
400
401 REG_INDI_NEWPROP_NOCB(m_indiP_warn, "warning", pcf::IndiProperty::Switch);
402 m_indiP_warn.setRule(pcf::IndiProperty::AnyOfMany);
403 for(size_t n=0; n< m_stages.size(); ++n)
404 {
405 m_indiP_warn.add (pcf::IndiElement(m_stages[n].name()));
406 m_indiP_warn[m_stages[n].name()].setSwitchState(pcf::IndiElement::Off);
407 }
408
409 REG_INDI_NEWPROP(m_indiP_tgt_pos, "tgt_pos", pcf::IndiProperty::Number);
410 for(size_t n=0; n< m_stages.size(); ++n)
411 {
412 m_indiP_tgt_pos.add (pcf::IndiElement(m_stages[n].name()));
413 }
414
415 /*--> Kill this */
416 REG_INDI_NEWPROP(m_indiP_tgt_relpos, "tgt_relpos", pcf::IndiProperty::Number);
417 for(size_t n=0; n< m_stages.size(); ++n)
418 {
419 m_indiP_tgt_relpos.add (pcf::IndiElement(m_stages[n].name()));
420 }
421
422 /*--> Make a switch */
423 REG_INDI_NEWPROP(m_indiP_req_home, "req_home", pcf::IndiProperty::Number);
424 for(size_t n=0; n< m_stages.size(); ++n)
425 {
426 m_indiP_req_home.add (pcf::IndiElement(m_stages[n].name()));
427 }
428
429 /*--> Make a switch */
430 REG_INDI_NEWPROP(m_indiP_req_halt, "req_halt", pcf::IndiProperty::Number);
431 for(size_t n=0; n< m_stages.size(); ++n)
432 {
433 m_indiP_req_halt.add (pcf::IndiElement(m_stages[n].name()));
434 }
435
436 /*--> Make a switch */
437 REG_INDI_NEWPROP(m_indiP_req_ehalt, "req_ehalt", pcf::IndiProperty::Number);
438 for(size_t n=0; n< m_stages.size(); ++n)
439 {
440 m_indiP_req_ehalt.add (pcf::IndiElement(m_stages[n].name()));
441 }
442
443 return 0;
444}
445
447{
449 {
450 log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
451 return -1;
452 }
453
454 if( state() == stateCodes::POWERON)
455 {
457 for(size_t i=0; i < m_stages.size();++i)
458 {
459 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
460 }
461 }
462
463 if( state() == stateCodes::NODEVICE )
464 {
466 if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
467 {
468 if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
469
471 if(!stateLogged())
472 {
474 }
475 return -1;
476 }
477
479 {
481
482 if(!stateLogged())
483 {
484 std::stringstream logs;
485 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
486 log<text_log>(logs.str());
487 }
488 return 0;
489 }
490 else
491 {
492 std::stringstream logs;
493 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
494 log<text_log>(logs.str());
495
497
498 for(size_t i=0; i < m_stages.size();++i)
499 {
500 if(m_stages[i].deviceAddress() < 1) continue;
501 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTCONNECTED"));
502 }
503
504 return 0; //we return to give the stage time to initialize the connection if this is a USB-FTDI power on/plug-in event.
505 }
506
507 }
508
510 {
511 std::lock_guard<std::mutex> guard(m_indiMutex);
512
513 int rv = connect();
514
515 if( rv == ZC_CONNECTED)
516 {
518 for(size_t i=0; i < m_stages.size();++i)
519 {
520 if(m_stages[i].deviceAddress() < 1) continue;
521 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("CONNECTED"));
522 }
523
524 if(!stateLogged())
525 {
526 log<text_log>("Connected to stage(s) on " + m_deviceName);
527 }
528 }
529 else if(rv == ZC_NOT_CONNECTED)
530 {
531 return 0;
532 }
533 else
534 {
535
536 }
537 }
538
540 {
541 for(size_t i=0; i < m_stages.size();++i)
542 {
543 if(m_stages[i].deviceAddress() < 1)
544 {
545 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
546 continue; //Skip configured but not found stage
547 }
548 std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
549
550 m_stages[i].getMaxPos(m_port);
551 std::cerr << i << " " << m_stages[i].name() << " " << m_stages[i].maxPos() << "\n";
552 updateIfChanged(m_indiP_max_pos, m_stages[i].name(), m_stages[i].maxPos());
553
554 //Get warnings so first pass through has correct state for home/not-homed
555 if(m_stages[i].getWarnings(m_port) < 0)
556 {
557 if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
560 return 0;
561 }
562
563 }
565
566 return 0;
567 }
568
569 if( state() == stateCodes::READY )
570 {
571
572 //Here we check complete stage state.
573 for(size_t i=0; i < m_stages.size();++i)
574 {
575 if(m_stages[i].deviceAddress() < 1) continue; //Skip configured but not found stage
576
577 std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
578
579 m_stages[i].updatePos(m_port);
580
582
583 if(m_stages[i].rawPos() == m_stages[i].tgtPos())
584 {
585 updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
586 }
587 else
588 {
589 updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), m_stages[i].tgtPos());
590 }
591
592
593 if(m_stages[i].deviceStatus() == 'B')
594 {
595 if(m_stages[i].homing())
596 {
597 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("HOMING"));
598 }
599 else
600 {
601 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("OPERATING"));
602 }
603 }
604 else if(m_stages[i].deviceStatus() == 'I')
605 {
606 if(m_stages[i].homing())
607 {
608 std::cerr << __FILE__ << " " << __LINE__ << "\n";
609 return 0;
610 }
611 if(m_stages[i].warnWR())
612 {
613 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTHOMED"));
614 }
615 else
616 {
617 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("READY"));
618 }
619 }
620 else updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
621
622 if(m_stages[i].warn())
623 {
624 updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::On);
625 }
626 else
627 {
628 updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::Off);
629 }
630
631 m_stages[i].updateTemp(m_port);
632 updateIfChanged(m_indiP_temp, m_stages[i].name(), m_stages[i].temp());
633
634 if(m_stages[i].getWarnings(m_port) < 0)
635 {
636 if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
639 return 0;
640 }
641
642 }
643 }
644
645 if( state() == stateCodes::ERROR )
646 {
648 if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
649 {
650 if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
652 for(size_t i=0; i < m_stages.size();++i)
653 {
654 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
655 }
656 if(!stateLogged())
657 {
659 }
660 return rv;
661 }
662
664 {
665 if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
667 for(size_t i=0; i < m_stages.size();++i)
668 {
669 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
670 }
671
672 if(!stateLogged())
673 {
674 std::stringstream logs;
675 logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
676 log<text_log>(logs.str());
677 }
678 return 0;
679 }
680
681 if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
683 for(size_t i=0; i < m_stages.size();++i)
684 {
685 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
686 }
687
689 log<text_log>("Error NOT due to loss of USB connection. I can't fix it myself.", logPrio::LOG_CRITICAL);
690 }
691
692
693
694 if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
695
696 if( state() == stateCodes::FAILURE )
697 {
698 return -1;
699 }
700
701 return 0;
702}
703
704inline
706{
707 int rv = za_disconnect(m_port);
708 if(rv < 0)
709 {
710 log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
711 }
712
713 m_port = 0;
714
715 std::lock_guard<std::mutex> lock(m_indiMutex);
716
717 for(size_t i=0; i < m_stages.size();++i)
718 {
719 updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
720 updateIfChanged(m_indiP_tgt_relpos, m_stages[i].name(), std::string(""));
721 updateIfChanged(m_indiP_temp, m_stages[i].name(), std::string(""));
722
723 m_stages[i].onPowerOff();
724
725 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("POWEROFF"));
726
727 updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::Off);
728 }
729 return 0;
730}
731
732inline
734{
735 return 0;
736}
737
738inline
740{
741 for(size_t i=0; i < m_stages.size();++i)
742 {
743 if(m_stages[i].deviceAddress() < 1) continue;
744 updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
745 }
746
747 return 0;
748}
749
750INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_pos)(const pcf::IndiProperty &ipRecv)
751{
752 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_pos, ipRecv);
753
754 for(size_t n=0; n < m_stages.size(); ++n)
755 {
756 if( ipRecv.find(m_stages[n].name()) )
757 {
758 long tgt = ipRecv[m_stages[n].name()].get<long>();
759 if(tgt >= 0)
760 {
761 if(m_stages[n].deviceAddress() < 1)
762 {
763 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
764 }
765
766 std::lock_guard<std::mutex> guard(m_indiMutex);
767 updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
768 return m_stages[n].moveAbs(m_port, tgt);
769 }
770 }
771 }
772
773 return 0;
774}
775
776INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_relpos)(const pcf::IndiProperty &ipRecv)
777{
778 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_relpos, ipRecv);
779
780 for(size_t n=0; n < m_stages.size(); ++n)
781 {
782 if( ipRecv.find(m_stages[n].name()) )
783 {
784 long tgt = ipRecv[m_stages[n].name()].get<long>();
785 tgt += m_stages[n].rawPos();
786 if(tgt >= 0)
787 {
788 if(m_stages[n].deviceAddress() < 1)
789 {
790 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
791 }
792 std::lock_guard<std::mutex> guard(m_indiMutex);
793
794 updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
795 return m_stages[n].moveAbs(m_port, tgt);
796 }
797 }
798 }
799
800 return 0;
801}
802
803INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_home)(const pcf::IndiProperty &ipRecv)
804{
805 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_home, ipRecv);
806
807 for(size_t n=0; n < m_stages.size(); ++n)
808 {
809 if( ipRecv.find(m_stages[n].name()) )
810 {
811 int tgt = ipRecv[m_stages[n].name()].get<int>();
812 if(tgt > 0)
813 {
814 if(m_stages[n].deviceAddress() < 1)
815 {
816 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
817 }
818 std::lock_guard<std::mutex> guard(m_indiMutex);
819 return m_stages[n].home(m_port);
820 }
821 }
822 }
823
824 return 0;
825}
826
827INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_halt)(const pcf::IndiProperty &ipRecv)
828{
829 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_halt, ipRecv);
830
831
832 for(size_t n=0; n < m_stages.size(); ++n)
833 {
834 if( ipRecv.find(m_stages[n].name()) )
835 {
836 int tgt = ipRecv[m_stages[n].name()].get<int>();
837 if(tgt > 0)
838 {
839 if(m_stages[n].deviceAddress() < 1)
840 {
841 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
842 }
843
844 std::lock_guard<std::mutex> guard(m_indiMutex);
845 return m_stages[n].stop(m_port);
846 }
847 }
848 }
849
850 return 0;
851}
852
853INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_ehalt)(const pcf::IndiProperty &ipRecv)
854{
855 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_ehalt, ipRecv);
856
857 for(size_t n=0; n < m_stages.size(); ++n)
858 {
859 if( ipRecv.find(m_stages[n].name()) )
860 {
861 int tgt = ipRecv[m_stages[n].name()].get<int>();
862 if(tgt > 0)
863 {
864 if(m_stages[n].deviceAddress() < 1)
865 {
866 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
867 }
868
869 std::lock_guard<std::mutex> guard(m_indiMutex);
870 return m_stages[n].estop(m_port);
871 }
872 }
873 }
874
875 return 0;
876}
877
878} //namespace app
879} //namespace MagAOX
880
881#endif //zaberLowLevel_hpp
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
stateCodes::stateCodeT state()
Get the current state code.
int powerState()
Returns the current power state.
int powerStateTarget()
Returns the target power state.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::mutex m_indiMutex
Mutex for locking INDI communications.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_ehalt)
pcf::IndiProperty m_indiP_req_home
Command a stage to home.
pcf::IndiProperty m_indiP_warn
Current temperature of the stage.
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.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
@ NODEVICE
No device exists for the application to control.
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ UNINITIALIZED
The application is unitialized, the default.
@ INITIALIZED
The application has been initialized, set just before calling appStartup().
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
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.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
static constexpr logPrioT LOG_DEBUG
Used for debugging.
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.
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:151
int za_send(z_port port, const char *command, size_t sMaxSz)
Definition za_serial.c:112
int za_drain(z_port port)
Definition za_serial.c:235
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