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 : ////////////////////////////////////////////////////////////////////////////////
|