From 9f4ab7aa194790bc4c5d301856c2d03c2ac4d70a Mon Sep 17 00:00:00 2001 From: Don Neill Date: Mon, 12 Aug 2024 11:26:11 -0700 Subject: [PATCH 1/9] Initial try at adding power control to camerad in archon.cpp, archon.h, camerad.cpp, and generic.cpp --- camerad/archon.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++ camerad/archon.h | 3 ++ camerad/camerad.cpp | 6 +++ camerad/generic.cpp | 19 +++++++++ 4 files changed, 130 insertions(+) diff --git a/camerad/archon.cpp b/camerad/archon.cpp index 8e6cef22..203f0edc 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -102,6 +102,108 @@ namespace Archon { } /**************** Archon::Interface::interface ******************************/ + /***** Archon::Interface::do_power ******************************************/ + /** + * @brief set/get the power state + * @param[in] state_in input string contains requested power state + * @param[out] retstring return string contains the current power state + * @return ERROR or NO_ERROR + * + */ + long Interface::do_power(std::string state_in, std::string &retstring) { + std::string function = "Archon::Interface::do_power"; + std::stringstream message; + long error = ERROR; + + if ( !this->archon.isconnected() ) { // nothing to do if no connection open to controller + this->camera.log_error( function, "connection not open to controller" ); + return( ERROR ); + } + + // set the Archon power state as requested + // + if ( !state_in.empty() ) { // received something + std::transform( state_in.begin(), state_in.end(), state_in.begin(), ::toupper ); // make uppercase + if ( state_in == "ON" ) { + error = this->archon_cmd( POWERON ); // send POWERON command to Archon + if ( error == NO_ERROR ) std::this_thread::sleep_for( std::chrono::seconds(2) ); // wait 2s to ensure power is stable + } + else + if ( state_in == "OFF" ) { + error = this->archon_cmd( POWEROFF ); // send POWEROFF command to Archon + if ( error == NO_ERROR ) std::this_thread::sleep_for( std::chrono::milliseconds(200) ); // wait 200ms to ensure power is off + } + else { + message.str(""); message << "unrecognized argument " << state_in << ": expected {on|off}"; + this->camera.log_error( function, message.str() ); + return( ERROR ); + } + if ( error != NO_ERROR ) { + message.str(""); message << "setting Archon power " << state_in; + this->camera.log_error( function, message.str() ); + return( ERROR ); + } + } + + // Read the Archon power state directly from Archon + // + std::string power; + error = get_status_key( "POWER", power ); + + if ( error != NO_ERROR ) return( ERROR ); + + int status=-1; + + try { status = std::stoi( power ); } + catch (std::invalid_argument &) { + this->camera.log_error( function, "unable to convert power status message to integer" ); + return(ERROR); + } + catch (std::out_of_range &) { + this->camera.log_error( function, "power status out of range" ); + return(ERROR); + } + + // set the power status (or not) depending on the value extracted from the STATUS message + // + switch( status ) { + case -1: // no POWER token found in status message + this->camera.log_error( function, "unable to find power in Archon status message" ); + return( ERROR ); + case 0: // usually an internal error + this->camera.power_status = "UNKNOWN"; + break; + case 1: // no configuration applied + this->camera.power_status = "NOT_CONFIGURED"; + break; + case 2: // power is off + this->camera.power_status = "OFF"; + break; + case 3: // some modules powered, some not + this->camera.power_status = "INTERMEDIATE"; + break; + case 4: // power is on + this->camera.power_status = "ON"; + break; + case 5: // system is in standby + this->camera.power_status = "STANDBY"; + break; + default: // should be impossible + message.str(""); message << "unknown power status: " << status; + this->camera.log_error( function, message.str() ); + return( ERROR ); + } + + message.str(""); message << "POWER:" << this->camera.power_status; + this->camera.async.enqueue( message.str() ); + + retstring = this->camera.power_status; + + return(NO_ERROR); + } + /***** Archon::Interface::do_power ******************************************/ + + /***** Archon::Interface::configure_controller ******************************/ /** diff --git a/camerad/archon.h b/camerad/archon.h index 8612381f..b4bf0fb4 100644 --- a/camerad/archon.h +++ b/camerad/archon.h @@ -217,6 +217,9 @@ namespace Archon { void add_filename_key(); + long power( std::string state_in, std::string &retstring ); /// wrapper for do_power + long do_power( std::string state_in, std::string &retstring ); /// set/get Archon power state + long expose(std::string nseq_in); long hexpose(std::string nseq_in); diff --git a/camerad/camerad.cpp b/camerad/camerad.cpp index c3431205..b9441d45 100644 --- a/camerad/camerad.cpp +++ b/camerad/camerad.cpp @@ -787,6 +787,12 @@ void doit(Network::TcpSocket sock) { ret = server.interface(retstring); sock.Write(retstring); sock.Write(" "); + } else if (cmd =="power") { + ret = server.power( args, retstring ); + if (!retstring.empty()) { + sock.Write(retstring); + sock.Write(" "); + } } else if (cmd == "test") { ret = server.test(args, retstring); if (!retstring.empty()) { diff --git a/camerad/generic.cpp b/camerad/generic.cpp index 8cf0d316..325029d3 100644 --- a/camerad/generic.cpp +++ b/camerad/generic.cpp @@ -41,4 +41,23 @@ namespace Archon { } /**************** Archon::Interface::region_of_interest *****************/ + + + /***** Archon::Interface::power *****************************************/ + /** + * @brief wrapper for Archon::Interface::do_power() + * @param[in] state_in requested power state + * @param[out] restring return string holds power state + * @return ERROR or NO_ERROR + * + */ + long Interface::power( std::string state_in, std::string &retstring ) { + std::string function = "Archon::Instrument::power"; + std::stringstream message; + + // use Archon::Interface::do_power() to set/get the power + // + return( this->do_power( state_in, retstring ) ); + } + /***** Archon::Interface::power *****************************************/ } From 15b66b93aef37d0ee745806c2376435a5482156b Mon Sep 17 00:00:00 2001 From: Don Neill Date: Mon, 12 Aug 2024 11:31:49 -0700 Subject: [PATCH 2/9] Added power_status property to Camera object in camera.h --- camerad/camera.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/camerad/camera.h b/camerad/camera.h index 75cc9179..7c48c0df 100644 --- a/camerad/camera.h +++ b/camerad/camera.h @@ -69,6 +69,8 @@ namespace Camera { std::map readout_time; //!< readout time in msec for given controller device number, read from .cfg file + std::string power_status; //!< archon power status + void log_error(std::string function, std::string message); std::string get_longerror(); From 7e37b3da04f2da78364d9bb0357b756b969cfdea Mon Sep 17 00:00:00 2001 From: Don Neill Date: Mon, 12 Aug 2024 11:36:36 -0700 Subject: [PATCH 3/9] Added get_status_key to archon.cpp --- camerad/archon.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/camerad/archon.cpp b/camerad/archon.cpp index 203f0edc..ad9c71a3 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -3634,6 +3634,46 @@ namespace Archon { /**************** Archon::Interface::add_filename_key ***********************/ + /***** Archon::Interface::get_status_key ************************************/ + /** + * @brief get value for the indicated key from the Archon "STATUS" string + * @param[in] key key to extract from STATUS + * @param[out] value value of key + * @return ERROR or NO_ERROR + * + */ + long Interface::get_status_key( std::string key, std::string &value ) { + std::string function = "Archon::Interface::get_status_key"; + std::stringstream message; + std::string reply; + + long error = this->archon_cmd( STATUS, reply ); // first the whole reply in one string + + if ( error != NO_ERROR ) return error; + + std::vector lines, tokens; + Tokenize( reply, lines, " " ); // then each line in a separate token "lines" + + for ( auto line : lines ) { + Tokenize( line, tokens, "=" ); // break each line into tokens to get KEY=value + if ( tokens.size() != 2 ) continue; // need 2 tokens + try { + if ( tokens.at(0) == key ) { // looking for the KEY + value = tokens.at(1); // found the KEY= status here + break; // done looking + } else continue; + } + catch (std::out_of_range &) { // should be impossible + this->camera.log_error( function, "token out of range" ); + return(ERROR); + } + } + + return( NO_ERROR ); + } + /***** Archon::Interface::get_status_key ************************************/ + + /**************** Archon::Interface::expose *********************************/ /** * @fn expose From cff3c3ea0086596014dd26fe09ef4dccca1d8be3 Mon Sep 17 00:00:00 2001 From: Don Neill Date: Mon, 12 Aug 2024 11:38:29 -0700 Subject: [PATCH 4/9] Added get_status_key to archon.h --- camerad/archon.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/camerad/archon.h b/camerad/archon.h index b4bf0fb4..a6604f46 100644 --- a/camerad/archon.h +++ b/camerad/archon.h @@ -217,6 +217,8 @@ namespace Archon { void add_filename_key(); + long get_status_key( std::string key, std::string &value ); /// get value for indicated key from STATUS string + long power( std::string state_in, std::string &retstring ); /// wrapper for do_power long do_power( std::string state_in, std::string &retstring ); /// set/get Archon power state From 44b851076f42ba2821167de735e810c15d9970d8 Mon Sep 17 00:00:00 2001 From: Don Neill Date: Mon, 12 Aug 2024 16:28:11 -0700 Subject: [PATCH 5/9] Putting in all the steps for H2RG setup and sleeps in hsetup routine in archon.cpp --- camerad/archon.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/camerad/archon.cpp b/camerad/archon.cpp index ad9c71a3..241597fb 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -4059,17 +4059,25 @@ namespace Archon { std::string reg; long error = NO_ERROR; + this->set_parameter("Start", 1); + usleep(1000); + this->set_camera_mode("VIDEORXR"); + usleep(1000); + // H2RG manual says to pull this value low for 100 ns // however, currently it is pulled low for ~1000 usec this->set_parameter("H2RGMainReset", 1); - usleep(1500); // to be sure we are done with the reset + usleep(1000); // to be sure we are done with the reset this->set_parameter("H2RGMainReset", 0); usleep(1000); // to be sure we are done with the reset // Enable output to Pad B and HIGHOHM error = this->inreg("10 1 16402"); // 0100 000000010010 + usleep(1000); if (error == NO_ERROR) error = this->inreg("10 0 1"); // send to detector + usleep(1000); if (error == NO_ERROR) error = this->inreg("10 0 0"); // reset to 0 + usleep(1000); if (error != NO_ERROR) { message.str(""); message << "enabling output to Pad B and HIGHOHM"; From 1ee30f44027e10c15fa55bd9f53451441ee428fe Mon Sep 17 00:00:00 2001 From: Don Neill Date: Tue, 13 Aug 2024 12:57:38 -0700 Subject: [PATCH 6/9] returning hsetup routine to just the setup command used before in archon.cpp --- camerad/archon.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/camerad/archon.cpp b/camerad/archon.cpp index 241597fb..f2f6c0a5 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -4059,11 +4059,6 @@ namespace Archon { std::string reg; long error = NO_ERROR; - this->set_parameter("Start", 1); - usleep(1000); - this->set_camera_mode("VIDEORXR"); - usleep(1000); - // H2RG manual says to pull this value low for 100 ns // however, currently it is pulled low for ~1000 usec this->set_parameter("H2RGMainReset", 1); From b9cccaca07138d44bcd9e25a6594bcb0072bfabd Mon Sep 17 00:00:00 2001 From: neill Date: Wed, 14 Aug 2024 10:28:12 -0700 Subject: [PATCH 7/9] Added tested code for hsetup that will actually configure the chip correctly in archon.cpp --- camerad/archon.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/camerad/archon.cpp b/camerad/archon.cpp index f2f6c0a5..874f64d9 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -4059,20 +4059,13 @@ namespace Archon { std::string reg; long error = NO_ERROR; - // H2RG manual says to pull this value low for 100 ns - // however, currently it is pulled low for ~1000 usec - this->set_parameter("H2RGMainReset", 1); - usleep(1000); // to be sure we are done with the reset - this->set_parameter("H2RGMainReset", 0); - usleep(1000); // to be sure we are done with the reset - // Enable output to Pad B and HIGHOHM error = this->inreg("10 1 16402"); // 0100 000000010010 - usleep(1000); + sleep(1); if (error == NO_ERROR) error = this->inreg("10 0 1"); // send to detector - usleep(1000); + sleep(1); if (error == NO_ERROR) error = this->inreg("10 0 0"); // reset to 0 - usleep(1000); + sleep(1); if (error != NO_ERROR) { message.str(""); message << "enabling output to Pad B and HIGHOHM"; From a0ab6362120798f88ffeac18a20c173d53a32e11 Mon Sep 17 00:00:00 2001 From: neill Date: Wed, 14 Aug 2024 10:31:33 -0700 Subject: [PATCH 8/9] Update hsetup docstring in archon.cpp --- camerad/archon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/camerad/archon.cpp b/camerad/archon.cpp index 874f64d9..f0bab757 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -4048,9 +4048,9 @@ namespace Archon { * @return ERROR or NO_ERROR * * NOTE: this assumes LVDS is module 10 - * This function does the following: - * 1) Pulse low on MainResetB - * 2) sets output to Pad B and HIGHOHM + * This function sets output to Pad B and HIGHOHM + * The register reset of H2RGMainReset is already done + * if you power on and then setp Start 1 * */ long Interface::hsetup() { From 8498722e21f7f3ee83f0f7d5353847a5b323a91a Mon Sep 17 00:00:00 2001 From: neill Date: Wed, 14 Aug 2024 10:38:59 -0700 Subject: [PATCH 9/9] Changing deprecated sleep commands with std::this_thread::sleep_for commands in hsetup function in archon.cpp --- camerad/archon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/camerad/archon.cpp b/camerad/archon.cpp index f0bab757..1bb48d93 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -4061,11 +4061,11 @@ namespace Archon { // Enable output to Pad B and HIGHOHM error = this->inreg("10 1 16402"); // 0100 000000010010 - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); if (error == NO_ERROR) error = this->inreg("10 0 1"); // send to detector - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); if (error == NO_ERROR) error = this->inreg("10 0 0"); // reset to 0 - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); if (error != NO_ERROR) { message.str(""); message << "enabling output to Pad B and HIGHOHM";