14#include "../../../tests/testXWC.hpp"
28#define protected public
29#include "../cred2Ctrl.hpp"
43 int commandResult{ 0 };
44 int initialWaitResult{ 1 };
54 lastStartNumBuffs = -1;
62 serialResponses.clear();
63 serialCommands.clear();
64 activeSerialResponse.clear();
65 activeSerialReadPending =
false;
66 activeSerialTransaction =
false;
67 activeSerialWaitResult = 0;
68 activeSerialWaitServed =
false;
73 lastRequestedBaud = -1;
76 int startImagesCalls{ 0 };
77 int lastStartNumBuffs{ -1 };
78 int startImageCalls{ 0 };
79 uint waitTimeSec{ 0 };
80 uint waitTimeNsec{ 0 };
81 std::array<u_char, 64> waitImage{};
82 int readcfgReturn{ 0 };
83 int readcfgCalls{ 0 };
84 int multibufCalls{ 0 };
85 std::deque<serialResponse> serialResponses;
86 std::vector<std::string> serialCommands;
87 std::string activeSerialResponse;
88 bool activeSerialReadPending{
false };
89 bool activeSerialTransaction{
false };
90 int activeSerialWaitResult{ 0 };
91 bool activeSerialWaitServed{
false };
92 int baudSetResult{ 0 };
93 int baudCurrent{ 115200 };
94 int baudSetCalls{ 0 };
95 int baudGetCalls{ 0 };
96 int lastRequestedBaud{ -1 };
100edtStubState g_edtStubState;
103[[maybe_unused]]
void resetStubState()
105 g_edtStubState.reset();
110queueSerialResponse(
const std::string &response,
int commandResult = 0,
int initialWaitResult = 1 )
112 g_edtStubState.serialResponses.push_back( { response, commandResult, initialWaitResult } );
127 static_cast<void>( configFile );
128 static_cast<void>( dd_p );
129 static_cast<void>( edtinfo );
130 ++g_edtStubState.readcfgCalls;
131 return g_edtStubState.readcfgReturn;
137 static_cast<void>( deviceName );
138 static_cast<void>( unit );
139 static_cast<void>( channel );
145 if( errstr !=
nullptr )
155 const char *configFile,
159 static_cast<void>( edt_p );
160 static_cast<void>( dd_p );
161 static_cast<void>( unit );
162 static_cast<void>( edtinfo );
163 static_cast<void>( configFile );
164 static_cast<void>( bitdir );
165 static_cast<void>( pdv_debug );
171 static_cast<void>( edt_p );
177 static_cast<void>( deviceName );
178 static_cast<void>( unit );
179 static_cast<void>( channel );
185 static_cast<void>( pdv_p );
190 static_cast<void>( pdv_p );
195 static_cast<void>( pdv_p );
200 static_cast<void>( pdv_p );
206 static_cast<void>( pdv_p );
212 static_cast<void>( pdv_p );
218 static char cameraType[] =
"stub_pdv";
219 static_cast<void>( pdv_p );
225 static_cast<void>( pdv_p );
226 static_cast<void>( numBuffs );
227 ++g_edtStubState.multibufCalls;
232 static_cast<void>( pdv_p );
233 ++g_edtStubState.startImagesCalls;
234 g_edtStubState.lastStartNumBuffs = numBuffs;
239 static_cast<void>( pdv_p );
240 dmaTimeStamp[0] = g_edtStubState.waitTimeSec;
241 dmaTimeStamp[1] = g_edtStubState.waitTimeNsec;
242 return g_edtStubState.waitImage.data();
247 static_cast<void>( pdv_p );
248 ++g_edtStubState.startImageCalls;
253 static_cast<void>( pdv_p );
254 if( buf !=
nullptr && size > 0 )
259 if( !g_edtStubState.activeSerialReadPending || buf ==
nullptr || size <= 0 )
264 size_t copyCount = g_edtStubState.activeSerialResponse.size();
265 if( copyCount >
static_cast<size_t>( size - 1 ) )
267 copyCount =
static_cast<size_t>( size - 1 );
270 std::copy_n( g_edtStubState.activeSerialResponse.data(), copyCount, buf );
271 buf[copyCount] =
'\0';
273 g_edtStubState.activeSerialReadPending =
false;
275 return static_cast<int>( copyCount );
280 static_cast<void>( pdv_p );
281 g_edtStubState.serialCommands.emplace_back( command ==
nullptr ?
"" : command );
283 g_edtStubState.activeSerialResponse.clear();
284 g_edtStubState.activeSerialReadPending =
false;
285 g_edtStubState.activeSerialTransaction =
false;
286 g_edtStubState.activeSerialWaitResult = 0;
287 g_edtStubState.activeSerialWaitServed =
false;
289 if( g_edtStubState.serialResponses.empty() )
294 serialResponse response = g_edtStubState.serialResponses.front();
295 g_edtStubState.serialResponses.pop_front();
297 if( response.commandResult < 0 )
299 return response.commandResult;
302 g_edtStubState.activeSerialResponse = response.response;
303 g_edtStubState.activeSerialReadPending =
true;
304 g_edtStubState.activeSerialTransaction =
true;
305 g_edtStubState.activeSerialWaitResult = response.initialWaitResult;
307 return response.commandResult;
312 static_cast<void>( pdv_p );
313 static_cast<void>( timeout );
314 static_cast<void>( count );
316 if( !g_edtStubState.activeSerialTransaction )
321 if( !g_edtStubState.activeSerialWaitServed )
323 g_edtStubState.activeSerialWaitServed =
true;
324 return g_edtStubState.activeSerialWaitResult;
332 static_cast<void>( pdv_p );
333 if( waitc !=
nullptr )
342 static_cast<void>( pdv_p );
343 ++g_edtStubState.baudSetCalls;
344 g_edtStubState.lastRequestedBaud = baud;
345 if( g_edtStubState.baudSetResult == 0 )
347 g_edtStubState.baudCurrent = baud;
350 return g_edtStubState.baudSetResult;
355 static_cast<void>( pdv_p );
356 ++g_edtStubState.baudGetCalls;
357 return g_edtStubState.baudCurrent;
368namespace cred2CtrlTest
375std::string uniqueShmimName(
const std::string &suffix )
377 static unsigned counter = 0;
381 return "cred2Ctrl_test_" + suffix +
"_" + std::to_string( ::getpid() ) +
"_" + std::to_string( counter );
385std::string uniqueConfigPath(
const std::string &suffix )
387 return "/tmp/cred2Ctrl_test_" + suffix +
"_" + std::to_string( ::getpid() ) +
".conf";
391void removeIfPresent(
const std::string &path )
395 static_cast<void>( ::unlink( path.c_str() ) );
406 m_configName = uniqueShmimName(
"config" );
407 m_shmimName = uniqueShmimName(
"main" );
411 ~cred2Ctrl_test() noexcept
413 removeIfPresent( m_configFile );
418[[maybe_unused]]
void setPoweredOn( cred2Ctrl_test &app )
420 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 1;
421 app.m_powerTargetState = 1;
425[[maybe_unused]]
void loadAppConfig( cred2Ctrl_test &app,
426 const std::string &suffix,
427 const std::vector<std::string> §ions,
428 const std::vector<std::string> &keywords,
429 const std::vector<std::string> &values )
431 const std::string configPath = uniqueConfigPath( suffix );
434 mx::app::writeConfigFile( configPath, sections, keywords, values );
435 app.config.readConfig( configPath );
437 removeIfPresent( configPath );
439 app.m_tel.logPath(
"/tmp" );
440 app.m_tel.logName( app.m_configName );
441 app.m_tel.logExt(
"bintel" );
445[[maybe_unused]]
void loadDefaultConfig( cred2Ctrl_test &app,
const std::string &suffix )
447 loadAppConfig( app, suffix, {
"framegrabber" }, {
"shmimName" }, { uniqueShmimName(
"stream" ) } );
451[[maybe_unused]]
void startFgThread( cred2Ctrl_test &app,
int sleepMs = 200 )
454 std::thread( [sleepMs]() { std::this_thread::sleep_for( std::chrono::milliseconds( sleepMs ) ); } );
458[[maybe_unused]]
void joinFgThread( cred2Ctrl_test &app )
460 if( app.m_fgThread.joinable() )
462 app.m_fgThread.join();
469 cred2Ctrl_test &m_app;
472 explicit fgThreadScope( cred2Ctrl_test &app,
int sleepMs = 200 ) : m_app( app )
474 startFgThread( m_app, sleepMs );
480 joinFgThread( m_app );
487 cred2Ctrl_test &m_app;
491 explicit startupScope( cred2Ctrl_test &app ) : m_app( app ), m_started( false )
496 void markStarted(
bool started )
506 m_app.m_shutdown = 1;
507 m_app.m_tel.logShutdown(
true );
508 static_cast<void>( m_app.appShutdown() );
514[[maybe_unused]]
bool hasRecordedTime(
const timespec &ts )
516 return ts.tv_sec != 0 || ts.tv_nsec != 0;
521#ifndef CRED2CTRL_TEST_SUPPORT_ONLY
527TEST_CASE(
"cred2Ctrl configuration loading writes a runtime EDT config",
"[cred2Ctrl]" )
532 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
539 SECTION(
"loadConfig uses the default serial baud and seeds the runtime camera mode" )
543 loadAppConfig( app,
"defaults", {
"unused" }, {
"value" }, {
"0" } );
545 REQUIRE( app.m_serialBaud == 115200 );
546 REQUIRE( app.m_startupMode ==
"runtime" );
547 REQUIRE( app.m_cameraModes.count(
"runtime" ) == 1 );
548 REQUIRE( app.m_cameraModes[
"runtime"].m_configFile == app.m_configFile );
550 std::ifstream configFile( app.m_configFile );
551 REQUIRE( configFile.good() );
553 const std::string contents( ( std::istreambuf_iterator<char>( configFile ) ),
554 std::istreambuf_iterator<char>() );
555 REQUIRE( contents.find(
"camera_model: \"C-RED 2\"" ) != std::string::npos );
556 REQUIRE( contents.find(
"width: 640" ) != std::string::npos );
557 REQUIRE( contents.find(
"height: 512" ) != std::string::npos );
558 REQUIRE( contents.find(
"serial_baud: 115200" ) != std::string::npos );
561 SECTION(
"loadConfig applies configured serial-baud overrides to the runtime config file" )
567 {
"camera",
"framegrabber" },
568 {
"serialBaud",
"shmimName" },
569 {
"57600", uniqueShmimName(
"override" ) } );
571 REQUIRE( app.m_serialBaud == 57600 );
572 REQUIRE( app.m_configFile.find(
"cred2_" ) != std::string::npos );
573 REQUIRE( app.m_cameraModes[
"runtime"].m_configFile == app.m_configFile );
575 std::ifstream configFile( app.m_configFile );
576 REQUIRE( configFile.good() );
578 const std::string contents( ( std::istreambuf_iterator<char>( configFile ) ),
579 std::istreambuf_iterator<char>() );
580 REQUIRE( contents.find(
"serial_baud: 57600" ) != std::string::npos );
583 SECTION(
"loadConfig marks the app for shutdown when the runtime EDT config cannot be written" )
586 const std::string configPath = uniqueConfigPath(
"load_failure" );
588 app.m_configName =
"missing/cred2_write_failure";
590 mx::app::writeConfigFile(
591 configPath, {
"framegrabber" }, {
"shmimName" }, { uniqueShmimName(
"load_failure_stream" ) } );
592 app.config.readConfig( configPath );
594 removeIfPresent( configPath );
596 REQUIRE( app.m_shutdown ==
true );
599 SECTION(
"heap deletion and direct EDT stub calls cover the remaining test harness wrappers" )
601 char shortError[2] = {
'x',
'\0' };
602 char shortRead[4] = {
'\0',
'\0',
'\0',
'\0' };
605 REQUIRE( shortError[0] ==
'\0' );
607 queueSerialResponse(
"abcdef\n" );
610 REQUIRE(
pdv_serial_read(
nullptr, shortRead,
sizeof( shortRead ) ) == 3 );
611 REQUIRE( std::string( shortRead ) ==
"abc" );
613 cred2Ctrl_test *heapApp =
new cred2Ctrl_test;
614 REQUIRE( heapApp !=
nullptr );
618 REQUIRE( baseHeapApp !=
nullptr );
622 REQUIRE( plainHeapApp !=
nullptr );
631TEST_CASE(
"cred2Ctrl helper mappings normalize responses and preset names",
"[cred2Ctrl]" )
633 bool enabled =
false;
634 float parsedValue = 0;
637 int fanRangeFirst = 0;
638 int fanRangeSecond = 0;
639 std::string gainName;
640 std::string commandGain;
649 std::vector<float> parsedValues;
651 REQUIRE( cred2LowerResponse(
" Medium\r\nfli-cli>" ) ==
"medium" );
653 REQUIRE( cred2FanPresetName( 0.0f ) ==
"off" );
654 REQUIRE( cred2FanPresetName( 25.0f ) ==
"p25" );
655 REQUIRE( cred2FanPresetName( 50.0f ) ==
"p50" );
656 REQUIRE( cred2FanPresetName( 75.0f ) ==
"p75" );
657 REQUIRE( cred2FanPresetName( 100.0f ) ==
"p100" );
659 REQUIRE( cred2FanPresetPercent( fanPercent,
"off" ) == 0 );
660 REQUIRE( fanPercent == 0 );
661 REQUIRE( cred2FanPresetPercent( fanPercent,
"p25" ) == 0 );
662 REQUIRE( fanPercent == 25 );
663 REQUIRE( cred2FanPresetPercent( fanPercent,
"p50" ) == 0 );
664 REQUIRE( fanPercent == 50 );
665 REQUIRE( cred2FanPresetPercent( fanPercent,
"p75" ) == 0 );
666 REQUIRE( fanPercent == 75 );
667 REQUIRE( cred2FanPresetPercent( fanPercent,
"p100" ) == 0 );
668 REQUIRE( fanPercent == 100 );
669 REQUIRE( cred2FanPresetPercent( fanPercent,
"mystery" ) == -1 );
671 REQUIRE( cred2AnalogGainName( gainName,
"medium" ) == 0 );
672 REQUIRE( gainName ==
"med" );
673 REQUIRE( cred2AnalogGainName( gainName,
"med" ) == 0 );
674 REQUIRE( gainName ==
"med" );
675 REQUIRE( cred2AnalogGainName( gainName,
"high" ) == 0 );
676 REQUIRE( gainName ==
"high" );
677 REQUIRE( cred2AnalogGainName( gainName,
"low" ) == 0 );
678 REQUIRE( gainName ==
"low" );
679 REQUIRE( cred2AnalogGainName( gainName,
"unknown" ) == -1 );
681 REQUIRE( cred2AnalogGainCommand( commandGain,
"low" ) == 0 );
682 REQUIRE( commandGain ==
"low" );
683 REQUIRE( cred2AnalogGainCommand( commandGain,
"med" ) == 0 );
684 REQUIRE( commandGain ==
"medium" );
685 REQUIRE( cred2AnalogGainCommand( commandGain,
"high" ) == 0 );
686 REQUIRE( commandGain ==
"high" );
687 REQUIRE( cred2AnalogGainCommand( commandGain,
"invalid" ) == -1 );
690 REQUIRE( parsedValue == Approx( 12.5f ) );
695 REQUIRE( parsedValues == std::vector<float>{ 1.0f, 2.0f, 3.0f } );
698 REQUIRE( parsedValues.empty() );
700 REQUIRE(
cred2ParseCropState( enabled, startColumn, endColumn, startRow, endRow,
"off" ) == 0 );
701 REQUIRE( enabled ==
false );
702 REQUIRE( startColumn == 0 );
703 REQUIRE( endColumn == 0 );
704 REQUIRE( startRow == 0 );
705 REQUIRE( endRow == 0 );
706 REQUIRE(
cred2ParseCropState( enabled, startColumn, endColumn, startRow, endRow,
"" ) == -1 );
707 REQUIRE(
cred2ParseCropState( enabled, startColumn, endColumn, startRow, endRow,
"maybe" ) == -1 );
708 REQUIRE(
cred2ParseCropState( enabled, startColumn, endColumn, startRow, endRow,
"on:1-2" ) == -1 );
709 REQUIRE(
cred2ParseCropState( enabled, startColumn, endColumn, startRow, endRow,
"on:bad:1-2" ) == -1 );
711 REQUIRE(
cred2ParseRange( fanRangeFirst, fanRangeSecond,
"1-4" ) == 0 );
712 REQUIRE( fanRangeFirst == 1 );
713 REQUIRE( fanRangeSecond == 4 );
714 REQUIRE(
cred2ParseRange( fanRangeFirst, fanRangeSecond,
"1" ) == -1 );
719 REQUIRE(
cred2RoiToCenter( centerX, centerY, width, height, roi, 640, 512 ) == 0 );
720 REQUIRE( centerX == Approx( 319.5f ) );
721 REQUIRE( centerY == Approx( 255.5f ) );
722 REQUIRE( width == 640 );
723 REQUIRE( height == 512 );
727 REQUIRE(
cred2RoiToCenter( centerX, centerY, width, height, roi, 640, 512 ) == -1 );
734TEST_CASE(
"cred2Ctrl command helpers clean responses and validate acknowledgements",
"[cred2Ctrl]" )
737 std::string response;
742 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
748 SECTION(
"sendCommand strips prompts and returns the cleaned response" )
751 queueSerialResponse(
"400\r\nfli-cli>\n" );
753 REQUIRE( app.sendCommand( response,
"fps raw" ) == 0 );
754 REQUIRE( response ==
"400" );
755 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"fps raw" } );
758 SECTION(
"sendCommand returns -1 without logging when the camera is powered off" )
760 queueSerialResponse(
"", -1 );
762 REQUIRE( app.sendCommand( response,
"fps raw" ) == -1 );
763 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"fps raw" } );
766 SECTION(
"issueCommand accepts missing responses when allowNoResponse is true" )
769 queueSerialResponse(
"", -1 );
771 REQUIRE( app.issueCommand(
"set fps 100",
true ) == 0 );
772 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set fps 100" } );
775 SECTION(
"issueCommand rejects explicit error responses" )
778 queueSerialResponse(
"Error: busy\n" );
780 REQUIRE( app.issueCommand(
"set fps 100",
false ) == -1 );
781 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set fps 100" } );
784 SECTION(
"issueCommand accepts normal acknowledgement responses" )
787 queueSerialResponse(
"OK\n" );
789 REQUIRE( app.issueCommand(
"set fps 100",
false ) == 0 );
790 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set fps 100" } );
798TEST_CASE(
"cred2Ctrl syncROIFromCamera tracks camera cropping state",
"[cred2Ctrl]" )
803 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
808 SECTION(
"full-frame cropping disables camera-side crop tracking" )
813 app.m_raw_width = app.m_full_w;
814 app.m_raw_height = app.m_full_h;
815 queueSerialResponse(
"off\n" );
817 REQUIRE( app.syncROIFromCamera() == 0 );
818 REQUIRE( app.m_cameraCropEnabled ==
false );
819 REQUIRE( app.m_currentROI.x == Approx( app.m_full_x ) );
820 REQUIRE( app.m_currentROI.y == Approx( app.m_full_y ) );
821 REQUIRE( app.m_currentROI.w == app.m_full_w );
822 REQUIRE( app.m_currentROI.h == app.m_full_h );
825 SECTION(
"explicit crop coordinates update the cached ROI" )
830 app.m_raw_width = 256;
831 app.m_raw_height = 256;
832 queueSerialResponse(
"on:192-447:128-383\n" );
834 REQUIRE( app.syncROIFromCamera() == 0 );
835 REQUIRE( app.m_cameraCropEnabled ==
true );
836 REQUIRE( app.m_currentROI.x == Approx( 319.5f ) );
837 REQUIRE( app.m_currentROI.y == Approx( 255.5f ) );
838 REQUIRE( app.m_currentROI.w == 256 );
839 REQUIRE( app.m_currentROI.h == 256 );
840 REQUIRE( app.m_width == 256 );
841 REQUIRE( app.m_height == 256 );
844 SECTION(
"querying separate row and column limits handles cameras that omit them from the summary response" )
849 app.m_raw_width = 256;
850 app.m_raw_height = 256;
851 queueSerialResponse(
"on\n" );
852 queueSerialResponse(
"192-447\n" );
853 queueSerialResponse(
"128-383\n" );
855 REQUIRE( app.syncROIFromCamera() == 0 );
856 REQUIRE( app.m_cameraCropEnabled ==
true );
857 REQUIRE( app.m_currentROI.x == Approx( 319.5f ) );
858 REQUIRE( app.m_currentROI.y == Approx( 255.5f ) );
859 REQUIRE( app.m_currentROI.w == 256 );
860 REQUIRE( app.m_currentROI.h == 256 );
861 REQUIRE( g_edtStubState.serialCommands ==
862 std::vector<std::string>{
"cropping raw",
"cropping columns raw",
"cropping rows raw" } );
865 SECTION(
"invalid crop responses return an error" )
870 queueSerialResponse(
"garbage\n" );
872 REQUIRE( app.syncROIFromCamera() == -1 );
875 SECTION(
"mismatched EDT dimensions trigger a reconfiguration and config rewrite" )
879 loadDefaultConfig( app,
"roi_reconfig" );
881 app.m_modeName =
"runtime";
882 app.m_raw_width = 320;
883 app.m_raw_height = 256;
884 queueSerialResponse(
"off\n" );
886 REQUIRE( app.syncROIFromCamera() == 0 );
887 REQUIRE( g_edtStubState.readcfgCalls > 0 );
888 REQUIRE( app.m_modeName ==
"runtime" );
891 SECTION(
"missing crop-column details are reported as an error" )
896 app.m_raw_width = 256;
897 app.m_raw_height = 256;
898 queueSerialResponse(
"on\n" );
899 queueSerialResponse(
"bad\n" );
901 REQUIRE( app.syncROIFromCamera() == -1 );
904 SECTION(
"missing crop-row details are reported as an error" )
909 app.m_raw_width = 256;
910 app.m_raw_height = 256;
911 queueSerialResponse(
"on\n" );
912 queueSerialResponse(
"192-447\n" );
913 queueSerialResponse(
"bad\n" );
915 REQUIRE( app.syncROIFromCamera() == -1 );
918 SECTION(
"camera-reported crop corners that cannot map to a valid ROI are rejected" )
923 app.m_raw_width = 256;
924 app.m_raw_height = 256;
925 queueSerialResponse(
"on:447-192:128-383\n" );
927 REQUIRE( app.syncROIFromCamera() == -1 );
930 SECTION(
"reconfiguration failures bubble out when the EDT runtime reload fails" )
934 loadDefaultConfig( app,
"roi_reconfig_fail" );
936 app.m_modeName =
"runtime";
937 app.m_raw_width = 320;
938 app.m_raw_height = 256;
939 g_edtStubState.readcfgReturn = -1;
940 queueSerialResponse(
"off\n" );
942 REQUIRE( app.syncROIFromCamera() == -1 );
945 SECTION(
"runtime config rewrite failures bubble out while syncing a resized ROI" )
949 loadDefaultConfig( app,
"roi_write_fail" );
951 app.m_modeName =
"runtime";
952 app.m_configFile =
"/tmp/cred2Ctrl_missing_dir/roi_runtime.cfg";
953 app.m_raw_width = 320;
954 app.m_raw_height = 256;
955 queueSerialResponse(
"off\n" );
957 REQUIRE( app.syncROIFromCamera() == -1 );
965TEST_CASE(
"cred2Ctrl getter helpers parse temperatures fps and limits",
"[cred2Ctrl]" )
970 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
977 SECTION(
"getTemps caches valid bundled temperatures and setpoint state" )
982 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
983 queueSerialResponse(
"-15.00\n" );
985 REQUIRE( app.getTemps() == 0 );
986 REQUIRE( app.m_temps.motherboard == Approx( 40.50f ) );
987 REQUIRE( app.m_temps.frontend == Approx( 37.00f ) );
988 REQUIRE( app.m_temps.powerboard == Approx( 40.25f ) );
989 REQUIRE( app.m_temps.snake == Approx( -14.92f ) );
990 REQUIRE( app.m_temps.setpoint == Approx( -15.0f ) );
991 REQUIRE( app.m_temps.peltier == Approx( 2.29f ) );
992 REQUIRE( app.m_temps.heatsink == Approx( 27.50f ) );
993 REQUIRE( app.m_tempControlStatus ==
true );
994 REQUIRE( app.m_tempControlStatusStr ==
"ON TARGET" );
995 REQUIRE( app.m_tempControlOnTarget ==
true );
998 SECTION(
"getTemps keeps the previous cache on malformed bundled temperatures" )
1002 setPoweredOn( app );
1003 app.m_temps.motherboard = 1;
1004 app.m_temps.frontend = 2;
1005 app.m_temps.powerboard = 3;
1006 app.m_temps.snake = 4;
1007 app.m_temps.setpoint = 5;
1008 app.m_temps.peltier = 6;
1009 app.m_temps.heatsink = 7;
1011 app.m_ccdTempSetpt = 5;
1013 queueSerialResponse(
"40.50:37.00\n" );
1015 REQUIRE( app.getTemps() == 0 );
1016 REQUIRE( app.m_temps.motherboard == Approx( 1.0f ) );
1017 REQUIRE( app.m_ccdTemp == Approx( 4.0f ) );
1018 REQUIRE( app.m_ccdTempSetpt == Approx( 5.0f ) );
1021 SECTION(
"getTemps keeps the previous cache when the snake-setpoint query transport fails" )
1025 setPoweredOn( app );
1026 app.m_ccdTempSetpt = -5.0f;
1027 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
1028 queueSerialResponse(
"", -1 );
1030 REQUIRE( app.getTemps() == 0 );
1031 REQUIRE( app.m_ccdTempSetpt == Approx( -5.0f ) );
1034 SECTION(
"getTemps keeps the previous cache when the snake-setpoint response is malformed" )
1038 setPoweredOn( app );
1039 app.m_ccdTempSetpt = -6.0f;
1040 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
1041 queueSerialResponse(
"bad-setpoint\n" );
1043 REQUIRE( app.getTemps() == 0 );
1044 REQUIRE( app.m_ccdTempSetpt == Approx( -6.0f ) );
1047 SECTION(
"getTemps reports off-target cooling when the detector is still far from the setpoint" )
1051 setPoweredOn( app );
1052 queueSerialResponse(
"40.50:37.00:40.25:-10.00:2.29:27.50\n" );
1053 queueSerialResponse(
"-15.00\n" );
1055 REQUIRE( app.getTemps() == 0 );
1056 REQUIRE( app.m_tempControlStatus ==
true );
1057 REQUIRE( app.m_tempControlStatusStr ==
"OFF TARGET" );
1058 REQUIRE( app.m_tempControlOnTarget ==
false );
1061 SECTION(
"getTemps reports TEMP OFF once the warm setpoint is reached" )
1065 setPoweredOn( app );
1066 queueSerialResponse(
"40.50:37.00:40.25:20.00:2.29:27.50\n" );
1067 queueSerialResponse(
"20.00\n" );
1069 REQUIRE( app.getTemps() == 0 );
1070 REQUIRE( app.m_tempControlStatus ==
false );
1071 REQUIRE( app.m_tempControlStatusStr ==
"TEMP OFF" );
1074 SECTION(
"getTemps reports WARMING while returning to the warm setpoint" )
1078 setPoweredOn( app );
1079 queueSerialResponse(
"40.50:37.00:40.25:10.00:2.29:27.50\n" );
1080 queueSerialResponse(
"20.00\n" );
1082 REQUIRE( app.getTemps() == 0 );
1083 REQUIRE( app.m_tempControlStatus ==
false );
1084 REQUIRE( app.m_tempControlStatusStr ==
"WARMING" );
1087 SECTION(
"getFPS updates the cached frame rate on valid responses" )
1091 setPoweredOn( app );
1092 queueSerialResponse(
"150.5\n" );
1094 REQUIRE( app.getFPS() == 0 );
1095 REQUIRE( app.m_fps == Approx( 150.5f ) );
1098 SECTION(
"getFPS rejects malformed responses" )
1102 setPoweredOn( app );
1103 queueSerialResponse(
"Result: OK\n" );
1105 REQUIRE( app.getFPS() == -1 );
1108 SECTION(
"updateFPSLimits updates bounds and clamps the requested fps" )
1112 setPoweredOn( app );
1113 app.m_fpsSet = 1000.0f;
1114 queueSerialResponse(
"10.0\n" );
1115 queueSerialResponse(
"500.0\n" );
1117 REQUIRE( app.updateFPSLimits() == 0 );
1118 REQUIRE( app.m_minFPS == Approx( 10.0f ) );
1119 REQUIRE( app.m_maxFPS == Approx( 500.0f ) );
1120 REQUIRE( app.m_fpsSet == Approx( 500.0f ) );
1123 SECTION(
"updateFPSLimits reports malformed minimum-fps responses" )
1127 setPoweredOn( app );
1128 queueSerialResponse(
"not-a-number\n" );
1130 REQUIRE( app.updateFPSLimits() == -1 );
1133 SECTION(
"updateFPSLimits reports malformed maximum-fps responses" )
1137 setPoweredOn( app );
1138 queueSerialResponse(
"10.0\n" );
1139 queueSerialResponse(
"not-a-number\n" );
1141 REQUIRE( app.updateFPSLimits() == -1 );
1149TEST_CASE(
"cred2Ctrl discrete getter helpers cover fallback parsing paths",
"[cred2Ctrl]" )
1154 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
1161 SECTION(
"getFanSpeed accepts automatic mode without querying the manual percentage" )
1165 setPoweredOn( app );
1166 queueSerialResponse(
"automatic\n" );
1168 REQUIRE( app.getFanSpeed() == 0 );
1169 REQUIRE( app.m_fanSpeedName ==
"auto" );
1170 REQUIRE( app.m_fanSpeedNameSet ==
"auto" );
1171 REQUIRE( app.m_fanSpeedValid ==
true );
1174 SECTION(
"getFanSpeed falls back to the non-raw fan-speed response when needed" )
1178 setPoweredOn( app );
1179 queueSerialResponse(
"manual\n" );
1180 queueSerialResponse(
"speed\n" );
1181 queueSerialResponse(
"75\n" );
1183 REQUIRE( app.getFanSpeed() == 0 );
1184 REQUIRE( app.m_fanSpeedName ==
"p75" );
1185 REQUIRE( g_edtStubState.serialCommands ==
1186 std::vector<std::string>{
"fan mode raw",
"fan speed raw",
"fan speed" } );
1189 SECTION(
"getFanSpeed falls back to the non-raw mode command when the raw response is not recognizable" )
1193 setPoweredOn( app );
1194 queueSerialResponse(
"mystery\n" );
1195 queueSerialResponse(
"manual\n" );
1196 queueSerialResponse(
"50\n" );
1198 REQUIRE( app.getFanSpeed() == 0 );
1199 REQUIRE( app.m_fanSpeedName ==
"p50" );
1200 REQUIRE( g_edtStubState.serialCommands ==
1201 std::vector<std::string>{
"fan mode raw",
"fan mode",
"fan speed raw" } );
1204 SECTION(
"getFanSpeed rejects mode responses that never indicate auto or manual" )
1208 setPoweredOn( app );
1209 queueSerialResponse(
"mystery\n" );
1210 queueSerialResponse(
"still mystery\n" );
1212 REQUIRE( app.getFanSpeed() == -1 );
1215 SECTION(
"getFanSpeed returns -1 when the raw mode query transport fails" )
1219 setPoweredOn( app );
1220 queueSerialResponse(
"", -1 );
1222 REQUIRE( app.getFanSpeed() == -1 );
1225 SECTION(
"getFanSpeed returns -1 when the fallback mode query transport fails" )
1229 setPoweredOn( app );
1230 queueSerialResponse(
"mystery\n" );
1231 queueSerialResponse(
"", -1 );
1233 REQUIRE( app.getFanSpeed() == -1 );
1236 SECTION(
"getFanSpeed rejects manual fan-speed responses that remain unparseable" )
1240 setPoweredOn( app );
1241 queueSerialResponse(
"manual\n" );
1242 queueSerialResponse(
"speed\n" );
1243 queueSerialResponse(
"still bad\n" );
1245 REQUIRE( app.getFanSpeed() == -1 );
1248 SECTION(
"getFanSpeed returns -1 when the manual fan-speed transport fails" )
1252 setPoweredOn( app );
1253 queueSerialResponse(
"manual\n" );
1254 queueSerialResponse(
"", -1 );
1256 REQUIRE( app.getFanSpeed() == -1 );
1259 SECTION(
"getAnalogGain maps the reported sensibility name" )
1263 setPoweredOn( app );
1264 queueSerialResponse(
"medium\n" );
1266 REQUIRE( app.getAnalogGain() == 0 );
1267 REQUIRE( app.m_analogGainName ==
"med" );
1268 REQUIRE( app.m_analogGainNameSet ==
"med" );
1269 REQUIRE( app.m_analogGainValid ==
true );
1272 SECTION(
"getAnalogGain rejects unsupported sensibility responses" )
1276 setPoweredOn( app );
1277 queueSerialResponse(
"mystery\n" );
1279 REQUIRE( app.getAnalogGain() == -1 );
1282 SECTION(
"getAnalogGain returns -1 on transport failure" )
1286 setPoweredOn( app );
1287 queueSerialResponse(
"", -1 );
1289 REQUIRE( app.getAnalogGain() == -1 );
1292 SECTION(
"getLEDState falls back from the raw command to the human-readable response" )
1296 setPoweredOn( app );
1297 queueSerialResponse(
"", -1 );
1298 queueSerialResponse(
"on\n" );
1300 REQUIRE( app.getLEDState() == 0 );
1301 REQUIRE( app.m_ledState ==
true );
1302 REQUIRE( app.m_ledStateSet ==
true );
1303 REQUIRE( app.m_ledStateValid ==
true );
1304 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"led raw",
"led" } );
1307 SECTION(
"getLEDState rejects responses that do not indicate on or off" )
1311 setPoweredOn( app );
1312 queueSerialResponse(
"unknown\n" );
1313 queueSerialResponse(
"still unknown\n" );
1315 REQUIRE( app.getLEDState() == -1 );
1318 SECTION(
"getLEDState returns -1 when both raw and fallback LED queries fail" )
1322 setPoweredOn( app );
1323 queueSerialResponse(
"", -1 );
1324 queueSerialResponse(
"", -1 );
1326 REQUIRE( app.getLEDState() == -1 );
1329 SECTION(
"getLEDState recognizes descriptive raw responses that still contain off" )
1333 setPoweredOn( app );
1334 queueSerialResponse(
"led is off\n" );
1336 REQUIRE( app.getLEDState() == 0 );
1337 REQUIRE( app.m_ledState ==
false );
1340 SECTION(
"getLEDState recognizes descriptive raw responses that still contain on" )
1344 setPoweredOn( app );
1345 queueSerialResponse(
"led is on\n" );
1347 REQUIRE( app.getLEDState() == 0 );
1348 REQUIRE( app.m_ledState ==
true );
1351 SECTION(
"getLEDState falls back to an off state on the human-readable retry" )
1355 setPoweredOn( app );
1356 queueSerialResponse(
"unknown\n" );
1357 queueSerialResponse(
"off\n" );
1359 REQUIRE( app.getLEDState() == 0 );
1360 REQUIRE( app.m_ledState ==
false );
1363 SECTION(
"getLEDState returns -1 when the retry transport fails after an ambiguous raw response" )
1367 setPoweredOn( app );
1368 queueSerialResponse(
"unknown\n" );
1369 queueSerialResponse(
"", -1 );
1371 REQUIRE( app.getLEDState() == -1 );
1374 SECTION(
"getLEDState falls back to an on state on the human-readable retry" )
1378 setPoweredOn( app );
1379 queueSerialResponse(
"unknown\n" );
1380 queueSerialResponse(
"on\n" );
1382 REQUIRE( app.getLEDState() == 0 );
1383 REQUIRE( app.m_ledState ==
true );
1391TEST_CASE(
"cred2Ctrl setter helpers validate bounds and update state",
"[cred2Ctrl]" )
1396 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
1410 SECTION(
"powerOnDefaults restores controller-side defaults" )
1414 app.m_tempControlStatusSet =
true;
1415 app.m_tempControlStatus =
true;
1416 app.m_tempControlStatusStr =
"ON TARGET";
1417 app.m_cameraCropEnabled =
true;
1418 app.m_fanSpeedValid =
true;
1419 app.m_analogGainValid =
true;
1420 app.m_ledStateValid =
true;
1422 REQUIRE( app.powerOnDefaults() == 0 );
1423 REQUIRE( app.m_tempControlStatusSet ==
false );
1424 REQUIRE( app.m_tempControlStatus ==
false );
1425 REQUIRE( app.m_tempControlStatusStr ==
"TEMP OFF" );
1426 REQUIRE( app.m_cameraCropEnabled ==
false );
1427 REQUIRE( app.m_fanSpeedNameSet ==
"auto" );
1428 REQUIRE( app.m_analogGainNameSet ==
"med" );
1429 REQUIRE( app.m_ledStateSet ==
true );
1432 SECTION(
"setTempControl uses the configured setpoint when cooling is enabled" )
1436 setPoweredOn( app );
1437 app.m_ccdTempSetpt = -15.5f;
1438 app.m_tempControlStatusSet =
true;
1439 queueSerialResponse(
"OK\n" );
1441 REQUIRE( app.setTempControl() == 0 );
1442 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set temperatures snake -15.5" } );
1443 REQUIRE( app.m_tempControlStatus ==
true );
1444 REQUIRE( app.m_tempControlStatusStr ==
"OFF TARGET" );
1447 SECTION(
"setTempControl switches to the warm-up setpoint when cooling is disabled" )
1451 setPoweredOn( app );
1452 app.m_tempControlStatusSet =
false;
1453 queueSerialResponse(
"OK\n" );
1455 REQUIRE( app.setTempControl() == 0 );
1456 REQUIRE( app.m_ccdTempSetpt == Approx( 20.0f ) );
1457 REQUIRE( app.m_tempControlStatus ==
false );
1458 REQUIRE( app.m_tempControlStatusStr ==
"WARMING" );
1461 SECTION(
"setTempControl logs a notice when cooling is requested with a warm setpoint" )
1465 app.m_ccdTempSetpt = 20.0f;
1466 app.m_tempControlStatusSet =
true;
1468 REQUIRE( app.setTempControl() == 0 );
1469 REQUIRE( g_edtStubState.serialCommands.empty() );
1472 SECTION(
"setTempSetPt rejects out-of-range values" )
1476 app.m_ccdTempSetpt = 50.0f;
1477 REQUIRE( app.setTempSetPt() == -1 );
1478 REQUIRE( g_edtStubState.serialCommands.empty() );
1481 SECTION(
"setFPS validates bounds before sending the command and refreshes the cached fps" )
1485 setPoweredOn( app );
1488 app.m_fpsSet = 125.5f;
1489 queueSerialResponse(
"OK\n" );
1490 queueSerialResponse(
"125.5\n" );
1492 REQUIRE( app.setFPS() == 0 );
1493 REQUIRE( app.m_fps == Approx( 125.5f ) );
1494 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set fps 125.5",
"fps raw" } );
1497 SECTION(
"setFPS rejects requests outside the current min-max bounds" )
1503 app.m_fpsSet = 600.0f;
1505 REQUIRE( app.setFPS() == -1 );
1506 REQUIRE( g_edtStubState.serialCommands.empty() );
1509 SECTION(
"setFPS returns an error when the camera rejects the command" )
1513 setPoweredOn( app );
1516 app.m_fpsSet = 125.5f;
1517 queueSerialResponse(
"Error\n" );
1519 REQUIRE( app.setFPS() == -1 );
1522 SECTION(
"setFanSpeed handles automatic and manual presets" )
1526 setPoweredOn( app );
1527 app.m_fanSpeedNameSet =
"p75";
1528 queueSerialResponse(
"OK\n" );
1529 queueSerialResponse(
"OK\n" );
1530 queueSerialResponse(
"manual\n" );
1531 queueSerialResponse(
"75\n" );
1533 REQUIRE( app.setFanSpeed() == 0 );
1534 REQUIRE( app.m_fanSpeedName ==
"p75" );
1536 g_edtStubState.serialCommands ==
1537 std::vector<std::string>{
"set fan mode manual",
"set fan speed 75",
"fan mode raw",
"fan speed raw" } );
1540 SECTION(
"setFanSpeed handles the automatic preset" )
1544 setPoweredOn( app );
1545 app.m_fanSpeedNameSet =
"auto";
1546 queueSerialResponse(
"OK\n" );
1547 queueSerialResponse(
"automatic\n" );
1549 REQUIRE( app.setFanSpeed() == 0 );
1550 REQUIRE( app.m_fanSpeedName ==
"auto" );
1553 SECTION(
"setFanSpeed rejects unknown presets before sending commands" )
1557 app.m_fanSpeedNameSet =
"mystery";
1559 REQUIRE( app.setFanSpeed() == -1 );
1560 REQUIRE( g_edtStubState.serialCommands.empty() );
1563 SECTION(
"setFanSpeed returns an error when automatic mode cannot be enabled" )
1567 setPoweredOn( app );
1568 app.m_fanSpeedNameSet =
"auto";
1569 queueSerialResponse(
"Error\n" );
1571 REQUIRE( app.setFanSpeed() == -1 );
1574 SECTION(
"setFanSpeed returns an error when the manual speed update is rejected" )
1578 setPoweredOn( app );
1579 app.m_fanSpeedNameSet =
"p25";
1580 queueSerialResponse(
"OK\n" );
1581 queueSerialResponse(
"Error\n" );
1583 REQUIRE( app.setFanSpeed() == -1 );
1586 SECTION(
"setFanSpeed returns an error when manual mode cannot be enabled" )
1590 setPoweredOn( app );
1591 app.m_fanSpeedNameSet =
"p25";
1592 queueSerialResponse(
"Error\n" );
1594 REQUIRE( app.setFanSpeed() == -1 );
1597 SECTION(
"setAnalogGain rejects unknown presets and accepts valid ones" )
1601 setPoweredOn( app );
1602 app.m_analogGainNameSet =
"bad";
1603 REQUIRE( app.setAnalogGain() == -1 );
1606 setPoweredOn( app );
1607 app.m_analogGainNameSet =
"high";
1608 queueSerialResponse(
"OK\n" );
1609 queueSerialResponse(
"high\n" );
1611 REQUIRE( app.setAnalogGain() == 0 );
1612 REQUIRE( app.m_analogGainName ==
"high" );
1613 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set sensibility high",
"sensibility" } );
1616 SECTION(
"setAnalogGain returns an error when the camera rejects the update" )
1620 setPoweredOn( app );
1621 app.m_analogGainNameSet =
"med";
1622 queueSerialResponse(
"Error\n" );
1624 REQUIRE( app.setAnalogGain() == -1 );
1627 SECTION(
"setLED sends the expected on and off commands" )
1631 setPoweredOn( app );
1632 app.m_ledStateSet =
false;
1633 queueSerialResponse(
"OK\n" );
1634 queueSerialResponse(
"off\n" );
1636 REQUIRE( app.setLED() == 0 );
1637 REQUIRE( app.m_ledState ==
false );
1638 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set led off",
"led raw" } );
1641 SECTION(
"setLED sends the on command when the LED is enabled" )
1645 setPoweredOn( app );
1646 app.m_ledStateSet =
true;
1647 queueSerialResponse(
"OK\n" );
1648 queueSerialResponse(
"on\n" );
1650 REQUIRE( app.setLED() == 0 );
1651 REQUIRE( app.m_ledState ==
true );
1654 SECTION(
"setLED returns an error when the requested LED update is rejected" )
1658 setPoweredOn( app );
1659 app.m_ledStateSet =
true;
1660 queueSerialResponse(
"Error\n" );
1662 REQUIRE( app.setLED() == -1 );
1665 SECTION(
"setLED returns an error when disabling the LED is rejected" )
1669 setPoweredOn( app );
1670 app.m_ledStateSet =
false;
1671 queueSerialResponse(
"Error\n" );
1673 REQUIRE( app.setLED() == -1 );
1676 SECTION(
"setExpTime remains a no-op" )
1680 REQUIRE( app.setExpTime() == 0 );
1683 SECTION(
"checkNextROI rounds sizes and centers to the hardware step sizes" )
1688 app.m_nextROI.x = 140.0f;
1689 app.m_nextROI.y = 65.0f;
1690 app.m_nextROI.w = 250;
1691 app.m_nextROI.h = 130;
1693 REQUIRE( app.checkNextROI() == 0 );
1694 REQUIRE( app.m_nextROI.w == 256 );
1695 REQUIRE( app.m_nextROI.h == 132 );
1698 roi, app.m_nextROI.x, app.m_nextROI.y, app.m_nextROI.w, app.m_nextROI.h, app.m_full_w, app.m_full_h ) ==
1704 SECTION(
"setNextROI requests a reconfiguration of the current mode" )
1708 app.m_modeName =
"runtime";
1709 app.m_nextROI.x = 127.5f;
1710 app.m_nextROI.y = 63.5f;
1711 app.m_nextROI.w = 256;
1712 app.m_nextROI.h = 128;
1713 app.m_nextROI.bin_x = 1;
1714 app.m_nextROI.bin_y = 1;
1716 REQUIRE( app.setNextROI() == 0 );
1718 REQUIRE( app.m_reconfig ==
true );
1719 REQUIRE( app.m_nextMode ==
"runtime" );
1727TEST_CASE(
"cred2Ctrl framegrabber helpers manage acquisition and ROI configuration",
"[cred2Ctrl]" )
1732 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
1742 SECTION(
"configureAcquisition handles full-frame and cropped ROI requests" )
1746 setPoweredOn( app );
1747 app.m_cameraCropEnabled =
true;
1748 app.m_fpsSet = 125.0f;
1749 app.m_nextROI.x = app.m_full_x;
1750 app.m_nextROI.y = app.m_full_y;
1751 app.m_nextROI.w = app.m_full_w;
1752 app.m_nextROI.h = app.m_full_h;
1753 app.m_nextROI.bin_x = 1;
1754 app.m_nextROI.bin_y = 1;
1756 REQUIRE( app.configureAcquisition() == 0 );
1757 REQUIRE( app.m_cameraCropEnabled ==
false );
1758 REQUIRE( app.m_width ==
static_cast<uint32_t
>( app.m_full_w ) );
1759 REQUIRE( app.m_height ==
static_cast<uint32_t
>( app.m_full_h ) );
1760 REQUIRE( app.m_fps == Approx( 125.0f ) );
1761 REQUIRE( app.m_roiSettleCounter == 5 );
1763 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set cropping off" } );
1766 setPoweredOn( app );
1767 app.m_nextROI.x = 319.5f;
1768 app.m_nextROI.y = 255.5f;
1769 app.m_nextROI.w = 256;
1770 app.m_nextROI.h = 256;
1771 app.m_nextROI.bin_x = 1;
1772 app.m_nextROI.bin_y = 1;
1774 REQUIRE( app.configureAcquisition() == 0 );
1775 REQUIRE( app.m_cameraCropEnabled ==
true );
1776 REQUIRE( app.m_width == 256 );
1777 REQUIRE( app.m_height == 256 );
1778 REQUIRE( g_edtStubState.serialCommands == std::vector<std::string>{
"set cropping columns 192-447",
1779 "set cropping rows 128-383",
1780 "set cropping on" } );
1783 SECTION(
"configureAcquisition rejects invalid ROIs" )
1787 setPoweredOn( app );
1788 app.m_nextROI.x = -10;
1789 app.m_nextROI.y = -10;
1790 app.m_nextROI.w = 256;
1791 app.m_nextROI.h = 256;
1793 REQUIRE( app.configureAcquisition() == -1 );
1797 SECTION(
"fps and acquisition helpers report the current rate and timestamps" )
1801 app.m_fps = 250.25f;
1804 REQUIRE( app.fps() == Approx( 250.25f ) );
1805 REQUIRE( app.startAcquisition() == 0 );
1807 REQUIRE( g_edtStubState.startImagesCalls == 1 );
1808 REQUIRE( g_edtStubState.lastStartNumBuffs == 6 );
1810 g_edtStubState.waitTimeSec = 12;
1811 g_edtStubState.waitTimeNsec = 345;
1813 REQUIRE( app.acquireAndCheckValid() == 0 );
1814 REQUIRE( app.m_currImageTimestamp.tv_sec == 12 );
1815 REQUIRE( app.m_currImageTimestamp.tv_nsec == 345 );
1816 REQUIRE( g_edtStubState.startImageCalls == 1 );
1819 SECTION(
"loadImageIntoStream copies the raw EDT frame into the destination buffer" )
1822 std::array<uint16_t, 4> source{ 1, 2, 3, 4 };
1823 std::array<uint16_t, 4> dest{ 0, 0, 0, 0 };
1825 app.m_image_p =
reinterpret_cast<u_char *
>( source.data() );
1828 app.m_typeSize =
sizeof( uint16_t );
1830 REQUIRE( app.loadImageIntoStream( dest.data() ) == 0 );
1831 REQUIRE( dest == source );
1834 SECTION(
"reconfig reloads the runtime EDT configuration and restores READY state" )
1838 loadDefaultConfig( app,
"reconfig" );
1839 app.m_modeName =
"runtime";
1840 app.m_nextMode =
"runtime";
1842 REQUIRE( app.reconfig() == 0 );
1843 REQUIRE( g_edtStubState.readcfgCalls > 0 );
1845 REQUIRE( app.m_nextMode ==
"runtime" );
1848 SECTION(
"writeConfig reports file-open failures" )
1852 app.m_configFile =
"/tmp/cred2Ctrl_missing_dir/runtime.cfg";
1854 REQUIRE( app.writeConfig() == -1 );
1857 SECTION(
"configureAcquisition reports failures while disabling full-frame cropping" )
1861 setPoweredOn( app );
1862 app.m_cameraCropEnabled =
true;
1863 app.m_nextROI.x = app.m_full_x;
1864 app.m_nextROI.y = app.m_full_y;
1865 app.m_nextROI.w = app.m_full_w;
1866 app.m_nextROI.h = app.m_full_h;
1867 app.m_nextROI.bin_x = 1;
1868 app.m_nextROI.bin_y = 1;
1869 queueSerialResponse(
"Error\n" );
1871 REQUIRE( app.configureAcquisition() == -1 );
1875 SECTION(
"configureAcquisition reports failures while programming cropped ROI commands" )
1879 setPoweredOn( app );
1880 app.m_nextROI.x = 319.5f;
1881 app.m_nextROI.y = 255.5f;
1882 app.m_nextROI.w = 256;
1883 app.m_nextROI.h = 256;
1884 app.m_nextROI.bin_x = 1;
1885 app.m_nextROI.bin_y = 1;
1886 queueSerialResponse(
"OK\n" );
1887 queueSerialResponse(
"Error\n" );
1889 REQUIRE( app.configureAcquisition() == -1 );
1893 SECTION(
"loadImageIntoStream reports a failure when the configured flip mode is invalid" )
1896 std::array<uint16_t, 4> dest{ 0, 0, 0, 0 };
1898 app.m_image_p =
reinterpret_cast<u_char *
>( g_edtStubState.waitImage.data() );
1901 app.m_typeSize =
sizeof( uint16_t );
1902 app.m_defaultFlip = 999;
1904 REQUIRE( app.loadImageIntoStream( dest.data() ) == -1 );
1907 SECTION(
"reconfig reports file-write failures before touching the EDT driver" )
1911 app.m_modeName =
"runtime";
1912 app.m_nextMode =
"runtime";
1913 app.m_configFile =
"/tmp/cred2Ctrl_missing_dir/reconfig.cfg";
1915 REQUIRE( app.reconfig() == -1 );
1918 SECTION(
"reconfig returns the EDT reload error when pdvReconfig fails" )
1922 loadDefaultConfig( app,
"reconfig_driver_fail" );
1923 app.m_modeName =
"runtime";
1924 app.m_nextMode =
"runtime";
1925 g_edtStubState.readcfgReturn = -1;
1927 REQUIRE( app.reconfig() == -1 );
1935TEST_CASE(
"cred2Ctrl telemetry wrappers and power-off helpers update cached state",
"[cred2Ctrl]" )
1942 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
1954 app.m_temps.motherboard = 40.5f;
1955 app.m_temps.frontend = 37.0f;
1956 app.m_temps.powerboard = 40.25f;
1957 app.m_temps.snake = -14.92f;
1958 app.m_temps.setpoint = -15.0f;
1959 app.m_temps.peltier = 2.29f;
1960 app.m_temps.heatsink = 27.5f;
1961 app.m_modeName =
"runtime";
1962 app.m_currentROI.x = 319.5f;
1963 app.m_currentROI.y = 255.5f;
1964 app.m_currentROI.w = 640;
1965 app.m_currentROI.h = 512;
1966 app.m_currentROI.bin_x = 1;
1967 app.m_currentROI.bin_y = 1;
1969 app.m_ccdTemp = -14.92f;
1970 app.m_ccdTempSetpt = -15.0f;
1971 app.m_tempControlStatus =
true;
1972 app.m_tempControlOnTarget =
true;
1973 app.m_tempControlStatusStr =
"ON TARGET";
1981 SECTION(
"recordTelem overloads forward to the typed telemetry recorders" )
1997 SECTION(
"checkRecordTimes emits stale telemetry once intervals have elapsed" )
2003 app.m_maxInterval = 10.0;
2004 static_cast<cred2MagAOXAppT &
>( app ).m_loopPause = 0;
2006 REQUIRE( app.checkRecordTimes() == 0 );
2012 SECTION(
"recordTemps honors cached values and the force flag" )
2015 app.m_temps.motherboard = 41.5f;
2016 app.m_temps.frontend = 38.0f;
2017 app.m_temps.powerboard = 41.25f;
2018 app.m_temps.snake = -13.92f;
2019 app.m_temps.setpoint = -14.0f;
2020 app.m_temps.peltier = 3.29f;
2021 app.m_temps.heatsink = 28.5f;
2023 REQUIRE( app.recordTemps(
false ) == 0 );
2028 REQUIRE( app.recordTemps(
false ) == 0 );
2032 REQUIRE( app.recordTemps(
true ) == 0 );
2036 SECTION(
"power-off helpers invalidate cached temperatures and leave shutdown clean" )
2038 app.m_powerOnCounter = -1;
2039 REQUIRE( app.onPowerOff() == 0 );
2040 REQUIRE( app.m_powerOnCounter == 0 );
2041 REQUIRE( app.m_temps.snake == Approx( -999.0f ) );
2042 REQUIRE( app.m_ccdTemp == Approx( -999.0f ) );
2043 REQUIRE( app.m_tempControlStatus ==
false );
2044 REQUIRE( app.m_tempControlStatusStr ==
"UNKNOWN" );
2046 REQUIRE( app.whilePowerOff() == 0 );
2047 REQUIRE( app.appShutdown() == 0 );
2055TEST_CASE(
"cred2Ctrl appLogic handles connection and housekeeping flow",
"[cred2Ctrl]" )
2060 #ifdef CRED2CTRL_TEST_DOXYGEN_REF
2065 SECTION(
"POWERON returns immediately while the controller waits for the hardware" )
2069 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 1;
2070 app.m_powerTargetState = 1;
2072 app.m_powerOnCounter = 0;
2073 static_cast<cred2MagAOXAppT &
>( app ).m_loopPause = 0;
2074 fgThreadScope fgThread( app );
2076 REQUIRE( app.appLogic() == 0 );
2080 SECTION(
"NOTCONNECTED returns immediately once the hardware power is already off" )
2084 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2085 app.m_powerTargetState = 0;
2087 fgThreadScope fgThread( app );
2089 REQUIRE( app.appLogic() == 0 );
2093 SECTION(
"a valid connection refresh transitions from NOTCONNECTED to READY" )
2097 setPoweredOn( app );
2099 app.m_modeName =
"runtime";
2100 app.m_startupMode =
"runtime";
2101 app.m_raw_width = 640;
2102 app.m_raw_height = 512;
2103 app.m_ccdTempSetpt = -999;
2104 fgThreadScope fgThread( app );
2106 queueSerialResponse(
"150.5\n" );
2107 queueSerialResponse(
"off\n" );
2108 queueSerialResponse(
"10.0\n" );
2109 queueSerialResponse(
"500.0\n" );
2110 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2111 queueSerialResponse(
"-15.0\n" );
2112 queueSerialResponse(
"150.5\n" );
2113 queueSerialResponse(
"automatic\n" );
2114 queueSerialResponse(
"medium\n" );
2115 queueSerialResponse(
"on\n" );
2117 REQUIRE( app.appLogic() == 0 );
2119 REQUIRE( app.m_fps == Approx( 150.5f ) );
2120 REQUIRE( app.m_fanSpeedName ==
"auto" );
2121 REQUIRE( app.m_analogGainName ==
"med" );
2122 REQUIRE( app.m_ledState ==
true );
2125 SECTION(
"a malformed initial fps response leaves the controller in NODEVICE" )
2129 setPoweredOn( app );
2131 fgThreadScope fgThread( app );
2132 queueSerialResponse(
"bad fps\n" );
2134 REQUIRE( app.appLogic() == 0 );
2138 SECTION(
"a transport failure during the initial fps probe also leaves the controller in NODEVICE" )
2142 setPoweredOn( app );
2144 fgThreadScope fgThread( app );
2145 queueSerialResponse(
"", -1 );
2147 REQUIRE( app.appLogic() == 0 );
2151 SECTION(
"connected-state sync failures are ignored after power has already been lost" )
2155 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2156 app.m_powerTargetState = 0;
2158 fgThreadScope fgThread( app );
2159 queueSerialResponse(
"garbage\n" );
2161 REQUIRE( app.appLogic() == 0 );
2165 SECTION(
"connected-state sync failures promote the controller to ERROR while power is still on" )
2169 setPoweredOn( app );
2171 fgThreadScope fgThread( app );
2172 queueSerialResponse(
"garbage\n" );
2174 REQUIRE( app.appLogic() == 0 );
2178 SECTION(
"connected-state refresh failures promote the controller to ERROR while still powered" )
2182 setPoweredOn( app );
2184 app.m_raw_width = app.m_full_w;
2185 app.m_raw_height = app.m_full_h;
2186 fgThreadScope fgThread( app );
2187 queueSerialResponse(
"off\n" );
2188 queueSerialResponse(
"bad-fps-limit\n" );
2190 REQUIRE( app.appLogic() == 0 );
2194 SECTION(
"connected-state refresh failures are ignored after power has already been lost" )
2198 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2199 app.m_powerTargetState = 0;
2201 app.m_raw_width = app.m_full_w;
2202 app.m_raw_height = app.m_full_h;
2203 fgThreadScope fgThread( app );
2204 queueSerialResponse(
"off\n" );
2205 queueSerialResponse(
"bad-fps-limit\n" );
2207 REQUIRE( app.appLogic() == 0 );
2211 SECTION(
"connected-state setpoint update failures are logged while the camera is still powered" )
2215 setPoweredOn( app );
2217 app.m_raw_width = app.m_full_w;
2218 app.m_raw_height = app.m_full_h;
2219 fgThreadScope fgThread( app );
2221 queueSerialResponse(
"off\n" );
2222 queueSerialResponse(
"10.0\n" );
2223 queueSerialResponse(
"500.0\n" );
2224 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2225 queueSerialResponse(
"-15.0\n" );
2226 queueSerialResponse(
"150.5\n" );
2227 queueSerialResponse(
"automatic\n" );
2228 queueSerialResponse(
"medium\n" );
2229 queueSerialResponse(
"on\n" );
2230 queueSerialResponse(
"Error\n" );
2232 REQUIRE( app.appLogic() == 0 );
2236 SECTION(
"connected-state setpoint update failures are ignored after power has already been lost" )
2240 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2241 app.m_powerTargetState = 0;
2243 app.m_raw_width = app.m_full_w;
2244 app.m_raw_height = app.m_full_h;
2245 fgThreadScope fgThread( app );
2247 queueSerialResponse(
"off\n" );
2248 queueSerialResponse(
"10.0\n" );
2249 queueSerialResponse(
"500.0\n" );
2250 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2251 queueSerialResponse(
"-15.0\n" );
2252 queueSerialResponse(
"150.5\n" );
2253 queueSerialResponse(
"automatic\n" );
2254 queueSerialResponse(
"medium\n" );
2255 queueSerialResponse(
"on\n" );
2256 queueSerialResponse(
"", -1 );
2258 REQUIRE( app.appLogic() == 0 );
2262 SECTION(
"ready-state housekeeping failures promote the controller to ERROR while powered on" )
2266 setPoweredOn( app );
2268 app.m_roiSettleCounter = 0;
2269 fgThreadScope fgThread( app );
2271 queueSerialResponse(
"10.0\n" );
2272 queueSerialResponse(
"500.0\n" );
2273 queueSerialResponse(
"", -1 );
2275 REQUIRE( app.appLogic() == 0 );
2279 SECTION(
"ready-state housekeeping failures are ignored once power has already been lost" )
2283 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2284 app.m_powerTargetState = 0;
2286 app.m_roiSettleCounter = 0;
2287 fgThreadScope fgThread( app );
2289 queueSerialResponse(
"10.0\n" );
2290 queueSerialResponse(
"500.0\n" );
2291 queueSerialResponse(
"", -1 );
2293 REQUIRE( app.appLogic() == 0 );
2297 SECTION(
"ready-state returns immediately when the INDI mutex is already held elsewhere" )
2300 std::atomic<bool> mutexHeld{
false };
2302 setPoweredOn( app );
2304 app.m_roiSettleCounter = 0;
2305 fgThreadScope fgThread( app );
2307 std::thread mutexHolder(
2308 [&app, &mutexHeld]()
2310 std::lock_guard<std::mutex>
lock( app.m_indiMutex );
2311 mutexHeld.store(
true );
2312 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
2315 while( !mutexHeld.load() )
2317 std::this_thread::yield();
2320 REQUIRE( app.appLogic() == 0 );
2322 REQUIRE( g_edtStubState.serialCommands.empty() );
2327 SECTION(
"ready-state update-limit failures are ignored once power has already been lost" )
2331 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2332 app.m_powerTargetState = 0;
2334 app.m_roiSettleCounter = 0;
2335 fgThreadScope fgThread( app );
2337 queueSerialResponse(
"bad-fps-limit\n" );
2339 REQUIRE( app.appLogic() == 0 );
2343 SECTION(
"ready-state update-limit failures promote the controller to ERROR while powered on" )
2347 setPoweredOn( app );
2349 app.m_roiSettleCounter = 0;
2350 fgThreadScope fgThread( app );
2352 queueSerialResponse(
"bad-fps-limit\n" );
2354 REQUIRE( app.appLogic() == 0 );
2358 SECTION(
"ready-state fan-query failures promote the controller to ERROR while powered on" )
2362 setPoweredOn( app );
2364 app.m_roiSettleCounter = 0;
2365 fgThreadScope fgThread( app );
2367 queueSerialResponse(
"10.0\n" );
2368 queueSerialResponse(
"500.0\n" );
2369 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2370 queueSerialResponse(
"-15.0\n" );
2371 queueSerialResponse(
"150.5\n" );
2372 queueSerialResponse(
"", -1 );
2374 REQUIRE( app.appLogic() == 0 );
2378 SECTION(
"ready-state fan-query failures are ignored once power has already been lost" )
2382 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2383 app.m_powerTargetState = 0;
2385 app.m_roiSettleCounter = 0;
2386 fgThreadScope fgThread( app );
2388 queueSerialResponse(
"10.0\n" );
2389 queueSerialResponse(
"500.0\n" );
2390 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2391 queueSerialResponse(
"-15.0\n" );
2392 queueSerialResponse(
"150.5\n" );
2393 queueSerialResponse(
"", -1 );
2395 REQUIRE( app.appLogic() == 0 );
2399 SECTION(
"ready-state analog-gain failures are ignored once power has already been lost" )
2403 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2404 app.m_powerTargetState = 0;
2406 app.m_roiSettleCounter = 0;
2407 fgThreadScope fgThread( app );
2409 queueSerialResponse(
"10.0\n" );
2410 queueSerialResponse(
"500.0\n" );
2411 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2412 queueSerialResponse(
"-15.0\n" );
2413 queueSerialResponse(
"150.5\n" );
2414 queueSerialResponse(
"automatic\n" );
2415 queueSerialResponse(
"", -1 );
2417 REQUIRE( app.appLogic() == 0 );
2421 SECTION(
"ready-state analog-gain failures promote the controller to ERROR while powered on" )
2425 setPoweredOn( app );
2427 app.m_roiSettleCounter = 0;
2428 fgThreadScope fgThread( app );
2430 queueSerialResponse(
"10.0\n" );
2431 queueSerialResponse(
"500.0\n" );
2432 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2433 queueSerialResponse(
"-15.0\n" );
2434 queueSerialResponse(
"150.5\n" );
2435 queueSerialResponse(
"automatic\n" );
2436 queueSerialResponse(
"", -1 );
2438 REQUIRE( app.appLogic() == 0 );
2442 SECTION(
"ready-state LED failures promote the controller to ERROR while powered on" )
2446 setPoweredOn( app );
2448 app.m_roiSettleCounter = 0;
2449 fgThreadScope fgThread( app );
2451 queueSerialResponse(
"10.0\n" );
2452 queueSerialResponse(
"500.0\n" );
2453 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2454 queueSerialResponse(
"-15.0\n" );
2455 queueSerialResponse(
"150.5\n" );
2456 queueSerialResponse(
"automatic\n" );
2457 queueSerialResponse(
"medium\n" );
2458 queueSerialResponse(
"unknown\n" );
2459 queueSerialResponse(
"still unknown\n" );
2461 REQUIRE( app.appLogic() == 0 );
2465 SECTION(
"ready-state LED failures are ignored once power has already been lost" )
2469 static_cast<cred2MagAOXAppT &
>( app ).m_powerState = 0;
2470 app.m_powerTargetState = 0;
2472 app.m_roiSettleCounter = 0;
2473 fgThreadScope fgThread( app );
2475 queueSerialResponse(
"10.0\n" );
2476 queueSerialResponse(
"500.0\n" );
2477 queueSerialResponse(
"40.50:37.00:40.25:-14.92:2.29:27.50\n" );
2478 queueSerialResponse(
"-15.0\n" );
2479 queueSerialResponse(
"150.5\n" );
2480 queueSerialResponse(
"automatic\n" );
2481 queueSerialResponse(
"medium\n" );
2482 queueSerialResponse(
"unknown\n" );
2483 queueSerialResponse(
"still unknown\n" );
2485 REQUIRE( app.appLogic() == 0 );
2489 SECTION(
"ready-state ROI settling skips one housekeeping cycle" )
2493 setPoweredOn( app );
2495 app.m_roiSettleCounter = 2;
2496 fgThreadScope fgThread( app );
2498 REQUIRE( app.appLogic() == 0 );
2499 REQUIRE( app.m_roiSettleCounter == 1 );
2500 REQUIRE( g_edtStubState.serialCommands.empty() );
The base-class for XWCTk applications.
MagAO-X application to control the C-RED 2 camera.
int checkNextROI()
Validate and normalize the requested ROI.
int getFanSpeed()
Query and update the current fan-control state.
float fps()
Return the currently measured frame rate.
int setTempControl()
Implement the C-RED 2 temperature-controller toggle semantics.
int setTempSetPt()
Send the current target detector temperature setpoint to the camera.
int checkRecordTimes()
Check the telemetry record timers.
virtual int onPowerOff()
Actions required when the camera power turns off.
int loadImageIntoStream(void *dest)
Copy the current EDT image into the output stream.
int reconfig()
Reconfigure the EDT board for the pending ROI.
int syncROIFromCamera()
Query the camera for its current ROI and synchronize local state.
int configureAcquisition()
Configure camera-side ROI settings before acquisition starts.
int getAnalogGain()
Query and update the current analog-gain state.
virtual void loadConfig()
Load the configuration system results.
int recordTemps(bool force=false)
Record the detailed C-RED 2 temperature telemetry when values change.
int setLED()
Send the requested LED state to the camera.
int powerOnDefaults()
Set defaults for a power-on state.
virtual void setupConfig()
Setup the configuration system.
int updateFPSLimits()
Query and update the current camera FPS limits.
int getLEDState()
Query and update the current LED state.
int setAnalogGain()
Send the requested analog-gain mode to the camera.
virtual int appShutdown()
Shutdown function.
int setFPS()
Send the requested frame rate to the camera.
int setExpTime()
Required by stdCamera, but unused for C-RED 2.
int acquireAndCheckValid()
Wait for and validate the next acquired image.
int setNextROI()
Request that the next valid ROI be applied through reconfiguration.
virtual int appLogic()
Main FSM logic.
int startAcquisition()
Start frame acquisition on the EDT board.
int getTemps()
Query and update the camera temperature channels.
int writeConfig()
Write the temporary EDT configuration file for the pending ROI.
int getFPS()
Query and update the current camera frame rate.
int recordTelem(const cred2_temps *)
Record the detailed C-RED 2 temperature telemetry.
int issueCommand(const std::string &command, bool allowNoResponse=false)
Send a command that should return a success acknowledgement.
int sendCommand(std::string &response, const std::string &command, bool logFailure=true)
Send a command over Camera Link serial and clean the response.
int setFanSpeed()
Send the requested fan-control mode to the camera.
virtual int whilePowerOff()
Actions required while the camera remains 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.
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.
int pdv_serial_set_baud(PdvDev *pdv_p, int baud)
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_get_baud(PdvDev *pdv_p)
int pdv_serial_read(PdvDev *pdv_p, char *buf, int size)
Read bytes from the stub PDV serial channel.
Stub EDT dependent-configuration type used by unit tests.
Stub EDT device handle type used by unit tests.
Stub EDT configuration info structure used by unit tests.
Stub EDT camera handle type used by unit tests.
TEST_CASE("cred2Ctrl lifecycle entrypoints handle startup failures and success", "[cred2Ctrl]")
Verify lifecycle entrypoints cover startup failure handling and successful startup cleanup.
bool fullFrame
True when the ROI spans the full detector.
int startRow
First included row.
int endColumn
Last included column.
int startColumn
First included column.
C-RED 2 ROI expressed as 0-based inclusive column and row limits.
@ OPERATING
The device is operating, other than homing.
@ NODEVICE
No device exists for the application to control.
@ CONFIGURING
The application is configuring the device.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ 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.
@ POWERON
The device power is on.
#define XWCTEST_DOXYGEN_REF(fxn)
This inserts an unused call to a function signature to make doxygen make the link.
int cred2ParseRange(int &firstValue, int &secondValue, const std::string &response)
Parse a raw range response such as 0-639.
int cred2RoiToCenter(float ¢erX, float ¢erY, int &width, int &height, const cred2Roi &roi, int fullWidth, int fullHeight)
Convert C-RED 2 ROI corners into a MagAO-X ROI center/size description.
int cred2ParseCropState(bool &enabled, int &startColumn, int &endColumn, int &startRow, int &endRow, const std::string &response)
Parse a raw cropping status response such as on or on:192-447:128-383.
int cred2RoiFromCenter(cred2Roi &roi, float centerX, float centerY, int width, int height, int fullWidth, int fullHeight)
Convert a MagAO-X ROI center/size description into C-RED 2 corners.
int cred2ParseFloatVector(std::vector< float > &values, const std::string &response, size_t expectedValues)
Parse a delimited list of raw numeric responses into a float vector.
int cred2ParseFloat(float &value, const std::string &response)
Parse a raw numeric response into a float.
std::unique_lock< std::mutex > lock(m_indiMutex)
Namespace for all libXWC tests.
Log entry recording the C-RED 2 detailed temperature channels.
static timespec lastRecord
The timestamp of the last time this log was recorded. Used by telemetry.
Log entry recording framegrabber timings.
static timespec lastRecord
The timestamp of the last time this log was recorded. Used by the telemetry system.
Log entry recording stdcam stage specific status.
static timespec lastRecord
The timestamp of the last time this log was recorded. Used by the telemetry system.