Line data Source code
1 : /** \file dmPokeWFS.hpp
2 : * \brief The MagAO-X DM Poke Centering header file
3 : *
4 : * \ingroup dmPokeWFS_files
5 : */
6 :
7 : #ifndef dmPokeWFS_hpp
8 : #define dmPokeWFS_hpp
9 :
10 : #include <mx/improc/eigenImage.hpp>
11 : #include <mx/improc/milkImage.hpp>
12 : #include <mx/improc/eigenCube.hpp>
13 : using namespace mx::improc;
14 :
15 : #include "semUtilsDerived.hpp"
16 :
17 : #include "../../ImageStreamIO/pixaccess.hpp"
18 :
19 : /** \defgroup dmPokeWFS
20 : * \brief The MagAO-X device to coordinate poking a deformable mirror's actuators and synchronize reads of a camera image.
21 : *
22 : * <a href="../handbook/operating/software/apps/dmPokeWFS.html">Application Documentation</a>
23 : *
24 : * \ingroup apps
25 : *
26 : */
27 :
28 :
29 : /** \defgroup dmPokeWFS_files
30 : * \ingroup dmPokeWFS
31 : */
32 :
33 : namespace MagAOX
34 : {
35 : namespace app
36 : {
37 : namespace dev
38 : {
39 :
40 :
41 :
42 : /// A base class to coordinate poking a deformable mirror's actuators and synchronizedreads of a camera image.
43 : /** CRTP class `derivedT` has the following requirements:
44 : *
45 : * - Must be derived from MagAOXApp<true>
46 : *
47 : * - Must be derived from `dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT>` (replace DERIVEDNAME with derivedT class name)
48 : *
49 : * - Must be derived from `dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT>` (replace DERIVEDNAME with derivedT class name)
50 : *
51 : * - Must contain the following friend declarations (replace DERIVEDNAME with derivedT class name):
52 : * \code
53 : * friend class dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT>;
54 : * friend class dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT>;
55 : * friend class dev::dmPokeWFS<DERIVEDNAME>
56 : * \endcode
57 : *
58 : * - Must contain the following typedefs (replace DERIVEDNAME with derivedT class name):
59 : * \code
60 : * typedef dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT> shmimMonitorT;
61 : * typedef dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT> darkShmimMonitorT;
62 : * typedef dev::dmPokeWFS<DERIVEDNAME> dmPokeWFST;
63 : *
64 : * \endcode
65 : * - Must provide the following interfaces:
66 : * \code
67 : * shmimMonitorT & shmimMonitor()
68 : * {
69 : * return *static_cast<shmimMonitorT *>(this);
70 : * }
71 : *
72 : * darkShmimMonitorT & darkShmimMonitor()
73 : * {
74 : * return *static_cast<darkShmimMonitorT *>(this);
75 : * }
76 : * \endcode
77 : *
78 : * - If derivedT has additional shmimMonitor parents, you will need to include these lines in the class
79 : * declaration:
80 : * \code
81 : * using dmPokeWFST::allocate;
82 : * using dmPokeWFST::processImage;
83 : * \endcode
84 : *
85 : * - Must provide the following interface:
86 : * \code
87 : * // Run the sensor steps
88 : * // Coordinates the actions of poking and collecting images.
89 : * // Upon completion this calls runSensor. If \p firstRun == true, one time
90 : * // actions such as taking a dark can be executed.
91 : * //
92 : * // returns 0 on success
93 : * // returns \< 0 on an error
94 : * int runSensor(bool firstRun ///< [in] flag indicating this is the first call. triggers taking a dark if true.
95 : * );
96 : * \endcode
97 : *
98 : * - Must provide the following interface:
99 : * \code
100 : * // Analyze the poke image
101 : * // This analyzes the resulting poke images.
102 : * //
103 : * // returns 0 on success
104 : * // returns \< 0 on an error
105 : * int analyzeSensor();
106 : * \endcode
107 : * At the conclusion of analyzeSensor the measured signal (e.g. deltaX and deltaY) should be updated and set in m_indiP_measurement.
108 : * The function \ref updateMeasurement() can be used for this. However, the updating of the loop counter and the subsequent INDI
109 : * property update is handled automatically after that.
110 : *
111 : * - Must be a telemeter with the following interface:
112 : *
113 : * - Must be derived from `dev::telemeter<DERIVEDNAME>` (replace DERIVEDNAME with derivedT class name) and meet the requirements
114 : * of `dev::telemeter`
115 : *
116 : * - In the function `derivedT::checkRecordTimes()` required by `dev::telemeter`, the `telem_pokeloop` type must be checked.
117 : * The minimum `derivedT::checkRecordTimes()` is:
118 : * \code
119 : * int checkRecordTimes()
120 : * {
121 : * return telemeterT::checkRecordTimes(telem_pokeloop());
122 : * }
123 : * \endcode
124 : *
125 : * - Must call this base class's setupConfig(), loadConfig(), appStartup(), appStartup(), and appShutdown() in the
126 : * appropriate functions. For convenience the following macros are defined to provide error checking:
127 : * \code
128 : * DMPOKEWFS_SETUP_CONFIG( cfig )
129 : * DMPOKEWFS_LOAD_CONFIG( cfig )
130 : * DMPOKEWFS_APP_STARTUP
131 : * DMPOKEWFS_APP_LOGIC
132 : * DMPOKEWFS_APP_SHUTDOWN
133 : * \endcode
134 : *
135 : * \ingroup appdev
136 : */
137 : template<class derivedT>
138 : class dmPokeWFS
139 : {
140 :
141 : public:
142 :
143 : struct wfsShmimT
144 : {
145 0 : static std::string configSection()
146 : {
147 0 : return "wfscam";
148 : };
149 :
150 0 : static std::string indiPrefix()
151 : {
152 0 : return "wfscam";
153 : };
154 : };
155 :
156 : struct darkShmimT
157 : {
158 0 : static std::string configSection()
159 : {
160 0 : return "wfsdark";
161 : };
162 :
163 0 : static std::string indiPrefix()
164 : {
165 0 : return "wfsdark";
166 : };
167 : };
168 :
169 : protected:
170 :
171 : /** \name Configurable Parameters
172 : *@{
173 : */
174 :
175 : std::string m_wfsCamDevName; ///<INDI device name of the WFS camera. Default is wfscam.shmimName.
176 :
177 : double m_wfsSemWait {1.5}; ///< The time in sec to wait on the WFS semaphore. Default 0.5 sec.
178 :
179 : double m_imageSemWait {0.5}; ///< The time in sec to wait on the image semaphore. Default 0.5 sec.
180 :
181 : unsigned m_nPokeImages {5}; ///< The number of images to average for the poke images. Default is 5.
182 :
183 : unsigned m_nPokeAverage {10}; ///< The number of poke sequences to average. Default is 10.
184 :
185 : std::string m_dmChan;
186 :
187 : std::vector<int> m_poke_x;
188 : std::vector<int> m_poke_y;
189 :
190 : float m_poke_amp {0.0};
191 :
192 : float m_dmSleep {10000}; ///<The time to sleep for the DM command to be applied, in microseconds. Default is 10000.
193 :
194 : ///@}
195 :
196 : std::mutex m_wfsImageMutex;
197 :
198 : mx::improc::milkImage<float> m_rawImage;
199 :
200 : mx::improc::milkImage<float> m_pokeImage;
201 : mx::improc::eigenImage<float> m_pokeLocal;
202 :
203 : float (*wfsPixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as float
204 :
205 : float m_wfsFps {-1}; ///< The WFS camera FPS
206 :
207 : mx::improc::eigenImage<float> m_darkImage; ///< The dark image
208 :
209 : bool m_darkValid {false}; ///< Flag indicating if dark is valid based on its size.
210 :
211 : float (*darkPixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the dark image data as float
212 :
213 : mx::improc::milkImage<float> m_dmStream;
214 :
215 : mx::improc::eigenImage<float> m_dmImage;
216 :
217 : float m_deltaX {0};
218 : float m_deltaY {0};
219 : uint64_t m_counter {0};
220 :
221 : public:
222 :
223 : /**\name MagAOXApp Interface
224 : *
225 : * @{
226 : */
227 :
228 : /// Setup the configuration system
229 : /**
230 : * This should be called in `derivedT::setupConfig` as
231 : * \code
232 : dmPokeWFS<derivedT,realT>::setupConfig(config);
233 : \endcode
234 : * with appropriate error checking.
235 : */
236 : int setupConfig( mx::app::appConfigurator & config /**< [in] an application configuration to load values to*/);
237 :
238 : /// load the configuration system results
239 : /**
240 : * This should be called in `derivedT::loadConfig` as
241 : * \code
242 : dmPokeWFS<derivedT,realT>::loadConfig(config);
243 : \endcode
244 : * with appropriate error checking.
245 : */
246 : int loadConfig( mx::app::appConfigurator & config /**< [in] an application configuration from which to load values */);
247 :
248 : /// Startup function
249 : /**
250 : * This should be called in `derivedT::appStartup` as
251 : * \code
252 : dmPokeWFS<derivedT,realT>::appStartup();
253 : \endcode
254 : * with appropriate error checking.
255 : *
256 : * \returns 0 on success
257 : * \returns -1 on error, which is logged.
258 : */
259 : int appStartup();
260 :
261 : /// dmPokeWFS application logic
262 : /** This should be called in `derivedT::appLogic` as
263 : * \code
264 : dmPokeWFS<derivedT,realT>::appLogic();
265 : \endcode
266 : * with appropriate error checking.
267 : *
268 : * \returns 0 on success
269 : * \returns -1 on error, which is logged.
270 : */
271 : int appLogic();
272 :
273 : /// dmPokeWFS shutdown
274 : /** This should be called in `derivedT::appShutdown` as
275 : * \code
276 : dmPokeWFS<derivedT,realT>::appShutdown();
277 : \endcode
278 : * with appropriate error checking.
279 : *
280 : * \returns 0 on success
281 : * \returns -1 on error, which is logged.
282 : */
283 : int appShutdown();
284 :
285 : ///@}
286 :
287 : /** \name shmimMonitor Interface
288 : * @{
289 : */
290 :
291 : int allocate( const wfsShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/);
292 :
293 : int processImage( void * curr_src, ///< [in] pointer to the start of the current frame
294 : const wfsShmimT & ///< [in] tag to differentiate shmimMonitor parents.
295 : );
296 : ///@}
297 :
298 :
299 : /** \name darkShmimMonitor Interface
300 : * @{
301 : */
302 :
303 : int allocate( const darkShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/);
304 :
305 : int processImage( void * curr_src, ///< [in] pointer to the start of the current frame
306 : const darkShmimT & ///< [in] tag to differentiate shmimMonitor parents.
307 : );
308 : ///@}
309 :
310 : /** \name WFS Thread
311 : * This thread coordinates the WFS process
312 : *
313 : * @{
314 : */
315 : protected:
316 :
317 : int m_wfsThreadPrio {1}; ///< Priority of the WFS thread, should normally be > 00.
318 :
319 : std::string m_wfsCpuset; ///< The cpuset for the framegrabber thread. Ignored if empty (the default).
320 :
321 : std::thread m_wfsThread; ///< A separate thread for the actual WFSing
322 :
323 : bool m_wfsThreadInit {true}; ///< Synchronizer to ensure wfs thread initializes before doing dangerous things.
324 :
325 : pid_t m_wfsThreadID {0}; ///< WFS thread PID.
326 :
327 : pcf::IndiProperty m_wfsThreadProp; ///< The property to hold the WFS thread details.
328 :
329 : ///Thread starter, called by wfsThreadStart on thread construction. Calls wfsThreadExec.
330 : static void wfsThreadStart( dmPokeWFS * s /**< [in] a pointer to an streamWriter instance (normally this) */);
331 :
332 : /// Execute the frame grabber main loop.
333 : void wfsThreadExec();
334 :
335 : sem_t m_wfsSemaphore; ///< Semaphore used to signal the WFS thread to start WFSing.
336 :
337 : unsigned m_wfsSemWait_sec {1}; ///< The timeout for the WFS semaphore, seconds component.
338 :
339 : unsigned m_wfsSemWait_nsec {0}; ///< The timeoutfor the WFS semaphore, nanoseconds component.
340 :
341 : int m_measuring {0}; ///< Status of measuring: 0 no, 1 single in progress, 2 continuous in progress.
342 :
343 : bool m_single {false}; ///< True a single measurement is in progress.
344 :
345 : bool m_continuous {false}; ///< True if continuous measurements are in progress.
346 :
347 : bool m_stopMeasurement {false}; ///< Used to request that the measurement in progress stop.
348 :
349 : ///@}
350 :
351 : sem_t m_imageSemaphore; ///< Semaphore used to signal that an image is ready
352 :
353 : unsigned m_imageSemWait_sec {1}; ///< The timeout for the image semaphore, seconds component.
354 :
355 : unsigned m_imageSemWait_nsec {0}; ///< The timeout for the image semaphore, nanoseconds component.
356 :
357 :
358 : /// Apply a single DM poke pattern and record the results
359 : /** This accumulates m_nPokeImages*m_nPokeAverage images in m_pokeLocal, so m_pokeLocal
360 : * should be zeroed before the first call to this (e.g. for a +1 poke),
361 : * but not zeroed before the second call (e.g. for the -1 poke). You also need
362 : * to 0 the DM after finishing a poke pair.
363 : * See basicRunSensor() for how to use.
364 : *
365 : * \returns +1 if exit is due to shutdown or stop request
366 : * \returns 0 if no error
367 : * \returns -1 if an error occurs
368 : */
369 : int basicTimedPoke(float pokeSign /**< [in] the sign, and possibly a scaling, to apply to m_pokeAmplitude*/);
370 :
371 : /// Run the basic +/- poke sensor steps
372 : /** Coordinates the actions of poking and collecting images.
373 : *
374 : * This can be called from the derived class runSensor.
375 : *
376 : * \returns +1 if exit is due to shutdown or stop request
377 : * \returns 0 if no error
378 : * \returns -1 if an error occurs
379 : */
380 : int basicRunSensor();
381 :
382 : int updateMeasurement( float deltaX,
383 : float deltaY
384 : );
385 :
386 : /** \name INDI Interface
387 : * @{
388 : */
389 : protected:
390 :
391 : pcf::IndiProperty m_indiP_poke_amp;
392 0 : INDI_NEWCALLBACK_DECL(derivedT, m_indiP_poke_amp);
393 :
394 : pcf::IndiProperty m_indiP_nPokeImages;
395 0 : INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeImages);
396 :
397 : pcf::IndiProperty m_indiP_nPokeAverage;
398 0 : INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeAverage);
399 :
400 : pcf::IndiProperty m_indiP_wfsFps; ///< Property to get the FPS from the WFS camera
401 0 : INDI_SETCALLBACK_DECL(derivedT, m_indiP_wfsFps);
402 :
403 : pcf::IndiProperty m_indiP_single; ///< Switch to start a single measurement
404 0 : INDI_NEWCALLBACK_DECL(derivedT, m_indiP_single);
405 :
406 : pcf::IndiProperty m_indiP_continuous; ///< Switch to start continuous measurement
407 0 : INDI_NEWCALLBACK_DECL(derivedT, m_indiP_continuous);
408 :
409 : pcf::IndiProperty m_indiP_stop; ///< Switch to request that measurement stop
410 0 : INDI_NEWCALLBACK_DECL(derivedT, m_indiP_stop);
411 :
412 : pcf::IndiProperty m_indiP_measurement; ///< Property to report the delta measurement, including the loop counter.
413 :
414 :
415 : ///@}
416 :
417 : /** \name Telemeter Interface
418 : * @{
419 : */
420 :
421 : int recordTelem(const telem_pokeloop *);
422 :
423 : int recordPokeLoop(bool force = false);
424 :
425 : ///@}
426 :
427 : private:
428 0 : derivedT & derived()
429 : {
430 0 : return *static_cast<derivedT *>(this);
431 : }
432 :
433 : };
434 :
435 : template<class derivedT>
436 0 : int dmPokeWFS<derivedT>::setupConfig(mx::app::appConfigurator & config)
437 : {
438 0 : if(derived().shmimMonitor().setupConfig(config) < 0)
439 : {
440 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "shmimMonitorT::setupConfig"});
441 0 : return -1;
442 : }
443 :
444 0 : config.add("wfscam.camDevName", "", "wfscam.camDevName", argType::Required, "wfscam", "camDevName", false, "string", "INDI device name of the WFS camera. Default is wfscam.shmimName.");
445 0 : config.add("wfscam.loopSemWait", "", "wfscam.loopSemWait", argType::Required, "wfscam", "loopSemWait", false, "float", "The semaphore wait time for the wfs loop start signal");
446 0 : config.add("wfscam.imageSemWait", "", "wfscam.imageSemWait", argType::Required, "wfscam", "imageSemWait", false, "float", "The semaphore wait time for the image availability signal");
447 :
448 0 : if(derived().darkShmimMonitor().setupConfig(config) < 0)
449 : {
450 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "darkShmimMonitorT::setupConfig"});
451 0 : return -1;
452 : }
453 :
454 0 : config.add("pokecen.dmChannel", "", "pokecen.dmChannel", argType::Required, "pokecen", "dmChannel", false, "string", "The dm channel to use for pokes, e.g. dm01disp06.");
455 0 : config.add("pokecen.pokeX", "", "pokecen.pokeX", argType::Required, "pokecen", "pokeX", false, "vector<int>", "The x-coordinates of the actuators to poke. ");
456 0 : config.add("pokecen.pokeY", "", "pokecen.pokeY", argType::Required, "pokecen", "pokeY", false, "vector<int>", "The y-coordinates of the actuators to poke. ");
457 0 : config.add("pokecen.pokeAmp", "", "pokecen.pokeAmp", argType::Required, "pokecen", "pokeAmp", false, "float", "The poke amplitude, in DM command units. Default is 0.");
458 0 : config.add("pokecen.dmSleep", "", "pokecen.dmSleep", argType::Required, "pokecen", "dmSleep", false, "float", "The time to sleep for the DM command to be applied, in microseconds. Default is 10000.");
459 0 : config.add("pokecen.nPokeImages", "", "pokecen.nPokeImages", argType::Required, "pokecen", "nPokeImages", false, "int", "The number of poke images to average. Default 5.");
460 0 : config.add("pokecen.nPokeAverage", "", "pokecen.nPokeAverage", argType::Required, "pokecen", "nPokeAverage", false, "int", "The number of poke sequences to average. Default 10.");
461 :
462 :
463 0 : return 0;
464 : }
465 :
466 :
467 : template<class derivedT>
468 0 : int dmPokeWFS<derivedT>::loadConfig( mx::app::appConfigurator & config)
469 : {
470 0 : if(derived().shmimMonitor().loadConfig(config) < 0)
471 : {
472 0 : return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "shmimMonitorT::loadConfig"});
473 : }
474 :
475 0 : m_wfsCamDevName = derived().shmimMonitor().shmimName();
476 0 : config(m_wfsCamDevName, "wfscam.camDevName");
477 :
478 : //configure the semaphore waits
479 0 : config(m_wfsSemWait, "wfscam.loopSemWait");
480 :
481 0 : m_wfsSemWait_sec = floor(m_wfsSemWait);
482 0 : m_wfsSemWait_nsec = (m_wfsSemWait - m_wfsSemWait_sec) * 1e9;
483 :
484 0 : config(m_imageSemWait, "wfscam.imageSemWait");
485 :
486 0 : m_imageSemWait_sec = floor(m_imageSemWait);
487 0 : m_imageSemWait_nsec = (m_imageSemWait - m_imageSemWait_sec) * 1e9;
488 :
489 0 : if(derived().darkShmimMonitor().loadConfig(config) < 0)
490 : {
491 0 : return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "darkShmimMonitorT::loadConfig"});
492 : }
493 :
494 0 : config(m_dmChan, "pokecen.dmChannel");
495 :
496 0 : config(m_poke_x, "pokecen.pokeX");
497 :
498 0 : config(m_poke_y, "pokecen.pokeY");
499 :
500 0 : if(m_poke_x.size() == 0 || (m_poke_x.size() != m_poke_y.size()))
501 : {
502 0 : return derivedT::template log<software_error,-1>({__FILE__, __LINE__, "invalid poke specification"});
503 : }
504 :
505 0 : config(m_poke_amp, "pokecen.pokeAmp");
506 :
507 0 : config(m_dmSleep, "pokecen.dmSleep");
508 :
509 0 : config(m_nPokeImages, "pokecen.nPokeImages");
510 :
511 0 : config(m_nPokeAverage, "pokecen.nPokeAverage");
512 :
513 0 : return 0;
514 : }
515 :
516 : template<class derivedT>
517 0 : int dmPokeWFS<derivedT>::appStartup()
518 : {
519 0 : if( derived().shmimMonitor().appStartup() < 0)
520 : {
521 0 : return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
522 : }
523 :
524 0 : if( derived().darkShmimMonitor().appStartup() < 0)
525 : {
526 0 : return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
527 : }
528 :
529 0 : CREATE_REG_INDI_NEW_NUMBERF_DERIVED(m_indiP_poke_amp, "poke_amp", -1, 1, 1e-1, "%0.01f", "", "");
530 0 : m_indiP_poke_amp["current"].setValue(m_poke_amp);
531 0 : m_indiP_poke_amp["target"].setValue(m_poke_amp);
532 :
533 0 : CREATE_REG_INDI_NEW_NUMBERI_DERIVED(m_indiP_nPokeImages, "nPokeImages", 1, 1000, 1, "%d", "", "");
534 0 : m_indiP_nPokeImages["current"].setValue(m_nPokeImages);
535 0 : m_indiP_nPokeImages["target"].setValue(m_nPokeImages);
536 :
537 0 : CREATE_REG_INDI_NEW_NUMBERI_DERIVED(m_indiP_nPokeAverage, "nPokeAverage", 1, 1000, 1, "%d", "", "");
538 0 : m_indiP_nPokeAverage["current"].setValue(m_nPokeAverage);
539 0 : m_indiP_nPokeAverage["target"].setValue(m_nPokeAverage);
540 :
541 0 : REG_INDI_SETPROP_DERIVED(m_indiP_wfsFps, m_wfsCamDevName, std::string("fps"));
542 :
543 0 : CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED( m_indiP_single, "single");
544 :
545 0 : CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED( m_indiP_continuous, "continuous");
546 :
547 0 : CREATE_REG_INDI_NEW_REQUESTSWITCH_DERIVED( m_indiP_stop, "stop");
548 :
549 0 : derived().template registerIndiPropertyReadOnly( m_indiP_measurement, "measurement", pcf::IndiProperty::Number, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle);
550 0 : m_indiP_measurement.add({"delta_x", 0.0});
551 0 : m_indiP_measurement.add({"delta_y", 0.0});
552 0 : m_indiP_measurement.add({"counter", 0});
553 :
554 0 : if(sem_init(&m_wfsSemaphore, 0,0) < 0)
555 : {
556 0 : return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0, "Initializing wfs semaphore"});
557 : }
558 :
559 0 : if(sem_init(&m_imageSemaphore, 0,0) < 0)
560 : {
561 0 : return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0, "Initializing image semaphore"});
562 : }
563 :
564 0 : if(derived().template threadStart( m_wfsThread, m_wfsThreadInit, m_wfsThreadID, m_wfsThreadProp, m_wfsThreadPrio, m_wfsCpuset, "wfs", this, wfsThreadStart) < 0)
565 : {
566 0 : return derivedT::template log<software_critical,-1>({__FILE__, __LINE__});
567 : }
568 :
569 0 : return 0;
570 : }
571 :
572 : template<class derivedT>
573 0 : int dmPokeWFS<derivedT>::appLogic()
574 : {
575 :
576 0 : if( derived().shmimMonitor().appLogic() < 0)
577 : {
578 0 : return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
579 : }
580 :
581 0 : if( derived().darkShmimMonitor().appLogic() < 0)
582 : {
583 0 : return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
584 : }
585 :
586 : //first do a join check to see if other threads have exited.
587 : //these will throw if the threads are really gone
588 : try
589 : {
590 0 : if(pthread_tryjoin_np(m_wfsThread.native_handle(),0) == 0)
591 : {
592 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "WFS thread has exited"});
593 0 : return -1;
594 : }
595 : }
596 0 : catch(...)
597 : {
598 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "WFS thread has exited"});
599 0 : return -1;
600 : }
601 :
602 0 : if(m_measuring > 0)
603 : {
604 0 : if(m_continuous)
605 : {
606 0 : derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::On, INDI_OK);
607 : }
608 : else
609 : {
610 0 : derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
611 : }
612 :
613 0 : if(m_single)
614 : {
615 0 : derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::On, INDI_OK);
616 : }
617 : else
618 : {
619 0 : derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
620 : }
621 : }
622 : else
623 : {
624 0 : derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
625 0 : derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
626 : }
627 :
628 0 : derived().template updateIfChanged( m_indiP_nPokeImages, "current", m_nPokeImages);
629 0 : derived().template updateIfChanged( m_indiP_nPokeAverage, "current", m_nPokeAverage);
630 0 : derived().template updateIfChanged( m_indiP_poke_amp, "current", m_poke_amp);
631 :
632 0 : return 0;
633 : }
634 :
635 : template<class derivedT>
636 0 : int dmPokeWFS<derivedT>::appShutdown()
637 : {
638 0 : if(derived().shmimMonitor().appShutdown() < 0)
639 : {
640 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "error from shmimMonitorT::appShutdown"});
641 : }
642 :
643 0 : if(derived().darkShmimMonitor().appShutdown() < 0)
644 : {
645 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "error from darkShmimMonitorT::appShutdown"});
646 : }
647 :
648 0 : if (m_wfsThread.joinable())
649 : {
650 0 : pthread_kill(m_wfsThread.native_handle(), SIGUSR1);
651 : try
652 : {
653 0 : m_wfsThread.join(); // this will throw if it was already joined
654 : }
655 0 : catch (...)
656 : {
657 : }
658 : }
659 :
660 0 : return 0;
661 : }
662 :
663 : template<class derivedT>
664 0 : int dmPokeWFS<derivedT>::allocate( const wfsShmimT & dummy)
665 : {
666 : static_cast<void>(dummy); //be unused
667 :
668 0 : std::unique_lock<std::mutex> lock(m_wfsImageMutex);
669 :
670 0 : m_rawImage.create( derived().m_configName + "_raw", derived().shmimMonitor().width(), derived().shmimMonitor().height());
671 :
672 0 : wfsPixget = getPixPointer<float>(derived().shmimMonitor().dataType());
673 :
674 : try
675 : {
676 0 : m_dmStream.open(m_dmChan);
677 : }
678 0 : catch(const std::exception& e)
679 : {
680 0 : return derivedT::template log<software_error,-1>({__FILE__, __LINE__, std::string("exception opening DM: ") + e.what()});
681 : }
682 :
683 0 : m_dmStream.passive(true);
684 :
685 0 : m_dmImage.resize(m_dmStream.rows(), m_dmStream.cols());
686 :
687 0 : if(derived().darkShmimMonitor().width() == derived().shmimMonitor().width() &&
688 0 : derived().darkShmimMonitor().height() == derived().shmimMonitor().height() )
689 : {
690 0 : m_darkValid = true;
691 : }
692 : else
693 : {
694 0 : m_darkValid = false;
695 : }
696 :
697 0 : if(m_pokeImage.rows() != derived().shmimMonitor().width() || m_pokeImage.cols() != derived().shmimMonitor().height())
698 : {
699 0 : m_pokeImage.create(derived().m_configName + "_poke", derived().shmimMonitor().width(), derived().shmimMonitor().height());
700 : }
701 :
702 0 : m_pokeLocal.resize(derived().shmimMonitor().width(), derived().shmimMonitor().height());
703 :
704 0 : return 0;
705 0 : }
706 :
707 : template<class derivedT>
708 0 : int dmPokeWFS<derivedT>::processImage( void * curr_src,
709 : const wfsShmimT & dummy
710 : )
711 : {
712 : static_cast<void>(dummy); //be unused
713 :
714 0 : std::unique_lock<std::mutex> lock(m_wfsImageMutex);
715 :
716 0 : float * data = m_rawImage().data();
717 0 : float * darkData = m_darkImage.data();
718 :
719 : //Copy the data out as float no matter what type it is
720 0 : uint64_t Npix = derived().shmimMonitor().width()*derived().shmimMonitor().height();
721 :
722 0 : if(m_darkValid)
723 : {
724 0 : for(unsigned nn=0; nn < Npix; ++nn)
725 : {
726 0 : data[nn] = wfsPixget(curr_src, nn) - darkData[nn];
727 : }
728 : }
729 : else
730 : {
731 0 : for(unsigned nn=0; nn < Npix; ++nn)
732 : {
733 0 : data[nn] = wfsPixget(curr_src, nn);
734 : }
735 : }
736 :
737 0 : if(sem_post(&m_imageSemaphore) < 0)
738 : {
739 0 : return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
740 : }
741 :
742 0 : return 0;
743 0 : }
744 :
745 : //--dark shmim
746 :
747 : template<class derivedT>
748 0 : int dmPokeWFS<derivedT>::allocate( const darkShmimT & dummy)
749 : {
750 : static_cast<void>(dummy); //be unused
751 :
752 0 : std::unique_lock<std::mutex> lock(m_wfsImageMutex);
753 :
754 0 : m_darkImage.resize(derived().darkShmimMonitor().width(), derived().darkShmimMonitor().height());
755 :
756 0 : darkPixget = getPixPointer<float>(derived().darkShmimMonitor().dataType());
757 :
758 0 : if(derived().darkShmimMonitor().width() == derived().shmimMonitor().width() &&
759 0 : derived().darkShmimMonitor().height() == derived().shmimMonitor().height() )
760 : {
761 0 : std::cerr << "dark is valid " << derived().darkShmimMonitor().width() << " " << derived().shmimMonitor().width() << " ";
762 0 : std::cerr << derived().darkShmimMonitor().height() << " " << derived().shmimMonitor().height() << "\n";
763 0 : m_darkValid = true;
764 : }
765 : else
766 : {
767 0 : m_darkValid = false;
768 : }
769 :
770 0 : return 0;
771 0 : }
772 :
773 : template<class derivedT>
774 0 : int dmPokeWFS<derivedT>::processImage( void * curr_src,
775 : const darkShmimT & dummy
776 : )
777 : {
778 : static_cast<void>(dummy); //be unused
779 :
780 0 : std::unique_lock<std::mutex> lock(m_wfsImageMutex);
781 :
782 0 : float * darkData = m_darkImage.data();
783 :
784 : //Copy the data out as float no matter what type it is
785 0 : uint64_t nPix = derived().darkShmimMonitor().width()*derived().darkShmimMonitor().height();
786 0 : for(unsigned nn=0; nn < nPix; ++nn)
787 : {
788 0 : darkData[nn] = darkPixget(curr_src, nn);
789 : }
790 :
791 0 : return 0;
792 0 : }
793 :
794 : template<class derivedT>
795 0 : void dmPokeWFS<derivedT>::wfsThreadStart( dmPokeWFS * d)
796 : {
797 0 : d->wfsThreadExec();
798 0 : }
799 :
800 : template<class derivedT>
801 0 : void dmPokeWFS<derivedT>::wfsThreadExec()
802 : {
803 0 : m_wfsThreadID = syscall(SYS_gettid);
804 :
805 : //Wait fpr the thread starter to finish initializing this thread.
806 0 : while(m_wfsThreadInit == true && derived().m_shutdown == 0)
807 : {
808 0 : sleep(1);
809 : }
810 :
811 0 : while(derived().m_shutdown == 0)
812 : {
813 : timespec ts;
814 0 : XWC_SEM_WAIT_TS_RETVOID_DERIVED(ts, m_wfsSemWait_sec, m_wfsSemWait_nsec);
815 :
816 0 : XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_wfsSemaphore, ts )
817 :
818 : //Lock a mutex here
819 0 : if(m_single)
820 : {
821 0 : m_measuring = 1;
822 : }
823 0 : else if(m_continuous)
824 : {
825 0 : m_measuring = 2;
826 : }
827 : else
828 : {
829 0 : m_measuring = 0;
830 0 : return;
831 : }
832 :
833 0 : derived().template state(stateCodes::OPERATING);
834 :
835 0 : while(!m_pokeImage.valid())
836 : {
837 0 : mx::sys::milliSleep(10);
838 : }
839 :
840 0 : m_stopMeasurement = false;
841 :
842 0 : bool firstRun = true;
843 :
844 0 : while(!m_stopMeasurement && !derived().m_shutdown)
845 : {
846 0 : if( derived().runSensor(firstRun) < 0)
847 : {
848 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "runSensor returned error"});
849 0 : break;
850 : }
851 :
852 0 : if(m_stopMeasurement || derived().m_shutdown)
853 : {
854 0 : break;
855 : }
856 :
857 0 : if( derived().analyzeSensor() < 0)
858 : {
859 0 : derivedT::template log<software_error>({__FILE__, __LINE__, "runSensor returned error"});
860 0 : break;
861 : }
862 :
863 0 : ++m_counter;
864 0 : derived().updateIfChanged(m_indiP_measurement, "counter", m_counter);
865 0 : derived().recordPokeLoop();
866 :
867 0 : firstRun = false;
868 :
869 0 : if(m_measuring == 1)
870 : {
871 0 : break;
872 : }
873 : }
874 :
875 0 : m_measuring = 0;
876 0 : m_single = 0;
877 0 : m_continuous = 0;
878 :
879 0 : derived().template state(stateCodes::READY);
880 :
881 :
882 : } //outer loop, will exit if derived().m_shutdown==true
883 :
884 0 : return;
885 :
886 : }
887 :
888 : template<class derivedT>
889 0 : int dmPokeWFS<derivedT>::basicTimedPoke(float pokeSign)
890 : {
891 : timespec ts;
892 :
893 0 : int sign = 1;
894 0 : if(pokeSign < 0) sign = -1;
895 :
896 : //Prepare the DM image with the pokes
897 0 : m_dmImage.setZero();
898 :
899 0 : for(size_t nn = 0; nn < m_poke_x.size(); ++nn)
900 : {
901 0 : m_dmImage( m_poke_x[nn], m_poke_y[nn]) = pokeSign*m_poke_amp;
902 : }
903 :
904 : //This is where the pokes are applied to the DM
905 0 : m_dmStream = m_dmImage;
906 :
907 0 : mx::sys::microSleep(m_dmSleep);
908 :
909 : //flush semaphore so we take the _next_ good image
910 0 : XWC_SEM_FLUSH_DERIVED(m_imageSemaphore);
911 :
912 : //** And wait one image to be sure we are on a whole poke**//
913 0 : XWC_SEM_WAIT_TS_DERIVED(ts, m_imageSemWait_sec, m_imageSemWait_nsec);
914 0 : bool ready = false;
915 0 : while(!ready && !(m_stopMeasurement || derived().m_shutdown))
916 : {
917 0 : XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_imageSemaphore, ts )
918 : else
919 : {
920 0 : ready = true;
921 : }
922 : }
923 :
924 0 : uint32_t n = 0;
925 0 : while(n < m_nPokeImages && !(m_stopMeasurement || derived().m_shutdown))
926 : {
927 : //** Now we record the poke image **//
928 0 : XWC_SEM_WAIT_TS_DERIVED(ts, m_imageSemWait_sec, m_imageSemWait_nsec);
929 0 : XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_imageSemaphore, ts )
930 :
931 : //If here, we got an image. m_rawImage will have been updated
932 0 : m_pokeLocal += sign*m_rawImage();
933 :
934 0 : ++n;
935 : }
936 :
937 0 : if(m_stopMeasurement || derived().m_shutdown)
938 : {
939 0 : m_dmImage.setZero();
940 0 : m_dmStream = m_dmImage;
941 0 : return 1;
942 : }
943 :
944 0 : return 0;
945 : }
946 :
947 : template<class derivedT>
948 0 : int dmPokeWFS<derivedT>::basicRunSensor()
949 : {
950 : int rv;
951 :
952 0 : if(!m_pokeImage.valid())
953 : {
954 0 : return derivedT::template log<software_error,-1>({__FILE__, __LINE__, "poke image is not allocated"});
955 : }
956 :
957 0 : m_pokeLocal.setZero();
958 :
959 0 : for(unsigned nseq = 0; nseq < m_nPokeAverage; ++nseq)
960 : {
961 :
962 : //************** positive POKE **********************/
963 :
964 0 : rv = basicTimedPoke(+1);
965 :
966 0 : if(rv < 0)
967 : {
968 0 : derivedT::template log<software_error>({__FILE__, __LINE__});
969 0 : return rv;
970 : }
971 0 : else if (rv > 0) // shutdown
972 : {
973 0 : return rv;
974 : }
975 :
976 0 : if(m_stopMeasurement || derived().m_shutdown)
977 : {
978 0 : break;
979 : }
980 :
981 : //************** NEGATIVE POKE **********************/
982 :
983 0 : rv = basicTimedPoke(-1);
984 :
985 0 : if(rv < 0)
986 : {
987 0 : derivedT::template log<software_error>({__FILE__, __LINE__});
988 0 : return rv;
989 : }
990 0 : else if (rv > 0) // shutdown
991 : {
992 0 : return rv;
993 : }
994 :
995 0 : if(m_stopMeasurement || derived().m_shutdown)
996 : {
997 0 : break;
998 : }
999 : }
1000 :
1001 : try
1002 : {
1003 0 : m_pokeImage = m_pokeLocal/(2.0*m_nPokeImages*m_nPokeAverage);
1004 : }
1005 0 : catch(const std::exception& e)
1006 : {
1007 0 : return derivedT::template log<software_error,-1>({__FILE__, __LINE__, e.what()});
1008 : }
1009 :
1010 :
1011 :
1012 0 : m_dmImage.setZero();
1013 0 : m_dmStream = m_dmImage;
1014 :
1015 0 : return 0;
1016 : }
1017 :
1018 : template<class derivedT>
1019 0 : int dmPokeWFS<derivedT>::updateMeasurement( float deltaX,
1020 : float deltaY
1021 : )
1022 : {
1023 0 : m_deltaX = deltaX;
1024 0 : m_deltaY = deltaY;
1025 0 : m_indiP_measurement["delta_x"] = deltaX;
1026 0 : m_indiP_measurement["delta_y"] = deltaY;
1027 :
1028 0 : return 0;
1029 : }
1030 :
1031 : template<class derivedT>
1032 0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_nPokeImages )(const pcf::IndiProperty &ipRecv)
1033 : {
1034 0 : INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_nPokeImages, ipRecv)
1035 :
1036 : float target;
1037 :
1038 0 : if( derived().template indiTargetUpdate(m_indiP_nPokeImages, target, ipRecv, false) < 0)
1039 : {
1040 0 : return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1041 : }
1042 :
1043 0 : m_nPokeImages = target;
1044 :
1045 0 : return 0;
1046 : }
1047 :
1048 : template<class derivedT>
1049 0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_nPokeAverage )(const pcf::IndiProperty &ipRecv)
1050 : {
1051 0 : INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_nPokeAverage, ipRecv)
1052 :
1053 : float target;
1054 :
1055 0 : if( derived().template indiTargetUpdate(m_indiP_nPokeAverage, target, ipRecv, false) < 0)
1056 : {
1057 0 : return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1058 : }
1059 :
1060 0 : m_nPokeAverage = target;
1061 :
1062 0 : return 0;
1063 : }
1064 :
1065 : template<class derivedT>
1066 0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_poke_amp )(const pcf::IndiProperty &ipRecv)
1067 : {
1068 0 : INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_poke_amp, ipRecv)
1069 :
1070 : float target;
1071 :
1072 0 : if( derived().template indiTargetUpdate(m_indiP_poke_amp, target, ipRecv, false) < 0)
1073 : {
1074 0 : return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1075 : }
1076 :
1077 0 : m_poke_amp = target;
1078 :
1079 0 : return 0;
1080 : }
1081 :
1082 : template<class derivedT>
1083 0 : INDI_SETCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_wfsFps )(const pcf::IndiProperty &ipRecv)
1084 : {
1085 0 : INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_wfsFps, ipRecv)
1086 :
1087 0 : if( ipRecv.find("current") != true ) //this isn't valid
1088 : {
1089 0 : return 0;
1090 : }
1091 :
1092 0 : m_wfsFps = ipRecv["current"].get<float>();
1093 :
1094 0 : return 0;
1095 : }
1096 :
1097 : template<class derivedT>
1098 0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_single )(const pcf::IndiProperty &ipRecv)
1099 : {
1100 0 : INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_single, ipRecv)
1101 :
1102 0 : if( ipRecv.find("toggle") != true ) //this isn't valid
1103 : {
1104 0 : return -1;
1105 : }
1106 :
1107 0 : if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
1108 : {
1109 0 : if(m_measuring == 0)
1110 : {
1111 0 : m_continuous = 0;
1112 0 : m_single = 1;
1113 0 : if(sem_post(&m_wfsSemaphore) < 0)
1114 : {
1115 0 : return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
1116 : }
1117 : }
1118 : }
1119 :
1120 0 : return 0;
1121 : }
1122 :
1123 : template<class derivedT>
1124 0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_continuous )(const pcf::IndiProperty &ipRecv)
1125 : {
1126 0 : INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_continuous, ipRecv)
1127 :
1128 0 : if( ipRecv.find("toggle") != true ) //this isn't valid
1129 : {
1130 0 : return -1;
1131 : }
1132 :
1133 0 : if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
1134 : {
1135 0 : if(m_measuring == 0)
1136 : {
1137 0 : m_continuous = 1;
1138 0 : m_single = 0;
1139 0 : if(sem_post(&m_wfsSemaphore) < 0)
1140 : {
1141 0 : return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
1142 : }
1143 : }
1144 : }
1145 0 : else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
1146 : {
1147 0 : if(m_measuring != 0)
1148 : {
1149 0 : m_stopMeasurement = true;
1150 : }
1151 : }
1152 :
1153 0 : return 0;
1154 : }
1155 :
1156 : template<class derivedT>
1157 0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_stop )(const pcf::IndiProperty &ipRecv)
1158 : {
1159 0 : INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_stop, ipRecv)
1160 :
1161 0 : if( ipRecv.find("request") != true ) //this isn't valid
1162 : {
1163 0 : return -1;
1164 : }
1165 :
1166 0 : if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
1167 : {
1168 0 : if(m_measuring != 0)
1169 : {
1170 0 : m_stopMeasurement = true;
1171 : }
1172 : }
1173 :
1174 0 : return 0;
1175 : }
1176 :
1177 : template<class derivedT>
1178 0 : int dmPokeWFS<derivedT>::recordTelem(const telem_pokeloop *)
1179 : {
1180 0 : return recordPokeLoop(true);
1181 : }
1182 :
1183 : template<class derivedT>
1184 0 : int dmPokeWFS<derivedT>::recordPokeLoop(bool force)
1185 : {
1186 : static int measuring = -1;
1187 : static float deltaX = std::numeric_limits<float>::max();
1188 : static float deltaY = std::numeric_limits<float>::max();
1189 : static uint64_t counter = std::numeric_limits<uint64_t>::max();
1190 :
1191 0 : if(force || (m_counter != counter) || (m_deltaX != deltaX) || (m_deltaY != deltaY) || (m_measuring != measuring))
1192 : {
1193 0 : uint8_t meas = m_measuring;
1194 0 : derived().template telem<telem_pokeloop>({meas, m_deltaX, m_deltaY, m_counter});
1195 :
1196 0 : measuring = m_measuring;
1197 0 : deltaX = m_deltaX;
1198 0 : deltaY = m_deltaY;
1199 0 : counter = m_counter;
1200 : }
1201 :
1202 0 : return 0;
1203 : }
1204 :
1205 : /// Call dmPokeWFS::setupConfig with error checking
1206 : /**
1207 : * \param cfig the application configurator
1208 : */
1209 : #define DMPOKEWFS_SETUP_CONFIG( cfig ) \
1210 : if(dmPokeWFST::setupConfig(cfig) < 0) \
1211 : { \
1212 : log<software_error>({__FILE__, __LINE__, "Error from dmPokeWFST::setupConfig"}); \
1213 : m_shutdown = true; \
1214 : return; \
1215 : }
1216 :
1217 : /// Call dmPokeWFS::loadConfig with error checking
1218 : /** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
1219 : * \param cfig the application configurator
1220 : */
1221 : #define DMPOKEWFS_LOAD_CONFIG( cfig ) \
1222 : if(dmPokeWFST::loadConfig(cfig) < 0) \
1223 : { \
1224 : return log<software_error,-1>({__FILE__, __LINE__, "Error from dmPokeWFST::loadConfig"}); \
1225 : }
1226 :
1227 : /// Call dmPokeWFS::appStartup with error checking
1228 : #define DMPOKEWFS_APP_STARTUP \
1229 : if( dmPokeWFST::appStartup() < 0) \
1230 : { \
1231 : return log<software_error, -1>({__FILE__,__LINE__}); \
1232 : }
1233 :
1234 : /// Call dmPokeWFS::appLogic with error checking
1235 : #define DMPOKEWFS_APP_LOGIC \
1236 : if( dmPokeWFST::appLogic() < 0) \
1237 : { \
1238 : return log<software_error, -1>({__FILE__,__LINE__}); \
1239 : }
1240 :
1241 : /// Call dmPokeWFS::appShutdown with error checking
1242 : #define DMPOKEWFS_APP_SHUTDOWN \
1243 : if(dmPokeWFST::appShutdown() < 0) \
1244 : { \
1245 : log<software_error>({__FILE__, __LINE__, "error from dmPokeWFST::appShutdown"}); \
1246 : }
1247 :
1248 : } //namespace dev
1249 : } //namespace app
1250 : } //namespace MagAOX
1251 :
1252 : #endif //dmPokeWFS_hpp
|