API
 
Loading...
Searching...
No Matches
dmMode.hpp
Go to the documentation of this file.
1/** \file dmMode.hpp
2 * \brief The MagAO-X DM mode command header file
3 *
4 * \ingroup dmMode_files
5 */
6
7#ifndef dmMode_hpp
8#define dmMode_hpp
9
10#include <mx/improc/eigenCube.hpp>
11#include <mx/ioutils/fits/fitsFile.hpp>
12#include <mx/improc/eigenImage.hpp>
13#include <mx/ioutils/stringUtils.hpp>
14#include <mx/sys/timeUtils.hpp>
15
16#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
17#include "../../magaox_git_version.h"
18
19/** \defgroup dmMode
20 * \brief The DM mode command app, places modes on a DM channel
21 * \todo update md doc
22 * \todo the current_amps/target_amps thing is dumb. Should consider mode00.target, mode00.current maybe.
23 *
24 * <a href="../handbook/operating/software/apps/dmMode.html">Application Documentation</a>
25 *
26 * \ingroup apps
27 *
28 */
29
30/** \defgroup dmMode_files
31 * \ingroup dmMode
32 */
33
34
35namespace MagAOX
36{
37namespace app
38{
39
40/// The MagAO-X DM mode commander
41/**
42 * \ingroup dmMode
43 */
44class dmMode : public MagAOXApp<true>, public dev::telemeter<dmMode>
45{
46
47 typedef float realT;
48
50
51 friend class dev::telemeter<dmMode>;
52 friend class dmMode_test;
53
54protected:
55
56 /** \name Configurable Parameters
57 *@{
58 */
59
60 std::string m_modeCube;
61
62 int m_maxModes {50};
63
64 std::string m_dmName;
65
66 std::string m_dmChannelName;
67
68 ///@}
69
70 mx::improc::eigenCube<realT> m_modes;
71
72 std::vector<realT> m_amps;
73
74 mx::improc::eigenImage<realT> m_shape;
75
77 uint32_t m_width {0}; ///< The width of the image
78 uint32_t m_height {0}; ///< The height of the image.
79
80 uint8_t m_dataType{0}; ///< The ImageStreamIO type code.
81 size_t m_typeSize {0}; ///< The size of the type, in bytes.
82
83 bool m_opened {true};
84 bool m_restart {false};
85
86public:
87 /// Default c'tor.
88 dmMode();
89
90 /// D'tor, declared and defined for noexcept.
93
94 virtual void setupConfig();
95
96 /// Implementation of loadConfig logic, separated for testing.
97 /** This is called by loadConfig().
98 */
99 int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
100
101 virtual void loadConfig();
102
103 /// Startup function
104 /**
105 *
106 */
107 virtual int appStartup();
108
109 /// Implementation of the FSM for dmMode.
110 /**
111 * \returns 0 on no critical error
112 * \returns -1 on an error requiring shutdown
113 */
114 virtual int appLogic();
115
116 /// Shutdown the app.
117 /**
118 *
119 */
120 virtual int appShutdown();
121
122
123 int sendCommand();
124
125 //INDI:
126protected:
127 //declare our properties
128 pcf::IndiProperty m_indiP_dm;
129 pcf::IndiProperty m_indiP_currAmps;
130 pcf::IndiProperty m_indiP_tgtAmps;
131
132 std::vector<std::string> m_elNames;
133public:
136
137 /** \name Telemeter Interface
138 *
139 * @{
140 */
141 int checkRecordTimes();
142
143 int recordTelem( const telem_dmmodes * );
144
145 int recordDmModes( bool force = false );
146 ///@}
147
148
149};
150
151dmMode::dmMode() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
152{
153
154 return;
155}
156
158{
159 config.add("dm.modeCube", "", "dm.modeCube", argType::Required, "dm", "modeCube", false, "string", "Full path to the FITS file containing the modes for this DM.");
160 config.add("dm.maxModes", "", "dm.maxModes", argType::Required, "dm", "maxModes", false, "int", "The maximum number of modes to use (truncates the cube). If <=0 all modes in cube are used.");
161 config.add("dm.name", "", "dm.name", argType::Required, "dm", "name", false, "string", "The descriptive name of this dm. Default is the channel name.");
162 config.add("dm.channelName", "", "dm.channelName", argType::Required, "dm", "channelName", false, "string", "The name of the DM channel to write to.");
163 config.add("dm.maxModes", "", "dm.maxModes", argType::Required, "dm", "maxModes", false, "int", "The maximum number of modes to use (truncates the cube).");
164
166}
167
168int dmMode::loadConfigImpl( mx::app::appConfigurator & _config )
169{
170
171 _config(m_modeCube, "dm.modeCube");
172 _config(m_maxModes, "dm.maxModes");
173 _config(m_dmChannelName, "dm.channelName");
174
176 _config(m_dmName, "dm.name");
177
179 {
180 log<text_log>("Error during telemeter config", logPrio::LOG_CRITICAL);
181 m_shutdown = true;
182 }
183
184 return 0;
185}
186
188{
189 loadConfigImpl(config);
190}
191
193{
194 mx::fits::fitsFile<realT> ff;
195
196 mx::error_t errc = ff.read(m_modes, m_modeCube);
197 if(errc != mx::error_t::noerror)
198 {
199 return log<text_log,-1>(std::format("Could not open mode cube file: {} "
200 "({})", mx::errorMessage(errc), mx::errorName(errc)), logPrio::LOG_ERROR);
201 }
202
203 if(m_maxModes > 0 && m_maxModes < m_modes.planes())
204 {
205 mx::improc::eigenCube<realT> modes;
206 //This probably just works as a realloc in eigenCube but I haven't looked.
207 modes.resize(m_modes.rows(), m_modes.cols(), m_maxModes);
208 for(int p =0; p < modes.planes(); ++p) modes.image(p) = m_modes.image(p);
209 m_modes.resize(m_modes.rows(), m_modes.cols(), m_maxModes);
210 for(int p =0; p < modes.planes(); ++p) m_modes.image(p) = modes.image(p);
211 }
212
213
214
215 m_amps.resize(m_modes.planes(), 0);
216 m_shape.resize(m_modes.rows(), m_modes.cols());
217
218 REG_INDI_NEWPROP_NOCB(m_indiP_dm, "dm", pcf::IndiProperty::Text);
219 m_indiP_dm.add(pcf::IndiElement("name"));
220 m_indiP_dm["name"] = m_dmName;
221 m_indiP_dm.add(pcf::IndiElement("channel"));
222 m_indiP_dm["channel"] = m_dmChannelName;
223
224 REG_INDI_NEWPROP(m_indiP_currAmps, "current_amps", pcf::IndiProperty::Number);
225 REG_INDI_NEWPROP(m_indiP_tgtAmps, "target_amps", pcf::IndiProperty::Number);
226
227 m_elNames.resize(m_amps.size());
228
229 for(size_t n=0; n < m_amps.size(); ++n)
230 {
231 //std::string el = std::to_string(n);
232 m_elNames[n] = mx::ioutils::convertToString<size_t, 4, '0'>(n);
233
234 m_indiP_currAmps.add( pcf::IndiElement(m_elNames[n]) );
236
237 m_indiP_tgtAmps.add( pcf::IndiElement(m_elNames[n]) );
238 }
239
240 if(telemeterT::appStartup() < 0)
241 {
242 return log<software_error,-1>({__FILE__,__LINE__});
243 }
244
246
247
248
249 return 0;
250}
251
253{
255 {
256 m_opened = false;
257 m_restart = false; //Set this up front, since we're about to restart.
258
260 {
261 if(m_imageStream.md[0].sem < 10) ///<\todo this is hardcoded in ImageStreamIO.c -- should be a define
262 {
264 }
265 else
266 {
267 m_opened = true;
268 }
269 }
270
271 if(m_opened)
272 {
274 }
275 }
276
278 {
279 m_dataType = m_imageStream.md[0].datatype;
281 m_width = m_imageStream.md[0].size[0];
282 m_height = m_imageStream.md[0].size[1];
283
284
286 {
287 return log<text_log,-1>("Data type of DM channel is not float.", logPrio::LOG_CRITICAL);
288 }
289
290 if(m_typeSize != sizeof(realT))
291 {
292 return log<text_log,-1>("Type-size mismatch, realT is not float.", logPrio::LOG_CRITICAL);
293 }
294
295 if(m_width != m_modes.rows())
296 {
297 return log<text_log,-1>("Size mismatch between DM and modes (rows)", logPrio::LOG_CRITICAL);
298 }
299
300 if(m_height != m_modes.cols())
301 {
302 return log<text_log,-1>("Size mismatch between DM and modes (cols)", logPrio::LOG_CRITICAL);
303 }
304
305 for(size_t n=0; n < m_amps.size(); ++n) m_amps[n] = 0;
306 sendCommand();
307
309 }
310
311 if(state() == stateCodes::READY)
312 {
313 if(telemeterT::appLogic() < 0)
314 {
316 return 0;
317 }
318 }
319
320 return 0;
321}
322
324{
326
327 return 0;
328}
329
331{
332 if(!m_opened)
333 {
334 log<text_log>("not connected to DM channel.", logPrio::LOG_WARNING);
335 return 0;
336 }
337
338 m_shape = m_amps[0]*m_modes.image(0);
339
340 for(size_t n = 1; n<m_amps.size(); ++n)
341 {
342 m_shape += m_amps[n]*m_modes.image(n);
343 }
344
345 if(m_imageStream.md[0].write)
346 {
347 while(m_imageStream.md[0].write) mx::sys::microSleep(10);
348 }
349
350 recordDmModes(true);
351 m_imageStream.md[0].write = 1;
352
354 if(m_imageStream.md[0].size[2] > 0) ///\todo change to naxis?
355 {
356 curr_image = m_imageStream.md[0].cnt1;
357 }
358 else curr_image = 0;
359
360 char* next_dest = (char *) m_imageStream.array.raw + curr_image*m_width*m_height*m_typeSize;
361
363
364 m_imageStream.md[0].cnt0++;
365
366 m_imageStream.md->write=0;
368
369 recordDmModes(true);
370
371 for(size_t n = 0; n<m_amps.size(); ++n)
372 {
374 }
375 m_indiP_currAmps.setState (pcf::IndiProperty::Ok);
376 m_indiDriver->sendSetProperty (m_indiP_currAmps);
377
378 return 0;
379
380}
381
382INDI_NEWCALLBACK_DEFN(dmMode, m_indiP_currAmps)(const pcf::IndiProperty &ipRecv)
383{
384 if (ipRecv.getName() == m_indiP_currAmps.getName())
385 {
386 size_t found = 0;
387 for(size_t n=0; n < m_amps.size(); ++n)
388 {
389 if(ipRecv.find(m_elNames[n]))
390 {
391 realT amp = ipRecv[m_elNames[n]].get<realT>();
392
393 ///\todo add bounds checks here
394
395 m_amps[n] = amp;
396 ++found;
397 }
398 }
399
400 if(found)
401 {
402 return sendCommand();
403 }
404
405 return 0;
406
407 }
408
409 return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
410}
411
412INDI_NEWCALLBACK_DEFN(dmMode, m_indiP_tgtAmps)(const pcf::IndiProperty &ipRecv)
413{
414 if (ipRecv.getName() == m_indiP_tgtAmps.getName())
415 {
416 size_t found = 0;
417 for(size_t n=0; n < m_amps.size(); ++n)
418 {
419 if(ipRecv.find(m_elNames[n]))
420 {
421 realT amp = ipRecv[m_elNames[n]].get<realT>();
422
423 ///\todo add bounds checks here
424
425 m_amps[n] = amp;
426 ++found;
427 }
428 }
429
430 if(found)
431 {
432 return sendCommand();
433 }
434
435 return 0;
436
437 }
438
439 return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
440}
441
446
448{
449 return recordDmModes(true);
450}
451
452int dmMode::recordDmModes( bool force )
453{
454 static std::vector<float> lastamps(m_amps.size(), std::numeric_limits<float>::max());
455
456 bool changed = false;
457 for(size_t p=0; p < m_amps.size(); ++p)
458 {
459 if(m_amps[p] != lastamps[p]) changed = true;
460 }
461
462 if( changed || force )
463 {
464 for(size_t p=0; p < m_amps.size(); ++p)
465 {
466 lastamps[p] = m_amps[p];
467 }
468
470 }
471
472 return 0;
473}
474
475} //namespace app
476} //namespace MagAOX
477
478#endif //dmMode_hpp
The base-class for XWCTk applications.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
The MagAO-X DM mode commander.
Definition dmMode.hpp:45
std::string m_dmChannelName
Definition dmMode.hpp:66
uint32_t m_width
The width of the image.
Definition dmMode.hpp:77
mx::improc::eigenCube< realT > m_modes
Definition dmMode.hpp:70
virtual int appShutdown()
Shutdown the app.
Definition dmMode.hpp:323
int recordDmModes(bool force=false)
Definition dmMode.hpp:452
std::string m_modeCube
Definition dmMode.hpp:60
std::string m_dmName
Definition dmMode.hpp:64
INDI_NEWCALLBACK_DECL(dmMode, m_indiP_currAmps)
pcf::IndiProperty m_indiP_currAmps
Definition dmMode.hpp:129
dev::telemeter< dmMode > telemeterT
Definition dmMode.hpp:49
friend class dmMode_test
Definition dmMode.hpp:52
mx::improc::eigenImage< realT > m_shape
Definition dmMode.hpp:74
size_t m_typeSize
The size of the type, in bytes.
Definition dmMode.hpp:81
pcf::IndiProperty m_indiP_dm
Definition dmMode.hpp:128
std::vector< realT > m_amps
Definition dmMode.hpp:72
~dmMode() noexcept
D'tor, declared and defined for noexcept.
Definition dmMode.hpp:91
uint32_t m_height
The height of the image.
Definition dmMode.hpp:78
INDI_NEWCALLBACK_DECL(dmMode, m_indiP_tgtAmps)
virtual int appLogic()
Implementation of the FSM for dmMode.
Definition dmMode.hpp:252
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition dmMode.hpp:168
uint8_t m_dataType
The ImageStreamIO type code.
Definition dmMode.hpp:80
virtual int appStartup()
Startup function.
Definition dmMode.hpp:192
virtual void setupConfig()
Definition dmMode.hpp:157
virtual void loadConfig()
Definition dmMode.hpp:187
int recordTelem(const telem_dmmodes *)
Definition dmMode.hpp:447
std::vector< std::string > m_elNames
Definition dmMode.hpp:132
dmMode()
Default c'tor.
Definition dmMode.hpp:151
pcf::IndiProperty m_indiP_tgtAmps
Definition dmMode.hpp:130
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
const pcf::IndiProperty & ipRecv
Definition dm.hpp:19
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
A device base class which saves telemetry.
Definition telemeter.hpp:75
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int appLogic()
Perform telemeter application logic.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
int appStartup()
Starts the telemetry log thread.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
Software ERR log entry.
The type of the input message.
Log entry recording DM Mode Amplitudes.
A simple text log, a string-type log.
Definition text_log.hpp:24