LCOV - code coverage report
Current view: top level - apps/zaberLowLevel - zaberStage.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 41.2 % 568 234
Test Date: 2026-04-15 19:34:29 Functions: 43.1 % 65 28

            Line data    Source code
       1              : /** \file zaberStage.hpp
       2              :  * \brief A class with details of a single zaber stage
       3              :  *
       4              :  * \ingroup zaberLowLevel_files
       5              :  */
       6              : 
       7              : #ifndef zaberStage_hpp
       8              : #define zaberStage_hpp
       9              : 
      10              : #include <iostream>
      11              : 
      12              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      13              : 
      14              : #include "za_serial.h"
      15              : 
      16              : namespace MagAOX
      17              : {
      18              : namespace app
      19              : {
      20              : 
      21              : /// A class to manage the details of one stage in a Zaber system.
      22              : /**
      23              :  * \ingroup zaberLowLevel
      24              :  */
      25              : template <class parentT>
      26              : class zaberStage
      27              : {
      28              :   protected:
      29              :     parentT *m_parent{ nullptr }; // The parent MagAOXApp
      30              : 
      31              :     std::string m_name; ///< The stage's name.
      32              : 
      33              :     std::string m_serial; ///< The stage's serial number.
      34              : 
      35              :     int m_deviceAddress{ -1 }; ///< The device's address, a.k.a. its order in the chain
      36              : 
      37              :     int m_axisNumber{ 0 }; ///< The axis number at the address (normally 0 in MagAO-X)
      38              : 
      39              :     bool m_commandStatus{ true }; ///< The status of the last command sent. true = OK, false = RJ (rejected)
      40              : 
      41              :     char m_deviceStatus{
      42              :         'U' }; ///< Current status.  Either 'I' for IDLE or 'B' for BUSY.  Intializes to 'U' for UNKOWN.
      43              : 
      44              :     bool m_homing{ false };
      45              : 
      46              :     timespec m_lastHomed{ 0, 0 }; ///< Time stamp of the last time the stage was homed
      47              : 
      48              :     bool m_parked{ false };
      49              : 
      50              :     long m_rawPos; ///< The raw position reported by the device, in microsteps.
      51              : 
      52              :     long m_tgtPos{ 0 }; ///< The tgt position last sent to the device, in microsteps.
      53              : 
      54              :     long m_maxPos; ///< The max position allowed for the device, set by config.  Will be set to no larger m_maxPosHW.
      55              : 
      56              :     float m_temp{ -999 }; ///< The driver temperature, in C.
      57              : 
      58              :     bool m_knobEnabled{ false };
      59              : 
      60              :     bool m_ledEnabled{ false };
      61              : 
      62              :     bool m_warn{ false };
      63              : 
      64              :     bool m_warnFD{ false };
      65              :     bool m_warnFDreported{ false };
      66              :     bool m_warnFQ{ false };
      67              :     bool m_warnFQreported{ false };
      68              :     bool m_warnFS{ false };
      69              :     bool m_warnFSreported{ false };
      70              :     bool m_warnFT{ false };
      71              :     bool m_warnFTreported{ false };
      72              :     bool m_warnFB{ false };
      73              :     bool m_warnFBreported{ false };
      74              :     bool m_warnFP{ false };
      75              :     bool m_warnFPreported{ false };
      76              :     bool m_warnFE{ false };
      77              :     bool m_warnFEreported{ false };
      78              :     bool m_warnWH{ false };
      79              :     bool m_warnWHreported{ false };
      80              :     bool m_warnWL{ false };
      81              :     bool m_warnWLreported{ false };
      82              :     bool m_warnWP{ false };
      83              :     bool m_warnWPreported{ false };
      84              :     bool m_warnWV{ false };
      85              :     bool m_warnWVreported{ false };
      86              :     bool m_warnWT{ false };
      87              :     bool m_warnWTreported{ false };
      88              :     bool m_warnWM{ false };
      89              :     bool m_warnWMreported{ false };
      90              :     bool m_warnWR{ false };
      91              :     bool m_warnWRreported{ false };
      92              :     bool m_warnNC{ false };
      93              :     bool m_warnNCreported{ false };
      94              :     bool m_warnNI{ false };
      95              :     bool m_warnNIreported{ false };
      96              :     bool m_warnND{ false };
      97              :     bool m_warnNDreported{ false };
      98              :     bool m_warnNU{ false };
      99              :     bool m_warnNUreported{ false };
     100              :     bool m_warnNJ{ false };
     101              :     bool m_warnNJreported{ false };
     102              :     bool m_warnUNK{ false };
     103              : 
     104              :   public:
     105              :     zaberStage() = delete;
     106              : 
     107           13 :     zaberStage( parentT *parent )
     108           13 :     {
     109           13 :         if( parent == nullptr )
     110              :         {
     111            0 :             throw mx::exception( mx::error_t::invalidarg, "parent was null on construction of zaberState" );
     112              :         }
     113              : 
     114           13 :         m_parent = parent;
     115           13 :     }
     116              : 
     117              :     /// Get the device name
     118              :     /**
     119              :      * \returns the current value of m_name
     120              :      */
     121              :     std::string name();
     122              : 
     123              :     /// Set the device name
     124              :     /**
     125              :      * \returns 0 on success
     126              :      * \returns -1 on error
     127              :      */
     128              :     int name( const std::string &n /**< [in] the new device name*/ );
     129              : 
     130              :     /// Get the device serial  number
     131              :     /**
     132              :      * \returns the current value of m_serial
     133              :      */
     134              :     std::string serial();
     135              : 
     136              :     /// Set the device serial
     137              :     /**
     138              :      * \returns 0 on success
     139              :      * \returns -1 on error
     140              :      */
     141              :     int serial( const std::string &s /**< [in] the new device serial*/ );
     142              : 
     143              :     /// Get the device address
     144              :     /**
     145              :      * \returns the current value of m_deviceAddress
     146              :      */
     147              :     int deviceAddress();
     148              : 
     149              :     /// Set the device address
     150              :     /**
     151              :      * \returns 0 on success
     152              :      * \returns -1 on error
     153              :      */
     154              :     int deviceAddress( const int &da /**< [in] the new device address*/ );
     155              : 
     156              :     /// Get the axis number
     157              :     /**
     158              :      * \returns the current value of m_axisNumber
     159              :      */
     160              :     int axisNumber();
     161              : 
     162              :     /// Set the axis number
     163              :     /**
     164              :      * \returns 0 on success
     165              :      * \returns -1 on error
     166              :      */
     167              :     int axisNumber( const int &an /**< [in] the new axis number */ );
     168              : 
     169              :     /// Get the command status
     170              :     /**
     171              :      * \returns the current value of m_commandStatus
     172              :      */
     173              :     bool commandStatus();
     174              : 
     175              :     /// Get the device status
     176              :     /**
     177              :      * \returns the current value of m_deviceStatus
     178              :      */
     179              :     char deviceStatus();
     180              : 
     181              :     /// Get the homing status
     182              :     /**
     183              :      * \returns the current value of m_homing
     184              :      */
     185              :     bool homing();
     186              : 
     187              :     /// Get the time of last homing
     188              :     /**
     189              :      * \returns the current value of m_lastHomed
     190              :      */
     191              :     time_t lastHomed();
     192              : 
     193              :     /// Get the parked status
     194              :     /**
     195              :      * \returns the current value of m_parked
     196              :      */
     197              :     int parked();
     198              : 
     199              :     /// Get the knob status
     200              :     /**
     201              :      * \returns the current value of m_knobEnabled
     202              :      */
     203              :     bool knobEnabled();
     204              : 
     205              :     /// Get the LED status
     206              :     /**
     207              :      * \returns the current value of m_ledEnabled
     208              :      */
     209              :     bool ledEnabled();
     210              : 
     211              :     /// Get the current raw position, in counts
     212              :     /**
     213              :      * \returns the current value of m_rawPos
     214              :      */
     215              :     long rawPos();
     216              : 
     217              :     /// Get the current tgt position, in counts
     218              :     /**
     219              :      * \returns the current value of m_tgtPos
     220              :      */
     221              :     long tgtPos();
     222              : 
     223              :     /// Get the max position, in counts
     224              :     /**
     225              :      * \returns the current value of m_maxPos
     226              :      */
     227              :     long maxPos();
     228              : 
     229              :     /// Get the status of the warning flag
     230              :     /**
     231              :      * \returns the current value of m_warn
     232              :      */
     233              :     bool warn();
     234              : 
     235              :     /// Get the temperature, in C
     236              :     /**
     237              :      * \returns the current value of m_temp
     238              :      */
     239              :     float temp();
     240              : 
     241              :     /// Get the warning state
     242              :     /**
     243              :      * \returns the true if any warning flags are set.
     244              :      */
     245              :     bool warningState();
     246              : 
     247              :     bool warnFD();
     248              :     bool warnFQ();
     249              :     bool warnFS();
     250              :     bool warnFT();
     251              :     bool warnFB();
     252              :     bool warnFP();
     253              :     bool warnFE();
     254              :     bool warnWH();
     255              :     bool warnWL();
     256              :     bool warnWP();
     257              :     bool warnWV();
     258              :     bool warnWT();
     259              :     bool warnWM();
     260              :     bool warnWR();
     261              :     bool warnNC();
     262              :     bool warnNI();
     263              :     bool warnND();
     264              :     bool warnNU();
     265              :     bool warnNJ();
     266              :     bool warnUNK();
     267              : 
     268              :     /// Get a response from the device, after a command has been sent.
     269              :     /** Parses the standard parts of the response in this stage's fields,
     270              :      * and extracts the response string.
     271              :      *
     272              :      * \returns 0 on success.
     273              :      * \returns -1 on error.
     274              :      */
     275              :     int getResponse( std::string       &response, ///< [out]  the text response
     276              :                      const std::string &repBuff   ///< [in] the reply buffer, not decoded.
     277              :     );
     278              : 
     279              :     /// Get a response from the device, after a command has been sent.
     280              :     /** Parses the standard parts of the response in this stages fields,
     281              :      * and extracts the response string.
     282              :      *
     283              :      * \overload
     284              :      *
     285              :      * \returns 0 on success.
     286              :      * \returns -1 on error.
     287              :      */
     288              :     int getResponse( std::string    &response, ///< [out] the text response component
     289              :                      const za_reply &rep       ///< [in] the decodedstage reply
     290              :     );
     291              : 
     292              :     /// Determine whether a decoded message is the awaited command reply.
     293              :     bool isCommandReply( const za_reply &rep /**< [in] the decoded device message */ );
     294              : 
     295              :     /// Send a command and get the response
     296              :     int sendCommand( std::string       &response, ///< [out] the response received from the stage
     297              :                      z_port             port,     ///< [in]  the port with which to communicate
     298              :                      const std::string &command   ///< [in] the command to send
     299              :     );
     300              : 
     301              :     /// Send a command for which no response is expected
     302              :     int sendCommand( z_port             port,   /**< [in] the port with which to communicate */
     303              :                      const std::string &command /**< [in] the command to send */
     304              :     );
     305              : 
     306              :     /// Get a value for this device
     307              :     /** Sends the command specified and converts the response to the specified type.
     308              :      *
     309              :      * The command passed should include `get` if needed.
     310              :      *
     311              :      * \tparam valT is the value type to convert to.
     312              :      */
     313              :     template <typename valT>
     314              :     int getValue( valT             &val,    /**< [out] the value to update */
     315              :                   z_port            port,   /**< [in] the port with which to communicate */
     316              :                   const std::string command /**< [in] the command to send */
     317              :     );
     318              : 
     319              :     /// Get the maximum position from the stage
     320              :     int getMaxPos( z_port port /**< [in] the port with which to communicate */ );
     321              : 
     322              :     /// Get the parked state from the stage
     323              :     int getParked( z_port port /**< [in] the port with which to communicate */ );
     324              : 
     325              :     int getKnob( z_port port /**< [in] the port with which to communicate */ );
     326              : 
     327              :     int getLED( z_port port /**< [in] the port with which to communicate */ );
     328              : 
     329              :     /// Update the position of the stage
     330              :     int updatePos( z_port port /**< [in] the port with which to communicate */ );
     331              : 
     332              :     /// Update the stage temperature
     333              :     int updateTemp( z_port port /**< [in] the port with which to communicate */ );
     334              : 
     335              :     /// Enable/Disable the knob
     336              :     int enableKnob( z_port port, bool enable );
     337              : 
     338              :     // Enable/disable LED
     339              :     int enableLED( z_port port, bool enable );
     340              : 
     341              :     /// Stop the stage
     342              :     int stop( z_port port );
     343              : 
     344              :     /// Emergency stop the stage
     345              :     int estop( z_port port );
     346              : 
     347              :     /// Initiate homing
     348              :     int home( z_port port /**< [in] the port with which to communicate */ );
     349              : 
     350              :     /// Park the stage
     351              :     int park( z_port port /**< [in] the port with which to communicate */ );
     352              : 
     353              :     /// Unpark the stage
     354              :     int unpark( z_port port /**< [in] the port with which to communicate */ );
     355              : 
     356              :     /// Move to a new absolute position
     357              :     int moveAbs( z_port port,  ///< [in] the port with which to communicate
     358              :                  long   rawPos ///< [in] the position to move to, in counts
     359              :     );
     360              : 
     361              :     /// Sets all warning flags to false
     362              :     /** This is not the same as clearing warnings on the device, this is just used for
     363              :      * bookkeeping.
     364              :      *
     365              :      * \returns 0 on success (always)
     366              :      */
     367              :     int unsetWarnings();
     368              : 
     369              :     /// Process a single warning from the device, setting the appropriate flag.
     370              :     /** Warnings are two ASCII characeters, e.g. "WR".
     371              :      *
     372              :      * \returns 0 if the warning is processed, including if it's not recognized.
     373              :      * \returns -1 on an error, currently not possible.
     374              :      */
     375              :     int processWarning( std::string &warn /**< [in] the two-character warning flag */ );
     376              : 
     377              :     /// Parse the warning response from the device.
     378              :     /** Sends each warning flag to processWarning.
     379              :      *
     380              :      * \returns 0 on success
     381              :      * \returns -1 on error
     382              :      */
     383              :     int parseWarnings( std::string &response /**< [in] the response from the warnings query*/ );
     384              : 
     385              :     /// Get warnings from the device
     386              :     /** Log entries will be made and flags will be set in this structure.
     387              :      *
     388              :      * \returns 0 on success
     389              :      * \returns -1 on error.
     390              :      */
     391              :     int getWarnings( z_port port /**< [in] the port with which to communicate */ );
     392              : 
     393              :     /// Clear all state so that when the system is powered back on we get the correct new state.
     394              :     int onPowerOff();
     395              : 
     396              :     int writeStateFile( std::ofstream &fout /**< [in] an open ofstream to write to */ );
     397              : 
     398              :     int readStateFile( std::ifstream &fin /**< [in] an open ofstream to write to */ );
     399              : };
     400              : 
     401              : template <class parentT>
     402            0 : std::string zaberStage<parentT>::name()
     403              : {
     404            0 :     return m_name;
     405              : }
     406              : 
     407              : template <class parentT>
     408            1 : int zaberStage<parentT>::name( const std::string &n )
     409              : {
     410            1 :     m_name = n;
     411            1 :     return 0;
     412              : }
     413              : 
     414              : template <class parentT>
     415            0 : std::string zaberStage<parentT>::serial()
     416              : {
     417            0 :     return m_serial;
     418              : }
     419              : 
     420              : template <class parentT>
     421            1 : int zaberStage<parentT>::serial( const std::string &s )
     422              : {
     423            1 :     m_serial = s;
     424            1 :     return 0;
     425              : }
     426              : 
     427              : template <class parentT>
     428            0 : int zaberStage<parentT>::deviceAddress()
     429              : {
     430            0 :     return m_deviceAddress;
     431              : }
     432              : 
     433              : template <class parentT>
     434            4 : int zaberStage<parentT>::deviceAddress( const int &da )
     435              : {
     436            4 :     m_deviceAddress = da;
     437            4 :     return 0;
     438              : }
     439              : 
     440              : template <class parentT>
     441              : int zaberStage<parentT>::axisNumber()
     442              : {
     443              :     return m_axisNumber;
     444              : }
     445              : 
     446              : template <class parentT>
     447              : int zaberStage<parentT>::axisNumber( const int &an )
     448              : {
     449              :     m_axisNumber = an;
     450              :     return 0;
     451              : }
     452              : 
     453              : template <class parentT>
     454              : bool zaberStage<parentT>::commandStatus()
     455              : {
     456              :     return m_commandStatus;
     457              : }
     458              : 
     459              : template <class parentT>
     460            0 : char zaberStage<parentT>::deviceStatus()
     461              : {
     462            0 :     return m_deviceStatus;
     463              : }
     464              : 
     465              : template <class parentT>
     466            0 : bool zaberStage<parentT>::homing()
     467              : {
     468            0 :     return m_homing;
     469              : }
     470              : 
     471              : template <class parentT>
     472            0 : time_t zaberStage<parentT>::lastHomed()
     473              : {
     474            0 :     return m_lastHomed.tv_sec;
     475              : }
     476              : 
     477              : template <class parentT>
     478            0 : int zaberStage<parentT>::parked()
     479              : {
     480            0 :     return m_parked;
     481              : }
     482              : 
     483              : template <class parentT>
     484            0 : bool zaberStage<parentT>::knobEnabled()
     485              : {
     486            0 :     return m_knobEnabled;
     487              : }
     488              : 
     489              : template <class parentT>
     490            0 : bool zaberStage<parentT>::ledEnabled()
     491              : {
     492            0 :     return m_ledEnabled;
     493              : }
     494              : 
     495              : template <class parentT>
     496            0 : long zaberStage<parentT>::rawPos()
     497              : {
     498            0 :     return m_rawPos;
     499              : }
     500              : 
     501              : template <class parentT>
     502            0 : long zaberStage<parentT>::tgtPos()
     503              : {
     504            0 :     return m_tgtPos;
     505              : }
     506              : 
     507              : template <class parentT>
     508            0 : long zaberStage<parentT>::maxPos()
     509              : {
     510            0 :     return m_maxPos;
     511              : }
     512              : 
     513              : template <class parentT>
     514            0 : bool zaberStage<parentT>::warn()
     515              : {
     516            0 :     return m_warn;
     517              : }
     518              : 
     519              : template <class parentT>
     520            0 : float zaberStage<parentT>::temp()
     521              : {
     522            0 :     return m_temp;
     523              : }
     524              : 
     525              : template <class parentT>
     526            6 : bool zaberStage<parentT>::warningState()
     527              : {
     528            6 :     return m_warn;
     529              : }
     530              : 
     531              : template <class parentT>
     532            6 : bool zaberStage<parentT>::warnFD()
     533              : {
     534            6 :     return m_warnFD;
     535              : }
     536              : 
     537              : template <class parentT>
     538            6 : bool zaberStage<parentT>::warnFQ()
     539              : {
     540            6 :     return m_warnFQ;
     541              : }
     542              : 
     543              : template <class parentT>
     544            6 : bool zaberStage<parentT>::warnFS()
     545              : {
     546            6 :     return m_warnFS;
     547              : }
     548              : 
     549              : template <class parentT>
     550            6 : bool zaberStage<parentT>::warnFT()
     551              : {
     552            6 :     return m_warnFT;
     553              : }
     554              : 
     555              : template <class parentT>
     556            6 : bool zaberStage<parentT>::warnFB()
     557              : {
     558            6 :     return m_warnFB;
     559              : }
     560              : 
     561              : template <class parentT>
     562            6 : bool zaberStage<parentT>::warnFP()
     563              : {
     564            6 :     return m_warnFP;
     565              : }
     566              : 
     567              : template <class parentT>
     568            6 : bool zaberStage<parentT>::warnFE()
     569              : {
     570            6 :     return m_warnFE;
     571              : }
     572              : 
     573              : template <class parentT>
     574            6 : bool zaberStage<parentT>::warnWH()
     575              : {
     576            6 :     return m_warnWH;
     577              : }
     578              : 
     579              : template <class parentT>
     580            6 : bool zaberStage<parentT>::warnWL()
     581              : {
     582            6 :     return m_warnWL;
     583              : }
     584              : 
     585              : template <class parentT>
     586            6 : bool zaberStage<parentT>::warnWP()
     587              : {
     588            6 :     return m_warnWP;
     589              : }
     590              : 
     591              : template <class parentT>
     592            6 : bool zaberStage<parentT>::warnWV()
     593              : {
     594            6 :     return m_warnWV;
     595              : }
     596              : 
     597              : template <class parentT>
     598            6 : bool zaberStage<parentT>::warnWT()
     599              : {
     600            6 :     return m_warnWT;
     601              : }
     602              : 
     603              : template <class parentT>
     604            6 : bool zaberStage<parentT>::warnWM()
     605              : {
     606            6 :     return m_warnWM;
     607              : }
     608              : 
     609              : template <class parentT>
     610            7 : bool zaberStage<parentT>::warnWR()
     611              : {
     612            7 :     return m_warnWR;
     613              : }
     614              : 
     615              : template <class parentT>
     616            6 : bool zaberStage<parentT>::warnNC()
     617              : {
     618            6 :     return m_warnNC;
     619              : }
     620              : 
     621              : template <class parentT>
     622            6 : bool zaberStage<parentT>::warnNI()
     623              : {
     624            6 :     return m_warnNI;
     625              : }
     626              : 
     627              : template <class parentT>
     628            6 : bool zaberStage<parentT>::warnND()
     629              : {
     630            6 :     return m_warnND;
     631              : }
     632              : 
     633              : template <class parentT>
     634            6 : bool zaberStage<parentT>::warnNU()
     635              : {
     636            6 :     return m_warnNU;
     637              : }
     638              : 
     639              : template <class parentT>
     640            6 : bool zaberStage<parentT>::warnNJ()
     641              : {
     642            6 :     return m_warnNJ;
     643              : }
     644              : 
     645              : template <class parentT>
     646            8 : bool zaberStage<parentT>::warnUNK()
     647              : {
     648            8 :     return m_warnUNK;
     649              : }
     650              : 
     651              : template <class parentT>
     652              : int zaberStage<parentT>::getResponse( std::string &response, const std::string &repBuff )
     653              : {
     654              :     za_reply rep;
     655              :     int      rv = za_decode( &rep, repBuff.c_str(), repBuff.size() );
     656              :     if( rv != Z_SUCCESS )
     657              :     {
     658              :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     659              :         {
     660              :             return rv; // don't log, but propagate error
     661              :         }
     662              : 
     663              :         MagAOXAppT::log<software_error>( { rv, "za_decode !=Z_SUCCESS" } );
     664              :         return rv;
     665              :     }
     666              : 
     667              :     return getResponse( response, rep );
     668              : }
     669              : 
     670              : template <class parentT>
     671            0 : int zaberStage<parentT>::getResponse( std::string &response, const za_reply &rep )
     672              : {
     673            0 :     if( rep.device_address == m_deviceAddress )
     674              :     {
     675            0 :         if( rep.reply_flags[0] == 'O' )
     676              :         {
     677            0 :             m_commandStatus = true;
     678              :         }
     679              :         else
     680              :         {
     681            0 :             m_commandStatus = false;
     682              :         }
     683              : 
     684            0 :         m_deviceStatus = rep.device_status[0];
     685              : 
     686            0 :         if( m_deviceStatus == 'I' && m_homing )
     687              :         {
     688            0 :             m_warnWR = false; // Clear preemptively
     689            0 :             m_homing = false;
     690            0 :             if( clock_gettime( CLOCK_ISIO, &m_lastHomed ) < 0 )
     691              :             {
     692            0 :                 MagAOXAppT::log<software_error>( { errno, 0, "clock_gettime for last homed" } );
     693              :             }
     694              :         }
     695              : 
     696            0 :         if( rep.warning_flags[0] == '-' )
     697              :         {
     698            0 :             unsetWarnings();
     699              :         }
     700              :         else
     701              :         {
     702            0 :             m_warn = true;
     703              :         }
     704              : 
     705            0 :         response = rep.response_data;
     706              : 
     707            0 :         return 0;
     708              :     }
     709              :     else
     710              :     {
     711            0 :         MagAOXAppT::log<software_error>( "wrong device" );
     712            0 :         return -1;
     713              :     }
     714              : }
     715              : 
     716              : template <class parentT>
     717            4 : bool zaberStage<parentT>::isCommandReply( const za_reply &rep )
     718              : {
     719            4 :     return rep.message_type == '@' && rep.device_address == m_deviceAddress;
     720              : }
     721              : 
     722              : template <class parentT>
     723            0 : int zaberStage<parentT>::sendCommand( std::string &response, z_port port, const std::string &command )
     724              : {
     725            0 :     za_send( port, command.c_str(), command.size() );
     726              : 
     727              :     char buff[256];
     728              : 
     729            0 :     while( 1 )
     730              :     {
     731            0 :         int rv = za_receive( port, buff, sizeof( buff ) );
     732              : 
     733            0 :         if( rv == Z_ERROR_TIMEOUT )
     734              :         {
     735            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     736              :             {
     737            0 :                 return rv; // don't log, but propagate error
     738              :             }
     739              : 
     740            0 :             MagAOXAppT::log<software_error>( "Z_ERROR_TIMEOUT" );
     741            0 :             break; // assume error and just get out.
     742              :         }
     743            0 :         else if( rv < 0 )
     744              :         {
     745            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     746              :             {
     747            0 :                 return rv; // don't log, but propagate error
     748              :             }
     749              : 
     750            0 :             MagAOXAppT::log<software_error>( { 0, "za_receive !=Z_SUCCESS" } );
     751            0 :             break;
     752              :         }
     753              :         za_reply rep;
     754              : 
     755            0 :         rv = za_decode( &rep, buff, sizeof( buff ) );
     756            0 :         if( rv != Z_SUCCESS )
     757              :         {
     758            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     759              :             {
     760            0 :                 return rv; // don't log, but propagate error
     761              :             }
     762              : 
     763            0 :             MagAOXAppT::log<software_error>( "za_decode !=Z_SUCCESS" );
     764            0 :             break;
     765              :         }
     766              : 
     767            0 :         if( isCommandReply( rep ) )
     768            0 :             return getResponse( response, rep );
     769              :     }
     770              : 
     771            0 :     response = "";
     772              : 
     773            0 :     return -1;
     774              : }
     775              : 
     776              : template <class parentT>
     777              : template <typename valT>
     778            0 : int zaberStage<parentT>::getValue( valT &val, z_port port, const std::string command )
     779              : {
     780            0 :     if( m_deviceAddress < 1 )
     781              :     {
     782            0 :         return MagAOXAppT::log<software_error, -1>(
     783            0 :             { std::format( "stage {} with s/n {} not found in system.", m_name, m_serial ) } );
     784              :     }
     785              : 
     786            0 :     std::string response;
     787              : 
     788            0 :     int rv = sendCommand( response, port, std::format( "/{} {}", m_deviceAddress, command ) );
     789              : 
     790            0 :     if( rv == 0 )
     791              :     {
     792            0 :         if( m_commandStatus )
     793              :         {
     794              :             mx::error_t errc;
     795              : 
     796            0 :             val = mx::ioutils::stoT<valT>( response, &errc );
     797              : 
     798            0 :             if( !!errc )
     799              :             {
     800            0 :                 return MagAOXAppT::log<software_error, -1>(
     801            0 :                     std::format( "parsing [{}] response from {}: {}", command, m_name, mx::errorMessage( errc ) ) );
     802              :             }
     803              : 
     804            0 :             return 0;
     805              :         }
     806              :         else
     807              :         {
     808            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     809              :             {
     810            0 :                 return -1; // don't log, but propagate error
     811              :             }
     812              : 
     813            0 :             MagAOXAppT::log<software_error>( { rv, command + "command Rejected" } );
     814            0 :             return -1;
     815              :         }
     816              :     }
     817              :     else
     818              :     {
     819            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     820              :         {
     821            0 :             return -1; // don't log, but propagate error
     822              :         }
     823              : 
     824            0 :         MagAOXAppT::log<software_error>();
     825              : 
     826            0 :         return -1;
     827              :     }
     828            0 : }
     829              : 
     830              : template <class parentT>
     831            0 : int zaberStage<parentT>::getMaxPos( z_port port )
     832              : {
     833            0 :     int rv = getValue( m_maxPos, port, "get limit.max" );
     834              : 
     835            0 :     if( rv < 0 )
     836              :     {
     837            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     838              :         {
     839            0 :             return -1; // don't log, but propagate error
     840              :         }
     841              : 
     842            0 :         return MagAOXAppT::log<software_error, -1>();
     843              :     }
     844              : 
     845            0 :     return 0;
     846              : }
     847              : 
     848              : template <class parentT>
     849            0 : int zaberStage<parentT>::getParked( z_port port )
     850              : {
     851            0 :     int rv = getValue( m_parked, port, "tools parking state" );
     852              : 
     853            0 :     if( rv < 0 )
     854              :     {
     855            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     856              :         {
     857            0 :             return -1; // don't log, but propagate error
     858              :         }
     859              : 
     860            0 :         return MagAOXAppT::log<software_error, -1>();
     861              :     }
     862              : 
     863            0 :     return 0;
     864              : }
     865              : 
     866              : template <class parentT>
     867            0 : int zaberStage<parentT>::getKnob( z_port port )
     868              : {
     869            0 :     int rv = getValue( m_knobEnabled, port, "get knob.enable" );
     870              : 
     871            0 :     if( rv < 0 )
     872              :     {
     873            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     874              :         {
     875            0 :             return -1; // don't log, but propagate error
     876              :         }
     877              : 
     878            0 :         return MagAOXAppT::log<software_error, -1>();
     879              :     }
     880              : 
     881            0 :     return 0;
     882              : }
     883              : 
     884              : template <class parentT>
     885            0 : int zaberStage<parentT>::getLED( z_port port )
     886              : {
     887            0 :     int rv = getValue( m_ledEnabled, port, "get system.led.enable" );
     888              : 
     889            0 :     if( rv < 0 )
     890              :     {
     891            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     892              :         {
     893            0 :             return -1; // don't log, but propagate error
     894              :         }
     895              : 
     896            0 :         return MagAOXAppT::log<software_error, -1>();
     897              :     }
     898              : 
     899            0 :     return 0;
     900              : }
     901              : 
     902              : template <class parentT>
     903            0 : int zaberStage<parentT>::updatePos( z_port port )
     904              : {
     905            0 :     int rv = getValue( m_rawPos, port, "get pos" );
     906              : 
     907            0 :     if( rv < 0 )
     908              :     {
     909            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     910              :         {
     911            0 :             return -1; // don't log, but propagate error
     912              :         }
     913              : 
     914            0 :         return MagAOXAppT::log<software_error, -1>();
     915              :     }
     916              : 
     917            0 :     return 0;
     918              : }
     919              : 
     920              : template <class parentT>
     921            0 : int zaberStage<parentT>::updateTemp( z_port port )
     922              : {
     923            0 :     int rv = getValue( m_temp, port, "get driver.temperature" );
     924              : 
     925            0 :     if( rv < 0 )
     926              :     {
     927            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     928              :         {
     929            0 :             return -1; // don't log, but propagate error
     930              :         }
     931              : 
     932            0 :         return MagAOXAppT::log<software_error, -1>();
     933              :     }
     934              : 
     935            0 :     return 0;
     936              : }
     937              : 
     938              : template <class parentT>
     939            0 : int zaberStage<parentT>::sendCommand( z_port port, const std::string &command )
     940              : {
     941            0 :     if( m_deviceAddress < 1 )
     942              :     {
     943            0 :         return MagAOXAppT::log<software_error, -1>(
     944            0 :             std::format( "stage {} with s/n {} not found in system.", m_name, m_serial ) );
     945              :     }
     946              : 
     947            0 :     std::string response;
     948              : 
     949            0 :     int rv = sendCommand( response, port, std::format( "/{} {}", m_deviceAddress, command ) );
     950              : 
     951            0 :     if( rv == 0 )
     952              :     {
     953            0 :         if( m_commandStatus )
     954              :         {
     955            0 :             return 0;
     956              :         }
     957              :         else
     958              :         {
     959            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     960              :             {
     961            0 :                 return -1; // don't log, but propagate error
     962              :             }
     963              : 
     964            0 :             return MagAOXAppT::log<software_error, -1>(
     965            0 :                 { rv, std::format( "{} {} commmand rejected", m_name, command ) } );
     966              :         }
     967              :     }
     968              :     else
     969              :     {
     970            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     971              :         {
     972            0 :             return -1; // don't log, but propagate error
     973              :         }
     974              : 
     975            0 :         MagAOXAppT::log<software_error>();
     976            0 :         return -1;
     977              :     }
     978            0 : }
     979              : 
     980              : template <class parentT>
     981            0 : int zaberStage<parentT>::enableKnob( z_port port, bool enable )
     982              : {
     983            0 :     std::string cmd = std::format( "set knob.enable {}", enable ? "1" : "0" );
     984              : 
     985            0 :     int rv = sendCommand( port, cmd );
     986              : 
     987            0 :     if( rv < 0 )
     988              :     {
     989            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
     990              :         {
     991            0 :             return -1; // don't log, but propagate error
     992              :         }
     993              : 
     994            0 :         return MagAOXAppT::log<software_error, -1>();
     995              :     }
     996              : 
     997            0 :     return 0;
     998            0 : }
     999              : 
    1000              : template <class parentT>
    1001            0 : int zaberStage<parentT>::enableLED( z_port port, bool enable )
    1002              : {
    1003            0 :     std::string cmd = std::format( "set system.led.enable {}", enable ? "1" : "0" );
    1004              : 
    1005            0 :     int rv = sendCommand( port, cmd );
    1006              : 
    1007            0 :     if( rv < 0 )
    1008              :     {
    1009            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1010              :         {
    1011            0 :             return -1; // don't log, but propagate error
    1012              :         }
    1013              : 
    1014            0 :         return MagAOXAppT::log<software_error, -1>();
    1015              :     }
    1016              : 
    1017            0 :     return 0;
    1018            0 : }
    1019              : 
    1020              : template <class parentT>
    1021            0 : int zaberStage<parentT>::stop( z_port port )
    1022              : {
    1023            0 :     int rv = sendCommand( port, "stop" );
    1024              : 
    1025            0 :     if( rv < 0 )
    1026              :     {
    1027            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1028              :         {
    1029            0 :             return -1; // don't log, but propagate error
    1030              :         }
    1031              : 
    1032            0 :         return MagAOXAppT::log<software_error, -1>();
    1033              :     }
    1034              : 
    1035            0 :     return 0;
    1036              : }
    1037              : 
    1038              : template <class parentT>
    1039            0 : int zaberStage<parentT>::estop( z_port port )
    1040              : {
    1041            0 :     int rv = sendCommand( port, "estop" );
    1042              : 
    1043            0 :     if( rv < 0 )
    1044              :     {
    1045            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1046              :         {
    1047            0 :             return -1; // don't log, but propagate error
    1048              :         }
    1049              : 
    1050            0 :         return MagAOXAppT::log<software_error, -1>();
    1051              :     }
    1052              : 
    1053            0 :     return 0;
    1054              : }
    1055              : 
    1056              : template <class parentT>
    1057            0 : int zaberStage<parentT>::home( z_port port )
    1058              : {
    1059            0 :     int rv = sendCommand( port, "home" );
    1060              : 
    1061            0 :     if( rv < 0 )
    1062              :     {
    1063            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1064              :         {
    1065            0 :             return -1; // don't log, but propagate error
    1066              :         }
    1067              : 
    1068            0 :         return MagAOXAppT::log<software_error, -1>();
    1069              :     }
    1070              : 
    1071            0 :     m_homing = true;
    1072            0 :     m_tgtPos = 0;
    1073            0 :     return 0;
    1074              : }
    1075              : 
    1076              : template <class parentT>
    1077            0 : int zaberStage<parentT>::park( z_port port )
    1078              : {
    1079            0 :     int rv = sendCommand( port, "tools parking park" );
    1080              : 
    1081            0 :     if( rv < 0 )
    1082              :     {
    1083            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1084              :         {
    1085            0 :             return -1; // don't log, but propagate error
    1086              :         }
    1087              : 
    1088            0 :         return MagAOXAppT::log<software_error, -1>();
    1089              :     }
    1090              : 
    1091            0 :     m_parked = true;
    1092              : 
    1093            0 :     return 0;
    1094              : }
    1095              : 
    1096              : template <class parentT>
    1097            0 : int zaberStage<parentT>::unpark( z_port port )
    1098              : {
    1099            0 :     int rv = sendCommand( port, "tools parking unpark" );
    1100              : 
    1101            0 :     if( rv < 0 )
    1102              :     {
    1103            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1104              :         {
    1105            0 :             return -1; // don't log, but propagate error
    1106              :         }
    1107              : 
    1108            0 :         return MagAOXAppT::log<software_error, -1>();
    1109              :     }
    1110              : 
    1111            0 :     m_parked = false;
    1112            0 :     return 0;
    1113              : }
    1114              : 
    1115              : template <class parentT>
    1116            0 : int zaberStage<parentT>::moveAbs( z_port port, long rawPos )
    1117              : {
    1118            0 :     m_tgtPos = rawPos;
    1119              : 
    1120            0 :     if( m_parked )
    1121              :     {
    1122            0 :         if( unpark( port ) < 0 )
    1123              :         {
    1124            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1125              :             {
    1126            0 :                 return -1; // don't log, but propagate error
    1127              :             }
    1128              : 
    1129            0 :             return MagAOXAppT::log<software_error, -1>();
    1130              :         }
    1131              :     }
    1132              : 
    1133            0 :     int rv = sendCommand( port, std::format( "move abs {}", rawPos ) );
    1134              : 
    1135            0 :     if( rv < 0 )
    1136              :     {
    1137            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1138              :         {
    1139            0 :             return -1; // don't log, but propagate error
    1140              :         }
    1141              : 
    1142            0 :         return MagAOXAppT::log<software_error, -1>();
    1143              :     }
    1144              : 
    1145            0 :     return 0;
    1146              : }
    1147              : 
    1148              : template <class parentT>
    1149            0 : int zaberStage<parentT>::unsetWarnings()
    1150              : {
    1151            0 :     m_warn = false;
    1152              : 
    1153            0 :     m_warnFD  = false;
    1154            0 :     m_warnFQ  = false;
    1155            0 :     m_warnFS  = false;
    1156            0 :     m_warnFT  = false;
    1157            0 :     m_warnFB  = false;
    1158            0 :     m_warnFP  = false;
    1159            0 :     m_warnFE  = false;
    1160            0 :     m_warnWH  = false;
    1161            0 :     m_warnWL  = false;
    1162            0 :     m_warnWP  = false;
    1163            0 :     m_warnWV  = false;
    1164            0 :     m_warnWT  = false;
    1165            0 :     m_warnWM  = false;
    1166            0 :     m_warnWR  = false;
    1167            0 :     m_warnNC  = false;
    1168            0 :     m_warnNI  = false;
    1169            0 :     m_warnND  = false;
    1170            0 :     m_warnNU  = false;
    1171            0 :     m_warnNJ  = false;
    1172            0 :     m_warnUNK = false;
    1173              : 
    1174            0 :     return 0;
    1175              : }
    1176              : 
    1177              : template <class parentT>
    1178           21 : int zaberStage<parentT>::processWarning( std::string &warn )
    1179              : {
    1180           21 :     if( warn == "FD" )
    1181              :     {
    1182            1 :         if( !m_warnFDreported )
    1183              :         {
    1184            1 :             MagAOXAppT::log<text_log>( m_name +
    1185              :                                            " Driver Disabled (FD): The driver has disabled itself due to overheating.",
    1186              :                                        logPrio::LOG_EMERGENCY );
    1187            1 :             m_warnFDreported = true;
    1188              :         }
    1189              : 
    1190            1 :         m_warnFD = true;
    1191            1 :         return 0;
    1192              :     }
    1193           20 :     else if( warn == "FQ" )
    1194              :     {
    1195            1 :         if( !m_warnFQreported )
    1196              :         {
    1197            1 :             MagAOXAppT::log<text_log>( m_name + " warning FQ: you should probably check.", logPrio::LOG_EMERGENCY );
    1198            1 :             m_warnFQreported = true;
    1199              :         }
    1200              : 
    1201            1 :         m_warnFQ = true;
    1202            1 :         return 0;
    1203              :     }
    1204           19 :     else if( warn == "FS" )
    1205              :     {
    1206            1 :         if( !m_warnFSreported )
    1207              :         {
    1208            1 :             MagAOXAppT::log<text_log>(
    1209            1 :                 m_name + " Stalled and Stopped (FS): Stalling was detected and the axis has stopped itself. ",
    1210              :                 logPrio::LOG_WARNING );
    1211            1 :             m_warnFSreported = true;
    1212              :         }
    1213            1 :         m_warnFS = true;
    1214            1 :         return 0;
    1215              :     }
    1216           18 :     else if( warn == "FT" )
    1217              :     {
    1218            1 :         if( !m_warnFTreported )
    1219              :         {
    1220            1 :             MagAOXAppT::log<text_log>(
    1221            1 :                 m_name + " Excessive Twist (FT): The lockstep group has exceeded allowable twist and has stopped. ",
    1222              :                 logPrio::LOG_WARNING );
    1223            1 :             m_warnFTreported = true;
    1224              :         }
    1225              : 
    1226            1 :         m_warnFT = true;
    1227            1 :         return 0;
    1228              :     }
    1229           17 :     else if( warn == "FB" )
    1230              :     {
    1231            1 :         if( !m_warnFBreported )
    1232              :         {
    1233            1 :             MagAOXAppT::log<text_log>( m_name + " Stream Bounds Error (FB): A previous streamed motion could not be "
    1234              :                                                 "executed because it failed a precondition",
    1235              :                                        logPrio::LOG_WARNING );
    1236            1 :             m_warnFBreported = true;
    1237              :         }
    1238              : 
    1239            1 :         m_warnFB = true;
    1240            1 :         return 0;
    1241              :     }
    1242           16 :     else if( warn == "FP" )
    1243              :     {
    1244            1 :         if( !m_warnFPreported )
    1245              :         {
    1246            1 :             MagAOXAppT::log<text_log>(
    1247            1 :                 m_name + " Interpolated Path Deviation (FP): Streamed or sinusoidal motion was terminated because an "
    1248              :                          "axis slipped and thus the device deviated from the requested path. ",
    1249              :                 logPrio::LOG_WARNING );
    1250            1 :             m_warnFPreported = true;
    1251              :         }
    1252              : 
    1253            1 :         m_warnFP = true;
    1254            1 :         return 0;
    1255              :     }
    1256           15 :     else if( warn == "FE" )
    1257              :     {
    1258            1 :         if( !m_warnFEreported )
    1259              :         {
    1260            1 :             MagAOXAppT::log<text_log>(
    1261            1 :                 m_name + " Limit Error (FE): The target limit sensor cannot be reached or is faulty. ",
    1262              :                 logPrio::LOG_WARNING );
    1263            1 :             m_warnFEreported = true;
    1264              :         }
    1265              : 
    1266            1 :         m_warnFE = true;
    1267            1 :         return 0;
    1268              :     }
    1269           14 :     else if( warn == "WH" )
    1270              :     {
    1271            1 :         if( m_warnWHreported == false )
    1272              :         {
    1273            1 :             MagAOXAppT::log<text_log>(
    1274            1 :                 m_name + " Device not homed (WH): The device has a position reference, but has not been homed.",
    1275              :                 logPrio::LOG_WARNING );
    1276            1 :             m_warnWHreported = true;
    1277              :         }
    1278              : 
    1279            1 :         m_warnWH = true;
    1280            1 :         return 0;
    1281              :     }
    1282           13 :     else if( warn == "WL" )
    1283              :     {
    1284            1 :         if( !m_warnWLreported )
    1285              :         {
    1286            1 :             MagAOXAppT::log<text_log>( m_name + " Unexpected Limit Trigger warning (WL): A movement operation did not "
    1287              :                                                 "complete due to a triggered limit sensor.",
    1288              :                                        logPrio::LOG_WARNING );
    1289            1 :             m_warnWLreported = true;
    1290              :         }
    1291              : 
    1292            1 :         m_warnWL = true;
    1293            1 :         return 0;
    1294              :     }
    1295           12 :     else if( warn == "WP" )
    1296              :     {
    1297            1 :         if( !m_warnWPreported )
    1298              :         {
    1299            1 :             MagAOXAppT::log<text_log>(
    1300            1 :                 m_name + " Invalid calibration type (WP): The saved calibration data type is unsupported",
    1301              :                 logPrio::LOG_WARNING );
    1302            1 :             m_warnWPreported = true;
    1303              :         }
    1304              : 
    1305            1 :         m_warnWP = true;
    1306            1 :         return 0;
    1307              :     }
    1308           11 :     else if( warn == "WV" )
    1309              :     {
    1310            1 :         if( !m_warnWVreported )
    1311              :         {
    1312            1 :             MagAOXAppT::log<text_log>( m_name + " Voltage Out of Range (WV): The supply voltage is outside the "
    1313              :                                                 "recommended operating range of the device",
    1314              :                                        logPrio::LOG_WARNING );
    1315            1 :             m_warnWVreported = true;
    1316              :         }
    1317              : 
    1318            1 :         m_warnWV = true;
    1319            1 :         return 0;
    1320              :     }
    1321           10 :     else if( warn == "WT" )
    1322              :     {
    1323            1 :         if( !m_warnWTreported )
    1324              :         {
    1325            1 :             MagAOXAppT::log<text_log>( m_name + " Controller Temperature High (WT): The internal temperature of the "
    1326              :                                                 "controller has exceeded the recommended limit for the device.",
    1327              :                                        logPrio::LOG_WARNING );
    1328            1 :             m_warnWTreported = true;
    1329              :         }
    1330              : 
    1331            1 :         m_warnWT = true;
    1332            1 :         return 0;
    1333              :     }
    1334            9 :     else if( warn == "WM" )
    1335              :     {
    1336            1 :         if( m_warnWMreported == false )
    1337              :         {
    1338            1 :             MagAOXAppT::log<text_log>( m_name + " Displaced when Stationary (WM): While not in motion, the axis has "
    1339              :                                                 "been forced out of its position.",
    1340              :                                        logPrio::LOG_WARNING );
    1341            1 :             m_warnWMreported = true;
    1342              :         }
    1343              : 
    1344            1 :         m_warnWM = true;
    1345            1 :         return 0;
    1346              :     }
    1347            8 :     else if( warn == "WR" )
    1348              :     {
    1349            2 :         if( m_warnWRreported == false )
    1350              :         {
    1351            2 :             MagAOXAppT::log<text_log>(
    1352            2 :                 m_name +
    1353              :                     " No Reference Position (WR): Axis has not had a reference position established. [homing required]",
    1354              :                 logPrio::LOG_WARNING );
    1355            2 :             m_warnWRreported = true;
    1356              :         }
    1357              : 
    1358            2 :         m_warnWR = true;
    1359            2 :         return 0;
    1360              :     }
    1361            6 :     else if( warn == "NC" )
    1362              :     {
    1363            1 :         if( !m_warnNCreported )
    1364              :         {
    1365            1 :             MagAOXAppT::log<text_log>( m_name +
    1366              :                                            " Manual Control (NC): Axis is busy due to manual control via the knob.",
    1367              :                                        logPrio::LOG_WARNING );
    1368            1 :             m_warnNCreported = true;
    1369              :         }
    1370              : 
    1371            1 :         m_warnNC = true;
    1372            1 :         return 0;
    1373              :     }
    1374            5 :     else if( warn == "NI" )
    1375              :     {
    1376            1 :         if( m_homing == true || warnWR() )
    1377              :         {
    1378            0 :             return 0; // ignore this during homing
    1379              :         }
    1380              : 
    1381            1 :         if( !m_warnNIreported )
    1382              :         {
    1383            1 :             MagAOXAppT::log<text_log>( m_name + " Command Interrupted (NI): A movement operation "
    1384              :                                                 "(command or manual control) was requested "
    1385              :                                                 "while the axis was executing another movement command.",
    1386              :                                        logPrio::LOG_WARNING );
    1387            1 :             m_warnNIreported = true;
    1388              :         }
    1389              : 
    1390            1 :         m_warnNI = true;
    1391            1 :         return 0;
    1392              :     }
    1393            4 :     else if( warn == "ND" )
    1394              :     {
    1395            1 :         if( !m_warnNDreported )
    1396              :         {
    1397            1 :             MagAOXAppT::log<text_log>( m_name + " Stream Discontinuity (ND): The device has slowed down while "
    1398              :                                                 "following a "
    1399              :                                                 "streamed motion path because it has run out of queued motions.",
    1400              :                                        logPrio::LOG_WARNING );
    1401            1 :             m_warnNDreported = true;
    1402              :         }
    1403              : 
    1404            1 :         m_warnND = true;
    1405            1 :         return 0;
    1406              :     }
    1407            3 :     else if( warn == "NU" )
    1408              :     {
    1409            1 :         if( !m_warnNUreported )
    1410              :         {
    1411            1 :             MagAOXAppT::log<text_log>( m_name + " Setting Update Pending (NU): A setting is pending to be "
    1412              :                                                 "updated or a reset is pending.",
    1413              :                                        logPrio::LOG_WARNING );
    1414            1 :             m_warnNUreported = true;
    1415              :         }
    1416              : 
    1417            1 :         m_warnNU = true;
    1418            1 :         return 0;
    1419              :     }
    1420            2 :     else if( warn == "NJ" )
    1421              :     {
    1422            1 :         if( !m_warnNJreported )
    1423              :         {
    1424            1 :             MagAOXAppT::log<text_log>( m_name + " Joystick Calibrating (NJ): Joystick calibration is "
    1425              :                                                 "in progress.",
    1426              :                                        logPrio::LOG_WARNING );
    1427            1 :             m_warnNJreported = true;
    1428              :         }
    1429              : 
    1430            1 :         m_warnNJ = true;
    1431            1 :         return 0;
    1432              :     }
    1433              :     else
    1434              :     {
    1435            1 :         MagAOXAppT::log<software_warning>( std::format( "{} unknown stage warning: {}", m_name, warn ) );
    1436              : 
    1437            1 :         m_warnUNK = true;
    1438              : 
    1439            1 :         return 0;
    1440              :     }
    1441              : 
    1442              :     return -1;
    1443              : }
    1444              : 
    1445              : template <class parentT>
    1446            8 : int zaberStage<parentT>::parseWarnings( std::string &response )
    1447              : {
    1448              : 
    1449              :     size_t nwarn;
    1450              : 
    1451              :     try
    1452              :     {
    1453            8 :         nwarn = std::stoi( response.substr( 0, 2 ) );
    1454              :     }
    1455            0 :     catch( ... )
    1456              :     {
    1457            0 :         return MagAOXAppT::log<software_error, -1>( { "exception while parsing warning" } );
    1458              :     }
    1459              : 
    1460            8 :     if( nwarn > 0 )
    1461              :     {
    1462            7 :         m_warn = true;
    1463              :     }
    1464              : 
    1465           50 :     for( size_t n = 0; n < nwarn; ++n )
    1466              :     {
    1467           23 :         if( response.size() < 5 + n * 3 )
    1468              :         {
    1469            2 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1470              :             {
    1471            2 :                 return -1; // don't log, but propagate error
    1472              :             }
    1473              : 
    1474            0 :             return MagAOXAppT::log<software_error, -1>( { "parsing incomplete warning response" } );
    1475              :         }
    1476              : 
    1477           21 :         std::string warn = response.substr( 3 + n * 3, 2 );
    1478              : 
    1479           21 :         int rv = processWarning( warn );
    1480           21 :         if( rv < 0 )
    1481              :         {
    1482            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1483              :             {
    1484            0 :                 return -1; // don't log, but propagate error
    1485              :             }
    1486              : 
    1487            0 :             MagAOXAppT::log<software_error>();
    1488            0 :             return -1;
    1489              :         }
    1490              :     }
    1491              : 
    1492            6 :     if( m_warnFDreported )
    1493              :     {
    1494            1 :         if( !m_warnFD )
    1495              :         {
    1496            0 :             m_warnFDreported = false;
    1497              :         }
    1498              :     }
    1499              : 
    1500            6 :     if( m_warnFQreported )
    1501              :     {
    1502            1 :         if( !m_warnFQ )
    1503              :         {
    1504            0 :             m_warnFQreported = false;
    1505              :         }
    1506              :     }
    1507              : 
    1508            6 :     if( m_warnFSreported )
    1509              :     {
    1510            1 :         if( !m_warnFS )
    1511              :         {
    1512            0 :             m_warnFSreported = false;
    1513              :         }
    1514              :     }
    1515              : 
    1516            6 :     if( m_warnFTreported )
    1517              :     {
    1518            1 :         if( !m_warnFT )
    1519              :         {
    1520            0 :             m_warnFTreported = false;
    1521              :         }
    1522              :     }
    1523              : 
    1524            6 :     if( m_warnFBreported )
    1525              :     {
    1526            1 :         if( !m_warnFB )
    1527              :         {
    1528            0 :             m_warnFBreported = false;
    1529              :         }
    1530              :     }
    1531              : 
    1532            6 :     if( m_warnFPreported )
    1533              :     {
    1534            1 :         if( !m_warnFP )
    1535              :         {
    1536            0 :             m_warnFPreported = false;
    1537              :         }
    1538              :     }
    1539              : 
    1540            6 :     if( m_warnFEreported )
    1541              :     {
    1542            1 :         if( !m_warnFE )
    1543              :         {
    1544            0 :             m_warnFEreported = false;
    1545              :         }
    1546              :     }
    1547              : 
    1548            6 :     if( m_warnWHreported )
    1549              :     {
    1550            1 :         if( !m_warnWH )
    1551              :         {
    1552            0 :             m_warnWHreported = false;
    1553              :         }
    1554              :     }
    1555              : 
    1556            6 :     if( m_warnWLreported )
    1557              :     {
    1558            1 :         if( !m_warnWL )
    1559              :         {
    1560            0 :             m_warnWLreported = false;
    1561              :         }
    1562              :     }
    1563              : 
    1564            6 :     if( m_warnWPreported )
    1565              :     {
    1566            1 :         if( !m_warnWP )
    1567              :         {
    1568            0 :             m_warnWPreported = false;
    1569              :         }
    1570              :     }
    1571              : 
    1572            6 :     if( m_warnWVreported )
    1573              :     {
    1574            1 :         if( !m_warnWV )
    1575              :         {
    1576            0 :             m_warnWVreported = false;
    1577              :         }
    1578              :     }
    1579              : 
    1580            6 :     if( m_warnWTreported )
    1581              :     {
    1582            1 :         if( !m_warnWT )
    1583              :         {
    1584            0 :             m_warnWTreported = false;
    1585              :         }
    1586              :     }
    1587              : 
    1588            6 :     if( m_warnWMreported )
    1589              :     {
    1590            1 :         if( !m_warnWM )
    1591              :         {
    1592            0 :             m_warnWMreported = false;
    1593              :         }
    1594              :     }
    1595              : 
    1596            6 :     if( m_warnWRreported )
    1597              :     {
    1598            2 :         if( !m_warnWR )
    1599              :         {
    1600            0 :             m_warnWRreported = false;
    1601              :         }
    1602              :     }
    1603              : 
    1604            6 :     if( m_warnNCreported )
    1605              :     {
    1606            1 :         if( !m_warnNC )
    1607              :         {
    1608            0 :             m_warnNCreported = false;
    1609              :         }
    1610              :     }
    1611              : 
    1612            6 :     if( m_warnNIreported )
    1613              :     {
    1614            1 :         if( !m_warnNI )
    1615              :         {
    1616            0 :             m_warnNIreported = false;
    1617              :         }
    1618              :     }
    1619              : 
    1620            6 :     if( m_warnNDreported )
    1621              :     {
    1622            1 :         if( !m_warnND )
    1623              :         {
    1624            0 :             m_warnNDreported = false;
    1625              :         }
    1626              :     }
    1627              : 
    1628            6 :     if( m_warnNUreported )
    1629              :     {
    1630            1 :         if( !m_warnNU )
    1631              :         {
    1632            0 :             m_warnNUreported = false;
    1633              :         }
    1634              :     }
    1635              : 
    1636            6 :     if( m_warnNJreported )
    1637              :     {
    1638            1 :         if( !m_warnNJ )
    1639              :         {
    1640            0 :             m_warnNJreported = false;
    1641              :         }
    1642              :     }
    1643              : 
    1644            6 :     return 0;
    1645              : }
    1646              : 
    1647              : template <class parentT>
    1648            0 : int zaberStage<parentT>::getWarnings( z_port port )
    1649              : {
    1650            0 :     if( m_deviceAddress < 1 )
    1651              :     {
    1652            0 :         return MagAOXAppT::log<software_error, -1>(
    1653            0 :             { "stage " + m_name + " with with s/n " + m_serial + " not found in system." } );
    1654              :     }
    1655              : 
    1656            0 :     std::string response;
    1657              : 
    1658            0 :     int rv = sendCommand( response, port, std::format( "/{} warnings", m_deviceAddress ) );
    1659              : 
    1660            0 :     if( rv == 0 )
    1661              :     {
    1662            0 :         if( m_commandStatus )
    1663              :         {
    1664            0 :             unsetWarnings(); // Clear all the flags before setting them to stay current.
    1665            0 :             return parseWarnings( response );
    1666              :         }
    1667              :         else
    1668              :         {
    1669            0 :             if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1670              :             {
    1671            0 :                 return -1; // don't log, but propagate error
    1672              :             }
    1673              : 
    1674            0 :             MagAOXAppT::log<software_error>( { rv, "warnings Command Rejected" } );
    1675            0 :             return -1;
    1676              :         }
    1677              :     }
    1678              :     else
    1679              :     {
    1680            0 :         if( m_parent->powerState() != 1 || m_parent->powerStateTarget() != 1 )
    1681              :         {
    1682            0 :             return -1; // don't log, but propagate error
    1683              :         }
    1684              : 
    1685            0 :         MagAOXAppT::log<software_error>();
    1686            0 :         return -1;
    1687              :     }
    1688            0 : }
    1689              : 
    1690              : template <class parentT>
    1691            0 : int zaberStage<parentT>::onPowerOff()
    1692              : {
    1693            0 :     m_commandStatus = true; ///< The status of the last command sent. true = OK, false = RJ (rejected)
    1694              : 
    1695            0 :     m_deviceStatus = 'U'; ///< Current status.  Either 'I' for IDLE or 'B' for BUSY.  Intializes to 'U' for UNKOWN.
    1696              : 
    1697            0 :     m_homing = false;
    1698              : 
    1699              :     // We don't 0 rawPos so it is retained
    1700              : 
    1701            0 :     m_temp = -999; ///< The driver temperature, in C.
    1702              : 
    1703            0 :     unsetWarnings();
    1704            0 :     m_warnWRreported = false;
    1705              : 
    1706            0 :     return 0;
    1707              : }
    1708              : 
    1709              : template <class parentT>
    1710            0 : int zaberStage<parentT>::writeStateFile( std::ofstream &fout )
    1711              : {
    1712            0 :     fout << m_rawPos << '\n';
    1713              : 
    1714            0 :     if( !fout )
    1715              :     {
    1716            0 :         return MagAOXAppT::log<software_error, -1>( { "error writing raw position" } );
    1717              :     }
    1718              : 
    1719            0 :     fout << m_parked << '\n';
    1720              : 
    1721            0 :     if( !fout )
    1722              :     {
    1723            0 :         return MagAOXAppT::log<software_error, -1>( { "error writing parked state" } );
    1724              :     }
    1725              : 
    1726            0 :     fout << m_maxPos << '\n';
    1727              : 
    1728            0 :     if( !fout )
    1729              :     {
    1730            0 :         return MagAOXAppT::log<software_error, -1>( { "error writing max position" } );
    1731              :     }
    1732              : 
    1733            0 :     fout << m_lastHomed.tv_sec << '\n';
    1734              : 
    1735            0 :     if( !fout )
    1736              :     {
    1737            0 :         return MagAOXAppT::log<software_error, -1>( { "error writing last home time" } );
    1738              :     }
    1739              : 
    1740            0 :     return 0;
    1741              : }
    1742              : 
    1743              : template <class parentT>
    1744            0 : int zaberStage<parentT>::readStateFile( std::ifstream &fin )
    1745              : {
    1746              :     long   rawPos;
    1747              :     bool   parked;
    1748              :     long   maxPos;
    1749              :     time_t lastHomed;
    1750              : 
    1751            0 :     fin >> rawPos;
    1752              : 
    1753            0 :     if( !fin )
    1754              :     {
    1755            0 :         return MagAOXAppT::log<software_error, -1>( { "error reading raw position" } );
    1756              :     }
    1757              : 
    1758            0 :     fin >> parked;
    1759              : 
    1760            0 :     if( !fin )
    1761              :     {
    1762            0 :         return MagAOXAppT::log<software_error, -1>( { "error reading parked state" } );
    1763              :     }
    1764              : 
    1765            0 :     fin >> maxPos;
    1766              : 
    1767            0 :     if( !fin )
    1768              :     {
    1769            0 :         return MagAOXAppT::log<software_error, -1>( { "error reading max position" } );
    1770              :     }
    1771              : 
    1772            0 :     fin >> lastHomed;
    1773              : 
    1774            0 :     if( !fin )
    1775              :     {
    1776            0 :         return MagAOXAppT::log<software_error, -1>( { "error reading last home time" } );
    1777              :     }
    1778              : 
    1779            0 :     m_rawPos            = rawPos;
    1780            0 :     m_tgtPos            = rawPos;
    1781            0 :     m_parked            = parked;
    1782            0 :     m_maxPos            = maxPos;
    1783            0 :     m_lastHomed.tv_sec  = lastHomed;
    1784            0 :     m_lastHomed.tv_nsec = 0;
    1785              : 
    1786            0 :     return 0;
    1787              : }
    1788              : 
    1789              : } // namespace app
    1790              : } // namespace MagAOX
    1791              : 
    1792              : #endif // zaberStage_hpp
        

Generated by: LCOV version 2.0-1