LCOV - code coverage report
Current view: top level - libMagAOX/app/dev - outletController.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 42.9 % 210 90
Test Date: 2026-01-03 21:03:39 Functions: 68.4 % 19 13

            Line data    Source code
       1              : /** \file outletController.hpp
       2              :  * \author Jared R. Males
       3              :  * \brief Declares and defines a power control device framework in the MagAOXApp context
       4              :  *
       5              :  * \ingroup
       6              :  *
       7              :  */
       8              : 
       9              : #ifndef app_outletController_hpp
      10              : #define app_outletController_hpp
      11              : 
      12              : #include <mx/app/application.hpp>
      13              : #include <mx/sys/timeUtils.hpp>
      14              : 
      15              : 
      16              : #include "../../INDI/libcommon/IndiProperty.hpp"
      17              : #include "../../libMagAOX/libMagAOX.hpp"
      18              : #include "../indiUtils.hpp"
      19              : 
      20              : 
      21              : #define OUTLET_STATE_UNKNOWN (-1)
      22              : #define OUTLET_STATE_OFF (0)
      23              : #define OUTLET_STATE_INTERMEDIATE (1)
      24              : #define OUTLET_STATE_ON (2)
      25              : 
      26              : 
      27              : #define OUTLET_E_NOOUTLETS (-10)
      28              : #define OUTLET_E_NOCHANNELS (-15)
      29              : #define OUTLET_E_NOVALIDCH (-20)
      30              : 
      31              : namespace MagAOX
      32              : {
      33              : namespace app
      34              : {
      35              : namespace dev
      36              : {
      37              : 
      38              : /// A generic outlet controller
      39              : /** Controls a set of outlets on a device, such as A/C power outlets or digital outputs.
      40              :   * The outlets are organized into channels, which could be made up of multiple outlets.
      41              :   *
      42              :   * derivedT must be a MagAOXApp, and additionally it must implement the functions
      43              :   * \code
      44              :     int turnOutletOn( int outletNum );
      45              : 
      46              :     int turnOutletOff( int outletNum );
      47              : 
      48              :     int updateOutletState( int outletNum );
      49              :   * \endcode
      50              :   * and optionally
      51              :   * \code
      52              :     int updateOutletStates();
      53              :     \endcode
      54              :   *
      55              :   * Other requirements:
      56              :   * - call `setNumberOfOutlets` in the derived class constructor
      57              :   *
      58              :   *
      59              :   * \tparam derivedT specifies a MagAOXApp parent base class which is accessed with a `static_cast` (downcast)
      60              :   *         to perform various methods.
      61              :   *
      62              :   * \ingroup appdev
      63              :   *
      64              :   * \todo finalize 0-counting vs 1-counting rules --> do we subtract one from config-ed outlet indices if m_firstOne is true?
      65              :   *
      66              :   */
      67              : template<class derivedT>
      68              : struct outletController
      69              : {
      70              :    bool m_firstOne {false}; ///< Flag is true if the first outlet is numbered 1, otherwise assumes starting at 0.
      71              : 
      72              :    std::vector<int> m_outletStates; ///< The current states of each outlet.  These MUST be updated by derived classes in the overridden \ref updatedOutletState.
      73              : 
      74              :    pcf::IndiProperty m_indiP_outletStates; ///< Indi Property to show individual outlet states.
      75              : 
      76              :    /// Structure containing the specification of one channel.
      77              :    /** A channel may include more than one outlet, may specify the order in which
      78              :      * outlets are turned on and/or off, and may specify a delay between turning outlets on
      79              :      * and/or off.
      80              :      */
      81              :    struct channelSpec
      82              :    {
      83              :       std::vector<size_t> m_outlets; ///< The outlets in this channel
      84              : 
      85              :       std::vector<size_t> m_onOrder; ///< [optional] The order in which outlets are turned on.  This contains the indices of m_outlets, not the outlet numbers of the device.
      86              :       std::vector<size_t> m_offOrder; ///< [optional] The order in which outlets are turned off.  This contains the indices of m_outlets, not the outlet numbers of the device.
      87              : 
      88              :       std::vector<unsigned> m_onDelays; ///< [optional] The delays between outlets in a multi-oultet channel.  The first entry is always ignored.  The second entry is the dealy between the first and second outlet, etc.
      89              :       std::vector<unsigned> m_offDelays; ///< [optional] The delays between outlets in a multi-oultet channel.  The first entry is always ignored.  The second entry is the dealy between the first and second outlet, etc.
      90              : 
      91              :       pcf::IndiProperty m_indiP_prop;
      92              :    };
      93              : 
      94              :    /// The map of channel specifications, which can be accessed by their names.
      95              :    std::unordered_map<std::string, channelSpec> m_channels;
      96              : 
      97              :    /// An INDI property which publishes the outlets associated with each channel.  Useful for GUIs, etc.
      98              :    pcf::IndiProperty m_indiP_chOutlets;
      99              : 
     100              :    /// An INDI property which publishes the total on delay for each channel.  Useful for GUIs, etc.
     101              :    pcf::IndiProperty m_indiP_chOnDelays;
     102              : 
     103              :    /// An INDI property which publishes the total off delay for each channel.  Useful for GUIs, etc.
     104              :    pcf::IndiProperty m_indiP_chOffDelays;
     105              : 
     106              : 
     107              :    ///Setup an application configurator for an outletController
     108              :    /** This is currently a no-op
     109              :      *
     110              :      * \returns 0 on success
     111              :      * \returns -1 on failure
     112              :      */
     113              :    int setupConfig( mx::app::appConfigurator & config /**< [in] an application configuration to setup */);
     114              : 
     115              :    /// Load the [channel] sections from an application configurator
     116              :    /** Any "unused" section from the config parser is analyzed to determine if it is a channel specification.
     117              :      * If it contains the `outlet` or `outlets` keyword, then it is considered a channel. `outlet` and `outlets`
     118              :      * are equivalent, and specify the one or more device outlets included in this channel (i.e. this may be a vector
     119              :      * value entry).
     120              :      *
     121              :      * This function then looks for `onOrder` and `offOrder` keywords, which specify the order outlets are turned
     122              :      * on or off by their indices in the vector specified by the `outlet`/`outlets` keyword (i.e not the outlet numbers).
     123              :      *
     124              :      * Next it looks for `onDelays` and `offDelays`, which specify the delays between outlet operations in milliseconds.
     125              :      * The first entry is always ignored, then the second entry specifies the delay between the first and second outlet
     126              :      * operation, etc.
     127              :      *
     128              :      * An example config file section is:
     129              :      \verbatim
     130              :      [sue]           #this channel will be named sue
     131              :      outlets=4,5     #this channel uses outlets 4 and 5
     132              :      onOrder=1,0     #outlet 5 will be turned on first
     133              :      offOrder=0,1    #Outlet 4 will be turned off first
     134              :      onDelays=0,150  #a 150 msec delay between outlet turn on
     135              :      offDelays=0,345 #a 345 msec delay between outlet turn off
     136              :      \endverbatim
     137              :      *
     138              :      * \returns 0 on success
     139              :      * \returns -1 on failure
     140              :      */
     141              :    int loadConfig( mx::app::appConfigurator & config /**< [in] an application configuration from which to load values */);
     142              : 
     143              :    /// Sets the number of outlets.  This should be called by the derived class constructor.
     144              :    /**
     145              :      * \returns 0 on success
     146              :      * \returns -1 on failure
     147              :      */
     148              :    int setNumberOfOutlets( int numOuts /**< [in] the number of outlets to allocate */);
     149              : 
     150              :    /// Get the currently stored outlet state, without updating from device.
     151              :    int outletState( int outletNum );
     152              : 
     153              :    /// Get the state of the outlet from the device.
     154              :    /** This will be implemented in derived classes to update the outlet state.
     155              :      * \todo this is declared pure virtual, but there is an implementation.
     156              :      * \returns 0 on success.
     157              :      * \returns -1 on error.
     158              :      */
     159              :    virtual int updateOutletState( int outletNum /**< [in] the outlet number to update */) = 0;
     160              : 
     161              :    /// Get the states of all outlets from the device.
     162              :    /** The default implementation for-loops through each outlet, calling \ref updateOutletState.
     163              :      * Can be re-implemented in derived classes to update the outlet states.
     164              :      *
     165              :      * \returns 0 on success.
     166              :      * \returns -1 on error.
     167              :      */
     168              :    virtual int updateOutletStates();
     169              : 
     170              :    /// Turn an outlet on.
     171              :    /** This will be implemented in derived classes to turn an outlet on.
     172              :      *
     173              :      * \returns 0 on success.
     174              :      * \returns -1 on error.
     175              :      */
     176              :    virtual int turnOutletOn( int outletNum /**< [in] the outlet number to turn on */) = 0;
     177              : 
     178              :    /// Turn an outlet off.
     179              :    /** This will be implemented in derived classes to turn an outlet off.
     180              :      *
     181              :      * \returns 0 on success.
     182              :      * \returns -1 on error.
     183              :      */
     184              :    virtual int turnOutletOff( int outletNum /**< [in] the outlet number to turn off */) = 0;
     185              : 
     186              :    /// Get the number of channels
     187              :    /**
     188              :      * \returns the number of entries in m_channels.
     189              :      */
     190              :    size_t numChannels();
     191              : 
     192              :    /// Get the vector of outlet indices for a channel.
     193              :    /** Mainly used for testing.
     194              :      *
     195              :      * \returns the m_outlets member of the channelSpec specified by its name.
     196              :      */
     197              :    std::vector<size_t> channelOutlets( const std::string & channel /**< [in] the name of the channel */);
     198              : 
     199              :    /// Get the vector of outlet on orders for a channel.
     200              :    /** Mainly used for testing.
     201              :      *
     202              :      * \returns the m_onOrder member of the channelSpec specified by its name.
     203              :      */
     204              :    std::vector<size_t> channelOnOrder( const std::string & channel /**< [in] the name of the channel */);
     205              : 
     206              :    /// Get the vector of outlet off orders for a channel.
     207              :    /** Mainly used for testing.
     208              :      *
     209              :      * \returns the m_offOrder member of the channelSpec specified by its name.
     210              :      */
     211              :    std::vector<size_t> channelOffOrder( const std::string & channel /**< [in] the name of the channel */);
     212              : 
     213              :    /// Get the vector of outlet on delays for a channel.
     214              :    /** Mainly used for testing.
     215              :      *
     216              :      * \returns the m_onDelays member of the channelSpec specified by its name.
     217              :      */
     218              :    std::vector<unsigned> channelOnDelays( const std::string & channel /**< [in] the name of the channel */);
     219              : 
     220              :    /// Get the vector of outlet off delays for a channel.
     221              :    /** Mainly used for testing.
     222              :      *
     223              :      * \returns the m_offDelays member of the channelSpec specified by its name.
     224              :      */
     225              :    std::vector<unsigned> channelOffDelays( const std::string & channel /**< [in] the name of the channel */);
     226              : 
     227              :    /// Get the state of a channel.
     228              :    /**
     229              :      * \returns OUTLET_STATE_UNKNOWN if the state is not known
     230              :      * \returns OUTLET_STATE_OFF if the channel is off (all outlets off)
     231              :      * \returns OUTLET_STATE_INTERMEDIATE if outlets are intermediate or not in the same state
     232              :      * \returns OUTLET_STATE_ON if channel is on (all outlets on)
     233              :      */
     234              :    int channelState( const std::string & channel /**< [in] the name of the channel */);
     235              : 
     236              :    /// Turn a channel on.
     237              :    /** This implements the outlet order and delay logic.
     238              :      *
     239              :      * \returns 0 on success.
     240              :      * \returns -1 on error.
     241              :      */
     242              :    int turnChannelOn( const std::string & channel /**< [in] the name of the channel to turn on*/);
     243              : 
     244              :    /// Turn a channel off.
     245              :    /** This implements the outlet order and delay logic.
     246              :      *
     247              :      * \returns 0 on success.
     248              :      * \returns -1 on error.
     249              :      */
     250              :    int turnChannelOff( const std::string & channel /**< [in] the name of the channel to turn on*/);
     251              : 
     252              : 
     253              :    /** \name INDI Setup
     254              :      *@{
     255              :      */
     256              : 
     257              :    /// The static callback function to be registered for the channel properties.
     258              :    /**
     259              :      * \returns 0 on success.
     260              :      * \returns -1 on error.
     261              :      */
     262              :    static int st_newCallBack_channels( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
     263              :                                        const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
     264              :                                      );
     265              : 
     266              :    /// The callback called by the static version, to actually process the new request.
     267              :    /**
     268              :      * \returns 0 on success.
     269              :      * \returns -1 on error.
     270              :      */
     271              :    int newCallBack_channels( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     272              : 
     273              :    /// Setup the INDI properties for this device controller
     274              :    /** This should be called in the `appStartup` function of the derived MagAOXApp.
     275              :      * \todo change this to be appStartup like other devs.
     276              :      * \returns 0 on success.
     277              :      * \returns -1 on error.
     278              :      */
     279              :    int setupINDI();
     280              : 
     281              :    /// Update the INDI properties for this device controller
     282              :    /** You should call this after updating the outlet states.
     283              :      * It is not called automatically.
     284              :      *
     285              :      * \returns 0 on success.
     286              :      * \returns -1 on error.
     287              :      */
     288              :    int updateINDI();
     289              : 
     290              :    ///@}
     291              : 
     292              : 
     293              : private:
     294            0 :    derivedT & derived()
     295              :    {
     296            0 :       return *static_cast<derivedT *>(this);
     297              :    }
     298              : };
     299              : 
     300              : template<class derivedT>
     301           29 : int outletController<derivedT>::setupConfig( mx::app::appConfigurator & config )
     302              : {
     303              :    static_cast<void>(config);
     304              : 
     305           29 :    return 0;
     306              : }
     307              : 
     308              : template<class derivedT>
     309           29 : int outletController<derivedT>::loadConfig( mx::app::appConfigurator & config )
     310              : {
     311           29 :    if( m_outletStates.size() == 0) return OUTLET_E_NOOUTLETS;
     312              : 
     313              :    //Get the "unused" sections.
     314           29 :    std::vector<std::string> sections;
     315              : 
     316           29 :    config.unusedSections(sections);
     317              : 
     318           29 :    if( sections.size() == 0 ) return OUTLET_E_NOCHANNELS;
     319              : 
     320              :    //Now see if any are channels, which means they have an outlet= or outlets= entry
     321           29 :    std::vector<std::string> chSections;
     322              : 
     323          103 :    for(size_t i=0;i<sections.size(); ++i)
     324              :    {
     325          222 :       if( config.isSetUnused( mx::app::iniFile::makeKey(sections[i], "outlet" ))
     326          172 :               || config.isSetUnused( mx::app::iniFile::makeKey(sections[i], "outlets" )) )
     327              :       {
     328           74 :          chSections.push_back(sections[i]);
     329              :       }
     330              :    }
     331              : 
     332           29 :    if( chSections.size() == 0 ) return OUTLET_E_NOVALIDCH;
     333              : 
     334              :    //Now configure the chanels.
     335          177 :    for(size_t n = 0; n < chSections.size(); ++n)
     336              :    {
     337           74 :       m_channels.emplace( chSections[n] , channelSpec());
     338              : 
     339              :       //---- Set outlets ----
     340           74 :       std::vector<size_t> outlets;
     341          148 :       if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "outlet" )))
     342              :       {
     343          124 :          config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n], "outlet" ) );
     344              :       }
     345              :       else
     346              :       {
     347           24 :          config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n], "outlets" ) );
     348              :       }
     349              : 
     350              :       //Subtract one if the device numbers from 1.
     351          190 :       for(size_t k=0;k<outlets.size(); ++k)
     352              :       {
     353              :          ///\todo test this error
     354          116 :          if( (int) outlets[k] - m_firstOne < 0 || (int) outlets[k] - m_firstOne > (int) m_outletStates.size())
     355              :          {
     356              :             #ifndef OUTLET_CTRL_TEST_NOLOG
     357            0 :             return derivedT::template log<text_log,-1>("Outlet " + std::to_string(outlets[k]) + " in Channel " + chSections[n] + " is invalid", logPrio::LOG_ERROR);
     358              :             #else
     359            0 :             return -1;
     360              :             #endif
     361              :          }
     362              : 
     363          116 :          outlets[k] -= m_firstOne;
     364              :       }
     365              : 
     366           74 :       m_channels[chSections[n]].m_outlets = outlets;
     367              :       ///\todo error checking on outlets
     368              : 
     369              :       //---- Set optional configs ----
     370          148 :       if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "onOrder" )))
     371              :       {
     372           34 :          std::vector<size_t> onOrder;
     373           34 :          config.configUnused( onOrder, mx::app::iniFile::makeKey(chSections[n], "onOrder" ) );
     374              : 
     375              :          ///\todo test this error
     376           34 :          if(onOrder.size() != m_channels[chSections[n]].m_outlets.size())
     377              :          {
     378              :             #ifndef OUTLET_CTRL_TEST_NOLOG
     379            0 :             return derivedT::template log<text_log,-1>("onOrder be same size as outlets.  In Channel " + chSections[n], logPrio::LOG_ERROR);
     380              :             #else
     381            0 :             return -1;
     382              :             #endif
     383              :          }
     384              : 
     385           34 :          m_channels[chSections[n]].m_onOrder = onOrder;
     386           34 :       }
     387              : 
     388          148 :       if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "offOrder" )))
     389              :       {
     390           26 :          std::vector<size_t> offOrder;
     391           26 :          config.configUnused( offOrder, mx::app::iniFile::makeKey(chSections[n], "offOrder" ) );
     392           26 :          m_channels[chSections[n]].m_offOrder = offOrder;
     393              :          ///\todo error checking on offOrder, should complain if not same length
     394           26 :       }
     395              : 
     396          148 :       if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "onDelays" )))
     397              :       {
     398           16 :          std::vector<unsigned> onDelays;
     399           16 :          config.configUnused( onDelays, mx::app::iniFile::makeKey(chSections[n], "onDelays" ) );
     400           16 :          m_channels[chSections[n]].m_onDelays = onDelays;
     401              :          ///\todo error checking on onDelays, should complain if not same length
     402           16 :       }
     403              : 
     404          148 :       if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "offDelays" )))
     405              :       {
     406           16 :          std::vector<unsigned> offDelays;
     407           16 :          config.configUnused( offDelays, mx::app::iniFile::makeKey(chSections[n], "offDelays" ) );
     408           16 :          m_channels[chSections[n]].m_offDelays = offDelays;
     409              :          ///\todo error checking on offDelays, should complain if not same length
     410           16 :       }
     411              :    }
     412              : 
     413           29 :    return 0;
     414           29 : }
     415              : 
     416              : template<class derivedT>
     417           29 : int outletController<derivedT>::setNumberOfOutlets( int numOuts )
     418              : {
     419           29 :    m_outletStates.resize(numOuts, -1);
     420           29 :    return 0;
     421              : }
     422              : 
     423              : template<class derivedT>
     424          428 : int outletController<derivedT>::outletState( int outletNum )
     425              : {
     426          428 :    return m_outletStates[outletNum];
     427              : }
     428              : 
     429              : template<class derivedT>
     430            0 : int outletController<derivedT>::updateOutletStates()
     431              : {
     432            0 :    for(size_t n=0; n<m_outletStates.size(); ++n)
     433              :    {
     434            0 :       int rv = updateOutletState(n);
     435            0 :       if(rv < 0) return rv;
     436              :    }
     437              : 
     438            0 :    return 0;
     439              : }
     440              : 
     441              : template<class derivedT>
     442            8 : size_t outletController<derivedT>::numChannels()
     443              : {
     444            8 :    return m_channels.size();
     445              : }
     446              : 
     447              : template<class derivedT>
     448           24 : std::vector<size_t> outletController<derivedT>::channelOutlets( const std::string & channel )
     449              : {
     450           24 :    return m_channels[channel].m_outlets;
     451              : }
     452              : 
     453              : template<class derivedT>
     454           24 : std::vector<size_t> outletController<derivedT>::channelOnOrder( const std::string & channel )
     455              : {
     456           24 :    return m_channels[channel].m_onOrder;
     457              : }
     458              : 
     459              : template<class derivedT>
     460           24 : std::vector<size_t> outletController<derivedT>::channelOffOrder( const std::string & channel )
     461              : {
     462           24 :    return m_channels[channel].m_offOrder;
     463              : }
     464              : 
     465              : template<class derivedT>
     466           24 : std::vector<unsigned> outletController<derivedT>::channelOnDelays( const std::string & channel )
     467              : {
     468           24 :    return m_channels[channel].m_onDelays;
     469              : }
     470              : 
     471              : template<class derivedT>
     472           24 : std::vector<unsigned> outletController<derivedT>::channelOffDelays( const std::string & channel )
     473              : {
     474           24 :    return m_channels[channel].m_offDelays;
     475              : }
     476              : 
     477              : template<class derivedT>
     478          188 : int outletController<derivedT>::channelState( const std::string & channel )
     479              : {
     480          188 :    int st = outletState(m_channels[channel].m_outlets[0]);
     481              : 
     482          292 :    for( size_t n = 1; n < m_channels[channel].m_outlets.size(); ++n )
     483              :    {
     484          104 :       if( st != outletState(m_channels[channel].m_outlets[n]) ) st = 1;
     485              :    }
     486              : 
     487          188 :    return st;
     488              : }
     489              : 
     490              : template<class derivedT>
     491           34 : int outletController<derivedT>::turnChannelOn( const std::string & channel )
     492              : {
     493              : 
     494              :    #ifndef OUTLET_CTRL_TEST_NOLOG
     495            0 :    derivedT::template log<software_debug>({__FILE__, __LINE__, "turning on channel " + channel});
     496              :    #endif
     497              : 
     498              :    #ifndef OUTLET_CTRL_TEST_NOINDI
     499              :    //m_channels[channel].m_indiP_prop["target"].setValue("On");
     500            0 :    indi::updateIfChanged(m_channels[channel].m_indiP_prop, "target", std::string("On"), derived().m_indiDriver, INDI_BUSY );
     501              :    #endif
     502              : 
     503              :    //If order is specified, get first outlet number
     504           34 :    size_t n = 0;
     505           34 :    if( m_channels[channel].m_onOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_onOrder[0];
     506              : 
     507              :    //turn on first outlet.
     508           34 :    if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
     509              :    {
     510              : 
     511              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     512            0 :          derivedT::template log<software_error>({__FILE__, __LINE__, "error turning on outlet " + std::to_string(n)});
     513              :       #else
     514            0 :          std::cerr << "Failed to turn on outlet " << n << "\n";
     515              :       #endif
     516              : 
     517            0 :       return -1;
     518              :    }
     519              : 
     520              :    #ifndef OUTLET_CTRL_TEST_NOLOG
     521            0 :    derivedT::template log<outlet_state>({ (uint8_t) (n  + m_firstOne), 2});
     522              :    #endif
     523              : 
     524              :    //Now do the rest
     525           56 :    for(size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
     526              :    {
     527              :       //If order is specified, get next outlet number
     528           22 :       n=i;
     529           22 :       if( m_channels[channel].m_onOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_onOrder[i];
     530              : 
     531              :       //Delay if specified
     532           22 :       if( m_channels[channel].m_onDelays.size() == m_channels[channel].m_outlets.size() )
     533              :       {
     534            4 :          mx::sys::milliSleep(m_channels[channel].m_onDelays[i]);
     535              :       }
     536              : 
     537              :       //turn on next outlet
     538              : 
     539           22 :       if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
     540              :       {
     541              : 
     542              :          #ifndef OUTLET_CTRL_TEST_NOLOG
     543            0 :          derivedT::template log<software_error>({__FILE__, __LINE__, "error turning on outlet " + std::to_string(n)});
     544              :          #else
     545            0 :          std::cerr << "Failed to turn on outlet " << n << "\n";
     546              :          #endif
     547              : 
     548            0 :          return -1;
     549              :       }
     550              : 
     551              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     552            0 :       derivedT::template log<outlet_state>({ (uint8_t) (n  + m_firstOne ), 2});
     553              :       #endif
     554              :    }
     555              : 
     556              :    #ifndef OUTLET_CTRL_TEST_NOLOG
     557            0 :    derivedT::template log<outlet_channel_state>({ channel, 2});
     558              :    #endif
     559              : 
     560           34 :    return 0;
     561              : }
     562              : 
     563              : template<class derivedT>
     564           34 : int outletController<derivedT>::turnChannelOff( const std::string & channel )
     565              : {
     566              : 
     567              :    #ifndef OUTLET_CTRL_TEST_NOLOG
     568            0 :    derivedT::template log<software_debug>({__FILE__, __LINE__, "turning off channel " + channel});
     569              :    #endif
     570              : 
     571              :    #ifndef OUTLET_CTRL_TEST_NOINDI
     572              :    //m_channels[channel].m_indiP_prop["target"].setValue("Off");
     573            0 :    indi::updateIfChanged(m_channels[channel].m_indiP_prop, "target", std::string("Off"), derived().m_indiDriver, INDI_BUSY );
     574              :    #endif
     575              : 
     576              :    //If order is specified, get first outlet number
     577           34 :    size_t n = 0;
     578           34 :    if( m_channels[channel].m_offOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_offOrder[0];
     579              : 
     580              :    //turn off first outlet.
     581           34 :    if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
     582              :    {
     583              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     584            0 :          derivedT::template log<software_error>({__FILE__, __LINE__, "error turning off outlet " + std::to_string(n)});
     585              :       #else
     586            0 :          std::cerr << "Failed to turn off outlet " << n << "\n";
     587              :       #endif
     588              : 
     589            0 :       return -1;
     590              :    }
     591              : 
     592              :    #ifndef OUTLET_CTRL_TEST_NOLOG
     593            0 :    derivedT::template log<outlet_state>({ (uint8_t) (n  + m_firstOne), 0});
     594              :    #endif
     595              : 
     596              :    //Now do the rest
     597           56 :    for(size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
     598              :    {
     599              :       //If order is specified, get next outlet number
     600           22 :       n=i;
     601           22 :       if( m_channels[channel].m_offOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_offOrder[i];
     602              : 
     603              :       //Delay if specified
     604           22 :       if( m_channels[channel].m_offDelays.size() == m_channels[channel].m_outlets.size() )
     605              :       {
     606            4 :          mx::sys::milliSleep(m_channels[channel].m_offDelays[i]);
     607              :       }
     608              : 
     609              :       //turn off next outlet
     610           22 :       if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
     611              :       {
     612              :          #ifndef OUTLET_CTRL_TEST_NOLOG
     613            0 :             derivedT::template log<software_error>({__FILE__, __LINE__, "error turning off outlet " + std::to_string(n)});
     614              :          #else
     615            0 :             std::cerr << "Failed to turn off outlet " << n << "\n";
     616              :          #endif
     617              : 
     618            0 :          return -1;
     619              :       }
     620              : 
     621              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     622            0 :       derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 0});
     623              :       #endif
     624              :    }
     625              : 
     626              :    #ifndef OUTLET_CTRL_TEST_NOLOG
     627            0 :    derivedT::template log<outlet_channel_state>({ channel, 0});
     628              :    #endif
     629              : 
     630           34 :    return 0;
     631              : }
     632              : 
     633              : template<class derivedT>
     634            0 : int outletController<derivedT>::st_newCallBack_channels( void * app,
     635              :                                                          const pcf::IndiProperty &ipRecv
     636              :                                                        )
     637              : {
     638            0 :    return static_cast<derivedT *>(app)->newCallBack_channels(ipRecv);
     639              : }
     640              : 
     641              : template<class derivedT>
     642            0 : int outletController<derivedT>::newCallBack_channels( const pcf::IndiProperty &ipRecv )
     643              : {
     644              :    //Check if we're in state READY before doing anything
     645            0 :    if(derived().state() != stateCodes::READY)
     646              :    {
     647              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     648            0 :       derivedT::template log<text_log>("can't change outlet state when not READY", logPrio::LOG_ERROR);
     649              :       #endif
     650              : 
     651            0 :       return -1;
     652              :    }
     653              : 
     654              :    //Interogate ipRecv to figure out which channel it is.
     655              :    //And then call turn on or turn off based on requested state.
     656            0 :    std::string name = ipRecv.getName();
     657              : 
     658            0 :    std::string state, target;
     659              : 
     660            0 :    if(ipRecv.find("state"))
     661              :    {
     662            0 :       state = ipRecv["state"].get<std::string>();
     663              :    }
     664              : 
     665            0 :    if(ipRecv.find("target"))
     666              :    {
     667            0 :       target = ipRecv["target"].get<std::string>();
     668              :    }
     669              : 
     670            0 :    if( target == "" ) target = state;
     671            0 :    target = mx::ioutils::toUpper(target);
     672              : 
     673            0 :    if( target == "ON" )
     674              :    {
     675            0 :       return turnChannelOn(name);
     676              :    }
     677              : 
     678            0 :    if(target == "OFF")
     679              :    {
     680            0 :       return turnChannelOff(name);
     681              :    }
     682              : 
     683            0 :    return 0;
     684            0 : }
     685              : 
     686              : template<class derivedT>
     687            0 : int outletController<derivedT>::setupINDI()
     688              : {
     689              :    //Register the static INDI properties
     690            0 :    m_indiP_chOutlets = pcf::IndiProperty(pcf::IndiProperty::Text);
     691            0 :    m_indiP_chOutlets.setDevice(derived().configName());
     692            0 :    m_indiP_chOutlets.setName("channelOutlets");
     693            0 :    m_indiP_chOutlets.setPerm(pcf::IndiProperty::ReadOnly);
     694            0 :    m_indiP_chOutlets.setState(pcf::IndiProperty::Idle);
     695              : 
     696            0 :    if(derived().registerIndiPropertyReadOnly(m_indiP_chOutlets) < 0)
     697              :    {
     698              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     699            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     700              :       #endif
     701            0 :       return -1;
     702              :    }
     703              : 
     704            0 :    m_indiP_chOnDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
     705            0 :    m_indiP_chOnDelays.setDevice(derived().configName());
     706            0 :    m_indiP_chOnDelays.setName("channelOnDelays");
     707            0 :    m_indiP_chOnDelays.setPerm(pcf::IndiProperty::ReadOnly);
     708            0 :    m_indiP_chOnDelays.setState(pcf::IndiProperty::Idle);
     709              : 
     710            0 :    if(derived().registerIndiPropertyReadOnly(m_indiP_chOnDelays) < 0)
     711              :    {
     712              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     713            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     714              :       #endif
     715            0 :       return -1;
     716              :    }
     717              : 
     718            0 :    m_indiP_chOffDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
     719            0 :    m_indiP_chOffDelays.setDevice(derived().configName());
     720            0 :    m_indiP_chOffDelays.setName("channelOffDelays");
     721            0 :    m_indiP_chOffDelays.setPerm(pcf::IndiProperty::ReadOnly);
     722            0 :    m_indiP_chOffDelays.setState(pcf::IndiProperty::Idle);
     723              : 
     724            0 :    if(derived().registerIndiPropertyReadOnly(m_indiP_chOffDelays) < 0)
     725              :    {
     726              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     727            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     728              :       #endif
     729            0 :       return -1;
     730              :    }
     731              : 
     732              :    //Create channel properties and register callback.
     733            0 :    for(auto it = m_channels.begin(); it != m_channels.end(); ++it)
     734              :    {
     735            0 :       it->second.m_indiP_prop = pcf::IndiProperty (pcf::IndiProperty::Text);
     736            0 :       it->second.m_indiP_prop.setDevice(derived().configName());
     737            0 :       it->second.m_indiP_prop.setName(it->first);
     738            0 :       it->second.m_indiP_prop.setPerm(pcf::IndiProperty::ReadWrite);
     739            0 :       it->second.m_indiP_prop.setState( pcf::IndiProperty::Idle );
     740              : 
     741              :       //add elements 'state' and 'target'
     742            0 :       it->second.m_indiP_prop.add (pcf::IndiElement("state"));
     743            0 :       it->second.m_indiP_prop.add (pcf::IndiElement("target"));
     744              : 
     745            0 :       if( derived().registerIndiPropertyNew( it->second.m_indiP_prop, st_newCallBack_channels) < 0)
     746              :       {
     747              :          #ifndef OUTLET_CTRL_TEST_NOLOG
     748            0 :          derivedT::template log<software_error>({__FILE__,__LINE__});
     749              :          #endif
     750            0 :          return -1;
     751              :       }
     752              : 
     753              :       //Load values into the static INDI properties
     754            0 :       m_indiP_chOutlets.add(pcf::IndiElement(it->first));
     755            0 :       std::string os = std::to_string(it->second.m_outlets[0]);
     756            0 :       for(size_t i=1;i< it->second.m_outlets.size();++i) os += "," + std::to_string(it->second.m_outlets[i]);
     757            0 :       m_indiP_chOutlets[it->first].set(os);
     758              : 
     759            0 :       m_indiP_chOnDelays.add(pcf::IndiElement(it->first));
     760            0 :       double sum=0;
     761            0 :       for(size_t i=0;i< it->second.m_onDelays.size();++i) sum += it->second.m_onDelays[i];
     762            0 :       m_indiP_chOnDelays[it->first].set(sum);
     763              : 
     764            0 :       m_indiP_chOffDelays.add(pcf::IndiElement(it->first));
     765            0 :       sum=0;
     766            0 :       for(size_t i=0;i< it->second.m_offDelays.size();++i) sum += it->second.m_offDelays[i];
     767            0 :       m_indiP_chOffDelays[it->first].set(sum);
     768              : 
     769              :    }
     770              : 
     771              :    //Register the outletStates INDI property, and add an element for each outlet.
     772            0 :    m_indiP_outletStates = pcf::IndiProperty (pcf::IndiProperty::Text);
     773            0 :    m_indiP_outletStates.setDevice(derived().configName());
     774            0 :    m_indiP_outletStates.setName("outlet");
     775            0 :    m_indiP_outletStates.setPerm(pcf::IndiProperty::ReadWrite);
     776            0 :    m_indiP_outletStates.setState( pcf::IndiProperty::Idle );
     777              : 
     778            0 :    if( derived().registerIndiPropertyReadOnly(m_indiP_outletStates) < 0)
     779              :    {
     780              :       #ifndef OUTLET_CTRL_TEST_NOLOG
     781            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     782              :       #endif
     783            0 :       return -1;
     784              :    }
     785              : /*
     786              :    auto result =  derived().m_indiNewCallBacks.insert( { "outlet", {&m_indiP_outletStates, nullptr}});
     787              : 
     788              :    if(!result.second)
     789              :    {
     790              :       return -1;
     791              :    }*/
     792              : 
     793            0 :    for(size_t i=0; i< m_outletStates.size(); ++i)
     794              :    {
     795            0 :       m_indiP_outletStates.add (pcf::IndiElement(std::to_string(i+m_firstOne)));
     796              :    }
     797              : 
     798            0 :    return 0;
     799              : }
     800              : 
     801              : std::string stateIntToString(int st);
     802              : 
     803              : template<class derivedT>
     804            0 : int outletController<derivedT>::updateINDI()
     805              : {
     806            0 :    if( !derived().m_indiDriver ) return 0;
     807              : 
     808              :    //Publish outlet states (only bother if they've changed)
     809            0 :    for(size_t i=0; i< m_outletStates.size(); ++i)
     810              :    {
     811            0 :       indi::updateIfChanged(m_indiP_outletStates, std::to_string(i+m_firstOne), stateIntToString(m_outletStates[i]), derived().m_indiDriver);
     812              :    }
     813              : 
     814              :    //Publish channel states (only bother if they've changed)
     815            0 :    for(auto it = m_channels.begin(); it != m_channels.end(); ++it)
     816              :    {
     817            0 :       std::string state = stateIntToString( channelState( it->first ));
     818              : 
     819            0 :       indi::updateIfChanged( it->second.m_indiP_prop, "state", state, derived().m_indiDriver );
     820              :    }
     821              : 
     822              : 
     823              : 
     824            0 :    return 0;
     825              : }
     826              : 
     827              : } //namespace dev
     828              : } //namespace app
     829              : } //namespace MagAOX
     830              : 
     831              : #endif //app_outletController_hpp
        

Generated by: LCOV version 2.0-1