Line data Source code
1 : /** \file outletController.hpp
2 : * \author Jared R. Males
3 : * \brief Declares and defines a power control device framework in the MagAOXApp context
4 : *
5 : * \ingroup
6 : *
7 : */
8 :
9 : #ifndef app_outletController_hpp
10 : #define app_outletController_hpp
11 :
12 : #include <mx/app/application.hpp>
13 : #include <mx/sys/timeUtils.hpp>
14 :
15 :
16 : #include "../../INDI/libcommon/IndiProperty.hpp"
17 : #include "../../libMagAOX/libMagAOX.hpp"
18 : #include "../indiUtils.hpp"
19 :
20 :
21 : #define OUTLET_STATE_UNKNOWN (-1)
22 : #define OUTLET_STATE_OFF (0)
23 : #define OUTLET_STATE_INTERMEDIATE (1)
24 : #define OUTLET_STATE_ON (2)
25 :
26 :
27 : #define OUTLET_E_NOOUTLETS (-10)
28 : #define OUTLET_E_NOCHANNELS (-15)
29 : #define OUTLET_E_NOVALIDCH (-20)
30 :
31 : namespace MagAOX
32 : {
33 : namespace app
34 : {
35 : namespace dev
36 : {
37 :
38 : /// A generic outlet controller
39 : /** Controls a set of outlets on a device, such as A/C power outlets or digital outputs.
40 : * The outlets are organized into channels, which could be made up of multiple outlets.
41 : *
42 : * derivedT must be a MagAOXApp, and additionally it must implement the functions
43 : * \code
44 : int turnOutletOn( int outletNum );
45 :
46 : int turnOutletOff( int outletNum );
47 :
48 : int updateOutletState( int outletNum );
49 : * \endcode
50 : * and optionally
51 : * \code
52 : int updateOutletStates();
53 : \endcode
54 : *
55 : * Other requirements:
56 : * - call `setNumberOfOutlets` in the derived class constructor
57 : *
58 : *
59 : * \tparam derivedT specifies a MagAOXApp parent base class which is accessed with a `static_cast` (downcast)
60 : * to perform various methods.
61 : *
62 : * \ingroup appdev
63 : *
64 : * \todo finalize 0-counting vs 1-counting rules --> do we subtract one from config-ed outlet indices if m_firstOne is true?
65 : *
66 : */
67 : template<class derivedT>
68 : struct outletController
69 : {
70 : bool m_firstOne {false}; ///< Flag is true if the first outlet is numbered 1, otherwise assumes starting at 0.
71 :
72 : std::vector<int> m_outletStates; ///< The current states of each outlet. These MUST be updated by derived classes in the overridden \ref updatedOutletState.
73 :
74 : pcf::IndiProperty m_indiP_outletStates; ///< Indi Property to show individual outlet states.
75 :
76 : /// Structure containing the specification of one channel.
77 : /** A channel may include more than one outlet, may specify the order in which
78 : * outlets are turned on and/or off, and may specify a delay between turning outlets on
79 : * and/or off.
80 : */
81 : struct channelSpec
82 : {
83 : std::vector<size_t> m_outlets; ///< The outlets in this channel
84 :
85 : std::vector<size_t> m_onOrder; ///< [optional] The order in which outlets are turned on. This contains the indices of m_outlets, not the outlet numbers of the device.
86 : std::vector<size_t> m_offOrder; ///< [optional] The order in which outlets are turned off. This contains the indices of m_outlets, not the outlet numbers of the device.
87 :
88 : std::vector<unsigned> m_onDelays; ///< [optional] The delays between outlets in a multi-oultet channel. The first entry is always ignored. The second entry is the dealy between the first and second outlet, etc.
89 : std::vector<unsigned> m_offDelays; ///< [optional] The delays between outlets in a multi-oultet channel. The first entry is always ignored. The second entry is the dealy between the first and second outlet, etc.
90 :
91 : pcf::IndiProperty m_indiP_prop;
92 : };
93 :
94 : /// The map of channel specifications, which can be accessed by their names.
95 : std::unordered_map<std::string, channelSpec> m_channels;
96 :
97 : /// An INDI property which publishes the outlets associated with each channel. Useful for GUIs, etc.
98 : pcf::IndiProperty m_indiP_chOutlets;
99 :
100 : /// An INDI property which publishes the total on delay for each channel. Useful for GUIs, etc.
101 : pcf::IndiProperty m_indiP_chOnDelays;
102 :
103 : /// An INDI property which publishes the total off delay for each channel. Useful for GUIs, etc.
104 : pcf::IndiProperty m_indiP_chOffDelays;
105 :
106 :
107 : ///Setup an application configurator for an outletController
108 : /** This is currently a no-op
109 : *
110 : * \returns 0 on success
111 : * \returns -1 on failure
112 : */
113 : int setupConfig( mx::app::appConfigurator & config /**< [in] an application configuration to setup */);
114 :
115 : /// Load the [channel] sections from an application configurator
116 : /** Any "unused" section from the config parser is analyzed to determine if it is a channel specification.
117 : * If it contains the `outlet` or `outlets` keyword, then it is considered a channel. `outlet` and `outlets`
118 : * are equivalent, and specify the one or more device outlets included in this channel (i.e. this may be a vector
119 : * value entry).
120 : *
121 : * This function then looks for `onOrder` and `offOrder` keywords, which specify the order outlets are turned
122 : * on or off by their indices in the vector specified by the `outlet`/`outlets` keyword (i.e not the outlet numbers).
123 : *
124 : * Next it looks for `onDelays` and `offDelays`, which specify the delays between outlet operations in milliseconds.
125 : * The first entry is always ignored, then the second entry specifies the delay between the first and second outlet
126 : * operation, etc.
127 : *
128 : * An example config file section is:
129 : \verbatim
130 : [sue] #this channel will be named sue
131 : outlets=4,5 #this channel uses outlets 4 and 5
132 : onOrder=1,0 #outlet 5 will be turned on first
133 : offOrder=0,1 #Outlet 4 will be turned off first
134 : onDelays=0,150 #a 150 msec delay between outlet turn on
135 : offDelays=0,345 #a 345 msec delay between outlet turn off
136 : \endverbatim
137 : *
138 : * \returns 0 on success
139 : * \returns -1 on failure
140 : */
141 : int loadConfig( mx::app::appConfigurator & config /**< [in] an application configuration from which to load values */);
142 :
143 : /// Sets the number of outlets. This should be called by the derived class constructor.
144 : /**
145 : * \returns 0 on success
146 : * \returns -1 on failure
147 : */
148 : int setNumberOfOutlets( int numOuts /**< [in] the number of outlets to allocate */);
149 :
150 : /// Get the currently stored outlet state, without updating from device.
151 : int outletState( int outletNum );
152 :
153 : /// Get the state of the outlet from the device.
154 : /** This will be implemented in derived classes to update the outlet state.
155 : * \todo this is declared pure virtual, but there is an implementation.
156 : * \returns 0 on success.
157 : * \returns -1 on error.
158 : */
159 : virtual int updateOutletState( int outletNum /**< [in] the outlet number to update */) = 0;
160 :
161 : /// Get the states of all outlets from the device.
162 : /** The default implementation for-loops through each outlet, calling \ref updateOutletState.
163 : * Can be re-implemented in derived classes to update the outlet states.
164 : *
165 : * \returns 0 on success.
166 : * \returns -1 on error.
167 : */
168 : virtual int updateOutletStates();
169 :
170 : /// Turn an outlet on.
171 : /** This will be implemented in derived classes to turn an outlet on.
172 : *
173 : * \returns 0 on success.
174 : * \returns -1 on error.
175 : */
176 : virtual int turnOutletOn( int outletNum /**< [in] the outlet number to turn on */) = 0;
177 :
178 : /// Turn an outlet off.
179 : /** This will be implemented in derived classes to turn an outlet off.
180 : *
181 : * \returns 0 on success.
182 : * \returns -1 on error.
183 : */
184 : virtual int turnOutletOff( int outletNum /**< [in] the outlet number to turn off */) = 0;
185 :
186 : /// Get the number of channels
187 : /**
188 : * \returns the number of entries in m_channels.
189 : */
190 : size_t numChannels();
191 :
192 : /// Get the vector of outlet indices for a channel.
193 : /** Mainly used for testing.
194 : *
195 : * \returns the m_outlets member of the channelSpec specified by its name.
196 : */
197 : std::vector<size_t> channelOutlets( const std::string & channel /**< [in] the name of the channel */);
198 :
199 : /// Get the vector of outlet on orders for a channel.
200 : /** Mainly used for testing.
201 : *
202 : * \returns the m_onOrder member of the channelSpec specified by its name.
203 : */
204 : std::vector<size_t> channelOnOrder( const std::string & channel /**< [in] the name of the channel */);
205 :
206 : /// Get the vector of outlet off orders for a channel.
207 : /** Mainly used for testing.
208 : *
209 : * \returns the m_offOrder member of the channelSpec specified by its name.
210 : */
211 : std::vector<size_t> channelOffOrder( const std::string & channel /**< [in] the name of the channel */);
212 :
213 : /// Get the vector of outlet on delays for a channel.
214 : /** Mainly used for testing.
215 : *
216 : * \returns the m_onDelays member of the channelSpec specified by its name.
217 : */
218 : std::vector<unsigned> channelOnDelays( const std::string & channel /**< [in] the name of the channel */);
219 :
220 : /// Get the vector of outlet off delays for a channel.
221 : /** Mainly used for testing.
222 : *
223 : * \returns the m_offDelays member of the channelSpec specified by its name.
224 : */
225 : std::vector<unsigned> channelOffDelays( const std::string & channel /**< [in] the name of the channel */);
226 :
227 : /// Get the state of a channel.
228 : /**
229 : * \returns OUTLET_STATE_UNKNOWN if the state is not known
230 : * \returns OUTLET_STATE_OFF if the channel is off (all outlets off)
231 : * \returns OUTLET_STATE_INTERMEDIATE if outlets are intermediate or not in the same state
232 : * \returns OUTLET_STATE_ON if channel is on (all outlets on)
233 : */
234 : int channelState( const std::string & channel /**< [in] the name of the channel */);
235 :
236 : /// Turn a channel on.
237 : /** This implements the outlet order and delay logic.
238 : *
239 : * \returns 0 on success.
240 : * \returns -1 on error.
241 : */
242 : int turnChannelOn( const std::string & channel /**< [in] the name of the channel to turn on*/);
243 :
244 : /// Turn a channel off.
245 : /** This implements the outlet order and delay logic.
246 : *
247 : * \returns 0 on success.
248 : * \returns -1 on error.
249 : */
250 : int turnChannelOff( const std::string & channel /**< [in] the name of the channel to turn on*/);
251 :
252 :
253 : /** \name INDI Setup
254 : *@{
255 : */
256 :
257 : /// The static callback function to be registered for the channel properties.
258 : /**
259 : * \returns 0 on success.
260 : * \returns -1 on error.
261 : */
262 : static int st_newCallBack_channels( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
263 : const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
264 : );
265 :
266 : /// The callback called by the static version, to actually process the new request.
267 : /**
268 : * \returns 0 on success.
269 : * \returns -1 on error.
270 : */
271 : int newCallBack_channels( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
272 :
273 : /// Setup the INDI properties for this device controller
274 : /** This should be called in the `appStartup` function of the derived MagAOXApp.
275 : * \todo change this to be appStartup like other devs.
276 : * \returns 0 on success.
277 : * \returns -1 on error.
278 : */
279 : int setupINDI();
280 :
281 : /// Update the INDI properties for this device controller
282 : /** You should call this after updating the outlet states.
283 : * It is not called automatically.
284 : *
285 : * \returns 0 on success.
286 : * \returns -1 on error.
287 : */
288 : int updateINDI();
289 :
290 : ///@}
291 :
292 :
293 : private:
294 0 : derivedT & derived()
295 : {
296 0 : return *static_cast<derivedT *>(this);
297 : }
298 : };
299 :
300 : template<class derivedT>
301 29 : int outletController<derivedT>::setupConfig( mx::app::appConfigurator & config )
302 : {
303 : static_cast<void>(config);
304 :
305 29 : return 0;
306 : }
307 :
308 : template<class derivedT>
309 29 : int outletController<derivedT>::loadConfig( mx::app::appConfigurator & config )
310 : {
311 29 : if( m_outletStates.size() == 0) return OUTLET_E_NOOUTLETS;
312 :
313 : //Get the "unused" sections.
314 29 : std::vector<std::string> sections;
315 :
316 29 : config.unusedSections(sections);
317 :
318 29 : if( sections.size() == 0 ) return OUTLET_E_NOCHANNELS;
319 :
320 : //Now see if any are channels, which means they have an outlet= or outlets= entry
321 29 : std::vector<std::string> chSections;
322 :
323 103 : for(size_t i=0;i<sections.size(); ++i)
324 : {
325 222 : if( config.isSetUnused( mx::app::iniFile::makeKey(sections[i], "outlet" ))
326 172 : || config.isSetUnused( mx::app::iniFile::makeKey(sections[i], "outlets" )) )
327 : {
328 74 : chSections.push_back(sections[i]);
329 : }
330 : }
331 :
332 29 : if( chSections.size() == 0 ) return OUTLET_E_NOVALIDCH;
333 :
334 : //Now configure the chanels.
335 177 : for(size_t n = 0; n < chSections.size(); ++n)
336 : {
337 74 : m_channels.emplace( chSections[n] , channelSpec());
338 :
339 : //---- Set outlets ----
340 74 : std::vector<size_t> outlets;
341 148 : if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "outlet" )))
342 : {
343 124 : config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n], "outlet" ) );
344 : }
345 : else
346 : {
347 24 : config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n], "outlets" ) );
348 : }
349 :
350 : //Subtract one if the device numbers from 1.
351 190 : for(size_t k=0;k<outlets.size(); ++k)
352 : {
353 : ///\todo test this error
354 116 : if( (int) outlets[k] - m_firstOne < 0 || (int) outlets[k] - m_firstOne > (int) m_outletStates.size())
355 : {
356 : #ifndef OUTLET_CTRL_TEST_NOLOG
357 0 : return derivedT::template log<text_log,-1>("Outlet " + std::to_string(outlets[k]) + " in Channel " + chSections[n] + " is invalid", logPrio::LOG_ERROR);
358 : #else
359 0 : return -1;
360 : #endif
361 : }
362 :
363 116 : outlets[k] -= m_firstOne;
364 : }
365 :
366 74 : m_channels[chSections[n]].m_outlets = outlets;
367 : ///\todo error checking on outlets
368 :
369 : //---- Set optional configs ----
370 148 : if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "onOrder" )))
371 : {
372 34 : std::vector<size_t> onOrder;
373 34 : config.configUnused( onOrder, mx::app::iniFile::makeKey(chSections[n], "onOrder" ) );
374 :
375 : ///\todo test this error
376 34 : if(onOrder.size() != m_channels[chSections[n]].m_outlets.size())
377 : {
378 : #ifndef OUTLET_CTRL_TEST_NOLOG
379 0 : return derivedT::template log<text_log,-1>("onOrder be same size as outlets. In Channel " + chSections[n], logPrio::LOG_ERROR);
380 : #else
381 0 : return -1;
382 : #endif
383 : }
384 :
385 34 : m_channels[chSections[n]].m_onOrder = onOrder;
386 34 : }
387 :
388 148 : if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "offOrder" )))
389 : {
390 26 : std::vector<size_t> offOrder;
391 26 : config.configUnused( offOrder, mx::app::iniFile::makeKey(chSections[n], "offOrder" ) );
392 26 : m_channels[chSections[n]].m_offOrder = offOrder;
393 : ///\todo error checking on offOrder, should complain if not same length
394 26 : }
395 :
396 148 : if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "onDelays" )))
397 : {
398 16 : std::vector<unsigned> onDelays;
399 16 : config.configUnused( onDelays, mx::app::iniFile::makeKey(chSections[n], "onDelays" ) );
400 16 : m_channels[chSections[n]].m_onDelays = onDelays;
401 : ///\todo error checking on onDelays, should complain if not same length
402 16 : }
403 :
404 148 : if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "offDelays" )))
405 : {
406 16 : std::vector<unsigned> offDelays;
407 16 : config.configUnused( offDelays, mx::app::iniFile::makeKey(chSections[n], "offDelays" ) );
408 16 : m_channels[chSections[n]].m_offDelays = offDelays;
409 : ///\todo error checking on offDelays, should complain if not same length
410 16 : }
411 : }
412 :
413 29 : return 0;
414 29 : }
415 :
416 : template<class derivedT>
417 29 : int outletController<derivedT>::setNumberOfOutlets( int numOuts )
418 : {
419 29 : m_outletStates.resize(numOuts, -1);
420 29 : return 0;
421 : }
422 :
423 : template<class derivedT>
424 428 : int outletController<derivedT>::outletState( int outletNum )
425 : {
426 428 : return m_outletStates[outletNum];
427 : }
428 :
429 : template<class derivedT>
430 0 : int outletController<derivedT>::updateOutletStates()
431 : {
432 0 : for(size_t n=0; n<m_outletStates.size(); ++n)
433 : {
434 0 : int rv = updateOutletState(n);
435 0 : if(rv < 0) return rv;
436 : }
437 :
438 0 : return 0;
439 : }
440 :
441 : template<class derivedT>
442 8 : size_t outletController<derivedT>::numChannels()
443 : {
444 8 : return m_channels.size();
445 : }
446 :
447 : template<class derivedT>
448 24 : std::vector<size_t> outletController<derivedT>::channelOutlets( const std::string & channel )
449 : {
450 24 : return m_channels[channel].m_outlets;
451 : }
452 :
453 : template<class derivedT>
454 24 : std::vector<size_t> outletController<derivedT>::channelOnOrder( const std::string & channel )
455 : {
456 24 : return m_channels[channel].m_onOrder;
457 : }
458 :
459 : template<class derivedT>
460 24 : std::vector<size_t> outletController<derivedT>::channelOffOrder( const std::string & channel )
461 : {
462 24 : return m_channels[channel].m_offOrder;
463 : }
464 :
465 : template<class derivedT>
466 24 : std::vector<unsigned> outletController<derivedT>::channelOnDelays( const std::string & channel )
467 : {
468 24 : return m_channels[channel].m_onDelays;
469 : }
470 :
471 : template<class derivedT>
472 24 : std::vector<unsigned> outletController<derivedT>::channelOffDelays( const std::string & channel )
473 : {
474 24 : return m_channels[channel].m_offDelays;
475 : }
476 :
477 : template<class derivedT>
478 188 : int outletController<derivedT>::channelState( const std::string & channel )
479 : {
480 188 : int st = outletState(m_channels[channel].m_outlets[0]);
481 :
482 292 : for( size_t n = 1; n < m_channels[channel].m_outlets.size(); ++n )
483 : {
484 104 : if( st != outletState(m_channels[channel].m_outlets[n]) ) st = 1;
485 : }
486 :
487 188 : return st;
488 : }
489 :
490 : template<class derivedT>
491 34 : int outletController<derivedT>::turnChannelOn( const std::string & channel )
492 : {
493 :
494 : #ifndef OUTLET_CTRL_TEST_NOLOG
495 0 : derivedT::template log<software_debug>({__FILE__, __LINE__, "turning on channel " + channel});
496 : #endif
497 :
498 : #ifndef OUTLET_CTRL_TEST_NOINDI
499 : //m_channels[channel].m_indiP_prop["target"].setValue("On");
500 0 : indi::updateIfChanged(m_channels[channel].m_indiP_prop, "target", std::string("On"), derived().m_indiDriver, INDI_BUSY );
501 : #endif
502 :
503 : //If order is specified, get first outlet number
504 34 : size_t n = 0;
505 34 : if( m_channels[channel].m_onOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_onOrder[0];
506 :
507 : //turn on first outlet.
508 34 : if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
509 : {
510 :
511 : #ifndef OUTLET_CTRL_TEST_NOLOG
512 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "error turning on outlet " + std::to_string(n)});
513 : #else
514 0 : std::cerr << "Failed to turn on outlet " << n << "\n";
515 : #endif
516 :
517 0 : return -1;
518 : }
519 :
520 : #ifndef OUTLET_CTRL_TEST_NOLOG
521 0 : derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 2});
522 : #endif
523 :
524 : //Now do the rest
525 56 : for(size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
526 : {
527 : //If order is specified, get next outlet number
528 22 : n=i;
529 22 : if( m_channels[channel].m_onOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_onOrder[i];
530 :
531 : //Delay if specified
532 22 : if( m_channels[channel].m_onDelays.size() == m_channels[channel].m_outlets.size() )
533 : {
534 4 : mx::sys::milliSleep(m_channels[channel].m_onDelays[i]);
535 : }
536 :
537 : //turn on next outlet
538 :
539 22 : if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
540 : {
541 :
542 : #ifndef OUTLET_CTRL_TEST_NOLOG
543 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "error turning on outlet " + std::to_string(n)});
544 : #else
545 0 : std::cerr << "Failed to turn on outlet " << n << "\n";
546 : #endif
547 :
548 0 : return -1;
549 : }
550 :
551 : #ifndef OUTLET_CTRL_TEST_NOLOG
552 0 : derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne ), 2});
553 : #endif
554 : }
555 :
556 : #ifndef OUTLET_CTRL_TEST_NOLOG
557 0 : derivedT::template log<outlet_channel_state>({ channel, 2});
558 : #endif
559 :
560 34 : return 0;
561 : }
562 :
563 : template<class derivedT>
564 34 : int outletController<derivedT>::turnChannelOff( const std::string & channel )
565 : {
566 :
567 : #ifndef OUTLET_CTRL_TEST_NOLOG
568 0 : derivedT::template log<software_debug>({__FILE__, __LINE__, "turning off channel " + channel});
569 : #endif
570 :
571 : #ifndef OUTLET_CTRL_TEST_NOINDI
572 : //m_channels[channel].m_indiP_prop["target"].setValue("Off");
573 0 : indi::updateIfChanged(m_channels[channel].m_indiP_prop, "target", std::string("Off"), derived().m_indiDriver, INDI_BUSY );
574 : #endif
575 :
576 : //If order is specified, get first outlet number
577 34 : size_t n = 0;
578 34 : if( m_channels[channel].m_offOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_offOrder[0];
579 :
580 : //turn off first outlet.
581 34 : if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
582 : {
583 : #ifndef OUTLET_CTRL_TEST_NOLOG
584 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "error turning off outlet " + std::to_string(n)});
585 : #else
586 0 : std::cerr << "Failed to turn off outlet " << n << "\n";
587 : #endif
588 :
589 0 : return -1;
590 : }
591 :
592 : #ifndef OUTLET_CTRL_TEST_NOLOG
593 0 : derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 0});
594 : #endif
595 :
596 : //Now do the rest
597 56 : for(size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
598 : {
599 : //If order is specified, get next outlet number
600 22 : n=i;
601 22 : if( m_channels[channel].m_offOrder.size() == m_channels[channel].m_outlets.size() ) n = m_channels[channel].m_offOrder[i];
602 :
603 : //Delay if specified
604 22 : if( m_channels[channel].m_offDelays.size() == m_channels[channel].m_outlets.size() )
605 : {
606 4 : mx::sys::milliSleep(m_channels[channel].m_offDelays[i]);
607 : }
608 :
609 : //turn off next outlet
610 22 : if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
611 : {
612 : #ifndef OUTLET_CTRL_TEST_NOLOG
613 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "error turning off outlet " + std::to_string(n)});
614 : #else
615 0 : std::cerr << "Failed to turn off outlet " << n << "\n";
616 : #endif
617 :
618 0 : return -1;
619 : }
620 :
621 : #ifndef OUTLET_CTRL_TEST_NOLOG
622 0 : derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 0});
623 : #endif
624 : }
625 :
626 : #ifndef OUTLET_CTRL_TEST_NOLOG
627 0 : derivedT::template log<outlet_channel_state>({ channel, 0});
628 : #endif
629 :
630 34 : return 0;
631 : }
632 :
633 : template<class derivedT>
634 0 : int outletController<derivedT>::st_newCallBack_channels( void * app,
635 : const pcf::IndiProperty &ipRecv
636 : )
637 : {
638 0 : return static_cast<derivedT *>(app)->newCallBack_channels(ipRecv);
639 : }
640 :
641 : template<class derivedT>
642 0 : int outletController<derivedT>::newCallBack_channels( const pcf::IndiProperty &ipRecv )
643 : {
644 : //Check if we're in state READY before doing anything
645 0 : if(derived().state() != stateCodes::READY)
646 : {
647 : #ifndef OUTLET_CTRL_TEST_NOLOG
648 0 : derivedT::template log<text_log>("can't change outlet state when not READY", logPrio::LOG_ERROR);
649 : #endif
650 :
651 0 : return -1;
652 : }
653 :
654 : //Interogate ipRecv to figure out which channel it is.
655 : //And then call turn on or turn off based on requested state.
656 0 : std::string name = ipRecv.getName();
657 :
658 0 : std::string state, target;
659 :
660 0 : if(ipRecv.find("state"))
661 : {
662 0 : state = ipRecv["state"].get<std::string>();
663 : }
664 :
665 0 : if(ipRecv.find("target"))
666 : {
667 0 : target = ipRecv["target"].get<std::string>();
668 : }
669 :
670 0 : if( target == "" ) target = state;
671 0 : target = mx::ioutils::toUpper(target);
672 :
673 0 : if( target == "ON" )
674 : {
675 0 : return turnChannelOn(name);
676 : }
677 :
678 0 : if(target == "OFF")
679 : {
680 0 : return turnChannelOff(name);
681 : }
682 :
683 0 : return 0;
684 0 : }
685 :
686 : template<class derivedT>
687 0 : int outletController<derivedT>::setupINDI()
688 : {
689 : //Register the static INDI properties
690 0 : m_indiP_chOutlets = pcf::IndiProperty(pcf::IndiProperty::Text);
691 0 : m_indiP_chOutlets.setDevice(derived().configName());
692 0 : m_indiP_chOutlets.setName("channelOutlets");
693 0 : m_indiP_chOutlets.setPerm(pcf::IndiProperty::ReadOnly);
694 0 : m_indiP_chOutlets.setState(pcf::IndiProperty::Idle);
695 :
696 0 : if(derived().registerIndiPropertyReadOnly(m_indiP_chOutlets) < 0)
697 : {
698 : #ifndef OUTLET_CTRL_TEST_NOLOG
699 0 : derivedT::template log<software_error>({__FILE__,__LINE__});
700 : #endif
701 0 : return -1;
702 : }
703 :
704 0 : m_indiP_chOnDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
705 0 : m_indiP_chOnDelays.setDevice(derived().configName());
706 0 : m_indiP_chOnDelays.setName("channelOnDelays");
707 0 : m_indiP_chOnDelays.setPerm(pcf::IndiProperty::ReadOnly);
708 0 : m_indiP_chOnDelays.setState(pcf::IndiProperty::Idle);
709 :
710 0 : if(derived().registerIndiPropertyReadOnly(m_indiP_chOnDelays) < 0)
711 : {
712 : #ifndef OUTLET_CTRL_TEST_NOLOG
713 0 : derivedT::template log<software_error>({__FILE__,__LINE__});
714 : #endif
715 0 : return -1;
716 : }
717 :
718 0 : m_indiP_chOffDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
719 0 : m_indiP_chOffDelays.setDevice(derived().configName());
720 0 : m_indiP_chOffDelays.setName("channelOffDelays");
721 0 : m_indiP_chOffDelays.setPerm(pcf::IndiProperty::ReadOnly);
722 0 : m_indiP_chOffDelays.setState(pcf::IndiProperty::Idle);
723 :
724 0 : if(derived().registerIndiPropertyReadOnly(m_indiP_chOffDelays) < 0)
725 : {
726 : #ifndef OUTLET_CTRL_TEST_NOLOG
727 0 : derivedT::template log<software_error>({__FILE__,__LINE__});
728 : #endif
729 0 : return -1;
730 : }
731 :
732 : //Create channel properties and register callback.
733 0 : for(auto it = m_channels.begin(); it != m_channels.end(); ++it)
734 : {
735 0 : it->second.m_indiP_prop = pcf::IndiProperty (pcf::IndiProperty::Text);
736 0 : it->second.m_indiP_prop.setDevice(derived().configName());
737 0 : it->second.m_indiP_prop.setName(it->first);
738 0 : it->second.m_indiP_prop.setPerm(pcf::IndiProperty::ReadWrite);
739 0 : it->second.m_indiP_prop.setState( pcf::IndiProperty::Idle );
740 :
741 : //add elements 'state' and 'target'
742 0 : it->second.m_indiP_prop.add (pcf::IndiElement("state"));
743 0 : it->second.m_indiP_prop.add (pcf::IndiElement("target"));
744 :
745 0 : if( derived().registerIndiPropertyNew( it->second.m_indiP_prop, st_newCallBack_channels) < 0)
746 : {
747 : #ifndef OUTLET_CTRL_TEST_NOLOG
748 0 : derivedT::template log<software_error>({__FILE__,__LINE__});
749 : #endif
750 0 : return -1;
751 : }
752 :
753 : //Load values into the static INDI properties
754 0 : m_indiP_chOutlets.add(pcf::IndiElement(it->first));
755 0 : std::string os = std::to_string(it->second.m_outlets[0]);
756 0 : for(size_t i=1;i< it->second.m_outlets.size();++i) os += "," + std::to_string(it->second.m_outlets[i]);
757 0 : m_indiP_chOutlets[it->first].set(os);
758 :
759 0 : m_indiP_chOnDelays.add(pcf::IndiElement(it->first));
760 0 : double sum=0;
761 0 : for(size_t i=0;i< it->second.m_onDelays.size();++i) sum += it->second.m_onDelays[i];
762 0 : m_indiP_chOnDelays[it->first].set(sum);
763 :
764 0 : m_indiP_chOffDelays.add(pcf::IndiElement(it->first));
765 0 : sum=0;
766 0 : for(size_t i=0;i< it->second.m_offDelays.size();++i) sum += it->second.m_offDelays[i];
767 0 : m_indiP_chOffDelays[it->first].set(sum);
768 :
769 : }
770 :
771 : //Register the outletStates INDI property, and add an element for each outlet.
772 0 : m_indiP_outletStates = pcf::IndiProperty (pcf::IndiProperty::Text);
773 0 : m_indiP_outletStates.setDevice(derived().configName());
774 0 : m_indiP_outletStates.setName("outlet");
775 0 : m_indiP_outletStates.setPerm(pcf::IndiProperty::ReadWrite);
776 0 : m_indiP_outletStates.setState( pcf::IndiProperty::Idle );
777 :
778 0 : if( derived().registerIndiPropertyReadOnly(m_indiP_outletStates) < 0)
779 : {
780 : #ifndef OUTLET_CTRL_TEST_NOLOG
781 0 : derivedT::template log<software_error>({__FILE__,__LINE__});
782 : #endif
783 0 : return -1;
784 : }
785 : /*
786 : auto result = derived().m_indiNewCallBacks.insert( { "outlet", {&m_indiP_outletStates, nullptr}});
787 :
788 : if(!result.second)
789 : {
790 : return -1;
791 : }*/
792 :
793 0 : for(size_t i=0; i< m_outletStates.size(); ++i)
794 : {
795 0 : m_indiP_outletStates.add (pcf::IndiElement(std::to_string(i+m_firstOne)));
796 : }
797 :
798 0 : return 0;
799 : }
800 :
801 : std::string stateIntToString(int st);
802 :
803 : template<class derivedT>
804 0 : int outletController<derivedT>::updateINDI()
805 : {
806 0 : if( !derived().m_indiDriver ) return 0;
807 :
808 : //Publish outlet states (only bother if they've changed)
809 0 : for(size_t i=0; i< m_outletStates.size(); ++i)
810 : {
811 0 : indi::updateIfChanged(m_indiP_outletStates, std::to_string(i+m_firstOne), stateIntToString(m_outletStates[i]), derived().m_indiDriver);
812 : }
813 :
814 : //Publish channel states (only bother if they've changed)
815 0 : for(auto it = m_channels.begin(); it != m_channels.end(); ++it)
816 : {
817 0 : std::string state = stateIntToString( channelState( it->first ));
818 :
819 0 : indi::updateIfChanged( it->second.m_indiP_prop, "state", state, derived().m_indiDriver );
820 : }
821 :
822 :
823 :
824 0 : return 0;
825 : }
826 :
827 : } //namespace dev
828 : } //namespace app
829 : } //namespace MagAOX
830 :
831 : #endif //app_outletController_hpp
|