LCOV - code coverage report
Current view: top level - apps/zaberLowLevel - zaberLowLevel.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 4.0 % 371 15
Test Date: 2026-01-03 21:03:39 Functions: 33.3 % 21 7

            Line data    Source code
       1              : /** \file zaberLowLevel.hpp
       2              :   * \brief The MagAO-X Low-Level Zaber Controller
       3              :   *
       4              :   * \ingroup zaberLowLevel_files
       5              :   */
       6              : 
       7              : #ifndef zaberLowLevel_hpp
       8              : #define zaberLowLevel_hpp
       9              : 
      10              : #include <iostream>
      11              : 
      12              : 
      13              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      14              : #include "../../magaox_git_version.h"
      15              : 
      16              : typedef MagAOX::app::MagAOXApp<true> MagAOXAppT; //This needs to be before zaberStage.hpp for logging to work.
      17              : 
      18              : #include "zaberUtils.hpp"
      19              : #include "zaberStage.hpp"
      20              : #include "za_serial.h"
      21              : 
      22              : #define ZC_CONNECTED (0)
      23              : #define ZC_ERROR (-1)
      24              : #define ZC_NOT_CONNECTED (10)
      25              : 
      26              : /** \defgroup zaberLowLevel low-level zaber controller
      27              :   * \brief The low-level interface to a set of chained Zaber stages
      28              :   *
      29              :   * <a href="../handbook/operating/software/apps/zaberLowLevel.html">Application Documentation</a>
      30              :   *
      31              :   * \ingroup apps
      32              :   *
      33              :   */
      34              : 
      35              : /** \defgroup zaberLowLevel_files zaber low-level files
      36              :   * \ingroup zaberLowLevel
      37              :   */
      38              : 
      39              : namespace MagAOX
      40              : {
      41              : namespace app
      42              : {
      43              : 
      44              : class zaberLowLevel : public MagAOXAppT, public tty::usbDevice
      45              : {
      46              : 
      47              :    //Give the test harness access.
      48              :    friend class zaberLowLevel_test;
      49              : 
      50              : 
      51              : protected:
      52              : 
      53              :    int m_numStages {0};
      54              : 
      55              :    z_port m_port {0};
      56              : 
      57              :    std::vector<zaberStage<zaberLowLevel>> m_stages;
      58              : 
      59              :    std::unordered_map<int, size_t> m_stageAddress;
      60              :    std::unordered_map<std::string, size_t> m_stageSerial;
      61              :    std::unordered_map<std::string, size_t> m_stageName;
      62              : 
      63              : public:
      64              :    /// Default c'tor.
      65              :    zaberLowLevel();
      66              : 
      67              :    /// D'tor, declared and defined for noexcept.
      68           21 :    ~zaberLowLevel() noexcept
      69           21 :    {}
      70              : 
      71              :    virtual void setupConfig();
      72              : 
      73              :    virtual void loadConfig();
      74              : 
      75              :    int connect();
      76              : 
      77              :    int loadStages( std:: string & serialRes );
      78              : 
      79              :    /// Startup functions
      80              :    /** Sets up the INDI vars.
      81              :      *
      82              :      */
      83              :    virtual int appStartup();
      84              : 
      85              :    /// Implementation of the FSM for zaberLowLevel.
      86              :    virtual int appLogic();
      87              : 
      88              :    /// Implementation of the on-power-off FSM logic
      89              :    virtual int onPowerOff();
      90              : 
      91              :    /// Implementation of the while-powered-off FSM
      92              :    virtual int whilePowerOff();
      93              : 
      94              :    /// Do any needed shutdown tasks.  Currently nothing in this app.
      95              :    virtual int appShutdown();
      96              : 
      97              : protected:
      98              :    ///Current state of the stage.
      99              :    pcf::IndiProperty m_indiP_curr_state;
     100              : 
     101              :    ///Maximum raw position of the stage.
     102              :    pcf::IndiProperty m_indiP_max_pos;
     103              : 
     104              :    ///Current raw position of the stage.
     105              :    pcf::IndiProperty m_indiP_curr_pos;
     106              : 
     107              :    ///Current temperature of the stage.
     108              :    pcf::IndiProperty m_indiP_temp;
     109              : 
     110              :    ///Current temperature of the stage.
     111              :    pcf::IndiProperty m_indiP_warn;
     112              : 
     113              :    ///Target raw position of the stage.
     114              :    pcf::IndiProperty m_indiP_tgt_pos;
     115              : 
     116              :    ///Target relative position of the stage.
     117              :    pcf::IndiProperty m_indiP_tgt_relpos;
     118              : 
     119              :    ///Command a stage to home.
     120              :    pcf::IndiProperty m_indiP_req_home;
     121              : 
     122              :    ///Command a stage to safely halt.
     123              :    pcf::IndiProperty m_indiP_req_halt;
     124              : 
     125              :    ///Command a stage to safely immediately halt.
     126              :    pcf::IndiProperty m_indiP_req_ehalt;
     127              : 
     128              : public:
     129            0 :    INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_tgt_pos);
     130            0 :    INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_tgt_relpos);
     131            0 :    INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_home);
     132            0 :    INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_halt);
     133            0 :    INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_ehalt);
     134              : 
     135              : };
     136              : 
     137           63 : zaberLowLevel::zaberLowLevel() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     138              : {
     139           21 :    m_powerMgtEnabled = true;
     140              : 
     141           21 :    return;
     142            0 : }
     143              : 
     144            0 : void zaberLowLevel::setupConfig()
     145              : {
     146            0 :    tty::usbDevice::setupConfig(config);
     147              : 
     148              : 
     149            0 : }
     150              : 
     151            0 : void zaberLowLevel::loadConfig()
     152              : {
     153              : 
     154            0 :    this->m_baudRate = B115200; //default for Zaber stages.  Will be overridden by any config setting.
     155              : 
     156            0 :    int rv = tty::usbDevice::loadConfig(config);
     157              : 
     158            0 :    if(rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND) //Ignore error if not plugged in
     159              :    {
     160            0 :       log<software_error>( {__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     161              :    }
     162              : 
     163            0 :    std::vector<std::string> sections;
     164              : 
     165            0 :    config.unusedSections(sections);
     166              : 
     167            0 :    if(sections.size() == 0)
     168              :    {
     169            0 :       log<software_error>({__FILE__, __LINE__, "No stages found"});
     170            0 :       return;
     171              :    }
     172              : 
     173            0 :    for(size_t n=0; n<sections.size(); ++n)
     174              :    {
     175            0 :       if(config.isSetUnused(mx::app::iniFile::makeKey(sections[n], "serial" )))
     176              :       {
     177            0 :          m_stages.push_back(zaberStage<zaberLowLevel>(this));
     178              : 
     179            0 :          size_t idx = m_stages.size()-1;
     180              : 
     181            0 :          m_stages[idx].name(sections[n]);
     182              : 
     183              : 
     184              :          //Get serial number from config.
     185            0 :          std::string tmp = m_stages[idx].serial(); //get default
     186            0 :          config.configUnused( tmp , mx::app::iniFile::makeKey(sections[n], "serial" ) );
     187            0 :          m_stages[idx].serial(tmp);
     188              : 
     189            0 :          m_stageName.insert( {m_stages[idx].name(), idx});
     190            0 :          m_stageSerial.insert( {m_stages[idx].serial(), idx});
     191            0 :       }
     192              :    }
     193            0 : }
     194              : 
     195            0 : int zaberLowLevel::connect()
     196              : {
     197            0 :    if(m_port > 0)
     198              :    {
     199            0 :       int rv = za_disconnect(m_port);
     200            0 :       if(rv < 0)
     201              :       {
     202            0 :          log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
     203              :       }
     204            0 :       m_port = 0;
     205              :    }
     206              : 
     207              : 
     208            0 :    if(m_port <= 0)
     209              :    {
     210              : 
     211              :       int zrv;
     212              : 
     213              :       {//scope for elPriv
     214            0 :          elevatedPrivileges elPriv(this);
     215            0 :          zrv = za_connect(&m_port, m_deviceName.c_str());
     216            0 :       }
     217              : 
     218            0 :       if(zrv != Z_SUCCESS)
     219              :       {
     220            0 :          if(m_port > 0)
     221              :          {
     222            0 :             za_disconnect(m_port);
     223            0 :             m_port = 0;
     224              :          }
     225              : 
     226            0 :          if(!stateLogged())
     227              :          {
     228            0 :             log<software_error>({__FILE__, __LINE__, "can not connect to zaber stage(s)"});
     229              :          }
     230              : 
     231            0 :          return ZC_NOT_CONNECTED; //We aren't connected.
     232              :       }
     233              :    }
     234              : 
     235            0 :    if(m_port <= 0)
     236              :    {
     237              :       //state(stateCodes::ERROR); //Should not get this here.  Probably means no device.
     238            0 :       log<text_log>("can not connect to zaber stage(s): no port", logPrio::LOG_WARNING);
     239            0 :       return ZC_NOT_CONNECTED; //We aren't connected.
     240              :    }
     241              : 
     242            0 :    log<text_log>("DRAINING", logPrio::LOG_DEBUG);
     243              : 
     244            0 :    int rv = za_drain(m_port);
     245              : 
     246            0 :    if(rv != Z_SUCCESS)
     247              :    {
     248            0 :       log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
     249            0 :       state(stateCodes::ERROR);
     250            0 :       return ZC_ERROR;
     251              :    }
     252              : 
     253              :    char buffer[256];
     254              : 
     255              :    //===== First renumber so they are unique.
     256            0 :    log<text_log>("Sending: / renumber", logPrio::LOG_DEBUG);
     257            0 :    std::string renum = "/ renumber";
     258            0 :    int nwr = za_send(m_port, renum.c_str(), renum.size());
     259              : 
     260            0 :    if(nwr == Z_ERROR_SYSTEM_ERROR)
     261              :    {
     262            0 :       log<text_log>("Error sending renumber query to stages", logPrio::LOG_ERROR);
     263            0 :       state(stateCodes::ERROR);
     264            0 :       return ZC_ERROR;
     265              :    }
     266              : 
     267              :    //===== Drain the result
     268            0 :    log<text_log>("DRAINING", logPrio::LOG_DEBUG);
     269              : 
     270            0 :    rv = za_drain(m_port);
     271              : 
     272            0 :    if(rv != Z_SUCCESS)
     273              :    {
     274            0 :       log<software_error>({__FILE__,__LINE__, rv, "error from za_drain"});
     275            0 :       state(stateCodes::ERROR);
     276            0 :       return ZC_ERROR;
     277              :    }
     278              : 
     279              :    //======= Now find the stages
     280            0 :    log<text_log>("Sending: / get system.serial", logPrio::LOG_DEBUG);
     281            0 :    std::string gss = "/ get system.serial";
     282            0 :    nwr = za_send(m_port, gss.c_str(), gss.size() );
     283              : 
     284            0 :    if(nwr == Z_ERROR_SYSTEM_ERROR)
     285              :    {
     286            0 :       log<text_log>("Error sending system.serial query to stages", logPrio::LOG_ERROR);
     287            0 :       state(stateCodes::ERROR);
     288            0 :       return ZC_ERROR;
     289              :    }
     290              : 
     291            0 :    std::string serialRes;
     292              :    while(1)
     293              :    {
     294            0 :       int nrd = za_receive(m_port, buffer, sizeof(buffer));
     295            0 :       if(nrd >= 0 )
     296              :       {
     297            0 :          buffer[nrd] = '\0';
     298            0 :          log<text_log>(std::string("Received: ")+buffer, logPrio::LOG_DEBUG);
     299            0 :          serialRes += buffer;
     300              :       }
     301            0 :       else if( nrd != Z_ERROR_TIMEOUT)
     302              :       {
     303            0 :          log<text_log>("Error receiving from stages", logPrio::LOG_ERROR);
     304            0 :          state(stateCodes::ERROR);
     305            0 :          return ZC_ERROR;
     306              :       }
     307              :       else
     308              :       {
     309            0 :          log<text_log>("TIMEOUT", logPrio::LOG_DEBUG);
     310            0 :          break; //Timeout ok.
     311              :       }
     312            0 :    }
     313              : 
     314            0 :    return loadStages( serialRes );
     315            0 : }
     316              : 
     317            0 : int zaberLowLevel::loadStages( std::string & serialRes )
     318              : {
     319            0 :    std::vector<int> addresses;
     320            0 :    std::vector<std::string> serials;
     321              : 
     322            0 :    int rv = parseSystemSerial( addresses, serials, serialRes );
     323            0 :    if( rv < 0)
     324              :    {
     325            0 :       log<software_error>({__FILE__, __LINE__, errno, rv, "error in parseSystemSerial"});
     326            0 :       state(stateCodes::ERROR);
     327            0 :       return ZC_ERROR;
     328              :    }
     329              :    else
     330              :    {
     331            0 :       log<text_log>("Found " + std::to_string(addresses.size()) + " stages.");
     332            0 :       m_stageAddress.clear(); //We clear this map before re-populating.
     333            0 :       for(size_t n=0;n<addresses.size(); ++n)
     334              :       {
     335            0 :          if( m_stageSerial.count( serials[n] ) == 1)
     336              :          {
     337            0 :             m_stages[m_stageSerial[serials[n]]].deviceAddress(addresses[n]);
     338              : 
     339            0 :             m_stageAddress.insert({ addresses[n], m_stageSerial[serials[n]]});
     340            0 :             log<text_log>("stage @" + std::to_string(addresses[n]) + " with s/n " + serials[n] + " corresponds to " + m_stages[m_stageSerial[serials[n]]].name());
     341              :          }
     342              :          else
     343              :          {
     344            0 :             log<text_log>("Unkown stage @" + std::to_string(addresses[n]) + " with s/n " + serials[n], logPrio::LOG_WARNING);
     345              :          }
     346              :       }
     347              : 
     348            0 :       for(size_t n=0; n < m_stages.size(); ++n)
     349              :       {
     350            0 :          if(m_stages[n].deviceAddress() < 1)
     351              :          {
     352            0 :             log<text_log>("stage " + m_stages[n].name() + " with with s/n " + serials[n] + " not found in system.", logPrio::LOG_ERROR);
     353            0 :             state(state(), true);
     354              :          }
     355              :       }
     356              :    }
     357              : 
     358            0 :    return ZC_CONNECTED;
     359            0 : }
     360              : 
     361              : 
     362            0 : int zaberLowLevel::appStartup()
     363              : {
     364            0 :    if( state() == stateCodes::UNINITIALIZED )
     365              :    {
     366            0 :       log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
     367            0 :       return -1;
     368              :    }
     369              : 
     370            0 :    if( m_stages.size() == 0 )
     371              :    {
     372            0 :       log<text_log>( "No stages configured.", logPrio::LOG_CRITICAL);
     373            0 :       return -1;
     374              :    }
     375              : 
     376            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_curr_state, "curr_state", pcf::IndiProperty::Text);
     377            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     378              :    {
     379            0 :       m_indiP_curr_state.add (pcf::IndiElement(m_stages[n].name()));
     380              :    }
     381              : 
     382            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_max_pos, "max_pos", pcf::IndiProperty::Text);
     383            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     384              :    {
     385            0 :       m_indiP_max_pos.add (pcf::IndiElement(m_stages[n].name()));
     386            0 :       m_indiP_max_pos[m_stages[n].name()] = -1;
     387              :    }
     388              : 
     389            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_curr_pos, "curr_pos", pcf::IndiProperty::Number);
     390            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     391              :    {
     392            0 :       m_indiP_curr_pos.add (pcf::IndiElement(m_stages[n].name()));
     393              :    }
     394              : 
     395            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_temp, "temp", pcf::IndiProperty::Number);
     396            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     397              :    {
     398            0 :       m_indiP_temp.add (pcf::IndiElement(m_stages[n].name()));
     399              :    }
     400              : 
     401            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_warn, "warning", pcf::IndiProperty::Switch);
     402            0 :    m_indiP_warn.setRule(pcf::IndiProperty::AnyOfMany);
     403            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     404              :    {
     405            0 :       m_indiP_warn.add (pcf::IndiElement(m_stages[n].name()));
     406            0 :       m_indiP_warn[m_stages[n].name()].setSwitchState(pcf::IndiElement::Off);
     407              :    }
     408              : 
     409            0 :    REG_INDI_NEWPROP(m_indiP_tgt_pos, "tgt_pos", pcf::IndiProperty::Number);
     410            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     411              :    {
     412            0 :       m_indiP_tgt_pos.add (pcf::IndiElement(m_stages[n].name()));
     413              :    }
     414              : 
     415              :    /*--> Kill this */
     416            0 :    REG_INDI_NEWPROP(m_indiP_tgt_relpos, "tgt_relpos", pcf::IndiProperty::Number);
     417            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     418              :    {
     419            0 :       m_indiP_tgt_relpos.add (pcf::IndiElement(m_stages[n].name()));
     420              :    }
     421              : 
     422              :    /*--> Make a switch */
     423            0 :    REG_INDI_NEWPROP(m_indiP_req_home, "req_home", pcf::IndiProperty::Number);
     424            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     425              :    {
     426            0 :       m_indiP_req_home.add (pcf::IndiElement(m_stages[n].name()));
     427              :    }
     428              : 
     429              :    /*--> Make a switch */
     430            0 :    REG_INDI_NEWPROP(m_indiP_req_halt, "req_halt", pcf::IndiProperty::Number);
     431            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     432              :    {
     433            0 :       m_indiP_req_halt.add (pcf::IndiElement(m_stages[n].name()));
     434              :    }
     435              : 
     436              :    /*--> Make a switch */
     437            0 :    REG_INDI_NEWPROP(m_indiP_req_ehalt, "req_ehalt", pcf::IndiProperty::Number);
     438            0 :    for(size_t n=0; n< m_stages.size(); ++n)
     439              :    {
     440            0 :       m_indiP_req_ehalt.add (pcf::IndiElement(m_stages[n].name()));
     441              :    }
     442              : 
     443            0 :    return 0;
     444              : }
     445              : 
     446            0 : int zaberLowLevel::appLogic()
     447              : {
     448            0 :    if( state() == stateCodes::INITIALIZED )
     449              :    {
     450            0 :       log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
     451            0 :       return -1;
     452              :    }
     453              : 
     454            0 :    if( state() == stateCodes::POWERON)
     455              :    {
     456            0 :       state(stateCodes::NODEVICE);
     457            0 :       for(size_t i=0; i < m_stages.size();++i)
     458              :       {
     459            0 :          updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
     460              :       }
     461              :    }
     462              : 
     463            0 :    if( state() == stateCodes::NODEVICE )
     464              :    {
     465            0 :       int rv = tty::usbDevice::getDeviceName();
     466            0 :       if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
     467              :       {
     468            0 :          if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
     469              : 
     470            0 :          state(stateCodes::FAILURE);
     471            0 :          if(!stateLogged())
     472              :          {
     473            0 :             log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     474              :          }
     475            0 :          return -1;
     476              :       }
     477              : 
     478            0 :       if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
     479              :       {
     480            0 :          state(stateCodes::NODEVICE);
     481              : 
     482            0 :          if(!stateLogged())
     483              :          {
     484            0 :             std::stringstream logs;
     485            0 :             logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
     486            0 :             log<text_log>(logs.str());
     487            0 :          }
     488            0 :          return 0;
     489              :       }
     490              :       else
     491              :       {
     492            0 :          std::stringstream logs;
     493            0 :          logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " found in udev as " << m_deviceName;
     494            0 :          log<text_log>(logs.str());
     495              : 
     496            0 :          state(stateCodes::NOTCONNECTED);
     497              : 
     498            0 :          for(size_t i=0; i < m_stages.size();++i)
     499              :          {
     500            0 :             if(m_stages[i].deviceAddress() < 1) continue;
     501            0 :             updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTCONNECTED"));
     502              :          }
     503              : 
     504            0 :          return 0; //we return to give the stage time to initialize the connection if this is a USB-FTDI power on/plug-in event.
     505            0 :       }
     506              : 
     507              :    }
     508              : 
     509            0 :    if( state() == stateCodes::NOTCONNECTED )
     510              :    {
     511            0 :       std::lock_guard<std::mutex> guard(m_indiMutex);
     512              : 
     513            0 :       int rv = connect();
     514              : 
     515            0 :       if( rv == ZC_CONNECTED)
     516              :       {
     517            0 :          state(stateCodes::CONNECTED);
     518            0 :          for(size_t i=0; i < m_stages.size();++i)
     519              :          {
     520            0 :             if(m_stages[i].deviceAddress() < 1) continue;
     521            0 :             updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("CONNECTED"));
     522              :          }
     523              : 
     524            0 :          if(!stateLogged())
     525              :          {
     526            0 :             log<text_log>("Connected to stage(s) on " + m_deviceName);
     527              :          }
     528              :       }
     529            0 :       else if(rv == ZC_NOT_CONNECTED)
     530              :       {
     531            0 :          return 0;
     532              :       }
     533              :       else
     534              :       {
     535              : 
     536              :       }
     537            0 :    }
     538              : 
     539            0 :    if( state() == stateCodes::CONNECTED )
     540              :    {
     541            0 :       for(size_t i=0; i < m_stages.size();++i)
     542              :       {
     543            0 :          if(m_stages[i].deviceAddress() < 1)
     544              :          {
     545            0 :             updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
     546            0 :             continue; //Skip configured but not found stage
     547              :          }
     548            0 :          std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
     549              : 
     550            0 :          m_stages[i].getMaxPos(m_port);
     551            0 :          std::cerr << i << " " << m_stages[i].name() << " " <<  m_stages[i].maxPos() << "\n";
     552            0 :          updateIfChanged(m_indiP_max_pos, m_stages[i].name(), m_stages[i].maxPos());
     553              : 
     554              :          //Get warnings so first pass through has correct state for home/not-homed
     555            0 :          if(m_stages[i].getWarnings(m_port) < 0)
     556              :          {
     557            0 :             if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
     558            0 :             log<software_error>({__FILE__, __LINE__});
     559            0 :             state(stateCodes::ERROR);
     560            0 :             return 0;
     561              :          }
     562              : 
     563            0 :       }
     564            0 :       state(stateCodes::READY);
     565              : 
     566            0 :       return 0;
     567              :    }
     568              : 
     569            0 :    if( state() == stateCodes::READY )
     570              :    {
     571              : 
     572              :       //Here we check complete stage state.
     573            0 :       for(size_t i=0; i < m_stages.size();++i)
     574              :       {
     575            0 :          if(m_stages[i].deviceAddress() < 1) continue; //Skip configured but not found stage
     576              : 
     577            0 :          std::lock_guard<std::mutex> guard(m_indiMutex); //Inside loop so INDI requests can steal it
     578              : 
     579            0 :          m_stages[i].updatePos(m_port);
     580              : 
     581            0 :          updateIfChanged(m_indiP_curr_pos, m_stages[i].name(), m_stages[i].rawPos());
     582              : 
     583            0 :          if(m_stages[i].rawPos() == m_stages[i].tgtPos())
     584              :          {
     585            0 :             updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
     586              :          }
     587              :          else
     588              :          {
     589            0 :             updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), m_stages[i].tgtPos());
     590              :          }
     591              : 
     592              : 
     593            0 :          if(m_stages[i].deviceStatus() == 'B')
     594              :          {
     595            0 :             if(m_stages[i].homing())
     596              :             {
     597            0 :                updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("HOMING"));
     598              :             }
     599              :             else
     600              :             {
     601            0 :                updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("OPERATING"));
     602              :             }
     603              :          }
     604            0 :          else if(m_stages[i].deviceStatus() == 'I')
     605              :          {
     606            0 :             if(m_stages[i].homing())
     607              :             {
     608            0 :                std::cerr << __FILE__ << " " << __LINE__ << "\n";
     609            0 :                return 0;
     610              :             }
     611            0 :             if(m_stages[i].warnWR())
     612              :             {
     613            0 :                updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NOTHOMED"));
     614              :             }
     615              :             else
     616              :             {
     617            0 :                updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("READY"));
     618              :             }
     619              :          }
     620            0 :          else updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
     621              : 
     622            0 :          if(m_stages[i].warn())
     623              :          {
     624            0 :             updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::On);
     625              :          }
     626              :          else
     627              :          {
     628            0 :             updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::Off);
     629              :          }
     630              : 
     631            0 :          m_stages[i].updateTemp(m_port);
     632            0 :          updateIfChanged(m_indiP_temp, m_stages[i].name(), m_stages[i].temp());
     633              : 
     634            0 :          if(m_stages[i].getWarnings(m_port) < 0)
     635              :          {
     636            0 :             if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
     637            0 :             log<software_error>({__FILE__, __LINE__});
     638            0 :             state(stateCodes::ERROR);
     639            0 :             return 0;
     640              :          }
     641              : 
     642            0 :       }
     643              :    }
     644              : 
     645            0 :    if( state() == stateCodes::ERROR )
     646              :    {
     647            0 :       int rv = tty::usbDevice::getDeviceName();
     648            0 :       if(rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
     649              :       {
     650            0 :          if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
     651            0 :          state(stateCodes::FAILURE);
     652            0 :          for(size_t i=0; i < m_stages.size();++i)
     653              :          {
     654            0 :             updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
     655              :          }
     656            0 :          if(!stateLogged())
     657              :          {
     658            0 :             log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     659              :          }
     660            0 :          return rv;
     661              :       }
     662              : 
     663            0 :       if(rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
     664              :       {
     665            0 :          if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
     666            0 :          state(stateCodes::NODEVICE);
     667            0 :          for(size_t i=0; i < m_stages.size();++i)
     668              :          {
     669            0 :             updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
     670              :          }
     671              : 
     672            0 :          if(!stateLogged())
     673              :          {
     674            0 :             std::stringstream logs;
     675            0 :             logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " not found in udev";
     676            0 :             log<text_log>(logs.str());
     677            0 :          }
     678            0 :          return 0;
     679              :       }
     680              : 
     681            0 :       if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
     682            0 :       state(stateCodes::FAILURE);
     683            0 :       for(size_t i=0; i < m_stages.size();++i)
     684              :       {
     685            0 :          updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("FAILURE"));
     686              :       }
     687              : 
     688            0 :       log<software_critical>({__FILE__, __LINE__});
     689            0 :       log<text_log>("Error NOT due to loss of USB connection.  I can't fix it myself.", logPrio::LOG_CRITICAL);
     690              :    }
     691              : 
     692              : 
     693              : 
     694            0 :    if( powerState() != 1 || powerStateTarget() != 1 ) return 0; //means we're powering off
     695              : 
     696            0 :    if( state() == stateCodes::FAILURE )
     697              :    {
     698            0 :       return -1;
     699              :    }
     700              : 
     701            0 :    return 0;
     702              : }
     703              : 
     704              : inline
     705            0 : int zaberLowLevel::onPowerOff()
     706              : {
     707            0 :    int rv = za_disconnect(m_port);
     708            0 :    if(rv < 0)
     709              :    {
     710            0 :       log<text_log>("Error disconnecting from zaber system.", logPrio::LOG_ERROR);
     711              :    }
     712              : 
     713            0 :    m_port = 0;
     714              : 
     715            0 :    std::lock_guard<std::mutex> lock(m_indiMutex);
     716              : 
     717            0 :    for(size_t i=0; i < m_stages.size();++i)
     718              :    {
     719            0 :       updateIfChanged(m_indiP_tgt_pos, m_stages[i].name(), std::string(""));
     720            0 :       updateIfChanged(m_indiP_tgt_relpos, m_stages[i].name(), std::string(""));
     721            0 :       updateIfChanged(m_indiP_temp, m_stages[i].name(), std::string(""));
     722              : 
     723            0 :       m_stages[i].onPowerOff();
     724              : 
     725            0 :       updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("POWEROFF"));
     726              : 
     727            0 :       updateIfChanged(m_indiP_warn, m_stages[i].name(), pcf::IndiElement::Off);
     728              :    }
     729            0 :    return 0;
     730            0 : }
     731              : 
     732              : inline
     733            0 : int zaberLowLevel::whilePowerOff()
     734              : {
     735            0 :    return 0;
     736              : }
     737              : 
     738              : inline
     739            0 : int zaberLowLevel::appShutdown()
     740              : {
     741            0 :    for(size_t i=0; i < m_stages.size();++i)
     742              :    {
     743            0 :       if(m_stages[i].deviceAddress() < 1) continue;
     744            0 :       updateIfChanged(m_indiP_curr_state, m_stages[i].name(), std::string("NODEVICE"));
     745              :    }
     746              : 
     747            0 :    return 0;
     748              : }
     749              : 
     750            3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_pos)(const pcf::IndiProperty &ipRecv)
     751              : {
     752            3 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_pos, ipRecv);
     753              : 
     754            0 :     for(size_t n=0; n < m_stages.size(); ++n)
     755              :     {
     756            0 :        if( ipRecv.find(m_stages[n].name()) )
     757              :        {
     758            0 :           long tgt = ipRecv[m_stages[n].name()].get<long>();
     759            0 :           if(tgt >= 0)
     760              :           {
     761            0 :              if(m_stages[n].deviceAddress() < 1)
     762              :              {
     763            0 :                 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
     764              :              }
     765              : 
     766            0 :              std::lock_guard<std::mutex> guard(m_indiMutex);
     767            0 :              updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
     768            0 :              return m_stages[n].moveAbs(m_port, tgt);
     769            0 :           }
     770              :        }
     771              :     }
     772              : 
     773            0 :    return 0;
     774              : }
     775              : 
     776            3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_tgt_relpos)(const pcf::IndiProperty &ipRecv)
     777              : {
     778            3 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tgt_relpos, ipRecv);
     779              : 
     780            0 :     for(size_t n=0; n < m_stages.size(); ++n)
     781              :     {
     782            0 :        if( ipRecv.find(m_stages[n].name()) )
     783              :        {
     784            0 :           long tgt = ipRecv[m_stages[n].name()].get<long>();
     785            0 :           tgt += m_stages[n].rawPos();
     786            0 :           if(tgt >= 0)
     787              :           {
     788            0 :              if(m_stages[n].deviceAddress() < 1)
     789              :              {
     790            0 :                 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
     791              :              }
     792            0 :              std::lock_guard<std::mutex> guard(m_indiMutex);
     793              : 
     794            0 :              updateIfChanged(m_indiP_curr_state, m_stages[n].name(), std::string("OPERATING"));
     795            0 :              return m_stages[n].moveAbs(m_port, tgt);
     796            0 :           }
     797              :        }
     798              :     }
     799              : 
     800            0 :    return 0;
     801              : }
     802              : 
     803            3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_home)(const pcf::IndiProperty &ipRecv)
     804              : {
     805            3 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_home, ipRecv);
     806              : 
     807            0 :     for(size_t n=0; n < m_stages.size(); ++n)
     808              :     {
     809            0 :        if( ipRecv.find(m_stages[n].name()) )
     810              :        {
     811            0 :           int tgt = ipRecv[m_stages[n].name()].get<int>();
     812            0 :           if(tgt > 0)
     813              :           {
     814            0 :              if(m_stages[n].deviceAddress() < 1)
     815              :              {
     816            0 :                 return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
     817              :              }
     818            0 :              std::lock_guard<std::mutex> guard(m_indiMutex);
     819            0 :              return m_stages[n].home(m_port);
     820            0 :           }
     821              :        }
     822              :     }
     823              : 
     824            0 :    return 0;
     825              : }
     826              : 
     827            3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_halt)(const pcf::IndiProperty &ipRecv)
     828              : {
     829            3 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_halt, ipRecv);
     830              : 
     831              : 
     832            0 :     for(size_t n=0; n < m_stages.size(); ++n)
     833              :     {
     834            0 :         if( ipRecv.find(m_stages[n].name()) )
     835              :         {
     836            0 :            int tgt = ipRecv[m_stages[n].name()].get<int>();
     837            0 :            if(tgt > 0)
     838              :            {
     839            0 :               if(m_stages[n].deviceAddress() < 1)
     840              :               {
     841            0 :                  return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
     842              :               }
     843              : 
     844            0 :               std::lock_guard<std::mutex> guard(m_indiMutex);
     845            0 :               return m_stages[n].stop(m_port);
     846            0 :            }
     847              :         }
     848              :     }
     849              : 
     850            0 :    return 0;
     851              : }
     852              : 
     853            3 : INDI_NEWCALLBACK_DEFN(zaberLowLevel, m_indiP_req_ehalt)(const pcf::IndiProperty &ipRecv)
     854              : {
     855            3 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_req_ehalt, ipRecv);
     856              : 
     857            0 :     for(size_t n=0; n < m_stages.size(); ++n)
     858              :     {
     859            0 :         if( ipRecv.find(m_stages[n].name()) )
     860              :         {
     861            0 :             int tgt = ipRecv[m_stages[n].name()].get<int>();
     862            0 :             if(tgt > 0)
     863              :             {
     864            0 :                 if(m_stages[n].deviceAddress() < 1)
     865              :                 {
     866            0 :                     return log<software_error,-1>({__FILE__, __LINE__, "stage " + m_stages[n].name() + " with with s/n " + m_stages[n].serial() + " not found in system."});
     867              :                 }
     868              : 
     869            0 :                 std::lock_guard<std::mutex> guard(m_indiMutex);
     870            0 :                 return m_stages[n].estop(m_port);
     871            0 :             }
     872              :         }
     873              :     }
     874              : 
     875            0 :     return 0;
     876              : }
     877              : 
     878              : } //namespace app
     879              : } //namespace MagAOX
     880              : 
     881              : #endif //zaberLowLevel_hpp
        

Generated by: LCOV version 2.0-1