API
 
Loading...
Searching...
No Matches
xt1121Ctrl.hpp
Go to the documentation of this file.
1/** \file xt1121Ctrl.hpp
2 * \brief The MagAO-X Acromag XT 1121digital I/O controller.
3 *
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 * \ingroup xt1121Ctrl_files
7 */
8
9#ifndef xt1121Ctrl_hpp
10#define xt1121Ctrl_hpp
11
12
13#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
14#include "../../magaox_git_version.h"
15
16#include "xtChannels.hpp"
17
18namespace MagAOX
19{
20namespace app
21{
22
23
24/** \defgroup xt1121Ctrl Acromag xt1121Controller
25 * \brief Control of an Acromag xt1121digital I/O module
26 *
27 * <a href="../handbook/operating/software/apps/xt1121Ctrl.html">Application Documentation</a>
28 *
29 * \ingroup apps
30 *
31 */
32
33/** \defgroup xt1121Ctrl_files Acromag xt1121Controller Files
34 * \ingroup xt1121Ctrl
35 */
36
37/** MagAO-X application to control an Acromag xt1121digital i/o module
38 *
39 * \ingroup xt1121Ctrl
40 *
41 */
42class xt1121Ctrl : public MagAOXApp<>, public xt1121Channels
43{
44
45protected:
46
47 /** \name configurable parameters
48 *@{
49 */
50
51 std::string m_address; ///< The I.P. address of the device
52
53 uint16_t m_port {502}; ///< The port to use. Default is 502 for modbus.
54
55 unsigned long m_powerOnWait {2}; ///< Time in sec to wait for device to boot after power on.
56
57 ///@}
58
59 int m_powerOnCounter {0}; ///< Counts numer of loops after power on, implements delay for camera bootup.
60
61 modbus * m_mb {nullptr}; ///< The modbus protocol communication object
62
63public:
64
65 ///Default c'tor
66 xt1121Ctrl();
67
68 ///Destructor
70
71 /// Setup the configuration system (called by MagAOXApp::setup())
72 virtual void setupConfig();
73
74 /// load the configuration system results (called by MagAOXApp::setup())
75 virtual void loadConfig();
76
77 /// Startup functions
78 /** Sets up the INDI vars.
79 *
80 */
81 virtual int appStartup();
82
83 /// Implementation of the FSM for the Siglent SDG
84 virtual int appLogic();
85
86 /// Implementation of the on-power-off FSM logic
87 virtual int onPowerOff();
88
89 /// Implementation of the while-powered-off FSM
91
92 /// Do any needed shutdown tasks. Currently nothing in this app.
93 virtual int appShutdown();
94
95 /// Get the current state of the outlets.
96 /**
97 * \returns 0 on success
98 * \returns -1 on error
99 */
100 int getState();
101
102 //INDI:
104 //declare our properties
121
122public:
123
124 /// Callback worker to actually set or clear a channel and send it to the device
125 /** Contains the target/current logic, and calls the xtChannels::setRegisters
126 * function, and then the modbus write_registers.
127 *
128 * \returns 0 on success
129 * \returns -1 on error
130 */
131 int channelSetCallback( size_t chNo, ///< [in] The channel number to set
132 pcf::IndiProperty & ipToSet, ///< [in] The corresponding local INDI property
133 const pcf::IndiProperty & ipRecv ///< [in] The received INDI property
134 );
135
152};
153
154inline
161
162inline
164{
165 if(m_mb)
166 {
167 delete m_mb;
168 };
169
170 return;
171}
172
173inline
175{
176 config.add("device.address", "", "device.address", argType::Required, "device", "address", true, "string", "The device I.P. address.");
177 config.add("device.port", "", "device.port", argType::Required, "device", "port", true, "int", "The device port. Default is 502.");
178 config.add("device.powerOnWait", "", "device.powerOnWait", argType::Required, "device", "powerOnWait", false, "int", "Time after power-on to begin attempting connections [sec]. Default is 2 sec.");
179 config.add("device.inputOnly", "", "device.inputOnly", argType::Required, "device", "inputOnly", false, "vector<int>", "List of channels which are input-only.");
180
181}
182
183
184///\todo mxlib loadConfig needs to return int to propagate errors!
185
186inline
188{
189 config(m_address, "device.address");
190 config(m_port, "device.port");
191 config(m_powerOnWait, "device.powerOnWait");
192
193 std::vector<int> ino;
194 config(ino, "device.inputOnly");
195
196 for(size_t i=0; i< ino.size();++i)
197 {
198 if(setInputOnly(ino[i]) != 0)
199 {
200 log<text_log>("Error setting channel " + std::to_string(i) + " to input only.", logPrio::LOG_ERROR);
201 }
202 }
203}
204
205
206
207inline
209{
210 // set up the INDI properties
211 REG_INDI_NEWPROP(m_indiP_ch00, "ch00", pcf::IndiProperty::Number);
212 m_indiP_ch00.add (pcf::IndiElement("current"));
213 m_indiP_ch00["current"].set(-1);
214 m_indiP_ch00.add (pcf::IndiElement("target"));
215
216 REG_INDI_NEWPROP(m_indiP_ch01, "ch01", pcf::IndiProperty::Number);
217 m_indiP_ch01.add (pcf::IndiElement("current"));
218 m_indiP_ch01["current"].set(-1);
219 m_indiP_ch01.add (pcf::IndiElement("target"));
220
221 REG_INDI_NEWPROP(m_indiP_ch02, "ch02", pcf::IndiProperty::Number);
222 m_indiP_ch02.add (pcf::IndiElement("current"));
223 m_indiP_ch02["current"].set(-1);
224 m_indiP_ch02.add (pcf::IndiElement("target"));
225
226 REG_INDI_NEWPROP(m_indiP_ch03, "ch03", pcf::IndiProperty::Number);
227 m_indiP_ch03.add (pcf::IndiElement("current"));
228 m_indiP_ch03["current"].set(-1);
229 m_indiP_ch03.add (pcf::IndiElement("target"));
230
231 REG_INDI_NEWPROP(m_indiP_ch04, "ch04", pcf::IndiProperty::Number);
232 m_indiP_ch04.add (pcf::IndiElement("current"));
233 m_indiP_ch04["current"].set(-1);
234 m_indiP_ch04.add (pcf::IndiElement("target"));
235
236 REG_INDI_NEWPROP(m_indiP_ch05, "ch05", pcf::IndiProperty::Number);
237 m_indiP_ch05.add (pcf::IndiElement("current"));
238 m_indiP_ch05["current"].set(-1);
239 m_indiP_ch05.add (pcf::IndiElement("target"));
240
241 REG_INDI_NEWPROP(m_indiP_ch06, "ch06", pcf::IndiProperty::Number);
242 m_indiP_ch06.add (pcf::IndiElement("current"));
243 m_indiP_ch06["current"].set(-1);
244 m_indiP_ch06.add (pcf::IndiElement("target"));
245
246 REG_INDI_NEWPROP(m_indiP_ch07, "ch07", pcf::IndiProperty::Number);
247 m_indiP_ch07.add (pcf::IndiElement("current"));
248 m_indiP_ch07["current"].set(-1);
249 m_indiP_ch07.add (pcf::IndiElement("target"));
250
251 REG_INDI_NEWPROP(m_indiP_ch08, "ch08", pcf::IndiProperty::Number);
252 m_indiP_ch08.add (pcf::IndiElement("current"));
253 m_indiP_ch08["current"].set(-1);
254 m_indiP_ch08.add (pcf::IndiElement("target"));
255
256 REG_INDI_NEWPROP(m_indiP_ch09, "ch09", pcf::IndiProperty::Number);
257 m_indiP_ch09.add (pcf::IndiElement("current"));
258 m_indiP_ch09["current"].set(-1);
259 m_indiP_ch09.add (pcf::IndiElement("target"));
260
261 REG_INDI_NEWPROP(m_indiP_ch10, "ch10", pcf::IndiProperty::Number);
262 m_indiP_ch10.add (pcf::IndiElement("current"));
263 m_indiP_ch10["current"].set(-1);
264 m_indiP_ch10.add (pcf::IndiElement("target"));
265
266 REG_INDI_NEWPROP(m_indiP_ch11, "ch11", pcf::IndiProperty::Number);
267 m_indiP_ch11.add (pcf::IndiElement("current"));
268 m_indiP_ch11["current"].set(-1);
269 m_indiP_ch11.add (pcf::IndiElement("target"));
270
271 REG_INDI_NEWPROP(m_indiP_ch12, "ch12", pcf::IndiProperty::Number);
272 m_indiP_ch12.add (pcf::IndiElement("current"));
273 m_indiP_ch12["current"].set(-1);
274 m_indiP_ch12.add (pcf::IndiElement("target"));
275
276 REG_INDI_NEWPROP(m_indiP_ch13, "ch13", pcf::IndiProperty::Number);
277 m_indiP_ch13.add (pcf::IndiElement("current"));
278 m_indiP_ch13["current"].set(-1);
279 m_indiP_ch13.add (pcf::IndiElement("target"));
280
281 REG_INDI_NEWPROP(m_indiP_ch14, "ch14", pcf::IndiProperty::Number);
282 m_indiP_ch14.add (pcf::IndiElement("current"));
283 m_indiP_ch14["current"].set(-1);
284 m_indiP_ch14.add (pcf::IndiElement("target"));
285
286 REG_INDI_NEWPROP(m_indiP_ch15, "ch15", pcf::IndiProperty::Number);
287 m_indiP_ch15.add (pcf::IndiElement("current"));
288 m_indiP_ch15["current"].set(-1);
289 m_indiP_ch15.add (pcf::IndiElement("target"));
290
291
292 return 0;
293
294}
295
296
297
298inline
300{
301 if( state() == stateCodes::POWERON )
302 {
304 {
307 }
308 else
309 {
311 return 0;
312 }
313 }
314
316 {
317 std::string response;
318
319 //Might have gotten here because of a power off.
320 if(m_powerState == 0) return 0;
321
322 m_mb = new(std::nothrow) modbus(m_address, m_port);
323
324 if(m_mb == nullptr)
325 {
326 return log<software_critical, -1>({__FILE__, __LINE__, "allocation failure"});
327 }
328
330
331 if( m_mb->modbus_connect() == false)
332 {
333 if(!stateLogged())
334 {
335 log<text_log>("connect failed at " + m_address + ":" + std::to_string(m_port));
336 }
337 delete m_mb;
338 m_mb = nullptr;
339 return 0;
340 }
341
343 log<text_log>("connected to " + m_address + ":" + std::to_string(m_port));
344 }
345
347 {
348 //Get a lock
349 std::unique_lock<std::mutex> lock(m_indiMutex);
350
351 if( getState() == 0 )
352 {
354 return 0;
355 }
356 else
357 {
360 }
361 }
362
364 {
365 //Get a lock if we can
366 std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
367
368 //but don't wait for it, just go back around.
369 if(!lock.owns_lock()) return 0;
370
371 if(getState() < 0)
372 {
373 if(m_powerState == 0) return 0;
374
376 return 0;
377 }
378
379 return 0;
380 }
381
382 //Fall through check?
383
384 return 0;
385
386}
387
388inline
390{
392
393 std::lock_guard<std::mutex> lock(m_indiMutex);
394
395 updateIfChanged(m_indiP_ch00, "current", -1);
396 updateIfChanged(m_indiP_ch00, "target", -1);
397
398 updateIfChanged(m_indiP_ch01, "current", -1);
399 updateIfChanged(m_indiP_ch01, "target", -1);
400
401 updateIfChanged(m_indiP_ch02, "current", -1);
402 updateIfChanged(m_indiP_ch02, "target", -1);
403
404 updateIfChanged(m_indiP_ch03, "current", -1);
405 updateIfChanged(m_indiP_ch03, "target", -1);
406
407 updateIfChanged(m_indiP_ch04, "current", -1);
408 updateIfChanged(m_indiP_ch04, "target", -1);
409
410 updateIfChanged(m_indiP_ch05, "current", -1);
411 updateIfChanged(m_indiP_ch05, "target", -1);
412
413 updateIfChanged(m_indiP_ch06, "current", -1);
414 updateIfChanged(m_indiP_ch06, "target", -1);
415
416 updateIfChanged(m_indiP_ch07, "current", -1);
417 updateIfChanged(m_indiP_ch07, "target", -1);
418
419 updateIfChanged(m_indiP_ch08, "current", -1);
420 updateIfChanged(m_indiP_ch08, "target", -1);
421
422 updateIfChanged(m_indiP_ch09, "current", -1);
423 updateIfChanged(m_indiP_ch09, "target", -1);
424
425 updateIfChanged(m_indiP_ch10, "current", -1);
426 updateIfChanged(m_indiP_ch10, "target", -1);
427
428 updateIfChanged(m_indiP_ch11, "current", -1);
429 updateIfChanged(m_indiP_ch11, "target", -1);
430
431 updateIfChanged(m_indiP_ch12, "current", -1);
432 updateIfChanged(m_indiP_ch12, "target", -1);
433
434 updateIfChanged(m_indiP_ch13, "current", -1);
435 updateIfChanged(m_indiP_ch13, "target", -1);
436
437 updateIfChanged(m_indiP_ch14, "current", -1);
438 updateIfChanged(m_indiP_ch14, "target", -1);
439
440 updateIfChanged(m_indiP_ch15, "current", -1);
441 updateIfChanged(m_indiP_ch15, "target", -1);
442
443
444 return 0;
445}
446
447inline
449{
450 return 0;
451}
452
453inline
455{
456 if(m_mb) m_mb->modbus_close();
457 return 0;
458}
459
460
461
462inline
464{
466
467 try
468 {
469 ///\todo this hangs if power goes off during call
471 }
472 catch(std::exception & e)
473 {
474 if(m_powerState == 0) return 0; //due to power off
475
476 return log<software_error,-1>({__FILE__, __LINE__, std::string("Exception caught: ") + e.what()});
477 }
478
479 if( readRegisters(input_regs) !=0 )
480 {
481 return log<software_error,-1>({__FILE__, __LINE__});
482 }
483
484 updateIfChanged(m_indiP_ch00, "current", channel(0));
485 updateIfChanged(m_indiP_ch01, "current", channel(1));
486 updateIfChanged(m_indiP_ch02, "current", channel(2));
487 updateIfChanged(m_indiP_ch03, "current", channel(3));
488 updateIfChanged(m_indiP_ch04, "current", channel(4));
489 updateIfChanged(m_indiP_ch05, "current", channel(5));
490 updateIfChanged(m_indiP_ch06, "current", channel(6));
491 updateIfChanged(m_indiP_ch07, "current", channel(7));
492 updateIfChanged(m_indiP_ch08, "current", channel(8));
493 updateIfChanged(m_indiP_ch09, "current", channel(9));
494 updateIfChanged(m_indiP_ch10, "current", channel(10));
495 updateIfChanged(m_indiP_ch11, "current", channel(11));
496 updateIfChanged(m_indiP_ch12, "current", channel(12));
497 updateIfChanged(m_indiP_ch13, "current", channel(13));
498 updateIfChanged(m_indiP_ch14, "current", channel(14));
499 updateIfChanged(m_indiP_ch15, "current", channel(15));
500
501 return 0;
502
503
504}
505
506
508 pcf::IndiProperty & ipToSet,
509 const pcf::IndiProperty & ipRecv
510 )
511{
512 int current = -1, target = -1;
513
514 if(ipRecv.find("current"))
515 {
516 current = ipRecv["current"].get<unsigned>();
517 }
518
519 if(ipRecv.find("target"))
520 {
521 target = ipRecv["target"].get<unsigned>();
522 }
523
524 if(target == -1) target = current;
525
526 if(target < 0) return 0;
527
528 //Lock the mutex, waiting if necessary
529 std::unique_lock<std::mutex> lock(m_indiMutex);
530
531 if(target == 0) clearChannel(chNo);
532 else setChannel(chNo);
533
534 target = channel(chNo); //This checks for inputOnly
535
536 updateIfChanged(ipToSet, "target", target);
537
539
540 if( setRegisters(input_regs) !=0 )
541 {
542 return log<software_error,-1>({__FILE__, __LINE__});
543 }
544
545 try
546 {
548 }
549 catch(std::exception & e)
550 {
551 if(m_powerState == 0) return 0; //due to power off
552
553 return log<software_error,-1>({__FILE__, __LINE__, std::string("Exception caught: ") + e.what()});
554 }
555
556
557 log<text_log>("Set channel " + std::to_string(chNo) + " to " + std::to_string(target));
558
559 return 0;
560
561}
562
564{
566
568
569 return -1;
570}
571
573{
575
577
578 return -1;
579}
580
582{
584
586
587 return -1;
588}
589
591{
593
595
596 return -1;
597}
598
600{
602
604
605 return -1;
606}
607
609{
611
613
614 return -1;
615}
616
618{
620
622
623 return -1;
624}
625
627{
629
631
632 return -1;
633}
634
636{
638
640
641 return -1;
642}
643
645{
647
649
650 return -1;
651}
652
654{
656
658
659 return -1;
660}
661
663{
665
667
668 return -1;
669}
670
672{
674
676
677 return -1;
678}
679
681{
683
685
686 return -1;
687}
688
690{
692
694
695 return -1;
696}
697
699{
701
703
704 return -1;
705}
706
707
708}//namespace app
709} //namespace MagAOX
710#endif
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
stateCodes::stateCodeT state()
Get the current state code.
unsigned long m_loopPause
Definition MagAOXApp.hpp:95
int m_powerState
Current power state, 1=On, 0=Off, -1=Unk.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::mutex m_indiMutex
Mutex for locking INDI communications.
virtual void setupConfig()
Setup the configuration system (called by MagAOXApp::setup())
pcf::IndiProperty m_indiP_ch11
virtual void loadConfig()
load the configuration system results (called by MagAOXApp::setup())
virtual int appLogic()
Implementation of the FSM for the Siglent SDG.
pcf::IndiProperty m_indiP_ch02
pcf::IndiProperty m_indiP_ch14
virtual int appStartup()
Startup functions.
~xt1121Ctrl() noexcept
Destructor.
virtual int appShutdown()
Do any needed shutdown tasks. Currently nothing in this app.
pcf::IndiProperty m_indiP_ch07
std::string m_address
The I.P. address of the device.
uint16_t m_port
The port to use. Default is 502 for modbus.
pcf::IndiProperty m_indiP_ch10
int getState()
Get the current state of the outlets.
pcf::IndiProperty m_indiP_ch00
pcf::IndiProperty m_indiP_ch08
pcf::IndiProperty m_indiP_ch06
pcf::IndiProperty m_indiP_ch15
int m_powerOnCounter
Counts numer of loops after power on, implements delay for camera bootup.
pcf::IndiProperty m_indiP_ch01
pcf::IndiProperty m_indiP_ch05
xt1121Ctrl()
Default c'tor.
pcf::IndiProperty m_indiP_ch09
virtual int whilePowerOff()
Implementation of the while-powered-off FSM.
modbus * m_mb
The modbus protocol communication object.
virtual int onPowerOff()
Implementation of the on-power-off FSM logic.
pcf::IndiProperty m_indiP_ch12
unsigned long m_powerOnWait
Time in sec to wait for device to boot after power on.
pcf::IndiProperty m_indiP_ch03
pcf::IndiProperty m_indiP_ch13
pcf::IndiProperty m_indiP_ch04
int channelSetCallback(size_t chNo, pcf::IndiProperty &ipToSet, const pcf::IndiProperty &ipRecv)
Callback worker to actually set or clear a channel and send it to the device.
Modbus Operator Class.
Definition modbus.hpp:63
bool modbus_connect()
Definition modbus.cpp:19
void modbus_read_input_registers(int address, int amount, uint16_t *buffer)
Definition modbus.cpp:131
void modbus_set_slave_id(int id)
Definition modbus.cpp:15
void modbus_close()
Definition modbus.cpp:49
void modbus_write_registers(int address, int amount, uint16_t *value)
Definition modbus.cpp:254
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
#define INDI_NEWCALLBACK_DECL(class, prop)
Declare the callback for a new property request, and declare and define the static wrapper.
@ OPERATING
The device is operating, other than homing.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Software CRITICAL log entry.
Software ERR log entry.
Utility class for managing Acromag xt12XX digital I/O channels.
int setRegisters(uint16_t registers[numRegisters])
Set registers based on current channel states.
int setChannel(size_t chNo)
Set a channel to true.
static constexpr int numRegisters
The number of registers needed for the number of channels.
int channel(size_t chNo)
Gets the current state of a channel.
int setInputOnly(size_t chNo)
Set a channel to be input only.
int clearChannel(size_t chNo)
Set a channel to false.
int readRegisters(uint16_t registers[numRegisters])
Read channel states from the registers.
Utility class for managing Acromag xt12XX digital I/O channels.