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