API
dm.hpp
Go to the documentation of this file.
1 /** \file dm.hpp
2  * \brief The MagAO-X generic deformable mirror controller.
3  *
4  * \author Jared R. Males (jaredmales@gmail.com)
5  *
6  * \ingroup app_files
7  */
8 
9 #ifndef dm_hpp
10 #define dm_hpp
11 
12 /** Tests
13  \todo test that restarting fpsCtrl doesn't scram this
14  */
15 
16 #include <mx/improc/eigenImage.hpp>
17 #include <mx/ioutils/fits/fitsFile.hpp>
18 
19 #include <boost/filesystem/operations.hpp>
20 
21 #include "../../ImageStreamIO/ImageStruct.hpp"
22 
23 namespace MagAOX
24 {
25 namespace app
26 {
27 namespace dev
28 {
29 
30 template <typename typeT>
31 constexpr uint8_t ImageStreamTypeCode()
32 {
33  return 0;
34 }
35 
36 template <>
37 constexpr uint8_t ImageStreamTypeCode<float>()
38 {
39  return _DATATYPE_FLOAT;
40 }
41 
42 template <>
43 constexpr uint8_t ImageStreamTypeCode<double>()
44 {
45  return _DATATYPE_DOUBLE;
46 }
47 
48 /** MagAO-X generic deformable mirror controller
49  *
50  *
51  * The derived class `derivedT` must expose the following interface
52  \code
53 
54  \endcode
55  * Each of the above functions should return 0 on success, and -1 on an error.
56  *
57  * This class should be declared a friend in the derived class, like so:
58  \code
59  friend class dev::dm<derivedT,realT>;
60  \endcode
61  *
62  * Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic`, `appShutdown`, and `udpdateINDI`
63  * functions must be placed in the derived class's functions of the same name.
64  *
65  * \ingroup appdev
66  */
67 template <class derivedT, typename realT>
68 class dm
69 {
70 
71 protected:
72  /** \name Configurable Parameters
73  * @{
74  */
75 
76  std::string m_calibPath; ///< The path to this DM's calibration files.
77  std::string m_flatPath; ///< The path to this DM's flat files (usually the same as calibPath)
78  std::string m_testPath; ///< The path to this DM's test files (default is calibPath/tests;
79 
80  std::string m_flatDefault; ///< The file name of the this DM's default flat command. Path and extension will be ignored and can be omitted.
81  std::string m_testDefault; ///< The file name of the this DM's default test command. Path and extension will be ignored and can be omitted.
82 
83  std::string m_shmimFlat; ///< The name of the shmim stream to write the flat to.
84  std::string m_shmimTest; ///< The name of the shmim stream to write the test to.
85  std::string m_shmimSat; ///< The name of the shmim stream to write the saturation map to.
86  std::string m_shmimSatPerc; ///< The name of the shmim stream to write the saturation percentage map to.
87 
88  int m_satAvgInt{100}; ///< The time in milliseconds to accumulate saturation over.
89 
90  ///\todo satThreadPrio configuration is not actually implemented.
91  int m_satThreadPrio{0}; ///< Priority of the saturation thread, should normally be > 0.
92 
93  uint32_t m_dmWidth{0}; ///< The width of the images in the stream
94  uint32_t m_dmHeight{0}; ///< The height of the images in the stream
95 
96  static constexpr uint8_t m_dmDataType = ImageStreamTypeCode<realT>(); ///< The ImageStreamIO type code.
97 
98  float m_percThreshold{0.98}; // percentage of frames saturated over interval
99  float m_intervalSatThreshold{0.50}; //
101 
102  std::vector<std::string> m_satTriggerDevice;
103  std::vector<std::string> m_satTriggerProperty;
104 
105  ///@}
106 
107  std::string m_calibRelDir; ///< The directory relative to the calibPath. Set this before calling dm<derivedT,realT>::loadConfig().
108 
109  int m_channels{0}; ///< The number of dmcomb channels found as part of allocation.
110 
111  std::map<std::string, std::string> m_flatCommands; ///< Map of flat file name to full path
112  std::string m_flatCurrent; ///< The name of the current flat command
113 
114  mx::improc::eigenImage<realT> m_flatCommand; ///< Data storage for the flat command
115  bool m_flatLoaded{false}; ///< Flag indicating whether a flat is loaded in memory
116 
117  IMAGE m_flatImageStream; ///< The ImageStreamIO shared memory buffer for the flat.
118  bool m_flatSet{false}; ///< Flag indicating whether the flat command has been set.
119 
120  std::map<std::string, std::string> m_testCommands; ///< Map of test file name to full path
121  std::string m_testCurrent;
122 
123  mx::improc::eigenImage<realT> m_testCommand; ///< Data storage for the test command
124  bool m_testLoaded{false}; ///< Flag indicating whether a test command is loaded in memory
125 
126  IMAGE m_testImageStream; ///< The ImageStreamIO shared memory buffer for the test.
127  bool m_testSet{false}; ///< Flag indicating whether the test command has been set.
128 
129  int m_overSatAct{0}; // counter
130  int m_intervalSatExceeds{0}; // counter
131  bool m_intervalSatTrip{0}; // flag to trip the loop opening
132 
133 public:
134  /// Setup the configuration system
135  /**
136  * This should be called in `derivedT::setupConfig` as
137  * \code
138  dm<derivedT,realT>::setupConfig(config);
139  \endcode
140  * with appropriate error checking.
141  */
142  int setupConfig(mx::app::appConfigurator &config /**< [out] the derived classes configurator*/);
143 
144  /// load the configuration system results
145  /**
146  * This should be called in `derivedT::loadConfig` as
147  * \code
148  dm<derivedT,realT>::loadConfig(config);
149  \endcode
150  * with appropriate error checking.
151  */
152  int loadConfig(mx::app::appConfigurator &config /**< [in] the derived classes configurator*/);
153 
154  /// Startup function
155  /**
156  * This should be called in `derivedT::appStartup` as
157  * \code
158  dm<derivedT,realT>::appStartup();
159  \endcode
160  * with appropriate error checking.
161  *
162  * \returns 0 on success
163  * \returns -1 on error, which is logged.
164  */
165  int appStartup();
166 
167  /// DM application logic
168  /** This should be called in `derivedT::appLogic` as
169  * \code
170  dm<derivedT,realT>::appLogic();
171  \endcode
172  * with appropriate error checking.
173  *
174  * \returns 0 on success
175  * \returns -1 on error, which is logged.
176  */
177  int appLogic();
178 
179  /// DM shutdown
180  /** This should be called in `derivedT::appShutdown` as
181  * \code
182  dm<derivedT,realT>::appShutdown();
183  \endcode
184  * with appropriate error checking.
185  *
186  * \returns 0 on success
187  * \returns -1 on error, which is logged.
188  */
189  int appShutdown();
190 
191  /// DM Poweroff
192  /** This should be called in `derivedT::onPowerOff` as
193  * \code
194  dm<derivedT,realT>::onPowerOff();
195  \endcode
196  * with appropriate error checking.
197  *
198  * \returns 0 on success
199  * \returns -1 on error, which is logged.
200  */
201  int onPowerOff();
202 
203  /// DM Poweroff Updates
204  /** This should be called in `derivedT::whilePowerOff` as
205  * \code
206  dm<derivedT,realT>::whilePowerOff();
207  \endcode
208  * with appropriate error checking.
209  *
210  * \returns 0 on success
211  * \returns -1 on error, which is logged.
212  */
214 
215  /// Find the DM comb channels
216  /** Introspectively finds all dmXXdispYY channels, zeroes them, and raises the semapahore
217  * on the last to cause dmcomb to update.
218  */
220 
221  /// Called after shmimMonitor connects to the dmXXdisp stream. Checks for proper size.
222  /**
223  * \returns 0 on success
224  * \returns -1 if incorrect size or data type in stream.
225  */
226  int allocate(const dev::shmimT &sp);
227 
228  /// Called by shmimMonitor when a new DM command is available. This is just a pass-through to derivedT::commandDM(char*).
229  int processImage(void *curr_src,
230  const dev::shmimT &sp);
231 
232  /// Calls derived()->releaseDM() and then 0s all channels and the sat map.
233  /** This is called by the relevant INDI callback
234  *
235  * \returns 0 on success
236  * \returns -1 on error
237  */
238  int releaseDM();
239 
240  /// Check the flats directory and update the list of flats if anything changes
241  /** This is called once per appLogic and whilePowerOff loops.
242  *
243  * \returns 0 on success
244  * \returns -1 on error
245  */
246  int checkFlats();
247 
248  /// Load a flat file
249  /** Uses the target argument for lookup in m_flatCommands to find the path
250  * and loads the command in the local memory. Calls setFlat if the flat
251  * is currently set.
252  *
253  * \returns 0 on success
254  * \returns -1 on error
255  */
256  int loadFlat(const std::string &target /**< [in] the name of the flat to load */);
257 
258  /// Send the current flat command to the DM
259  /** Writes the command to the designated shmim.
260  *
261  * \returns 0 on success
262  * \returns -1 on error
263  */
264  int setFlat(bool update = false /**< [in] If true, this is an update rather than a new set*/);
265 
266  /// Zero the flat command on the DM
267  /** Writes a 0 array the designated shmim.
268  *
269  * \returns 0 on success
270  * \returns -1 on error
271  */
272  int zeroFlat();
273 
274  /// Check the tests directory and update the list of tests if anything changes
275  /** This is called once per appLogic and whilePowerOff loops.
276  *
277  * \returns 0 on success
278  * \returns -1 on error
279  */
280  int checkTests();
281 
282  /// Load a test file
283  /** Uses the target argument for lookup in m_testCommands to find the path
284  * and loads the command in the local memory. Calls setTest if the test
285  * is currently set.
286  */
287  int loadTest(const std::string &target);
288 
289  /// Send the current test command to the DM
290  /** Writes the command to the designated shmim.
291  *
292  * \returns 0 on success
293  * \returns -1 on error
294  */
295  int setTest();
296 
297  /// Zero the test command on the DM
298  /** Writes a 0 array the designated shmim.
299  *
300  * \returns 0 on success
301  * \returns -1 on error
302  */
303  int zeroTest();
304 
305  /// Zero all channels
306  /**
307  * \returns 0 on sucess
308  * \returns <0 on an error
309  */
310  int zeroAll(bool nosem = false /**< [in] [optional] if true then the semaphore is not raised after zeroing all channels*/);
311 
312 protected:
313  mx::improc::eigenImage<uint8_t> m_instSatMap; ///< The instantaneous saturation map, 0/1, set by the commandDM() function of the derived class.
314  mx::improc::eigenImage<uint16_t> m_accumSatMap; ///< The accumulated saturation map, which acccumulates for m_satAvgInt then is publised as a 0/1 image.
315  mx::improc::eigenImage<float> m_satPercMap; ///< Map of the percentage of time each actator was saturated during the avg. interval.
316 
317  IMAGE m_satImageStream; ///< The ImageStreamIO shared memory buffer for the sat map.
318  IMAGE m_satPercImageStream; ///< The ImageStreamIO shared memory buffer for the sat percentage map.
319 
320  /// Clear the saturation maps and zero the shared membory.
321  /**
322  * \returns 0 on success
323  * \returns -1 on error
324  */
325  int clearSat();
326 
327  /** \name Saturation Thread
328  * This thread processes the saturation maps
329  * @{
330  */
331 
332  sem_t m_satSemaphore; ///< Semaphore used to tell the saturation thread to run.
333 
334  bool m_satThreadInit{true}; ///< Synchronizer for thread startup, to allow priority setting to finish.
335 
336  pid_t m_satThreadID{0}; ///< The ID of the saturation thread.
337 
338  pcf::IndiProperty m_satThreadProp; ///< The property to hold the saturation thread details.
339 
340  std::thread m_satThread; ///< A separate thread for the actual saturation processing
341 
342  /// Thread starter, called by MagAOXApp::threadStart on thread construction. Calls satThreadExec.
343  static void satThreadStart(dm *d /**< [in] a pointer to a dm instance (normally this) */);
344 
345  /// Execute saturation processing
347 
349  {
350  if (m_satTriggerDevice.size() > 0 && m_satTriggerProperty.size() == m_satTriggerDevice.size())
351  {
352  for (size_t n = 0; n < m_satTriggerDevice.size(); ++n)
353  {
354  // We just silently fail
355  try
356  {
357  pcf::IndiProperty ipFreq(pcf::IndiProperty::Switch);
358 
359  ipFreq.setDevice(m_satTriggerDevice[n]);
360  ipFreq.setName(m_satTriggerProperty[n]);
361  ipFreq.add(pcf::IndiElement("toggle"));
362  ipFreq["toggle"] = pcf::IndiElement::Off;
363  derived().sendNewProperty(ipFreq);
364 
365  derivedT::template log<text_log>("DM saturation threshold exceeded. Loop opened.", logPrio::LOG_WARNING);
366  }
367  catch (...)
368  {
369  }
370  }
371  }
372  }
373 
374  ///@}
375 
376 protected:
377  /** \name INDI
378  *
379  *@{
380  */
381 protected:
382  // declare our properties
383 
384  pcf::IndiProperty m_indiP_flat; ///< Property used to set and report the current flat
385 
386  pcf::IndiProperty m_indiP_init;
387  pcf::IndiProperty m_indiP_zero;
388  pcf::IndiProperty m_indiP_release;
389 
390  pcf::IndiProperty m_indiP_flats; ///< INDI Selection switch containing the flat files.
391  pcf::IndiProperty m_indiP_flatShmim; ///< Publish the shmim being used for the flat
392  pcf::IndiProperty m_indiP_setFlat; ///< INDI toggle switch to set the current flat.
393 
394  pcf::IndiProperty m_indiP_tests; ///< INDI Selection switch containing the test pattern files.
395  pcf::IndiProperty m_indiP_testShmim; ///< Publish the shmim being used for the test command
396  pcf::IndiProperty m_indiP_setTest; ///< INDI toggle switch to set the current test pattern.
397 
398  pcf::IndiProperty m_indiP_zeroAll;
399 
400 public:
401  /// The static callback function to be registered for initializing the DM.
402  /**
403  * \returns 0 on success.
404  * \returns -1 on error.
405  */
406  static int st_newCallBack_init(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
407  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
408  );
409 
410  /// The callback called by the static version, to actually process the new request.
411  /**
412  * \returns 0 on success.
413  * \returns -1 on error.
414  */
415  int newCallBack_init(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
416 
417  /// The static callback function to be registered for initializing the DM.
418  /**
419  * \returns 0 on success.
420  * \returns -1 on error.
421  */
422  static int st_newCallBack_zero(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
423  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
424  );
425 
426  /// The callback called by the static version, to actually process the new request.
427  /**
428  * \returns 0 on success.
429  * \returns -1 on error.
430  */
431  int newCallBack_zero(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
432 
433  /// The static callback function to be registered for initializing the DM.
434  /**
435  * \returns 0 on success.
436  * \returns -1 on error.
437  */
438  static int st_newCallBack_release(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
439  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
440  );
441 
442  /// The callback called by the static version, to actually process the new request.
443  /**
444  * \returns 0 on success.
445  * \returns -1 on error.
446  */
447  int newCallBack_release(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
448 
449  /// The static callback function to be registered for selecting the flat file
450  /**
451  * \returns 0 on success.
452  * \returns -1 on error.
453  */
454  static int st_newCallBack_flats(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
455  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
456  );
457 
458  /// The callback called by the static version, to actually process the new request.
459  /**
460  * \returns 0 on success.
461  * \returns -1 on error.
462  */
463  int newCallBack_flats(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
464 
465  /// The static callback function to be registered for setting the flat
466  /**
467  * \returns 0 on success.
468  * \returns -1 on error.
469  */
470  static int st_newCallBack_setFlat(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
471  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
472  );
473 
474  /// The callback called by the static version, to actually process the new request.
475  /**
476  * \returns 0 on success.
477  * \returns -1 on error.
478  */
479  int newCallBack_setFlat(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
480 
481  /// The static callback function to be registered for selecting the test file
482  /**
483  * \returns 0 on success.
484  * \returns -1 on error.
485  */
486  static int st_newCallBack_tests(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
487  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
488  );
489 
490  /// The callback called by the static version, to actually process the new request.
491  /**
492  * \returns 0 on success.
493  * \returns -1 on error.
494  */
495  int newCallBack_tests(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
496 
497  /// The static callback function to be registered for setting the test shape
498  /**
499  * \returns 0 on success.
500  * \returns -1 on error.
501  */
502  static int st_newCallBack_setTest(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
503  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
504  );
505 
506  /// The callback called by the static version, to actually process the new request.
507  /**
508  * \returns 0 on success.
509  * \returns -1 on error.
510  */
511  int newCallBack_setTest(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
512 
513  /// The static callback function to be registered for zeroing all channels
514  /**
515  * \returns 0 on success.
516  * \returns -1 on error.
517  */
518  static int st_newCallBack_zeroAll(void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
519  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
520  );
521 
522  /// The callback for the zeroAll toggle switch, called by the static version
523  /**
524  * \returns 0 on success.
525  * \returns -1 on error.
526  */
527  int newCallBack_zeroAll(const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
528 
529  /// Update the INDI properties for this device controller
530  /** You should call this once per main loop.
531  * It is not called automatically.
532  *
533  * \returns 0 on success.
534  * \returns -1 on error.
535  */
536  int updateINDI();
537 
538  ///@}
539 
540 public:
541 
542  #ifdef XWC_DMTIMINGS
543  typedef uint16_t cbIndexT;
544 
545  double m_t0 {0}, m_tf {0}, m_tsat0 {0}, m_tsatf {0};
546  double m_tact0 {0}, m_tact1 {0}, m_tact2 {0}, m_tact3 {0}, m_tact4 {0};
547 
548  mx::sigproc::circularBufferIndex<double, cbIndexT> m_piTimes;
549 
550  mx::sigproc::circularBufferIndex<double, cbIndexT> m_satSem;
551 
552  mx::sigproc::circularBufferIndex<double, cbIndexT> m_actProc;
553 
554  mx::sigproc::circularBufferIndex<double, cbIndexT> m_actCom;
555 
556  mx::sigproc::circularBufferIndex<double, cbIndexT> m_satUp;
557 
558  std::vector<double> m_piTimesD;
559  std::vector<double> m_satSemD;
560  std::vector<double> m_actProcD;
561  std::vector<double> m_actComD;
562  std::vector<double> m_satUpD;
563 
564  #endif
565 
566 private:
567  derivedT &derived()
568  {
569  return *static_cast<derivedT *>(this);
570  }
571 };
572 
573 template <class derivedT, typename realT>
574 int dm<derivedT, realT>::setupConfig(mx::app::appConfigurator &config)
575 {
576  config.add("dm.calibPath", "", "dm.calibPath", argType::Required, "dm", "calibPath", false, "string", "The path to calibration files, relative to the MagAO-X calibration path.");
577 
578  config.add("dm.flatPath", "", "dm.flatPath", argType::Required, "dm", "flatPath", false, "string", "The path to flat files. Default is the calibration path.");
579  config.add("dm.flatDefault", "", "dm.flatDefault", argType::Required, "dm", "flatDefault", false, "string", "The default flat file (path and extension are not required).");
580 
581  config.add("dm.testPath", "", "dm.testPath", argType::Required, "dm", "testPath", false, "string", "The path to test files. Default is the calibration path plus /tests.");
582  config.add("dm.testDefault", "", "dm.testDefault", argType::Required, "dm", "testDefault", false, "string", "The default test file (path and extension are not required).");
583 
584  // Overriding the shmimMonitor setup so that these all go in the dm section
585  // Otherwise, would call shmimMonitor<dm<derivedT,realT>>::setupConfig();
586  ///\todo we shmimMonitor now has configSection so this isn't necessary.
587  config.add("dm.threadPrio", "", "dm.threadPrio", argType::Required, "dm", "threadPrio", false, "int", "The real-time priority of the dm control thread.");
588  config.add("dm.cpuset", "", "dm.cpuset", argType::Required, "dm", "cpuset", false, "int", "The cpuset for the dm control thread.");
589 
590  config.add("dm.shmimName", "", "dm.shmimName", argType::Required, "dm", "shmimName", false, "string", "The name of the ImageStreamIO shared memory image to monitor for DM comands. Will be used as /tmp/<shmimName>.im.shm.");
591 
592  config.add("dm.shmimFlat", "", "dm.shmimFlat", argType::Required, "dm", "shmimFlat", false, "string", "The name of the ImageStreamIO shared memory image to write the flat command to. Default is shmimName with 00 apended (i.e. dm00disp -> dm00disp00). ");
593 
594  config.add("dm.shmimTest", "", "dm.shmimTest", argType::Required, "dm", "shmimTest", false, "string", "The name of the ImageStreamIO shared memory image to write the test command to. Default is shmimName with 01 apended (i.e. dm00disp -> dm00disp01). ");
595 
596  config.add("dm.shmimSat", "", "dm.shmimSat", argType::Required, "dm", "shmimSat", false, "string", "The name of the ImageStreamIO shared memory image to write the saturation map to. Default is shmimName with SA apended (i.e. dm00disp -> dm00dispSA). This is created.");
597 
598  config.add("dm.shmimSatPerc", "", "dm.shmimSatPerc", argType::Required, "dm", "shmimSatPerc", false, "string", "The name of the ImageStreamIO shared memory image to write the saturation percentage map to. Default is shmimName with SP apended (i.e. dm00disp -> dm00dispSP). This is created.");
599 
600  config.add("dm.satAvgInt", "", "dm.satAvgInt", argType::Required, "dm", "satAvgInt", false, "int", "The interval in milliseconds over which saturation is accumulated before updating. Default is 100 ms.");
601 
602  config.add("dm.width", "", "dm.width", argType::Required, "dm", "width", false, "string", "The width of the DM in actuators.");
603  config.add("dm.height", "", "dm.height", argType::Required, "dm", "height", false, "string", "The height of the DM in actuators.");
604 
605  config.add("dm.percThreshold", "", "dm.percThreshold", argType::Required, "dm", "percThreshold", false, "float", "Threshold on percentage of frames an actuator is saturated over an interval. Default is 0.98.");
606  config.add("dm.intervalSatThreshold", "", "dm.intervalSatThreshold", argType::Required, "dm", "intervalSatThreshold", false, "float", "Threshold on percentage of actuators which exceed percThreshold in an interval. Default is 0.5.");
607  config.add("dm.intervalSatCountThreshold", "", "dm.intervalSatCountThreshold", argType::Required, "dm", "intervalSatCountThreshold", false, "float", "Threshold one number of consecutive intervals the intervalSatThreshold is exceeded. Default is 10.");
608 
609  config.add("dm.satTriggerDevice", "", "dm.satTriggerDevice", argType::Required, "dm", "satTriggerDevice", false, "vector<string>", "Device(s) with a toggle switch to toggle on saturation trigger.");
610  config.add("dm.satTriggerProperty", "", "dm.satTriggerProperty", argType::Required, "dm", "satTriggerProperty", false, "vector<string>", "Property with a toggle switch to toggle on saturation trigger, one per entry in satTriggerDevice.");
611 
612  return 0;
613 }
614 
615 template <class derivedT, typename realT>
616 int dm<derivedT, realT>::loadConfig(mx::app::appConfigurator &config)
617 {
618 
619  m_calibPath = derived().m_calibDir + "/" + m_calibRelDir;
620  config(m_calibPath, "dm.calibPath");
621 
622  // setup flats
623  m_flatPath = m_calibPath + "/flats";
624  config(m_flatPath, "dm.flatPath");
625 
626  config(m_flatDefault, "dm.flatDefault");
627  if (m_flatDefault != "")
628  {
629  m_flatDefault = mx::ioutils::pathStem(m_flatDefault); // strip off path and extension if provided.
630  m_flatCurrent = "default";
631  }
632 
633  // setup tests
634  m_testPath = m_calibPath + "/tests";
635  config(m_testPath, "dm.testPath");
636 
637  config(m_testDefault, "dm.testDefault");
638  if (m_testDefault != "")
639  {
640  m_testDefault = mx::ioutils::pathStem(m_testDefault); // strip off path and extension if provided.
641  m_testCurrent = "default";
642  }
643 
644  // Overriding the shmimMonitor setup so that these all go in the dm section
645  // Otherwise, would call shmimMonitor<dm<derivedT,realT>>::loadConfig(config);
646  config(derived().m_smThreadPrio, "dm.threadPrio");
647  config(derived().m_smCpuset, "dm.cpuset");
648 
649  config(derived().m_shmimName, "dm.shmimName");
650 
651  if (derived().m_shmimName != "")
652  {
653  m_shmimFlat = derived().m_shmimName + "00";
654  config(m_shmimFlat, "dm.shmimFlat");
655 
656  m_shmimTest = derived().m_shmimName + "02";
657  config(m_shmimTest, "dm.shmimTest");
658 
659  m_shmimSat = derived().m_shmimName + "ST";
660  config(m_shmimSat, "dm.shmimSat");
661 
662  m_shmimSatPerc = derived().m_shmimName + "SP";
663  config(m_shmimSatPerc, "dm.shmimSatPerc");
664 
665  config(m_satAvgInt, "dm.satAvgInt");
666  }
667  else
668  {
669  config.isSet("dm.shmimFlat");
670  config.isSet("dm.shmimTest");
671  config.isSet("dm.shmimSat");
672  config.isSet("dm.shmimSatPerc");
673  config.isSet("dm.satAvgInt");
674  }
675 
676  config(m_dmWidth, "dm.width");
677  config(m_dmHeight, "dm.height");
678 
679  config(m_percThreshold, "dm.percThreshold");
680  config(m_intervalSatThreshold, "dm.intervalSatThreshold");
681  config(m_intervalSatCountThreshold, "dm.intervalSatCountThreshold");
682  config(m_satTriggerDevice, "dm.satTriggerDevice");
683  config(m_satTriggerProperty, "dm.satTriggerProperty");
684 
685  return 0;
686 }
687 
688 template <class derivedT, typename realT>
690 {
691  if (m_dmDataType == 0)
692  {
693  derivedT::template log<software_error>({__FILE__, __LINE__, "unsupported DM data type"});
694  return -1;
695  }
696 
697  //-----------------
698  // Get the flats
699  checkFlats();
700 
701  // Register the test shmim INDI property
702  m_indiP_flatShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
703  m_indiP_flatShmim.setDevice(derived().configName());
704  m_indiP_flatShmim.setName("flat_shmim");
705  m_indiP_flatShmim.setPerm(pcf::IndiProperty::ReadOnly);
706  m_indiP_flatShmim.setState(pcf::IndiProperty::Idle);
707  m_indiP_flatShmim.add(pcf::IndiElement("channel"));
708  m_indiP_flatShmim["channel"] = m_shmimFlat;
709 
710  if (derived().registerIndiPropertyReadOnly(m_indiP_flatShmim) < 0)
711  {
712 #ifndef DM_TEST_NOLOG
713  derivedT::template log<software_error>({__FILE__, __LINE__});
714 #endif
715  return -1;
716  }
717 
718  // Register the setFlat INDI property
719  derived().createStandardIndiToggleSw(m_indiP_setFlat, "flat_set");
720  if (derived().registerIndiPropertyNew(m_indiP_setFlat, st_newCallBack_setFlat) < 0)
721  {
722 #ifndef DM_TEST_NOLOG
723  derivedT::template log<software_error>({__FILE__, __LINE__});
724 #endif
725  return -1;
726  }
727 
728  //-----------------
729  // Get the tests
730  checkTests();
731 
732  // Register the test shmim INDI property
733  m_indiP_testShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
734  m_indiP_testShmim.setDevice(derived().configName());
735  m_indiP_testShmim.setName("test_shmim");
736  m_indiP_testShmim.setPerm(pcf::IndiProperty::ReadOnly);
737  m_indiP_testShmim.setState(pcf::IndiProperty::Idle);
738  m_indiP_testShmim.add(pcf::IndiElement("channel"));
739  m_indiP_testShmim["channel"] = m_shmimTest;
740  derived().createStandardIndiToggleSw(m_indiP_setTest, "test_shmim");
741  if (derived().registerIndiPropertyReadOnly(m_indiP_testShmim) < 0)
742  {
743 #ifndef DM_TEST_NOLOG
744  derivedT::template log<software_error>({__FILE__, __LINE__});
745 #endif
746  return -1;
747  }
748 
749  // Register the setTest INDI property
750  derived().createStandardIndiToggleSw(m_indiP_setTest, "test_set");
751  if (derived().registerIndiPropertyNew(m_indiP_setTest, st_newCallBack_setTest) < 0)
752  {
753 #ifndef DM_TEST_NOLOG
754  derivedT::template log<software_error>({__FILE__, __LINE__});
755 #endif
756  return -1;
757  }
758 
759  // Register the init INDI property
760  derived().createStandardIndiRequestSw(m_indiP_init, "initDM");
761  if (derived().registerIndiPropertyNew(m_indiP_init, st_newCallBack_init) < 0)
762  {
763 #ifndef DM_TEST_NOLOG
764  derivedT::template log<software_error>({__FILE__, __LINE__});
765 #endif
766  return -1;
767  }
768 
769  // Register the zero INDI property
770  derived().createStandardIndiRequestSw(m_indiP_zero, "zeroDM");
771  if (derived().registerIndiPropertyNew(m_indiP_zero, st_newCallBack_zero) < 0)
772  {
773 #ifndef DM_TEST_NOLOG
774  derivedT::template log<software_error>({__FILE__, __LINE__});
775 #endif
776  return -1;
777  }
778 
779  // Register the release INDI property
780  derived().createStandardIndiRequestSw(m_indiP_release, "releaseDM");
781  if (derived().registerIndiPropertyNew(m_indiP_release, st_newCallBack_release) < 0)
782  {
783 #ifndef DM_TEST_NOLOG
784  derivedT::template log<software_error>({__FILE__, __LINE__});
785 #endif
786  return -1;
787  }
788 
789  derived().createStandardIndiRequestSw(m_indiP_zeroAll, "zeroAll");
790  if (derived().registerIndiPropertyNew(m_indiP_zeroAll, st_newCallBack_zeroAll) < 0)
791  {
792 #ifndef DM_TEST_NOLOG
793  derivedT::template log<software_error>({__FILE__, __LINE__});
794 #endif
795  return -1;
796  }
797 
798  if (m_flatDefault != "")
799  {
800  loadFlat("default");
801  }
802 
803  if (m_testDefault != "")
804  {
805  loadTest("default");
806  }
807 
808  if (sem_init(&m_satSemaphore, 0, 0) < 0)
809  {
810  return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Initializing sat semaphore"});
811  }
812 
813  if (derived().threadStart(m_satThread, m_satThreadInit, m_satThreadID, m_satThreadProp, m_satThreadPrio, "", "saturation", this, satThreadStart) < 0)
814  {
815  derivedT::template log<software_error, -1>({__FILE__, __LINE__});
816  return -1;
817  }
818 
819  return 0;
820 }
821 
822 template <class derivedT, typename realT>
824 {
825  // do a join check to see if other threads have exited.
826  if (pthread_tryjoin_np(m_satThread.native_handle(), 0) == 0)
827  {
828  derivedT::template log<software_error>({__FILE__, __LINE__, "saturation thread has exited"});
829 
830  return -1;
831  }
832 
833  checkFlats();
834 
835  checkTests();
836 
837  if (m_intervalSatTrip)
838  {
839  intervalSatTrip();
840  m_intervalSatTrip = false;
841  }
842 
843  #ifdef XWC_DMTIMINGS
844  static uint64_t lastMono = 0;
845 
846  if(m_piTimes.size() >= m_piTimes.maxEntries() && m_piTimes.maxEntries() > 0 && m_piTimes.mono() != lastMono)
847  {
848  cbIndexT refEntry = m_piTimes.earliest();
849 
850  m_piTimesD.resize(m_piTimes.maxEntries());
851  m_satSemD.resize(m_satSem.maxEntries());
852  m_actProcD.resize(m_actProc.maxEntries());
853  m_actComD.resize(m_actCom.maxEntries());
854  m_satUpD.resize(m_satUp.maxEntries());
855 
856  for(size_t n=0; n < m_piTimesD.size(); ++n)
857  {
858  m_piTimesD[n] = m_piTimes.at(refEntry,n);
859  m_satSemD[n] = m_satSem.at(refEntry,n);
860  m_actProcD[n] = m_actProc.at(refEntry,n);
861  m_actComD[n] = m_actCom.at(refEntry,n);
862  m_satUpD[n] = m_satUp.at(refEntry,n);
863  }
864 
865  std::cerr << "Act. Process: " << mx::math::vectorMean(m_actProcD) << " +/- " << sqrt(mx::math::vectorVariance(m_actProcD)) << "\n";
866  std::cerr << "Act. Command: " << mx::math::vectorMean(m_actComD) << " +/- " << sqrt(mx::math::vectorVariance(m_actComD)) << "\n";
867  std::cerr << "Sat. Update: " << mx::math::vectorMean(m_satUpD) << " +/- " << sqrt(mx::math::vectorVariance(m_satUpD)) << "\n";
868  std::cerr << "Tot. CommandDM: " << mx::math::vectorMean(m_piTimesD) << " +/- " << sqrt(mx::math::vectorVariance(m_piTimesD)) << "\n";
869  std::cerr << "Sat. Semaphore: " << mx::math::vectorMean(m_satSemD) << " +/- " << sqrt(mx::math::vectorVariance(m_satSemD)) << "\n";
870  std::cerr << "\n";
871 
872  lastMono = m_piTimes.mono();
873  }
874  #endif //XWC_DMTIMINGS
875 
876  return 0;
877 }
878 
879 template <class derivedT, typename realT>
881 {
882  if (m_satThread.joinable())
883  {
884  pthread_kill(m_satThread.native_handle(), SIGUSR1);
885  try
886  {
887  m_satThread.join(); // this will throw if it was already joined
888  }
889  catch (...)
890  {
891  }
892  }
893 
894  return 0;
895 }
896 
897 template <class derivedT, typename realT>
899 {
900  releaseDM();
901 
902  return 0;
903 }
904 
905 template <class derivedT, typename realT>
907 {
908  checkFlats();
909  checkTests();
910 
911  return 0;
912 }
913 
914 template <class derivedT, typename realT>
916 {
917  std::vector<std::string> dmlist = mx::ioutils::getFileNames("/milk/shm/", derived().m_shmimName, ".im", ".shm");
918 
919  if (dmlist.size() == 0)
920  {
921  derivedT::template log<software_error>({__FILE__, __LINE__, "no dm channels found for " + derived().m_shmimName});
922  return -1;
923  }
924 
925  m_channels = -1;
926  for (size_t n = 0; n < dmlist.size(); ++n)
927  {
928  char nstr[16];
929  snprintf(nstr, sizeof(nstr), "%02d.im.shm", (int)n);
930  std::string tgt = derived().m_shmimName;
931  tgt += nstr;
932 
933  for (size_t m = 0; m < dmlist.size(); ++m)
934  {
935  if (dmlist[m].find(tgt) != std::string::npos)
936  {
937  if ((int)n > m_channels)
938  m_channels = n;
939  }
940  }
941  }
942 
943  ++m_channels;
944 
945  derivedT::template log<text_log>({std::string("Found ") + std::to_string(m_channels) + " channels for " + derived().m_shmimName});
946 
947  return 0;
948 }
949 
950 template <class derivedT, typename realT>
952 {
953  static_cast<void>(sp); // be unused
954 
955  int err = 0;
956 
957  if (derived().m_width != m_dmWidth)
958  {
959  derivedT::template log<software_critical>({__FILE__, __LINE__, "shmim width does not match configured DM width"});
960  ++err;
961  }
962 
963  if (derived().m_height != m_dmHeight)
964  {
965  derivedT::template log<software_critical>({__FILE__, __LINE__, "shmim height does not match configured DM height"});
966  ++err;
967  }
968 
969  if (derived().m_dataType != m_dmDataType)
970  {
971  derivedT::template log<software_critical>({__FILE__, __LINE__, "shmim data type does not match configured DM data type"});
972  ++err;
973  }
974 
975  if (err)
976  return -1;
977 
978  m_instSatMap.resize(m_dmWidth, m_dmHeight);
979  m_instSatMap.setZero();
980 
981  m_accumSatMap.resize(m_dmWidth, m_dmHeight);
982  m_accumSatMap.setZero();
983 
984  m_satPercMap.resize(m_dmWidth, m_dmHeight);
985  m_satPercMap.setZero();
986 
987  if (findDMChannels() < 0)
988  {
989  derivedT::template log<software_critical>({__FILE__, __LINE__, "error finding DM channels"});
990 
991  return -1;
992  }
993 
994  #ifdef XWC_DMTIMINGS
995  m_piTimes.maxEntries(2000);
996  m_satSem.maxEntries(2000);
997  m_actProc.maxEntries(2000);
998  m_actCom.maxEntries(2000);
999  m_satUp.maxEntries(2000);
1000  #endif
1001 
1002  return 0;
1003 }
1004 
1005 template <class derivedT, typename realT>
1007  const dev::shmimT &sp)
1008 {
1009  static_cast<void>(sp); // be unused
1010 
1011  #ifdef XWC_DMTIMINGS
1012  m_t0 = mx::sys::get_curr_time();
1013  #endif
1014 
1015  int rv = derived().commandDM(curr_src);
1016 
1017  #ifdef XWC_DMTIMINGS
1018  m_tf = mx::sys::get_curr_time();
1019  #endif
1020 
1021  if (rv < 0)
1022  {
1023  derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv, "Error from commandDM"});
1024  return rv;
1025  }
1026 
1027  #ifdef XWC_DMTIMINGS
1028  m_tsat0 = mx::sys::get_curr_time();
1029  #endif
1030 
1031  // Tell the sat thread to get going
1032  if (sem_post(&m_satSemaphore) < 0)
1033  {
1034  derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
1035  return -1;
1036  }
1037 
1038  #ifdef XWC_DMTIMINGS
1039  m_tsatf = mx::sys::get_curr_time();
1040  #endif
1041 
1042  #ifdef XWC_DMTIMINGS
1043  //Update the latency circ. buffs
1044  if(m_piTimes.maxEntries() > 0)
1045  {
1046  m_piTimes.nextEntry(m_tf-m_t0);
1047  m_satSem.nextEntry(m_tsatf - m_tsat0);
1048  m_actProc.nextEntry(m_tact1 - m_tact0);
1049  m_actCom.nextEntry(m_tact2 - m_tact1);
1050  m_satUp.nextEntry(m_tact4 - m_tact3);
1051  }
1052  #endif
1053  return rv;
1054 }
1055 
1056 template <class derivedT, typename realT>
1058 {
1059  int rv;
1060  if ((rv = derived().releaseDM()) < 0)
1061  {
1062  derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv, "Error from releaseDM"});
1063  return rv;
1064  }
1065 
1066  if ((rv = zeroAll(true)) < 0)
1067  {
1068  derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv, "Error from zeroAll"});
1069  return rv;
1070  }
1071 
1072  return 0;
1073 }
1074 
1075 template <class derivedT, typename realT>
1077 {
1078  std::vector<std::string> tfs = mx::ioutils::getFileNames(m_flatPath, "", "", ".fits");
1079 
1080  // First remove default, b/c we always add it and don't want to include it in timestamp selected ones
1081  for (size_t n = 0; n < tfs.size(); ++n)
1082  {
1083  if (mx::ioutils::pathStem(tfs[n]) == "default")
1084  {
1085  tfs.erase(tfs.begin() + n);
1086  --n;
1087  }
1088  }
1089 
1090  unsigned m_nFlatFiles = 5;
1091 
1092  // Here we keep only the m_nFlatFiles most recent files
1093  if (tfs.size() >= m_nFlatFiles)
1094  {
1095  std::vector<std::time_t> wtimes(tfs.size());
1096 
1097  for (size_t n = 0; n < wtimes.size(); ++n)
1098  {
1099  wtimes[n] = boost::filesystem::last_write_time(tfs[n]);
1100  }
1101 
1102  std::sort(wtimes.begin(), wtimes.end());
1103 
1104  std::time_t tn = wtimes[wtimes.size() - m_nFlatFiles];
1105 
1106  for (size_t n = 0; n < tfs.size(); ++n)
1107  {
1108  std::time_t lmt = boost::filesystem::last_write_time(tfs[n]);
1109  if (lmt < tn)
1110  {
1111  tfs.erase(tfs.begin() + n);
1112  --n;
1113  }
1114  }
1115  }
1116 
1117  for (auto it = m_flatCommands.begin(); it != m_flatCommands.end(); ++it)
1118  {
1119  it->second = "";
1120  }
1121 
1122  bool changed = false;
1123  for (size_t n = 0; n < tfs.size(); ++n)
1124  {
1125  auto ir = m_flatCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1126  if (ir.second == true)
1127  changed = true;
1128  else
1129  ir.first->second = tfs[n];
1130  }
1131 
1132  for (auto it = m_flatCommands.begin(); it != m_flatCommands.end(); ++it)
1133  {
1134  if (it->second == "")
1135  {
1136  changed = true;
1137  // Erase the current iterator safely, even if the first one.
1138  auto itdel = it;
1139  ++it;
1140  m_flatCommands.erase(itdel);
1141  --it;
1142  };
1143  }
1144 
1145  if (changed)
1146  {
1147  if (derived().m_indiDriver)
1148  {
1149  derived().m_indiDriver->sendDelProperty(m_indiP_flats);
1150  derived().m_indiNewCallBacks.erase(m_indiP_flats.createUniqueKey());
1151  }
1152 
1153  m_indiP_flats = pcf::IndiProperty(pcf::IndiProperty::Switch);
1154  m_indiP_flats.setDevice(derived().configName());
1155  m_indiP_flats.setName("flat");
1156  m_indiP_flats.setPerm(pcf::IndiProperty::ReadWrite);
1157  m_indiP_flats.setState(pcf::IndiProperty::Idle);
1158  m_indiP_flats.setRule(pcf::IndiProperty::OneOfMany);
1159 
1160  // Add the toggle element initialized to Off
1161  for (auto it = m_flatCommands.begin(); it != m_flatCommands.end(); ++it)
1162  {
1163  if (it->first == m_flatCurrent || m_flatCurrent == "")
1164  {
1165  m_indiP_flats.add(pcf::IndiElement(it->first, pcf::IndiElement::On));
1166  m_flatCurrent = it->first; // handles the case m_flatCurrent == "" b/c it was not set in config
1167  }
1168  else
1169  {
1170  m_indiP_flats.add(pcf::IndiElement(it->first, pcf::IndiElement::Off));
1171  }
1172  }
1173 
1174  if (m_flatDefault != "")
1175  {
1176  if (m_flatCurrent == "default")
1177  {
1178  m_indiP_flats.add(pcf::IndiElement("default", pcf::IndiElement::On));
1179  }
1180  else
1181  {
1182  m_indiP_flats.add(pcf::IndiElement("default", pcf::IndiElement::Off));
1183  }
1184  }
1185 
1186  if (derived().registerIndiPropertyNew(m_indiP_flats, st_newCallBack_flats) < 0)
1187  {
1188 #ifndef DM_TEST_NOLOG
1189  derivedT::template log<software_error>({__FILE__, __LINE__});
1190 #endif
1191  return -1;
1192  }
1193 
1194  if (derived().m_indiDriver)
1195  {
1196  derived().m_indiDriver->sendDefProperty(m_indiP_flats);
1197  }
1198  }
1199 
1200  return 0;
1201 }
1202 
1203 template <class derivedT, typename realT>
1204 int dm<derivedT, realT>::loadFlat(const std::string &intarget)
1205 {
1206  std::string target = intarget;
1207 
1208  std::string targetPath;
1209 
1210  if (target == "default")
1211  {
1212  target = m_flatDefault;
1213  targetPath = m_flatPath + "/" + m_flatDefault + ".fits";
1214  }
1215  else
1216  {
1217  try
1218  {
1219  targetPath = m_flatCommands.at(target);
1220  }
1221  catch (...)
1222  {
1223  derivedT::template log<text_log>("flat file " + target + " not found", logPrio::LOG_ERROR);
1224  return -1;
1225  }
1226  }
1227 
1228  m_flatLoaded = false;
1229  // load into memory.
1230  mx::fits::fitsFile<realT> ff;
1231  if (ff.read(m_flatCommand, targetPath) < 0)
1232  {
1233  derivedT::template log<text_log>("flat file " + targetPath + " not found", logPrio::LOG_ERROR);
1234  return -1;
1235  }
1236 
1237  derivedT::template log<text_log>("loaded flat file " + targetPath);
1238  m_flatLoaded = true;
1239 
1240  m_flatCurrent = intarget;
1241 
1242  if (m_indiP_flats.find("default"))
1243  {
1244  if (m_flatCurrent == "default")
1245  {
1246  m_indiP_flats["default"] = pcf::IndiElement::On;
1247  }
1248  else
1249  {
1250  m_indiP_flats["default"] = pcf::IndiElement::Off;
1251  }
1252  }
1253 
1254  for (auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
1255  {
1256  if (!m_indiP_flats.find(i->first))
1257  {
1258  continue;
1259  }
1260 
1261  if (i->first == m_flatCurrent)
1262  {
1263  m_indiP_flats[i->first] = pcf::IndiElement::On;
1264  }
1265  else
1266  {
1267  m_indiP_flats[i->first] = pcf::IndiElement::Off;
1268  }
1269  }
1270 
1271  if (derived().m_indiDriver)
1272  {
1273  derived().m_indiDriver->sendSetProperty(m_indiP_flats);
1274  }
1275 
1276  if (m_flatSet)
1277  {
1278  setFlat();
1279  }
1280 
1281  return 0;
1282 }
1283 
1284 template <class derivedT, typename realT>
1286 {
1287  if (m_shmimFlat == "")
1288  return 0;
1289 
1290  if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1291  {
1292  derivedT::template log<text_log>("could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING);
1293  return -1;
1294  }
1295 
1296  if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1297  {
1298  ImageStreamIO_closeIm(&m_flatImageStream);
1299  derivedT::template log<text_log>("width mismatch between " + m_shmimFlat + " and configured DM", logPrio::LOG_ERROR);
1300  return -1;
1301  }
1302 
1303  if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1304  {
1305  ImageStreamIO_closeIm(&m_flatImageStream);
1306  derivedT::template log<text_log>("height mismatch between " + m_shmimFlat + " and configured DM", logPrio::LOG_ERROR);
1307  return -1;
1308  }
1309 
1310  if (!m_flatLoaded)
1311  {
1312  bool flatSet = m_flatSet;
1313  m_flatSet = false; // make sure we don't loop
1314 
1315  if (loadFlat(m_flatCurrent) < 0)
1316  {
1317  derivedT::template log<text_log>("error loading flat " + m_flatCurrent, logPrio::LOG_ERROR);
1318  }
1319  m_flatSet = flatSet;
1320  }
1321 
1322  if (!m_flatLoaded)
1323  {
1324  ImageStreamIO_closeIm(&m_flatImageStream);
1325  derivedT::template log<text_log>("no flat loaded", logPrio::LOG_ERROR);
1326  return -1;
1327  }
1328 
1329  if (m_flatCommand.rows() != m_dmWidth)
1330  {
1331  ImageStreamIO_closeIm(&m_flatImageStream);
1332  derivedT::template log<text_log>("width mismatch between flat file and configured DM", logPrio::LOG_ERROR);
1333  return -1;
1334  }
1335 
1336  if (m_flatCommand.cols() != m_dmHeight)
1337  {
1338  ImageStreamIO_closeIm(&m_flatImageStream);
1339  derivedT::template log<text_log>("height mismatch between flat file and configured DM", logPrio::LOG_ERROR);
1340  return -1;
1341  }
1342 
1343  m_flatImageStream.md->write = 1;
1344 
1345  ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here anyway. With bounds checks b/c not everyone handles cnt1 properly.
1346  // Copy
1347  memcpy(m_flatImageStream.array.raw, m_flatCommand.data(), m_dmWidth * m_dmHeight * sizeof(realT));
1348 
1349  // Set the time of last write
1350  clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1351 
1352  // Set the image acquisition timestamp
1353  m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1354 
1355  m_flatImageStream.md->cnt0++;
1356  m_flatImageStream.md->write = 0;
1357  ImageStreamIO_sempost(&m_flatImageStream, -1);
1358 
1359  m_flatSet = true;
1360 
1361  // Post the semaphore
1362  ImageStreamIO_closeIm(&m_flatImageStream);
1363 
1364  if (!update)
1365  {
1366  derived().updateSwitchIfChanged(m_indiP_setFlat, "toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1367 
1368  derivedT::template log<text_log>("flat set");
1369  }
1370 
1371  return 0;
1372 }
1373 
1374 template <class derivedT, typename realT>
1376 {
1377  if (m_shmimFlat == "")
1378  return 0;
1379 
1380  if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1381  {
1382  derivedT::template log<text_log>("could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING);
1383  return -1;
1384  }
1385 
1386  if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1387  {
1388  ImageStreamIO_closeIm(&m_flatImageStream);
1389  derivedT::template log<text_log>("width mismatch between " + m_shmimFlat + " and configured DM", logPrio::LOG_ERROR);
1390  return -1;
1391  }
1392 
1393  if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1394  {
1395  ImageStreamIO_closeIm(&m_flatImageStream);
1396  derivedT::template log<text_log>("height mismatch between " + m_shmimFlat + " and configured DM", logPrio::LOG_ERROR);
1397  return -1;
1398  }
1399 
1400  m_flatImageStream.md->write = 1;
1401 
1402  ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here anyway. With bounds checks b/c not everyone handles cnt1 properly.
1403  // Zero
1404  memset(m_flatImageStream.array.raw, 0, m_dmWidth * m_dmHeight * sizeof(realT));
1405 
1406  // Set the time of last write
1407  clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1408 
1409  // Set the image acquisition timestamp
1410  m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1411 
1412  m_flatImageStream.md->cnt0++;
1413  m_flatImageStream.md->write = 0;
1414  ImageStreamIO_sempost(&m_flatImageStream, -1);
1415 
1416  m_flatSet = false;
1417 
1418  // Post the semaphore
1419  ImageStreamIO_closeIm(&m_flatImageStream);
1420 
1421  derived().updateSwitchIfChanged(m_indiP_setFlat, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1422 
1423  derivedT::template log<text_log>("flat zeroed");
1424 
1425  return 0;
1426 }
1427 
1428 template <class derivedT, typename realT>
1430 {
1431  std::vector<std::string> tfs = mx::ioutils::getFileNames(m_testPath, "", "", ".fits");
1432 
1433  for (auto it = m_testCommands.begin(); it != m_testCommands.end(); ++it)
1434  {
1435  it->second = "";
1436  }
1437 
1438  bool changed = false;
1439  for (size_t n = 0; n < tfs.size(); ++n)
1440  {
1441  auto ir = m_testCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1442  if (ir.second == true)
1443  changed = true;
1444  else
1445  ir.first->second = tfs[n];
1446  }
1447 
1448  for (auto it = m_testCommands.begin(); it != m_testCommands.end(); ++it)
1449  {
1450  if (it->second == "")
1451  {
1452  changed = true;
1453  // Erase the current iterator safely, even if the first one.
1454  auto itdel = it;
1455  ++it;
1456  m_testCommands.erase(itdel);
1457  --it;
1458  };
1459  }
1460 
1461  if (changed)
1462  {
1463  if (derived().m_indiDriver)
1464  {
1465  derived().m_indiDriver->sendDelProperty(m_indiP_tests);
1466  derived().m_indiNewCallBacks.erase(m_indiP_tests.createUniqueKey());
1467  }
1468 
1469  m_indiP_tests = pcf::IndiProperty(pcf::IndiProperty::Switch);
1470  m_indiP_tests.setDevice(derived().configName());
1471  m_indiP_tests.setName("test");
1472  m_indiP_tests.setPerm(pcf::IndiProperty::ReadWrite);
1473  m_indiP_tests.setState(pcf::IndiProperty::Idle);
1474  m_indiP_tests.setRule(pcf::IndiProperty::OneOfMany);
1475 
1476  // Add the toggle element initialized to Off
1477  for (auto it = m_testCommands.begin(); it != m_testCommands.end(); ++it)
1478  {
1479  if (it->first == m_testCurrent || m_testCurrent == "")
1480  {
1481  m_indiP_tests.add(pcf::IndiElement(it->first, pcf::IndiElement::On));
1482  m_testCurrent = it->first; // Handles the case when m_testCurrent=="" b/c it was not set in config
1483  }
1484  else
1485  {
1486  m_indiP_tests.add(pcf::IndiElement(it->first, pcf::IndiElement::Off));
1487  }
1488  }
1489 
1490  if (m_testDefault != "")
1491  {
1492  if (m_testCurrent == "default")
1493  {
1494  m_indiP_tests.add(pcf::IndiElement("default", pcf::IndiElement::On));
1495  }
1496  else
1497  {
1498  m_indiP_tests.add(pcf::IndiElement("default", pcf::IndiElement::Off));
1499  }
1500  }
1501 
1502  if (derived().registerIndiPropertyNew(m_indiP_tests, st_newCallBack_tests) < 0)
1503  {
1504 #ifndef DM_TEST_NOLOG
1505  derivedT::template log<software_error>({__FILE__, __LINE__});
1506 #endif
1507  return -1;
1508  }
1509 
1510  if (derived().m_indiDriver)
1511  {
1512  derived().m_indiDriver->sendDefProperty(m_indiP_tests);
1513  }
1514  }
1515 
1516  return 0;
1517 }
1518 
1519 template <class derivedT, typename realT>
1520 int dm<derivedT, realT>::loadTest(const std::string &intarget)
1521 {
1522  std::string target = intarget; // store this for later to resolve default next:
1523 
1524  if (target == "default")
1525  {
1526  target = m_testDefault;
1527  }
1528 
1529  std::string targetPath;
1530 
1531  try
1532  {
1533  targetPath = m_testCommands.at(target);
1534  }
1535  catch (...)
1536  {
1537  derivedT::template log<text_log>("test file " + target + " not found", logPrio::LOG_ERROR);
1538  return -1;
1539  }
1540 
1541  m_testLoaded = false;
1542  // load into memory.
1543  mx::fits::fitsFile<realT> ff;
1544  if (ff.read(m_testCommand, targetPath) < 0)
1545  {
1546  derivedT::template log<text_log>("test file " + targetPath + " not found", logPrio::LOG_ERROR);
1547  return -1;
1548  }
1549 
1550  derivedT::template log<text_log>("loaded test file " + targetPath);
1551  m_testLoaded = true;
1552 
1553  m_testCurrent = intarget;
1554 
1555  if (m_indiP_tests.find("default"))
1556  {
1557  if (m_testCurrent == "default")
1558  {
1559  m_indiP_tests["default"] = pcf::IndiElement::On;
1560  }
1561  else
1562  {
1563  m_indiP_tests["default"] = pcf::IndiElement::Off;
1564  }
1565  }
1566 
1567  for (auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
1568  {
1569  if (!m_indiP_tests.find(i->first))
1570  {
1571  continue;
1572  }
1573 
1574  if (i->first == m_testCurrent)
1575  {
1576  m_indiP_tests[i->first] = pcf::IndiElement::On;
1577  }
1578  else
1579  {
1580  m_indiP_tests[i->first] = pcf::IndiElement::Off;
1581  }
1582  }
1583 
1584  if (derived().m_indiDriver)
1585  derived().m_indiDriver->sendSetProperty(m_indiP_tests);
1586 
1587  if (m_testSet)
1588  setTest();
1589 
1590  return 0;
1591 }
1592 
1593 template <class derivedT, typename realT>
1595 {
1596 
1597  if (m_shmimTest == "")
1598  return 0;
1599 
1600  if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1601  {
1602  derivedT::template log<text_log>("could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING);
1603  return -1;
1604  }
1605 
1606  if (m_testImageStream.md->size[0] != m_dmWidth)
1607  {
1608  ImageStreamIO_closeIm(&m_testImageStream);
1609  derivedT::template log<text_log>("width mismatch between " + m_shmimTest + " and configured DM", logPrio::LOG_ERROR);
1610  return -1;
1611  }
1612 
1613  if (m_testImageStream.md->size[1] != m_dmHeight)
1614  {
1615  ImageStreamIO_closeIm(&m_testImageStream);
1616  derivedT::template log<text_log>("height mismatch between " + m_shmimTest + " and configured DM", logPrio::LOG_ERROR);
1617  return -1;
1618  }
1619 
1620  if (!m_testLoaded)
1621  {
1622  bool testSet = m_testSet;
1623  m_testSet = false; // make sure we don't loop
1624 
1625  if (loadTest(m_testCurrent) < 0)
1626  {
1627  derivedT::template log<text_log>("error loading test " + m_testCurrent, logPrio::LOG_ERROR);
1628  }
1629  m_testSet = testSet;
1630  }
1631 
1632  if (!m_testLoaded)
1633  {
1634  ImageStreamIO_closeIm(&m_testImageStream);
1635  derivedT::template log<text_log>("no test loaded", logPrio::LOG_ERROR);
1636  return -1;
1637  }
1638 
1639  if (m_testCommand.rows() != m_dmWidth)
1640  {
1641  ImageStreamIO_closeIm(&m_testImageStream);
1642  derivedT::template log<text_log>("width mismatch between test file and configured DM", logPrio::LOG_ERROR);
1643  return -1;
1644  }
1645 
1646  if (m_testCommand.cols() != m_dmHeight)
1647  {
1648  ImageStreamIO_closeIm(&m_testImageStream);
1649  derivedT::template log<text_log>("height mismatch between test file and configured DM", logPrio::LOG_ERROR);
1650  return -1;
1651  }
1652 
1653  m_testImageStream.md->write = 1;
1654 
1655  ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here anyway. With bounds checks b/c not everyone handles cnt1 properly.
1656  // Copy
1657  memcpy(m_testImageStream.array.raw, m_testCommand.data(), m_dmWidth * m_dmHeight * sizeof(realT));
1658 
1659  // Set the time of last write
1660  clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1661 
1662  // Set the image acquisition timestamp
1663  m_testImageStream.md->atime = m_testImageStream.md->writetime;
1664 
1665  m_testImageStream.md->cnt0++;
1666  m_testImageStream.md->write = 0;
1667  ImageStreamIO_sempost(&m_testImageStream, -1);
1668 
1669  m_testSet = true;
1670 
1671  // Post the semaphore
1672  ImageStreamIO_closeIm(&m_testImageStream);
1673 
1674  derived().updateSwitchIfChanged(m_indiP_setTest, "toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1675 
1676  derivedT::template log<text_log>("test set");
1677 
1678  return 0;
1679 }
1680 
1681 template <class derivedT, typename realT>
1683 {
1684  if (m_shmimTest == "")
1685  return 0;
1686 
1687  if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1688  {
1689  derivedT::template log<text_log>("could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING);
1690  return -1;
1691  }
1692 
1693  if (m_testImageStream.md[0].size[0] != m_dmWidth)
1694  {
1695  ImageStreamIO_closeIm(&m_testImageStream);
1696  derivedT::template log<text_log>("width mismatch between " + m_shmimTest + " and configured DM", logPrio::LOG_ERROR);
1697  return -1;
1698  }
1699 
1700  if (m_testImageStream.md[0].size[1] != m_dmHeight)
1701  {
1702  ImageStreamIO_closeIm(&m_testImageStream);
1703  derivedT::template log<text_log>("height mismatch between " + m_shmimTest + " and configured DM", logPrio::LOG_ERROR);
1704  return -1;
1705  }
1706 
1707  m_testImageStream.md->write = 1;
1708 
1709  ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here anyway. With bounds checks b/c not everyone handles cnt1 properly.
1710  // Zero
1711  memset(m_testImageStream.array.raw, 0, m_dmWidth * m_dmHeight * sizeof(realT));
1712 
1713  // Set the time of last write
1714  clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1715 
1716  // Set the image acquisition timestamp
1717  m_testImageStream.md->atime = m_testImageStream.md->writetime;
1718 
1719  m_testImageStream.md->cnt0++;
1720  m_testImageStream.md->write = 0;
1721 
1722  // Post the semaphore
1723  ImageStreamIO_sempost(&m_testImageStream, -1);
1724 
1725  m_testSet = false;
1726 
1727  ImageStreamIO_closeIm(&m_testImageStream);
1728 
1729  derived().updateSwitchIfChanged(m_indiP_setTest, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1730 
1731  derivedT::template log<text_log>("test zeroed");
1732 
1733  return 0;
1734 }
1735 
1736 template <class derivedT, typename realT>
1738 {
1739  if (derived().m_shmimName == "")
1740  {
1741  return 0;
1742  }
1743 
1744  IMAGE imageStream;
1745 
1746  for (int n = 0; n < m_channels; ++n)
1747  {
1748  char nstr[16];
1749  snprintf(nstr, sizeof(nstr), "%02d", n);
1750  std::string shmimN = derived().m_shmimName + nstr;
1751 
1752  if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1753  {
1754  derivedT::template log<text_log>("could not connect to channel " + shmimN, logPrio::LOG_WARNING);
1755  continue;
1756  }
1757 
1758  if (imageStream.md->size[0] != m_dmWidth)
1759  {
1760  ImageStreamIO_closeIm(&imageStream);
1761  derivedT::template log<text_log>("width mismatch between " + shmimN + " and configured DM", logPrio::LOG_ERROR);
1762  derived().updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE);
1763  return -1;
1764  }
1765 
1766  if (imageStream.md->size[1] != m_dmHeight)
1767  {
1768  ImageStreamIO_closeIm(&imageStream);
1769  derivedT::template log<text_log>("height mismatch between " + shmimN + " and configured DM", logPrio::LOG_ERROR);
1770  derived().updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE);
1771  return -1;
1772  }
1773 
1774  imageStream.md->write = 1;
1775  memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight * sizeof(realT));
1776 
1777  clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1778 
1779  // Set the image acquisition timestamp
1780  imageStream.md->atime = imageStream.md->writetime;
1781 
1782  imageStream.md->cnt0++;
1783  imageStream.md->write = 0;
1784 
1785  // Raise the semaphore on last one.
1786  if (n == m_channels - 1 && !nosem)
1787  ImageStreamIO_sempost(&imageStream, -1);
1788 
1789  ImageStreamIO_closeIm(&imageStream);
1790  }
1791 
1792  derivedT::template log<text_log>("all channels zeroed", logPrio::LOG_NOTICE);
1793 
1794  derived().updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE);
1795 
1796  // Also cleanup flat and test
1797  m_flatSet = false;
1798  derived().updateSwitchIfChanged(m_indiP_setFlat, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1799 
1800  // Also cleanup flat and test
1801  m_testSet = false;
1802  derived().updateSwitchIfChanged(m_indiP_setTest, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1803 
1804  int rv;
1805  if ((rv = clearSat()) < 0)
1806  {
1807  derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv, "Error from clearSat"});
1808  return rv;
1809  }
1810 
1811  return 0;
1812 }
1813 
1814 template <class derivedT, typename realT>
1816 {
1817  if(m_shmimSat == "" || m_dmWidth == 0 || m_dmHeight == 0)
1818  {
1819  return 0;
1820  }
1821 
1822  IMAGE imageStream;
1823 
1824  std::vector<std::string> sats = {m_shmimSat, m_shmimSatPerc};
1825 
1826  for (size_t n = 0; n < sats.size(); ++n)
1827  {
1828  std::string shmimN = sats[n];
1829 
1830  if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1831  {
1832  derivedT::template log<text_log>("could not connect to sat map " + shmimN, logPrio::LOG_WARNING);
1833  return 0;
1834  }
1835 
1836  if (imageStream.md->size[0] != m_dmWidth)
1837  {
1838  ImageStreamIO_closeIm(&imageStream);
1839  derivedT::template log<text_log>("width mismatch between " + shmimN + " and configured DM", logPrio::LOG_ERROR);
1840  derived().updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE);
1841  return -1;
1842  }
1843 
1844  if (imageStream.md->size[1] != m_dmHeight)
1845  {
1846  ImageStreamIO_closeIm(&imageStream);
1847  derivedT::template log<text_log>("height mismatch between " + shmimN + " and configured DM", logPrio::LOG_ERROR);
1848  derived().updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE);
1849  return -1;
1850  }
1851 
1852  imageStream.md->write = 1;
1853  memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight * ImageStreamIO_typesize(imageStream.md->datatype));
1854 
1855  clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1856 
1857  // Set the image acquisition timestamp
1858  imageStream.md->atime = imageStream.md->writetime;
1859 
1860  imageStream.md->cnt0++;
1861  imageStream.md->write = 0;
1862 
1863  ImageStreamIO_closeIm(&imageStream);
1864  }
1865 
1866  m_accumSatMap.setZero();
1867  m_instSatMap.setZero();
1868 
1869  return 0;
1870 }
1871 
1872 template <class derivedT, typename realT>
1874 {
1875  d->satThreadExec();
1876 }
1877 
1878 template <class derivedT, typename realT>
1880 {
1881  // Get the thread PID immediately so the caller can return.
1882  m_satThreadID = syscall(SYS_gettid);
1883 
1884  // Wait for the thread starter to finish initializing this thread.
1885  while (m_satThreadInit == true && derived().shutdown() == 0)
1886  {
1887  sleep(1);
1888  }
1889  if (derived().shutdown())
1890  return;
1891 
1892  uint32_t imsize[3] = {0, 0, 0};
1893 
1894  // Check for allocation to have happened.
1895  while ((m_shmimSat == "" || m_accumSatMap.rows() == 0 || m_accumSatMap.cols() == 0) && !derived().shutdown())
1896  {
1897  sleep(1);
1898  }
1899 
1900  if (derived().shutdown())
1901  {
1902  return;
1903  }
1904 
1905  imsize[0] = m_dmWidth;
1906  imsize[1] = m_dmHeight;
1907  imsize[2] = 1;
1908 
1909  ImageStreamIO_createIm_gpu(&m_satImageStream, m_shmimSat.c_str(), 3, imsize, IMAGESTRUCT_UINT8, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1910  ImageStreamIO_createIm_gpu(&m_satPercImageStream, m_shmimSatPerc.c_str(), 3, imsize, IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1911 
1912  bool opened = true;
1913 
1914  m_satImageStream.md->cnt1 = 0;
1915  m_satPercImageStream.md->cnt1 = 0;
1916 
1917  // This is the working memory for making the 1/0 mask out of m_accumSatMap
1918  mx::improc::eigenImage<uint8_t> satmap(m_dmWidth, m_dmHeight);
1919 
1920  int naccum = 0;
1921  double t_accumst = mx::sys::get_curr_time();
1922 
1923  // This is the main image grabbing loop.
1924  while (!derived().shutdown())
1925  {
1926  // Get timespec for sem_timedwait
1927  timespec ts;
1928  if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
1929  {
1930  derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0, "clock_gettime"});
1931  return;
1932  }
1933  ts.tv_sec += 1;
1934 
1935  // Wait on semaphore
1936  if (sem_timedwait(&m_satSemaphore, &ts) == 0)
1937  {
1938  // not a timeout -->accumulate
1939  for (int rr = 0; rr < m_instSatMap.rows(); ++rr)
1940  {
1941  for (int cc = 0; cc < m_instSatMap.cols(); ++cc)
1942  {
1943  m_accumSatMap(rr, cc) += m_instSatMap(rr, cc);
1944  }
1945  }
1946  ++naccum;
1947 
1948  // If less than avg int --> go back and wait again
1949  if (mx::sys::get_curr_time(ts) - t_accumst < m_satAvgInt / 1000.0)
1950  {
1951  continue;
1952  }
1953 
1954  // If greater than avg int --> calc stats, write to streams.
1955  m_overSatAct = 0;
1956  for (int rr = 0; rr < m_instSatMap.rows(); ++rr)
1957  {
1958  for (int cc = 0; cc < m_instSatMap.cols(); ++cc)
1959  {
1960  m_satPercMap(rr, cc) = m_accumSatMap(rr, cc) / naccum;
1961  if (m_satPercMap(rr, cc) >= m_percThreshold)
1962  {
1963  ++m_overSatAct;
1964  }
1965  satmap(rr, cc) = (m_accumSatMap(rr, cc) > 0); // it's 1/0 map
1966  }
1967  }
1968 
1969  // Check of the number of actuators saturated above the percent threshold is greater than the number threshold
1970  // if it is, increment the counter
1971  if (m_overSatAct / (m_satPercMap.rows() * m_satPercMap.cols() * 0.75) > m_intervalSatThreshold)
1972  {
1973  ++m_intervalSatExceeds;
1974  }
1975  else
1976  {
1977  m_intervalSatExceeds = 0;
1978  }
1979 
1980  // If enough consecutive intervals exceed the count threshold, we trigger
1981  if (m_intervalSatExceeds >= m_intervalSatCountThreshold)
1982  {
1983  m_intervalSatTrip = true;
1984  }
1985 
1986  m_satImageStream.md->write = 1;
1987  m_satPercImageStream.md->write = 1;
1988 
1989  memcpy(m_satImageStream.array.raw, satmap.data(), m_dmWidth * m_dmHeight * sizeof(uint8_t));
1990  memcpy(m_satPercImageStream.array.raw, m_satPercMap.data(), m_dmWidth * m_dmHeight * sizeof(float));
1991 
1992  // Set the time of last write
1993  clock_gettime(CLOCK_REALTIME, &m_satImageStream.md->writetime);
1994  m_satPercImageStream.md->writetime = m_satImageStream.md->writetime;
1995 
1996  // Set the image acquisition timestamp
1997  m_satImageStream.md->atime = m_satImageStream.md->writetime;
1998  m_satPercImageStream.md->atime = m_satPercImageStream.md->writetime;
1999 
2000  // Update cnt1
2001  m_satImageStream.md->cnt1 = 0;
2002  m_satPercImageStream.md->cnt1 = 0;
2003 
2004  // Update cnt0
2005  m_satImageStream.md->cnt0++;
2006  m_satPercImageStream.md->cnt0++;
2007 
2008  m_satImageStream.writetimearray[0] = m_satImageStream.md->writetime;
2009  m_satImageStream.atimearray[0] = m_satImageStream.md->atime;
2010  m_satImageStream.cntarray[0] = m_satImageStream.md->cnt0;
2011 
2012  m_satPercImageStream.writetimearray[0] = m_satPercImageStream.md->writetime;
2013  m_satPercImageStream.atimearray[0] = m_satPercImageStream.md->atime;
2014  m_satPercImageStream.cntarray[0] = m_satPercImageStream.md->cnt0;
2015 
2016  // And post
2017  m_satImageStream.md->write = 0;
2018  ImageStreamIO_sempost(&m_satImageStream, -1);
2019 
2020  m_satPercImageStream.md->write = 0;
2021  ImageStreamIO_sempost(&m_satPercImageStream, -1);
2022 
2023  m_accumSatMap.setZero();
2024  naccum = 0;
2025  t_accumst = mx::sys::get_curr_time(ts);
2026  }
2027  else
2028  {
2029  // Check for why we timed out
2030  if (errno == EINTR)
2031  {
2032  break; // This indicates signal interrupted us, time to restart or shutdown, loop will exit normally if flags set.
2033  }
2034 
2035  // ETIMEDOUT just means we should wait more.
2036  // Otherwise, report an error.
2037  if (errno != ETIMEDOUT)
2038  {
2039  derivedT::template log<software_error>({__FILE__, __LINE__, errno, "sem_timedwait"});
2040  break;
2041  }
2042  }
2043  }
2044 
2045  if (opened)
2046  {
2047  ImageStreamIO_destroyIm(&m_satImageStream);
2048 
2049  ImageStreamIO_destroyIm(&m_satPercImageStream);
2050  }
2051 }
2052 
2053 template <class derivedT, typename realT>
2055 {
2056  if (!derived().m_indiDriver)
2057  return 0;
2058 
2059  return 0;
2060 }
2061 
2062 template <class derivedT, typename realT>
2064  const pcf::IndiProperty &ipRecv)
2065 {
2066  return static_cast<derivedT *>(app)->newCallBack_init(ipRecv);
2067 }
2068 
2069 template <class derivedT, typename realT>
2070 int dm<derivedT, realT>::newCallBack_init(const pcf::IndiProperty &ipRecv)
2071 {
2072  if (ipRecv.createUniqueKey() != m_indiP_init.createUniqueKey())
2073  {
2074  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "wrong INDI-P in callback"});
2075  }
2076 
2077  if (!ipRecv.find("request"))
2078  return 0;
2079 
2080  if (ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
2081  {
2082  return derived().initDM();
2083  }
2084  return 0;
2085 }
2086 
2087 template <class derivedT, typename realT>
2089  const pcf::IndiProperty &ipRecv)
2090 {
2091  return static_cast<derivedT *>(app)->newCallBack_zero(ipRecv);
2092 }
2093 
2094 template <class derivedT, typename realT>
2095 int dm<derivedT, realT>::newCallBack_zero(const pcf::IndiProperty &ipRecv)
2096 {
2097  if (ipRecv.createUniqueKey() != m_indiP_zero.createUniqueKey())
2098  {
2099  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "wrong INDI-P in callback"});
2100  }
2101 
2102  if (!ipRecv.find("request"))
2103  return 0;
2104 
2105  if (ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
2106  {
2107  return derived().zeroDM();
2108  }
2109  return 0;
2110 }
2111 
2112 template <class derivedT, typename realT>
2114  const pcf::IndiProperty &ipRecv)
2115 {
2116  return static_cast<derivedT *>(app)->newCallBack_release(ipRecv);
2117 }
2118 
2119 template <class derivedT, typename realT>
2121 {
2122  if (ipRecv.createUniqueKey() != m_indiP_release.createUniqueKey())
2123  {
2124  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "wrong INDI-P in callback"});
2125  }
2126 
2127  if (!ipRecv.find("request"))
2128  return 0;
2129 
2130  if (ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
2131  {
2132  return releaseDM();
2133  }
2134  return 0;
2135 }
2136 
2137 template <class derivedT, typename realT>
2139  const pcf::IndiProperty &ipRecv)
2140 {
2141  return static_cast<derivedT *>(app)->newCallBack_flats(ipRecv);
2142 }
2143 
2144 template <class derivedT, typename realT>
2145 int dm<derivedT, realT>::newCallBack_flats(const pcf::IndiProperty &ipRecv)
2146 {
2147  if (ipRecv.createUniqueKey() != m_indiP_flats.createUniqueKey())
2148  {
2149  derivedT::template log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
2150  return -1;
2151  }
2152 
2153  std::string newFlat;
2154 
2155  if (ipRecv.find("default"))
2156  {
2157  if (ipRecv["default"].getSwitchState() == pcf::IndiElement::On)
2158  {
2159  newFlat = "default";
2160  }
2161  }
2162 
2163  // always do this to check for error:
2164  for (auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
2165  {
2166  if (!ipRecv.find(i->first))
2167  continue;
2168 
2169  if (ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2170  {
2171  if (newFlat != "")
2172  {
2173  derivedT::template log<text_log>("More than one flat selected", logPrio::LOG_ERROR);
2174  return -1;
2175  }
2176 
2177  newFlat = i->first;
2178  }
2179  }
2180 
2181  if (newFlat == "")
2182  return 0;
2183 
2184  return loadFlat(newFlat);
2185 }
2186 
2187 template <class derivedT, typename realT>
2189  const pcf::IndiProperty &ipRecv)
2190 {
2191  return static_cast<derivedT *>(app)->newCallBack_setFlat(ipRecv);
2192 }
2193 
2194 template <class derivedT, typename realT>
2196 {
2197  if (ipRecv.createUniqueKey() != m_indiP_setFlat.createUniqueKey())
2198  {
2199  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "wrong INDI-P in callback"});
2200  }
2201 
2202  if (!ipRecv.find("toggle"))
2203  return 0;
2204 
2205  if (ipRecv["toggle"] == pcf::IndiElement::On)
2206  {
2207  return setFlat();
2208  }
2209  else
2210  {
2211  return zeroFlat();
2212  }
2213 }
2214 
2215 template <class derivedT, typename realT>
2217  const pcf::IndiProperty &ipRecv)
2218 {
2219  return static_cast<derivedT *>(app)->newCallBack_tests(ipRecv);
2220 }
2221 
2222 template <class derivedT, typename realT>
2223 int dm<derivedT, realT>::newCallBack_tests(const pcf::IndiProperty &ipRecv)
2224 {
2225  if (ipRecv.createUniqueKey() != m_indiP_tests.createUniqueKey())
2226  {
2227  derivedT::template log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
2228  return -1;
2229  }
2230 
2231  std::string newTest;
2232 
2233  if (ipRecv.find("default"))
2234  {
2235  if (ipRecv["default"].getSwitchState() == pcf::IndiElement::On)
2236  {
2237  newTest = "default";
2238  }
2239  }
2240 
2241  // always do this to check for error:
2242  for (auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
2243  {
2244  if (!ipRecv.find(i->first))
2245  continue;
2246 
2247  if (ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2248  {
2249  if (newTest != "")
2250  {
2251  derivedT::template log<text_log>("More than one test selected", logPrio::LOG_ERROR);
2252  return -1;
2253  }
2254 
2255  newTest = i->first;
2256  }
2257  }
2258 
2259  if (newTest == "")
2260  return 0;
2261 
2262  return loadTest(newTest);
2263 }
2264 
2265 template <class derivedT, typename realT>
2267  const pcf::IndiProperty &ipRecv)
2268 {
2269  return static_cast<derivedT *>(app)->newCallBack_setTest(ipRecv);
2270 }
2271 
2272 template <class derivedT, typename realT>
2274 {
2275  if (ipRecv.createUniqueKey() != m_indiP_setTest.createUniqueKey())
2276  {
2277  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "wrong INDI-P in callback"});
2278  }
2279 
2280  if (!ipRecv.find("toggle"))
2281  return 0;
2282 
2283  if (ipRecv["toggle"] == pcf::IndiElement::On)
2284  {
2285  return setTest();
2286  }
2287  else
2288  {
2289  return zeroTest();
2290  }
2291 }
2292 
2293 template <class derivedT, typename realT>
2295  const pcf::IndiProperty &ipRecv)
2296 {
2297  return static_cast<derivedT *>(app)->newCallBack_zeroAll(ipRecv);
2298 }
2299 
2300 template <class derivedT, typename realT>
2302 {
2303  if (ipRecv.createUniqueKey() != m_indiP_zeroAll.createUniqueKey())
2304  {
2305  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "wrong INDI-P in callback"});
2306  }
2307 
2308  if (!ipRecv.find("request"))
2309  return 0;
2310 
2311  if (ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
2312  {
2313  indi::updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::On, derived().m_indiDriver, INDI_BUSY);
2314 
2315  std::lock_guard<std::mutex> guard(derived().m_indiMutex);
2316  return zeroAll();
2317  }
2318  return 0;
2319 }
2320 
2321 /// Call dmT::setupConfig with error checking for dm
2322 /**
2323  * \param cfig the application configurator
2324  */
2325 #define DM_SETUP_CONFIG(cfig) \
2326  if (dmT::setupConfig(cfig) < 0) \
2327  { \
2328  log<software_error>({__FILE__, __LINE__, "Error from dmT::setupConfig"}); \
2329  m_shutdown = true; \
2330  return; \
2331  }
2332 
2333 /// Call dmT::loadConfig with error checking for dm
2334 /** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
2335  * \param cfig the application configurator
2336  */
2337 #define DM_LOAD_CONFIG(cfig) \
2338  if (dmT::loadConfig(cfig) < 0) \
2339  { \
2340  return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::loadConfig"}); \
2341  }
2342 
2343 /// Call shmimMonitorT::appStartup with error checking for dm
2344 #define DM_APP_STARTUP \
2345  if (dmT::appStartup() < 0) \
2346  { \
2347  return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appStartup"}); \
2348  }
2349 
2350 /// Call dmT::appLogic with error checking for dm
2351 #define DM_APP_LOGIC \
2352  if (dmT::appLogic() < 0) \
2353  { \
2354  return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appLogic"}); \
2355  }
2356 
2357 /// Call dmT::updateINDI with error checking for dm
2358 #define DM_UPDATE_INDI \
2359  if (dmT::updateINDI() < 0) \
2360  { \
2361  return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::updateINDI"}); \
2362  }
2363 
2364 /// Call dmT::appShutdown with error checking for dm
2365 #define DM_APP_SHUTDOWN \
2366  if (dmT::appShutdown() < 0) \
2367  { \
2368  return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appShutdown"}); \
2369  }
2370 
2371 } // namespace dev
2372 } // namespace app
2373 } // namespace MagAOX
2374 #endif
#define IMAGESTRUCT_FLOAT
Definition: ImageStruct.hpp:22
#define IMAGESTRUCT_UINT8
Definition: ImageStruct.hpp:14
std::string m_calibPath
The path to this DM's calibration files.
Definition: dm.hpp:76
std::map< std::string, std::string > m_flatCommands
Map of flat file name to full path.
Definition: dm.hpp:111
int newCallBack_tests(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition: dm.hpp:2223
bool m_testSet
Flag indicating whether the test command has been set.
Definition: dm.hpp:127
std::string m_flatPath
The path to this DM's flat files (usually the same as calibPath)
Definition: dm.hpp:77
pcf::IndiProperty m_indiP_setFlat
INDI toggle switch to set the current flat.
Definition: dm.hpp:392
static void satThreadStart(dm *d)
Thread starter, called by MagAOXApp::threadStart on thread construction. Calls satThreadExec.
Definition: dm.hpp:1873
pcf::IndiProperty m_indiP_init
Definition: dm.hpp:386
uint32_t m_dmWidth
The width of the images in the stream.
Definition: dm.hpp:93
pcf::IndiProperty m_indiP_flats
INDI Selection switch containing the flat files.
Definition: dm.hpp:390
int zeroFlat()
Zero the flat command on the DM.
Definition: dm.hpp:1375
static int st_newCallBack_init(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
Definition: dm.hpp:2063
int checkFlats()
Check the flats directory and update the list of flats if anything changes.
Definition: dm.hpp:1076
pcf::IndiProperty m_indiP_testShmim
Publish the shmim being used for the test command.
Definition: dm.hpp:395
int checkTests()
Check the tests directory and update the list of tests if anything changes.
Definition: dm.hpp:1429
static int st_newCallBack_tests(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for selecting the test file.
Definition: dm.hpp:2216
std::string m_flatDefault
The file name of the this DM's default flat command. Path and extension will be ignored and can be om...
Definition: dm.hpp:80
int setTest()
Send the current test command to the DM.
Definition: dm.hpp:1594
int clearSat()
Clear the saturation maps and zero the shared membory.
Definition: dm.hpp:1815
static int st_newCallBack_zero(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
Definition: dm.hpp:2088
int loadTest(const std::string &target)
Load a test file.
Definition: dm.hpp:1520
derivedT & derived()
Definition: dm.hpp:567
int loadFlat(const std::string &target)
Load a flat file.
Definition: dm.hpp:1204
int m_satThreadPrio
Priority of the saturation thread, should normally be > 0.
Definition: dm.hpp:91
IMAGE m_satPercImageStream
The ImageStreamIO shared memory buffer for the sat percentage map.
Definition: dm.hpp:318
std::string m_calibRelDir
The directory relative to the calibPath. Set this before calling dm<derivedT,realT>::loadConfig().
Definition: dm.hpp:107
pcf::IndiProperty m_indiP_setTest
INDI toggle switch to set the current test pattern.
Definition: dm.hpp:396
int findDMChannels()
Find the DM comb channels.
Definition: dm.hpp:915
static constexpr uint8_t m_dmDataType
The ImageStreamIO type code.
Definition: dm.hpp:96
pcf::IndiProperty m_indiP_tests
INDI Selection switch containing the test pattern files.
Definition: dm.hpp:394
std::string m_shmimFlat
The name of the shmim stream to write the flat to.
Definition: dm.hpp:83
int processImage(void *curr_src, const dev::shmimT &sp)
Called by shmimMonitor when a new DM command is available. This is just a pass-through to derivedT::c...
Definition: dm.hpp:1006
uint32_t m_dmHeight
The height of the images in the stream.
Definition: dm.hpp:94
int appShutdown()
DM shutdown.
Definition: dm.hpp:880
std::string m_testPath
The path to this DM's test files (default is calibPath/tests;.
Definition: dm.hpp:78
int newCallBack_zeroAll(const pcf::IndiProperty &ipRecv)
The callback for the zeroAll toggle switch, called by the static version.
Definition: dm.hpp:2301
bool m_flatSet
Flag indicating whether the flat command has been set.
Definition: dm.hpp:118
pcf::IndiProperty m_satThreadProp
The property to hold the saturation thread details.
Definition: dm.hpp:338
std::string m_shmimSatPerc
The name of the shmim stream to write the saturation percentage map to.
Definition: dm.hpp:86
int newCallBack_setTest(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition: dm.hpp:2273
static int st_newCallBack_flats(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for selecting the flat file.
Definition: dm.hpp:2138
std::string m_testCurrent
Definition: dm.hpp:121
int m_intervalSatExceeds
Definition: dm.hpp:130
bool m_flatLoaded
Flag indicating whether a flat is loaded in memory.
Definition: dm.hpp:115
bool m_testLoaded
Flag indicating whether a test command is loaded in memory.
Definition: dm.hpp:124
mx::improc::eigenImage< float > m_satPercMap
Map of the percentage of time each actator was saturated during the avg. interval.
Definition: dm.hpp:315
int setFlat(bool update=false)
Send the current flat command to the DM.
Definition: dm.hpp:1285
static int st_newCallBack_zeroAll(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for zeroing all channels.
Definition: dm.hpp:2294
int newCallBack_init(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition: dm.hpp:2070
int zeroAll(bool nosem=false)
Zero all channels.
Definition: dm.hpp:1737
pid_t m_satThreadID
The ID of the saturation thread.
Definition: dm.hpp:336
int zeroTest()
Zero the test command on the DM.
Definition: dm.hpp:1682
int m_satAvgInt
The time in milliseconds to accumulate saturation over.
Definition: dm.hpp:88
int newCallBack_release(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition: dm.hpp:2120
std::vector< std::string > m_satTriggerProperty
Definition: dm.hpp:103
int updateINDI()
Update the INDI properties for this device controller.
Definition: dm.hpp:2054
int newCallBack_flats(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition: dm.hpp:2145
sem_t m_satSemaphore
Semaphore used to tell the saturation thread to run.
Definition: dm.hpp:332
pcf::IndiProperty m_indiP_flat
Property used to set and report the current flat.
Definition: dm.hpp:384
IMAGE m_satImageStream
The ImageStreamIO shared memory buffer for the sat map.
Definition: dm.hpp:317
int newCallBack_zero(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition: dm.hpp:2095
bool m_satThreadInit
Synchronizer for thread startup, to allow priority setting to finish.
Definition: dm.hpp:334
std::vector< std::string > m_satTriggerDevice
Definition: dm.hpp:102
mx::improc::eigenImage< realT > m_flatCommand
Data storage for the flat command.
Definition: dm.hpp:114
mx::improc::eigenImage< uint8_t > m_instSatMap
The instantaneous saturation map, 0/1, set by the commandDM() function of the derived class.
Definition: dm.hpp:313
void satThreadExec()
Execute saturation processing.
Definition: dm.hpp:1879
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
Definition: dm.hpp:616
static int st_newCallBack_release(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
Definition: dm.hpp:2113
pcf::IndiProperty m_indiP_flatShmim
Publish the shmim being used for the flat.
Definition: dm.hpp:391
float m_intervalSatThreshold
Definition: dm.hpp:99
int whilePowerOff()
DM Poweroff Updates.
Definition: dm.hpp:906
static int st_newCallBack_setFlat(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for setting the flat.
Definition: dm.hpp:2188
static int st_newCallBack_setTest(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for setting the test shape.
Definition: dm.hpp:2266
pcf::IndiProperty m_indiP_zeroAll
Definition: dm.hpp:398
std::map< std::string, std::string > m_testCommands
Map of test file name to full path.
Definition: dm.hpp:120
IMAGE m_testImageStream
The ImageStreamIO shared memory buffer for the test.
Definition: dm.hpp:126
std::string m_testDefault
The file name of the this DM's default test command. Path and extension will be ignored and can be om...
Definition: dm.hpp:81
int m_channels
The number of dmcomb channels found as part of allocation.
Definition: dm.hpp:109
std::string m_shmimTest
The name of the shmim stream to write the test to.
Definition: dm.hpp:84
bool m_intervalSatTrip
Definition: dm.hpp:131
int m_intervalSatCountThreshold
Definition: dm.hpp:100
int appStartup()
Startup function.
Definition: dm.hpp:689
int newCallBack_setFlat(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition: dm.hpp:2195
mx::improc::eigenImage< uint16_t > m_accumSatMap
The accumulated saturation map, which acccumulates for m_satAvgInt then is publised as a 0/1 image.
Definition: dm.hpp:314
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
Definition: dm.hpp:574
int onPowerOff()
DM Poweroff.
Definition: dm.hpp:898
float m_percThreshold
Definition: dm.hpp:98
std::string m_shmimSat
The name of the shmim stream to write the saturation map to.
Definition: dm.hpp:85
mx::improc::eigenImage< realT > m_testCommand
Data storage for the test command.
Definition: dm.hpp:123
IMAGE m_flatImageStream
The ImageStreamIO shared memory buffer for the flat.
Definition: dm.hpp:117
std::thread m_satThread
A separate thread for the actual saturation processing.
Definition: dm.hpp:340
pcf::IndiProperty m_indiP_release
Definition: dm.hpp:388
pcf::IndiProperty m_indiP_zero
Definition: dm.hpp:387
int releaseDM()
Calls derived()->releaseDM() and then 0s all channels and the sat map.
Definition: dm.hpp:1057
int allocate(const dev::shmimT &sp)
Called after shmimMonitor connects to the dmXXdisp stream. Checks for proper size.
Definition: dm.hpp:951
std::string m_flatCurrent
The name of the current flat command.
Definition: dm.hpp:112
void intervalSatTrip()
Definition: dm.hpp:348
int appLogic()
DM application logic.
Definition: dm.hpp:823
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_BUSY
Definition: indiUtils.hpp:30
std::ostream & cerr()
constexpr uint8_t ImageStreamTypeCode< float >()
Definition: dm.hpp:37
constexpr uint8_t ImageStreamTypeCode()
Definition: dm.hpp:31
constexpr uint8_t ImageStreamTypeCode< double >()
Definition: dm.hpp:43
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &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:212
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
Definition: dm.hpp:24
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46