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

Generated by: LCOV version 2.0-1