LCOV - code coverage report
Current view: top level - INDI/libcommon - IndiConnection.cpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 60.0 % 155 93
Test Date: 2026-04-15 19:34:29 Functions: 66.7 % 27 18

            Line data    Source code
       1              : /// $Id: IndiConnection.cpp
       2              : ///
       3              : /// @author Paul Grenz
       4              : ///
       5              : ////////////////////////////////////////////////////////////////////////////////
       6              : 
       7              : #include <stdlib.h>
       8              : #include <iostream>
       9              : #include <signal.h>
      10              : #include <errno.h>
      11              : #include <unistd.h>
      12              : #include <stdexcept>
      13              : #include <sys/types.h>  // provides 'umask'
      14              : #include <sys/stat.h>  // provides 'umask'
      15              : #include <sys/time.h>  // provides 'setrlimit'
      16              : #include <sys/resource.h>  // provides 'setrlimit'
      17              : #include "IndiConnection.hpp"
      18              : #include "TimeStamp.hpp"
      19              : 
      20              : using std::exception;
      21              : using std::runtime_error;
      22              : using std::string;
      23              : using std::vector;
      24              : using std::stringstream;
      25              : using std::endl;
      26              : using pcf::TimeStamp;
      27              : using pcf::IndiConnection;
      28              : using pcf::IndiXmlParser;
      29              : using pcf::IndiMessage;
      30              : using pcf::IndiProperty;
      31              : 
      32              : 
      33              : ////////////////////////////////////////////////////////////////////////////////
      34              : /// \brief IndiConnection::IndiConnection
      35              : /// Standard constructor.
      36              : 
      37            0 : IndiConnection::IndiConnection()
      38              : {
      39            0 :   construct( "generic_indi_process", "1", "1" );
      40            0 : }
      41              : 
      42              : ////////////////////////////////////////////////////////////////////////////////
      43              : /// \brief IndiConnection::IndiConnection
      44              : /// Constructor which sets the name, version, and INDI protocol version.
      45              : /// \param szName Name of this object.
      46              : /// \param szVersion Version of this object.
      47              : /// \param szProtocolVersion INDI protocol version.
      48              : 
      49           12 : IndiConnection::IndiConnection( const string &szName,
      50              :                                 const string &szVersion,
      51           36 :                                 const string &szProtocolVersion )
      52              : {
      53           12 :   construct( szName, szVersion, szProtocolVersion );
      54           12 : }
      55              : 
      56              : ////////////////////////////////////////////////////////////////////////////////
      57              : /// \brief IndiConnection::IndiConnection
      58              : /// Copy constructor.
      59              : /// \param idRhs Another version of the driver.
      60              : 
      61            0 : IndiConnection::IndiConnection( const IndiConnection &idRhs ) : Thread()
      62              : {
      63              :   static_cast<void>(idRhs);
      64              :   // Empty because this is private.
      65            0 : }
      66              : 
      67              : ////////////////////////////////////////////////////////////////////////////////
      68              : /// \brief IndiConnection::operator =
      69              : /// Assignment operator.
      70              : /// \param idRhs The right-hand side of the operation.
      71              : /// \return This object.
      72              : 
      73            0 : const IndiConnection &IndiConnection::operator= ( const IndiConnection &idRhs )
      74              : {
      75              :   static_cast<void>(idRhs);
      76              :   // Empty because this is private.
      77            0 :   return *this;
      78              : }
      79              : 
      80              : ////////////////////////////////////////////////////////////////////////////////
      81              : /// \brief IndiConnection::~IndiConnection
      82              : /// Standard destructor.
      83              : 
      84           12 : IndiConnection::~IndiConnection()
      85              : {
      86              :     try
      87              :     {
      88           12 :         deactivate();
      89              :     }
      90            0 :     catch(...)
      91              :     {
      92              :         //do nothing
      93            0 :     }
      94              : 
      95           12 :     MutexLock::AutoLock autoOut( &m_mutOutput );
      96           12 :     if(m_fdOutput >= 0 && m_fdOutput != STDOUT_FILENO)
      97              :     {
      98            3 :         ::close(m_fdOutput);
      99              :     }
     100           12 :     m_fdOutput = -1;
     101              : 
     102           12 : }
     103              : 
     104              : ////////////////////////////////////////////////////////////////////////////////
     105              : /// \brief IndiConnection::construct
     106              : /// Called from the constructor to initialize member variables.
     107              : /// \param szName Name for this INDI process to use.
     108              : /// \param szVersion Version of the process.
     109              : /// \param szProtocolVersion Version of the INDI protocol.
     110              : 
     111           12 : void IndiConnection::construct( const string &szName,
     112              :                                 const string &szVersion,
     113              :                                 const string &szProtocolVersion )
     114              : {
     115              :   // Make sure we are in a known state.
     116           12 :   m_oQuitProcess = false;
     117              : 
     118              :   // The process thread has not been set up yet.
     119           12 :   m_idProcessThread = 0;
     120              : 
     121              :   // These are the two descriptors we will use to talk to the outside world.
     122           12 :   m_fdInput = STDIN_FILENO;
     123              : 
     124              :   // We start with STDOUT.
     125           12 :   m_fdOutput = STDOUT_FILENO;
     126              : 
     127              :   // setup the signal handler.
     128              :   //::signal( SIGHUP, IndiConnection::handleSignal );
     129              :   //::signal( SIGINT, IndiConnection::handleSignal );
     130              :   //::signal( SIGTERM, IndiConnection::handleSignal );
     131              : 
     132              :   // Set our information that sets us up as a unique INDI component.
     133           12 :   setName( szName );
     134           12 :   setVersion( szVersion );
     135           12 :   setProtocolVersion( szProtocolVersion );
     136              : 
     137           12 :   m_oIsVerboseModeEnabled = false;
     138              : 
     139              :   // What is the interval at which our 'execute' function is called?
     140              :   // This is the same if we are in simulation mode or not.
     141              :   // The default is one second.
     142           12 :   setInterval(1000);
     143              : 
     144              :   // What is our CPU affinity? This is the CPU we will run on.
     145              :   // A -1 indicates we don't care where it runs.
     146           12 :   m_iCpuAffinity = -1;
     147              : 
     148              :   // allocate a big buffer to hold the input data.
     149           12 :   m_vecInputBuf = vector<unsigned char>( InputBufSize );
     150              : 
     151           12 : }
     152              : 
     153              : ////////////////////////////////////////////////////////////////////////////////
     154              : /// \brief IndiConnection::beforeExecute
     155              : /// Override this function to do something before this device has been told to
     156              : /// start running the thread, like allocate memory.
     157              : 
     158            0 : void IndiConnection::beforeExecute()
     159              : {
     160            0 : }
     161              : 
     162              : ////////////////////////////////////////////////////////////////////////////////
     163              : /// \brief IndiConnection::afterExecute
     164              : /// Override this function to do something after this device has been told to
     165              : /// stop running the thread, like clean up allocated memory.
     166              : 
     167            0 : void IndiConnection::afterExecute()
     168              : {
     169            0 : }
     170              : 
     171              : ////////////////////////////////////////////////////////////////////////////////
     172              : /// \brief IndiConnection::execute
     173              : /// Function which executes in a loop in a separate thread.
     174              : /// Override in derived class to perform some action.
     175              : 
     176            0 : void IndiConnection::execute()
     177              : {
     178            0 : }
     179              : 
     180              : ////////////////////////////////////////////////////////////////////////////////
     181              : /// \brief IndiConnection::activate
     182              : /// Try to start the driver 'execute' thread. If it is already running, this
     183              : /// will throw. When the thread starts, it calls 'beforeExecute' before
     184              : /// calling 'execute' in a loop.
     185              : 
     186            3 : void IndiConnection::activate()
     187              : {
     188              :   // is the thread already running?
     189            3 :   if ( isRunning() == true )
     190            0 :     throw runtime_error( string( "Tried to activate when already active." ) );
     191              : 
     192              :   // Start the 'execute' thread running to perform the component.
     193            3 :   start( m_iCpuAffinity );
     194            3 : }
     195              : 
     196              : /////////////////////////////////////////////////////////////////////////////////
     197              : /// \brief IndiConnection::deactivate
     198              : /// Try to stop the driver 'execute' thread and the 'process' thread. If
     199              : /// neither is running, this will have no effect. This will stop the 'execute'
     200              : /// being called in a loop and call 'afterExecute' before stopping the thread.
     201              : 
     202           15 : void IndiConnection::deactivate()
     203              : {
     204           15 :   if ( isRunning() == true )
     205              :   {
     206            3 :     stop();
     207            3 :     join();
     208            3 :     if ( m_idProcessThread != 0 )
     209              :     {
     210            0 :       ::pthread_join( m_idProcessThread, NULL );
     211            0 :       m_idProcessThread = 0;
     212              :     }
     213              :   }
     214           15 : }
     215              : 
     216              : ////////////////////////////////////////////////////////////////////////////////
     217              : /// \brief IndiConnection::isActive
     218              : /// Is the driver currently active ('execute' thread running)?
     219              : /// \return true or false.
     220              : 
     221           12 : bool IndiConnection::isActive() const
     222              : {
     223           12 :   return isRunning();
     224              : }
     225              : 
     226              : ////////////////////////////////////////////////////////////////////////////////
     227              : /// \brief IndiConnection::processIndiRequests
     228              : /// Called to ensure that incoming INDI messages are received and handled.
     229              : /// It will not exit until we receive a signal. May create a new thread.
     230              : /// \param oUseThread Run this in a separate thread or not.
     231              : 
     232            3 : void IndiConnection::processIndiRequests( const bool &oUseThread )
     233              : {
     234            3 :   if ( oUseThread == false )
     235              :   {
     236            3 :     process();
     237              :   }
     238              :   else
     239              :   {
     240            0 :     m_idProcessThread = 0;
     241            0 :     ::pthread_create( &m_idProcessThread, NULL,
     242              :                       IndiConnection::pthreadProcess, this );
     243              :   }
     244            3 : }
     245              : 
     246              : ////////////////////////////////////////////////////////////////////////////////
     247              : /// \brief IndiConnection::pthreadProcess
     248              : /// 'pthread_create' needs a static function to get the thread going.
     249              : /// Passing a pointer back to this class allows us to call the 'runLoop'
     250              : /// function from within the new thread.
     251              : /// \param pUnknown A pointer to this class that the thread function can use.
     252              : /// \return NULL - no return is needed.
     253              : 
     254            0 : void *IndiConnection::pthreadProcess( void *pUnknown )
     255              : {
     256              :   //  do a static cast to get the pointer back to a "Thread" object.
     257            0 :   IndiConnection *pThis = static_cast<IndiConnection *>( pUnknown );
     258              : 
     259              :   //  we are now within the new thread and up and running.
     260              :   try
     261              :   {
     262            0 :     pThis->process();
     263              :   }
     264            0 :   catch ( const std::exception &excep )
     265              :   {
     266            0 :     std::cerr << "Process thread exited: " << excep.what() << std::endl;
     267            0 :   }
     268            0 :   catch ( ... )
     269              :   {
     270            0 :     std::cerr << "An exception was thrown, process thread exited." << std::endl;
     271            0 :   }
     272              : 
     273              :   ///  no return is necessary, since it is not examined.
     274            0 :   return NULL;
     275              : }
     276              : 
     277              : ////////////////////////////////////////////////////////////////////////////////
     278              : /// \brief IndiConnection::process
     279              : /// Listens on the file descriptor for incoming INDI messages. Exits when the
     280              : /// 'Quit Process' flag becomes true.
     281              : 
     282            3 : void IndiConnection::process()
     283              : {
     284              :   // Loop here until we are told to quit or we hit an error.
     285            6 :   while ( m_oQuitProcess == false )
     286              :   {
     287              :     try
     288              :     {
     289              :       // Call the 'update' function to do something each time we pass through
     290              :       // this loop reading input.
     291            3 :       update();
     292              : 
     293              :       // The length of the command received.
     294            3 :       int nInputBufLen = 0;
     295            3 :       ::memset( &m_vecInputBuf[0], 0, InputBufSize );
     296              : 
     297              :       // Create and clear out the FD set.
     298              :       fd_set fdsRead;
     299           51 :       FD_ZERO( &fdsRead );
     300              :       // Watch input to see when we get some input.
     301            3 :       FD_SET( m_fdInput, &fdsRead );
     302              : 
     303              :       // The argument to 'select' must be +1 greater than the largest fd.
     304            3 :       int nHighestNumberedFd = m_fdInput;
     305              : 
     306              :       // Set the timeout on the select call.
     307              :       timeval tv;
     308            3 :       tv.tv_sec = 1; //0;
     309            3 :       tv.tv_usec = 0; //10000;
     310              : 
     311              :       // We need a timeout on the select to ensure that we loop around and
     312              :       // call the 'update' function regularly.
     313            3 :       int nRetval = ::select( nHighestNumberedFd + 1, &fdsRead, NULL, NULL, &tv );
     314              :       //int nRetval = ::select( nHighestNumberedFd+1, &fdsRead, NULL, NULL, NULL );
     315              : 
     316            3 :       if ( nRetval == -1 )
     317              :       {
     318            3 :         if ( m_oQuitProcess == false )
     319              :         {
     320            0 :           Thread::sleep( 1 );
     321              :         }
     322              :       }
     323            0 :       else if ( nRetval == 0 )
     324              :       {
     325              :         // Timed out - just loop back around.
     326              :       }
     327              :       // We must check the input file descriptor.
     328            0 :       else if ( FD_ISSET( m_fdInput, &fdsRead ) != 0 )
     329              :       {
     330              :         // Receive a command
     331            0 :         nInputBufLen = ::read( m_fdInput, &m_vecInputBuf[0], InputBufSize );
     332            0 :         if ( nInputBufLen < 0 )
     333              :         {
     334            0 :           m_oQuitProcess = true;
     335              :         }
     336            0 :         else if ( nInputBufLen == 0 )
     337              :         {
     338              :           // If we read an EOF, this is a signal that we should die.
     339            0 :           m_oQuitProcess = true;
     340              :         }
     341              :         else
     342              :         {
     343              :           // A message for the error.
     344            0 :           std::string szErrorMsg;
     345              :           // Now, is this a command which fits our requirements?
     346            0 :           m_ixpIndi.parseXml( ( char * )( &m_vecInputBuf[0] ), nInputBufLen, szErrorMsg );
     347              : 
     348            0 :           while( m_ixpIndi.getState() == IndiXmlParser::CompleteState )
     349              :           {
     350              :             // Create the message from the XML.
     351            0 :             IndiMessage imRecv = m_ixpIndi.createIndiMessage();
     352            0 :             const IndiProperty &ipRecv = imRecv.getProperty();
     353              : 
     354              :             // Dispatch!
     355            0 :             dispatch( imRecv.getType(), ipRecv );
     356              : 
     357              :             // Get ready for some new XML.
     358              :             //m_ixpIndi.clear();
     359              : 
     360              :             //Test whether there is more unparsed data.
     361            0 :             m_ixpIndi.parseXml( "", szErrorMsg);
     362            0 :           }
     363            0 :         }
     364              :       }
     365              :     }
     366            0 :     catch ( const runtime_error &excep )
     367              :     {
     368            0 :     }
     369            0 :     catch ( const exception &excep )
     370              :     {
     371            0 :     }
     372              :   }
     373              : 
     374            3 : }
     375              : 
     376              : ////////////////////////////////////////////////////////////////////////////////
     377              : /// \brief IndiConnection::sendXml
     378              : /// Sends an XML string out.
     379              : /// \param szXml The XML to send.
     380            6 : void IndiConnection::sendXml( const string &szXml ) const
     381              : {
     382            6 :   MutexLock::AutoLock autoOut( &m_mutOutput );
     383              : 
     384            6 :   if(m_fdOutput < 0)
     385              :   {
     386            0 :     return;
     387              :   }
     388              : 
     389            6 :   const char * buf = szXml.c_str();
     390            6 :   size_t remaining = szXml.size();
     391              : 
     392           12 :   while(remaining > 0)
     393              :   {
     394            6 :     ssize_t nwr = ::write(m_fdOutput, buf, remaining);
     395            6 :     if(nwr > 0)
     396              :     {
     397            6 :       buf += nwr;
     398            6 :       remaining -= static_cast<size_t>(nwr);
     399            6 :       continue;
     400              :     }
     401              : 
     402            0 :     if(nwr < 0 && errno == EINTR)
     403              :     {
     404            0 :       continue;
     405              :     }
     406              : 
     407            0 :     break;
     408              :   }
     409              : 
     410            6 : }
     411              : 
     412              : ////////////////////////////////////////////////////////////////////////////////
     413              : /// \brief IndiConnection::isVerboseModeEnabled
     414              : /// Are we logging additional messages? True if yes, false otherwise.
     415              : /// \return true or false.
     416              : 
     417            0 : bool IndiConnection::isVerboseModeEnabled() const
     418              : {
     419            0 :   return m_oIsVerboseModeEnabled;
     420              : }
     421              : 
     422              : ////////////////////////////////////////////////////////////////////////////////
     423              : /// \brief IndiConnection::enableVerboseMode
     424              : /// Turns the additional logging on or off.
     425              : /// \param oEnable true or false to turn it on or off.
     426              : 
     427            0 : void IndiConnection::enableVerboseMode( const bool &oEnable )
     428              : {
     429            0 :   m_oIsVerboseModeEnabled = oEnable;
     430            0 : }
     431              : 
     432              : ////////////////////////////////////////////////////////////////////////////////
     433              : /// \brief setInputFd
     434              : /// Which FD will be used for input?
     435              : /// \param iFd The file descriptor to use.
     436              : 
     437            3 : void IndiConnection::setInputFd( const int &iFd )
     438              : {
     439            3 :   m_fdInput = iFd;
     440            3 : }
     441              : 
     442              : ////////////////////////////////////////////////////////////////////////////////
     443              : /// \brief setOutputFd
     444              : /// Which FD will be used for output?
     445              : /// \param iFd The file descriptor to use.
     446              : 
     447            3 : void IndiConnection::setOutputFd( const int &iFd )
     448              : {
     449            3 :     MutexLock::AutoLock autoOut( &m_mutOutput );
     450              : 
     451            3 :     if(iFd == m_fdOutput)
     452              :     {
     453            0 :         return;
     454              :     }
     455              : 
     456            3 :     if(m_fdOutput >= 0 && m_fdOutput != STDOUT_FILENO)
     457              :     {
     458            0 :         ::close(m_fdOutput);
     459              :     }
     460              : 
     461            3 :     m_fdOutput = iFd;
     462            3 : }
     463              : 
     464              : ////////////////////////////////////////////////////////////////////////////////
     465              : /// \brief IndiConnection::setName
     466              : /// Sets the name of this component.
     467              : /// \param szName The name to use.
     468              : 
     469           12 : void IndiConnection::setName( const string &szName )
     470              : {
     471           12 :   m_szName = szName;
     472           12 : }
     473              : 
     474              : ////////////////////////////////////////////////////////////////////////////////
     475              : /// \brief IndiConnection::getName
     476              : /// Returns the name of this component.
     477              : /// \return The name
     478              : 
     479           12 : string IndiConnection::getName() const
     480              : {
     481           12 :   return m_szName;
     482              : }
     483              : 
     484              : ////////////////////////////////////////////////////////////////////////////////
     485              : /// \brief IndiConnection::setVersion
     486              : /// Sets the version of this component.
     487              : /// \param szVersion The version as a string.
     488              : 
     489           12 : void IndiConnection::setVersion( const string &szVersion )
     490              : {
     491           12 :   m_szVersion = szVersion;
     492           12 : }
     493              : 
     494              : ////////////////////////////////////////////////////////////////////////////////
     495              : /// \brief IndiConnection::getVersion
     496              : /// Returns the version of this component.
     497              : /// \return The version as a string.
     498              : 
     499           12 : string IndiConnection::getVersion() const
     500              : {
     501           12 :   return m_szVersion;
     502              : }
     503              : 
     504              : ////////////////////////////////////////////////////////////////////////////////
     505              : /// \brief IndiConnection::setProtocolVersion
     506              : /// Sets the INDI protocol version.
     507              : /// \param ProtocolVersion The protocol version as a string.
     508              : 
     509           12 : void IndiConnection::setProtocolVersion( const string &ProtocolVersion )
     510              : {
     511           12 :   m_ixpIndi.setProtocolVersion( ProtocolVersion );
     512           12 : }
     513              : 
     514              : ////////////////////////////////////////////////////////////////////////////////
     515              : /// \brief IndiConnection::getProtocolVersion
     516              : /// Returns the INDI protocol version.
     517              : /// \param szVersion The protocol version as a string.
     518              : 
     519           18 : string IndiConnection::getProtocolVersion() const
     520              : {
     521           18 :   return m_ixpIndi.getProtocolVersion();
     522              : }
     523              : 
     524              : ////////////////////////////////////////////////////////////////////////////////
     525              : /// \brief IndiConnection::quitProcess
     526              : /// Causes the process to quit, the same as if a ctrl-c was sent.
     527              : 
     528            3 : void IndiConnection::quitProcess()
     529              : {
     530            3 :   m_oQuitProcess = true;
     531            3 : }
     532              : 
     533              : ////////////////////////////////////////////////////////////////////////////////
        

Generated by: LCOV version 2.0-1