API
edtCamera.hpp
Go to the documentation of this file.
1 /** \file edtCamera.hpp
2  * \brief EDT framegrabber interface
3  *
4  * \author Jared R. Males (jaredmales@gmail.com)
5  *
6  * \ingroup app_files
7  */
8 
9 #ifndef edtCamera_hpp
10 #define edtCamera_hpp
11 
12 #ifndef MAGAOX_NOEDT
13 
14 #include <edtinc.h>
15 
16 #include "../../common/paths.hpp"
17 
18 #include "ioDevice.hpp"
19 
20 #include "stdCamera.hpp"
21 
22 namespace MagAOX
23 {
24 namespace app
25 {
26 namespace dev
27 {
28 
29 
30 
31 
32 /// MagAO-X EDT framegrabber interface
33 /** Implements an interface to the EDT PDV SDK
34  *
35  * The derived class `derivedT` must be a MagAOXApp<true>, and must declare this class a friend like so:
36  * \code
37  * friend class dev::dssShutter<derivedT>;
38  * \endcode
39  *
40  * A static configuration variable must be defined in derivedT as
41  * \code
42  * static constexpr bool c_edtCamera_relativeConfigPath = true; //or: false
43  * \endcode
44  * which determines whether or not the EDT config path is relative to the MagAO-X config path (true) or absolute (false).
45  *
46  * In addition, `derivedT` should be `dev::frameGrabber` or equivalent, with an `m_reconfig` member, and calls
47  * to this class's `pdvStartAcquisition`, `pdvAcquire`, and `pdvReconfig` in the relevant `dev::frameGrabber`
48  * implementation functions.
49  *
50  * Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic`, `appShutdown`
51  * `onPowerOff`, and `whilePowerOff`, must be placed in the derived class's functions of the same name.
52  *
53  * \ingroup appdev
54  */
55 template<class derivedT>
56 class edtCamera : public ioDevice
57 {
58 
59 public:
60 
61 
62 protected:
63 
64  /** \name Configurable Parameters
65  * @{
66  */
67  //Framegrabber:
68  int m_unit {0}; ///< EDT PDV board unit number
69  int m_channel {0}; ///< EDT PDV board channel number
70  int m_numBuffs {4}; ///< EDT PDV DMA buffer size, indicating number of images.
71 
72  //cameraConfigMap derived().m_cameraModes; ///< Map holding the possible camera mode configurations
73 
74  //std::string derived().m_startupMode; ///< The camera mode to load during first init after a power-on.
75 
76  ///@}
77 
78  PdvDev * m_pdv {nullptr}; ///< The EDT PDV device handle
79 
80  u_char * m_image_p {nullptr}; ///< The image data grabbed
81 
82  //std::string m_modeName; ///< The current mode name
83 
84  //std::string derived().m_nextMode; ///< The mode to be set by the next reconfiguration
85 
86  int m_raw_height {0}; ///< The height of the frame, according to the framegrabber
87  int m_raw_width {0}; ///< The width of the frame, according to the framegrabber
88  int m_raw_depth {0}; ///< The bit-depth of the frame, according to the framegrabber
89  std::string m_cameraType; ///< The camera type according to the framegrabber
90 
91 public:
92 
93  ///C'tor, sets up stdCamera
95 
96  ///Destructor, destroys the PdvDev structure
97  ~edtCamera() noexcept;
98 
99  /// Send a serial command over cameralink and retrieve the response
100  int pdvSerialWriteRead( std::string & response, ///< [out] the response to the command from the device
101  const std::string & command ///< [in] the command to send to the device
102  );
103 
104  /// Configure the EDT framegrabber
105  int pdvConfig( std::string & cfgname /**< [in] The configuration name for the mode to set */);
106 
107  /// Setup the configuration system
108  /**
109  * This should be called in `derivedT::setupConfig` as
110  * \code
111  edtCamera<derivedT>::setupConfig(config);
112  \endcode
113  * with appropriate error checking.
114  */
115  void setupConfig(mx::app::appConfigurator & config /**< [out] the derived classes configurator*/);
116 
117  /// load the configuration system results
118  /**
119  * This should be called in `derivedT::loadConfig` as
120  * \code
121  edtCamera<derivedT>::loadConfig(config);
122  \endcode
123  * with appropriate error checking.
124  */
125  void loadConfig(mx::app::appConfigurator & config /**< [in] the derived classes configurator*/);
126 
127  /// Startup function
128  /**
129  * This should be called in `derivedT::appStartup` as
130  * \code
131  edtCamera<derivedT>::appStartup();
132  \endcode
133  * with appropriate error checking.
134  *
135  * \returns 0 on success
136  * \returns -1 on error, which is logged.
137  */
138  int appStartup();
139 
140  /// Application logic
141  /** Checks the edtCamera thread
142  *
143  * This should be called from the derived's appLogic() as in
144  * \code
145  edtCamera<derivedT>::appLogic();
146  \endcode
147  * with appropriate error checking.
148  *
149  * \returns 0 on success
150  * \returns -1 on error, which is logged.
151  */
152  int appLogic();
153 
154  /// Actions on power off
155  /**
156  * This should be called from the derived's onPowerOff() as in
157  * \code
158  edtCamera<derivedT>::onPowerOff();
159  \endcode
160  * with appropriate error checking.
161  *
162  * \returns 0 on success
163  * \returns -1 on error, which is logged.
164  */
165  int onPowerOff();
166 
167  /// Actions while powered off
168  /**
169  * This should be called from the derived's whilePowerOff() as in
170  * \code
171  edtCamera<derivedT>::whilePowerOff();
172  \endcode
173  * with appropriate error checking.
174  *
175  * \returns 0 on success
176  * \returns -1 on error, which is logged.
177  */
179 
180  /// Application the shutdown
181  /** Shuts down the edtCamera thread
182  *
183  * \code
184  edtCamera<derivedT>::appShutdown();
185  \endcode
186  * with appropriate error checking.
187  *
188  * \returns 0 on success
189  * \returns -1 on error, which is logged.
190  */
191  int appShutdown();
192 
193 
195 
196  int pdvAcquire( timespec & currImageTimestamp );
197 
198  int pdvReconfig();
199 
200 protected:
201 
202 
203  /** \name INDI
204  *
205  *@{
206  */
207 protected:
208  //declare our properties
209 
210 public:
211 
212  /// The static callback function to be registered for the channel properties.
213  /**
214  * \returns 0 on success.
215  * \returns -1 on error.
216  */
217  // static int st_newCallBack_mode( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
218  // const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
219  // );
220 
221  /// The callback called by the static version, to actually process the new request.
222  /**
223  * \returns 0 on success.
224  * \returns -1 on error.
225  */
226  //int newCallBack_mode( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
227 
228  /// Update the INDI properties for this device controller
229  /** You should call this once per main loop.
230  * It is not called automatically.
231  *
232  * \returns 0 on success.
233  * \returns -1 on error.
234  */
235  int updateINDI();
236 
237  ///@}
238 
239 private:
240  derivedT & derived()
241  {
242  return *static_cast<derivedT *>(this);
243  }
244 };
245 
246 template<class derivedT>
248 {
249 }
250 
251 
252 template<class derivedT>
254 {
255  if(m_pdv) pdv_close(m_pdv);
256 
257  return;
258 }
259 
260 
261 #define MAGAOX_PDV_SERBUFSIZE 512
262 
263 template<class derivedT>
264 int edtCamera<derivedT>::pdvSerialWriteRead( std::string & response,
265  const std::string & command
266  )
267 {
268  char buf[MAGAOX_PDV_SERBUFSIZE+1];
269 
270  // Flush the channel first.
271  // This does not indicate errors, so no checks possible.
272  pdv_serial_read(m_pdv, buf, MAGAOX_PDV_SERBUFSIZE);
273 
274  if( pdv_serial_command(m_pdv, command.c_str()) < 0)
275  {
276  derivedT::template log<software_error>({__FILE__, __LINE__, "PDV: error sending serial command"});
277  return -1;
278  }
279 
280  int ret;
281 
282  ret = pdv_serial_wait(m_pdv, m_readTimeout, 1);
283 
284  if(ret == 0)
285  {
286  if(derived().powerState() != 1 || derived().powerStateTarget() != 1) return -1;
287 
288  derivedT::template log<software_error>({__FILE__, __LINE__, "PDV: timeout, no serial response"});
289  return -1;
290  }
291 
292  u_char lastbyte, waitc;
293 
294  response.clear();
295 
296  do
297  {
298  ret = pdv_serial_read(m_pdv, buf, MAGAOX_PDV_SERBUFSIZE);
299 
300  if(ret > 0) response += buf;
301 
302  //Check for last char, wait for more otherwise.
303  if (*buf) lastbyte = (u_char)buf[strnlen(buf, sizeof(buf))-1];
304 
305  if (pdv_get_waitchar(m_pdv, &waitc) && (lastbyte == waitc))
306  break;
307  else ret = pdv_serial_wait(m_pdv, m_readTimeout/2, 1);
308  }
309  while(ret > 0);
310 
311  if(ret == 0 && pdv_get_waitchar(m_pdv, &waitc))
312  {
313  derivedT::template log<software_error>({__FILE__, __LINE__, "PDV: timeout in serial response"});
314  return -1;
315  }
316 
317  return 0;
318 }
319 
320 template<class derivedT>
321 int edtCamera<derivedT>::pdvConfig(std::string & modeName)
322 {
323  Dependent *dd_p;
324  EdtDev *edt_p = NULL;
325  Edtinfo edtinfo;
326 
327  //Preliminaries
328  if(m_pdv)
329  {
330  pdv_close(m_pdv);
331  m_pdv = nullptr;
332  }
333 
334  derived().m_modeName = modeName;
335 
336  if(modeName == "")
337  {
338  return derivedT::template log<text_log, -1>("Empty modeName passed to pdvConfig", logPrio::LOG_ERROR);
339  }
340 
341  if(derived().m_cameraModes.count(modeName) != 1)
342  {
343  return derivedT::template log<text_log, -1>("No mode named " + modeName + " found.", logPrio::LOG_ERROR);
344  }
345 
346  //Construct config file, adding relative path if configured that way
347  std::string configFile;
348 
349  if(derivedT::c_edtCamera_relativeConfigPath)
350  {
351  configFile = derived().configDir() + "/";
352  }
353 
354  configFile += derived().m_cameraModes[modeName].m_configFile;
355 
356 
357  derivedT::template log<text_log>("Loading EDT PDV config file: " + configFile);
358 
359  if ((dd_p = pdv_alloc_dependent()) == NULL)
360  {
361  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "EDT PDV alloc_dependent FAILED"});
362  }
363 
364  if (pdv_readcfg(configFile.c_str(), dd_p, &edtinfo) != 0)
365  {
366  free(dd_p);
367  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "EDT PDV readcfg FAILED"});
368 
369  }
370 
371  char edt_devname[128];
372  strncpy(edt_devname, EDT_INTERFACE, sizeof(edt_devname));
373 
374  if ((edt_p = edt_open_channel(edt_devname, m_unit, m_channel)) == NULL)
375  {
376  char errstr[256];
377  edt_perror(errstr);
378  free(dd_p);
379  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, std::string("EDT PDV edt_open_channel FAILED: ") + errstr});
380  }
381 
382  char bitdir[1];
383  bitdir[0] = '\0';
384 
385  int pdv_debug = 0;
386 
387  if(derived().m_log.logLevel() > logPrio::LOG_INFO) pdv_debug = 2;
388 
389  if (pdv_initcam(edt_p, dd_p, m_unit, &edtinfo, configFile.c_str(), bitdir, pdv_debug) != 0)
390  {
391  edt_close(edt_p);
392  free(dd_p);
393  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "initcam failed. Run with '--logLevel=DBG' to see complete debugging output."});
394  }
395 
396  edt_close(edt_p);
397  free(dd_p);
398 
399  //Now open the PDV device handle for talking to the camera via the EDT board.
400  if ((m_pdv = pdv_open_channel(edt_devname, m_unit, m_channel)) == NULL)
401  {
402  std::string errstr = std::string("pdv_open_channel(") + edt_devname + std::to_string(m_unit) + "_" + std::to_string(m_channel) + ")";
403 
404  derivedT::template log<software_error>({__FILE__, __LINE__, errstr});
405  derivedT::template log<software_error>({__FILE__, __LINE__, errno});
406 
407  return -1;
408  }
409 
410  pdv_flush_fifo(m_pdv);
411 
412  pdv_serial_read_enable(m_pdv); //This is undocumented, don't know if it's really needed.
413 
414  m_raw_width = pdv_get_width(m_pdv);
415  m_raw_height = pdv_get_height(m_pdv);
416  m_raw_depth = pdv_get_depth(m_pdv);
417  m_cameraType = pdv_get_cameratype(m_pdv);
418 
419  derivedT::template log<text_log>("Initialized framegrabber: " + m_cameraType);
420  derivedT::template log<text_log>("WxHxD: " + std::to_string(m_raw_width) + " X " + std::to_string(m_raw_height) + " X " + std::to_string(m_raw_depth));
421 
422  /*
423  * allocate four buffers for optimal pdv ring buffer pipeline (reduce if
424  * memory is at a premium)
425  */
426  pdv_multibuf(m_pdv, m_numBuffs);
427  derivedT::template log<text_log>("allocated " + std::to_string(m_numBuffs) + " buffers");
428 
429 
430 
431  return 0;
432 
433 }
434 
435 template<class derivedT>
436 void edtCamera<derivedT>::setupConfig(mx::app::appConfigurator & config)
437 {
438  config.add("framegrabber.pdv_unit", "", "framegrabber.pdv_unit", argType::Required, "framegrabber", "pdv_unit", false, "int", "The EDT PDV framegrabber unit number. Default is 0.");
439  config.add("framegrabber.pdv_channel", "", "framegrabber.pdv_channel", argType::Required, "framegrabber", "pdv_channel", false, "int", "The EDT PDV framegrabber channel number. Default is 0.");
440  config.add("framegrabber.numBuffs", "", "framegrabber.numBuffs", argType::Required, "framegrabber", "numBuffs", false, "int", "The EDT PDV framegrabber DMA buffer size [images]. Default is 4.");
441 
443 }
444 
445 template<class derivedT>
446 void edtCamera<derivedT>::loadConfig(mx::app::appConfigurator & config)
447 {
448  config(m_unit, "framegrabber.pdv_unit");
449  config(m_channel, "framegrabber.pdv_channel");
450  config(m_numBuffs, "framegrabber.numBuffs");
451 
452  m_readTimeout = 1000;
453  m_writeTimeout = 1000;
455 
456 }
457 
458 
459 template<class derivedT>
461 {
462  if(pdvConfig(derived().m_startupMode) < 0)
463  {
464  derivedT::template log<software_error>({__FILE__, __LINE__});
465  return -1;
466  }
467 
468  return 0;
469 
470 }
471 
472 template<class derivedT>
474 {
475  return 0;
476 
477 }
478 
479 template<class derivedT>
481 {
482  return 0;
483 }
484 
485 template<class derivedT>
487 {
488  return 0;
489 }
490 
491 template<class derivedT>
493 {
494  return 0;
495 }
496 
497 template<class derivedT>
499 {
500  pdv_start_images(m_pdv, m_numBuffs);
501 
502  return 0;
503 }
504 
505 template<class derivedT>
506 int edtCamera<derivedT>::pdvAcquire( timespec & currImageTimestamp )
507 {
508 
509  uint dmaTimeStamp[2];
510  m_image_p = pdv_wait_last_image_timed(m_pdv, dmaTimeStamp);
511  //m_image_p = pdv_wait_image_timed(m_pdv, dmaTimeStamp);
512  pdv_start_image(m_pdv);
513 
514  currImageTimestamp.tv_sec = dmaTimeStamp[0];
515  currImageTimestamp.tv_nsec = dmaTimeStamp[1];
516 
517 
518  return 0;
519 }
520 
521 template<class derivedT>
523 {
524 
525  if(pdvConfig(derived().m_nextMode) < 0)
526  {
527  derivedT::template log<text_log>("error trying to re-configure with " + derived().m_nextMode, logPrio::LOG_ERROR);
528  sleep(1);
529  }
530  else
531  {
532  derived().m_nextMode = "";
533  }
534 
535  return 0;
536 }
537 
538 
539 
540 template<class derivedT>
542 {
543  return 0;
544 }
545 
546 
547 } //namespace dev
548 } //namespace app
549 } //namespace MagAOX
550 
551 #endif //MAGAOX_NOEDT
552 #endif //edtCamera_hpp
MagAO-X EDT framegrabber interface.
Definition: edtCamera.hpp:57
int m_channel
EDT PDV board channel number.
Definition: edtCamera.hpp:69
int m_raw_depth
The bit-depth of the frame, according to the framegrabber.
Definition: edtCamera.hpp:88
int appLogic()
Application logic.
Definition: edtCamera.hpp:473
u_char * m_image_p
The image data grabbed.
Definition: edtCamera.hpp:80
int m_raw_height
The height of the frame, according to the framegrabber.
Definition: edtCamera.hpp:86
PdvDev * m_pdv
The EDT PDV device handle.
Definition: edtCamera.hpp:78
int pdvConfig(std::string &cfgname)
Configure the EDT framegrabber.
Definition: edtCamera.hpp:321
int pdvAcquire(timespec &currImageTimestamp)
Definition: edtCamera.hpp:506
int pdvSerialWriteRead(std::string &response, const std::string &command)
Send a serial command over cameralink and retrieve the response.
Definition: edtCamera.hpp:264
int m_unit
EDT PDV board unit number.
Definition: edtCamera.hpp:68
std::string m_cameraType
The camera type according to the framegrabber.
Definition: edtCamera.hpp:89
~edtCamera() noexcept
Destructor, destroys the PdvDev structure.
Definition: edtCamera.hpp:253
edtCamera()
C'tor, sets up stdCamera.
Definition: edtCamera.hpp:247
int appStartup()
Startup function.
Definition: edtCamera.hpp:460
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
Definition: edtCamera.hpp:436
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
Definition: edtCamera.hpp:446
int onPowerOff()
Actions on power off.
Definition: edtCamera.hpp:480
int updateINDI()
The static callback function to be registered for the channel properties.
Definition: edtCamera.hpp:541
int m_raw_width
The width of the frame, according to the framegrabber.
Definition: edtCamera.hpp:87
int m_numBuffs
EDT PDV DMA buffer size, indicating number of images.
Definition: edtCamera.hpp:70
int appShutdown()
Application the shutdown.
Definition: edtCamera.hpp:492
int whilePowerOff()
Actions while powered off.
Definition: edtCamera.hpp:486
#define MAGAOX_PDV_SERBUFSIZE
Definition: edtCamera.hpp:261
Configuration and control of an input and output device.
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_INFO
Informational. The info log level is the lowest level recorded during normal operations.
Definition: logPriority.hpp:49
Standard camera interface.
An input/output capable device.
Definition: ioDevice.hpp:27
int loadConfig(mx::app::appConfigurator &config)
Load the device section from an application configurator.
Definition: ioDevice.cpp:28
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the device section.
Definition: ioDevice.cpp:20
Software ERR log entry.
A simple text log, a string-type log.
Definition: text_log.hpp:24