API
hsfwCtrl.hpp
Go to the documentation of this file.
1 /** \file hsfwCtrl.hpp
2  * \brief The MagAO-X Optec HSFW Filter Wheel Controller
3  *
4  * \ingroup hsfwCtrl_files
5  */
6 
7 
8 #ifndef hsfwCtrl_hpp
9 #define hsfwCtrl_hpp
10 
11 
12 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
13 #include "../../magaox_git_version.h"
14 
15 #include "libhsfw.h"
16 
17 /** \defgroup hsfwCtrl Optec HSFW Filter Wheel Control
18  * \brief Control of an Optec HSFW f/w.
19  *
20  * <a href="../handbook/operating/software/apps/hsfwCtrl.html">Application Documentation</a>
21  *
22  * \ingroup apps
23  *
24  */
25 
26 /** \defgroup hsfwCtrl_files Filter Wheel Control Files
27  * \ingroup hsfwCtrl
28  */
29 
30 namespace MagAOX
31 {
32 namespace app
33 {
34 
35 /** MagAO-X application to control an Optec High Speed Filter Wheel (HSFW).
36  *
37  * \todo add tests
38  *
39  * \ingroup hsfwCtrl
40  */
41 class hsfwCtrl : public MagAOXApp<>, public dev::stdMotionStage<hsfwCtrl>, public dev::telemeter<hsfwCtrl>
42 {
43 
44  friend class dev::stdMotionStage<hsfwCtrl>;
45 
46  friend class dev::telemeter<hsfwCtrl>;
47 
48 protected:
49 
50  /** \name Non-configurable parameters
51  *@{
52  */
53 
54 
55  ///@}
56 
57  /** \name Configurable Parameters
58  * @{
59  */
60 
61  std::wstring m_serialNumber;
62 
63  ///@}
64 
65  /** \name Status
66  * @{
67  */
68 
69  hsfw_wheel* m_wheel {nullptr};
70 
71  double m_pos {0};
72 
73  ///@}
74 
75 
76 public:
77 
78  /// Default c'tor.
79  hsfwCtrl();
80 
81  /// D'tor, declared and defined for noexcept.
82  ~hsfwCtrl() noexcept
83  {}
84 
85  /// Setup the configuration system (called by MagAOXApp::setup())
86  virtual void setupConfig();
87 
88  /// load the configuration system results (called by MagAOXApp::setup())
89  virtual void loadConfig();
90 
91  /// Startup functions
92  /** Setsup the INDI vars.
93  *
94  * \returns 0 on success
95  * \returns -1 on error.
96  */
97  virtual int appStartup();
98 
99  /// Implementation of the FSM for the TTM Modulator
100  /**
101  * \returns 0 on success
102  * \returns -1 on error.
103  */
104  virtual int appLogic();
105 
106  /// Do any needed shutdown tasks. Currently nothing in this app.
107  /**
108  * \returns 0 on success
109  * \returns -1 on error.
110  */
111  virtual int appShutdown();
112 
113 
114  /// This method is called when the change to poweroff is detected.
115  /**
116  * \returns 0 on success.
117  * \returns -1 on any error which means the app should exit.
118  */
119  virtual int onPowerOff();
120 
121  /// This method is called while the power is off, once per FSM loop.
122  /**
123  * \returns 0 on success.
124  * \returns -1 on any error which means the app should exit.
125  */
126  virtual int whilePowerOff();
127 
128 
129 protected:
130 
131 
132 
133  /// Start a high-level homing sequence.
134  /** For this device this includes the homing dither.
135  *
136  * \returns 0 on success.
137  * \returns -1 on error.
138  */
139  int startHoming();
140 
141  int presetNumber();
142 
143  /// Start a low-level homing sequence.
144  /** This initiates the device homing sequence.
145  *
146  * \returns 0 on success.
147  * \returns -1 on error.
148  */
149  int home();
150 
151  /// Stop the wheel motion immediately.
152  /**
153  * \returns 0 on success.
154  * \returns -1 on error.
155  */
156  int stop();
157 
158  /// Move to an absolute position in filter units.
159  /**
160  * \returns 0 on success.
161  * \returns -1 on error.
162  */
163  int moveTo( const double & filters /**< [in] The new position in absolute filter units*/ );
164 
165  /** \name Telemeter Interface
166  *
167  * @{
168  */
169  int checkRecordTimes();
170 
171  int recordTelem( const telem_stage * );
172 
173  int recordStage( bool force = false );
174 
175 };
176 
177 inline
178 hsfwCtrl::hsfwCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
179 {
180  m_presetNotation = "filter"; //sets the name of the configs, etc.
181 
182  m_powerMgtEnabled = true;
183 
184  return;
185 }
186 
187 inline
189 {
190  config.add("stage.serialNumber", "", "stage.serialNumber", argType::Required, "stage", "serialNumber", false, "string", "The device serial number.");
191 
193 
195 
196 }
197 
198 inline
200 {
201  std::string serNum;
202  config(serNum, "stage.serialNumber");
203  m_serialNumber.assign(serNum.begin(), serNum.end());
204 
206 
208 }
209 
210 inline
212 {
214  {
215  log<text_log>( "In appStartup but in state UNINITIALIZED.", logPrio::LOG_CRITICAL );
216  return -1;
217  }
218 
219 
221  {
222  return log<software_critical,-1>({__FILE__,__LINE__});
223  }
224 
226  {
227  return log<software_error,-1>({__FILE__,__LINE__});
228  }
229  return 0;
230 }
231 
232 inline
234 {
235  if( state() == stateCodes::INITIALIZED )
236  {
237  log<text_log>( "In appLogic but in state INITIALIZED.", logPrio::LOG_CRITICAL );
238  return -1;
239  }
240 
241  if( state() == stateCodes::POWERON )
242  {
244  }
245 
246  if( state() == stateCodes::NODEVICE )
247  {
248  hsfw_wheel_info *devs, *cur_dev;
249 
250  //Make sure we don't try anything while off.
251  if( state() == stateCodes::POWEROFF ) return 0;
252 
253  devs = enumerate_wheels();
254 
255  if(devs == NULL)
256  {
257  return 0;
258  }
259 
260  cur_dev = devs;
261  while (cur_dev)
262  {
263  if(m_serialNumber == cur_dev->serial_number)
264  {
265  char logs[1024];
266 
267  snprintf(logs, sizeof(logs), "Device Found - type: %04hx %04hx serial_number: %ls",cur_dev->vendor_id, cur_dev->product_id, cur_dev->serial_number);
268  log<text_log>(logs);
269 
271  break;
272  }
273  cur_dev = cur_dev->next;
274  }
275  wheels_free_enumeration(devs);
276 
278  {
279  if(!stateLogged())
280  {
281  log<text_log>("Device " + std::string(m_serialNumber.begin(), m_serialNumber.end()) + " not found");
282  }
283  return 0;
284  }
285  }
286 
288  {
289  //Make sure we don't try anything while off.
290  if(powerState() != 1 || powerStateTarget() != 1) return 0;
291 
292  hsfw_wheel_info *devs, *cur_dev;
293  devs = enumerate_wheels();
294 
295  if(devs == NULL)
296  {
298  return 0;
299  }
300 
301  cur_dev = devs;
302 
303  while (cur_dev)
304  {
305  if(m_serialNumber == cur_dev->serial_number)
306  {
307  break;
308  }
309 
310  cur_dev = cur_dev->next;
311  }
312 
313  if(cur_dev == NULL)
314  {
315  wheels_free_enumeration(devs);
317  return 0;
318  }
319 
320  //Make sure we don't try anything while off.
321  if(powerState() != 1 || powerStateTarget() != 1) return 0;
322 
323  if(m_wheel) close_hsfw(m_wheel);
324 
325  {
326  elevatedPrivileges elPriv(this);
327  m_wheel = open_hsfw(cur_dev->vendor_id, cur_dev->product_id, cur_dev->serial_number);
328  }
329 
330  if(m_wheel == NULL)
331  {
333  return 0;
334  }
335 
336 
338  log<text_log>("Connected to HSFW " + std::string(m_serialNumber.begin(), m_serialNumber.end()));
339  }
340 
341 
342  //If here, we're connected.
343 
344  std::lock_guard<std::mutex> guard(m_indiMutex);
345 
346  //Make sure we don't try anything while off.
347  if(powerState() != 1 || powerStateTarget() != 1) return 0;
348 
349  wheel_status status;
350  if (get_hsfw_status(m_wheel, &status) < 0)
351  {
352  if(powerState() != 1 || powerStateTarget() != 1) return 0;
353  log<software_error>({__FILE__, __LINE__, "error from get_hsfw_status"});
354  return 0;
355  }
356 
357  if (status.error_state != 0)
358  {
359  clear_error_hsfw(m_wheel);
360  }
361 
362  m_pos = status.position;
363 
364  if(!status.is_homed && !status.is_homing)
365  {
367  m_moving = -1;
368 
369  if(m_powerOnHome)
370  {
371  startHoming();
372  }
373  }
374  else if( status.is_homing)
375  {
376  m_moving=2;
378  }
379  else if (status.is_moving)
380  {
381  m_moving = 1;
383  }
384  else
385  {
386  m_moving = 0;
388  }
389 
390  int n = presetNumber();
391  if(n == -1)
392  {
393  m_preset = 0;
394  m_preset_target = 0;
395  }
396  else
397  {
398  m_preset = n+1;
399  m_preset_target = n+1;
400  }
401 
402  //record telem if there have been any changes
403  recordStage();
404 
405 
407 
408  //record telem if it's been longer than 10 sec:
410  {
411  log<software_error>({__FILE__, __LINE__});
412  return 0;
413  }
414 
415 
416  return 0;
417 }
418 
419 
420 
421 inline
423 {
424  if(m_wheel) close_hsfw(m_wheel);
425 
426  exit_hsfw();
427 
428  return 0;
429 }
430 
431 inline
433 {
435  {
436  log<software_error>({__FILE__,__LINE__});
437  }
438 
439  recordStage();
440 
441  return 0;
442 }
443 
444 
445 inline
447 {
449  {
450  log<software_error>({__FILE__,__LINE__});
451  }
452 
453  //record telem if it's been longer than 10 sec:
455  {
456  log<software_error>({__FILE__, __LINE__});
457  }
458 
459  return 0;
460 }
461 
462 
464 {
465  updateSwitchIfChanged(m_indiP_home, "request", pcf::IndiElement::Off, INDI_IDLE);
466 
467  //Make sure we don't try anything while off.
468  if( state() == stateCodes::POWEROFF ) return 0;
469 
470  if(home_hsfw(m_wheel))
471  {
472  if(powerState() != 1 || powerStateTarget() != 1) return -1; //about to get POWEROFF
473  log<software_error>({__FILE__,__LINE__, "libhswf error"});
474  return -1;
475  }
476 
477  m_moving = 2;
478 
479  return 0;
480 }
481 
483 {
484  return m_pos-1;
485 }
486 
487 
488 
490 {
491  updateSwitchIfChanged(m_indiP_stop, "request", pcf::IndiElement::Off, INDI_IDLE);
492  return 0;
493 }
494 
495 
496 int hsfwCtrl::moveTo( const double & filters )
497 {
498  //Make sure we don't try anything while off.
499  if( state() == stateCodes::POWEROFF ) return 0;
500 
501  double ffilters = filters;
502  if(ffilters< 0.5)
503  {
504  while(ffilters < 8.5) ffilters += 8;
505  if(ffilters >= 8.5)
506  {
507  return log<software_error,-1>({__FILE__,__LINE__, "error getting modulo filter number"});
508  }
509  }
510 
511  m_moving = 1;
512  recordStage();
513 
514  if( move_hsfw(m_wheel, (unsigned short) (ffilters + 0.5)) < 0)
515  {
516  if(powerState() != 1 || powerStateTarget() != 1) return -1; //about to get POWEROFF
517  return log<software_error,-1>({__FILE__,__LINE__, "libhsfw error"});
518  }
519 
520  return 0;
521 }
522 
524 {
526 }
527 
529 {
530  return recordStage(true);
531 }
532 
533 int hsfwCtrl::recordStage( bool force )
534 {
536 }
537 
538 } //namespace app
539 } //namespace MagAOX
540 
541 #endif //hsfwCtrl_hpp
Internal class to manage setuid privilege escalation with RAII.
Definition: MagAOXApp.hpp:324
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int powerState()
Returns the current power state.
Definition: MagAOXApp.hpp:3414
int powerStateTarget()
Returns the target power state.
Definition: MagAOXApp.hpp:3423
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
Definition: MagAOXApp.hpp:3144
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2361
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:545
MagAO-X standard motion stage interface.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
bool m_powerOnHome
If true, then the motor is homed at startup (by this software or actual power on)
std::string m_presetNotation
Notation used to refer to a preset, should be singular, as in "preset" or "filter".
float m_preset_target
The target numerical preset position [1.0 is index 0 in the preset name vector].
pcf::IndiProperty m_indiP_home
Command the stage to home. .
int updateINDI()
Update the INDI properties for this device controller.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int whilePowerOff()
Actions while powered off.
int8_t m_moving
Whether or not the stage is moving. -2 means powered off, -1 means not homed, 0 means not moving,...
pcf::IndiProperty m_indiP_stop
Command the stage to halt.
float m_preset
The current numerical preset position [1.0 is index 0 in the preset name vector].
int recordStage(bool force=false)
virtual int whilePowerOff()
This method is called while the power is off, once per FSM loop.
Definition: hsfwCtrl.hpp:446
std::wstring m_serialNumber
Definition: hsfwCtrl.hpp:61
virtual void setupConfig()
Setup the configuration system (called by MagAOXApp::setup())
Definition: hsfwCtrl.hpp:188
hsfwCtrl()
Default c'tor.
Definition: hsfwCtrl.hpp:178
virtual int onPowerOff()
This method is called when the change to poweroff is detected.
Definition: hsfwCtrl.hpp:432
int startHoming()
Start a high-level homing sequence.
Definition: hsfwCtrl.hpp:463
~hsfwCtrl() noexcept
D'tor, declared and defined for noexcept.
Definition: hsfwCtrl.hpp:82
virtual void loadConfig()
load the configuration system results (called by MagAOXApp::setup())
Definition: hsfwCtrl.hpp:199
virtual int appShutdown()
Do any needed shutdown tasks. Currently nothing in this app.
Definition: hsfwCtrl.hpp:422
virtual int appLogic()
Implementation of the FSM for the TTM Modulator.
Definition: hsfwCtrl.hpp:233
int stop()
Stop the wheel motion immediately.
Definition: hsfwCtrl.hpp:489
int recordTelem(const telem_stage *)
Definition: hsfwCtrl.hpp:528
int home()
Start a low-level homing sequence.
hsfw_wheel * m_wheel
Definition: hsfwCtrl.hpp:69
int recordStage(bool force=false)
Definition: hsfwCtrl.hpp:533
virtual int appStartup()
Startup functions.
Definition: hsfwCtrl.hpp:211
int moveTo(const double &filters)
Move to an absolute position in filter units.
Definition: hsfwCtrl.hpp:496
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
@ POWEROFF
The device power is off.
Definition: stateCodes.hpp:47
@ NODEVICE
No device exists for the application to control.
Definition: stateCodes.hpp:46
@ NOTHOMED
The device has not been homed.
Definition: stateCodes.hpp:53
@ HOMING
The device is homing.
Definition: stateCodes.hpp:54
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:56
@ CONNECTED
The application has connected to the device or service.
Definition: stateCodes.hpp:50
@ UNINITIALIZED
The application is unitialized, the default.
Definition: stateCodes.hpp:44
@ INITIALIZED
The application has been initialized, set just before calling appStartup().
Definition: stateCodes.hpp:45
@ NOTCONNECTED
The application is not connected to the device or service.
Definition: stateCodes.hpp:49
@ POWERON
The device power is on.
Definition: stateCodes.hpp:48
#define INDI_IDLE
Definition: indiUtils.hpp:28
Definition: dm.hpp:24
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
A device base class which saves telemetry.
Definition: telemeter.hpp:69
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:223
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:268
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:211
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:281
Software CRITICAL log entry.
Software ERR log entry.
Log entry recording stdMotionStage status.
Definition: telem_stage.hpp:26