API
 
Loading...
Searching...
No Matches
outletController.hpp
Go to the documentation of this file.
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
31namespace MagAOX
32{
33namespace app
34{
35namespace 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 */
67template<class derivedT>
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 */
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 */
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 */
289
290 ///@}
291
292
293private:
294 derivedT & derived()
295 {
296 return *static_cast<derivedT *>(this);
297 }
298};
299
300template<class derivedT>
301int outletController<derivedT>::setupConfig( mx::app::appConfigurator & config )
302{
303 static_cast<void>(config);
304
305 return 0;
306}
307
308template<class derivedT>
309int outletController<derivedT>::loadConfig( mx::app::appConfigurator & config )
310{
311 if( m_outletStates.size() == 0) return OUTLET_E_NOOUTLETS;
312
313 //Get the "unused" sections.
314 std::vector<std::string> sections;
315
316 config.unusedSections(sections);
317
318 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 std::vector<std::string> chSections;
322
323 for(size_t i=0;i<sections.size(); ++i)
324 {
325 if( config.isSetUnused( mx::app::iniFile::makeKey(sections[i], "outlet" ))
326 || config.isSetUnused( mx::app::iniFile::makeKey(sections[i], "outlets" )) )
327 {
328 chSections.push_back(sections[i]);
329 }
330 }
331
332 if( chSections.size() == 0 ) return OUTLET_E_NOVALIDCH;
333
334 //Now configure the chanels.
335 for(size_t n = 0; n < chSections.size(); ++n)
336 {
337 m_channels.emplace( chSections[n] , channelSpec());
338
339 //---- Set outlets ----
340 std::vector<size_t> outlets;
341 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "outlet" )))
342 {
343 config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n], "outlet" ) );
344 }
345 else
346 {
347 config.configUnused( outlets, mx::app::iniFile::makeKey(chSections[n], "outlets" ) );
348 }
349
350 //Subtract one if the device numbers from 1.
351 for(size_t k=0;k<outlets.size(); ++k)
352 {
353 ///\todo test this error
354 if( (int) outlets[k] - m_firstOne < 0 || (int) outlets[k] - m_firstOne > (int) m_outletStates.size())
355 {
356 #ifndef OUTLET_CTRL_TEST_NOLOG
357 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 return -1;
360 #endif
361 }
362
363 outlets[k] -= m_firstOne;
364 }
365
366 m_channels[chSections[n]].m_outlets = outlets;
367 ///\todo error checking on outlets
368
369 //---- Set optional configs ----
370 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "onOrder" )))
371 {
372 std::vector<size_t> onOrder;
373 config.configUnused( onOrder, mx::app::iniFile::makeKey(chSections[n], "onOrder" ) );
374
375 ///\todo test this error
376 if(onOrder.size() != m_channels[chSections[n]].m_outlets.size())
377 {
378 #ifndef OUTLET_CTRL_TEST_NOLOG
379 return derivedT::template log<text_log,-1>("onOrder be same size as outlets. In Channel " + chSections[n], logPrio::LOG_ERROR);
380 #else
381 return -1;
382 #endif
383 }
384
385 m_channels[chSections[n]].m_onOrder = onOrder;
386 }
387
388 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "offOrder" )))
389 {
390 std::vector<size_t> offOrder;
391 config.configUnused( offOrder, mx::app::iniFile::makeKey(chSections[n], "offOrder" ) );
392 m_channels[chSections[n]].m_offOrder = offOrder;
393 ///\todo error checking on offOrder, should complain if not same length
394 }
395
396 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "onDelays" )))
397 {
398 std::vector<unsigned> onDelays;
399 config.configUnused( onDelays, mx::app::iniFile::makeKey(chSections[n], "onDelays" ) );
400 m_channels[chSections[n]].m_onDelays = onDelays;
401 ///\todo error checking on onDelays, should complain if not same length
402 }
403
404 if( config.isSetUnused( mx::app::iniFile::makeKey(chSections[n], "offDelays" )))
405 {
406 std::vector<unsigned> offDelays;
407 config.configUnused( offDelays, mx::app::iniFile::makeKey(chSections[n], "offDelays" ) );
408 m_channels[chSections[n]].m_offDelays = offDelays;
409 ///\todo error checking on offDelays, should complain if not same length
410 }
411 }
412
413 return 0;
414}
415
416template<class derivedT>
418{
419 m_outletStates.resize(numOuts, -1);
420 return 0;
421}
422
423template<class derivedT>
425{
426 return m_outletStates[outletNum];
427}
428
429template<class derivedT>
431{
432 for(size_t n=0; n<m_outletStates.size(); ++n)
433 {
434 int rv = updateOutletState(n);
435 if(rv < 0) return rv;
436 }
437
438 return 0;
439}
440
441template<class derivedT>
443{
444 return m_channels.size();
445}
446
447template<class derivedT>
448std::vector<size_t> outletController<derivedT>::channelOutlets( const std::string & channel )
449{
450 return m_channels[channel].m_outlets;
451}
452
453template<class derivedT>
454std::vector<size_t> outletController<derivedT>::channelOnOrder( const std::string & channel )
455{
456 return m_channels[channel].m_onOrder;
457}
458
459template<class derivedT>
460std::vector<size_t> outletController<derivedT>::channelOffOrder( const std::string & channel )
461{
462 return m_channels[channel].m_offOrder;
463}
464
465template<class derivedT>
466std::vector<unsigned> outletController<derivedT>::channelOnDelays( const std::string & channel )
467{
468 return m_channels[channel].m_onDelays;
469}
470
471template<class derivedT>
472std::vector<unsigned> outletController<derivedT>::channelOffDelays( const std::string & channel )
473{
474 return m_channels[channel].m_offDelays;
475}
476
477template<class derivedT>
478int outletController<derivedT>::channelState( const std::string & channel )
479{
480 int st = outletState(m_channels[channel].m_outlets[0]);
481
482 for( size_t n = 1; n < m_channels[channel].m_outlets.size(); ++n )
483 {
484 if( st != outletState(m_channels[channel].m_outlets[n]) ) st = 1;
485 }
486
487 return st;
488}
489
490template<class derivedT>
491int outletController<derivedT>::turnChannelOn( const std::string & channel )
492{
493
494 #ifndef OUTLET_CTRL_TEST_NOLOG
495 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 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 size_t n = 0;
505 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 if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
509 {
510
511 #ifndef OUTLET_CTRL_TEST_NOLOG
512 derivedT::template log<software_error>({__FILE__, __LINE__, "error turning on outlet " + std::to_string(n)});
513 #else
514 std::cerr << "Failed to turn on outlet " << n << "\n";
515 #endif
516
517 return -1;
518 }
519
520 #ifndef OUTLET_CTRL_TEST_NOLOG
521 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 2});
522 #endif
523
524 //Now do the rest
525 for(size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
526 {
527 //If order is specified, get next outlet number
528 n=i;
529 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 if( m_channels[channel].m_onDelays.size() == m_channels[channel].m_outlets.size() )
533 {
534 mx::sys::milliSleep(m_channels[channel].m_onDelays[i]);
535 }
536
537 //turn on next outlet
538
539 if( turnOutletOn(m_channels[channel].m_outlets[n]) < 0 )
540 {
541
542 #ifndef OUTLET_CTRL_TEST_NOLOG
543 derivedT::template log<software_error>({__FILE__, __LINE__, "error turning on outlet " + std::to_string(n)});
544 #else
545 std::cerr << "Failed to turn on outlet " << n << "\n";
546 #endif
547
548 return -1;
549 }
550
551 #ifndef OUTLET_CTRL_TEST_NOLOG
552 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne ), 2});
553 #endif
554 }
555
556 #ifndef OUTLET_CTRL_TEST_NOLOG
557 derivedT::template log<outlet_channel_state>({ channel, 2});
558 #endif
559
560 return 0;
561}
562
563template<class derivedT>
564int outletController<derivedT>::turnChannelOff( const std::string & channel )
565{
566
567 #ifndef OUTLET_CTRL_TEST_NOLOG
568 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 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 size_t n = 0;
578 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 if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
582 {
583 #ifndef OUTLET_CTRL_TEST_NOLOG
584 derivedT::template log<software_error>({__FILE__, __LINE__, "error turning off outlet " + std::to_string(n)});
585 #else
586 std::cerr << "Failed to turn off outlet " << n << "\n";
587 #endif
588
589 return -1;
590 }
591
592 #ifndef OUTLET_CTRL_TEST_NOLOG
593 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 0});
594 #endif
595
596 //Now do the rest
597 for(size_t i = 1; i< m_channels[channel].m_outlets.size(); ++i)
598 {
599 //If order is specified, get next outlet number
600 n=i;
601 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 if( m_channels[channel].m_offDelays.size() == m_channels[channel].m_outlets.size() )
605 {
606 mx::sys::milliSleep(m_channels[channel].m_offDelays[i]);
607 }
608
609 //turn off next outlet
610 if( turnOutletOff(m_channels[channel].m_outlets[n]) < 0 )
611 {
612 #ifndef OUTLET_CTRL_TEST_NOLOG
613 derivedT::template log<software_error>({__FILE__, __LINE__, "error turning off outlet " + std::to_string(n)});
614 #else
615 std::cerr << "Failed to turn off outlet " << n << "\n";
616 #endif
617
618 return -1;
619 }
620
621 #ifndef OUTLET_CTRL_TEST_NOLOG
622 derivedT::template log<outlet_state>({ (uint8_t) (n + m_firstOne), 0});
623 #endif
624 }
625
626 #ifndef OUTLET_CTRL_TEST_NOLOG
627 derivedT::template log<outlet_channel_state>({ channel, 0});
628 #endif
629
630 return 0;
631}
632
633template<class derivedT>
635 const pcf::IndiProperty &ipRecv
636 )
637{
638 return static_cast<derivedT *>(app)->newCallBack_channels(ipRecv);
639}
640
641template<class derivedT>
643{
644 //Check if we're in state READY before doing anything
645 if(derived().state() != stateCodes::READY)
646 {
647 #ifndef OUTLET_CTRL_TEST_NOLOG
648 derivedT::template log<text_log>("can't change outlet state when not READY", logPrio::LOG_ERROR);
649 #endif
650
651 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 std::string name = ipRecv.getName();
657
658 std::string state, target;
659
660 if(ipRecv.find("state"))
661 {
662 state = ipRecv["state"].get<std::string>();
663 }
664
665 if(ipRecv.find("target"))
666 {
667 target = ipRecv["target"].get<std::string>();
668 }
669
670 if( target == "" ) target = state;
671 target = mx::ioutils::toUpper(target);
672
673 if( target == "ON" )
674 {
675 return turnChannelOn(name);
676 }
677
678 if(target == "OFF")
679 {
680 return turnChannelOff(name);
681 }
682
683 return 0;
684}
685
686template<class derivedT>
688{
689 //Register the static INDI properties
690 m_indiP_chOutlets = pcf::IndiProperty(pcf::IndiProperty::Text);
691 m_indiP_chOutlets.setDevice(derived().configName());
692 m_indiP_chOutlets.setName("channelOutlets");
693 m_indiP_chOutlets.setPerm(pcf::IndiProperty::ReadOnly);
694 m_indiP_chOutlets.setState(pcf::IndiProperty::Idle);
695
696 if(derived().registerIndiPropertyReadOnly(m_indiP_chOutlets) < 0)
697 {
698 #ifndef OUTLET_CTRL_TEST_NOLOG
699 derivedT::template log<software_error>({__FILE__,__LINE__});
700 #endif
701 return -1;
702 }
703
704 m_indiP_chOnDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
705 m_indiP_chOnDelays.setDevice(derived().configName());
706 m_indiP_chOnDelays.setName("channelOnDelays");
707 m_indiP_chOnDelays.setPerm(pcf::IndiProperty::ReadOnly);
708 m_indiP_chOnDelays.setState(pcf::IndiProperty::Idle);
709
710 if(derived().registerIndiPropertyReadOnly(m_indiP_chOnDelays) < 0)
711 {
712 #ifndef OUTLET_CTRL_TEST_NOLOG
713 derivedT::template log<software_error>({__FILE__,__LINE__});
714 #endif
715 return -1;
716 }
717
718 m_indiP_chOffDelays = pcf::IndiProperty (pcf::IndiProperty::Number);
719 m_indiP_chOffDelays.setDevice(derived().configName());
720 m_indiP_chOffDelays.setName("channelOffDelays");
721 m_indiP_chOffDelays.setPerm(pcf::IndiProperty::ReadOnly);
722 m_indiP_chOffDelays.setState(pcf::IndiProperty::Idle);
723
724 if(derived().registerIndiPropertyReadOnly(m_indiP_chOffDelays) < 0)
725 {
726 #ifndef OUTLET_CTRL_TEST_NOLOG
727 derivedT::template log<software_error>({__FILE__,__LINE__});
728 #endif
729 return -1;
730 }
731
732 //Create channel properties and register callback.
733 for(auto it = m_channels.begin(); it != m_channels.end(); ++it)
734 {
735 it->second.m_indiP_prop = pcf::IndiProperty (pcf::IndiProperty::Text);
736 it->second.m_indiP_prop.setDevice(derived().configName());
737 it->second.m_indiP_prop.setName(it->first);
738 it->second.m_indiP_prop.setPerm(pcf::IndiProperty::ReadWrite);
739 it->second.m_indiP_prop.setState( pcf::IndiProperty::Idle );
740
741 //add elements 'state' and 'target'
742 it->second.m_indiP_prop.add (pcf::IndiElement("state"));
743 it->second.m_indiP_prop.add (pcf::IndiElement("target"));
744
745 if( derived().registerIndiPropertyNew( it->second.m_indiP_prop, st_newCallBack_channels) < 0)
746 {
747 #ifndef OUTLET_CTRL_TEST_NOLOG
748 derivedT::template log<software_error>({__FILE__,__LINE__});
749 #endif
750 return -1;
751 }
752
753 //Load values into the static INDI properties
754 m_indiP_chOutlets.add(pcf::IndiElement(it->first));
755 std::string os = std::to_string(it->second.m_outlets[0]);
756 for(size_t i=1;i< it->second.m_outlets.size();++i) os += "," + std::to_string(it->second.m_outlets[i]);
757 m_indiP_chOutlets[it->first].set(os);
758
759 m_indiP_chOnDelays.add(pcf::IndiElement(it->first));
760 double sum=0;
761 for(size_t i=0;i< it->second.m_onDelays.size();++i) sum += it->second.m_onDelays[i];
762 m_indiP_chOnDelays[it->first].set(sum);
763
764 m_indiP_chOffDelays.add(pcf::IndiElement(it->first));
765 sum=0;
766 for(size_t i=0;i< it->second.m_offDelays.size();++i) sum += it->second.m_offDelays[i];
767 m_indiP_chOffDelays[it->first].set(sum);
768
769 }
770
771 //Register the outletStates INDI property, and add an element for each outlet.
772 m_indiP_outletStates = pcf::IndiProperty (pcf::IndiProperty::Text);
773 m_indiP_outletStates.setDevice(derived().configName());
774 m_indiP_outletStates.setName("outlet");
775 m_indiP_outletStates.setPerm(pcf::IndiProperty::ReadWrite);
776 m_indiP_outletStates.setState( pcf::IndiProperty::Idle );
777
778 if( derived().registerIndiPropertyReadOnly(m_indiP_outletStates) < 0)
779 {
780 #ifndef OUTLET_CTRL_TEST_NOLOG
781 derivedT::template log<software_error>({__FILE__,__LINE__});
782 #endif
783 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 for(size_t i=0; i< m_outletStates.size(); ++i)
794 {
795 m_indiP_outletStates.add (pcf::IndiElement(std::to_string(i+m_firstOne)));
796 }
797
798 return 0;
799}
800
801std::string stateIntToString(int st);
802
803template<class derivedT>
805{
806 if( !derived().m_indiDriver ) return 0;
807
808 //Publish outlet states (only bother if they've changed)
809 for(size_t i=0; i< m_outletStates.size(); ++i)
810 {
811 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 for(auto it = m_channels.begin(); it != m_channels.end(); ++it)
816 {
817 std::string state = stateIntToString( channelState( it->first ));
818
819 indi::updateIfChanged( it->second.m_indiP_prop, "state", state, derived().m_indiDriver );
820 }
821
822
823
824 return 0;
825}
826
827} //namespace dev
828} //namespace app
829} //namespace MagAOX
830
831#endif //app_outletController_hpp
@ READY
The device is ready for operation, but is not operating.
#define INDI_BUSY
Definition indiUtils.hpp:30
std::string stateIntToString(int st)
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition indiUtils.hpp:95
const pcf::IndiProperty & ipRecv
Definition dm.hpp:24
#define OUTLET_E_NOCHANNELS
#define OUTLET_E_NOOUTLETS
#define OUTLET_E_NOVALIDCH
A generic outlet controller.
int updateINDI()
Update the INDI properties for this device controller.
int setupINDI()
Setup the INDI properties for this device controller.
int outletState(int outletNum)
Get the currently stored outlet state, without updating from device.
std::vector< size_t > m_offOrder
[optional] The order in which outlets are turned off. This contains the indices of m_outlets,...
std::vector< unsigned > m_onDelays
[optional] The delays between outlets in a multi-oultet channel. The first entry is always ignored....
std::vector< unsigned > m_offDelays
[optional] The delays between outlets in a multi-oultet channel. The first entry is always ignored....
std::vector< unsigned > channelOffDelays(const std::string &channel)
Get the vector of outlet off delays for a channel.
virtual int updateOutletState(int outletNum)=0
Get the state of the outlet from the device.
std::vector< size_t > m_onOrder
[optional] The order in which outlets are turned on. This contains the indices of m_outlets,...
int turnChannelOn(const std::string &channel)
Turn a channel on.
pcf::IndiProperty m_indiP_chOffDelays
An INDI property which publishes the total off delay for each channel. Useful for GUIs,...
int channelState(const std::string &channel)
Get the state of a channel.
pcf::IndiProperty m_indiP_chOutlets
An INDI property which publishes the outlets associated with each channel. Useful for GUIs,...
bool m_firstOne
Flag is true if the first outlet is numbered 1, otherwise assumes starting at 0.
std::unordered_map< std::string, channelSpec > m_channels
The map of channel specifications, which can be accessed by their names.
int loadConfig(mx::app::appConfigurator &config)
Load the [channel] sections from an application configurator.
size_t numChannels()
Get the number of channels.
pcf::IndiProperty m_indiP_outletStates
Indi Property to show individual outlet states.
std::vector< size_t > channelOnOrder(const std::string &channel)
Get the vector of outlet on orders for a channel.
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for an outletController.
int turnChannelOff(const std::string &channel)
Turn a channel off.
virtual int updateOutletStates()
Get the states of all outlets from the device.
std::vector< size_t > channelOutlets(const std::string &channel)
Get the vector of outlet indices for a channel.
int setNumberOfOutlets(int numOuts)
Sets the number of outlets. This should be called by the derived class constructor.
int newCallBack_channels(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
pcf::IndiProperty m_indiP_chOnDelays
An INDI property which publishes the total on delay for each channel. Useful for GUIs,...
virtual int turnOutletOff(int outletNum)=0
Turn an outlet off.
std::vector< unsigned > channelOnDelays(const std::string &channel)
Get the vector of outlet on delays for a channel.
std::vector< size_t > channelOffOrder(const std::string &channel)
Get the vector of outlet off orders for a channel.
static int st_newCallBack_channels(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for the channel properties.
std::vector< int > m_outletStates
The current states of each outlet. These MUST be updated by derived classes in the overridden updated...
std::vector< size_t > m_outlets
The outlets in this channel.
virtual int turnOutletOn(int outletNum)=0
Turn an outlet on.
Structure containing the specification of one channel.