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