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