diff --git a/camerad/archon.cpp b/camerad/archon.cpp index 1bb48d93..55001dca 100644 --- a/camerad/archon.cpp +++ b/camerad/archon.cpp @@ -31,6 +31,8 @@ namespace Archon { this->frame.next_index = 0; this->abort = false; this->taplines = 0; + this->configlines = 0; + this->logwconfig = false; this->image_data = nullptr; this->image_data_bytes = 0; this->image_data_allocated = 0; @@ -865,11 +867,11 @@ namespace Archon { * @return ERROR, BUSY or NO_ERROR * */ - long Interface::archon_cmd(const std::string& cmd) { // use this form when the calling + long Interface::archon_cmd(std::string cmd) { // use this form when the calling std::string reply; // function doesn't need to look at the reply return( archon_cmd(cmd, reply) ); } - long Interface::archon_cmd(const std::string& cmd, std::string &reply) { + long Interface::archon_cmd(std::string cmd, std::string &reply) { std::string function = "Archon::Interface::archon_cmd"; std::stringstream message; int retval; @@ -914,6 +916,15 @@ namespace Archon { return ERROR; } + // This allows sending commands that don't get logged, + // by prepending QUIET, which gets removed here if present. + // + bool quiet=false; + if ( cmd.find(QUIET)==0 ) { + cmd.erase(0, QUIET.length()); + quiet=true; + } + std::stringstream sscmd; // sscmd = stringstream, building command sscmd << prefix << cmd << "\n"; std::string scmd = sscmd.str(); // scmd = string, command to send @@ -924,10 +935,10 @@ namespace Archon { // log the command as long as it's not a STATUS, TIMER, WCONFIG or FRAME command // - if ( (cmd.compare(0,7,"WCONFIG") != 0) && - (cmd.compare(0,5,"TIMER") != 0) && - (cmd.compare(0,6,"STATUS") != 0) && - (cmd.compare(0,5,"FRAME") != 0) ) { + if ( !quiet && (cmd.compare(0,7,"WCONFIG") != 0) && + (cmd.compare(0,5,"TIMER") != 0) && + (cmd.compare(0,6,"STATUS") != 0) && + (cmd.compare(0,5,"FRAME") != 0) ) { // erase newline for logging purposes std::string fcmd = scmd; try { @@ -937,6 +948,10 @@ namespace Archon { logwrite(function, message.str()); } + // optionally log WCONFIG commands + // + if ( this->logwconfig && cmd.find("WCONFIG")!=std::string::npos ) logwrite( function, strip_newline(cmd) ); + // send the command // if ( (this->archon.Write(scmd)) == -1) { @@ -1011,10 +1026,10 @@ namespace Archon { error = NO_ERROR; // log the command as long as it's not a STATUS, TIMER, WCONFIG or FRAME command - if ( (cmd.compare(0,7,"WCONFIG") != 0) && - (cmd.compare(0,5,"TIMER") != 0) && - (cmd.compare(0,6,"STATUS") != 0) && - (cmd.compare(0,5,"FRAME") != 0) ) { + if ( !quiet && (cmd.compare(0,7,"WCONFIG") != 0) && + (cmd.compare(0,5,"TIMER") != 0) && + (cmd.compare(0,6,"STATUS") != 0) && + (cmd.compare(0,5,"FRAME") != 0) ) { message.str(""); message << "command 0x" << std::setfill('0') << std::setw(2) << std::uppercase << std::hex << this->msgref << " success"; logwrite(function, message.str()); @@ -1679,20 +1694,27 @@ namespace Archon { if (line.find_first_of('=', 0) == std::string::npos) { continue; } + Tokenize(line, tokens, "="); // separate into KEY, VALUE tokens if (tokens.empty()) { continue; // nothing to do here if no tokens (ie no "=") } - if (!tokens.empty() ) { // at least one token is the key - key = tokens[0]; // KEY - value = ""; // VALUE can be empty (e.g. labels not required) - this->configmap[ tokens[0] ].line = linecount; - this->configmap[ tokens[0] ].value = value; - } - if (tokens.size() > 1 ) { // if a second token then that's the value + + key = tokens[0]; // not empty so at least one token is the KEY + value.clear(); // VALUE can be empty (e.g. labels not required) + + if ( tokens.size() > 1 ) { // at least one more token is the value value = tokens[1]; // VALUE (there is a second token) - this->configmap[ tokens[0] ].value = tokens[1]; } + + if ( tokens.size() > 2 ) { // more tokens are possible + for ( size_t i=2; iconfigmap[ tokens[0] ].line = linecount; + this->configmap[ tokens[0] ].value = value; } // end else // Form the WCONFIG command to Archon and @@ -1706,10 +1728,12 @@ namespace Archon { << key << "=" << value << "\n"; // send the WCONFIG command here if (error == NO_ERROR) error = this->archon_cmd(sscmd.str()); + linecount++; } // end if ( !key.empty() && !value.empty() ) - linecount++; } // end while ( getline(filestream, line) ) + this->configlines = linecount; // save the number of configuration lines + // re-enable background polling // if (error == NO_ERROR) error = this->archon_cmd(POLLON); @@ -8041,6 +8065,80 @@ namespace Archon { retstring = "delta=" + std::to_string( m ) + " stddev=" + std::to_string( stdev ); // end if (testname==timer) + } else if (testname == "rconfigmap") { + // ---------------------------------------------------- + // rconfigmap + // reports the configmap (what should have been written) + // ---------------------------------------------------- + std::string filter; + if ( tokens.size() > 1 ) { + if ( tokens[1] == "line" ) filter="LINE"; + else + if ( tokens[1] == "mod" ) filter="MOD"; + else + if ( tokens[1] == "param" ) filter="PARAMETER"; + else + if ( tokens[1] == "state" ) filter="STATE"; + else + if ( tokens[1] == "vcpu" ) filter="VCPU"; + } + for ( const auto &[k,v] : this->configmap ) { + if ( k.find(filter)!=std::string::npos ) { + message.str(""); message << "RCONFIG" + << std::uppercase << std::setfill('0') << std::setw(4) << std::hex + << v.line << ": " << k << "=" << v.value; + logwrite( function, message.str() ); + } + } + error = NO_ERROR; + } else if (testname == "rconfig") { + // ---------------------------------------------------- + // rconfig + // reads config directly from Archon + // ---------------------------------------------------- + std::string filter; + if ( tokens.size() > 1 ) { + if ( tokens[1] == "line" ) filter="LINE"; + else + if ( tokens[1] == "mod" ) filter="MOD"; + else + if ( tokens[1] == "param" ) filter="PARAMETER"; + else + if ( tokens[1] == "state" ) filter="STATE"; + else + if ( tokens[1] == "vcpu" ) filter="VCPU"; + } + for ( int line=0; line < this->configlines; line++ ) { + // form the RCONFIG command to send to Archon (without logging each command) + // + std::stringstream cmd; + cmd.str(""); cmd << QUIET + << "RCONFIG" + << std::uppercase << std::setfill('0') << std::setw(4) << std::hex << line; + std::string reply; + error = this->archon_cmd(cmd.str(), reply); // send RCONFIG command here + if ( reply.find(filter)!=std::string::npos ) { + message.str(""); message << "RCONFIG" + << std::uppercase << std::setfill('0') << std::setw(4) << std::hex << line + << ": " << strip_newline(reply); + logwrite( function, message.str() ); + } + } + error = NO_ERROR; + } else if (testname == "logwconfig") { + // ---------------------------------------------------- + // logwconfig [ ] + // set/get state of logwconfig, to optionally log WCONFIG commands + // ---------------------------------------------------- + if ( tokens.size() > 1 ) { + if ( tokens[1] == "true" ) this->logwconfig=true; + else + if ( tokens[1] == "false" ) this->logwconfig=false; + } + retstring = ( this->logwconfig ? "true" : "false" ); + message.str(""); message << "logwconfig " << retstring; + logwrite( function, message.str() ); + error = NO_ERROR; } else { // ---------------------------------------------------- // invalid test name diff --git a/camerad/archon.h b/camerad/archon.h index a6604f46..dd976902 100644 --- a/camerad/archon.h +++ b/camerad/archon.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "utilities.h" #include "common.h" @@ -57,6 +58,9 @@ #define REV_VCPU std::string("1.0.784") namespace Archon { + + constexpr std::string_view QUIET = "quiet"; // allows sending commands without logging + // Archon hardware-based constants. // These shouldn't change unless there is a significant hardware change. // @@ -101,6 +105,8 @@ namespace Archon { int msgref; //!< Archon message reference identifier, matches reply to command bool abort; int taplines; + int configlines; //!< number of configuration lines + bool logwconfig; //!< optionally log WCONFIG commands std::vector gain; //!< digital CDS gain (from TAPLINE definition) std::vector offset; //!< digital CDS offset (from TAPLINE definition) bool modeselected; //!< true if a valid mode has been selected, false otherwise @@ -172,9 +178,9 @@ namespace Archon { long native(const std::string &cmd); - long archon_cmd(const std::string &cmd); + long archon_cmd(std::string cmd); - long archon_cmd(const std::string &cmd, std::string &reply); + long archon_cmd(std::string cmd, std::string &reply); long read_parameter(const std::string ¶mname, std::string &valstring); diff --git a/common/common.h b/common/common.h index c987a17d..974ed3cd 100644 --- a/common/common.h +++ b/common/common.h @@ -16,11 +16,12 @@ #include "logentry.h" -const long NOTHING = -1; -const long NO_ERROR = 0; -const long ERROR = 1; -const long BUSY = 2; -const long TIMEOUT = 3; +constexpr long NOTHING = -1; +constexpr long NO_ERROR = 0; +constexpr long ERROR = 1; +constexpr long BUSY = 2; +constexpr long TIMEOUT = 3; +constexpr long HELP = 4; namespace Common { /**************** Common::FitsKeys ******************************************/ diff --git a/utils/utilities.cpp b/utils/utilities.cpp index 042cdcba..c1959dac 100644 --- a/utils/utilities.cpp +++ b/utils/utilities.cpp @@ -842,6 +842,23 @@ const std::string &tchar(const std::string &str) { /***** tchar ****************************************************************/ +/***** strip_newline **********************************************************/ +/** + * @brief strip newline and cr chars from a string + * @details this makes a local copy of the original string which is not changed + * @param[in] str_in reference to input string + * @return string + * + */ +const std::string strip_newline( const std::string &str_in ) { + std::string str = str_in; + str.erase( std::remove( str.begin(), str.end(), '\n' ), str.end() ); + str.erase( std::remove( str.begin(), str.end(), '\r' ), str.end() ); + return str; +} +/***** strip_newline **********************************************************/ + + /***** strip_control_characters *********************************************/ /** * @brief strip all leading and trailing control chars from a string diff --git a/utils/utilities.h b/utils/utilities.h index 96a4bf10..065b0dfe 100644 --- a/utils/utilities.h +++ b/utils/utilities.h @@ -110,6 +110,8 @@ bool has_write_permission(const std::filesystem::path &filename); const std::string &tchar(const std::string &str); +const std::string strip_newline( const std::string &str_in ); + std::string strip_control_characters(const std::string &str); bool starts_with(const std::string &str, std::string_view prefix);