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

            Line data    Source code
       1              : /** \file rhusbMon.hpp
       2              :   * \brief The MagAO-X RH USB monitor
       3              :   *
       4              :   * \ingroup rhusbMon_files
       5              :   */
       6              : 
       7              : #ifndef rhusbMon_hpp
       8              : #define rhusbMon_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              : #include "rhusbMonParsers.hpp"
      15              : 
      16              : /** \defgroup rhusbMon
      17              :   * \brief Application to monitor an Omega RH USB probe.
      18              :   *
      19              :   * <a href="../handbook/operating/software/apps/rhusbMon.html">Application Documentation</a>
      20              :   *
      21              :   * \ingroup apps
      22              :   *
      23              :   */
      24              : 
      25              : /** \defgroup rhusbMon_files
      26              :   * \ingroup rhusbMon
      27              :   */
      28              : 
      29              : namespace MagAOX
      30              : {
      31              : namespace app
      32              : {
      33              : 
      34              : /// The MagAO-X RH-USB monitoring class
      35              : /** Interacts with the Omega RH-USB probe used for DM chamber humidity monitoring.
      36              :   *
      37              :   * \todo need a test mode (compile-time) which adds a way (INDI?) to initiate testing of parameter limits.
      38              :   *
      39              :   * \ingroup rhusbMon
      40              :   */
      41              : class rhusbMon : public MagAOXApp<true>, public tty::usbDevice, public dev::ioDevice, public dev::telemeter<rhusbMon>
      42              : {
      43              : 
      44              :    //Give the test harness access.
      45              :    friend class rhusbMon_test;
      46              : 
      47              :    //Let telemeter work.
      48              :    friend class dev::telemeter<rhusbMon>;
      49              : 
      50              : protected:
      51              : 
      52              :    /** \name Configurable Parameters
      53              :      *@{
      54              :      */
      55              :    float m_warnTemp {30};  ///< This is abnormally high if the system is working, but still safe.
      56              :    float m_alertTemp {35}; ///< This is the actual limit, shut down should occur.
      57              :    float m_emergTemp {40}; ///< Must shutdown immediately.
      58              : 
      59              :    float m_warnHumid {18};  ///< This is abnormally high if the system is working, but still safe.
      60              :    float m_alertHumid {20}; ///< This is the actual limit, shut down should occur.
      61              :    float m_emergHumid {22}; ///< Must shutdown immediately.
      62              : 
      63              :    ///@}
      64              : 
      65              :    float m_temp {-999};
      66              :    float m_rh {-999};
      67              : 
      68              :    pcf::IndiProperty m_indiP_temp;
      69              :    pcf::IndiProperty m_indiP_rh;
      70              : 
      71              : 
      72              : public:
      73              :    /// Default c'tor.
      74              :    rhusbMon();
      75              : 
      76              :    /// D'tor, declared and defined for noexcept.
      77            0 :    ~rhusbMon() noexcept
      78            0 :    {}
      79              : 
      80              :    virtual void setupConfig();
      81              : 
      82              :    /// Implementation of loadConfig logic, separated for testing.
      83              :    /** This is called by loadConfig().
      84              :      */
      85              :    int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
      86              : 
      87              :    virtual void loadConfig();
      88              : 
      89              :    /// Startup function
      90              :    /**
      91              :      *
      92              :      */
      93              :    virtual int appStartup();
      94              : 
      95              :    /// Implementation of the FSM for rhusbMon.
      96              :    /**
      97              :      * \returns 0 on no critical error
      98              :      * \returns -1 on an error requiring shutdown
      99              :      */
     100              :    virtual int appLogic();
     101              : 
     102              :    /// Shutdown the app.
     103              :    /**
     104              :      *
     105              :      */
     106              :    virtual int appShutdown();
     107              : 
     108              :    /// Connect to the probe
     109              :    /** Search for the USB device in udev and attempt ot open it.
     110              :      * The result is reported via the FSM state (NODEVICE, NOTCONNECTED, CONNECTED).
     111              :      *
     112              :      * \returns -1 on an error attempting to read udev
     113              :      * \returns 0 if device not found, or connection does not work, or if connected.
     114              :      */
     115              :    int connect();
     116              : 
     117              :    /// Read current values from the RH-USB probe
     118              :    /** Issues the 'C' and 'H' commands to get temperature and humidity.
     119              :      *
     120              :      * \returns -1 on error writing or reading, or on a parsing error
     121              :      *
     122              :      * \see \parseC( float &, const std::string)
     123              :      * \see \parseH( float &, const std::string)
     124              :      */
     125              :    int readProbe();
     126              : 
     127              :    /** \name Telemeter Interface
     128              :      *
     129              :      * @{
     130              :      */
     131              :    int checkRecordTimes();
     132              : 
     133              :    int recordTelem( const telem_rhusb * );
     134              : 
     135              : protected:
     136              : 
     137              :    int recordRH( bool force = false );
     138              : 
     139              :    ///@}
     140              : 
     141              : };
     142              : 
     143            0 : rhusbMon::rhusbMon() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     144              : {
     145            0 :    dev::ioDevice::m_readTimeout = 2000;
     146            0 :    dev::ioDevice::m_writeTimeout = 1000;
     147              : 
     148            0 :    return;
     149            0 : }
     150              : 
     151            0 : void rhusbMon::setupConfig()
     152              : {
     153            0 :    config.add("temp.warning", "", "temp.warning", argType::Required, "temp", "warning", false, "float", "Temperature at which to issue a warning.  Default is 30.");
     154            0 :    config.add("temp.alert", "", "temp.alert", argType::Required, "temp", "alert", false, "float", "Temperature at which to issue an alert.  Default is 35.");
     155            0 :    config.add("temp.emergency", "", "temp.emergency", argType::Required, "temp", "emergency", false, "float", "Temperature at which to issue an emergency.  Default is 40.");
     156              : 
     157            0 :    config.add("humid.warning", "", "humid.warning", argType::Required, "humid", "warning", false, "float", "Humidity at which to issue a warning.  Default is 18.");
     158            0 :    config.add("humid.alert", "", "humid.alert", argType::Required, "humid", "alert", false, "float", "Humidity at which to issue an alert.  Default is 20.");
     159            0 :    config.add("humid.emergency", "", "humid.emergency", argType::Required, "humid", "emergency", false, "float", "Humidity at which to issue an emergency.  Default is 22.");
     160              : 
     161            0 :    tty::usbDevice::setupConfig(config);
     162            0 :    dev::ioDevice::setupConfig(config);
     163              : 
     164            0 :    dev::telemeter<rhusbMon>::setupConfig(config);
     165            0 : }
     166              : 
     167            0 : int rhusbMon::loadConfigImpl( mx::app::appConfigurator & _config )
     168              : {
     169              : 
     170            0 :    _config(m_warnTemp, "temp.warning");
     171            0 :    _config(m_alertTemp, "temp.alert");
     172            0 :    _config(m_emergTemp, "temp.emergency");
     173              : 
     174            0 :    _config(m_warnHumid, "humid.warning");
     175            0 :    _config(m_alertHumid, "humid.alert");
     176            0 :    _config(m_emergHumid, "humid.emergency");
     177              : 
     178            0 :    tty::usbDevice::loadConfig(_config);
     179            0 :    dev::ioDevice::loadConfig(_config);
     180              : 
     181            0 :    dev::telemeter<rhusbMon>::loadConfig(_config);
     182              : 
     183            0 :    return 0;
     184              : }
     185              : 
     186            0 : void rhusbMon::loadConfig()
     187              : {
     188            0 :    loadConfigImpl(config);
     189            0 : }
     190              : 
     191            0 : int rhusbMon::appStartup()
     192              : {
     193            0 :    createROIndiNumber( m_indiP_temp, "temperature", "Temperature [C]");
     194            0 :    indi::addNumberElement<float>( m_indiP_temp, "current", -20., 120., 0, "%0.1f");
     195            0 :    m_indiP_temp["current"] = -999;
     196            0 :    registerIndiPropertyReadOnly(m_indiP_temp);
     197              : 
     198            0 :    createROIndiNumber( m_indiP_rh, "humidity", "Relative Humidity [%]");
     199            0 :    indi::addNumberElement<float>( m_indiP_rh, "current", 0., 100., 0, "%0.1f");
     200            0 :    m_indiP_rh["current"] = -999;
     201            0 :    registerIndiPropertyReadOnly(m_indiP_rh);
     202              : 
     203              : 
     204            0 :    if(dev::telemeter<rhusbMon>::appStartup() < 0)
     205              :    {
     206            0 :       return log<software_error,-1>({__FILE__,__LINE__});
     207              :    }
     208              : 
     209            0 :    return connect();
     210              : }
     211              : 
     212              : 
     213              : 
     214            0 : int rhusbMon::appLogic()
     215              : {
     216            0 :    if(state() == stateCodes::NODEVICE || state() == stateCodes::NOTCONNECTED || state() == stateCodes::ERROR)
     217              :    {
     218            0 :       int rv = connect();
     219            0 :       if(rv < 0) return log<software_error,-1>({__FILE__, __LINE__});
     220              :    }
     221              : 
     222            0 :    if(state() == stateCodes::CONNECTED || state() == stateCodes::OPERATING)
     223              :    {
     224            0 :       int rv = readProbe();
     225            0 :       if(rv == 0)
     226              :       {
     227            0 :          state(stateCodes::OPERATING);
     228              :       }
     229              :       else
     230              :       {
     231            0 :          state(stateCodes::ERROR);
     232            0 :          return log<software_error,0>({__FILE__, __LINE__});
     233              :       }
     234              :    }
     235              : 
     236            0 :    pcf::IndiProperty::PropertyStateType rhState = pcf::IndiProperty::Ok;
     237              :    //Check warning and alert values
     238            0 :    if(m_rh > m_emergHumid)
     239              :    {
     240            0 :       log<text_log>("RH > " + std::to_string(m_emergHumid) + "% : " + std::to_string(m_rh) + "%!  Shutdown immediately!", logPrio::LOG_EMERGENCY);
     241            0 :       rhState = pcf::IndiProperty::Alert;
     242              :    }
     243            0 :    else if(m_rh > m_alertHumid)
     244              :    {
     245            0 :       log<text_log>("RH > " + std::to_string(m_alertHumid) + "% : " + std::to_string(m_rh) + "%.  Fix or shutdown.", logPrio::LOG_ALERT);
     246            0 :       rhState = pcf::IndiProperty::Alert;
     247              :    }
     248            0 :    else if(m_rh > m_warnHumid)
     249              :    {
     250            0 :       log<text_log>("RH > " + std::to_string(m_warnHumid) + "% : " + std::to_string(m_rh) + "%.", logPrio::LOG_WARNING);
     251            0 :       rhState = pcf::IndiProperty::Alert;
     252              :    }
     253              : 
     254            0 :    pcf::IndiProperty::PropertyStateType tState = pcf::IndiProperty::Ok;
     255              :    //Check warning and alert values
     256            0 :    if(m_temp > m_emergTemp)
     257              :    {
     258            0 :       log<text_log>("Temp > " + std::to_string(m_emergTemp) + "C : " + std::to_string(m_temp) + "C!  Shutdown immediately!", logPrio::LOG_EMERGENCY);
     259            0 :       tState = pcf::IndiProperty::Alert;
     260              :    }
     261            0 :    else if(m_temp > m_alertTemp)
     262              :    {
     263            0 :       log<text_log>("Temp > " + std::to_string(m_alertTemp) + "C : " + std::to_string(m_temp) + "C.  Fix or shutdown.", logPrio::LOG_ALERT);
     264            0 :       tState = pcf::IndiProperty::Alert;
     265              :    }
     266            0 :    else if(m_temp > m_warnTemp)
     267              :    {
     268            0 :       log<text_log>("Temp > " + std::to_string(m_warnTemp) + "C : " + std::to_string(m_temp) + "C.", logPrio::LOG_WARNING);
     269            0 :       tState = pcf::IndiProperty::Alert;
     270              :    }
     271              : 
     272              :    //Scope for mutex
     273              :    {
     274            0 :       std::unique_lock<std::mutex> lock(m_indiMutex);
     275            0 :       updateIfChanged(m_indiP_temp, "current", m_temp, tState);
     276              : 
     277            0 :       m_indiP_rh["current"].set<float>(-999); //Force the update to get a new timestamp
     278            0 :       updateIfChanged(m_indiP_rh, "current", m_rh, rhState); ///\todo updateIfChanged should have a force flag
     279            0 :    }
     280              : 
     281            0 :    if(telemeter<rhusbMon>::appLogic() < 0)
     282              :    {
     283            0 :       return log<software_error,0>({__FILE__, __LINE__});
     284              :    }
     285              : 
     286            0 :    return 0;
     287              : }
     288              : 
     289            0 : int rhusbMon::appShutdown()
     290              : {
     291            0 :    return 0;
     292              : }
     293              : 
     294            0 : int rhusbMon::connect()
     295              : {
     296            0 :    int rv = tty::usbDevice::getDeviceName();
     297            0 :    if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
     298              :    {
     299              :       //There is no device reason for this to error.  Something is wrong.
     300            0 :       state(stateCodes::FAILURE);
     301            0 :       return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     302              :    }
     303              : 
     304            0 :    if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
     305              :    {
     306            0 :       state(stateCodes::NODEVICE);
     307              : 
     308            0 :       if(!stateLogged())
     309              :       {
     310            0 :          std::stringstream logs;
     311            0 :          logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
     312            0 :          log<text_log>(logs.str());
     313            0 :       }
     314            0 :       return 0;
     315              :    }
     316              :    else
     317              :    {
     318            0 :       state(stateCodes::NOTCONNECTED);
     319            0 :       if(!stateLogged())
     320              :       {
     321            0 :          std::stringstream logs;
     322            0 :          logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
     323            0 :          log<text_log>(logs.str());
     324            0 :       }
     325              : 
     326              :       //scope for elevated priv
     327              :       {
     328            0 :          elevatedPrivileges elPriv(this);
     329            0 :          rv = tty::usbDevice::connect();
     330            0 :       }
     331              : 
     332            0 :       if(rv == TTY_E_NOERROR)
     333              :       {
     334            0 :          state(stateCodes::CONNECTED);
     335            0 :          if(!stateLogged())
     336              :          {
     337            0 :             std::stringstream logs;
     338            0 :             logs << "Connected to " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " @ " << m_deviceName;
     339            0 :             log<text_log>(logs.str());
     340            0 :          }
     341              :       }
     342              :       else
     343              :       {
     344              :          //There is no power or other reason this should happen.  It means something is wrong and needs to be corrected.
     345            0 :          state(stateCodes::FAILURE);
     346            0 :          return log<software_critical, -1>({__FILE__,__LINE__, errno, rv, "Error opening connection: " + tty::ttyErrorString(rv)});
     347              :       }
     348              :    }
     349              : 
     350            0 :    return 0;
     351              : }
     352              : 
     353            0 : int rhusbMon::readProbe()
     354              : {
     355            0 :    std::string strRead;
     356              : 
     357            0 :    int rv = tty::ttyWriteRead( strRead, "C\r", "\r\n>", false, m_fileDescrip, dev::ioDevice::m_writeTimeout, dev::ioDevice::m_readTimeout);
     358            0 :    if(rv != TTY_E_NOERROR)
     359              :    {
     360            0 :       return log<software_error,-1>({__FILE__, __LINE__,  0, rv, "Error reading temp: " + tty::ttyErrorString(rv)});
     361              :    }
     362              : 
     363            0 :    rv = RH::parseC(m_temp, strRead);
     364            0 :    if(rv != 0)
     365              :    {
     366            0 :       if( rv == -1 )
     367              :       {
     368            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, no EOT"});
     369              :       }
     370            0 :       else if (rv == -2 )
     371              :       {
     372            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, no value"});
     373              :       }
     374            0 :       else if (rv == -3)
     375              :       {
     376            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, does not begin with digit"});
     377              :       }
     378              :       else
     379              :       {
     380            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp."});
     381              :       }
     382              :    }
     383              : 
     384            0 :    std::cout << m_temp << "\n";
     385              : 
     386            0 :    rv = tty::ttyWriteRead( strRead, "H\r", "\r\n>", false, m_fileDescrip, dev::ioDevice::m_writeTimeout, dev::ioDevice::m_readTimeout);
     387            0 :    if(rv != TTY_E_NOERROR)
     388              :    {
     389            0 :       return log<software_error,-1>({__FILE__, __LINE__,  0, rv, "Error reading RH: " + tty::ttyErrorString(rv)});
     390              :    }
     391              : 
     392            0 :    rv = RH::parseH(m_rh, strRead);
     393            0 :    if(rv != 0)
     394              :    {
     395            0 :       if( rv == -1 )
     396              :       {
     397            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing humid, no EOT"});
     398              :       }
     399            0 :       else if (rv == -2 )
     400              :       {
     401            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing humid, no value"});
     402              :       }
     403            0 :       else if (rv == -3)
     404              :       {
     405            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing temp, does not begin with digit"});
     406              :       }
     407              :       else
     408              :       {
     409            0 :          return log<software_error, -1>({__FILE__, __LINE__, "Error parsing humid."});
     410              :       }
     411              :    }
     412              : 
     413            0 :    std::cout << m_rh << "\n";
     414              : 
     415            0 :    return 0;
     416            0 : }
     417              : 
     418            0 : int rhusbMon::checkRecordTimes()
     419              : {
     420            0 :    return dev::telemeter<rhusbMon>::checkRecordTimes(telem_rhusb());
     421              : }
     422              : 
     423            0 : int rhusbMon::recordTelem( const telem_rhusb * )
     424              : {
     425            0 :    return recordRH(true);
     426              : }
     427              : 
     428              : inline
     429            0 : int rhusbMon::recordRH(bool force)
     430              : {
     431              :    static float lastTemp = -99;
     432              :    static float lastRH = -99;
     433              : 
     434            0 :    if(force || m_temp != lastTemp || m_rh != lastRH)
     435              :    {
     436            0 :       telem<telem_rhusb>({m_temp, m_rh});
     437              :    }
     438              : 
     439            0 :    lastTemp = m_temp;
     440            0 :    lastRH = m_rh;
     441              : 
     442            0 :    return 0;
     443              : 
     444              : }
     445              : 
     446              : } //namespace app
     447              : } //namespace MagAOX
     448              : 
     449              : #endif //rhusbMon_hpp
        

Generated by: LCOV version 2.0-1