//# FlagAgentDisplay.cc: This file contains the implementation of the FlagAgentDisplay class. //# //# CASA - Common Astronomy Software Applications (http://casa.nrao.edu/) //# Copyright (C) Associated Universities, Inc. Washington DC, USA 2011, All rights reserved. //# Copyright (C) European Southern Observatory, 2011, All rights reserved. //# //# This library is free software; you can redistribute it and/or //# modify it under the terms of the GNU Lesser General Public //# License as published by the Free software Foundation; either //# version 2.1 of the License, or (at your option) any later version. //# //# This library is distributed in the hope that it will be useful, //# but WITHOUT ANY WARRANTY, without even the implied warranty of //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //# Lesser General Public License for more details. //# //# You should have received a copy of the GNU Lesser General Public //# License along with this library; if not, write to the Free Software //# Foundation, Inc., 59 Temple Place, Suite 330, Boston, //# MA 02111-1307 USA //# $Id: rurvashi 28 Nov 2011$ #include <array> #include <thread> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <casatools/Config/State.h> #ifdef USE_GRPC #include <flagging/Flagging/grpcFlagAgentDisplay.h> #include <grpc++/grpc++.h> #include "shutdown.grpc.pb.h" #include "ping.grpc.pb.h" using namespace casacore; namespace casa { //# NAMESPACE CASA - BEGIN constexpr int FlagAgentDisplay::TIMEOUT; // https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring // C++ is so ridiculous... trim from start (in place) static inline void ltrim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); } // trim from end (in place) static inline void rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); } // trim from both ends (in place) static inline void trim(std::string &s) { ltrim(s); rtrim(s); } static bool isdir( const char *path ) { struct stat statbuf; int err = stat(path, &statbuf); if ( err == -1 ) return false; if ( S_ISDIR(statbuf.st_mode) ) return true; return false; } static std::string trim_trailing_slash( const char *str ) { char *temp = strdup(str); for ( int off = strlen(str) - 1; off >= 0; --off ) { if ( temp[off] == '/' ) temp[off] = '\0'; else break; } std::string result = temp; free(temp); return result; } grpcFlagAgentState::grpcFlagAgentState( ) : userChoice_p("Continue"), userFixA1_p(""), userFixA2_p(""), skipScan_p(-1), skipSpw_p(-1), skipField_p(-1), antenna1_p(""),antenna2_p(""), input_received(false) { } ::grpc::Status grpcFlagAgentResponse::button( ::grpc::ServerContext *context, const ::rpc::gui::ButtonEvent *req, ::google::protobuf::Empty* ) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "plotserver '" << req->name( ) << "' button event received " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } // Manage buttons from: Data Plot Window if ( req->name( ) == "NextBaseline" || req->name( ) == "PrevBaseline" || req->name( ) == "NextScan" || req->name( ) == "NextField" || req->name( ) == "NextSpw" || req->name( ) == "StopDisplay" || req->name( ) == "Quit") { std::lock_guard<std::mutex> lock(state->set_values); state->userChoice_p = req->name( ); // set input state->input_received = true; // set whenever state object is modified if ( state->input_needed ) { state->input_needed = false; // prevent setting future twice state->output.set_value(true); // signal controlling thread that wait is over } // Manage buttons from: Report Plot Window } else if ( req->name( ) == "Next" || req->name( ) == "Prev" || req->name( ) == "Quit") { std::lock_guard<std::mutex> lock(state->set_values); state->userChoice_p = req->name( ); // set input state->input_received = true; // set whenever state object is modified if ( state->input_needed ) { state->input_needed = false; // prevent setting future twice state->output.set_value(true); // signal controlling thread that wait is over } } return grpc::Status::OK; } ::grpc::Status grpcFlagAgentResponse::check( ::grpc::ServerContext *context, const ::rpc::gui::CheckEvent *req, ::google::protobuf::Empty* ) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "plotserver " << req->name( ) << " [" << req->state( ) << "] check event received " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } // Manage check boxes from: Data Plot Window if ( req->name( ) == "FixAntenna1" ) { std::lock_guard<std::mutex> lock(state->set_values); state->userFixA1_p = (req->state( ) == 0) ? "" : state->antenna1_p; } else if ( req->name( ) == "FixAntenna2" ) { std::lock_guard<std::mutex> lock(state->set_values); state->userFixA2_p = (req->state( ) == 0 ) ? "" : state->antenna2_p; } return grpc::Status::OK; } ::grpc::Status grpcFlagAgentResponse::radio( ::grpc::ServerContext *context, const ::rpc::gui::RadioEvent *req, ::google::protobuf::Empty* ) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "plotserver " << req->name( ) << " [" << req->state( ) << "] radio event received " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } return grpc::Status::OK; } ::grpc::Status grpcFlagAgentResponse::linetext( ::grpc::ServerContext *context, const ::rpc::gui::LineTextEvent *req, ::google::protobuf::Empty* ) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "plotserver " << req->name( ) << " [" << req->text( ) << "] linetext event received " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } return grpc::Status::OK; } ::grpc::Status grpcFlagAgentResponse::slidevalue( ::grpc::ServerContext *context, const ::rpc::gui::SlideValueEvent *req, ::google::protobuf::Empty* ) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "plotserver " << req->name( ) << "[" << req->value( ) << "] slidevalue event received " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } return grpc::Status::OK; } ::grpc::Status grpcFlagAgentResponse::exiting( ::grpc::ServerContext *context, const ::google::protobuf::Empty*, ::google::protobuf::Empty* ) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "plotserver exiting event received " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } return grpc::Status::OK; } ::grpc::Status grpcFlagAgentResponse::closing( ::grpc::ServerContext *context, const ::rpc::gui::ClosingEvent *req, ::google::protobuf::Empty* ){ static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "plotserver closing event received " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } std::lock_guard<std::mutex> lock(state->set_values); state->userChoice_p = "Quit"; // user stopped GUI state->input_received = true; // set whenever state object is modified if ( state->input_needed ) { state->input_needed = false; // prevent setting future twice state->output.set_value(true); // signal controlling thread that wait is over } return grpc::Status::OK; } std::string FlagAgentDisplay::plotter_t::get_casaplotserver_path( ) const { static std::string python_path = casatools::get_state( ).pythonPath( ); //*** python3 -m casaplotserver --app-path char python_cmd[python_path.size( ) + 35]; sprintf( python_cmd, "%s -m casaplotserver --app-path", python_path.c_str( ) ); std::array<char, 512> buffer; std::string result; std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(python_cmd, "r"), pclose); if ( ! pipe ) return std::string( ); //*** failed to start python while ( fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr ) { result += buffer.data(); } trim(result); if ( result.size( ) == 0 ) return std::string( ); return result; } std::string FlagAgentDisplay::plotter_t::get_fifo( ) const { static const char *env_tmpdir = getenv("TMPDIR"); static std::string fifo_template = trim_trailing_slash(env_tmpdir && isdir(env_tmpdir) ? env_tmpdir : P_tmpdir) + "/cps-XXXXXXXXXX"; static int fifo_template_size = fifo_template.size( ); char fifo_path[fifo_template_size+1]; strncpy( fifo_path, fifo_template.c_str( ), fifo_template_size ); fifo_path[fifo_template_size] = '\0'; int fd = mkstemp(fifo_path); if ( fd == -1 ) throw std::runtime_error("mkstemp failed..."); close( fd ); unlink(fifo_path); mkfifo( fifo_path, 0666 ); return fifo_path; } bool FlagAgentDisplay::plotter_t::start_response_manager( ) { static const auto debug = getenv("GRPC_DEBUG"); //*** //*** set up a default address (grpc picks port) and address buffers //*** char address_buf[100]; constexpr char address_template[] = "0.0.0.0:%d"; snprintf(address_buf,sizeof(address_buf),address_template,0); std::string server_address(address_buf); int selected_port = 0; //*** //*** build grpc service //*** grpc::ServerBuilder builder; // Listen on the given address without any authentication mechanism. builder.AddListeningPort(server_address, grpc::InsecureServerCredentials(), &selected_port); // Register "service" as the instance through which we'll receive from // the plot server client. builder.RegisterService(response_svc.get( )); // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- // Launch server... response_server = builder.BuildAndStart( ); if ( selected_port > 0 ) { // if an available port can be found, selected_port is set to a value greater than zero snprintf(address_buf,sizeof(address_buf),address_template,selected_port); response_uri = address_buf; if ( debug ) { std::cerr << "flagagentdisplay response service available at " << response_uri << std::endl; fflush(stdout); } return true; } return false; } bool FlagAgentDisplay::plotter_t::launch(std::string plotserver_path) { static const auto debug = getenv("GRPC_DEBUG"); std::string fifo = get_fifo( ); if ( fifo.size( ) == 0 ) return false; // plot server will generate events (formerly DBus signals) // in response to GUI operations... if ( start_response_manager( ) == false ) return false; // here we start the viewer in a very basic manner... we do not bother // with all of the theatrics needed to daemonize the launched process // (see https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux) // it could be that this should be done in the future, but for now we // will adopt the simple... // casaplotserver --server=<FIFO-or-GRPC> --event-uri=<GRPC> [ --datapath=<PATH> --casalog=<PATH> ] int argc = 3; int logarg = argc; // if a log file is specfied it comes last... std::string log_path = casatools::get_state( ).logPath( ); if ( log_path.size( ) > 0 ) ++argc; char **arguments = (char**) malloc(sizeof(char*) * (argc + 1)); arguments[argc] = 0; arguments[0] = strdup(plotserver_path.c_str( )); arguments[1] = (char*) malloc(sizeof(char) * (fifo.size( ) + 12)); sprintf( arguments[1], "--server=%s", fifo.c_str( ) ); arguments[2] = (char*) malloc(sizeof(char) * (response_uri.size( ) + 18)); sprintf( arguments[2], "--event-uri=%s", response_uri.c_str( ) ); if ( log_path.size( ) > 0 ) { arguments[logarg] = (char*) malloc(sizeof(char) * (log_path.size( ) + 17)); sprintf( arguments[logarg], "--logfile=%s", log_path.c_str( ) ); } if ( debug ) { std::cerr << "forking plotserver process: "; for (int i=0; i < argc; ++i) std::cerr << arguments[i] << " "; std::cerr << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } pid_t newpid = fork( ); if ( newpid == 0 ) { if ( debug ) { std::cerr << "execing plotserver process: "; for (int i=0; i < argc; ++i) std::cerr << arguments[i] << " "; std::cerr << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } execvp( arguments[0], (char* const*) arguments ); perror( "FlagAgentDisplay::plotter_t::launch(...) child process exec failed" ); exit(1); } for ( int i=0; i < argc; ++i ) free(arguments[i]); free(arguments); if ( newpid == -1 ) { perror( "FlagAgentDisplay::plotter_t::launch(...) child process fork failed" ); return false; } // perform a health check, after a delay... int status; sleep(2); pid_t w = waitpid( newpid, &status, WUNTRACED | WCONTINUED | WNOHANG ); if ( w == -1 ){ if ( debug ) { std::cerr << "plotserver process failed " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } // waitpid failed return false; } else if ( w != 0 ) { if ( debug ) { std::cerr << "plotserver process died " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } // process exited if ( WIFEXITED(status) ) { printf("exited, status=%d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("killed by signal %d\n", WTERMSIG(status)); } else if (WIFSTOPPED(status)) { printf("stopped by signal %d\n", WSTOPSIG(status)); } return false; } if ( debug ) { std::cerr << "fetching casaplotserver uri from " << fifo << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } char buffer[512]; std::string uri_buffer; FILE *fp = fopen(fifo.c_str( ), "r"); while ( fgets( buffer, sizeof(buffer), fp ) ) { uri_buffer = uri_buffer + buffer; } fclose(fp); trim(uri_buffer); // validate viewer uri... if ( ! std::regex_match( uri_buffer, std::regex("^([0-9]+\\.){3}[0-9]+:[0-9]+$") ) ) { //rework of regex required for IPv6... if ( debug ) { std::cerr << "bad casaplotserver uri " << uri_buffer << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } return false; } if ( debug ) { std::cerr << "received casaplotserver uri: " << uri_buffer << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } plot_uri = uri_buffer; pid = newpid; if ( debug ) { std::cerr << "creating plotserver stub: " << plot_uri << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } plot = rpc::gui::plotserver::NewStub( grpc::CreateChannel( plot_uri, grpc::InsecureChannelCredentials( ) ) ); grpc::ClientContext context; ::google::protobuf::Empty resp; ::google::protobuf::Empty msg; auto ping = casatools::rpc::Ping::NewStub( grpc::CreateChannel( plot_uri, grpc::InsecureChannelCredentials( ) ) ); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); ::grpc::Status st = ping->now( &context, msg, &resp ); bool ping_result = st.ok( ); if ( debug ) { std::cerr << "ping result: " << (ping_result ? "OK" : "FAIL")<< " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } plot_started_ = true; return true; } FlagAgentDisplay::plotter_t::plotter_t(std::shared_ptr<grpcFlagAgentState> state) : active_(false), response_svc(new grpcFlagAgentResponse(state)), plot_started_(false) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "attempting to start plotserver process " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } std::string ps_path = get_casaplotserver_path( ); if ( ps_path.size() > 0 ) { // sanity check on casaplotserver path... struct stat statbuf; if ( stat( ps_path.c_str( ), &statbuf ) < 0 ) { // file (or dir) does not exist... e.g. // >>>>>>registry available at 0.0.0.0:40939 // stopping registry<<<<<< if ( debug ) { std::cerr << "could not find casaplotserver executable " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } } else { if ( launch(ps_path) ) { active_ = true; } else if ( debug ) { std::cerr << "could not find casaplotserver executable " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } } } else if ( debug ) { std::cerr << "could not find casaplotserver executable " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } } FlagAgentDisplay::FlagAgentDisplay(FlagDataHandler *dh, Record config, Bool writePrivateFlagCube): FlagAgentBase(dh,config,ANTENNA_PAIRS_INTERACTIVE,writePrivateFlagCube), gui_state(new grpcFlagAgentState), dataplotter_p(NULL),reportplotter_p(NULL), pause_p(false), fieldId_p(-1), fieldName_p(""), scanStart_p(-1), scanEnd_p(-1), spwId_p(-1), nPolarizations_p(1), freqList_p(Vector<Double>()), dataDisplay_p(false), reportDisplay_p(false),reportFormat_p("screen"), stopAndExit_p(false),/* reportReturn_p(false),*/ showBandpass_p(false) { // Parse parameters and set base variables. setAgentParameters(config); // Request loading polarization map to FlagDataHandler flagDataHandler_p->setMapPolarizations(true); // Make the list of colours (these are almost all predefined ones for Qt. // Can add more later, based on RGB values. plotColours_p.resize(13); plotColours_p[0]="blue"; plotColours_p[1]="red"; plotColours_p[2]="green"; plotColours_p[3]="cyan"; plotColours_p[4]="darkGray"; plotColours_p[5]="magenta"; plotColours_p[6]="yellow"; plotColours_p[7]="darkBlue"; plotColours_p[8]="darkRed"; plotColours_p[9]="darkGreen"; plotColours_p[10]="darkCyan"; plotColours_p[11]="black"; plotColours_p[12]="darkMagenta"; } bool FlagAgentDisplay::done( std::shared_ptr<FlagAgentDisplay::plotter_t> plotter ) { // send shutdown message to plotserver... static const auto debug = getenv("GRPC_DEBUG"); if ( plotter && plotter->active( ) && plotter->plot_uri.size( ) > 0) { grpc::ClientContext context; ::google::protobuf::Empty req; ::google::protobuf::Empty resp; if ( debug ) { std::cerr << "attempting to shutdown plotserver [" << plotter->plot_uri << "] " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } auto shutdown = casatools::rpc::Shutdown::NewStub( grpc::CreateChannel( plotter->plot_uri, grpc::InsecureChannelCredentials( ) ) ); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); shutdown->now( &context, req, &resp ); // wait on plotserver to exit... int status; pid_t w = waitpid( plotter->pid, &status, WUNTRACED | WCONTINUED ); if ( w == -1 ){ if ( debug ) { std::cerr << "plotserver waitpid failed " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } // waitpid failed return false; } else if ( w == 0 ) { if ( debug ) { std::cerr << "plotserver process not found " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } return false; } else { if ( debug ) { std::cerr << "plotserver process (" << w << ") exited, status fetched " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } plotter->pid = 0; return true; } } plotter.reset( ); return true; } int FlagAgentDisplay::create_panel( std::shared_ptr<plotter_t> plot, int parent, bool new_row ) { static const auto debug = getenv("GRPC_DEBUG"); string zoomloc=""; string legendloc="bottom"; grpc::ClientContext context; ::rpc::gui::Id result; ::rpc::gui::NewPanel panel; panel.set_title(""); panel.set_xlabel(""); panel.set_ylabel(""); panel.set_window_title(""); *panel.mutable_size( ) = { }; panel.set_legend(legendloc); panel.set_zoom(zoomloc); panel.set_with_panel(parent); panel.set_new_row(new_row); panel.set_hidden(false); if ( debug ) { std::cerr << (plot->active( ) ? "FlagAgentDisplay creating panel " : "FlagAgentDisplay create ERROR plot not active ") << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! plot->active( ) ) return -1; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); ::grpc::Status st = plot->plot->panel(&context,panel,&result); bool stat = st.ok( ); if ( debug ) { std::cerr << "panel creation " << (stat ? "SUCCEEDED " : "FAILED ") << (stat ? result.id( ) : -1) << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } return stat ? result.id( ) : -1; } void FlagAgentDisplay::erase( std::shared_ptr<plotter_t> plot, int panel ) { static const auto debug = getenv("GRPC_DEBUG"); grpc::ClientContext context; ::google::protobuf::Empty resp; ::rpc::gui::Id id; if ( debug ) { std::cerr << (plot->active( ) ? "FlagAgentDisplay erase " : "FlagAgentDisplay erase ERROR plot not active ") << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! plot->active( ) ) return; id.set_id(panel); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); plot->plot->erase(&context,id,&resp); } void FlagAgentDisplay::setlabel( std::shared_ptr<plotter_t> plot, int panel, std::string xlabel, std::string ylabel, std::string title ) { static const auto debug = getenv("GRPC_DEBUG"); grpc::ClientContext context; ::google::protobuf::Empty resp; ::rpc::gui::Label label; if ( debug ) { std::cerr << (plot->active( ) ? "FlagAgentDisplay setlabel " : "FlagAgentDisplay setlabel ERROR plot not active ") << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! plot->active( ) ) return; label.mutable_panel( )->set_id(panel); label.set_xlabel(xlabel); label.set_ylabel(ylabel); label.set_title(title); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); plot->plot->setlabel(&context,label,&resp); } int FlagAgentDisplay::create_dock( std::shared_ptr<plotter_t> plot, int panel, std::string xml ) { static const auto debug = getenv("GRPC_DEBUG"); grpc::ClientContext context; ::rpc::gui::DockSpec spec; ::rpc::gui::Id result; if ( debug ) { std::cerr << (plot->active( ) ? "FlagAgentDisplay create_dock " : "FlagAgentDisplay create_dock ERROR plot not active ") << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! plot->active( ) ) return -1; spec.set_file_or_xml(xml); spec.set_loc("bottom"); spec.add_dockable("top"); spec.mutable_panel( )->set_id(panel); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); plot->plot->loaddock(&context,spec,&result); return result.id( ); } int FlagAgentDisplay::raster( std::shared_ptr<plotter_t> plot, int panel, const std::vector<float> &data, ssize_t sizex, ssize_t sizey ) { static const auto debug = getenv("GRPC_DEBUG"); grpc::ClientContext context; ::rpc::gui::Id result; ::rpc::gui::NewRaster raster; if ( debug ) { std::cerr << (plot->active( ) ? "FlagAgentDisplay raster " : "FlagAgentDisplay raster ERROR plot not active ") << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! plot->active( ) ) return -1; raster.mutable_panel( )->set_id(panel); *raster.mutable_matrix( ) = { data.begin( ), data.end( ) }; raster.set_sizex(sizex); raster.set_sizey(sizey); raster.set_colormap("Hot Metal 1"); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); plot->plot->raster(&context,raster,&result); return result.id( ); } int FlagAgentDisplay::line( std::shared_ptr<plotter_t> plot, int panel, const std::vector<float> &xdata, const std::vector<float> &ydata, std::string color, std::string label ) { static const auto debug = getenv("GRPC_DEBUG"); grpc::ClientContext context; ::rpc::gui::Id result; ::rpc::gui::NewLine line; if ( debug ) { std::cerr << (plot->active( ) ? "FlagAgentDisplay line " : "FlagAgentDisplay line ERROR plot not active ") << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! plot->active( ) ) return -1; line.mutable_panel( )->set_id(panel); *line.mutable_x( ) = { xdata.begin( ), xdata.end( ) }; *line.mutable_y( ) = { ydata.begin( ), ydata.end( ) }; line.set_color(color); line.set_label(label); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); plot->plot->line(&context,line,&result); return result.id( ); } int FlagAgentDisplay::scatter( std::shared_ptr<plotter_t> plot, int panel, const std::vector<float> &xdata, const std::vector<float> &ydata, std::string color, std::string label, std::string symbol, int symbol_size, int dot_size ) { static const auto debug = getenv("GRPC_DEBUG"); grpc::ClientContext context; ::rpc::gui::Id result; ::rpc::gui::NewScatter scatter; if ( debug ) { std::cerr << (plot->active( ) ? "FlagAgentDisplay scatter " : "FlagAgentDisplay scatter ERROR plot not active ") << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! plot->active( ) ) return -1; scatter.mutable_panel( )->set_id(panel); *scatter.mutable_x( ) = { xdata.begin( ), xdata.end( ) }; *scatter.mutable_y( ) = { ydata.begin( ), ydata.end( ) }; scatter.set_color(color); scatter.set_label(label); scatter.set_symbol(symbol); scatter.set_symbol_size(symbol_size); scatter.set_dot_size(dot_size); auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(TIMEOUT); context.set_deadline(deadline); plot->plot->scatter(&context,scatter,&result); return result.id( ); } FlagAgentDisplay::~FlagAgentDisplay() { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << "FlagAgentDisplay dtor " << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } // Compiler automagically calls FlagAgentBase::~FlagAgentBase() if ( dataplotter_p != NULL ) { done(dataplotter_p); dataplotter_p=NULL; } if ( reportplotter_p != NULL ) { done(reportplotter_p); reportplotter_p=NULL; } } void FlagAgentDisplay::setAgentParameters(Record config) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); int exists; exists = config.fieldNumber ("pause"); if (exists >= 0) { if( config.type(exists) != TpBool ) { throw( AipsError ( "Parameter 'pause' must be of type 'bool'" ) ); } pause_p = config.asBool("pause"); } else { pause_p = true; } *logger_p << LogIO::NORMAL << " pause is " << pause_p << LogIO::POST; exists = config.fieldNumber ("datadisplay"); if (exists >= 0) { if( config.type(exists) != TpBool ) { throw( AipsError ( "Parameter 'datadisplay' must be of type 'bool'" ) ); } dataDisplay_p = config.asBool("datadisplay"); } else { dataDisplay_p = false; } *logger_p << LogIO::NORMAL << " datadisplay is " << dataDisplay_p << LogIO::POST; exists = config.fieldNumber ("reportdisplay"); if (exists >= 0) { if( config.type(exists) != TpBool ) { throw( AipsError ( "Parameter 'reportdisplay' must be of type 'bool'" ) ); } reportDisplay_p = config.asBool("reportdisplay"); } else { reportDisplay_p = false; } *logger_p << LogIO::NORMAL << " reportdisplay is " << reportDisplay_p << LogIO::POST; exists = config.fieldNumber ("format"); if (exists >= 0) { if( config.type(exists) != TpString ) { throw( AipsError ( "Parameter 'format' must be of type 'bool'" ) ); } reportFormat_p = config.asString("format"); if( reportFormat_p != "screen" && reportFormat_p != "file") { throw( AipsError( "Unsupported report format : " + reportFormat_p + ". Supported formats are 'screen' and 'file'") ); } } else { reportFormat_p = String("screen"); } *logger_p << LogIO::NORMAL << " format is " << reportFormat_p << LogIO::POST; } void FlagAgentDisplay::preProcessBuffer(const vi::VisBuffer2 &visBuffer) { getChunkInfo(visBuffer); return; } void FlagAgentDisplay::iterateAntennaPairsInteractive(antennaPairMap *antennaPairMap_ptr) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); // Check if the visibility expression is suitable for this spw if (!checkVisExpression(flagDataHandler_p->getPolarizationMap())) return; // Iterate through antenna pair map std::pair<Int,Int> antennaPair; antennaPairMapIterator myAntennaPairMapIterator; bool stepback=false; for (myAntennaPairMapIterator=antennaPairMap_ptr->begin(); myAntennaPairMapIterator != antennaPairMap_ptr->end(); ++myAntennaPairMapIterator) { // Check whether to skip the rest of this chunk or not if(gui_state->skipSpw_p != -1) { if(gui_state->skipSpw_p == spwId_p) {dataDisplay_p=false;} // Skip the rest of this SPW else { std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->skipSpw_p = -1; dataDisplay_p=true; } // Reached next SPW. Reset state } if(gui_state->skipField_p != -1) { if(gui_state->skipField_p == fieldId_p) {dataDisplay_p=false;} // Skip the rest of this Field else { std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->skipField_p = -1; dataDisplay_p=true; } // Reached next Field. Reset state } if(gui_state->skipScan_p != -1) { if(gui_state->skipScan_p == scanEnd_p) {dataDisplay_p=false;} // Skip the rest of this Scan else { std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->skipScan_p = -1; dataDisplay_p=true; } // Reached next Scan. Reset state } // Display this baseline if(dataDisplay_p) { // If choice from previous plot was to go backwards in baseline. if(stepback) { // Go to previous baseline (decrement by 2) if( myAntennaPairMapIterator != antennaPairMap_ptr->begin() ) -- myAntennaPairMapIterator; if( myAntennaPairMapIterator != antennaPairMap_ptr->begin() ) -- myAntennaPairMapIterator; // If antenna constraints exist, keep going back until first match is found. // If not found, stay on current baseline (continue) if( gui_state->userFixA1_p != "" || gui_state->userFixA2_p != "" ) { antennaPairMapIterator tempIterator; bool found=false; for(tempIterator = myAntennaPairMapIterator; tempIterator != antennaPairMap_ptr->begin() ; --tempIterator ) { if( ! skipBaseline(tempIterator->first) ) {found=true; break;} } if(found) // Jump to this antenna pair { myAntennaPairMapIterator = tempIterator; } else { *logger_p << "No Previous baseline in this chunk with Ant1 : " << ( (gui_state->userFixA1_p != "") ? gui_state->userFixA1_p : "any" ) << " and Ant2 : " << ( (gui_state->userFixA2_p != "") ? gui_state->userFixA2_p : "any" ) << LogIO::POST; // Stay on current baseline if( myAntennaPairMapIterator != antennaPairMap_ptr->end() ) ++myAntennaPairMapIterator; } } // Reset state stepback=false; } // Get antenna pair from map antennaPair = myAntennaPairMapIterator->first; // Check whether or not to display this baseline (for going in the forward direction) if( skipBaseline(antennaPair) ) continue; // Process antenna pair processAntennaPair(antennaPair.first,antennaPair.second); // If Plot window is visible, and, if asked for, get and react to user-choices. if(pause_p==true) { // Wait for User Input getUserInput( ); // Fills in userChoice_p. userfix // React to user-input if(gui_state->userChoice_p=="Quit") { dataDisplay_p = false; stopAndExit_p = true; *logger_p << "Exiting flagger" << LogIO::POST; if ( dataplotter_p != NULL ) { done(dataplotter_p); dataplotter_p=NULL; } flagDataHandler_p->stopIteration(); return ; } else if(gui_state->userChoice_p=="StopDisplay") { dataDisplay_p = false; *logger_p << "Stopping display. Continuing flagging." << LogIO::POST; if ( dataplotter_p != NULL ) { done(dataplotter_p); dataplotter_p=NULL; } } else if(gui_state->userChoice_p=="PrevBaseline") { if( myAntennaPairMapIterator==antennaPairMap_ptr->begin() ) *logger_p << "Already on first baseline..." << LogIO::POST; stepback=true; } else if(gui_state->userChoice_p=="NextScan") { //*logger_p << "Next Scan " << LogIO::POST; std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->skipScan_p = scanEnd_p; } else if(gui_state->userChoice_p=="NextSpw") { //*logger_p << "Next SPW " << LogIO::POST; std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->skipSpw_p = spwId_p; } else if(gui_state->userChoice_p=="NextField") { //*logger_p << "Next Field " << LogIO::POST; std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->skipField_p = fieldId_p; } else if(gui_state->userChoice_p=="Continue") { //*logger_p << "Next chunk " << LogIO::POST; // Right now, a chunk is one baseline ! return; } }// end if pause=true }// if dataDisplay_p }// end antennaMapIterator return; }// end iterateAntennaPairsInteractive Bool FlagAgentDisplay::skipBaseline(std::pair<Int,Int> antennaPair) { std::string antenna1Name = flagDataHandler_p->antennaNames_p->operator()(antennaPair.first); std::string antenna2Name = flagDataHandler_p->antennaNames_p->operator()(antennaPair.second); // if(userFixA2_p != "") cout << "*********** userfixa2 : " << userFixA2_p << " thisant : " << antenna1Name << " && " << antenna2Name << LogIO::POST; return ( (gui_state->userFixA1_p != "" && gui_state->userFixA1_p != antenna1Name) || (gui_state->userFixA2_p != "" && gui_state->userFixA2_p != antenna2Name) ) ; } bool FlagAgentDisplay::computeAntennaPairFlags(const vi::VisBuffer2 &visBuffer, VisMapper &visibilities,FlagMapper &flags,Int antenna1,Int antenna2, vector<uInt> &/*rows*/) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); // Gather shapes IPosition flagCubeShape = visibilities.shape(); uInt nChannels = flagCubeShape(0); uInt nTimes = flagCubeShape(1); // Read antenna names for the current baseline String antenna1Name = flagDataHandler_p->antennaNames_p->operator()(antenna1); String antenna2Name = flagDataHandler_p->antennaNames_p->operator()(antenna2); String baselineName = antenna1Name + "&&" + antenna2Name; { std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->antenna1_p = antenna1Name; gui_state->antenna2_p = antenna2Name; } String scanRange = (scanStart_p!=scanEnd_p)?String::toString(scanStart_p)+"~"+String::toString(scanEnd_p) : String::toString(scanStart_p); String spwName = String::toString(visBuffer.spectralWindows()(0)); // Get Frequency List freqList_p.resize(nChannels); for(uInt ch=0;ch<nChannels;ch++) freqList_p[ch]=(Double)ch; /* // Read current Field name, SPW id, and correlation string from visBuffer Info. uInt fieldId_p = visBuffer.fieldId(); String fieldName = visBuffer.msColumns().field().name().getColumn()[fieldId_p]; String spwName = String::toString(visBuffer.spectralWindow()); Int scanstart = visBuffer.scan()[0]; int scanend = visBuffer.scan()[ (visBuffer.scan().nelements())-1 ]; String scanRange = (scanstart!=scanend)?String::toString(scanstart)+"~"+String::toString(scanend) : String::toString(scanstart); */ // Get Polarization Maps /* Vector<uInt> polarizations = flags.getSelectedCorrelations(); nPolarizations_p = polarizations.size(); polarizationIndexMap *polMap = flagDataHandler_p->getPolarizationIndexMap(); Vector<String> corrTypes(nPolarizations_p); for(uInt pol=0;pol<nPolarizations_p;pol++) corrTypes[pol] = (*polMap)[polarizations[pol]]; */ // Get Polarization Maps // jagonzal: Migrated to new implementation of multiple expressions handling vector<string> corrTypes = visibilities.getSelectedCorrelationStrings(); nPolarizations_p = corrTypes.size(); ///cout << "Selected Correlations : " << polarizations << LogIO::POST; // Print where we are... // *logger_p << LogIO::NORMAL << " Baseline : " << baselineName << " Field : " << fieldName_p << " Spw : " << spwName << " nChan : " << nChannels << " nPol : " << nPolarizations_p << " nTime : " << nTimes << LogIO::POST; // Build the Plot Window for the first time if(dataDisplay_p && dataplotter_p==NULL) buildDataPlotWindow(); // Adding this block of code as a fix for CAS-4052. // J.G. : If you find a better way to do this, please change this... if(dataDisplay_p==true && dataplotter_p!=NULL) { Int nrows; if(showBandpass_p==true) nrows=3; else nrows=2; if(panels_p.size() != nPolarizations_p*nrows) { //cout << "Wrong number of panels !" << endl; done(dataplotter_p); dataplotter_p = NULL; buildDataPlotWindow(); } } // Initialize Plot Arrays and other vars Float runningsum=0, runningflag=0,runningpreflag=0; Vector<Float> vecflagdat(0), vecdispdat(0); Vector<Float> origspectrum(0), flagspectrum(0), precountspec(0), countspec(0); if(dataDisplay_p) { vecflagdat.resize(nChannels * nTimes); vecdispdat.resize(nChannels * nTimes); origspectrum.resize(nChannels); flagspectrum.resize(nChannels); precountspec.resize(nChannels); countspec.resize(nChannels); } if(dataDisplay_p) { // Make and send plots for each polarization for(int pl=0;pl<(int) nPolarizations_p;pl++) // Start Correlation Loop { runningsum=0; runningflag=0; runningpreflag=0; origspectrum=0.0; flagspectrum=0.0; precountspec=0.0; countspec=0.0; for(int ch=0;ch<(int) nChannels;ch++) // Start Channel Loop { for(uInt tm=0;tm<nTimes;tm++) // Start Time Loop { // UUU FOR TEST ONLY -- Later, enable additional ManualFlagAgent in the tFlagAgentDisplay /////if(ch>10 && ch<20) flags.applyFlag(ch,tm); vecdispdat( ch*nTimes + tm ) = visibilities(pl,ch,tm) * ( ! flags.getOriginalFlags(pl,ch,tm) ); vecflagdat( ch*nTimes + tm ) = visibilities(pl,ch,tm) * ( ! flags.getModifiedFlags(pl,ch,tm) ); origspectrum[ch] += visibilities(pl,ch,tm) * ( ! flags.getOriginalFlags(pl,ch,tm) ); flagspectrum[ch] += visibilities(pl,ch,tm) * ( ! flags.getModifiedFlags(pl,ch,tm) ); precountspec[ch] += ( ! flags.getOriginalFlags(pl,ch,tm) ); countspec[ch] += ( ! flags.getModifiedFlags(pl,ch,tm) ); runningsum += visibilities(pl,ch,tm); runningflag += (Float)(flags.getModifiedFlags(pl,ch,tm)); runningpreflag += (Float)(flags.getOriginalFlags(pl,ch,tm)); }// End Time Loop }//End Channel Loop // Make the Labels stringstream ostr1,ostr2; ostr1 << "(" << fieldId_p << ") " << fieldName_p << " [scan:" << scanRange << "]\n[spw:" << spwName << "] " << baselineName << " ( " << corrTypes[pl] << " )"; ostr2 << fixed; ostr2.precision(1); ostr2 << " flag:" << 100 * runningflag/(nChannels*nTimes) << "% (pre-flag:" << 100 * runningpreflag/(nChannels*nTimes) << "%)"; //*logger_p << "[" << corrTypes[pl] << "]:" << 100 * runningflag/(nChannels*nTimes) << "%(" << 100 * runningpreflag/(nChannels*nTimes) << "%) "; // Make the Before/After Raster Plots DisplayRaster(nChannels,nTimes,vecdispdat,panels_p[pl]); setlabel( dataplotter_p, panels_p[pl], " ", pl?" ":"Time", ostr1.str() ); DisplayRaster(nChannels,nTimes,vecflagdat,panels_p[pl+nPolarizations_p]); setlabel( dataplotter_p, panels_p[pl+nPolarizations_p], "Frequency", pl?" ":"Time", ostr2.str() ); if(showBandpass_p==true) { // Make the Before/After bandpass plots for(uInt ch=0;ch<nChannels;ch++) { if(precountspec[ch]==0) {origspectrum[ch]=0.0; precountspec[ch]=1.0;} if(countspec[ch]==0) {flagspectrum[ch]=0.0; countspec[ch]=1.0;} } origspectrum = (origspectrum/precountspec); flagspectrum = (flagspectrum/countspec); AlwaysAssert( freqList_p.nelements()==nChannels , AipsError); DisplayLine( nChannels, freqList_p, origspectrum, String("before:")+String(corrTypes[pl]), String("red"), false, panels_p[pl+(2*nPolarizations_p)] ); DisplayScatter( nChannels, freqList_p, flagspectrum, String("after:")+String(corrTypes[pl]), String("blue"), true, panels_p[pl+(2*nPolarizations_p)] ); //// TODO : Can I query the tfcrop agent for a "view" to overlay here. // If available, get a plot from the agents /* for (uInt fmeth=0; fmeth<flagmethods.nelements(); fmeth++) { if(flagmethods[fmeth]->getMonitorSpectrum(flagspectrum,pl,bs)) { // flagspectrum = log10(flagspectrum); DisplayLine(nChannels, freqlist_p, flagspectrum, flagmethods[fmeth]->methodName(), String("green"), true, panels_p[pl+(2*nPolarizations_p)].getInt()); } } */ }// end of if (showBandPass_p) }//End Correlation Loop //*logger_p << LogIO::POST; }// end if dataDisplay_p return false; }// end computeAntennaPairFlags //---------------------------------------------------------------------------------------------------------- void FlagAgentDisplay::getChunkInfo(const vi::VisBuffer2 &visBuffer) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); // Read current Field name, SPW id, and scan info. fieldId_p = visBuffer.fieldId()(0); fieldName_p = flagDataHandler_p->fieldNames_p->operator()(fieldId_p); spwId_p = visBuffer.spectralWindows()(0); scanStart_p = visBuffer.scan()[0]; scanEnd_p = visBuffer.scan()[ (visBuffer.scan().nelements())-1 ]; *logger_p << LogIO::NORMAL << "FlagAgentDisplay::" << __FUNCTION__ << " Field : " << fieldId_p << " , " << fieldName_p << " Spw : " << spwId_p << " Scan : " << scanStart_p << " : " << scanEnd_p << LogIO::POST; } //---------------------------------------------------------------------------------------------------------- FlagReport FlagAgentDisplay::getReport() { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); // FlagReport dispRep("plot",agentName_p); // Make empty list FlagReport dispRep("list"); /* // Make sample arrays/vectors Int N=10; Array<Float> sample( IPosition(2, N, N) ); sample = 0.0; sample( IPosition(2,N/2,N/2)) = 1.0; Vector<Float> xdata( N ), ydata( N ), error ( N ); for(Int i=0;i<N;i++) {xdata[i]=i;} ydata = 1.0; // (1) Make a raster plot. Only one set of data is allowed here. FlagReport subRep0 = FlagReport("plotraster",agentName_p,"example raster", "xaxis", "yaxis"); subRep0.addData(sample); // add 2D data // Add this raster FlagReport to the list. dispRep.addReport( subRep0 ); // (2) Make a line plot. Can give multiple lines to overlay on the same panel. FlagReport subRep1 = FlagReport("plotpoints",agentName_p,"example line", "xaxis", "yaxis"); subRep1.addData("line", xdata,ydata,"",Vector<Float>(),"line 1"); // add first set of line data ydata[N/2]=2.0; subRep1.addData("scatter", xdata,ydata,"",Vector<Float>(),"scatter 2"); // add second set of line data to overlay // Add this line FlagReport to the list dispRep.addReport( subRep1 ); // (3) Make an overlay of a line with errorbar, scatter with errorbar, and scatter with circle FlagReport subRep2 = FlagReport("plotpoints",agentName_p,"example line", "xaxis", "yaxis"); for(Int i=0;i<N;i++) {error[i]=i;} subRep2.addData("line", xdata,ydata,"bar",error,"line+bar"); // add first set of line data, with errorbars for(Int i=0;i<N;i++) {xdata[i] += 0.3; error[i]=i; ydata[i]+=2.0;} subRep2.addData("scatter", xdata, ydata,"bar",error,"scatter+bar"); // add second set of scatter data to overlay, with error bars for(Int i=0;i<N;i++) {xdata[i] += 0.3; error[i]=i*10; ydata[i]+=2.0;} subRep2.addData("scatter", xdata, ydata,"circle",error,"scatter+circle"); // add third set, scatter data with circles. // Add this line FlagReport to the list dispRep.addReport( subRep2 ); if( ! dispRep.verifyFields() ) cout << "Problem ! " << endl; */ return dispRep; }// end of getReport() //---------------------------------------------------------------------------------------------------------- // Go through the list of reports and make plots Bool FlagAgentDisplay::displayReports(FlagReport &combinedReport) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); if(reportDisplay_p && !stopAndExit_p) { Int nReports = combinedReport.nReport(); if( dataplotter_p != NULL ) { done(dataplotter_p); dataplotter_p = NULL; } if(nReports>0 && reportplotter_p==NULL) buildReportPlotWindow(); Bool stepback=false; for (Int reportid=0; reportid<nReports; reportid++) { String agentName, title, xlabel, ylabel; FlagReport oneRep; if(stepback==true) { // Go back until a valid one is found. Int previd=-1; for( Int rep=reportid-1; rep>=0; --rep) { Bool valid = combinedReport.accessReport(rep,oneRep); if(valid) { String type = oneRep.reportType(); if( type=="plotraster" || type=="plotpoints" ) { previd=rep; break; } } } if(previd==-1) { *logger_p << "Already on first plot" << LogIO::POST; reportid=0; } else { reportid = previd; } stepback=false; } Bool valid = combinedReport.accessReport(reportid,oneRep); if(valid) { oneRep.get( RecordFieldId("name") , agentName ); String type = oneRep.reportType(); if( type=="plotraster" || type=="plotpoints" ) { *logger_p << reportid << " : " << type << " with " << oneRep.nData() << " layer(s) " << " from " << agentName << LogIO::POST; if( type == "plotraster" ) { oneRep.get( RecordFieldId("title") , title ); oneRep.get( RecordFieldId("xlabel") , xlabel ); oneRep.get( RecordFieldId("ylabel") , ylabel ); Array<Float> data; oneRep.get( RecordFieldId("data"+String::toString(0)) , data ); std::vector<float> flatdata(data.begin(),data.end()); erase( reportplotter_p, report_panels_p[0] ); raster( reportplotter_p, report_panels_p[0], flatdata, data.shape()[0], data.shape()[1] ); setlabel( reportplotter_p, report_panels_p[0], xlabel, ylabel, title ); } else if( type == "plotpoints") { oneRep.get( RecordFieldId("title") , title ); oneRep.get( RecordFieldId("xlabel") , xlabel ); oneRep.get( RecordFieldId("ylabel") , ylabel ); erase( reportplotter_p, report_panels_p[0] ); Int ndata = oneRep.nData(); for(Int datid=0;datid<ndata;datid++) { Vector<Float> xdata,ydata,error; String legendlabel,plottype="line",errortype=""; oneRep.get( RecordFieldId("xdata"+String::toString(datid)) , xdata ); oneRep.get( RecordFieldId("ydata"+String::toString(datid)) , ydata ); oneRep.get( RecordFieldId("label"+String::toString(datid)) , legendlabel ); oneRep.get( RecordFieldId("plottype"+String::toString(datid)) , plottype ); ///reportplotter_p->line(dbus::af(xdata), dbus::af(ydata),(datid%2)?String("red"):String("blue"),legendlabel, report_panels_p[0].getInt()); if ( oneRep.isDefined( "error"+String::toString(datid) ) ) { oneRep.get( RecordFieldId("error"+String::toString(datid)) , error ); oneRep.get( RecordFieldId("errortype"+String::toString(datid)) , errortype ); } DisplayLineScatterError(reportplotter_p , plottype, xdata, ydata, errortype, error, legendlabel, plotColours_p[datid%plotColours_p.nelements()], report_panels_p[0] ); /// (datid%2)?String("red"):String("blue") }// end of for datid setlabel( reportplotter_p, report_panels_p[0], xlabel, ylabel, title ); }// end of plotpoints else { *logger_p << "NO Display for : " << reportid << " : " << agentName << LogIO::POST; } getReportUserInput(); // React to user-input if(gui_state->userChoice_p=="Quit") { dataDisplay_p = false; stopAndExit_p = true; //*logger_p << "Exiting flagger" << LogIO::POST; if( reportplotter_p != NULL ) { done(reportplotter_p); reportplotter_p = NULL; } return true; } else if(gui_state->userChoice_p=="Prev") { //*logger_p << "Prev Plot" << LogIO::POST; if( reportid==0 ) *logger_p << "Already on first plot..." << LogIO::POST; else --reportid; stepback=true; } else if(gui_state->userChoice_p=="Continue" || gui_state->userChoice_p=="Next") { //*logger_p << "Next Plot " << LogIO::POST; if( reportid==nReports-1 ) { *logger_p << "Already on last plot..." << LogIO::POST; --reportid; } } }// if valid plot type else { //*logger_p << "No plot for Report : " << reportid << LogIO::POST; *logger_p << reportid << " : No plot for report from " << agentName << LogIO::POST; } }// if valid plot record. else { *logger_p << LogIO::WARN << "Invalid Plot Record for : " << reportid << LogIO::POST; } }// end of for-report-in-combinedReport }// end of reportDisplay_p==true else { *logger_p << "Report Displays are turned OFF " << LogIO::POST; } return true; } // end of displayReports() /***********************************************************************/ /****************** Plot Functions ******************************/ /***********************************************************************/ // Note : By default, only two rows of plots are created. // The third row, for bandpass plots (before and after) are turned off // by the private variable 'showBandPass_p'. This can be later // enabled when we get per-chunk 'extra info' displays/reports from // the agents. Right now, it's redundant info, and takes up too much // space on small laptop screens. Bool FlagAgentDisplay::buildDataPlotWindow() { setDataLayout(); AlwaysAssert( dock_xml_p != NULL , AipsError ); dataplotter_p.reset(new plotter_t(gui_state)); Int nrows; if(showBandpass_p==true) nrows=3; else nrows=2; panels_p.resize(nPolarizations_p*nrows); string zoomloc=""; string legendloc="bottom"; // First row : Data with no flags panels_p[0] = create_panel(dataplotter_p,0,false); if(nPolarizations_p>1) { for(Int i=1;i<(int) nPolarizations_p;i++) { panels_p[i] = create_panel(dataplotter_p,panels_p[i-1],false); } } // Second row : Data with flags panels_p[nPolarizations_p] = create_panel(dataplotter_p,panels_p[0],true); if(nPolarizations_p>1) { for(int i=static_cast<int>(nPolarizations_p)+1;i<2*static_cast<int>(nPolarizations_p);i++) { panels_p[i] = create_panel(dataplotter_p,panels_p[i-1],false); } } // Third row : Average Bandpass if(showBandpass_p==true) { panels_p[2*nPolarizations_p] = create_panel(dataplotter_p,panels_p[0],true); if(nPolarizations_p>1) { for(int i=2* static_cast<int>(nPolarizations_p)+1;i<3*static_cast<int>(nPolarizations_p);i++) { panels_p[i] = create_panel(dataplotter_p,panels_p[i-1],false); } } }// if showbandpass /* // Misc panel panels_p[8] = dataplotter_p->panel( "BandPass", "Frequency", "Amp", "", std::vector<int>( ),legendloc,zoomloc, panels_p[3].getInt(),false,false); // Dummy panel panels_p[9] = dataplotter_p->panel( "---", "----", "---", "", std::vector<int>( ),legendloc,zoomloc, panels_p[7].getInt(),false,false); */ create_dock(dataplotter_p, panels_p[0],dock_xml_p); return true; }// end buildDataPlotWindow Bool FlagAgentDisplay::buildReportPlotWindow() { setReportLayout(); AlwaysAssert( report_dock_xml_p != NULL , AipsError ); reportplotter_p.reset(new plotter_t(gui_state)); report_panels_p.resize(1); string zoomloc=""; string legendloc="bottom"; report_panels_p[0] = create_panel(reportplotter_p,0,false); create_dock( reportplotter_p, report_panels_p[0], report_dock_xml_p ); return true; }// end buildReportPlotWindow void FlagAgentDisplay::getUserInput( ) { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << ( active( ) ? "FlagAgentDisplay getUserInput " : "FlagAgentDisplay getUserInput ERROR no active GUIs " ) << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! active( ) ) return; if ( gui_state->input_received ) { // GUI input received while this thread was preoccupied... std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->input_received = false; return; } else { std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->userChoice_p = "Continue"; gui_state->output = std::promise<bool>( ); gui_state->input_needed = true; } auto fut = gui_state->output.get_future(); fut.get( ); { std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->input_received = false; } } void FlagAgentDisplay :: getReportUserInput() { static const auto debug = getenv("GRPC_DEBUG"); if ( debug ) { std::cerr << ( active( ) ? "FlagAgentDisplay getReportUserInput " : "FlagAgentDisplay getReportUserInput ERROR no active GUIs " ) << " (process " << getpid( ) << ", thread " << std::this_thread::get_id() << ")" << std::endl; fflush(stderr); } if ( ! active( ) ) return; if ( gui_state->input_received ) { // GUI input received while this thread was preoccupied... std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->input_received = false; return; } else { std::lock_guard<std::mutex> lock(gui_state->set_values); gui_state->userChoice_p = "Continue"; gui_state->output = std::promise<bool>( ); gui_state->input_needed = true; } auto fut = gui_state->output.get_future(); fut.get( ); } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////// Plot functions ////////////////////////////////////////////////////////////////////////////////////////////////////// void FlagAgentDisplay::DisplayRaster(Int xdim, Int ydim, Vector<Float> &data, int frame) { if(data.nelements() != (uInt) xdim*ydim) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); *logger_p << LogIO::WARN << "Error in data XY dimensions. Not plotting" << LogIO::POST; return; } // cout << "panel id : " << frame << endl; // dataplotter_p->release( panel_p.getInt( ) ); erase( dataplotter_p, frame ); // dataplotter_p->line(x, y, "blue", "time", panel_p.getInt() ); raster( dataplotter_p, frame, std::vector<float>(data.begin(),data.end()), xdim, ydim ); } ////////////////////////////////////////////////////////////////////////////////////////////////////// void FlagAgentDisplay::DisplayLine(Int /*xdim*/, Vector<Double> &xdata, Vector<Float> &ydata, String label, String color, Bool hold, int frame) { if(xdata.nelements() != ydata.nelements()) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); *logger_p << LogIO::WARN << "X and Y plot data have different sizes. Not plotting " << LogIO::POST; return; } if( hold==false ) erase( dataplotter_p, frame ); line( dataplotter_p, frame, std::vector<float>(xdata.begin(),xdata.end()), std::vector<float>(ydata.begin(),ydata.end()), color, label ); } ////////////////////////////////////////////////////////////////////////////////////////////////////// void FlagAgentDisplay::DisplayScatter(Int /*xdim*/, Vector<Double> &xdata, Vector<Float> &ydata, String label, String color, Bool hold, int frame) { if(xdata.nelements() != ydata.nelements()) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); *logger_p << LogIO::WARN << "X and Y plot data have different sizes. Not plotting " << LogIO::POST; return; } if( hold==false ) erase( dataplotter_p, frame ); scatter( dataplotter_p, frame, std::vector<float>(xdata.begin(),xdata.end()), std::vector<float>(ydata.begin(),ydata.end()), color, label, "dot", 1, 4 ); } ////////////////////////////////////////////////////////////////////////////////////////////////////// // Display scatter dots, with optional vertical error-bars, or circles or text labels, or all together. // Send in vectors of size 0 for radii and pointlabels and error to disable them. void FlagAgentDisplay::DisplayLineScatterError(std::shared_ptr<FlagAgentDisplay::plotter_t> plotter, String &plottype, Vector<Float> &xdata, Vector<Float> &ydata, String &errortype, Vector<Float> &error, String label, String color, int frame) { // Check X and Y shapes if(xdata.nelements() != ydata.nelements()) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); *logger_p << LogIO::WARN << "X, Y plot data have different sizes. Not plotting." << LogIO::POST; return; } if(plottype==String("scatter")) { // Scatter-plot of X and Y scatter( plotter, frame, std::vector<float>(xdata.begin(),xdata.end()), std::vector<float>(ydata.begin(),ydata.end()),color,label,"dot",1,4 ); } else if (plottype==String("line")) { // Line plot of X and Y line( plotter, frame, std::vector<float>(xdata.begin(),xdata.end()), std::vector<float>(ydata.begin(),ydata.end()),color,label ); } else { return; } // Check Error shape ( if not zero, must match xdata,ydata) if(error.nelements()>0) { if(error.nelements()!=xdata.nelements()) { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); *logger_p << LogIO::WARN << "Number of Error elements is different from data. Not plotting." << LogIO::POST; return; } if(errortype == "bar") { Vector<Double>pointerror(2); Vector<Double>xval(2); for ( Int onepoint=0; onepoint<(Int) error.nelements(); onepoint++) { xval[0]=xdata[onepoint]; xval[1]=xdata[onepoint]; pointerror[0]=ydata[onepoint] + error[onepoint]; pointerror[1]=ydata[onepoint] - error[onepoint]; line( plotter, frame, std::vector<float>(xval.begin(),xval.end()), std::vector<float>(pointerror.begin(),pointerror.end()), color,"" ); } } else if(errortype == "separator") { Vector<Double>pointerror(2); Vector<Double>xval(2); for ( Int onepoint=0; onepoint<(Int) error.nelements(); onepoint++) { xval[0]=xdata[onepoint]; xval[1]=xdata[onepoint]; pointerror[0]=ydata[onepoint] + error[onepoint]; pointerror[1]=0; line( plotter, frame, std::vector<float>(xval.begin(),xval.end()), std::vector<float>(pointerror.begin(),pointerror.end()),"black","" ); } } else if(errortype == "circle") { // Check Circle shape ( if not zero, must match xdata,ydata) Vector<Double>xval(1),yval(1); for ( Int onepoint=0; onepoint<(Int) error.nelements(); onepoint++) { xval[0]=xdata[onepoint]; yval[0]=ydata[onepoint]; // function signature should allow a float for the error bar scatter( plotter, frame, std::vector<float>(xval.begin(),xval.end()), std::vector<float>(yval.begin(),yval.end()), color, "", "ellipse", static_cast<Int>(error[onepoint]), 4 ); // Note : I think this plots the centre point too. If so, it's a repeat.... } } else { logger_p->origin(LogOrigin(agentName_p,__FUNCTION__,WHERE)); *logger_p << LogIO::WARN << "Unknown error-plot type. Not plotting." << LogIO::POST; } }// if error // Do a similar thing for labels. // NOTE : Cannot plot point-labels or text strings yet. } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////// GUI Layout Functions ////////////////////////////////////////////////////////////////////////////////////////////////////// Bool FlagAgentDisplay :: setDataLayout() { dock_xml_p = "\ <?xml version=\"1.0\" encoding=\"UTF-8\"?>\ <ui version=\"4.0\">\ <class>dock01</class>\ <widget class=\"QDockWidget\" name=\"dock01\">\ <property name=\"geometry\">\ <rect>\ <x>0</x>\ <y>0</y>\ <width>770</width>\ <height>120</height>\ </rect>\ </property>\ <property name=\"sizePolicy\">\ <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\ <horstretch>0</horstretch>\ <verstretch>0</verstretch>\ </sizepolicy>\ </property>\ <property name=\"minimumSize\">\ <size>\ <width>770</width>\ <height>87</height>\ </size>\ </property>\ <property name=\"windowTitle\">\ <string/>\ </property>\ <widget class=\"QWidget\" name=\"dockWidgetContents\">\ <layout class=\"QGridLayout\" name=\"gridLayout_2\">\ <item row=\"0\" column=\"0\">\ <layout class=\"QGridLayout\" name=\"gridLayout\">\ <item row=\"0\" column=\"0\">\ <widget class=\"QPushButton\" name=\"PrevBaseline\">\ <property name=\"text\">\ <string>Prev Baseline</string>\ </property>\ </widget>\ </item>\ <item row=\"0\" column=\"1\">\ <widget class=\"QPushButton\" name=\"NextBaseline\">\ <property name=\"text\">\ <string>Next Baseline</string>\ </property>\ </widget>\ </item>\ <item row=\"0\" column=\"2\">\ <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\ <property name=\"spacing\">\ <number>0</number>\ </property>\ <property name=\"sizeConstraint\">\ <enum>QLayout::SetMinimumSize</enum>\ </property>\ <item>\ <widget class=\"QCheckBox\" name=\"FixAntenna1\">\ <property name=\"text\">\ <string>Fix Antenna1</string>\ </property>\ </widget>\ </item>\ <item>\ <widget class=\"QCheckBox\" name=\"FixAntenna2\">\ <property name=\"text\">\ <string>Fix Antenna2</string>\ </property>\ </widget>\ </item>\ </layout>\ </item>\ <item row=\"0\" column=\"3\">\ <widget class=\"QPushButton\" name=\"NextSpw\">\ <property name=\"text\">\ <string>Next SPW</string>\ </property>\ </widget>\ </item>\ <item row=\"0\" column=\"4\">\ <widget class=\"QPushButton\" name=\"NextScan\">\ <property name=\"text\">\ <string>Next Scan</string>\ </property>\ </widget>\ </item>\ <item row=\"0\" column=\"5\">\ <widget class=\"QPushButton\" name=\"NextField\">\ <property name=\"text\">\ <string>Next Field</string>\ </property>\ </widget>\ </item>\ <item row=\"0\" column=\"6\">\ <widget class=\"QPushButton\" name=\"StopDisplay\">\ <property name=\"text\">\ <string>Stop Display</string>\ </property>\ </widget>\ </item>\ <item row=\"0\" column=\"7\">\ <widget class=\"QPushButton\" name=\"Quit\">\ <property name=\"text\">\ <string>Quit</string>\ </property>\ </widget>\ </item>\ </layout>\ </item>\ </layout>\ </widget>\ </widget>\ <resources/>\ <connections/>\ </ui>\ "; return true; }// end of SetLayout Bool FlagAgentDisplay :: setReportLayout() { report_dock_xml_p = "\ <?xml version=\"1.0\" encoding=\"UTF-8\"?>\ <ui version=\"4.0\">\ <class>dock01</class>\ <widget class=\"QDockWidget\" name=\"dock01\">\ <property name=\"geometry\">\ <rect>\ <x>0</x>\ <y>0</y>\ <width>320</width>\ <height>80</height>\ </rect>\ </property>\ <property name=\"sizePolicy\">\ <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\ <horstretch>0</horstretch>\ <verstretch>0</verstretch>\ </sizepolicy>\ </property>\ <property name=\"minimumSize\">\ <size>\ <width>320</width>\ <height>80</height>\ </size>\ </property>\ <property name=\"windowTitle\">\ <string/>\ </property>\ <widget class=\"QWidget\" name=\"dockWidgetContents\">\ <property name=\"enabled\">\ <bool>true</bool>\ </property>\ <property name=\"sizePolicy\">\ <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\ <horstretch>0</horstretch>\ <verstretch>0</verstretch>\ </sizepolicy>\ </property>\ <layout class=\"QGridLayout\" name=\"gridLayout\">\ <item row=\"0\" column=\"0\">\ <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\ <item>\ <widget class=\"QPushButton\" name=\"Prev\">\ <property name=\"text\">\ <string>Prev</string>\ </property>\ </widget>\ </item>\ <item>\ <widget class=\"QPushButton\" name=\"Next\">\ <property name=\"text\">\ <string>Next</string>\ </property>\ </widget>\ </item>\ <item>\ <widget class=\"QPushButton\" name=\"Quit\">\ <property name=\"text\">\ <string>Quit</string>\ </property>\ </widget>\ </item>\ </layout>\ </item>\ </layout>\ </widget>\ </widget>\ <resources/>\ <connections/>\ </ui>\ "; return true; } #endif // USE_GRPC } //# NAMESPACE CASA - END