9#ifndef stdMotionStage_hpp
10#define stdMotionStage_hpp
51template <
class derivedT>
208 const pcf::IndiProperty &
ipRecv
217 const pcf::IndiProperty &
ipRecv );
225 const pcf::IndiProperty &
ipRecv );
233 const pcf::IndiProperty &
ipRecv );
241 const pcf::IndiProperty &
ipRecv );
269 int presetNameIndex );
285 return *
static_cast<derivedT *
>( this );
290 return *
static_cast<const derivedT *
>( this );
294template <
class derivedT>
300template <
class derivedT>
303 static_cast<void>( config );
305 config.add(
"stage.powerOnHome",
313 "If true, home at startup/power-on. Default=false." );
315 config.add(
"stage.homePreset",
323 "If >=0, this preset number is moved to after homing." );
325 config.add( m_presetNotation +
"s.names",
327 m_presetNotation +
"s.names",
329 m_presetNotation +
"s",
333 "The names of the " + m_presetNotation +
"s." );
334 config.add( m_presetNotation +
"s.positions",
336 m_presetNotation +
"s.positions",
338 m_presetNotation +
"s",
342 "The positions of the " + m_presetNotation +
"s. If omitted or 0 then order is used." );
347template <
class derivedT>
350 config( m_powerOnHome,
"stage.powerOnHome" );
351 config( m_homePreset,
"stage.homePreset" );
353 config( m_presetNames, m_presetNotation +
"s.names" );
355 if( m_defaultPositions )
357 m_presetPositions.resize( m_presetNames.size(), 0 );
358 for(
size_t n = 0; n < m_presetPositions.size(); ++n )
359 m_presetPositions[n] = n + 1;
362 config( m_presetPositions, m_presetNotation +
"s.positions" );
364 if( m_defaultPositions )
366 for(
size_t n = 0; n < m_presetPositions.size(); ++n )
367 if( m_presetPositions[n] == 0 )
368 m_presetPositions[n] = n + 1;
374template <
class derivedT>
378 std::string format =
"%.4f";
379 if( !m_fractionalPresets )
385 derived().createStandardIndiNumber(
386 m_indiP_preset, m_presetNotation, 1.0, (
double)m_presetNames.size(), step, format );
387 m_indiP_preset[
"current"].set( 0 );
388 m_indiP_preset[
"target"].set( 0 );
389 if( derived().registerIndiPropertyNew( m_indiP_preset, st_newCallBack_stdMotionStage ) < 0 )
391#ifndef STDFILTERWHEEL_TEST_NOLOG
392 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
397 if( derived().createStandardIndiSelectionSw( m_indiP_presetName, m_presetNotation +
"Name", m_presetNames ) < 0 )
399 derivedT::template log<software_critical>( { __FILE__, __LINE__ } );
402 if( derived().registerIndiPropertyNew( m_indiP_presetName, st_newCallBack_stdMotionStage ) < 0 )
404#ifndef STDFILTERWHEEL_TEST_NOLOG
405 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
410 derived().createStandardIndiRequestSw( m_indiP_home,
"home" );
411 if( derived().registerIndiPropertyNew( m_indiP_home, st_newCallBack_stdMotionStage ) < 0 )
413#ifndef STDFILTERWHEEL_TEST_NOLOG
414 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
419 derived().createStandardIndiRequestSw( m_indiP_stop,
"stop" );
420 if( derived().registerIndiPropertyNew( m_indiP_stop, st_newCallBack_stdMotionStage ) < 0 )
422#ifndef STDFILTERWHEEL_TEST_NOLOG
423 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
431template <
class derivedT>
437template <
class derivedT>
445template <
class derivedT>
451template <
class derivedT>
457template <
class derivedT>
460 std::string name =
ipRecv.getName();
461 derivedT *_app =
static_cast<derivedT *
>( app );
464 return _app->newCallBack_m_indiP_stop(
ipRecv );
466 return _app->newCallBack_m_indiP_home(
ipRecv );
467 if( name == _app->m_presetNotation )
468 return _app->newCallBack_m_indiP_preset(
ipRecv );
469 if( name == _app->m_presetNotation +
"Name" )
470 return _app->newCallBack_m_indiP_presetName(
ipRecv );
475template <
class derivedT>
482 if( derived().indiTargetUpdate( m_indiP_preset,
target,
ipRecv,
true ) < 0 )
484 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
490 std::lock_guard<std::mutex> guard( derived().m_indiMutex );
491 clearPresetNameTracking();
493 return derived().moveTo(
target );
496template <
class derivedT>
501 static size_t s_presetNameCallbackSerial = 0;
503 std::vector<std::string> invalidSelections;
504 std::string onSelections;
505 for(
auto &&el :
ipRecv.getElements() )
507 if( el.second.getSwitchState() != pcf::IndiElement::On )
512 if( !onSelections.empty() )
514 onSelections +=
", ";
517 onSelections += el.first;
519 bool knownPreset =
false;
520 for(
size_t i = 0; i < m_presetNames.size(); ++i )
522 if( el.first == m_presetNames[i] )
531 invalidSelections.push_back( el.first );
535 if( invalidSelections.size() > 0 )
537 std::string invalidNames;
538 for(
size_t n = 0; n < invalidSelections.size(); ++n )
542 invalidNames +=
", ";
545 invalidNames += invalidSelections[n];
548 derivedT::template log<text_log>(
"Unknown " + m_presetNotation +
"Name selected: " + invalidNames,
549 logPrio::LOG_ERROR );
553 std::string newName =
"";
557 for( i = 0; i < m_presetNames.size(); ++i )
559 if( !
ipRecv.find( m_presetNames[i] ) )
562 if(
ipRecv[m_presetNames[i]].getSwitchState() == pcf::IndiElement::On )
566 derivedT::template log<text_log>(
"More than one " + m_presetNotation +
" selected",
567 logPrio::LOG_ERROR );
571 newName = m_presetNames[i];
576 if( newName ==
"" || newn < 0 )
581 std::lock_guard<std::mutex> guard( derived().m_indiMutex );
583 setPresetNameTracking( newn );
584 m_preset_target = m_presetPositions[newn];
585 derived().updateIfChanged( m_indiP_preset,
"target", m_preset_target,
INDI_BUSY );
587 ++s_presetNameCallbackSerial;
588 derivedT::template log<text_log>(
589 "stdMotionStage accepted " + m_presetNotation +
"Name[" + std::to_string( s_presetNameCallbackSerial ) +
590 "] device=" +
ipRecv.getDevice() +
" property=" +
ipRecv.getName() +
" selection=" + newName +
" on={" +
591 onSelections +
"} target=" + std::to_string( m_preset_target ),
592 logPrio::LOG_NOTICE );
595 return derived().moveTo( m_preset_target );
598template <
class derivedT>
603 if( !
ipRecv.find(
"request" ) )
606 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
610 std::lock_guard<std::mutex> guard( derived().m_indiMutex );
612 return derived().startHoming();
617template <
class derivedT>
622 if(
ipRecv.getName() != m_indiP_stop.getName() )
624 derivedT::template log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
628 if( !
ipRecv.find(
"request" ) )
631 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
637 return derived().stop();
642template <
class derivedT>
645 if( !derived().m_indiDriver )
648 int n = derived().presetNumber();
651 bool changed =
false;
653 static int8_t last_moving = -1;
654 int activePresetIndex = activePresetNameIndex( n );
656 if( last_moving != m_moving )
659 last_moving = m_moving;
662 for(
size_t i = 0; i < m_presetNames.size(); ++i )
664 if(
static_cast<int>( i ) == activePresetIndex )
666 if( m_indiP_presetName[m_presetNames[i]] != pcf::IndiElement::On )
669 m_indiP_presetName[m_presetNames[i]] = pcf::IndiElement::On;
674 if( m_indiP_presetName[m_presetNames[i]] != pcf::IndiElement::Off )
677 m_indiP_presetName[m_presetNames[i]] = pcf::IndiElement::Off;
685 m_indiP_presetName.setState(
INDI_BUSY );
689 m_indiP_presetName.setState(
INDI_IDLE );
692 m_indiP_presetName.setTimeStamp( pcf::TimeStamp() );
693 derived().m_indiDriver->sendSetProperty( m_indiP_presetName );
696 if( m_moving && m_movingState < 1 )
707 return recordStage();
710template <
class derivedT>
713 static int8_t last_moving = m_moving + 100;
714 static float last_preset;
715 static std::string last_presetName;
717 std::string presetName = telemetryPresetName();
719 if( m_moving != last_moving || m_preset != last_preset || presetName != last_presetName || force )
721 derived().template telem<telem_stage>( { m_moving, m_preset, presetName } );
722 last_moving = m_moving;
723 last_preset = m_preset;
724 last_presetName = presetName;
730template <
class derivedT>
733 m_presetNameIndex = -1;
736template <
class derivedT>
739 if( presetNameIndex < 0 || presetNameIndex >=
static_cast<int>( m_presetNames.size() ) )
741 clearPresetNameTracking();
745 m_presetNameIndex = presetNameIndex;
749template <
class derivedT>
752 if( m_presetNameIndex >= 0 && m_presetNameIndex <
static_cast<int>( m_presetPositions.size() ) )
754 if( m_moving != 0 && m_movingState == 1 )
756 return m_presetNameIndex;
759 if( presetIndex >= 0 && presetIndex <
static_cast<int>( m_presetPositions.size() ) &&
760 m_presetPositions[presetIndex] == m_presetPositions[m_presetNameIndex] )
762 return m_presetNameIndex;
766 if( presetIndex >= 0 && presetIndex <
static_cast<int>( m_presetNames.size() ) )
774template <
class derivedT>
777 int presetNameIndex = activePresetNameIndex( presetIndex );
779 if( presetNameIndex >= 0 && presetNameIndex <
static_cast<int>( m_presetNames.size() ) )
781 return m_presetNames[presetNameIndex];
787template <
class derivedT>
795 return activePresetName( derived().presetNumber() );
MagAO-X standard motion stage interface.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
std::string telemetryPresetName()
Resolve the preset name that should be recorded in telemetry.
int onPowerOff()
Actions on power off.
void clearPresetNameTracking()
Clear any tracked preset-name alias selection.
int setPresetNameTracking(int presetNameIndex)
Record the active preset-name alias index.
std::vector< std::string > m_presetNames
The names of each position on the stage.
bool m_powerOnHome
If true, then the motor is homed at startup (by this software or actual power on)
std::string m_presetNotation
Notation used to refer to a preset, should be singular, as in "preset" or "filter".
int activePresetNameIndex(int presetIndex) const
Resolve the preset-name alias index that should be reported.
float m_preset_target
The target numerical preset position [1.0 is index 0 in the preset name vector].
pcf::IndiProperty m_indiP_home
Command the stage to home. .
int newCallBack_m_indiP_stop(const pcf::IndiProperty &ipRecv)
Callback to process a NEW stop request switch toggle.
bool m_defaultPositions
Flag controlling whether the default preset positions (the vector index) are set in loadConfig.
int newCallBack_m_indiP_home(const pcf::IndiProperty &ipRecv)
Callback to process a NEW home request switch toggle.
int updateINDI()
Update the INDI properties for this device controller.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
const derivedT & derived() const
pcf::IndiProperty m_indiP_presetName
The name of the active preset selection.
int whilePowerOff()
Actions while powered off.
std::string activePresetName(int presetIndex) const
Resolve the preset name that should be reported.
~stdMotionStage() noexcept
Destructor.
int newCallBack_m_indiP_presetName(const pcf::IndiProperty &ipRecv)
Callback to process a NEW preset name request.
int m_homePreset
If >=0, this preset position is moved to after homing.
pcf::IndiProperty m_indiP_stop
Command the stage to halt.
pcf::IndiProperty m_indiP_preset
The position of the stage in presets.
float m_preset
The current numerical preset position [1.0 is index 0 in the preset name vector].
static int st_newCallBack_stdMotionStage(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for stdMotionStage properties.
bool m_fractionalPresets
Flag to set in constructor determining if fractional presets are allowed. Used for INDI/GUIs.
int appStartup()
Startup function.
std::vector< float > m_presetPositions
int recordStage(bool force=false)
Record the stage telemetry state.
int appLogic()
Application logic.
int appShutdown()
Application the shutdown.
int newCallBack_m_indiP_preset(const pcf::IndiProperty &ipRecv)
Callback to process a NEW preset position request.
int m_presetNameIndex
The selected preset-name alias index when the active command identifies a specific preset name.
#define INDI_VALIDATE_CALLBACK_PROPS_DERIVED(prop1, prop2)
Standard check for matching INDI properties in a callback in a CRTP base class.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
const pcf::IndiProperty & ipRecv