Line data Source code
1 : /** \file filterWheelCtrl.hpp
2 : * \brief The MagAO-X Filter Wheel Controller
3 : *
4 : * \ingroup filterWheelCtrl_files
5 : */
6 :
7 :
8 : #ifndef filterWheelCtrl_hpp
9 : #define filterWheelCtrl_hpp
10 :
11 :
12 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
13 : #include "../../magaox_git_version.h"
14 :
15 : /** \defgroup filterWheelCtrl Filter Wheel Control
16 : * \brief Control of MagAO-X MCBL-based f/w.
17 : *
18 : * <a href="../handbook/operating/software/apps/filterWheelCtrl.html">Application Documentation</a>
19 : *
20 : * \ingroup apps
21 : *
22 : */
23 :
24 : /** \defgroup filterWheelCtrl_files Filter Wheel Control Files
25 : * \ingroup filterWheelCtrl
26 : */
27 :
28 : namespace MagAOX
29 : {
30 : namespace app
31 : {
32 :
33 : /** MagAO-X application to control a Faulhaber MCBL controlled filter wheel.
34 : *
35 : * \todo add temperature monitoring
36 : * \todo add INDI props to md doc
37 : * \todo should move in least time direction, rather than always in the same direction.
38 : * \todo add tests
39 : * \todo should be an iodevice
40 : *
41 : * \ingroup filterWheelCtrl
42 : */
43 : class filterWheelCtrl : public MagAOXApp<>, public tty::usbDevice, public dev::stdMotionStage<filterWheelCtrl>, public dev::telemeter<filterWheelCtrl>
44 : {
45 :
46 : friend class dev::stdMotionStage<filterWheelCtrl>;
47 :
48 : friend class dev::telemeter<filterWheelCtrl>;
49 :
50 : protected:
51 :
52 : /** \name Non-configurable parameters
53 : *@{
54 : */
55 :
56 : int m_motorType {2};
57 :
58 : ///@}
59 :
60 : /** \name Configurable Parameters
61 : * @{
62 : */
63 :
64 : int m_writeTimeOut {1000}; ///< The timeout for writing to the device [msec].
65 : int m_readTimeOut {1000}; ///< The timeout for reading from the device [msec].
66 :
67 : double m_acceleration {100};
68 : double m_deceleration {50};
69 : double m_motorSpeed {3000};
70 :
71 : long m_circleSteps {0}; ///< The number of position counts in 1 360-degree revolution.
72 : long m_homeOffset {0}; ///< The number of position counts to offset from the home position
73 :
74 :
75 :
76 : ///@}
77 :
78 : /** \name Status
79 : * @{
80 : */
81 :
82 : bool m_switch{false}; ///< The home switch status
83 :
84 : long m_rawPos {0}; ///< The position of the wheel in motor counts.
85 :
86 : int m_homingState{0}; ///< The homing state, tracks the stages of homing.
87 : ///@}
88 :
89 :
90 : public:
91 :
92 : /// Default c'tor.
93 : filterWheelCtrl();
94 :
95 : /// D'tor, declared and defined for noexcept.
96 0 : ~filterWheelCtrl() noexcept
97 0 : {}
98 :
99 : /// Setup the configuration system (called by MagAOXApp::setup())
100 : virtual void setupConfig();
101 :
102 : /// load the configuration system results (called by MagAOXApp::setup())
103 : virtual void loadConfig();
104 :
105 : /// Startup functions
106 : /** Setsup the INDI vars.
107 : *
108 : * \returns 0 on success
109 : * \returns -1 on error.
110 : */
111 : virtual int appStartup();
112 :
113 : /// Implementation of the FSM for the TTM Modulator
114 : /**
115 : * \returns 0 on success
116 : * \returns -1 on error.
117 : */
118 : virtual int appLogic();
119 :
120 : /// Do any needed shutdown tasks. Currently nothing in this app.
121 : /**
122 : * \returns 0 on success
123 : * \returns -1 on error.
124 : */
125 : virtual int appShutdown();
126 :
127 :
128 : /// This method is called when the change to poweroff is detected.
129 : /**
130 : * \returns 0 on success.
131 : * \returns -1 on any error which means the app should exit.
132 : */
133 : virtual int onPowerOff();
134 :
135 : /// This method is called while the power is off, once per FSM loop.
136 : /**
137 : * \returns 0 on success.
138 : * \returns -1 on any error which means the app should exit.
139 : */
140 : virtual int whilePowerOff();
141 :
142 : protected:
143 :
144 : //declare our properties
145 :
146 : ///The position of the wheel in counts
147 : pcf::IndiProperty m_indiP_counts;
148 :
149 : public:
150 0 : INDI_NEWCALLBACK_DECL(filterWheelCtrl, m_indiP_counts);
151 :
152 : protected:
153 : //Each of these should have m_indiMutex locked before being called.
154 :
155 : /// Set up the MCBL controller, called after each power-on/connection
156 : /**
157 : * \returns 0 on success.
158 : * \returns -1 on error.
159 : */
160 : int onPowerOnConnect();
161 :
162 : /// Get the home switch status, sets m_switch to true or false.
163 : /**
164 : * \returns 0 on success.
165 : * \returns -1 on error.
166 : */
167 : int getSwitch();
168 :
169 : /// Get the moving-state of the wheel, sets m_moving to true or false.
170 : /**
171 : * \returns 0 on success.
172 : * \returns -1 on error.
173 : */
174 : int getMoving();
175 :
176 : /// Get the current position of the wheel, sets m_rawPos to the current motor counts.
177 : /**
178 : * \returns 0 on success.
179 : * \returns -1 on error.
180 : */
181 : int getPos();
182 :
183 : /// Start a high-level homing sequence.
184 : /** For this device this includes the homing dither.
185 : *
186 : * \returns 0 on success.
187 : * \returns -1 on error.
188 : */
189 : int startHoming();
190 :
191 : int presetNumber();
192 :
193 : /// Start a low-level homing sequence.
194 : /** This initiates the device homing sequence.
195 : *
196 : * \returns 0 on success.
197 : * \returns -1 on error.
198 : */
199 : int home();
200 :
201 : /// Stop the wheel motion immediately.
202 : /**
203 : * \returns 0 on success.
204 : * \returns -1 on error.
205 : */
206 : int stop();
207 :
208 : /// Move to an absolute position in raw counts.
209 : /**
210 : * \returns 0 on success.
211 : * \returns -1 on error.
212 : */
213 : int moveToRaw( const long & counts /**< [in] The new position in absolute motor counts*/);
214 :
215 : /// Move to a new position relative to current, in raw counts.
216 : /**
217 : * \returns 0 on success.
218 : * \returns -1 on error.
219 : */
220 : int moveToRawRelative( const long & counts /**< [in] The new position in relative motor counts*/ );
221 :
222 : /// Move to an absolute position in filter units.
223 : /**
224 : * \returns 0 on success.
225 : * \returns -1 on error.
226 : */
227 : int moveTo( const double & filters /**< [in] The new position in absolute filter units*/ );
228 :
229 : /** \name Telemeter Interface
230 : *
231 : * @{
232 : */
233 : int checkRecordTimes();
234 :
235 : int recordTelem( const telem_stage * );
236 :
237 : int recordTelem( const telem_position * );
238 :
239 : int recordStage( bool force = false );
240 :
241 : int recordPosition( bool force = false );
242 :
243 : ///@}
244 : };
245 :
246 : inline
247 : filterWheelCtrl::filterWheelCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
248 : {
249 : m_presetNotation = "filter"; //sets the name of the configs, etc.
250 :
251 : m_powerMgtEnabled = true;
252 :
253 : return;
254 : }
255 :
256 : inline
257 0 : void filterWheelCtrl::setupConfig()
258 : {
259 0 : tty::usbDevice::setupConfig(config);
260 :
261 0 : config.add("timeouts.write", "", "timeouts.write", argType::Required, "timeouts", "write", false, "int", "The timeout for writing to the device [msec]. Default = 1000");
262 0 : config.add("timeouts.read", "", "timeouts.read", argType::Required, "timeouts", "read", false, "int", "The timeout for reading the device [msec]. Default = 1000");
263 :
264 0 : config.add("motor.acceleration", "", "motor.acceleration", argType::Required, "motor", "acceleration", false, "real", "The motor acceleration parameter. Default=100.");
265 0 : config.add("motor.deceleration", "", "motor.deceleration", argType::Required, "motor", "deceleration", false, "real", "The motor deceleration parameter. Default=50.");
266 0 : config.add("motor.speed", "", "motor.speed", argType::Required, "motor", "speed", false, "real", "The motor speed parameter. Default=3000.");
267 0 : config.add("motor.circleSteps", "", "motor.circleSteps", argType::Required, "motor", "circleSteps", false, "long", "The number of steps in 1 revolution.");
268 0 : config.add("stage.homeOffset", "", "stage.homeOffset", argType::Required, "stage", "homeOffset", false, "long", "The homing offset in motor counts.");
269 :
270 0 : dev::stdMotionStage<filterWheelCtrl>::setupConfig(config);
271 :
272 0 : dev::telemeter<filterWheelCtrl>::setupConfig(config);
273 0 : }
274 :
275 : inline
276 0 : void filterWheelCtrl::loadConfig()
277 : {
278 0 : this->m_baudRate = B9600; //default for MCBL controller. Will be overridden by any config setting.
279 :
280 0 : int rv = tty::usbDevice::loadConfig(config);
281 :
282 0 : if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
283 : {
284 0 : log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
285 : }
286 :
287 0 : config(m_writeTimeOut, "timeouts.write");
288 0 : config(m_readTimeOut, "timeouts.read");
289 :
290 0 : config(m_acceleration, "motor.acceleration");
291 0 : config(m_deceleration, "motor.deceleration");
292 0 : config(m_motorSpeed, "motor.speed");
293 0 : config(m_circleSteps, "motor.circleSteps");
294 0 : config(m_homeOffset, "stage.homeOffset");
295 :
296 :
297 0 : dev::stdMotionStage<filterWheelCtrl>::loadConfig(config);
298 :
299 0 : dev::telemeter<filterWheelCtrl>::loadConfig(config);
300 0 : }
301 :
302 : inline
303 0 : int filterWheelCtrl::appStartup()
304 : {
305 0 : if( state() == stateCodes::UNINITIALIZED )
306 : {
307 0 : log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
308 0 : return -1;
309 : }
310 :
311 : // set up the INDI properties
312 0 : createStandardIndiNumber<long>( m_indiP_counts, "counts", std::numeric_limits<long>::lowest(), std::numeric_limits<long>::max(), 0.0, "%ld");
313 0 : registerIndiPropertyNew( m_indiP_counts, INDI_NEWCALLBACK(m_indiP_counts)) ;
314 :
315 :
316 0 : dev::stdMotionStage<filterWheelCtrl>::appStartup();
317 :
318 0 : if(dev::telemeter<filterWheelCtrl>::appStartup() < 0)
319 : {
320 0 : return log<software_error,-1>({__FILE__,__LINE__});
321 : }
322 :
323 0 : return 0;
324 : }
325 :
326 : inline
327 0 : int filterWheelCtrl::appLogic()
328 : {
329 0 : if( state() == stateCodes::INITIALIZED )
330 : {
331 0 : log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
332 0 : return -1;
333 : }
334 :
335 0 : if( state() == stateCodes::POWERON )
336 : {
337 0 : if(m_deviceName == "") state(stateCodes::NODEVICE);
338 : else
339 : {
340 0 : state(stateCodes::NOTCONNECTED);
341 0 : std::stringstream logs;
342 0 : logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
343 0 : log<text_log>(logs.str());
344 0 : }
345 : }
346 :
347 0 : if( state() == stateCodes::NODEVICE )
348 : {
349 0 : int rv = tty::usbDevice::getDeviceName();
350 0 : if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
351 : {
352 0 : state(stateCodes::FAILURE);
353 0 : if(!stateLogged())
354 : {
355 0 : log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
356 : }
357 0 : return -1;
358 : }
359 :
360 0 : if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
361 : {
362 0 : state(stateCodes::NODEVICE);
363 :
364 0 : if(!stateLogged())
365 : {
366 0 : std::stringstream logs;
367 0 : logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
368 0 : log<text_log>(logs.str());
369 0 : }
370 0 : return 0;
371 : }
372 : else
373 : {
374 0 : state(stateCodes::NOTCONNECTED);
375 0 : if(!stateLogged())
376 : {
377 0 : std::stringstream logs;
378 0 : logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
379 0 : log<text_log>(logs.str());
380 0 : }
381 : }
382 : }
383 :
384 0 : if( state() == stateCodes::NOTCONNECTED )
385 : {
386 : int rv;
387 : {
388 0 : elevatedPrivileges elPriv(this);
389 0 : rv = connect();
390 0 : }
391 :
392 0 : if(rv < 0)
393 : {
394 0 : int nrv = tty::usbDevice::getDeviceName();
395 0 : if(nrv < 0 && nrv != TTY_E_DEVNOTFOUND && nrv != TTY_E_NODEVNAMES)
396 : {
397 0 : state(stateCodes::FAILURE);
398 0 : if(!stateLogged()) log<software_critical>({__FILE__, __LINE__, nrv, tty::ttyErrorString(nrv)});
399 0 : return -1;
400 : }
401 :
402 0 : if(nrv == TTY_E_DEVNOTFOUND || nrv == TTY_E_NODEVNAMES)
403 : {
404 0 : state(stateCodes::NODEVICE);
405 :
406 0 : if(!stateLogged())
407 : {
408 0 : std::stringstream logs;
409 0 : logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " no longer found in udev";
410 0 : log<text_log>(logs.str());
411 0 : }
412 0 : return 0;
413 : }
414 :
415 : //if connect failed, and there is a device, then we have some other problem.
416 0 : sleep(1); //wait to see if power state updates
417 0 : if(m_powerState == 0) return 0;
418 :
419 : //Ok we can't figure this out, die.
420 0 : state(stateCodes::FAILURE);
421 0 : if(!stateLogged()) log<software_error>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
422 0 : return -1;
423 :
424 : }
425 :
426 :
427 :
428 0 : if( getPos() == 0 ) state(stateCodes::CONNECTED);
429 : else
430 : {
431 0 : return 0;
432 : }
433 :
434 0 : if(state() == stateCodes::CONNECTED)
435 : {
436 0 : std::stringstream logs;
437 0 : logs << "Connected to filter wheel on " << m_deviceName;
438 0 : log<text_log>(logs.str());
439 0 : }
440 :
441 : }
442 :
443 0 : if( state() == stateCodes::CONNECTED )
444 : {
445 0 : int rv = onPowerOnConnect();
446 :
447 0 : if(rv < 0)
448 : {
449 0 : log<software_error>({__FILE__,__LINE__});
450 0 : state(stateCodes::ERROR);
451 :
452 0 : return 0;
453 : }
454 :
455 0 : std::string landfill;
456 0 : tty::ttyRead(landfill, "\r", m_fileDescrip, m_readTimeOut); //read to timeout to kill any missed chars.
457 :
458 0 : if(m_powerOnHome)
459 : {
460 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
461 :
462 0 : if(startHoming()<0)
463 : {
464 0 : state(stateCodes::ERROR);
465 0 : log<software_error>({__FILE__,__LINE__});
466 0 : return 0;
467 : }
468 0 : }
469 0 : else state(stateCodes::NOTHOMED);
470 :
471 :
472 0 : }
473 :
474 0 : if( state() == stateCodes::NOTHOMED || state() == stateCodes::READY || state() == stateCodes::OPERATING || state() == stateCodes::HOMING)
475 : {
476 : { //mutex scope
477 : //Make sure we have exclusive attention of the device
478 0 : std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
479 :
480 0 : int rv = getSwitch();
481 :
482 0 : if(rv != 0 )
483 : {
484 0 : state(stateCodes::NOTCONNECTED);
485 0 : return 0;
486 : }
487 :
488 0 : rv = getMoving();
489 :
490 0 : if(rv != 0 )
491 : {
492 0 : state(stateCodes::NOTCONNECTED);
493 0 : return 0;
494 : }
495 :
496 0 : rv = getPos();
497 :
498 0 : if(rv != 0 )
499 : {
500 0 : state(stateCodes::NOTCONNECTED);
501 0 : return 0;
502 : }
503 :
504 0 : recordPosition();
505 0 : }
506 :
507 0 : if(m_moving)
508 : {
509 : //Started moving but we don't know yet.
510 0 : if(state() == stateCodes::READY) state(stateCodes::OPERATING);
511 : }
512 : else
513 : {
514 0 : if(state() == stateCodes::OPERATING)
515 : {
516 0 : if(m_movingState == 1)
517 : {
518 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
519 0 : if(moveToRawRelative(-20000) < 0)
520 : {
521 0 : sleep(1);
522 0 : if(m_powerState == 0) return 0;
523 :
524 0 : state(stateCodes::ERROR);
525 0 : return log<software_error,0>({__FILE__,__LINE__});
526 : }
527 :
528 0 : m_movingState=2;
529 0 : }
530 0 : else if(m_movingState == 2)
531 : {
532 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
533 0 : if(moveTo(m_preset_target) < 0)
534 : {
535 0 : sleep(1);
536 0 : if(m_powerState == 0) return 0;
537 :
538 0 : state(stateCodes::ERROR);
539 0 : return log<software_error,0>({__FILE__,__LINE__});
540 : }
541 :
542 0 : m_movingState=3;
543 0 : }
544 : else
545 : {
546 0 : m_movingState = 0;
547 0 : state(stateCodes::READY); //stopped moving but was just changing pos
548 : }
549 :
550 : }
551 0 : else if (state() == stateCodes::HOMING) //stopped moving but was in the homing sequence
552 : {
553 0 : if(m_homingState == 1)
554 : {
555 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
556 0 : if(moveToRawRelative(-50000) < 0)
557 : {
558 0 : sleep(1);
559 0 : if(m_powerState == 0) return 0;
560 :
561 0 : state(stateCodes::ERROR);
562 0 : return log<software_error,0>({__FILE__,__LINE__});
563 : }
564 :
565 0 : m_homingState=2;
566 0 : }
567 0 : else if (m_homingState == 2)
568 : {
569 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
570 0 : if(home()<0)
571 : {
572 0 : sleep(1);
573 0 : if(m_powerState == 0) return 0;
574 :
575 0 : state(stateCodes::ERROR);
576 0 : return log<software_error,0>({__FILE__,__LINE__});
577 : }
578 0 : m_homingState = 3;
579 0 : }
580 0 : else if(m_homingState == 3)
581 : {
582 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
583 0 : if(moveToRaw(m_homeOffset)<0)
584 : {
585 0 : sleep(1);
586 0 : if(m_powerState == 0) return 0;
587 :
588 0 : state(stateCodes::ERROR);
589 0 : return log<software_error,0>({__FILE__,__LINE__});
590 : }
591 0 : m_homingState=4;
592 0 : }
593 0 : else if(m_homingState == 4)
594 : {
595 0 : if(m_homePreset >= 0)
596 : {
597 0 : m_preset_target = m_presetPositions[m_homePreset];
598 0 : updateIfChanged(m_indiP_preset, "target", m_preset_target, INDI_BUSY);
599 :
600 0 : moveTo(m_preset_target);
601 : }
602 0 : m_homingState = 5;
603 : }
604 : else
605 : {
606 0 : m_homingState = 0;
607 0 : state(stateCodes::READY);
608 :
609 0 : m_preset_target = ((double) m_rawPos-m_homeOffset)/m_circleSteps*m_presetNames.size() + 1.0;
610 : }
611 : }
612 : }
613 :
614 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
615 :
616 0 : if(m_moving)
617 : {
618 0 : updateIfChanged(m_indiP_counts, "current", m_rawPos, INDI_BUSY);
619 : }
620 : else
621 : {
622 0 : updateIfChanged(m_indiP_counts, "current", m_rawPos, INDI_IDLE);
623 : }
624 :
625 0 : m_preset = ((double) m_rawPos-m_homeOffset)/m_circleSteps*m_presetNames.size() + 1.0;
626 :
627 0 : stdMotionStage<filterWheelCtrl>::updateINDI();
628 0 : recordStage();
629 :
630 0 : if(telemeter<filterWheelCtrl>::appLogic() < 0)
631 : {
632 0 : log<software_error>({__FILE__, __LINE__});
633 0 : return 0;
634 : }
635 :
636 0 : return 0;
637 0 : }
638 :
639 0 : if(state() == stateCodes::ERROR)
640 : {
641 0 : sleep(1);
642 0 : if(m_powerState == 0) return 0;
643 :
644 0 : return log<software_error,-1>({__FILE__,__LINE__, "In state ERROR but no recovery implemented. Terminating."});
645 : }
646 :
647 0 : return log<software_error,-1>({__FILE__,__LINE__, "appLogic fell through. Terminating."});
648 :
649 : }
650 :
651 :
652 :
653 : inline
654 0 : int filterWheelCtrl::appShutdown()
655 : {
656 : //don't bother
657 0 : return 0;
658 : }
659 :
660 : inline
661 0 : int filterWheelCtrl::onPowerOff()
662 : {
663 0 : if( stdMotionStage<filterWheelCtrl>::onPowerOff() < 0)
664 : {
665 0 : log<software_error>({__FILE__,__LINE__});
666 : }
667 :
668 0 : recordStage(true);
669 0 : recordPosition(true);
670 :
671 0 : return 0;
672 : }
673 :
674 : inline
675 0 : int filterWheelCtrl::whilePowerOff()
676 : {
677 0 : if( stdMotionStage<filterWheelCtrl>::whilePowerOff() < 0)
678 : {
679 0 : log<software_error>({__FILE__,__LINE__});
680 : }
681 :
682 : //record telem if it's been longer than 10 sec:
683 0 : if(telemeter<filterWheelCtrl>::appLogic() < 0)
684 : {
685 0 : log<software_error>({__FILE__, __LINE__});
686 : }
687 :
688 0 : return 0;
689 : }
690 :
691 0 : INDI_NEWCALLBACK_DEFN(filterWheelCtrl, m_indiP_counts)(const pcf::IndiProperty &ipRecv)
692 : {
693 0 : INDI_VALIDATE_CALLBACK_PROPS(m_indiP_counts, ipRecv);
694 :
695 0 : double counts = -1;
696 0 : double target_abs = -1;
697 0 : if(ipRecv.find("current"))
698 : {
699 0 : counts = ipRecv["current"].get<double>();
700 : }
701 0 : if(ipRecv.find("target"))
702 : {
703 0 : target_abs = ipRecv["target"].get<double>();
704 : }
705 0 : if(target_abs == -1) target_abs = counts;
706 :
707 :
708 0 : m_preset_target = ((double) target_abs - m_homeOffset)/m_circleSteps*m_presetNames.size() + 1.0;
709 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
710 0 : return moveToRaw(target_abs);
711 :
712 0 : }
713 :
714 :
715 :
716 :
717 0 : int filterWheelCtrl::onPowerOnConnect()
718 : {
719 0 : std::string com;
720 :
721 : int rv;
722 :
723 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
724 :
725 0 : rv = tty::ttyWrite( "ANSW0\r", m_fileDescrip, m_writeTimeOut); //turn off replies and asynchronous comms.
726 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
727 :
728 : //Send motor type
729 0 : com = "MOTTYP" + std::to_string(m_motorType) + "\r";
730 0 : rv = tty::ttyWrite( com, m_fileDescrip, m_writeTimeOut);
731 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
732 :
733 : //Set up acceleration and speed.
734 0 : com = "AC" + std::to_string(m_acceleration) + "\r";
735 0 : rv = tty::ttyWrite( com, m_fileDescrip, m_writeTimeOut);
736 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
737 :
738 : //Set up acceleration and speed.
739 0 : com = "DEC" + std::to_string(m_deceleration) + "\r";
740 0 : rv = tty::ttyWrite( com, m_fileDescrip, m_writeTimeOut);
741 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
742 :
743 0 : com = "SP" + std::to_string(m_motorSpeed) + "\r";
744 0 : rv = tty::ttyWrite( com, m_fileDescrip,m_writeTimeOut);
745 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
746 :
747 0 : return 0;
748 0 : }
749 :
750 0 : int filterWheelCtrl::getSwitch()
751 : {
752 : int rv;
753 0 : std::string resp;
754 :
755 0 : rv = tty::ttyWriteRead( resp, "GAST\r", "\r\n", false, m_fileDescrip, m_writeTimeOut, m_readTimeOut);
756 :
757 0 : if(rv == 0)
758 : {
759 :
760 0 : if(resp == "1011") m_switch=true;
761 0 : else m_switch=false;
762 :
763 0 : return 0;
764 : }
765 :
766 :
767 0 : return rv;
768 :
769 0 : }
770 :
771 0 : int filterWheelCtrl::getMoving()
772 : {
773 : int rv;
774 0 : std::string resp;
775 :
776 0 : rv = tty::ttyWriteRead( resp, "GN\r", "\r\n", false, m_fileDescrip, m_writeTimeOut, m_readTimeOut);
777 :
778 0 : if(rv == 0)
779 : {
780 : int speed;
781 0 : try{ speed = std::stol(resp.c_str());}
782 0 : catch(...){speed=0;}
783 :
784 0 : if(fabs(speed) > 0.1*m_motorSpeed)
785 : {
786 0 : if(m_homingState) m_moving = 2;
787 0 : else m_moving = 1;
788 : }
789 0 : else m_moving = 0;
790 :
791 0 : return 0;
792 : }
793 :
794 :
795 0 : return rv;
796 :
797 0 : }
798 :
799 0 : int filterWheelCtrl::getPos()
800 : {
801 : int rv;
802 0 : std::string resp;
803 :
804 0 : rv = tty::ttyWriteRead( resp, "POS\r", "\r\n", false, m_fileDescrip, m_writeTimeOut, m_readTimeOut);
805 :
806 0 : if(rv == 0)
807 : {
808 0 : try{ m_rawPos = std::stol(resp.c_str());}
809 0 : catch(...){m_rawPos=0;}
810 : }
811 :
812 :
813 0 : return rv;
814 :
815 0 : }
816 :
817 0 : int filterWheelCtrl::startHoming()
818 : {
819 0 : m_homingState = 1;
820 0 : m_moving = 2;
821 0 : recordStage(true);
822 0 : recordPosition(true);
823 :
824 0 : updateSwitchIfChanged(m_indiP_home, "request", pcf::IndiElement::Off, INDI_IDLE);
825 0 : return home();
826 : }
827 :
828 0 : int filterWheelCtrl::presetNumber()
829 : {
830 : //First we calculate current preset name
831 0 : int n = floor(m_preset + 0.5) - 1;
832 0 : if(n < 0)
833 : {
834 0 : while(n < 0) n += m_presetNames.size();
835 : }
836 0 : if( n > (long) m_presetNames.size()-1 )
837 : {
838 0 : while( n > (long) m_presetNames.size()-1 ) n -= m_presetNames.size();
839 : }
840 :
841 0 : if( n < 0)
842 : {
843 0 : log<software_error>({__FILE__,__LINE__, "error calculating " + m_presetNotation + " index, n < 0"});
844 0 : return -1;
845 : }
846 :
847 0 : return n;
848 : }
849 :
850 0 : int filterWheelCtrl::home()
851 : {
852 :
853 0 : state(stateCodes::HOMING);
854 :
855 : int rv;
856 :
857 0 : rv = tty::ttyWrite( "EN\r", m_fileDescrip, m_writeTimeOut);
858 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
859 :
860 0 : rv = tty::ttyWrite( "HA4\r", m_fileDescrip, m_writeTimeOut);
861 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
862 :
863 0 : rv = tty::ttyWrite( "HL4\r", m_fileDescrip, m_writeTimeOut);
864 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
865 :
866 0 : rv = tty::ttyWrite( "CAHOSEQ\r", m_fileDescrip, m_writeTimeOut);
867 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
868 :
869 0 : rv = tty::ttyWrite( "HP0\r", m_fileDescrip, m_writeTimeOut);
870 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
871 :
872 0 : std::string com = "HOSP" + std::to_string(m_motorSpeed) + "\r";
873 0 : rv = tty::ttyWrite( com, m_fileDescrip, m_writeTimeOut);
874 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
875 :
876 0 : rv = tty::ttyWrite( "GOHOSEQ\r", m_fileDescrip, m_writeTimeOut);
877 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
878 :
879 :
880 :
881 0 : return 0;
882 0 : }
883 :
884 0 : int filterWheelCtrl::stop()
885 : {
886 0 : m_homingState = 0;
887 : //First try without locking
888 0 : tty::ttyWrite( "DI\r", m_fileDescrip, m_writeTimeOut);
889 :
890 : //Now make sure it goes through
891 0 : std::lock_guard<std::mutex> guard(m_indiMutex);
892 0 : int rv = tty::ttyWrite( "DI\r", m_fileDescrip, m_writeTimeOut);
893 :
894 0 : updateSwitchIfChanged(m_indiP_stop, "request", pcf::IndiElement::Off, INDI_IDLE);
895 :
896 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
897 :
898 0 : return 0;
899 0 : }
900 :
901 0 : int filterWheelCtrl::moveToRaw( const long & counts )
902 : {
903 :
904 0 : std::string com;
905 : int rv;
906 :
907 0 : m_moving = 1;
908 0 : recordStage(true);
909 0 : recordPosition(true);
910 :
911 0 : rv = tty::ttyWrite( "EN\r", m_fileDescrip, m_writeTimeOut);
912 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
913 :
914 0 : com = "LA" + std::to_string(counts) + "\r";
915 0 : rv = tty::ttyWrite( com, m_fileDescrip, m_writeTimeOut);
916 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
917 :
918 0 : rv = tty::ttyWrite( "M\r", m_fileDescrip, m_writeTimeOut);
919 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
920 :
921 0 : updateIfChanged(m_indiP_counts, "target", counts, pcf::IndiProperty::Busy);
922 :
923 0 : return 0;
924 0 : }
925 :
926 0 : int filterWheelCtrl::moveToRawRelative( const long & counts_relative )
927 : {
928 :
929 0 : std::string com;
930 : int rv;
931 :
932 0 : m_moving = 1;
933 0 : recordStage(true);
934 0 : recordPosition(true);
935 :
936 0 : rv = tty::ttyWrite( "EN\r", m_fileDescrip, m_writeTimeOut);
937 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
938 :
939 0 : com = "LR" + std::to_string(counts_relative) +"\r";
940 0 : rv = tty::ttyWrite( com, m_fileDescrip, m_writeTimeOut);
941 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
942 :
943 0 : rv = tty::ttyWrite( "M\r", m_fileDescrip, m_writeTimeOut);
944 0 : if(rv < 0) return log<software_error,-1>({__FILE__,__LINE__,rv, tty::ttyErrorString(rv)});
945 :
946 :
947 :
948 0 : return 0;
949 0 : }
950 :
951 0 : int filterWheelCtrl::moveTo( const double & filters )
952 : {
953 : long counts;
954 :
955 0 : if(m_circleSteps ==0 || m_presetNames.size() == 0) counts = filters;
956 0 : else counts = m_homeOffset + m_circleSteps/m_presetNames.size() * (filters-1);
957 :
958 0 : return moveToRaw(counts);
959 :
960 : }
961 :
962 0 : int filterWheelCtrl::checkRecordTimes()
963 : {
964 0 : return dev::telemeter<filterWheelCtrl>::checkRecordTimes(telem_stage(), telem_position());
965 : }
966 :
967 0 : int filterWheelCtrl::recordTelem( const telem_stage * )
968 : {
969 0 : return recordStage(true);
970 : }
971 :
972 0 : int filterWheelCtrl::recordTelem( const telem_position * )
973 : {
974 0 : return recordPosition(true);
975 : }
976 :
977 0 : int filterWheelCtrl::recordStage(bool force)
978 : {
979 0 : return dev::stdMotionStage<filterWheelCtrl>::recordStage(force);
980 : }
981 :
982 0 : int filterWheelCtrl::recordPosition(bool force)
983 : {
984 : static long last_pos = 999999999999;
985 :
986 0 : if( m_rawPos != last_pos || force )
987 : {
988 0 : float fpos = m_rawPos;
989 :
990 0 : telem<telem_position>(fpos);
991 :
992 0 : last_pos = m_rawPos;
993 : }
994 :
995 0 : return 0;
996 : }
997 :
998 : } //namespace app
999 : } //namespace MagAOX
1000 :
1001 : #endif //filterWheelCtrl_hpp
|