Line data Source code
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 21 : ~zaberLowLevel() noexcept
69 21 : {}
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 : ///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 :
128 : public:
129 0 : INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_tgt_pos);
130 0 : INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_tgt_relpos);
131 0 : INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_home);
132 0 : INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_halt);
133 0 : INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_ehalt);
134 :
135 : };
136 :
137 63 : zaberLowLevel::zaberLowLevel() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
138 : {
139 21 : m_powerMgtEnabled = true;
140 :
141 21 : return;
142 0 : }
143 :
144 0 : void zaberLowLevel::setupConfig()
145 : {
146 0 : tty::usbDevice::setupConfig(config);
147 :
148 :
149 0 : }
150 :
151 0 : void zaberLowLevel::loadConfig()
152 : {
153 :
154 0 : this->m_baudRate = B115200; //default for Zaber stages. Will be overridden by any config setting.
155 :
156 0 : int rv = tty::usbDevice::loadConfig(config);
157 :
158 0 : if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
159 : {
160 0 : log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
161 : }
162 :
163 0 : std::vector<std::string> sections;
164 :
165 0 : config.unusedSections(sections);
166 :
167 0 : if(sections.size() == 0)
168 : {
169 0 : log<software_error>({__FILE__, __LINE__, "No stages found"});
170 0 : return;
171 : }
172 :
173 0 : for(size_t n=0; n<sections.size(); ++n)
174 : {
175 0 : if(config.isSetUnused(mx::app::iniFile::makeKey(sections[n], "serial" )))
176 : {
177 0 : m_stages.push_back(zaberStage<zaberLowLevel>(this));
178 :
179 0 : size_t idx = m_stages.size()-1;
180 :
181 0 : m_stages[idx].name(sections[n]);
182 :
183 :
184 : //Get serial number from config.
185 0 : std::string tmp = m_stages[idx].serial(); //get default
186 0 : config.configUnused( tmp , mx::app::iniFile::makeKey(sections[n], "serial" ) );
187 0 : m_stages[idx].serial(tmp);
188 :
189 0 : m_stageName.insert( {m_stages[idx].name(), idx});
190 0 : m_stageSerial.insert( {m_stages[idx].serial(), idx});
191 0 : }
192 : }
193 0 : }
194 :
195 0 : int zaberLowLevel::connect()
196 : {
197 0 : if(m_port > 0)
198 : {
199 0 : int rv = za_disconnect(m_port);
200 0 : if(rv < 0)
201 : {
202 0 : log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
203 : }
204 0 : m_port = 0;
205 : }
206 :
207 :
208 0 : if(m_port <= 0)
209 : {
210 :
211 : int zrv;
212 :
213 : {//scope for elPriv
214 0 : elevatedPrivileges elPriv(this);
215 0 : zrv = za_connect(&m_port, m_deviceName.c_str());
216 0 : }
217 :
218 0 : if(zrv != Z_SUCCESS)
219 : {
220 0 : if(m_port > 0)
221 : {
222 0 : za_disconnect(m_port);
223 0 : m_port = 0;
224 : }
225 :
226 0 : if(!stateLogged())
227 : {
228 0 : log<software_error>({__FILE__, __LINE__, "can not connect to zaber stage(s)"});
229 : }
230 :
231 0 : return ZC_NOT_CONNECTED; //We aren't connected.
232 : }
233 : }
234 :
235 0 : if(m_port <= 0)
236 : {
237 : //state(stateCodes::ERROR); //Should not get this here. Probably means no device.
238 0 : log<text_log>("can not connect to zaber stage(s): no port", logPrio::LOG_WARNING);
239 0 : return ZC_NOT_CONNECTED; //We aren't connected.
240 : }
241 :
242 0 : log<text_log>("DRAINING", logPrio::LOG_DEBUG);
243 :
244 0 : int rv = za_drain(m_port);
245 :
246 0 : if(rv != Z_SUCCESS)
247 : {
248 0 : log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
249 0 : state(stateCodes::ERROR);
250 0 : return ZC_ERROR;
251 : }
252 :
253 : char buffer[256];
254 :
255 : //===== First renumber so they are unique.
256 0 : log<text_log>("Sending: / renumber", logPrio::LOG_DEBUG);
257 0 : std::string renum = "/ renumber";
258 0 : int nwr = za_send(m_port, renum.c_str(), renum.size());
259 :
260 0 : if(nwr == Z_ERROR_SYSTEM_ERROR)
261 : {
262 0 : log<text_log>("Error sending renumber query to stages", logPrio::LOG_ERROR);
263 0 : state(stateCodes::ERROR);
264 0 : return ZC_ERROR;
265 : }
266 :
267 : //===== Drain the result
268 0 : log<text_log>("DRAINING", logPrio::LOG_DEBUG);
269 :
270 0 : rv = za_drain(m_port);
271 :
272 0 : if(rv != Z_SUCCESS)
273 : {
274 0 : log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
275 0 : state(stateCodes::ERROR);
276 0 : return ZC_ERROR;
277 : }
278 :
279 : //======= Now find the stages
280 0 : log<text_log>("Sending: / get system.serial", logPrio::LOG_DEBUG);
281 0 : std::string gss = "/ get system.serial";
282 0 : nwr = za_send(m_port, gss.c_str(), gss.size() );
283 :
284 0 : if(nwr == Z_ERROR_SYSTEM_ERROR)
285 : {
286 0 : log<text_log>("Error sending system.serial query to stages", logPrio::LOG_ERROR);
287 0 : state(stateCodes::ERROR);
288 0 : return ZC_ERROR;
289 : }
290 :
291 0 : std::string serialRes;
292 : while(1)
293 : {
294 0 : int nrd = za_receive(m_port, buffer, sizeof(buffer));
295 0 : if(nrd >= 0 )
296 : {
297 0 : buffer[nrd] = '\0';
298 0 : log<text_log>(std::string("Received: ")+buffer, logPrio::LOG_DEBUG);
299 0 : serialRes += buffer;
300 : }
301 0 : else if( nrd != Z_ERROR_TIMEOUT)
302 : {
303 0 : log<text_log>("Error receiving from stages", logPrio::LOG_ERROR);
304 0 : state(stateCodes::ERROR);
305 0 : return ZC_ERROR;
306 : }
307 : else
308 : {
309 0 : log<text_log>("TIMEOUT", logPrio::LOG_DEBUG);
310 0 : break; //Timeout ok.
311 : }
312 0 : }
313 :
314 0 : return loadStages( serialRes );
315 0 : }
316 :
317 0 : int zaberLowLevel::loadStages( std::string & serialRes )
318 : {
319 0 : std::vector<int> addresses;
320 0 : std::vector<std::string> serials;
321 :
322 0 : int rv = parseSystemSerial( addresses, serials, serialRes );
323 0 : if( rv < 0)
324 : {
325 0 : log<software_error>({__FILE__, __LINE__, errno, rv, "error in parseSystemSerial"});
326 0 : state(stateCodes::ERROR);
327 0 : return ZC_ERROR;
328 : }
329 : else
330 : {
331 0 : log<text_log>("Found " + std::to_string(addresses.size()) + " stages.");
332 0 : m_stageAddress.clear(); //We clear this map before re-populating.
333 0 : for(size_t n=0;n<addresses.size(); ++n)
334 : {
335 0 : if( m_stageSerial.count( serials[n] ) == 1)
336 : {
337 0 : m_stages[m_stageSerial[serials[n]]].deviceAddress(addresses[n]);
338 :
339 0 : m_stageAddress.insert({ addresses[n], m_stageSerial[serials[n]]});
340 0 : 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 0 : log<text_log>("Unkown stage @" + std::to_string(addresses[n]) + " with s/n " + serials[n], logPrio::LOG_WARNING);
345 : }
346 : }
347 :
348 0 : for(size_t n=0; n < m_stages.size(); ++n)
349 : {
350 0 : if(m_stages[n].deviceAddress() < 1)
351 : {
352 0 : log<text_log>("stage " + m_stages[n].name() + " with with s/n " + serials[n] + " not found in system.", logPrio::LOG_ERROR);
353 0 : state(state(), true);
354 : }
355 : }
356 : }
357 :
358 0 : return ZC_CONNECTED;
359 0 : }
360 :
361 :
362 0 : int zaberLowLevel::appStartup()
363 : {
364 0 : if( state() == stateCodes::UNINITIALIZED )
365 : {
366 0 : log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
367 0 : return -1;
368 : }
369 :
370 0 : if( m_stages.size() == 0 )
371 : {
372 0 : log<text_log>( "No stages configured.", logPrio::LOG_CRITICAL);
373 0 : return -1;
374 : }
375 :
376 0 : REG_INDI_NEWPROP_NOCB(m_indiP_curr_state, "curr_state", pcf::IndiProperty::Text);
377 0 : for(size_t n=0; n< m_stages.size(); ++n)
378 : {
379 0 : m_indiP_curr_state.add (pcf::IndiElement(m_stages[n].name()));
380 : }
381 :
382 0 : REG_INDI_NEWPROP_NOCB(m_indiP_max_pos, "max_pos", pcf::IndiProperty::Text);
383 0 : for(size_t n=0; n< m_stages.size(); ++n)
384 : {
385 0 : m_indiP_max_pos.add (pcf::IndiElement(m_stages[n].name()));
386 0 : m_indiP_max_pos[m_stages[n].name()] = -1;
387 : }
388 :
389 0 : REG_INDI_NEWPROP_NOCB(m_indiP_curr_pos, "curr_pos", pcf::IndiProperty::Number);
390 0 : for(size_t n=0; n< m_stages.size(); ++n)
391 : {
392 0 : m_indiP_curr_pos.add (pcf::IndiElement(m_stages[n].name()));
393 : }
394 :
395 0 : REG_INDI_NEWPROP_NOCB(m_indiP_temp, "temp", pcf::IndiProperty::Number);
396 0 : for(size_t n=0; n< m_stages.size(); ++n)
397 : {
398 0 : m_indiP_temp.add (pcf::IndiElement(m_stages[n].name()));
399 : }
400 :
401 0 : REG_INDI_NEWPROP_NOCB(m_indiP_warn, "warning", pcf::IndiProperty::Switch);
402 0 : m_indiP_warn.setRule(pcf::IndiProperty::AnyOfMany);
403 0 : for(size_t n=0; n< m_stages.size(); ++n)
404 : {
405 0 : m_indiP_warn.add (pcf::IndiElement(m_stages[n].name()));
406 0 : m_indiP_warn[m_stages[n].name()].setSwitchState(pcf::IndiElement::Off);
407 : }
408 :
409 0 : REG_INDI_NEWPROP(m_indiP_tgt_pos, "tgt_pos", pcf::IndiProperty::Number);
410 0 : for(size_t n=0; n< m_stages.size(); ++n)
411 : {
412 0 : m_indiP_tgt_pos.add (pcf::IndiElement(m_stages[n].name()));
413 : }
414 :
415 : /*--> Kill this */
416 0 : REG_INDI_NEWPROP(m_indiP_tgt_relpos, "tgt_relpos", pcf::IndiProperty::Number);
417 0 : for(size_t n=0; n< m_stages.size(); ++n)
418 : {
419 0 : m_indiP_tgt_relpos.add (pcf::IndiElement(m_stages[n].name()));
420 : }
421 :
422 : /*--> Make a switch */
423 0 : REG_INDI_NEWPROP(m_indiP_req_home, "req_home", pcf::IndiProperty::Number);
424 0 : for(size_t n=0; n< m_stages.size(); ++n)
425 : {
426 0 : m_indiP_req_home.add (pcf::IndiElement(m_stages[n].name()));
427 : }
428 :
429 : /*--> Make a switch */
430 0 : REG_INDI_NEWPROP(m_indiP_req_halt, "req_halt", pcf::IndiProperty::Number);
431 0 : for(size_t n=0; n< m_stages.size(); ++n)
432 : {
433 0 : m_indiP_req_halt.add (pcf::IndiElement(m_stages[n].name()));
434 : }
435 :
436 : /*--> Make a switch */
437 0 : REG_INDI_NEWPROP(m_indiP_req_ehalt, "req_ehalt", pcf::IndiProperty::Number);
438 0 : for(size_t n=0; n< m_stages.size(); ++n)
439 : {
440 0 : m_indiP_req_ehalt.add (pcf::IndiElement(m_stages[n].name()));
441 : }
442 :
443 0 : return 0;
444 : }
445 :
446 0 : int zaberLowLevel::appLogic()
447 : {
448 0 : if( state() == stateCodes::INITIALIZED )
449 : {
450 0 : log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
451 0 : return -1;
452 : }
453 :
454 0 : if( state() == stateCodes::POWERON)
455 : {
456 0 : state(stateCodes::NODEVICE);
457 0 : for(size_t i=0; i < m_stages.size();++i)
458 : {
459 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
460 : }
461 : }
462 :
463 0 : if( state() == stateCodes::NODEVICE )
464 : {
465 0 : int rv = tty::usbDevice::getDeviceName();
466 0 : if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
467 : {
468 0 : if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
469 :
470 0 : state(stateCodes::FAILURE);
471 0 : if(!stateLogged())
472 : {
473 0 : log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
474 : }
475 0 : return -1;
476 : }
477 :
478 0 : if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
479 : {
480 0 : state(stateCodes::NODEVICE);
481 :
482 0 : if(!stateLogged())
483 : {
484 0 : std::stringstream logs;
485 0 : logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
486 0 : log<text_log>(logs.str());
487 0 : }
488 0 : return 0;
489 : }
490 : else
491 : {
492 0 : std::stringstream logs;
493 0 : logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
494 0 : log<text_log>(logs.str());
495 :
496 0 : state(stateCodes::NOTCONNECTED);
497 :
498 0 : for(size_t i=0; i < m_stages.size();++i)
499 : {
500 0 : if(m_stages[i].deviceAddress() < 1) continue;
501 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTCONNECTED"));
502 : }
503 :
504 0 : 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 0 : }
506 :
507 : }
508 :
509 0 : if( state() == stateCodes::NOTCONNECTED )
510 : {
511 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
512 :
513 0 : int rv = connect();
514 :
515 0 : if( rv == ZC_CONNECTED)
516 : {
517 0 : state(stateCodes::CONNECTED);
518 0 : for(size_t i=0; i < m_stages.size();++i)
519 : {
520 0 : if(m_stages[i].deviceAddress() < 1) continue;
521 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("CONNECTED"));
522 : }
523 :
524 0 : if(!stateLogged())
525 : {
526 0 : log<text_log>("Connected to stage(s) on " + m_deviceName);
527 : }
528 : }
529 0 : else if(rv == ZC_NOT_CONNECTED)
530 : {
531 0 : return 0;
532 : }
533 : else
534 : {
535 :
536 : }
537 0 : }
538 :
539 0 : if( state() == stateCodes::CONNECTED )
540 : {
541 0 : for(size_t i=0; i < m_stages.size();++i)
542 : {
543 0 : if(m_stages[i].deviceAddress() < 1)
544 : {
545 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
546 0 : continue; //Skip configured but not found stage
547 : }
548 0 : std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
549 :
550 0 : m_stages[i].getMaxPos(m_port);
551 0 : std::cerr << i << " " << m_stages[i].name() << " " << m_stages[i].maxPos() << "\n";
552 0 : 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 0 : if(m_stages[i].getWarnings(m_port) < 0)
556 : {
557 0 : if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
558 0 : log<software_error>({__FILE__, __LINE__});
559 0 : state(stateCodes::ERROR);
560 0 : return 0;
561 : }
562 :
563 0 : }
564 0 : state(stateCodes::READY);
565 :
566 0 : return 0;
567 : }
568 :
569 0 : if( state() == stateCodes::READY )
570 : {
571 :
572 : //Here we check complete stage state.
573 0 : for(size_t i=0; i < m_stages.size();++i)
574 : {
575 0 : if(m_stages[i].deviceAddress() < 1) continue; //Skip configured but not found stage
576 :
577 0 : std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
578 :
579 0 : m_stages[i].updatePos(m_port);
580 :
581 0 : updateIfChanged(m_indiP_curr_pos, m_stages[i].name(), m_stages[i].rawPos());
582 :
583 0 : if(m_stages[i].rawPos() == m_stages[i].tgtPos())
584 : {
585 0 : updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
586 : }
587 : else
588 : {
589 0 : updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), m_stages[i].tgtPos());
590 : }
591 :
592 :
593 0 : if(m_stages[i].deviceStatus() == 'B')
594 : {
595 0 : if(m_stages[i].homing())
596 : {
597 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("HOMING"));
598 : }
599 : else
600 : {
601 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("OPERATING"));
602 : }
603 : }
604 0 : else if(m_stages[i].deviceStatus() == 'I')
605 : {
606 0 : if(m_stages[i].homing())
607 : {
608 0 : std::cerr << __FILE__ << " " << __LINE__ << "\n";
609 0 : return 0;
610 : }
611 0 : if(m_stages[i].warnWR())
612 : {
613 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTHOMED"));
614 : }
615 : else
616 : {
617 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("READY"));
618 : }
619 : }
620 0 : else updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
621 :
622 0 : if(m_stages[i].warn())
623 : {
624 0 : updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::On);
625 : }
626 : else
627 : {
628 0 : updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::Off);
629 : }
630 :
631 0 : m_stages[i].updateTemp(m_port);
632 0 : updateIfChanged(m_indiP_temp, m_stages[i].name(), m_stages[i].temp());
633 :
634 0 : if(m_stages[i].getWarnings(m_port) < 0)
635 : {
636 0 : if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
637 0 : log<software_error>({__FILE__, __LINE__});
638 0 : state(stateCodes::ERROR);
639 0 : return 0;
640 : }
641 :
642 0 : }
643 : }
644 :
645 0 : if( state() == stateCodes::ERROR )
646 : {
647 0 : int rv = tty::usbDevice::getDeviceName();
648 0 : if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
649 : {
650 0 : if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
651 0 : state(stateCodes::FAILURE);
652 0 : for(size_t i=0; i < m_stages.size();++i)
653 : {
654 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
655 : }
656 0 : if(!stateLogged())
657 : {
658 0 : log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
659 : }
660 0 : return rv;
661 : }
662 :
663 0 : if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
664 : {
665 0 : if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
666 0 : state(stateCodes::NODEVICE);
667 0 : for(size_t i=0; i < m_stages.size();++i)
668 : {
669 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
670 : }
671 :
672 0 : if(!stateLogged())
673 : {
674 0 : std::stringstream logs;
675 0 : logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
676 0 : log<text_log>(logs.str());
677 0 : }
678 0 : return 0;
679 : }
680 :
681 0 : if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
682 0 : state(stateCodes::FAILURE);
683 0 : for(size_t i=0; i < m_stages.size();++i)
684 : {
685 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
686 : }
687 :
688 0 : log<software_critical>({__FILE__, __LINE__});
689 0 : 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 0 : if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
695 :
696 0 : if( state() == stateCodes::FAILURE )
697 : {
698 0 : return -1;
699 : }
700 :
701 0 : return 0;
702 : }
703 :
704 : inline
705 0 : int zaberLowLevel::onPowerOff()
706 : {
707 0 : int rv = za_disconnect(m_port);
708 0 : if(rv < 0)
709 : {
710 0 : log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
711 : }
712 :
713 0 : m_port = 0;
714 :
715 0 : std::lock_guard<std::mutex> lock(m_indiMutex);
716 :
717 0 : for(size_t i=0; i < m_stages.size();++i)
718 : {
719 0 : updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
720 0 : updateIfChanged(m_indiP_tgt_relpos, m_stages[i].name(), std::string(""));
721 0 : updateIfChanged(m_indiP_temp, m_stages[i].name(), std::string(""));
722 :
723 0 : m_stages[i].onPowerOff();
724 :
725 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("POWEROFF"));
726 :
727 0 : updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::Off);
728 : }
729 0 : return 0;
730 0 : }
731 :
732 : inline
733 0 : int zaberLowLevel::whilePowerOff()
734 : {
735 0 : return 0;
736 : }
737 :
738 : inline
739 0 : int zaberLowLevel::appShutdown()
740 : {
741 0 : for(size_t i=0; i < m_stages.size();++i)
742 : {
743 0 : if(m_stages[i].deviceAddress() < 1) continue;
744 0 : updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
745 : }
746 :
747 0 : return 0;
748 : }
749 :
750 3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_pos)(const pcf::IndiProperty &ipRecv)
751 : {
752 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_pos, ipRecv);
753 :
754 0 : for(size_t n=0; n < m_stages.size(); ++n)
755 : {
756 0 : if( ipRecv.find(m_stages[n].name()) )
757 : {
758 0 : long tgt = ipRecv[m_stages[n].name()].get<long>();
759 0 : if(tgt >= 0)
760 : {
761 0 : if(m_stages[n].deviceAddress() < 1)
762 : {
763 0 : 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 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
767 0 : updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
768 0 : return m_stages[n].moveAbs(m_port, tgt);
769 0 : }
770 : }
771 : }
772 :
773 0 : return 0;
774 : }
775 :
776 3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_relpos)(const pcf::IndiProperty &ipRecv)
777 : {
778 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_relpos, ipRecv);
779 :
780 0 : for(size_t n=0; n < m_stages.size(); ++n)
781 : {
782 0 : if( ipRecv.find(m_stages[n].name()) )
783 : {
784 0 : long tgt = ipRecv[m_stages[n].name()].get<long>();
785 0 : tgt += m_stages[n].rawPos();
786 0 : if(tgt >= 0)
787 : {
788 0 : if(m_stages[n].deviceAddress() < 1)
789 : {
790 0 : 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 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
793 :
794 0 : updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
795 0 : return m_stages[n].moveAbs(m_port, tgt);
796 0 : }
797 : }
798 : }
799 :
800 0 : return 0;
801 : }
802 :
803 3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_home)(const pcf::IndiProperty &ipRecv)
804 : {
805 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_home, ipRecv);
806 :
807 0 : for(size_t n=0; n < m_stages.size(); ++n)
808 : {
809 0 : if( ipRecv.find(m_stages[n].name()) )
810 : {
811 0 : int tgt = ipRecv[m_stages[n].name()].get<int>();
812 0 : if(tgt > 0)
813 : {
814 0 : if(m_stages[n].deviceAddress() < 1)
815 : {
816 0 : 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 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
819 0 : return m_stages[n].home(m_port);
820 0 : }
821 : }
822 : }
823 :
824 0 : return 0;
825 : }
826 :
827 3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_halt)(const pcf::IndiProperty &ipRecv)
828 : {
829 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_halt, ipRecv);
830 :
831 :
832 0 : for(size_t n=0; n < m_stages.size(); ++n)
833 : {
834 0 : if( ipRecv.find(m_stages[n].name()) )
835 : {
836 0 : int tgt = ipRecv[m_stages[n].name()].get<int>();
837 0 : if(tgt > 0)
838 : {
839 0 : if(m_stages[n].deviceAddress() < 1)
840 : {
841 0 : 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 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
845 0 : return m_stages[n].stop(m_port);
846 0 : }
847 : }
848 : }
849 :
850 0 : return 0;
851 : }
852 :
853 3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_ehalt)(const pcf::IndiProperty &ipRecv)
854 : {
855 3 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_ehalt, ipRecv);
856 :
857 0 : for(size_t n=0; n < m_stages.size(); ++n)
858 : {
859 0 : if( ipRecv.find(m_stages[n].name()) )
860 : {
861 0 : int tgt = ipRecv[m_stages[n].name()].get<int>();
862 0 : if(tgt > 0)
863 : {
864 0 : if(m_stages[n].deviceAddress() < 1)
865 : {
866 0 : 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 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
870 0 : return m_stages[n].estop(m_port);
871 0 : }
872 : }
873 : }
874 :
875 0 : return 0;
876 : }
877 :
878 : } //namespace app
879 : } //namespace MagAOX
880 :
881 : #endif //zaberLowLevel_hpp
|