LCOV - code coverage report
Current view: top level - INDI/libcommon - IndiDriver.cpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 27.6 % 181 50
Test Date: 2026-01-03 21:03:39 Functions: 26.3 % 38 10

            Line data    Source code
       1              : /// $Id: IndiDriver.cpp
       2              : ///
       3              : /// @author Paul Grenz
       4              : ///
       5              : ////////////////////////////////////////////////////////////////////////////////
       6              : 
       7              : #include "IndiDriver.hpp"
       8              : #include "System.hpp"
       9              : 
      10              : using std::runtime_error;
      11              : using std::string;
      12              : using std::stringstream;
      13              : using std::endl;
      14              : using std::vector;
      15              : using pcf::System;
      16              : using pcf::TimeStamp;
      17              : using pcf::IndiConnection;
      18              : using pcf::IndiDriver;
      19              : using pcf::IndiMessage;
      20              : using pcf::IndiProperty;
      21              : 
      22              : ////////////////////////////////////////////////////////////////////////////////
      23              : /// Standard constructor.
      24              : 
      25            0 : IndiDriver::IndiDriver()
      26            0 :   : IndiConnection()
      27              : {
      28            0 :   setup();
      29            0 : }
      30              : 
      31              : ////////////////////////////////////////////////////////////////////////////////
      32              : 
      33           12 : IndiDriver::IndiDriver( const string &szName,
      34              :                         const string &szVersion,
      35           12 :                         const string &szProtocolVersion )
      36           12 :   : IndiConnection( szName, szVersion, szProtocolVersion )
      37              : {
      38           12 :   setup();
      39           12 : }
      40              : 
      41              : ////////////////////////////////////////////////////////////////////////////////
      42              : /// \brief IndiDriver::IndiDriver
      43              : /// Copy constructor.
      44              : /// \param idRhs Another version of the driver.
      45              : 
      46            0 : IndiDriver::IndiDriver(const IndiDriver &idRhs ) : IndiConnection()
      47              : //  : IndiConnection( idRhs )  // can't invoke - private
      48              : {
      49              :   static_cast<void>(idRhs);
      50              :   // Empty because this is private.
      51            0 : }
      52              : 
      53              : ////////////////////////////////////////////////////////////////////////////////
      54              : /// \brief IndiDriver::operator =
      55              : /// Assignment operator.
      56              : /// \param idRhs The right-hand side of the operation.
      57              : /// \return This object.
      58              : 
      59            0 : const IndiDriver &IndiDriver::operator= ( const IndiDriver &idRhs )
      60              : //  : IndiConnection::operator= ( idRhs )  // can't invoke - private
      61              : {
      62              :   static_cast<void>(idRhs);
      63              :   // Empty because this is private.
      64            0 :   return *this;
      65              : }
      66              : 
      67              : ////////////////////////////////////////////////////////////////////////////////
      68              : /// \brief IndiDriver::~IndiDriver
      69              : /// Standard destructor.
      70              : 
      71           12 : IndiDriver::~IndiDriver() noexcept(true)
      72              : {
      73           12 : }
      74              : 
      75              : ////////////////////////////////////////////////////////////////////////////////
      76              : /// \brief IndiDriver::setup Sets up file descriptors and other things that
      77              : /// need to be initialized at construction time.
      78              : 
      79           12 : void IndiDriver::setup()
      80              : {
      81              :   // Grab the current time to use for the 'uptime' message.
      82           12 :   TimeStamp m_tsStartTime = TimeStamp::now();
      83              :   // Initialize the the last time sent with the current time.
      84           12 :   TimeStamp m_tsLastSent = TimeStamp::now();
      85              : 
      86              : 
      87              :   // If this driver saves any files or data to disk, this is where to do it.
      88           12 :   m_szDataDirectory = "";
      89              : 
      90              : 
      91              :   // This is a flag which can be used to fake data or pretend to be
      92              :   // connected to hardware. By itself, it does nothing.
      93           12 :   m_oIsSimulationModeEnabled = false;
      94              : 
      95              :   // This is also a flag which does nothing on its own, but can be used to
      96              :   // decide whether or not to send an alarm email.
      97           12 :   m_oIsAlarmModeEnabled = true;
      98              : 
      99              :   // This is a comma-separated list of email recipients which will receive
     100              :   // an email when a alarm is logged.
     101           12 :   m_szEmailList = "";
     102              : 
     103              :   // What is our alarm interval? This is the same if we are in
     104              :   // simulation mode or not. The default is one day (1440 minutes).
     105              :   // This is the maximun speed that alarms will go out.
     106           12 :   m_uiAlarmInterval = 1440;
     107              : 
     108           12 :   m_oIsAlarmActive = false;
     109           12 :   m_uiAlarmInterval = 1440; // Once a day (every 1440 minutes)
     110              : 
     111              : 
     112              :   // This will not be set to true until at least one 'getProperties' has
     113              :   // been received, or the user of this class sets it manually.
     114           12 :   m_oIsResponseModeEnabled = false;
     115              : 
     116              :   // Create and initialize the uptime message.
     117           12 :   m_ipUpTime = IndiProperty( IndiProperty::Number, getName(), "Version" );
     118           12 :   m_ipUpTime.setPerm( IndiProperty::ReadOnly );
     119           12 :   m_ipUpTime.setState( IndiProperty::Ok );
     120           12 :   m_ipUpTime.setTimeStamp( TimeStamp::now() );
     121              : 
     122              :   // Device version number.
     123           36 :   m_ipUpTime.add( IndiElement( "Driver", getVersion() ) );
     124              :   // Protocol version number.
     125           36 :   m_ipUpTime.add( IndiElement( "Properties", getProtocolVersion() ) );
     126              :   // Seconds since this device started.
     127           36 :   m_ipUpTime.add( IndiElement( "Uptime", m_tsStartTime.elapsedMillis() / 1000 ) );
     128              :   // Whether or not the driver thread is running.
     129           24 :   m_ipUpTime.add( IndiElement( "active", ( isActive() ? (1) : (0) ) ) );
     130           12 : }
     131              : 
     132              : ////////////////////////////////////////////////////////////////////////////////
     133              : /// \brief IndiDriver::update
     134              : /// Called in the process loop to perform an action each time through.
     135              : 
     136            0 : void IndiDriver::update()
     137              : {
     138              :   // Should we send an 'uptime' message?
     139            0 :   if ( m_tsLastSent.elapsedMillis() > 5000 &&
     140            0 :        isResponseModeEnabled() == true)
     141              :   {
     142              :     // Update the "uptime" message.
     143            0 :     m_ipUpTime[ "Uptime" ] = m_tsStartTime.elapsedMillis() / 1000;
     144            0 :     m_ipUpTime[ "active" ] = ( isActive() ? (1) : (0) );
     145              :     // Reset the last sent to wait another 5 seconds.
     146            0 :     m_tsLastSent = TimeStamp::now();
     147            0 :     sendSetProperty( m_ipUpTime );
     148              :   }
     149            0 : }
     150              : 
     151              : ////////////////////////////////////////////////////////////////////////////////
     152              : /// \brief IndiDriver::dispatch
     153              : /// Chooses what to do with the received property.
     154              : /// \param tType Type of the message we received.
     155              : /// \param ipDispatch The property contained in the message.
     156              : 
     157            0 : void IndiDriver::dispatch( const IndiMessage::Type &tType,
     158              :                            const IndiProperty &ipDispatch )
     159              : {
     160              : 
     161              :   // Make sure the client knows about the basic properties the driver supports.
     162            0 :   if ( tType == IndiMessage::GetProperties )
     163              :   {
     164            0 :     handleDriverGetProperties( ipDispatch );
     165              :   }
     166              : 
     167              :   // Decide what we should do based on the type of the message.
     168            0 :   switch ( tType )
     169              :   {
     170            0 :     case IndiMessage::Define:
     171            0 :       handleDefProperty( ipDispatch ); break;
     172            0 :     case IndiMessage::Delete:
     173            0 :       handleDelProperty( ipDispatch ); break;
     174            0 :     case IndiMessage::GetProperties:
     175            0 :       handleGetProperties( ipDispatch ); break;
     176            0 :     case IndiMessage::Message:
     177            0 :       handleMessage( ipDispatch ); break;
     178            0 :     case IndiMessage::NewProperty:
     179            0 :       if ( handleDriverNewProperty( ipDispatch ) == false )
     180              :       {
     181            0 :         handleNewProperty( ipDispatch );
     182              :       }
     183            0 :       break;
     184            0 :     case IndiMessage::SetProperty:
     185            0 :       handleSetProperty( ipDispatch ); break;
     186            0 :     default:
     187            0 :       break;
     188              :   }
     189            0 : }
     190              : 
     191              : ////////////////////////////////////////////////////////////////////////////////
     192              : /// Override this function to do something before this device has been told to
     193              : /// start, like allocate memory.
     194              : 
     195            3 : void IndiDriver::beforeExecute()
     196              : {
     197            3 : }
     198              : 
     199              : ////////////////////////////////////////////////////////////////////////////////
     200              : /// Override this function to do something after this device has been told to
     201              : /// stop, like clean up allocated memory.
     202              : 
     203            3 : void IndiDriver::afterExecute()
     204              : {
     205            3 : }
     206              : 
     207              : ////////////////////////////////////////////////////////////////////////////////
     208              : /// Override in derived class, place the code to do something here.
     209              : 
     210            0 : void IndiDriver::execute()
     211              : {
     212            0 : }
     213              : 
     214              : ////////////////////////////////////////////////////////////////////////////////
     215              : /// Received a GET property, Respond with the properties the driver handles
     216              : /// at a basic level. These properties are common to all drivers.
     217              : /// Some of these are:
     218              : ///
     219              : ///   enable_active -> rmeoved for MagAO-X
     220              : ///
     221              : /// Returns true (for now).
     222              : 
     223            0 : bool IndiDriver::handleDriverGetProperties( const IndiProperty &ipRecv )
     224              : {
     225              :   static_cast<void>(ipRecv);
     226              : 
     227              :   // Log the fact that we can now respond to INDI requests.
     228            0 :   if ( isResponseModeEnabled() == false )
     229              :   {
     230            0 :     enableResponseMode( true );
     231              :   }
     232              : 
     233              : 
     234              :   // For now, this always succeeds.
     235            0 :   return true;
     236              : }
     237              : 
     238              : ////////////////////////////////////////////////////////////////////////////////
     239              : /// Received a NEW property, decide if it should be handled here. A remote
     240              : /// device can send a property which can change the state of the driver at
     241              : /// a basic level common to all drivers. Some of these are:
     242              : ///
     243              : ///   enable_active.value=On/Off -  start or stop the internal worker thread.
     244              : ///
     245              : /// Returns 'true' if the property was handled, 'false' otherwise.
     246              : 
     247            0 : bool IndiDriver::handleDriverNewProperty( const IndiProperty &ipRecv )
     248              : {
     249              :   static_cast<void>(ipRecv);
     250              : 
     251              :   // Assume we didn't handle the NEW and it will be handled by
     252              :   // the derived class.
     253            0 :   bool oHandledProperty = false;
     254              : 
     255            0 :   return oHandledProperty;
     256              : }
     257              : 
     258              : ////////////////////////////////////////////////////////////////////////////////
     259              : /// Received a DEF PROPERTY. A remote device sends a 'DEF' to tell other
     260              : /// INDI devices that are interested what properties it has available.
     261              : /// (see 'sendDefProperty')
     262              : 
     263            0 : void IndiDriver::handleDefProperty( const pcf::IndiProperty &ipRecv )
     264              : {
     265              :   static_cast<void>(ipRecv);
     266            0 : }
     267              : 
     268              : ////////////////////////////////////////////////////////////////////////////////
     269              : /// Received a DEL PROPERTY. A remote device is telling us that one of its
     270              : /// properties is no longer available, or a device is no longer available.
     271              : /// (see 'sendDelProperty')
     272              : 
     273            0 : void IndiDriver::handleDelProperty( const pcf::IndiProperty &ipRecv )
     274              : {
     275              :   static_cast<void>(ipRecv);
     276            0 : }
     277              : 
     278              : ////////////////////////////////////////////////////////////////////////////////
     279              : /// Received a GET PROPERTIES. An remote device will send this command to
     280              : /// learn what INDI properties this device has. A DEF PROPERTY can be
     281              : /// sent as a reply. (see 'sendDefProperty')
     282              : 
     283            0 : void IndiDriver::handleGetProperties( const pcf::IndiProperty &ipRecv )
     284              : {
     285              :   static_cast<void>(ipRecv);
     286            0 : }
     287              : 
     288              : ////////////////////////////////////////////////////////////////////////////////
     289              : /// Received a MESSAGE. a remote device sent a generic message,
     290              : /// associated with a device or entire system.
     291              : 
     292            0 : void IndiDriver::handleMessage( const pcf::IndiProperty &ipRecv )
     293              : {
     294              :   static_cast<void>(ipRecv);
     295            0 : }
     296              : 
     297              : ////////////////////////////////////////////////////////////////////////////////
     298              : /// Received a NEW PROPERTY. This is a request to update one of the
     299              : /// INDI properties we own.
     300              : 
     301            0 : void IndiDriver::handleNewProperty( const pcf::IndiProperty &ipRecv )
     302              : {
     303              :   static_cast<void>(ipRecv);
     304            0 : }
     305              : 
     306              : ////////////////////////////////////////////////////////////////////////////////
     307              : /// Received a SET PROPERTY. This is a notification telling us that a
     308              : /// remote device changed one of its INDI properties.
     309              : 
     310            0 : void IndiDriver::handleSetProperty( const pcf::IndiProperty &ipRecv )
     311              : {
     312              :   static_cast<void>(ipRecv);
     313            0 : }
     314              : 
     315              : ////////////////////////////////////////////////////////////////////////////////
     316              : /// Send an DEF property to a client. This is usually done in response
     317              : /// to receiving a 'GetProperties' message. (see 'sendGetProperties')
     318              : 
     319            0 : void IndiDriver::sendDefProperty( const IndiProperty &ipSend ) const
     320              : {
     321            0 :   if ( isResponseModeEnabled() == true )
     322              :   {
     323            0 :     IndiXmlParser ixp( IndiMessage( IndiMessage::Define, ipSend ),
     324            0 :                        getProtocolVersion() );
     325            0 :     sendXml( ixp.createXmlString() );
     326            0 :   }
     327            0 : }
     328              : 
     329              : ////////////////////////////////////////////////////////////////////////////////
     330              : /// Send an DEF property vector to a client. This is usually done in response
     331              : /// to receiving a 'GetProperties' message. (see 'sendGetProperties')
     332              : 
     333            0 : void IndiDriver::sendDefProperties( const vector<IndiProperty> &vecIpSend ) const
     334              : {
     335            0 :   if ( isResponseModeEnabled() == true )
     336              :   {
     337            0 :     for ( unsigned int ii = 0; ii < vecIpSend.size(); ii++ )
     338              :     {
     339            0 :       IndiXmlParser ixp( IndiMessage( IndiMessage::Define, vecIpSend[ii] ),
     340            0 :                          getProtocolVersion() );
     341            0 :       sendXml( ixp.createXmlString() );
     342            0 :     }
     343              :   }
     344            0 : }
     345              : 
     346              : ////////////////////////////////////////////////////////////////////////////////
     347              : /// Send a DEL PROPERTY to a client. This tells a Client a given Property
     348              : /// is no longer available. If the property only specifies
     349              : /// a Device without a Name, the Client must assume all the Properties
     350              : /// for that Device, and indeed the Device itself, are no longer available.
     351              : 
     352            3 : void IndiDriver::sendDelProperty( const pcf::IndiProperty &ipSend )
     353              : {
     354            3 :   if ( isResponseModeEnabled() == true )
     355              :   {
     356            0 :     IndiXmlParser ixp( IndiMessage( IndiMessage::Delete, ipSend ),
     357            0 :                        getProtocolVersion() );
     358            0 :     sendXml( ixp.createXmlString() );
     359            0 :   }
     360            3 : }
     361              : 
     362              : ////////////////////////////////////////////////////////////////////////////////
     363              : /// Send a DEL PROPERTY vector to a client. This tells a Client a given Property
     364              : /// is no longer available. If the property only specifies
     365              : /// a Device without a Name, the Client must assume all the Properties
     366              : /// for that Device, and indeed the Device itself, are no longer available.
     367              : 
     368            0 : void IndiDriver::sendDelProperties( const vector<IndiProperty> &vecIpSend )
     369              : {
     370            0 :   if ( isResponseModeEnabled() == true )
     371              :   {
     372            0 :     for ( unsigned int ii = 0; ii < vecIpSend.size(); ii++ )
     373              :     {
     374            0 :       IndiXmlParser ixp( IndiMessage( IndiMessage::Delete, vecIpSend[ii] ),
     375            0 :                          getProtocolVersion() );
     376            0 :       sendXml( ixp.createXmlString() );
     377            0 :     }
     378              :   }
     379            0 : }
     380              : 
     381              : ////////////////////////////////////////////////////////////////////////////////
     382              : /// Send an ENABLE BLOB. This behavior is only to be implemented in
     383              : /// intermediate INDI server processes; individual devices shall
     384              : /// disregard enableBLOB and send all elements at will.
     385              : 
     386            0 : void IndiDriver::sendEnableBLOB( const IndiProperty &ipSend )
     387              : {
     388            0 :   if ( isResponseModeEnabled() == true )
     389              :   {
     390            0 :     IndiXmlParser ixp( IndiMessage( IndiMessage::EnableBLOB, ipSend ),
     391            0 :                        getProtocolVersion() );
     392            0 :     sendXml( ixp.createXmlString() );
     393            0 :   }
     394            0 : }
     395              : 
     396              : ////////////////////////////////////////////////////////////////////////////////
     397              : /// Send a GET PROPERTIES. When a Client first starts up, it begins
     398              : /// by sending the getProperties command. This includes the protocol
     399              : /// version and may include the name of a specific Device and Property
     400              : /// if it is known by some other means. If no device is specified,
     401              : /// then all devices are reported; if no name is specified,
     402              : /// then all properties for the given device are reported. The Device
     403              : /// then calls 'sendDefProperty' for each matching Property it offers
     404              : /// for control, limited to the Properties of the specified Device if
     405              : /// included.
     406              : 
     407           10 : void IndiDriver::sendGetProperties( const IndiProperty &ipSend )
     408              : {
     409              :   // todo: Should this be disabled, or is this the one exception?
     410              :   //if ( isResponseModeEnabled() == true )
     411              :   {
     412           10 :     IndiXmlParser ixp( IndiMessage( IndiMessage::GetProperties, ipSend ),
     413           30 :                        getProtocolVersion() );
     414           10 :     sendXml( ixp.createXmlString() );
     415           10 :   }
     416           10 : }
     417              : 
     418              : ////////////////////////////////////////////////////////////////////////////////
     419              : // Send a MESSAGE.
     420              : 
     421           26 : void IndiDriver::sendMessage( const IndiProperty &ipSend )
     422              : {
     423           26 :   if ( isResponseModeEnabled() == true )
     424              :   {
     425            0 :     IndiXmlParser ixp( IndiMessage( IndiMessage::Message, ipSend ),
     426            0 :                        getProtocolVersion() );;
     427            0 :     sendXml( ixp.createXmlString() );
     428            0 :   }
     429           26 : }
     430              : 
     431              : ////////////////////////////////////////////////////////////////////////////////
     432              : // Send an SET PROPERTY. This is a notification that a property owned
     433              : // by this device has changed.
     434              : 
     435            9 : void IndiDriver::sendSetProperty( const IndiProperty &ipSend ) const
     436              : {
     437            9 :    IndiProperty _ipSend = ipSend;
     438              : 
     439            9 :    _ipSend.setTimeStamp(TimeStamp::now());
     440              : 
     441            9 :   if ( isResponseModeEnabled() == true )
     442              :   {
     443            0 :     IndiXmlParser ixp( IndiMessage( IndiMessage::SetProperty, _ipSend ),
     444            0 :                        getProtocolVersion() );
     445            0 :     sendXml( ixp.createXmlString() );
     446            0 :   }
     447            9 : }
     448              : 
     449              : ////////////////////////////////////////////////////////////////////////////////
     450              : // Send an SET PROPERTY vector. This is a notification that a property owned
     451              : // by this device has changed.
     452              : 
     453            0 : void IndiDriver::sendSetProperties( const vector<IndiProperty> &vecIpSend )
     454              : {
     455            0 :   if ( isResponseModeEnabled() == true )
     456              :   {
     457            0 :     for ( unsigned int ii = 0; ii < vecIpSend.size(); ii++ )
     458              :     {
     459            0 :       IndiXmlParser ixp( IndiMessage( IndiMessage::SetProperty, vecIpSend[ii] ),
     460            0 :                          getProtocolVersion() );
     461            0 :       sendXml( ixp.createXmlString() );
     462            0 :     }
     463              :   }
     464            0 : }
     465              : 
     466              : ////////////////////////////////////////////////////////////////////////////////
     467              : /// Are the alarms enabled? True if yes, false otherwise.
     468              : 
     469            0 : bool IndiDriver::isAlarmModeEnabled() const
     470              : {
     471            0 :   return m_oIsAlarmModeEnabled;
     472              : }
     473              : 
     474              : ////////////////////////////////////////////////////////////////////////////////
     475              : /// Turn on or off the alarms.
     476              : 
     477            0 : void IndiDriver::enableAlarmMode( const bool &oEnable )
     478              : {
     479            0 :   m_oIsAlarmModeEnabled = oEnable;
     480            0 : }
     481              : 
     482              : ////////////////////////////////////////////////////////////////////////////////
     483              : /// Are we sending outgoing messages? True if yes, false otherwise.
     484              : 
     485           38 : bool IndiDriver::isResponseModeEnabled() const
     486              : {
     487           38 :   return m_oIsResponseModeEnabled;
     488              : }
     489              : 
     490              : ////////////////////////////////////////////////////////////////////////////////
     491              : /// Turns the outgoing messages like 'SET', 'NEW', and 'DEF' on or off.
     492              : 
     493            0 : void IndiDriver::enableResponseMode( const bool &oEnable )
     494              : {
     495            0 :   m_oIsResponseModeEnabled = oEnable;
     496            0 : }
     497              : 
     498              : ////////////////////////////////////////////////////////////////////////////////
     499              : /// Is this device's data being simulated? True if yes, false otherwise.
     500              : 
     501            0 : bool IndiDriver::isSimulationModeEnabled() const
     502              : {
     503            0 :   return m_oIsSimulationModeEnabled;
     504              : }
     505              : 
     506              : ////////////////////////////////////////////////////////////////////////////////
     507              : // Turn on or off simulation mode. This can only be changed when this
     508              : // device is not active.
     509              : 
     510            0 : void IndiDriver::enableSimulationMode( const bool &oEnable )
     511              : {
     512            0 :   if ( isRunning() == true )
     513            0 :     throw runtime_error( string( "Tried to enabled simulation mode while active." ) );
     514              : 
     515            0 :   m_oIsSimulationModeEnabled = oEnable;
     516            0 : }
     517              : 
     518              : ////////////////////////////////////////////////////////////////////////////////
     519              : // Return the list of email recipients in case an alarm needs to be sent.
     520              : // This list is comma-separated.
     521              : 
     522            0 : string IndiDriver::getEmailList() const
     523              : {
     524            0 :   return m_szEmailList;
     525              : }
     526              : 
     527              : ////////////////////////////////////////////////////////////////////////////////
     528              : // Return the path to where data files that are generated by this driver
     529              : // should be saved.
     530              : 
     531            0 : string IndiDriver::getDataDirectory() const
     532              : {
     533            0 :   return m_szDataDirectory;
     534              : }
     535              : 
     536              : ////////////////////////////////////////////////////////////////////////////////
     537              : // The interval between giving alarms for the same alarm condition (sec).
     538              : 
     539            0 : unsigned int IndiDriver::getAlarmInterval() const
     540              : {
     541            0 :   return m_uiAlarmInterval;
     542              : }
     543              : 
     544              : ////////////////////////////////////////////////////////////////////////////////
     545              : /// At what time was this object instantiated (constructor called)?
     546              : 
     547            0 : TimeStamp IndiDriver::getStartTime() const
     548              : {
     549            0 :   return m_tsStartTime;
     550              : }
     551              : 
     552              : ////////////////////////////////////////////////////////////////////////////////
        

Generated by: LCOV version 2.0-1