9 #ifndef app_outletController_hpp
10 #define app_outletController_hpp
12 #include <mx/app/application.hpp>
13 #include <mx/sys/timeUtils.hpp>
16 #include "../../INDI/libcommon/IndiProperty.hpp"
17 #include "../../libMagAOX/libMagAOX.hpp"
18 #include "../indiUtils.hpp"
21 #define OUTLET_STATE_UNKNOWN (-1)
22 #define OUTLET_STATE_OFF (0)
23 #define OUTLET_STATE_INTERMEDIATE (1)
24 #define OUTLET_STATE_ON (2)
27 #define OUTLET_E_NOOUTLETS (-10)
28 #define OUTLET_E_NOCHANNELS (-15)
29 #define OUTLET_E_NOVALIDCH (-20)
67 template<
class derivedT>
263 const pcf::IndiProperty &
ipRecv
296 return *
static_cast<derivedT *
>(
this);
300 template<
class derivedT>
303 static_cast<void>(config);
308 template<
class derivedT>
314 std::vector<std::string> sections;
316 config.unusedSections(sections);
321 std::vector<std::string> chSections;
323 for(
size_t i=0;i<sections.size(); ++i)
325 if( config.isSetUnused( mx::app::iniFile::makeKey(sections[i],
"outlet" ))
326 || config.isSetUnused( mx::app::iniFile::makeKey(sections[i],
"outlets" )) )
328 chSections.push_back(sections[i]);
335 for(
size_t n = 0; n < chSections.size(); ++n)
337 m_channels.emplace( chSections[n] ,
channelSpec());
340 std::vector<size_t> outlets;
341 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n],
"outlet" )))
343 config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n],
"outlet" ) );
347 config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n],
"outlets" ) );
351 for(
size_t k=0;k<outlets.size(); ++k)
354 if( (
int) outlets[k] - m_firstOne < 0 || (
int) outlets[k] - m_firstOne > (
int) m_outletStates.size())
356 #ifndef OUTLET_CTRL_TEST_NOLOG
357 return derivedT::template
log<text_log,-1>(
"Outlet " + std::to_string(outlets[k]) +
" in Channel " + chSections[n] +
" is invalid",
logPrio::LOG_ERROR);
363 outlets[k] -= m_firstOne;
366 m_channels[chSections[n]].m_outlets = outlets;
370 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n],
"onOrder" )))
372 std::vector<size_t> onOrder;
373 config.configUnused( onOrder, mx::app::iniFile::makeKey(chSections[n],
"onOrder" ) );
376 if(onOrder.size() != m_channels[chSections[n]].m_outlets.size())
378 #ifndef OUTLET_CTRL_TEST_NOLOG
379 return derivedT::template
log<text_log,-1>(
"onOrder be same size as outlets. In Channel " + chSections[n],
logPrio::LOG_ERROR);
385 m_channels[chSections[n]].m_onOrder = onOrder;
388 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n],
"offOrder" )))
390 std::vector<size_t> offOrder;
391 config.configUnused( offOrder, mx::app::iniFile::makeKey(chSections[n],
"offOrder" ) );
392 m_channels[chSections[n]].m_offOrder = offOrder;
396 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n],
"onDelays" )))
398 std::vector<unsigned> onDelays;
399 config.configUnused( onDelays, mx::app::iniFile::makeKey(chSections[n],
"onDelays" ) );
400 m_channels[chSections[n]].m_onDelays = onDelays;
404 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n],
"offDelays" )))
406 std::vector<unsigned> offDelays;
407 config.configUnused( offDelays, mx::app::iniFile::makeKey(chSections[n],
"offDelays" ) );
408 m_channels[chSections[n]].m_offDelays = offDelays;
416 template<
class derivedT>
419 m_outletStates.resize(numOuts, -1);
423 template<
class derivedT>
426 return m_outletStates[outletNum];
429 template<
class derivedT>
432 for(
size_t n=0; n<m_outletStates.size(); ++n)
434 int rv = updateOutletState(n);
435 if(rv < 0)
return rv;
441 template<
class derivedT>
444 return m_channels.size();
447 template<
class derivedT>
450 return m_channels[channel].m_outlets;
453 template<
class derivedT>
456 return m_channels[channel].m_onOrder;
459 template<
class derivedT>
462 return m_channels[channel].m_offOrder;
465 template<
class derivedT>
468 return m_channels[channel].m_onDelays;
471 template<
class derivedT>
474 return m_channels[channel].m_offDelays;
477 template<
class derivedT>
480 int st = outletState(m_channels[channel].m_outlets[0]);
482 for(
size_t n = 1; n < m_channels[channel].m_outlets.size(); ++n )
484 if( st != outletState(m_channels[channel].m_outlets[n]) ) st = 1;
490 template<
class derivedT>
494 #ifndef OUTLET_CTRL_TEST_NOLOG
495 derivedT::template log<software_debug>({__FILE__, __LINE__,
"turning on channel " + channel});
498 #ifndef OUTLET_CTRL_TEST_NOINDI
505 if( m_channels[channel].m_onOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_onOrder[0];
508 if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
511 #ifndef OUTLET_CTRL_TEST_NOLOG
512 derivedT::template log<software_error>({__FILE__, __LINE__,
"error turning on outlet " + std::to_string(n)});
514 std::cerr <<
"Failed to turn on outlet " << n <<
"\n";
520 #ifndef OUTLET_CTRL_TEST_NOLOG
521 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 2});
525 for(
size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
529 if( m_channels[channel].m_onOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_onOrder[i];
532 if( m_channels[channel].m_onDelays.size() == m_channels[channel].m_outlets.size() )
534 mx::sys::milliSleep(m_channels[channel].m_onDelays[i]);
539 if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
542 #ifndef OUTLET_CTRL_TEST_NOLOG
543 derivedT::template log<software_error>({__FILE__, __LINE__,
"error turning on outlet " + std::to_string(n)});
545 std::cerr <<
"Failed to turn on outlet " << n <<
"\n";
551 #ifndef OUTLET_CTRL_TEST_NOLOG
552 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne ), 2});
556 #ifndef OUTLET_CTRL_TEST_NOLOG
557 derivedT::template log<outlet_channel_state>({ channel, 2});
563 template<
class derivedT>
567 #ifndef OUTLET_CTRL_TEST_NOLOG
568 derivedT::template log<software_debug>({__FILE__, __LINE__,
"turning off channel " + channel});
571 #ifndef OUTLET_CTRL_TEST_NOINDI
578 if( m_channels[channel].m_offOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_offOrder[0];
581 if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
583 #ifndef OUTLET_CTRL_TEST_NOLOG
584 derivedT::template log<software_error>({__FILE__, __LINE__,
"error turning off outlet " + std::to_string(n)});
586 std::cerr <<
"Failed to turn off outlet " << n <<
"\n";
592 #ifndef OUTLET_CTRL_TEST_NOLOG
593 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 0});
597 for(
size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
601 if( m_channels[channel].m_offOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_offOrder[i];
604 if( m_channels[channel].m_offDelays.size() == m_channels[channel].m_outlets.size() )
606 mx::sys::milliSleep(m_channels[channel].m_offDelays[i]);
610 if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
612 #ifndef OUTLET_CTRL_TEST_NOLOG
613 derivedT::template log<software_error>({__FILE__, __LINE__,
"error turning off outlet " + std::to_string(n)});
615 std::cerr <<
"Failed to turn off outlet " << n <<
"\n";
621 #ifndef OUTLET_CTRL_TEST_NOLOG
622 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 0});
626 #ifndef OUTLET_CTRL_TEST_NOLOG
627 derivedT::template log<outlet_channel_state>({ channel, 0});
633 template<
class derivedT>
635 const pcf::IndiProperty &
ipRecv
638 return static_cast<derivedT *
>(app)->newCallBack_channels(
ipRecv);
641 template<
class derivedT>
647 #ifndef OUTLET_CTRL_TEST_NOLOG
648 derivedT::template log<text_log>(
"can't change outlet state when not READY",
logPrio::LOG_ERROR);
656 std::string name =
ipRecv.getName();
658 std::string state, target;
662 state =
ipRecv[
"state"].get<std::string>();
667 target =
ipRecv[
"target"].get<std::string>();
670 if( target ==
"" ) target = state;
671 target = mx::ioutils::toUpper(target);
675 return turnChannelOn(name);
680 return turnChannelOff(name);
686 template<
class derivedT>
690 m_indiP_chOutlets = pcf::IndiProperty(pcf::IndiProperty::Text);
691 m_indiP_chOutlets.setDevice(derived().configName());
692 m_indiP_chOutlets.setName(
"channelOutlets");
693 m_indiP_chOutlets.setPerm(pcf::IndiProperty::ReadOnly);
694 m_indiP_chOutlets.setState(pcf::IndiProperty::Idle);
696 if(derived().registerIndiPropertyReadOnly(m_indiP_chOutlets) < 0)
698 #ifndef OUTLET_CTRL_TEST_NOLOG
699 derivedT::template log<software_error>({__FILE__,__LINE__});
704 m_indiP_chOnDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
705 m_indiP_chOnDelays.setDevice(derived().configName());
706 m_indiP_chOnDelays.setName(
"channelOnDelays");
707 m_indiP_chOnDelays.setPerm(pcf::IndiProperty::ReadOnly);
708 m_indiP_chOnDelays.setState(pcf::IndiProperty::Idle);
710 if(derived().registerIndiPropertyReadOnly(m_indiP_chOnDelays) < 0)
712 #ifndef OUTLET_CTRL_TEST_NOLOG
713 derivedT::template log<software_error>({__FILE__,__LINE__});
718 m_indiP_chOffDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
719 m_indiP_chOffDelays.setDevice(derived().configName());
720 m_indiP_chOffDelays.setName(
"channelOffDelays");
721 m_indiP_chOffDelays.setPerm(pcf::IndiProperty::ReadOnly);
722 m_indiP_chOffDelays.setState(pcf::IndiProperty::Idle);
724 if(derived().registerIndiPropertyReadOnly(m_indiP_chOffDelays) < 0)
726 #ifndef OUTLET_CTRL_TEST_NOLOG
727 derivedT::template log<software_error>({__FILE__,__LINE__});
733 for(
auto it = m_channels.begin();
it != m_channels.end(); ++
it)
735 it->second.m_indiP_prop = pcf::IndiProperty (pcf::IndiProperty::Text);
736 it->second.m_indiP_prop.setDevice(derived().configName());
737 it->second.m_indiP_prop.setName(
it->first);
738 it->second.m_indiP_prop.setPerm(pcf::IndiProperty::ReadWrite);
739 it->second.m_indiP_prop.setState( pcf::IndiProperty::Idle );
742 it->second.m_indiP_prop.add (pcf::IndiElement(
"state"));
743 it->second.m_indiP_prop.add (pcf::IndiElement(
"target"));
745 if( derived().registerIndiPropertyNew(
it->second.m_indiP_prop, st_newCallBack_channels) < 0)
747 #ifndef OUTLET_CTRL_TEST_NOLOG
748 derivedT::template log<software_error>({__FILE__,__LINE__});
754 m_indiP_chOutlets.add(pcf::IndiElement(
it->first));
755 std::string os = std::to_string(
it->second.m_outlets[0]);
756 for(
size_t i=1;i<
it->second.m_outlets.size();++i) os +=
"," + std::to_string(
it->second.m_outlets[i]);
757 m_indiP_chOutlets[
it->first].set(os);
759 m_indiP_chOnDelays.add(pcf::IndiElement(
it->first));
761 for(
size_t i=0;i<
it->second.m_onDelays.size();++i) sum +=
it->second.m_onDelays[i];
762 m_indiP_chOnDelays[
it->first].set(sum);
764 m_indiP_chOffDelays.add(pcf::IndiElement(
it->first));
766 for(
size_t i=0;i<
it->second.m_offDelays.size();++i) sum +=
it->second.m_offDelays[i];
767 m_indiP_chOffDelays[
it->first].set(sum);
772 m_indiP_outletStates = pcf::IndiProperty (pcf::IndiProperty::Text);
773 m_indiP_outletStates.setDevice(derived().configName());
774 m_indiP_outletStates.setName(
"outlet");
775 m_indiP_outletStates.setPerm(pcf::IndiProperty::ReadWrite);
776 m_indiP_outletStates.setState( pcf::IndiProperty::Idle );
778 if( derived().registerIndiPropertyReadOnly(m_indiP_outletStates) < 0)
780 #ifndef OUTLET_CTRL_TEST_NOLOG
781 derivedT::template log<software_error>({__FILE__,__LINE__});
793 for(
size_t i=0; i< m_outletStates.size(); ++i)
795 m_indiP_outletStates.add (pcf::IndiElement(std::to_string(i+m_firstOne)));
803 template<
class derivedT>
806 if( !derived().m_indiDriver )
return 0;
809 for(
size_t i=0; i< m_outletStates.size(); ++i)
815 for(
auto it = m_channels.begin();
it != m_channels.end(); ++
it)
@ READY
The device is ready for operation, but is not operating.
std::string stateIntToString(int st)
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.
const pcf::IndiProperty & ipRecv
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
#define OUTLET_E_NOCHANNELS
#define OUTLET_E_NOOUTLETS
#define OUTLET_E_NOVALIDCH
A generic outlet controller.
int updateINDI()
Update the INDI properties for this device controller.
int setupINDI()
Setup the INDI properties for this device controller.
int outletState(int outletNum)
Get the currently stored outlet state, without updating from device.
std::vector< size_t > m_offOrder
[optional] The order in which outlets are turned off. This contains the indices of m_outlets,...
std::vector< unsigned > m_onDelays
[optional] The delays between outlets in a multi-oultet channel. The first entry is always ignored....
std::vector< unsigned > m_offDelays
[optional] The delays between outlets in a multi-oultet channel. The first entry is always ignored....
std::vector< unsigned > channelOffDelays(const std::string &channel)
Get the vector of outlet off delays for a channel.
virtual int updateOutletState(int outletNum)=0
Get the state of the outlet from the device.
std::vector< size_t > m_onOrder
[optional] The order in which outlets are turned on. This contains the indices of m_outlets,...
int turnChannelOn(const std::string &channel)
Turn a channel on.
pcf::IndiProperty m_indiP_prop
pcf::IndiProperty m_indiP_chOffDelays
An INDI property which publishes the total off delay for each channel. Useful for GUIs,...
int channelState(const std::string &channel)
Get the state of a channel.
pcf::IndiProperty m_indiP_chOutlets
An INDI property which publishes the outlets associated with each channel. Useful for GUIs,...
bool m_firstOne
Flag is true if the first outlet is numbered 1, otherwise assumes starting at 0.
std::unordered_map< std::string, channelSpec > m_channels
The map of channel specifications, which can be accessed by their names.
int loadConfig(mx::app::appConfigurator &config)
Load the [channel] sections from an application configurator.
size_t numChannels()
Get the number of channels.
pcf::IndiProperty m_indiP_outletStates
Indi Property to show individual outlet states.
std::vector< size_t > channelOnOrder(const std::string &channel)
Get the vector of outlet on orders for a channel.
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for an outletController.
int turnChannelOff(const std::string &channel)
Turn a channel off.
virtual int updateOutletStates()
Get the states of all outlets from the device.
std::vector< size_t > channelOutlets(const std::string &channel)
Get the vector of outlet indices for a channel.
int setNumberOfOutlets(int numOuts)
Sets the number of outlets. This should be called by the derived class constructor.
int newCallBack_channels(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
pcf::IndiProperty m_indiP_chOnDelays
An INDI property which publishes the total on delay for each channel. Useful for GUIs,...
virtual int turnOutletOff(int outletNum)=0
Turn an outlet off.
std::vector< unsigned > channelOnDelays(const std::string &channel)
Get the vector of outlet on delays for a channel.
std::vector< size_t > channelOffOrder(const std::string &channel)
Get the vector of outlet off orders for a channel.
static int st_newCallBack_channels(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for the channel properties.
std::vector< int > m_outletStates
The current states of each outlet. These MUST be updated by derived classes in the overridden updated...
std::vector< size_t > m_outlets
The outlets in this channel.
virtual int turnOutletOn(int outletNum)=0
Turn an outlet on.
Structure containing the specification of one channel.