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

            Line data    Source code
       1              : /** \file pi335Ctrl.hpp
       2              :  * \brief The MagAO-X XXXXXX header file
       3              :  *
       4              :  * \ingroup pi335Ctrl_files
       5              :  */
       6              : 
       7              : #ifndef pi335Ctrl_hpp
       8              : #define pi335Ctrl_hpp
       9              : 
      10              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      11              : #include "../../magaox_git_version.h"
      12              : 
      13              : /** \defgroup pi335Ctrl
      14              :  * \brief The MagAO-X application to interface with a PI-335 Controller
      15              :  *
      16              :  * <a href="..//apps_html/page_module_pi335Ctrl.html">Application Documentation</a>
      17              :  *
      18              :  * \ingroup apps
      19              :  *
      20              :  */
      21              : 
      22              : /** \defgroup pi335Ctrl_files
      23              :  * \ingroup pi335Ctrl
      24              :  */
      25              : 
      26              : namespace MagAOX
      27              : {
      28              : namespace app
      29              : {
      30              : 
      31              : /// The MagAO-X PI 335 Controller Interface
      32              : /**
      33              :  * \ingroup pi335Ctrl
      34              :  */
      35              : class pi335Ctrl : public MagAOXApp<true>, public tty::usbDevice, public dev::ioDevice, public dev::dm<pi335Ctrl, float>, public dev::shmimMonitor<pi335Ctrl>, public dev::telemeter<pi335Ctrl>
      36              : {
      37              : 
      38              :     // Give the test harness access.
      39              :     friend class pi335Ctrl_test;
      40              : 
      41              :     friend class dev::dm<pi335Ctrl, float>;
      42              : 
      43              :     typedef dev::dm<pi335Ctrl, float> dmT;
      44              : 
      45              :     friend class dev::shmimMonitor<pi335Ctrl>;
      46              : 
      47              :     typedef dev::shmimMonitor<pi335Ctrl> shmimMonitorT;
      48              : 
      49              :     friend class dev::telemeter<pi335Ctrl>;
      50              : 
      51              :     typedef dev::telemeter<pi335Ctrl> telemeterT;
      52              : 
      53              : protected:
      54              :     /** \name Configurable Parameters
      55              :      *@{
      56              :      */
      57              : 
      58              :     float m_posTol{0.05}; ///< The tolerance for reporting a raw position rather than the setpoint.
      59              : 
      60              :     float m_homePos1{17.5}; ///< Home position of axis 1.  Default is 17.5
      61              :     float m_homePos2{17.5}; ///< Home position of axis 2.  Default is 17.5
      62              :     float m_homePos3{0.0};  ///< Home position of axis 2.  Default is 17.5
      63              : 
      64              :     ///@}
      65              : 
      66              : private:
      67              :     std::string m_ctrl;  ///< The controller connected.
      68              :     std::string m_stage; ///< The stage connected.
      69              :     int m_naxes{2};      ///< The number of axes, default is 2.  Max is 3.
      70              : 
      71              :     bool m_pos_3_sent{false};
      72              : 
      73              :     bool m_actuallyATZ{true};
      74              : 
      75              : protected:
      76              :     float m_min1{0};  ///< The minimum value for axis 1
      77              :     float m_max1{35}; ///< The maximum value for axis 1
      78              : 
      79              :     float m_min2{0};  ///< The minimum value for axis 2
      80              :     float m_max2{35}; ///< The maximum value for axis 2
      81              : 
      82              :     float m_min3{0}; ///< The minimum value for axis 3
      83              :     float m_max3{0}; ///< The maximum value for axis 3
      84              : 
      85              :     double m_homingStart{0};
      86              :     int m_homingState{0};
      87              : 
      88              :     int m_servoState{0};
      89              : 
      90              :     float m_pos1Set{0};
      91              :     float m_pos1{0};
      92              : 
      93              :     float m_pos2Set{0};
      94              :     float m_pos2{0};
      95              : 
      96              :     float m_pos3Set{0};
      97              :     float m_pos3{0};
      98              : 
      99              :     float m_sva1{0};
     100              :     float m_sva2{0};
     101              :     float m_sva3{0};
     102              : 
     103              : public:
     104              :     /// Default c'tor.
     105              :     pi335Ctrl();
     106              : 
     107              :     /// D'tor, declared and defined for noexcept.
     108            0 :     ~pi335Ctrl() noexcept
     109            0 :     {
     110            0 :     }
     111              : 
     112              :     virtual void setupConfig();
     113              : 
     114              :     /// Implementation of loadConfig logic, separated for testing.
     115              :     /** This is called by loadConfig().
     116              :      */
     117              :     int loadConfigImpl(mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/);
     118              : 
     119              :     virtual void loadConfig();
     120              : 
     121              :     /// Startup function
     122              :     /**
     123              :      *
     124              :      */
     125              :     virtual int appStartup();
     126              : 
     127              :     /// Implementation of the FSM for pi335Ctrl.
     128              :     /**
     129              :      * \returns 0 on no critical error
     130              :      * \returns -1 on an error requiring shutdown
     131              :      */
     132              :     virtual int appLogic();
     133              : 
     134              :     /// Shutdown the app.
     135              :     /**
     136              :      *
     137              :      */
     138              :     virtual int appShutdown();
     139              : 
     140              :     /// Test the connection to the device
     141              :     /** Uses the *IDN? query
     142              :      *
     143              :      * \returns 0 if the E227 is found
     144              :      * \returns -1 on an error
     145              :      */
     146              :     int testConnection();
     147              : 
     148              :     int initDM();
     149              : 
     150              :     /// Start the homing procedure
     151              :     /** Checks that servos are off (the manual says this doesn't matter),
     152              :      * and then begins homing by calling 'home_1()'.
     153              :      * Sets FSM state to homing.
     154              :      *
     155              :      * \returns 0 on success (from 'home_1()')
     156              :      * \returns -1 on error
     157              :      */
     158              :     int home();
     159              : 
     160              :     /// Get the status of homing on an axiz
     161              :     /** Homing is also autozero, ATZ.  This uses the ATZ? query to
     162              :      * check if the autozero procedure has completed on the given axis.
     163              :      *
     164              :      * \returns 0 if ATZ not complete
     165              :      * \returns 1 if ATZ complete
     166              :      * \returns -1 on an error
     167              :      */
     168              :     int homeState(int axis /**< [in] the axis to check, 0 or 1 */);
     169              : 
     170              :     /// Begin homing (ATZ) axis 1
     171              :     /** Repeats check that servos are off (the manual says this doesn't matter)
     172              :      * and checks that 'm_homingStatte' is 0.  Then sends 'ATZ 1 NaN'.
     173              :      *
     174              :      * \returns 0 on success
     175              :      * \returns -1 on error
     176              :      */
     177              :     int home_1();
     178              : 
     179              :     /// Begin homing (ATZ) axis 2
     180              :     /** Repeats check that servos are off (the manual says this doesn't matter)
     181              :      * and checks that 'm_homingStatte' is 1.  Then sends 'ATZ 2 NaN'.
     182              :      *
     183              :      * \returns 0 on success
     184              :      * \returns -1 on error
     185              :      */
     186              :     int home_2();
     187              : 
     188              :     /// Begin homing (ATZ) axis 3
     189              :     /** Repeats check that servos are off (the manual says this doesn't matter)
     190              :      * and checks that 'm_homingStatte' is 1.  Then sends 'ATZ 3 NaN'.
     191              :      *
     192              :      * \returns 0 on success
     193              :      * \returns -1 on error
     194              :      */
     195              :     int home_3();
     196              : 
     197              :     int finishInit();
     198              : 
     199              :     /// Zero all commands on the DM
     200              :     /** This does not update the shared memory buffer.
     201              :      *
     202              :      * \returns 0 on success
     203              :      * \returns -1 on error
     204              :      */
     205              :     int zeroDM();
     206              : 
     207              :     /// Send a command to the DM
     208              :     /** This is called by the shmim monitoring thread in response to a semaphore trigger.
     209              :      *
     210              :      * \returns 0 on success
     211              :      * \returns -1 on error
     212              :      */
     213              :     int commandDM(void *curr_src);
     214              : 
     215              :     /// Release the DM, making it safe to turn off power.
     216              :     /** The application will be state READY at the conclusion of this.
     217              :      *
     218              :      * \returns 0 on success
     219              :      * \returns -1 on error
     220              :      */
     221              :     int releaseDM();
     222              : 
     223              :     int setCom(const std::string &com);
     224              : 
     225              :     int setCom(const std::string &com,
     226              :                int axis);
     227              : 
     228              :     int setCom(const std::string &com,
     229              :                int axis,
     230              :                const std::string &arg);
     231              : 
     232              :     int getCom(std::string &resp,
     233              :                const std::string &com,
     234              :                int axis);
     235              : 
     236              :     int getPos(float &pos,
     237              :                int n);
     238              : 
     239              :     /// Get the open loop control value
     240              :     int getSva(float &sva,
     241              :                int n);
     242              : 
     243              :     /// Update the flat command and propagate
     244              :     /** Writes the new desired position to the flat shmim, which is then propagated via the dmcomb
     245              :      */
     246              :     int updateFlat(float absPos1, ///< [in] The new position of axis 1
     247              :                    float absPos2, ///< [in] The new position of axis 2
     248              :                    float absPos3  ///< [in] The new position of axis 3
     249              :     );
     250              : 
     251              :     /// Send command to device to move axis 1
     252              :     int move_1(float absPos);
     253              : 
     254              :     /// Send command to device to move axis 2
     255              :     int move_2(float absPos);
     256              : 
     257              :     /// Send command to device to move axis 3
     258              :     int move_3(float absPos);
     259              : 
     260              : protected:
     261              :     // declare our properties
     262              :     pcf::IndiProperty m_indiP_pos1;
     263              :     pcf::IndiProperty m_indiP_pos2;
     264              :     pcf::IndiProperty m_indiP_pos3;
     265              : 
     266              : public:
     267            0 :     INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos1);
     268            0 :     INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos2);
     269            0 :     INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos3);
     270              : 
     271              :     /** \name Telemeter Interface
     272              :      * @{
     273              :      */
     274              : 
     275              :     int checkRecordTimes();
     276              : 
     277              :     int recordTelem(const telem_pi335 *);
     278              : 
     279              :     int recordPI335(bool force = false);
     280              : 
     281              :     ///@}
     282              : };
     283              : 
     284            0 : pi335Ctrl::pi335Ctrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     285              : {
     286            0 :     m_powerMgtEnabled = true;
     287              : 
     288            0 :     return;
     289            0 : }
     290              : 
     291            0 : void pi335Ctrl::setupConfig()
     292              : {
     293            0 :     dev::ioDevice::setupConfig(config);
     294            0 :     tty::usbDevice::setupConfig(config);
     295              : 
     296            0 :     DM_SETUP_CONFIG(config);
     297              : 
     298            0 :     TELEMETER_SETUP_CONFIG(config);
     299              : 
     300            0 :     config.add("stage.naxes", "", "stage.naxes", argType::Required, "stage", "naxes", false, "int", "Number of axes.  Default is 2.  Max is 3.");
     301              : 
     302            0 :     config.add("stage.homePos1", "", "stage.homePos1", argType::Required, "stage", "homePos1", false, "float", "Home position of axis 1.  Default is 17.5.");
     303            0 :     config.add("stage.homePos2", "", "stage.homePos2", argType::Required, "stage", "homePos2", false, "float", "Home position of axis 2.  Default is 17.5.");
     304            0 :     config.add("stage.homePos3", "", "stage.homePos3", argType::Required, "stage", "homePos3", false, "float", "Home position of axis 3.  Default is 17.5.");
     305              : 
     306            0 :     config.add("dm.calibRelDir", "", "dm.calibRelDir", argType::Required, "dm", "calibRelDir", false, "string", "Used to find the default calib directory.");
     307              : }
     308              : 
     309            0 : int pi335Ctrl::loadConfigImpl(mx::app::appConfigurator &_config)
     310              : {
     311              : 
     312            0 :     this->m_baudRate = B115200; // default for E727 controller.  Will be overridden by any config setting.
     313              : 
     314            0 :     int rv = tty::usbDevice::loadConfig(_config);
     315              : 
     316            0 :     if (rv != 0 && rv != TTY_E_NODEVNAMES && rv != TTY_E_DEVNOTFOUND)
     317              :     {
     318            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     319              :     }
     320              : 
     321            0 :     dev::ioDevice::loadConfig(_config);
     322              : 
     323            0 :     m_calibRelDir = "ttmpupil";
     324            0 :     config(m_calibRelDir, "dm.calibRelDir");
     325              : 
     326            0 :     DM_LOAD_CONFIG(_config);
     327              : 
     328            0 :     config(m_naxes, "stage.naxes");
     329            0 :     config(m_homePos1, "stage.homePos1");
     330            0 :     config(m_homePos2, "stage.homePos2");
     331            0 :     config(m_homePos3, "stage.homePos3");
     332              : 
     333            0 :     TELEMETER_LOAD_CONFIG(_config);
     334              : 
     335            0 :     return 0;
     336              : }
     337              : 
     338            0 : void pi335Ctrl::loadConfig()
     339              : {
     340            0 :     loadConfigImpl(config);
     341            0 : }
     342              : 
     343            0 : int pi335Ctrl::appStartup()
     344              : {
     345            0 :     if (state() == stateCodes::UNINITIALIZED)
     346              :     {
     347            0 :         log<text_log>("In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL);
     348            0 :         return -1;
     349              :     }
     350              : 
     351              :     ///\todo promote usbDevice to dev:: and make this part of its appStartup
     352              :     // Get the USB device if it's in udev
     353            0 :     if (m_deviceName != "")
     354              :     {
     355            0 :         log<text_log>(std::string("USB Device ") + m_idVendor + ":" + m_idProduct + ":" + m_serial + " found in udev as " + m_deviceName);
     356              :     }
     357              : 
     358            0 :     DM_APP_STARTUP;
     359            0 :     SHMIMMONITOR_APP_STARTUP;
     360              : 
     361              :     // set up the  INDI properties
     362            0 :     REG_INDI_NEWPROP(m_indiP_pos1, "pos_1", pcf::IndiProperty::Number);
     363            0 :     m_indiP_pos1.add(pcf::IndiElement("current"));
     364            0 :     m_indiP_pos1.add(pcf::IndiElement("target"));
     365            0 :     m_indiP_pos1["current"] = -99999;
     366            0 :     m_indiP_pos1["target"] = -99999;
     367              : 
     368            0 :     REG_INDI_NEWPROP(m_indiP_pos2, "pos_2", pcf::IndiProperty::Number);
     369            0 :     m_indiP_pos2.add(pcf::IndiElement("current"));
     370            0 :     m_indiP_pos2.add(pcf::IndiElement("target"));
     371            0 :     m_indiP_pos2["current"] = -99999;
     372            0 :     m_indiP_pos2["target"] = -99999;
     373              : 
     374              :     // Note: 3rd axis added in testConnection if it's found
     375              : 
     376            0 :     TELEMETER_APP_STARTUP;
     377              : 
     378            0 :     return 0;
     379              : }
     380              : 
     381            0 : int pi335Ctrl::appLogic()
     382              : {
     383            0 :     DM_APP_LOGIC;
     384            0 :     SHMIMMONITOR_APP_LOGIC;
     385              : 
     386            0 :     TELEMETER_APP_LOGIC;
     387              : 
     388            0 :     if (state() == stateCodes::POWERON)
     389              :     {
     390            0 :         if (!powerOnWaitElapsed())
     391              :         {
     392            0 :             return 0;
     393              :         }
     394              :         else
     395              :         {
     396              :             ///\todo promote usbDevice to dev:: and make this part of its appStartup
     397              :             // Get the USB device if it's in udev
     398            0 :             if (m_deviceName == "")
     399              :             {
     400            0 :                 state(stateCodes::NODEVICE);
     401              :             }
     402              :             else
     403              :             {
     404            0 :                 state(stateCodes::NOTCONNECTED);
     405              :             }
     406              :         }
     407              :     }
     408              : 
     409              :     ///\todo promote usbDevice to dev:: and make this part of its appLogic
     410            0 :     if (state() == stateCodes::NODEVICE)
     411              :     {
     412            0 :         int rv = tty::usbDevice::getDeviceName();
     413            0 :         if (rv < 0 && rv != TTY_E_DEVNOTFOUND && rv != TTY_E_NODEVNAMES)
     414              :         {
     415            0 :             state(stateCodes::FAILURE);
     416            0 :             if (!stateLogged())
     417              :             {
     418            0 :                 log<software_critical>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     419              :             }
     420            0 :             return -1;
     421              :         }
     422              : 
     423            0 :         if (rv == TTY_E_DEVNOTFOUND || rv == TTY_E_NODEVNAMES)
     424              :         {
     425            0 :             state(stateCodes::NODEVICE);
     426              : 
     427            0 :             if (!stateLogged())
     428              :             {
     429            0 :                 log<text_log>(std::string("USB Device ") + m_idVendor + ":" + m_idProduct + ":" + m_serial + " not found in udev");
     430              :             }
     431            0 :             return 0;
     432              :         }
     433              :         else
     434              :         {
     435            0 :             state(stateCodes::NOTCONNECTED);
     436            0 :             if (!stateLogged())
     437              :             {
     438            0 :                 log<text_log>(std::string("USB Device ") + m_idVendor + ":" + m_idProduct + ":" + m_serial + " found in udev as " + m_deviceName);
     439              :             }
     440              :         }
     441              :     }
     442              : 
     443            0 :     if (state() == stateCodes::NOTCONNECTED)
     444              :     {
     445              :         int rv;
     446              :         { // scope for elPriv
     447            0 :             elevatedPrivileges elPriv(this);
     448            0 :             rv = connect();
     449            0 :         }
     450              : 
     451            0 :         if (rv < 0)
     452              :         {
     453            0 :             int nrv = tty::usbDevice::getDeviceName();
     454            0 :             if (nrv < 0 && nrv != TTY_E_DEVNOTFOUND && nrv != TTY_E_NODEVNAMES)
     455              :             {
     456            0 :                 state(stateCodes::FAILURE);
     457            0 :                 if (!stateLogged())
     458            0 :                     log<software_critical>({__FILE__, __LINE__, nrv, tty::ttyErrorString(nrv)});
     459            0 :                 return -1;
     460              :             }
     461              : 
     462            0 :             if (nrv == TTY_E_DEVNOTFOUND || nrv == TTY_E_NODEVNAMES)
     463              :             {
     464            0 :                 state(stateCodes::NODEVICE);
     465              : 
     466            0 :                 if (!stateLogged())
     467              :                 {
     468            0 :                     std::stringstream logs;
     469            0 :                     logs << "USB Device " << m_idVendor << ":" << m_idProduct << ":" << m_serial << " no longer found in udev";
     470            0 :                     log<text_log>(logs.str());
     471            0 :                 }
     472            0 :                 return 0;
     473              :             }
     474              : 
     475              :             // if connect failed, and there is a device, then we have some other problem.
     476            0 :             state(stateCodes::FAILURE);
     477            0 :             if (!stateLogged())
     478            0 :                 log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     479            0 :             return -1;
     480              :         }
     481              : 
     482            0 :         if (testConnection() == 0)
     483              :         {
     484            0 :             state(stateCodes::CONNECTED);
     485              :         }
     486              :         else
     487              :         {
     488            0 :             return 0;
     489              :         }
     490              :     }
     491              : 
     492            0 :     if (state() == stateCodes::CONNECTED)
     493              :     {
     494            0 :         state(stateCodes::NOTHOMED);
     495              :     }
     496              : 
     497            0 :     if (state() == stateCodes::HOMING)
     498              :     {
     499            0 :         int ax = m_homingState + 1;
     500              : 
     501            0 :         int atz = homeState(ax);
     502              : 
     503            0 :         if (!(atz == 0 || atz == 1))
     504              :         {
     505            0 :             state(stateCodes::ERROR);
     506            0 :             log<software_error, -1>({__FILE__, __LINE__, "error getting ATZ? home state."});
     507              :         }
     508              : 
     509            0 :         if (atz == 1)
     510              :         {
     511            0 :             ++m_homingState;
     512              : 
     513            0 :             if (m_homingState == 1) // x complete
     514              :             {
     515            0 :                 home_2();
     516              :             }
     517            0 :             else if (m_homingState == 2 && m_naxes == 2) // y complete, done
     518              :             {
     519            0 :                 finishInit();
     520              :             }
     521            0 :             else if (m_homingState == 2 && m_naxes == 3) // y complete
     522              :             {
     523            0 :                 home_3();
     524              :             }
     525            0 :             else if (m_homingState > 2)
     526              :             {
     527            0 :                 finishInit();
     528              :             }
     529              :         }
     530              :     }
     531              : 
     532            0 :     if (state() == stateCodes::READY || state() == stateCodes::OPERATING)
     533              :     {
     534            0 :         if (m_flatSet)
     535            0 :             state(stateCodes::OPERATING);
     536              :         else
     537            0 :             state(stateCodes::READY);
     538              :     }
     539              : 
     540            0 :     if (state() == stateCodes::READY)
     541              :     {
     542              :         float pos1;
     543              :         float sva1;
     544              :         float pos2;
     545              :         float sva2;
     546              : 
     547              : 
     548              :         // Get a lock if we can
     549            0 :         std::unique_lock<std::mutex> lock(m_indiMutex);
     550              : 
     551            0 :         if (getPos(pos1, 1) < 0)
     552              :         {
     553            0 :             log<software_error>({__FILE__, __LINE__});
     554            0 :             state(stateCodes::ERROR);
     555            0 :             return 0;
     556              :         }
     557              : 
     558            0 :         lock.unlock();
     559            0 :         mx::sys::milliSleep(1); // Put this thread to sleep to make sure other thread gets a lock
     560              : 
     561            0 :         m_pos1 = pos1;
     562            0 :         if (fabs(m_pos1Set - m_pos1) > m_posTol)
     563              :         {
     564            0 :             updateIfChanged(m_indiP_pos1, "current", m_pos1, INDI_BUSY);
     565              :         }
     566              :         else
     567              :         {
     568            0 :             updateIfChanged(m_indiP_pos1, "current", m_pos1Set, INDI_IDLE);
     569              :         }
     570              : 
     571            0 :         lock.lock();
     572            0 :         if (getSva(sva1, 1) < 0)
     573              :         {
     574            0 :             log<software_error>({__FILE__, __LINE__});
     575            0 :             state(stateCodes::ERROR);
     576            0 :             return 0;
     577              :         }
     578            0 :         m_sva1 = sva1;
     579              : 
     580            0 :         lock.unlock();
     581            0 :         mx::sys::milliSleep(1); // Put this thread to sleep to make sure other thread gets a lock
     582              : 
     583            0 :         lock.lock();
     584            0 :         if (getPos(pos2, 2) < 0)
     585              :         {
     586            0 :             log<software_error>({__FILE__, __LINE__});
     587            0 :             state(stateCodes::ERROR);
     588            0 :             return 0;
     589              :         }
     590            0 :         lock.unlock();
     591            0 :         mx::sys::milliSleep(1); // Put this thread to sleep to make sure other thread gets a lock
     592              : 
     593            0 :         m_pos2 = pos2;
     594            0 :         if (fabs(m_pos2Set - m_pos2) > m_posTol) // sva2 != m_sva2)
     595              :         {
     596            0 :             updateIfChanged(m_indiP_pos2, "current", m_pos2, INDI_BUSY);
     597              :         }
     598              :         else
     599              :         {
     600            0 :             updateIfChanged(m_indiP_pos2, "current", m_pos2Set, INDI_IDLE);
     601              :         }
     602              : 
     603            0 :         lock.lock();
     604            0 :         if (getSva(sva2, 2) < 0)
     605              :         {
     606            0 :             log<software_error>({__FILE__, __LINE__});
     607            0 :             state(stateCodes::ERROR);
     608            0 :             return 0;
     609              :         }
     610            0 :         lock.unlock();
     611            0 :         mx::sys::milliSleep(1); // Put this thread to sleep to make sure other thread gets a lock
     612              : 
     613            0 :         m_sva2 = sva2;
     614              : 
     615            0 :         if (m_naxes == 3)
     616              :         {
     617              :             float pos3;
     618              :             float sva3;
     619              : 
     620            0 :             lock.lock();
     621            0 :             if (getPos(pos3, 3) < 0)
     622              :             {
     623            0 :                 log<software_error>({__FILE__, __LINE__});
     624            0 :                 state(stateCodes::ERROR);
     625            0 :                 return 0;
     626              :             }
     627            0 :             lock.unlock();
     628            0 :             mx::sys::milliSleep(1); // Put this thread to sleep to make sure other thread gets a lock
     629              : 
     630            0 :             m_pos3 = pos3;
     631            0 :             if (fabs(m_pos3Set - m_pos3) > m_posTol)
     632              :             {
     633            0 :                 updateIfChanged(m_indiP_pos3, "current", m_pos3, INDI_BUSY);
     634              :             }
     635              :             else
     636              :             {
     637            0 :                 updateIfChanged(m_indiP_pos3, "current", m_pos3Set, INDI_IDLE);
     638              :             }
     639              : 
     640            0 :             lock.lock();
     641            0 :             if (getSva(sva3, 2) < 0)
     642              :             {
     643            0 :                 log<software_error>({__FILE__, __LINE__});
     644            0 :                 state(stateCodes::ERROR);
     645            0 :                 return 0;
     646              :             }
     647            0 :             lock.unlock();
     648            0 :             mx::sys::milliSleep(1); // Put this thread to sleep to make sure other thread gets a lock
     649              : 
     650            0 :             m_sva3 = sva3;
     651              :         }
     652              : 
     653            0 :         recordPI335();
     654              : 
     655              :         /*std::cerr << m_pos1Set << " " << pos1 << " " << m_sva1 << " " << m_pos2Set << " " << pos2 << " " << m_sva2;
     656              :         if(m_naxes == 3) std::cerr << " " << m_pos3Set << " " << pos3 << " " << m_sva3;
     657              :         std::cerr << "\n";*/
     658            0 :     }
     659            0 :     else if (state() == stateCodes::OPERATING)
     660              :     {
     661            0 :         updateIfChanged<float>(m_indiP_pos1, std::vector<std::string>({"current", "target"}), {m_pos1, m_pos1Set}, INDI_BUSY);
     662            0 :         updateIfChanged<float>(m_indiP_pos2, std::vector<std::string>({"current", "target"}), {m_pos2, m_pos2Set}, INDI_BUSY);
     663              : 
     664            0 :         if (m_naxes == 3)
     665              :         {
     666            0 :             updateIfChanged<float>(m_indiP_pos3, std::vector<std::string>({"current", "target"}), {m_pos3, m_pos3Set}, INDI_BUSY);
     667              :         }
     668              :     }
     669              : 
     670            0 :     SHMIMMONITOR_UPDATE_INDI;
     671            0 :     DM_UPDATE_INDI;
     672              : 
     673            0 :     return 0;
     674            0 : }
     675              : 
     676            0 : int pi335Ctrl::appShutdown()
     677              : {
     678            0 :     DM_APP_SHUTDOWN;
     679            0 :     SHMIMMONITOR_APP_SHUTDOWN;
     680            0 :     TELEMETER_APP_SHUTDOWN;
     681              : 
     682            0 :     return 0;
     683              : }
     684              : 
     685            0 : int pi335Ctrl::testConnection()
     686              : {
     687              :     int rv;
     688            0 :     std::string resp;
     689              : 
     690            0 :     if ((rv = tty::ttyWriteRead(resp, "*IDN?\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     691              :     {
     692            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     693              :     }
     694              : 
     695              :     size_t st;
     696            0 :     if ((st = resp.find("E-727.3SDA")) == std::string::npos)
     697              :     {
     698            0 :         return log<text_log, -1>("Unknown device found: " + resp, logPrio::LOG_CRITICAL);
     699              :     }
     700            0 :     m_ctrl = mx::ioutils::removeWhiteSpace(resp.substr(st));
     701            0 :     log<text_log>(std::string("Connected to " + m_ctrl + " on ") + m_deviceName);
     702              : 
     703            0 :     std::string resp1, resp2, resp3;
     704              : 
     705            0 :     if ((rv = tty::ttyWriteRead(resp1, "CST? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     706              :     {
     707            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     708              :     }
     709            0 :     resp1 = mx::ioutils::removeWhiteSpace(resp1);
     710              : 
     711            0 :     if ((rv = tty::ttyWriteRead(resp2, "CST? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     712              :     {
     713            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     714              :     }
     715            0 :     resp2 = mx::ioutils::removeWhiteSpace(resp2);
     716              : 
     717            0 :     if ((rv = tty::ttyWriteRead(resp3, "CST? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     718              :     {
     719            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     720              :     }
     721            0 :     resp3 = mx::ioutils::removeWhiteSpace(resp3);
     722              : 
     723            0 :     updateIfChanged(m_indiP_pos1, "current", 0.0);
     724            0 :     updateIfChanged(m_indiP_pos1, "target", 0.0);
     725              : 
     726            0 :     updateIfChanged(m_indiP_pos2, "current", 0.0);
     727            0 :     updateIfChanged(m_indiP_pos2, "target", 0.0);
     728              : 
     729            0 :     if (resp1.find("1=S-335") == 0 && resp2.find("2=S-335") == 0 && resp3.find("3=0") == 0)
     730              :     {
     731            0 :         m_stage = resp1.substr(2);
     732            0 :         m_naxes = 2;
     733            0 :         m_actuallyATZ = true;
     734              :     }
     735            0 :     else if (resp1.find("1=S-325") == 0 && resp2.find("2=S-325") == 0 && resp3.find("3=S-325") == 0)
     736              :     {
     737            0 :         m_stage = resp1.substr(2);
     738            0 :         m_naxes = 3;
     739            0 :         m_actuallyATZ = false;
     740            0 :         if (!m_pos_3_sent)
     741              :         {
     742              :             ///\todo this needs to only happen once, and then never again
     743            0 :             REG_INDI_NEWPROP(m_indiP_pos3, "pos_3", pcf::IndiProperty::Number);
     744            0 :             m_indiP_pos3.add(pcf::IndiElement("current"));
     745            0 :             m_indiP_pos3.add(pcf::IndiElement("target"));
     746            0 :             m_indiP_pos3["current"] = -99999999;
     747            0 :             m_indiP_pos3["target"] = -99999999;
     748            0 :             updateIfChanged(m_indiP_pos3, "current", 0.0);
     749            0 :             updateIfChanged(m_indiP_pos3, "target", 0.0);
     750            0 :             m_pos_3_sent = true;
     751              :         }
     752              :     }
     753              :     else
     754              :     {
     755            0 :         return log<text_log, -1>("Unknown stage found: " + resp1 + " " + resp2 + " " + resp3, logPrio::LOG_CRITICAL);
     756              :     }
     757              : 
     758            0 :     log<text_log>("Found " + m_stage + " with " + std::to_string(m_naxes) + " axes");
     759              : 
     760              :     //-------- now get axis limits
     761              : 
     762              :     // axis 1
     763              : 
     764            0 :     if ((rv = tty::ttyWriteRead(resp, "TMN? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     765              :     {
     766            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     767              :     }
     768              : 
     769            0 :     if ((st = resp.find('=')) == std::string::npos)
     770              :     {
     771            0 :         return log<software_critical, -1>({__FILE__, __LINE__, "invalid response"});
     772              :     }
     773              : 
     774            0 :     m_min1 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
     775            0 :     log<text_log>("axis 1 min: " + std::to_string(m_min1));
     776              : 
     777            0 :     if ((rv = tty::ttyWriteRead(resp, "TMX? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     778              :     {
     779            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     780              :     }
     781              : 
     782            0 :     if ((st = resp.find('=')) == std::string::npos)
     783              :     {
     784            0 :         return log<software_critical, -1>({__FILE__, __LINE__, "invalid response"});
     785              :     }
     786              : 
     787            0 :     m_max1 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
     788            0 :     log<text_log>("axis 1 max: " + std::to_string(m_max1));
     789              : 
     790            0 :     if ((rv = tty::ttyWriteRead(resp, "TMN? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     791              :     {
     792            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     793              :     }
     794              : 
     795            0 :     if ((st = resp.find('=')) == std::string::npos)
     796              :     {
     797            0 :         return log<software_critical, -1>({__FILE__, __LINE__, "invalid response"});
     798              :     }
     799              : 
     800            0 :     m_min2 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
     801            0 :     log<text_log>("axis 2 min: " + std::to_string(m_min2));
     802              : 
     803            0 :     if ((rv = tty::ttyWriteRead(resp, "TMX? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     804              :     {
     805            0 :         return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     806              :     }
     807              : 
     808            0 :     if ((st = resp.find('=')) == std::string::npos)
     809              :     {
     810            0 :         return log<software_critical, -1>({__FILE__, __LINE__, "invalid response"});
     811              :     }
     812              : 
     813            0 :     m_max2 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
     814            0 :     log<text_log>("axis 2 max: " + std::to_string(m_max2));
     815              : 
     816            0 :     if (m_naxes == 3)
     817              :     {
     818            0 :         if ((rv = tty::ttyWriteRead(resp, "TMN? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     819              :         {
     820            0 :             return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     821              :         }
     822              : 
     823            0 :         if ((st = resp.find('=')) == std::string::npos)
     824              :         {
     825            0 :             return log<software_critical, -1>({__FILE__, __LINE__, "invalid response"});
     826              :         }
     827              : 
     828            0 :         m_min3 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
     829            0 :         log<text_log>("axis 3 min: " + std::to_string(m_min3));
     830              : 
     831            0 :         if ((rv = tty::ttyWriteRead(resp, "TMX? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout)) < 0)
     832              :         {
     833            0 :             return log<software_critical, -1>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     834              :         }
     835              : 
     836            0 :         if ((st = resp.find('=')) == std::string::npos)
     837              :         {
     838            0 :             return log<software_critical, -1>({__FILE__, __LINE__, "invalid response"});
     839              :         }
     840              : 
     841            0 :         m_max3 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
     842            0 :         log<text_log>("axis 3 max: " + std::to_string(m_max3));
     843              :     }
     844              : 
     845            0 :     m_flatCommand.resize(3, 1);
     846            0 :     if (m_naxes == 2)
     847              :     {
     848            0 :         m_flatCommand(0, 0) = m_homePos1;
     849            0 :         m_flatCommand(1, 0) = m_homePos2;
     850            0 :         m_flatCommand(2, 0) = 0.0;
     851              :     }
     852            0 :     else if (m_naxes == 3)
     853              :     {
     854            0 :         m_flatCommand(0, 0) = m_homePos1;
     855            0 :         m_flatCommand(1, 0) = m_homePos2;
     856            0 :         m_flatCommand(2, 0) = m_homePos3;
     857              :     }
     858            0 :     m_flatLoaded = true;
     859              : 
     860            0 :     return 0;
     861            0 : }
     862              : 
     863            0 : int pi335Ctrl::initDM()
     864              : {
     865              :     int rv;
     866            0 :     std::string resp;
     867              : 
     868              :     // get open-loop position of axis 1 (should be zero)
     869            0 :     rv = tty::ttyWriteRead(resp, "SVA? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
     870              : 
     871            0 :     if (rv < 0)
     872              :     {
     873            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     874              :     }
     875              : 
     876              :     // get open-loop position of axis 2 (should be zero)
     877            0 :     rv = tty::ttyWriteRead(resp, "SVA? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
     878              : 
     879            0 :     if (rv < 0)
     880              :     {
     881            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     882              :     }
     883              : 
     884            0 :     if (m_naxes == 3)
     885              :     {
     886              :         // get open-loop position of axis 2 (should be zero)
     887            0 :         rv = tty::ttyWriteRead(resp, "SVA? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
     888              : 
     889            0 :         if (rv < 0)
     890              :         {
     891            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     892              :         }
     893              :     }
     894              : 
     895              :     // make sure axis 1 has servo off
     896            0 :     rv = tty::ttyWrite("SVO 1 0\n", m_fileDescrip, m_writeTimeout);
     897              : 
     898            0 :     if (rv < 0)
     899              :     {
     900            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     901              :     }
     902              : 
     903              :     // make sure axis 2 has servo off
     904            0 :     rv = tty::ttyWrite("SVA 2 0\n", m_fileDescrip, m_writeTimeout);
     905              : 
     906            0 :     if (rv < 0)
     907              :     {
     908            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     909              :     }
     910              : 
     911            0 :     if (m_naxes == 0)
     912              :     {
     913              :         // make sure axis 3 has servo off
     914            0 :         rv = tty::ttyWrite("SVA 3 0\n", m_fileDescrip, m_writeTimeout);
     915              : 
     916            0 :         if (rv < 0)
     917              :         {
     918            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     919              :         }
     920              :     }
     921              : 
     922            0 :     m_servoState = 0;
     923              : 
     924            0 :     log<text_log>("servos off", logPrio::LOG_NOTICE);
     925              : 
     926            0 :     return home();
     927            0 : }
     928              : 
     929            0 : int pi335Ctrl::home()
     930              : {
     931            0 :     if (m_servoState != 0)
     932              :     {
     933            0 :         log<text_log>("home requested but servos are not off", logPrio::LOG_ERROR);
     934            0 :         return -1;
     935              :     }
     936              : 
     937            0 :     m_homingStart = 0;
     938            0 :     m_homingState = 0;
     939              : 
     940            0 :     state(stateCodes::HOMING);
     941              : 
     942            0 :     return home_1();
     943              : }
     944              : 
     945            0 : int pi335Ctrl::homeState(int axis)
     946              : {
     947            0 :     if (!m_actuallyATZ)
     948            0 :         return 1;
     949            0 :     std::string resp;
     950              : 
     951            0 :     if (getCom(resp, "ATZ?", axis) < 0)
     952              :     {
     953            0 :         log<software_error>({__FILE__, __LINE__});
     954              : 
     955            0 :         return -1;
     956              :     }
     957              : 
     958              :     ///\todo this should be a separate unit-tested parser
     959            0 :     size_t st = resp.find('=');
     960            0 :     if (st == std::string::npos || st > resp.size() - 2)
     961              :     {
     962            0 :         log<software_error>({__FILE__, __LINE__, "error parsing response"});
     963            0 :         return -1;
     964              :     }
     965            0 :     st += 1;
     966              : 
     967            0 :     return mx::ioutils::convertFromString<double>(resp.substr(st));
     968            0 : }
     969              : 
     970            0 : int pi335Ctrl::home_1()
     971              : {
     972            0 :     if (m_servoState != 0)
     973              :     {
     974            0 :         log<text_log>("home_1 requested but servos are not off", logPrio::LOG_ERROR);
     975            0 :         return -1;
     976              :     }
     977              : 
     978            0 :     if (m_homingState != 0)
     979              :     {
     980            0 :         log<text_log>("home_1 requested but not in correct homing state", logPrio::LOG_ERROR);
     981            0 :         return -1;
     982              :     }
     983              : 
     984            0 :     if (m_actuallyATZ)
     985              :     {
     986              :         // zero range found in axis 1 (NOTE this moves mirror full range) TAKES 1min
     987            0 :         int rv = tty::ttyWrite("ATZ 1 NaN\n", m_fileDescrip, m_writeTimeout);
     988              : 
     989            0 :         if (rv < 0)
     990              :         {
     991            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
     992              :         }
     993              :     }
     994              : 
     995            0 :     m_homingStart = mx::sys::get_curr_time(); ///\todo remmove m_homingStart once ATZ? works.
     996            0 :     m_homingState = 0;
     997            0 :     log<text_log>("commenced homing x");
     998              : 
     999            0 :     return 0;
    1000              : }
    1001              : 
    1002            0 : int pi335Ctrl::home_2()
    1003              : {
    1004              :     int rv;
    1005              : 
    1006            0 :     if (m_servoState != 0)
    1007              :     {
    1008            0 :         log<text_log>("home_2 requested but servos are not off", logPrio::LOG_ERROR);
    1009            0 :         return -1;
    1010              :     }
    1011              : 
    1012            0 :     if (m_homingState != 1)
    1013              :     {
    1014            0 :         log<text_log>("home_2 requested but not in correct homing state", logPrio::LOG_ERROR);
    1015            0 :         return -1;
    1016              :     }
    1017              : 
    1018            0 :     if (m_actuallyATZ)
    1019              :     {
    1020              :         // zero range found in axis 2 (NOTE this moves mirror full range) TAKES 1min
    1021            0 :         rv = tty::ttyWrite("ATZ 2 NaN\n", m_fileDescrip, m_writeTimeout);
    1022              : 
    1023            0 :         if (rv < 0)
    1024              :         {
    1025            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1026              :         }
    1027              :     }
    1028              : 
    1029            0 :     m_homingStart = mx::sys::get_curr_time();
    1030            0 :     log<text_log>("commenced homing y");
    1031              : 
    1032            0 :     return 0;
    1033              : }
    1034              : 
    1035            0 : int pi335Ctrl::home_3()
    1036              : {
    1037              :     int rv;
    1038              : 
    1039            0 :     if (m_servoState != 0)
    1040              :     {
    1041            0 :         log<text_log>("home_3 requested but servos are not off", logPrio::LOG_ERROR);
    1042            0 :         return -1;
    1043              :     }
    1044              : 
    1045            0 :     if (m_homingState != 2)
    1046              :     {
    1047            0 :         log<text_log>("home_3 requested but not in correct homing state", logPrio::LOG_ERROR);
    1048            0 :         return -1;
    1049              :     }
    1050              : 
    1051            0 :     if (m_actuallyATZ)
    1052              :     {
    1053              :         // zero range found in axis 3 (NOTE this moves mirror full range) TAKES 1min
    1054            0 :         rv = tty::ttyWrite("ATZ 3 NaN\n", m_fileDescrip, m_writeTimeout);
    1055              : 
    1056            0 :         if (rv < 0)
    1057              :         {
    1058            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1059              :         }
    1060              :     }
    1061              : 
    1062            0 :     m_homingStart = mx::sys::get_curr_time();
    1063            0 :     log<text_log>("commenced homing z");
    1064              : 
    1065            0 :     return 0;
    1066              : }
    1067              : 
    1068            0 : int pi335Ctrl::finishInit()
    1069              : {
    1070              :     int rv;
    1071            0 :     std::string resp;
    1072              : 
    1073            0 :     if (m_servoState != 0)
    1074              :     {
    1075            0 :         log<text_log>("finishInit requested but servos are not off", logPrio::LOG_ERROR);
    1076            0 :         return -1;
    1077              :     }
    1078              : 
    1079            0 :     if (m_naxes == 2 && m_homingState != 2)
    1080              :     {
    1081            0 :         log<text_log>("finishInit requested but not in correct homing state", logPrio::LOG_ERROR);
    1082            0 :         return -1;
    1083              :     }
    1084            0 :     if (m_naxes == 3 && m_homingState != 3)
    1085              :     {
    1086            0 :         log<text_log>("finishInit requested but not in correct homing state", logPrio::LOG_ERROR);
    1087            0 :         return -1;
    1088              :     }
    1089              : 
    1090              :     // goto openloop pos zero (0 V) axis 1
    1091            0 :     rv = tty::ttyWrite("SVA 1 0.0\n", m_fileDescrip, m_writeTimeout);
    1092              : 
    1093            0 :     if (rv < 0)
    1094              :     {
    1095            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1096              :     }
    1097              : 
    1098            0 :     mx::sys::milliSleep(2000);
    1099              : 
    1100              :     // goto openloop pos zero (0 V) axis 2
    1101            0 :     rv = tty::ttyWrite("SVA 2 0.0\n", m_fileDescrip, m_writeTimeout);
    1102              : 
    1103            0 :     if (rv < 0)
    1104              :     {
    1105            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1106              :     }
    1107              : 
    1108            0 :     mx::sys::milliSleep(2000);
    1109              : 
    1110            0 :     if (m_naxes == 3)
    1111              :     {
    1112              :         // goto openloop pos zero (0 V) axis 3
    1113            0 :         rv = tty::ttyWrite("SVA 3 0.0\n", m_fileDescrip, m_writeTimeout);
    1114              : 
    1115            0 :         if (rv < 0)
    1116              :         {
    1117            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1118              :         }
    1119              : 
    1120            0 :         mx::sys::milliSleep(2000);
    1121              :     }
    1122              : 
    1123              :     // Get the real position of axis 1 (should be 0mrad st start)
    1124            0 :     rv = tty::ttyWriteRead(resp, "SVA? 1\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
    1125              : 
    1126            0 :     if (rv < 0)
    1127              :     {
    1128            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1129              :     }
    1130              : 
    1131              :     // Get the real position of axis 2 (should be 0mrad st start)
    1132            0 :     rv = tty::ttyWriteRead(resp, "SVA? 2\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
    1133              : 
    1134            0 :     if (rv < 0)
    1135              :     {
    1136            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1137              :     }
    1138              : 
    1139            0 :     if (m_naxes == 3)
    1140              :     {
    1141              :         // Get the real position of axis 3 (should be 0mrad st start)
    1142            0 :         rv = tty::ttyWriteRead(resp, "SVA? 3\n", "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
    1143              : 
    1144            0 :         if (rv < 0)
    1145              :         {
    1146            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1147              :         }
    1148              :     }
    1149              : 
    1150              :     // now safe to engage servos
    1151              :     //(IMPORTANT:    NEVER EVER enable servos on axis 3 -- will damage S-335)
    1152              :     //(CAVEAT: for S-325 you CAN enable servors on axis 3)
    1153              : 
    1154              :     // turn on servo to axis 1 (green servo LED goes on 727)
    1155            0 :     rv = tty::ttyWrite("SVO 1 1\n", m_fileDescrip, m_writeTimeout);
    1156              : 
    1157            0 :     if (rv < 0)
    1158              :     {
    1159            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1160              :     }
    1161              : 
    1162            0 :     mx::sys::milliSleep(250);
    1163              : 
    1164              :     // turn on servo to axis 2 (green servo LED goes on 727)
    1165            0 :     rv = tty::ttyWrite("SVO 2 1\n", m_fileDescrip, m_writeTimeout);
    1166              : 
    1167            0 :     if (rv < 0)
    1168              :     {
    1169            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1170              :     }
    1171              : 
    1172            0 :     if (m_naxes == 3)
    1173              :     {
    1174            0 :         mx::sys::milliSleep(250);
    1175              : 
    1176              :         // turn on servo to axis 3 (green servo LED goes on 727)
    1177            0 :         rv = tty::ttyWrite("SVO 3 1\n", m_fileDescrip, m_writeTimeout);
    1178              : 
    1179            0 :         if (rv < 0)
    1180              :         {
    1181            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1182              :         }
    1183              :     }
    1184              : 
    1185            0 :     m_servoState = 1;
    1186            0 :     log<text_log>("servos engaged", logPrio::LOG_NOTICE);
    1187              : 
    1188            0 :     mx::sys::milliSleep(1000);
    1189              : 
    1190              :     // now safe for closed loop moves
    1191              :     // center axis 1 (to configured home position)
    1192              : 
    1193            0 :     std::string com = "MOV 1 " + std::to_string(m_homePos1) + "\n";
    1194            0 :     rv = tty::ttyWrite(com, m_fileDescrip, m_writeTimeout);
    1195              : 
    1196            0 :     if (rv < 0)
    1197              :     {
    1198            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1199              :     }
    1200              : 
    1201            0 :     m_pos1Set = m_homePos1;
    1202            0 :     updateIfChanged(m_indiP_pos1, "target", m_homePos1);
    1203              : 
    1204              :     // center axis 2 (to configured home position)
    1205            0 :     com = "MOV 2 " + std::to_string(m_homePos2) + "\n";
    1206            0 :     rv = tty::ttyWrite(com, m_fileDescrip, m_writeTimeout);
    1207              : 
    1208            0 :     if (rv < 0)
    1209              :     {
    1210            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1211              :     }
    1212              : 
    1213            0 :     m_pos2Set = m_homePos2;
    1214            0 :     updateIfChanged(m_indiP_pos2, "target", m_homePos2);
    1215              : 
    1216            0 :     if (m_naxes == 3)
    1217              :     {
    1218              :         // center axis 3 (to configured home position)
    1219            0 :         com = "MOV 3 " + std::to_string(m_homePos3) + "\n";
    1220            0 :         rv = tty::ttyWrite(com, m_fileDescrip, m_writeTimeout);
    1221              : 
    1222            0 :         if (rv < 0)
    1223              :         {
    1224            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1225              :         }
    1226              : 
    1227            0 :         m_pos3Set = m_homePos3;
    1228            0 :         updateIfChanged(m_indiP_pos3, "target", m_homePos3);
    1229              :     }
    1230              : 
    1231            0 :     state(stateCodes::READY);
    1232              : 
    1233            0 :     return 0;
    1234            0 : }
    1235              : 
    1236            0 : int pi335Ctrl::zeroDM()
    1237              : {
    1238            0 :     move_1(0.0);
    1239            0 :     move_2(0.0);
    1240            0 :     if (m_naxes == 3)
    1241            0 :         move_3(0.0);
    1242              : 
    1243            0 :     log<text_log>("DM zeroed");
    1244            0 :     return 0;
    1245              : }
    1246              : 
    1247            0 : int pi335Ctrl::commandDM(void *curr_src)
    1248              : {
    1249            0 :     if (state() != stateCodes::OPERATING)
    1250            0 :         return 0;
    1251            0 :     float pos1 = (reinterpret_cast<float *>(curr_src))[0];
    1252            0 :     float pos2 = (reinterpret_cast<float *>(curr_src))[1];
    1253              : 
    1254            0 :     float pos3 = 0;
    1255            0 :     if (m_naxes == 3)
    1256            0 :         pos3 = (reinterpret_cast<float *>(curr_src))[2];
    1257              : 
    1258            0 :     std::unique_lock<std::mutex> lock(m_indiMutex);
    1259              : 
    1260              :     int rv;
    1261            0 :     if ((rv = move_1(pos1)) < 0)
    1262            0 :         return rv;
    1263              : 
    1264            0 :     if ((rv = move_2(pos2)) < 0)
    1265            0 :         return rv;
    1266              : 
    1267            0 :     if (m_naxes == 3)
    1268            0 :         if ((rv = move_3(pos3)) < 0)
    1269            0 :             return rv;
    1270              : 
    1271            0 :     return 0;
    1272            0 : }
    1273              : 
    1274            0 : int pi335Ctrl::releaseDM()
    1275              : {
    1276              :     int rv;
    1277              : 
    1278            0 :     if (m_servoState != 0)
    1279              :     {
    1280            0 :         if ((rv = tty::ttyWrite("SVO 1 0\n", m_fileDescrip, m_writeTimeout)) < 0)
    1281              :         {
    1282            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1283              :         }
    1284              : 
    1285            0 :         if ((rv = tty::ttyWrite("SVO 2 0\n", m_fileDescrip, m_writeTimeout)) < 0)
    1286              :         {
    1287            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1288              :         }
    1289              : 
    1290            0 :         m_servoState = 0;
    1291              : 
    1292            0 :         log<text_log>("servos off", logPrio::LOG_NOTICE);
    1293              :     }
    1294              : 
    1295            0 :     if ((rv = tty::ttyWrite("SVA 1 0\n", m_fileDescrip, m_writeTimeout)) < 0)
    1296              :     {
    1297            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1298              :     }
    1299              : 
    1300            0 :     if ((rv = tty::ttyWrite("SVA 2 0\n", m_fileDescrip, m_writeTimeout)) < 0)
    1301              :     {
    1302            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1303              :     }
    1304              : 
    1305            0 :     if (m_naxes == 3)
    1306              :     {
    1307            0 :         if ((rv = tty::ttyWrite("SVA 2 0\n", m_fileDescrip, m_writeTimeout)) < 0)
    1308              :         {
    1309            0 :             log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1310              :         }
    1311              :     }
    1312              : 
    1313            0 :     m_flatSet = false;
    1314            0 :     state(stateCodes::NOTHOMED);
    1315              : 
    1316            0 :     return 0;
    1317              : }
    1318              : 
    1319            0 : int pi335Ctrl::getCom(std::string &resp,
    1320              :                       const std::string &com,
    1321              :                       int axis)
    1322              : {
    1323            0 :     std::string sendcom = com;
    1324            0 :     if (axis == 1 || axis == 2)
    1325              :     {
    1326            0 :         sendcom += " ";
    1327            0 :         sendcom += std::to_string(axis);
    1328              :     }
    1329              : 
    1330            0 :     sendcom += "\n";
    1331              : 
    1332            0 :     int rv = tty::ttyWriteRead(resp, sendcom, "\n", false, m_fileDescrip, m_writeTimeout, m_readTimeout);
    1333            0 :     if (rv < 0)
    1334              :     {
    1335            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1336            0 :         return -1;
    1337              :     }
    1338              : 
    1339            0 :     return 0;
    1340            0 : }
    1341              : 
    1342            0 : int pi335Ctrl::getPos(float &pos,
    1343              :                       int n)
    1344              : {
    1345            0 :     std::string resp;
    1346            0 :     if (getCom(resp, "POS?", n) < 0)
    1347              :     {
    1348            0 :         log<software_error>({__FILE__, __LINE__});
    1349              :     }
    1350              : 
    1351              :     ///\todo this should be a separate unit-tested parser
    1352            0 :     size_t st = resp.find('=');
    1353            0 :     if (st == std::string::npos || st > resp.size() - 2)
    1354              :     {
    1355            0 :         log<software_error>({__FILE__, __LINE__, "error parsing response"});
    1356            0 :         return -1;
    1357              :     }
    1358            0 :     st += 1;
    1359            0 :     pos = mx::ioutils::convertFromString<double>(resp.substr(st));
    1360              : 
    1361            0 :     return 0;
    1362            0 : }
    1363              : 
    1364            0 : int pi335Ctrl::getSva(float &sva,
    1365              :                       int n)
    1366              : {
    1367            0 :     std::string resp;
    1368            0 :     if (getCom(resp, "SVA?", n) < 0)
    1369              :     {
    1370            0 :         log<software_error>({__FILE__, __LINE__});
    1371              :     }
    1372              : 
    1373              :     ///\todo this should be a separate unit-tested parser
    1374            0 :     size_t st = resp.find('=');
    1375            0 :     if (st == std::string::npos || st > resp.size() - 2)
    1376              :     {
    1377            0 :         log<software_error>({__FILE__, __LINE__, "error parsing response"});
    1378            0 :         return -1;
    1379              :     }
    1380            0 :     st += 1;
    1381            0 :     sva = mx::ioutils::convertFromString<double>(resp.substr(st));
    1382              : 
    1383            0 :     return 0;
    1384            0 : }
    1385              : 
    1386            0 : int pi335Ctrl::updateFlat(float absPos1,
    1387              :                           float absPos2,
    1388              :                           float absPos3)
    1389              : {
    1390            0 :     m_flatCommand(0, 0) = absPos1;
    1391            0 :     m_flatCommand(1, 0) = absPos2;
    1392            0 :     m_flatCommand(2, 0) = absPos3;
    1393              : 
    1394            0 :     if (state() == stateCodes::OPERATING)
    1395              :     {
    1396            0 :         return setFlat(true);
    1397              :     }
    1398              :     else
    1399              :     {
    1400            0 :         return 0;
    1401              :     }
    1402              : }
    1403              : 
    1404            0 : int pi335Ctrl::move_1(float absPos)
    1405              : {
    1406              :     int rv;
    1407              : 
    1408            0 :     if (absPos < m_min1 || absPos > m_max1)
    1409              :     {
    1410            0 :         log<text_log>("request move on azis 1 out of range", logPrio::LOG_ERROR);
    1411            0 :         return -1;
    1412              :     }
    1413              : 
    1414            0 :     m_pos1Set = absPos;
    1415              : 
    1416            0 :     std::string com = "MOV 1 " + std::to_string(absPos) + "\n";
    1417              : 
    1418            0 :     rv = tty::ttyWrite(com, m_fileDescrip, m_writeTimeout);
    1419              : 
    1420            0 :     if (rv < 0)
    1421              :     {
    1422            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1423              :     }
    1424              : 
    1425            0 :     return 0;
    1426            0 : }
    1427              : 
    1428            0 : int pi335Ctrl::move_2(float absPos)
    1429              : {
    1430              :     int rv;
    1431              : 
    1432            0 :     if (absPos < m_min2 || absPos > m_max2)
    1433              :     {
    1434            0 :         log<text_log>("request move on azis 2 out of range", logPrio::LOG_ERROR);
    1435            0 :         return -1;
    1436              :     }
    1437              : 
    1438            0 :     m_pos2Set = absPos;
    1439            0 :     std::string com = "MOV 2 " + std::to_string(absPos) + "\n";
    1440              : 
    1441            0 :     rv = tty::ttyWrite(com, m_fileDescrip, m_writeTimeout);
    1442              : 
    1443            0 :     if (rv < 0)
    1444              :     {
    1445            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1446              :     }
    1447              : 
    1448            0 :     return 0;
    1449            0 : }
    1450              : 
    1451            0 : int pi335Ctrl::move_3(float absPos)
    1452              : {
    1453            0 :     if (m_naxes < 3)
    1454              :     {
    1455            0 :         return log<software_error, -1>({__FILE__, __LINE__, "tried to move axis 3 but we don't have that"});
    1456              :     }
    1457              : 
    1458              :     int rv;
    1459              : 
    1460            0 :     if (absPos < m_min3 || absPos > m_max3)
    1461              :     {
    1462            0 :         log<text_log>("request move on azis 3 out of range", logPrio::LOG_ERROR);
    1463            0 :         return -1;
    1464              :     }
    1465              : 
    1466            0 :     m_pos3Set = absPos;
    1467            0 :     std::string com = "MOV 3 " + std::to_string(absPos) + "\n";
    1468              : 
    1469            0 :     rv = tty::ttyWrite(com, m_fileDescrip, m_writeTimeout);
    1470              : 
    1471            0 :     if (rv < 0)
    1472              :     {
    1473            0 :         log<software_error>({__FILE__, __LINE__, rv, tty::ttyErrorString(rv)});
    1474              :     }
    1475              : 
    1476            0 :     return 0;
    1477            0 : }
    1478              : 
    1479            0 : INDI_NEWCALLBACK_DEFN(pi335Ctrl, m_indiP_pos1)
    1480              : (const pcf::IndiProperty &ipRecv)
    1481              : {
    1482            0 :     if (ipRecv.createUniqueKey() == m_indiP_pos1.createUniqueKey())
    1483              :     {
    1484            0 :         float current = -999999, target = -999999;
    1485              : 
    1486            0 :         if (ipRecv.find("current"))
    1487              :         {
    1488            0 :             current = ipRecv["current"].get<float>();
    1489              :         }
    1490              : 
    1491            0 :         if (ipRecv.find("target"))
    1492              :         {
    1493            0 :             target = ipRecv["target"].get<float>();
    1494              :         }
    1495              : 
    1496            0 :         if (target == -999999)
    1497            0 :             target = current;
    1498              : 
    1499            0 :         if (target == -999999)
    1500            0 :             return 0;
    1501              : 
    1502            0 :         if (state() == stateCodes::READY)
    1503              :         {
    1504              :             // Lock the mutex, waiting if necessary
    1505            0 :             std::unique_lock<std::mutex> lock(m_indiMutex);
    1506              : 
    1507            0 :             updateIfChanged(m_indiP_pos1, "target", target);
    1508              : 
    1509            0 :             updateFlat(target, m_pos2Set, m_pos3Set); // This just changes the values, but doesn't move
    1510              : 
    1511            0 :             return move_1(target);
    1512            0 :         }
    1513            0 :         else if (state() == stateCodes::OPERATING)
    1514              :         {
    1515            0 :             return updateFlat(target, m_pos2Set, m_pos3Set);
    1516              :         }
    1517              :     }
    1518            0 :     return -1;
    1519              : }
    1520              : 
    1521            0 : INDI_NEWCALLBACK_DEFN(pi335Ctrl, m_indiP_pos2)
    1522              : (const pcf::IndiProperty &ipRecv)
    1523              : {
    1524            0 :     if (ipRecv.createUniqueKey() == m_indiP_pos2.createUniqueKey())
    1525              :     {
    1526            0 :         float current = -999999, target = -999999;
    1527              : 
    1528            0 :         if (ipRecv.find("current"))
    1529              :         {
    1530            0 :             current = ipRecv["current"].get<float>();
    1531              :         }
    1532              : 
    1533            0 :         if (ipRecv.find("target"))
    1534              :         {
    1535            0 :             target = ipRecv["target"].get<float>();
    1536              :         }
    1537              : 
    1538            0 :         if (target == -999999)
    1539            0 :             target = current;
    1540              : 
    1541            0 :         if (target == -999999)
    1542            0 :             return 0;
    1543              : 
    1544            0 :         if (state() == stateCodes::READY)
    1545              :         {
    1546              :             // Lock the mutex, waiting if necessary
    1547            0 :             std::unique_lock<std::mutex> lock(m_indiMutex);
    1548              : 
    1549            0 :             updateIfChanged(m_indiP_pos2, "target", target);
    1550            0 :             updateFlat(m_pos1Set, target, m_pos3Set); // This just changes the values, but doesn't move
    1551            0 :             return move_2(target);
    1552            0 :         }
    1553            0 :         else if (state() == stateCodes::OPERATING)
    1554              :         {
    1555            0 :             return updateFlat(m_pos1Set, target, m_pos3Set);
    1556              :         }
    1557              :     }
    1558            0 :     return -1;
    1559              : }
    1560              : 
    1561            0 : INDI_NEWCALLBACK_DEFN(pi335Ctrl, m_indiP_pos3)
    1562              : (const pcf::IndiProperty &ipRecv)
    1563              : {
    1564              : 
    1565            0 :     if (ipRecv.getName() == m_indiP_pos3.getName())
    1566              :     {
    1567            0 :         float current = -999999, target = -999999;
    1568              : 
    1569            0 :         if (ipRecv.find("current"))
    1570              :         {
    1571            0 :             current = ipRecv["current"].get<float>();
    1572              :         }
    1573              : 
    1574            0 :         if (ipRecv.find("target"))
    1575              :         {
    1576            0 :             target = ipRecv["target"].get<float>();
    1577              :         }
    1578              : 
    1579            0 :         if (target == -999999)
    1580            0 :             target = current;
    1581              : 
    1582            0 :         if (target == -999999)
    1583            0 :             return 0;
    1584              : 
    1585            0 :         if (state() == stateCodes::READY)
    1586              :         {
    1587              :             // Lock the mutex, waiting if necessary
    1588            0 :             std::unique_lock<std::mutex> lock(m_indiMutex);
    1589              : 
    1590            0 :             updateIfChanged(m_indiP_pos3, "target", target);
    1591              : 
    1592            0 :             updateFlat(m_pos1Set, m_pos2Set, target); // This just changes the values, but doesn't move
    1593            0 :             return move_3(target);
    1594            0 :         }
    1595            0 :         else if (state() == stateCodes::OPERATING)
    1596              :         {
    1597            0 :             return updateFlat(m_pos1Set, m_pos2Set, target);
    1598              :         }
    1599              :     }
    1600            0 :     return -1;
    1601              : }
    1602              : 
    1603            0 : int pi335Ctrl::checkRecordTimes()
    1604              : {
    1605            0 :     return telemeterT::checkRecordTimes(telem_pi335());
    1606              : }
    1607              : 
    1608            0 : int pi335Ctrl::recordTelem(const telem_pi335 *)
    1609              : {
    1610            0 :     return recordPI335(true);
    1611              : }
    1612              : 
    1613            0 : int pi335Ctrl::recordPI335(bool force)
    1614              : {
    1615              :     static float pos1Set = std::numeric_limits<float>::max();
    1616              :     static float pos1 = std::numeric_limits<float>::max();
    1617              :     static float sva1 = std::numeric_limits<float>::max();
    1618              :     static float pos2Set = std::numeric_limits<float>::max();
    1619              :     static float pos2 = std::numeric_limits<float>::max();
    1620              :     static float sva2 = std::numeric_limits<float>::max();
    1621              :     static float pos3Set = std::numeric_limits<float>::max();
    1622              :     static float pos3 = std::numeric_limits<float>::max();
    1623              :     static float sva3 = std::numeric_limits<float>::max();
    1624              : 
    1625            0 :     if (force || m_pos1Set != pos1Set || m_pos1 != pos1 || m_sva1 != sva1 ||
    1626            0 :         m_pos2Set != pos2Set || m_pos2 != pos2 || m_sva2 != sva2 ||
    1627            0 :         m_pos3Set != pos3Set || m_pos3 != pos3 || m_sva3 != sva3)
    1628              :     {
    1629            0 :         telem<telem_pi335>({m_pos1Set, m_pos1, m_sva1, m_pos2Set, m_pos2, m_sva2, m_pos3Set, m_pos3, m_sva3});
    1630              : 
    1631            0 :         pos1Set = m_pos1Set;
    1632            0 :         pos1 = m_pos1;
    1633            0 :         sva1 = m_sva1;
    1634            0 :         pos2Set = m_pos2Set;
    1635            0 :         pos2 = m_pos2;
    1636            0 :         sva2 = m_sva2;
    1637            0 :         pos3Set = m_pos3Set;
    1638            0 :         pos3 = m_pos3;
    1639            0 :         sva3 = m_sva3;
    1640              :     }
    1641              : 
    1642            0 :     return 0;
    1643              : }
    1644              : 
    1645              : } // namespace app
    1646              : } // namespace MagAOX
    1647              : 
    1648              : #endif // pi335Ctrl_hpp
        

Generated by: LCOV version 2.0-1