LCOV - code coverage report
Current view: top level - apps/flipperCtrl - flipperCtrl.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 0.0 % 189 0
Test Date: 2026-01-03 21:03:39 Functions: 0.0 % 15 0

            Line data    Source code
       1              : /** \file flipperCtrl.hpp
       2              :   * \brief The MagAO-X XXXXXX header file
       3              :   *
       4              :   * \ingroup flipperCtrl_files
       5              :   */
       6              : 
       7              : #ifndef flipperCtrl_hpp
       8              : #define flipperCtrl_hpp
       9              : 
      10              : 
      11              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      12              : #include "../../magaox_git_version.h"
      13              : 
      14              : /** \defgroup flipperCtrl
      15              :   * \brief The XXXXXX application to do YYYYYYY
      16              :   *
      17              :   * <a href="../handbook/operating/software/apps/XXXXXX.html">Application Documentation</a>
      18              :   *
      19              :   * \ingroup apps
      20              :   *
      21              :   */
      22              : 
      23              : /** \defgroup flipperCtrl_files
      24              :   * \ingroup flipperCtrl
      25              :   */
      26              : 
      27              : namespace MagAOX
      28              : {
      29              : namespace app
      30              : {
      31              : 
      32              : /// The MagAO-X xxxxxxxx
      33              : /**
      34              :   * \ingroup flipperCtrl
      35              :   */
      36              : class flipperCtrl : public MagAOXApp<true>, public tty::usbDevice, public dev::ioDevice, public dev::telemeter<flipperCtrl>
      37              : {
      38              : 
      39              :    //Give the test harness access.
      40              :    friend class flipperCtrl_test;
      41              : 
      42              :    friend class dev::telemeter<flipperCtrl>;
      43              : 
      44              :    typedef dev::telemeter<flipperCtrl> telemeterT;
      45              : 
      46              : protected:
      47              : 
      48              :    /** \name Configurable Parameters
      49              :      *@{
      50              :      */
      51              : 
      52              :    //here add parameters which will be config-able at runtime
      53              :    int m_inPos {1};
      54              :    int m_outPos {2};
      55              : 
      56              :    ///@}
      57              : 
      58              : 
      59              :    int m_pos {1};
      60              :    int m_tgt {0};
      61              : 
      62              : public:
      63              :    /// Default c'tor.
      64              :    flipperCtrl();
      65              : 
      66              :    /// D'tor, declared and defined for noexcept.
      67            0 :    ~flipperCtrl() noexcept
      68            0 :    {}
      69              : 
      70              :    virtual void setupConfig();
      71              : 
      72              :    /// Implementation of loadConfig logic, separated for testing.
      73              :    /** This is called by loadConfig().
      74              :      */
      75              :    int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
      76              : 
      77              :    virtual void loadConfig();
      78              : 
      79              :    /// Startup function
      80              :    /**
      81              :      *
      82              :      */
      83              :    virtual int appStartup();
      84              : 
      85              :    /// Implementation of the FSM for flipperCtrl.
      86              :    /**
      87              :      * \returns 0 on no critical error
      88              :      * \returns -1 on an error requiring shutdown
      89              :      */
      90              :    virtual int appLogic();
      91              : 
      92              :    /// Shutdown the app.
      93              :    /**
      94              :      *
      95              :      */
      96              :    virtual int appShutdown();
      97              : 
      98              : 
      99              :    int getPos();
     100              : 
     101              :    int moveTo(int pos);
     102              : 
     103              :    pcf::IndiProperty m_indiP_position;
     104              : 
     105            0 :    INDI_NEWCALLBACK_DECL(flipperCtrl, m_indiP_position);
     106              : 
     107              : 
     108              :    /* Telemetry */
     109              :    int checkRecordTimes();
     110              : 
     111              :    int recordTelem( const telem_stage *);
     112              : 
     113              :    int recordStage( bool force = false);
     114              : 
     115              : };
     116              : 
     117            0 : flipperCtrl::flipperCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     118              : {
     119            0 :    m_powerMgtEnabled = true;
     120            0 :    return;
     121            0 : }
     122              : 
     123            0 : void flipperCtrl::setupConfig()
     124              : {
     125            0 :    tty::usbDevice::setupConfig(config);
     126            0 :    dev::ioDevice::setupConfig(config);
     127              : 
     128            0 :    config.add("flipper.reverse", "", "flipper.reverse", argType::Required, "flipper", "reverse", false, "bool", "If true, reverse the positions for in and out.");
     129              : 
     130            0 :    telemeterT::setupConfig(config);
     131            0 : }
     132              : 
     133            0 : int flipperCtrl::loadConfigImpl( mx::app::appConfigurator & _config )
     134              : {
     135            0 :    this->m_baudRate = B115200; //default for MCBL controller.  Will be overridden by any config setting.
     136              : 
     137            0 :    int rv = tty::usbDevice::loadConfig(_config);
     138              : 
     139            0 :    if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
     140              :    {
     141            0 :       log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     142              :    }
     143              : 
     144            0 :    dev::ioDevice::loadConfig(_config);
     145              : 
     146            0 :    bool rev = false;
     147            0 :    _config(rev, "flipper.reverse");
     148              : 
     149            0 :    if(rev)
     150              :    {
     151            0 :       m_inPos = 2;
     152            0 :       m_outPos = 1;
     153              :    }
     154              : 
     155            0 :    telemeterT::loadConfig(_config);
     156              : 
     157            0 :    return 0;
     158              : }
     159              : 
     160            0 : void flipperCtrl::loadConfig()
     161              : {
     162            0 :    if(loadConfigImpl(config)<0)
     163              :    {
     164            0 :       log<software_critical>({__FILE__, __LINE__});
     165            0 :       m_shutdown = 1;
     166            0 :       return;
     167              :    }
     168              : }
     169              : 
     170            0 : int flipperCtrl::appStartup()
     171              : {
     172            0 :    createStandardIndiSelectionSw( m_indiP_position, "presetName", {"in", "out"});
     173              : 
     174            0 :    if( registerIndiPropertyNew( m_indiP_position, INDI_NEWCALLBACK(m_indiP_position)) < 0)
     175              :    {
     176            0 :       log<software_error>({__FILE__,__LINE__});
     177            0 :       return -1;
     178              :    }
     179              : 
     180            0 :    if(telemeterT::appStartup() < 0)
     181              :    {
     182            0 :       return log<software_error,-1>({__FILE__,__LINE__});
     183              :    }
     184              : 
     185            0 :    return 0;
     186              : }
     187              : 
     188            0 : int flipperCtrl::appLogic()
     189              : {
     190            0 :    if(state() == stateCodes::POWERON)
     191              :    {
     192            0 :       state(stateCodes::NODEVICE);
     193              :    }
     194              : 
     195            0 :    if( state() == stateCodes::NODEVICE )
     196              :    {
     197            0 :       int rv = tty::usbDevice::getDeviceName();
     198            0 :       if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
     199              :       {
     200            0 :          state(stateCodes::FAILURE);
     201            0 :          if(!stateLogged())
     202              :          {
     203            0 :             log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     204              :          }
     205            0 :          return -1;
     206              :       }
     207              : 
     208            0 :       if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
     209              :       {
     210            0 :          state(stateCodes::NODEVICE);
     211            0 :          if(!stateLogged())
     212              :          {
     213            0 :             std::stringstream logs;
     214            0 :             logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
     215            0 :             log<text_log>(logs.str());
     216            0 :          }
     217            0 :          return 0;
     218              :       }
     219              :       else
     220              :       {
     221            0 :          state(stateCodes::NOTCONNECTED);
     222            0 :          if(!stateLogged())
     223              :          {
     224            0 :             std::stringstream logs;
     225            0 :             logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
     226            0 :             log<text_log>(logs.str());
     227            0 :          }
     228              :       }
     229              :    }
     230              : 
     231            0 :    if( state() == stateCodes::NOTCONNECTED )
     232              :    {
     233            0 :       elevatedPrivileges ep(this);
     234            0 :       int rv = connect();
     235            0 :       ep.restore();
     236              : 
     237            0 :       if(rv < 0)
     238              :       {
     239            0 :          int nrv = tty::usbDevice::getDeviceName();
     240            0 :          if(nrv < 0 && nrv != TTY_E_DEVNOTFOUND && nrv != TTY_E_NODEVNAMES)
     241              :          {
     242            0 :             state(stateCodes::FAILURE);
     243            0 :             if(!stateLogged()) log<software_critical>({__FILE__, __LINE__, nrv, tty::ttyErrorString(nrv)});
     244            0 :             return -1;
     245              :          }
     246              : 
     247            0 :          if(nrv == TTY_E_DEVNOTFOUND || nrv == TTY_E_NODEVNAMES)
     248              :          {
     249            0 :             state(stateCodes::NODEVICE);
     250              : 
     251            0 :             if(!stateLogged())
     252              :             {
     253            0 :                std::stringstream logs;
     254            0 :                logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " no longer found in udev";
     255            0 :                log<text_log>(logs.str());
     256            0 :             }
     257            0 :             return 0;
     258              :          }
     259              :       }
     260              : 
     261            0 :       state(stateCodes::CONNECTED);
     262            0 :    }
     263              : 
     264            0 :    if( state() == stateCodes::CONNECTED )
     265              :    {
     266            0 :       std::unique_lock<std::mutex> lock(m_indiMutex);
     267            0 :       getPos();
     268            0 :       m_tgt = m_pos;
     269              : 
     270            0 :       state(stateCodes::READY);
     271            0 :    }
     272              : 
     273            0 :    if( state() == stateCodes::READY || state() == stateCodes::OPERATING)
     274              :    {
     275            0 :       std::unique_lock<std::mutex> lock(m_indiMutex);
     276              : 
     277            0 :       getPos();
     278              : 
     279            0 :       if(m_pos == m_inPos)
     280              :       {
     281            0 :          if(m_pos == m_tgt)
     282              :          {
     283            0 :             updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::On, INDI_IDLE);
     284            0 :             updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::Off, INDI_IDLE);
     285            0 :             state(stateCodes::READY);
     286              :          }
     287              :          else
     288              :          {
     289            0 :             updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::On, INDI_BUSY);
     290            0 :             updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::Off, INDI_BUSY);
     291            0 :             state(stateCodes::OPERATING);
     292              :          }
     293              :       }
     294              :       else
     295              :       {
     296            0 :          if(m_pos == m_tgt)
     297              :          {
     298            0 :             updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::Off, INDI_IDLE);
     299            0 :             updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::On, INDI_IDLE);
     300            0 :             state(stateCodes::READY);
     301              :          }
     302              :          else
     303              :          {
     304            0 :             updateSwitchIfChanged(m_indiP_position, "in", pcf::IndiElement::Off, INDI_BUSY);
     305            0 :             updateSwitchIfChanged(m_indiP_position, "out", pcf::IndiElement::On, INDI_BUSY);
     306            0 :             state(stateCodes::OPERATING);
     307              :          }
     308              :       }
     309              : 
     310            0 :       recordStage();
     311              : 
     312            0 :       if(telemeterT::appLogic() < 0)
     313              :       {
     314            0 :          log<software_error>({__FILE__, __LINE__});
     315            0 :          return 0;
     316              :       }
     317              :     /*  */
     318              : 
     319              :       //sleep(2);
     320            0 :    }
     321              : 
     322            0 :    return 0;
     323              : }
     324              : 
     325            0 : int flipperCtrl::appShutdown()
     326              : {
     327            0 :    return 0;
     328              : }
     329              : 
     330              : 
     331            0 : int flipperCtrl::getPos()
     332              : {
     333            0 :    std::string header(6,'\0');
     334              : 
     335            0 :    header[0] = 0x80;
     336            0 :    header[1] = 0x04;
     337            0 :    header[2] = 0x00;
     338            0 :    header[3] = 0x00;
     339            0 :    header[4] = 0x50;
     340            0 :    header[5] = 0x01;
     341              : 
     342            0 :    tty::ttyWrite( header, m_fileDescrip, m_writeTimeout);
     343              : 
     344            0 :    std::string response;
     345            0 :    if(tty::ttyRead(response, 20, m_fileDescrip, m_readTimeout) < 0)
     346              :    {
     347            0 :       log<software_error>({__FILE__,__LINE__, "error getting response from flipper"});
     348              :    }
     349              : 
     350            0 :    if(response[16] == 1)
     351              :    {
     352            0 :       m_pos = 1;
     353              :    }
     354              :    else
     355              :    {
     356            0 :       m_pos = 2;
     357              :    }
     358              : 
     359            0 :    return 0;
     360            0 : }
     361              : 
     362            0 : int flipperCtrl::moveTo(int pos)
     363              : {
     364            0 :    std::string header(6,'\0');
     365              : 
     366            0 :    header[0] = 0x6A;
     367            0 :    header[1] = 0x04;
     368            0 :    header[2] = 0x00;
     369            0 :    if(pos == 1)
     370              :    {
     371            0 :       header[3] = 0x01;
     372              :    }
     373            0 :    else if(pos == 2)
     374              :    {
     375            0 :       header[3] = 0x02;
     376              :    }
     377              :    else
     378              :    {
     379            0 :       return log<software_error,-1>({__FILE__,__LINE__, "invalid position"});
     380              :    }
     381            0 :    header[4] = 0x50;
     382            0 :    header[5] = 0x01;
     383              : 
     384            0 :    tty::ttyWrite( header, m_fileDescrip, m_writeTimeout);
     385              : 
     386            0 :    return 0;
     387            0 : }
     388              : 
     389            0 : INDI_NEWCALLBACK_DEFN(flipperCtrl, m_indiP_position )(const pcf::IndiProperty &ipRecv)
     390              : {
     391            0 :    if(ipRecv.getName() != m_indiP_position.getName())
     392              :    {
     393            0 :       log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
     394            0 :       return -1;
     395              :    }
     396              : 
     397              : 
     398            0 :    int newpos = 0;
     399              : 
     400            0 :    if(ipRecv.find("in"))
     401              :    {
     402            0 :       if(ipRecv["in"].getSwitchState() == pcf::IndiElement::On)
     403              :       {
     404            0 :          newpos = m_inPos;
     405              :       }
     406              :    }
     407              : 
     408            0 :    if(ipRecv.find("out"))
     409              :    {
     410            0 :       if(ipRecv["out"].getSwitchState() == pcf::IndiElement::On)
     411              :       {
     412            0 :          if(newpos)
     413              :          {
     414            0 :             log<text_log>("can not set position to both in and out", logPrio::LOG_ERROR);
     415              :          }
     416            0 :          else newpos = m_outPos;
     417              :       }
     418              :    }
     419              : 
     420            0 :    if(newpos)
     421              :    {
     422            0 :       m_tgt = newpos;
     423              : 
     424            0 :       std::unique_lock<std::mutex> lock(m_indiMutex);
     425              : 
     426            0 :       m_indiP_position.setState (INDI_BUSY);
     427            0 :       m_indiDriver->sendSetProperty (m_indiP_position);
     428              : 
     429            0 :       recordStage(true);
     430            0 :       state(stateCodes::OPERATING);
     431              : 
     432            0 :       if(moveTo(m_tgt) < 0)
     433              :       {
     434            0 :          return log<software_error,-1>({__FILE__, __LINE__});
     435              :       }
     436              : 
     437            0 :       recordStage(true);
     438              : 
     439            0 :       return 0;
     440            0 :    }
     441              : 
     442              : 
     443              : 
     444            0 :    return 0;
     445              : }
     446              : 
     447              : 
     448            0 : int flipperCtrl::checkRecordTimes()
     449              : {
     450            0 :    return telemeterT::checkRecordTimes(telem_stage());
     451              : }
     452              : 
     453            0 : int flipperCtrl::recordTelem( const telem_stage * )
     454              : {
     455            0 :    return recordStage(true);
     456              : }
     457              : 
     458              : inline
     459            0 : int flipperCtrl::recordStage( bool force )
     460              : {
     461              :    static int last_pos = -1;
     462              :    static int last_moving = -1;
     463              : 
     464            0 :    int moving = (m_tgt != m_pos);
     465              : 
     466            0 :    if(last_pos != m_pos || last_moving != moving || force)
     467              :    {
     468            0 :       std::string ps = "in";
     469            0 :       if(m_pos == m_outPos) ps = "out";
     470              : 
     471            0 :       int8_t mv = moving;
     472            0 :       float fpos = m_pos;
     473              : 
     474            0 :       telem<telem_stage>({ mv, fpos, ps});
     475              : 
     476            0 :       last_pos = m_pos;
     477            0 :       last_moving = moving;
     478            0 :    }
     479              : 
     480              : 
     481            0 :    return 0;
     482              : }
     483              : 
     484              : 
     485              : } //namespace app
     486              : } //namespace MagAOX
     487              : 
     488              : #endif //flipperCtrl_hpp
        

Generated by: LCOV version 2.0-1