Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-03 08:21:08

0001 #include "TpotMonDraw.h"
0002 #include "TpotMonDefs.h"
0003 
0004 #include <onlmon/OnlMonClient.h>
0005 #include <onlmon/OnlMonDB.h>
0006 
0007 #include <micromegas/MicromegasCalibrationData.h>
0008 
0009 #include <TAxis.h>  // for TAxis
0010 #include <TCanvas.h>
0011 #include <TDatime.h>
0012 #include <TH1.h>
0013 #include <TH2Poly.h>
0014 #include <TLine.h>
0015 #include <TLatex.h>
0016 #include <TPad.h>
0017 #include <TPaveText.h>
0018 #include <TROOT.h>
0019 #include <TSystem.h>
0020 #include <TText.h>
0021 
0022 #include <cstring>  // for memset
0023 #include <ctime>
0024 #include <fstream>
0025 #include <iostream>  // for operator<<, basic_ostream, basic_os...
0026 #include <sstream>
0027 #include <vector>  // for vector
0028 
0029 namespace
0030 {
0031   //! make canvas editable in creator, and non-editable in destructor
0032   class CanvasEditor
0033   {
0034     public:
0035     CanvasEditor( TCanvas* cv ):m_cv(cv)
0036     { if( m_cv ) m_cv->SetEditable(true); }
0037 
0038     ~CanvasEditor()
0039     // {}
0040     { if( m_cv ) m_cv->SetEditable(false); }
0041 
0042     private:
0043     TCanvas* m_cv = nullptr;
0044   };
0045 
0046   TPad* create_transparent_pad( const std::string& name )
0047   {
0048     auto transparent = new TPad( (name+"_transparent").c_str(), "", 0, 0, 1, 1);
0049     transparent->SetFillStyle(4000);
0050     transparent->Draw();
0051     return transparent;
0052   };
0053 
0054   TPad* get_transparent_pad( TPad* parent, const std::string& name, bool clear = true)
0055   {
0056     if( !parent ) return nullptr;
0057     const std::string transparent_name = name+"_transparent";
0058     auto out = dynamic_cast<TPad*>( parent->FindObject( transparent_name.c_str() ) );
0059 
0060     if( !out ) std::cout << "get_transparent_pad - " << transparent_name << " not found" << std::endl;
0061     if( out && clear ) out->Clear("D");
0062     return out;
0063 
0064   }
0065 
0066   // draw an vertical line that extends automatically from both sides of a canvas
0067   [[maybe_unused]] TLine* vertical_line( TVirtualPad* pad, Double_t x )
0068   {
0069     Double_t yMin = pad->GetUymin();
0070     Double_t yMax = pad->GetUymax();
0071 
0072     if( pad->GetLogy() )
0073     {
0074       yMin = std::pow( 10, yMin );
0075       yMax = std::pow( 10, yMax );
0076     }
0077 
0078     return new TLine( x, yMin, x, yMax );
0079   }
0080 
0081   // draw an horizontal line that extends automatically from both sides of a canvas
0082   [[maybe_unused]] TLine* horizontal_line( TVirtualPad* pad, Double_t y )
0083   {
0084     Double_t xMin = pad->GetUxmin();
0085     Double_t xMax = pad->GetUxmax();
0086 
0087     if( pad->GetLogx() )
0088     {
0089       xMin = std::pow( 10, xMin );
0090       xMax = std::pow( 10, xMax );
0091     }
0092 
0093     return new TLine( xMin, y, xMax, y );
0094   }
0095 
0096   // draw text in relative coordinate
0097   void draw_text( Double_t x_ndc, Double_t y_ndc, const TString& value, double text_size = 0.1 )
0098   {
0099     TLatex text;
0100     text.SetNDC( true );
0101     text.SetTextColor(1);
0102     text.SetTextSize(text_size);
0103     text.DrawLatex( x_ndc, y_ndc, value );
0104   }
0105 
0106   void mask_scoz( double xmin, double ymin, double xmax, double ymax )
0107   {
0108     auto text = new TPaveText(xmin, ymin, xmax, ymax, "NDC" );
0109     text->SetFillColor(0);
0110     text->SetFillStyle(1001);
0111     text->SetBorderSize(1);
0112     text->SetTextAlign(11);
0113     text->AddText( "   Ignore   " );
0114     text->Draw();
0115   }
0116 
0117   // divide canvas, adjusting canvas positions to leave room for a banner at the top
0118   void divide_canvas( TCanvas* cv, int ncol, int nrow )
0119   {
0120     static constexpr double max_height = 0.94;
0121 
0122     cv->Divide( ncol, nrow );
0123     for( int i = 0; i < ncol*nrow; ++i )
0124     {
0125       auto pad = cv->GetPad( i+1 );
0126       int col = i%ncol;
0127       int row = i/ncol;
0128       const double xmin = double(col)/ncol;
0129       const double xmax = double(col+1)/ncol;
0130 
0131       const double ymin = max_height*(1. - double(row+1)/nrow);
0132       const double ymax = max_height*(1. - double(row)/nrow);
0133       pad->SetPad( xmin, ymin, xmax, ymax );
0134     }
0135   }
0136 
0137   // hide margins between pads
0138   void hide_margins( TCanvas* cv, const double left_margin = 0.15, const double bottom_margin = 0.17 )
0139   {
0140     static constexpr double max_height = 0.94;
0141     const double height = 1./(4.+bottom_margin);
0142     const double width = 1./(4.+left_margin);
0143     for( int row = 0; row < 4; ++row )
0144     {
0145       double ymin = row < 3 ? max_height*(1.-double(row+1)*height):0;
0146       double ymax = max_height*(1.-double(row)*height);
0147 
0148       for( int column = 0; column < 4; ++column )
0149       {
0150         double xmin = column == 0 ? 0:width*(column+left_margin);
0151         double xmax = width*(column+1+left_margin);
0152 
0153         const int i = column + 4*row;
0154         const auto pad = cv->GetPad(i+1);
0155         pad->SetPad( xmin, ymin, xmax, ymax );
0156 
0157         pad->SetTopMargin(0);
0158 
0159         if(row<3) pad->SetBottomMargin(0);
0160         else pad->SetBottomMargin(bottom_margin);
0161 
0162         if(column>0) pad->SetLeftMargin(0);
0163         else pad->SetLeftMargin(left_margin);
0164 
0165         if(column<3) pad->SetRightMargin(0);
0166         else pad->SetRightMargin(0.01);
0167 
0168         // draw ticks on both sides
0169         pad->SetTicky();
0170 
0171       }
0172     }
0173   }
0174 
0175   // streamer for sample window
0176   std::ostream& operator << ( std::ostream&o, const TpotMonDraw::sample_window_t& window )
0177   {
0178     o << "{ " << window.first << ", " << window.second << "}";
0179     return o;
0180   }
0181 
0182 }
0183 
0184 //__________________________________________________________________________________
0185 TpotMonDraw::TpotMonDraw(const std::string &name)
0186   : OnlMonDraw(name)
0187 {
0188   // setup default calibration filename
0189   // note: this can be overriden by calling set_calibration_filename from the parent macro
0190   const auto tpotcalib = getenv("TPOTCALIB");
0191   if (!tpotcalib)
0192   {
0193     std::cout << "TpotMon::TpotMon - TPOTCALIB environment variable not set" << std::endl;
0194     exit(1);
0195   }
0196 
0197   m_calibration_filename = std::string(tpotcalib) + "/" + "TPOT_Pedestal-000.root";
0198 
0199   // setup default filename for reference histograms
0200   const auto tpotcalibref = getenv("TPOTCALIBREF");
0201   if( tpotcalibref )
0202   {
0203     m_ref_histograms_filename = std::string(tpotcalibref) + "/" + "Run_00000-TPOTMON_0.root";
0204     std::cout << "TpotMon::TpotMon - reading reference histograms from: " << m_ref_histograms_filename << std::endl;
0205     m_ref_histograms_tfile.reset( TFile::Open( m_ref_histograms_filename.c_str(), "READ" ) );
0206   } else {
0207     m_ref_histograms_filename = "Run_00000-TPOTMON_0.root";
0208     std::cout << "TpotMon::TpotMon - TPOTCALIBREF environment variable not set. Reading reference histograms from: " << m_ref_histograms_filename << std::endl;
0209     m_ref_histograms_tfile.reset( TFile::Open( m_ref_histograms_filename.c_str(), "READ" ) );
0210   }
0211 
0212   // this TimeOffsetTicks is neccessary to get the time axis right
0213   TDatime T0(2003, 01, 01, 00, 00, 00);
0214   TimeOffsetTicks = T0.Convert();
0215   dbvars.reset( new OnlMonDB(ThisName) );
0216 
0217   // initialize local list of detector names
0218   for( const auto& fee_id:m_mapping.get_fee_id_list() )
0219   { m_detnames_sphenix.push_back( m_mapping.get_detname_sphenix( fee_id ) ); }
0220 
0221 }
0222 
0223 //__________________________________________________________________________________
0224 int TpotMonDraw::Init()
0225 {
0226   if( Verbosity() )
0227   {
0228     std::cout << "TpotMonDraw::Init - m_calibration_filename: " << m_calibration_filename << std::endl;
0229     std::cout << "TpotMonDraw::Init - m_sample_window: " << m_sample_window << std::endl;
0230     std::cout << "TpotMonDraw::Init - m_sample_window_signal: " << m_sample_window_signal << std::endl;
0231     std::cout << "TpotMon::Init - m_n_sigma: " << m_n_sigma << std::endl;
0232   }
0233 
0234   // setup calibrations
0235   if( std::ifstream( m_calibration_filename.c_str() ).good() )
0236   {
0237 
0238     MicromegasCalibrationData calibration_data;
0239     calibration_data.read( m_calibration_filename );
0240 
0241     // get fee ids
0242     const auto fee_id_list = m_mapping.get_fee_id_list();
0243 
0244     // loop over FEES
0245     for( int i = 0; i < MicromegasDefs::m_nfee; ++i)
0246     {
0247 
0248       // get fee_id
0249       const int fee_id = fee_id_list[i];
0250 
0251       // reset mean
0252       m_mean_thresholds[i] = 0;
0253       unsigned int count = 0;
0254 
0255       // create histogram
0256       std::string hname = std::string( "h_threshold_" ) + m_detnames_sphenix[i];
0257       auto h = new TH1F( hname.c_str(), hname.c_str(), MicromegasDefs::m_nchannels_fee, 0, MicromegasDefs::m_nchannels_fee );
0258 
0259       // set range
0260       h->SetMinimum(0);
0261       h->SetMaximum(1024);
0262       h->SetLineColor(2);
0263 
0264       // store histograms
0265       m_threshold_histograms[i] = h;
0266 
0267       // set values
0268       for( int channel = 0; channel < MicromegasDefs::m_nchannels_fee; ++channel )
0269       {
0270 
0271         // get channel rms and pedestal from calibration data
0272         const double pedestal = calibration_data.get_pedestal( fee_id, channel );
0273         const double rms = calibration_data.get_rms( fee_id, channel );
0274         const double threshold = pedestal + m_n_sigma * rms;
0275         const auto strip_index = m_mapping.get_physical_strip(fee_id, channel );
0276 
0277         // fill histogram
0278         h->SetBinContent( strip_index+1, threshold );
0279 
0280         if( rms > 0 )
0281         {
0282           // increment average
0283           m_mean_thresholds[i] += threshold;
0284           ++count;
0285         }
0286       }
0287 
0288       if(count > 0) {m_mean_thresholds[i]/=count;}
0289     }
0290 
0291   } else {
0292     std::cout << "TpotMonDraw::Init -"
0293       << " file " << m_calibration_filename << " cannot be opened."
0294       << " No calibration loaded"
0295       << std::endl;
0296   }
0297 
0298   return 0;
0299 }
0300 
0301 //__________________________________________________________________________________
0302 int TpotMonDraw::DrawDeadServer( TPad* pad )
0303 {
0304   if( !pad )
0305   {
0306     if( Verbosity() ) std::cout << "TpotMonDraw::DrawDeadServer - invalid pad" << std::endl;
0307     return 0;
0308   } else {
0309     pad->SetPad(0,0,1,1);
0310     return OnlMonDraw::DrawDeadServer( pad );
0311   }
0312 }
0313 
0314 //__________________________________________________________________________________
0315 void TpotMonDraw::draw_time( TPad* pad )
0316 {
0317   if( !pad )
0318   {
0319     if( Verbosity() ) std::cout << "TpotMonDraw::draw_time - invalid pad" << std::endl;
0320     return;
0321   }
0322 
0323   pad->SetPad( 0, 0.95, 1, 1 );
0324   pad->Clear();
0325   TText PrintRun;
0326   PrintRun.SetTextFont(62);
0327   PrintRun.SetTextSize(0.6);
0328   PrintRun.SetNDC();          // set to normalized coordinates
0329   PrintRun.SetTextAlign(23);  // center/top alignment
0330 
0331   std::ostringstream runnostream;
0332   auto cl = OnlMonClient::instance();
0333   std::pair<time_t,int> evttime = cl->EventTime("CURRENT");
0334 
0335   runnostream
0336     << ThisName << " Run " << cl->RunNumber()
0337     << ", Time: " << ctime(&evttime.first);
0338 
0339   pad->cd();
0340   PrintRun.SetTextColor(evttime.second);
0341   PrintRun.DrawText(0.5, 0.5, runnostream.str().c_str());
0342 }
0343 
0344 //__________________________________________________________________________________
0345 TCanvas* TpotMonDraw::get_canvas(const std::string& name, bool clear )
0346 {
0347   auto cv = dynamic_cast<TCanvas*>( gROOT->FindObject( name.c_str() ) );
0348   if( !cv ) cv = create_canvas( name );
0349   if( cv && clear ) cv->Clear("D");
0350   return cv;
0351 }
0352 
0353 //__________________________________________________________________________________
0354 TCanvas* TpotMonDraw::create_canvas(const std::string &name)
0355 {
0356 
0357   if( Verbosity() )
0358   { std::cout << "TpotMonDraw::create_canvas - name: " << name << std::endl; }
0359 
0360   OnlMonClient *cl = OnlMonClient::instance();
0361   int xsize = cl->GetDisplaySizeX();
0362   int ysize = cl->GetDisplaySizeY();
0363 
0364   if (name == "TPOT_counters")
0365   {
0366 
0367     auto cv = new TCanvas(name.c_str(), "TPOT event counters", -1, 0, xsize / 2, ysize);
0368     gSystem->ProcessEvents();
0369     divide_canvas(cv, 1, 1);
0370     create_transparent_pad(name);
0371     cv->SetEditable(false);
0372     m_canvas.push_back( cv );
0373     return cv;
0374 
0375   } else if (name == "TPOT_detector_occupancy") {
0376 
0377     auto cv = new TCanvas(name.c_str(), "TPOT detector occupancy", -1, 0, xsize / 2, ysize);
0378     gSystem->ProcessEvents();
0379     divide_canvas(cv, 1, 2);
0380     create_transparent_pad(name);
0381     cv->SetEditable(false);
0382     m_canvas.push_back( cv );
0383     return cv;
0384 
0385   } else if (name == "TPOT_resist_occupancy") {
0386 
0387     auto cv = new TCanvas(name.c_str(), "TPOT resist occupancy", -1, 0, xsize / 2, ysize);
0388     gSystem->ProcessEvents();
0389     divide_canvas(cv, 1, 2);
0390     create_transparent_pad(name);
0391     cv->SetEditable(false);
0392     m_canvas.push_back( cv );
0393     return cv;
0394 
0395   } else if (name == "TPOT_adc_vs_sample") {
0396 
0397     auto cv = new TCanvas(name.c_str(), "TpotMon adc vs sample", -1, 0, xsize / 2, ysize);
0398     gSystem->ProcessEvents();
0399     divide_canvas(cv, 4, 4);
0400     hide_margins(cv,0.2);
0401     create_transparent_pad(name);
0402 
0403     cv->SetEditable(false);
0404     m_canvas.push_back( cv );
0405     return cv;
0406 
0407   } else if (name == "TPOT_sample_vs_channel") {
0408 
0409     auto cv = new TCanvas(name.c_str(), "TpotMon sample vs channel", -1, 0, xsize / 2, ysize);
0410     gSystem->ProcessEvents();
0411     divide_canvas(cv, 4, 4);
0412     hide_margins(cv,0.2);
0413     create_transparent_pad(name);
0414 
0415     cv->SetEditable(false);
0416     m_canvas.push_back( cv );
0417     return cv;
0418 
0419   } else if (name == "TPOT_adc_vs_channel") {
0420 
0421     auto cv = new TCanvas(name.c_str(), "TpotMon adc vs channel", -1, 0, xsize / 2, ysize);
0422     gSystem->ProcessEvents();
0423     divide_canvas(cv, 4, 4);
0424     hide_margins(cv,0.2);
0425     create_transparent_pad(name);
0426     cv->SetEditable(false);
0427     m_canvas.push_back( cv );
0428     return cv;
0429 
0430   } else if (name == "TPOT_counts_vs_sample") {
0431 
0432     auto cv = new TCanvas(name.c_str(), "TpotMon counts vs sample", -1, 0, xsize / 2, ysize);
0433     gSystem->ProcessEvents();
0434     divide_canvas(cv, 4, 4);
0435     hide_margins(cv,0.2);
0436     create_transparent_pad(name);
0437     cv->SetEditable(false);
0438     m_canvas.push_back( cv );
0439     return cv;
0440 
0441   } else if (name == "TPOT_hit_charge") {
0442 
0443     auto cv = new TCanvas(name.c_str(), "TpotMon hit charge", -1, 0, xsize / 2, ysize);
0444     gSystem->ProcessEvents();
0445     divide_canvas(cv, 4, 4);
0446     hide_margins(cv);
0447     create_transparent_pad(name);
0448     cv->SetEditable(false);
0449     m_canvas.push_back( cv );
0450     return cv;
0451 
0452   } else if (name == "TPOT_hit_multiplicity") {
0453 
0454     auto cv = new TCanvas(name.c_str(), "TpotMon hit multiplicity", -1, 0, xsize / 2, ysize);
0455     gSystem->ProcessEvents();
0456     divide_canvas(cv, 4, 4);
0457     hide_margins(cv);
0458     create_transparent_pad(name);
0459     cv->SetEditable(false);
0460     m_canvas.push_back( cv );
0461     return cv;
0462 
0463   } else if (name == "TPOT_waveform_vs_channel") {
0464 
0465     auto cv = new TCanvas(name.c_str(), "TpotMon waveform vs channel", -1, 0, xsize / 2, ysize);
0466     gSystem->ProcessEvents();
0467     divide_canvas(cv, 4, 4);
0468     hide_margins(cv,0.2);
0469     create_transparent_pad(name);
0470     cv->SetEditable(false);
0471     m_canvas.push_back( cv );
0472     return cv;
0473 
0474 
0475   } else if (name == "TPOT_heartbeat_vs_channel") {
0476 
0477     auto cv = new TCanvas(name.c_str(), "TpotMon heartbeat vs channel", -1, 0, xsize / 2, ysize);
0478     gSystem->ProcessEvents();
0479     divide_canvas(cv, 4, 4);
0480     hide_margins(cv,0.2);
0481     create_transparent_pad(name);
0482     cv->SetEditable(false);
0483     m_canvas.push_back( cv );
0484     return cv;
0485 
0486   } else if (name == "TPOT_hit_vs_channel") {
0487 
0488     auto cv = new TCanvas(name.c_str(), "TpotMon hit vs channel", -1, 0, xsize / 2, ysize);
0489     gSystem->ProcessEvents();
0490     divide_canvas(cv, 4, 4);
0491     hide_margins(cv,0.2);
0492     create_transparent_pad(name);
0493     cv->SetEditable(false);
0494     m_canvas.push_back( cv );
0495     return cv;
0496 
0497   } else if (name == "TPOT_server_stats") {
0498 
0499     auto cv = new TCanvas(name.c_str(), "TPOT Server Statistics", -1, 0, 2*xsize/3, ysize);
0500     gSystem->ProcessEvents();
0501     create_transparent_pad(name);
0502     cv->SetEditable(false);
0503     m_canvas.push_back( cv );
0504     return cv;
0505 
0506   }
0507 
0508   return nullptr;
0509 }
0510 
0511 //_______________________________________________________________________________
0512 int TpotMonDraw::Draw(const std::string &what)
0513 {
0514   if( Verbosity() )
0515   { std::cout << "TpotMonDraw::Draw - what: " << what << std::endl; }
0516 
0517   int iret = 0;
0518   int idraw = 0;
0519 
0520   {
0521     // get counters
0522     const auto m_counters = get_histogram( "m_counters");
0523     if( m_counters )
0524     {
0525       m_triggercnt =  m_counters->GetBinContent( TpotMonDefs::kTriggerCounter );
0526       m_heartbeatcnt =  m_counters->GetBinContent( TpotMonDefs::kHeartBeatCounter );
0527     } else {
0528       m_triggercnt = 0;
0529       m_heartbeatcnt = 0;
0530     }
0531 
0532     if( m_counters && Verbosity() )
0533     {
0534       const int events = m_counters->GetBinContent( TpotMonDefs::kEventCounter );
0535       const int valid_events = m_counters->GetBinContent( TpotMonDefs::kValidEventCounter );
0536       std::cout << "TpotMonDraw::Draw - RCDAQ frames: " << events << " valid RCDAQ frames: " << valid_events << std::endl;
0537     }
0538   }
0539 
0540   if( what == "ALL" || what == "TPOT_counters" )
0541   {
0542     iret += draw_counters();
0543     ++idraw;
0544   }
0545 
0546   if( what == "ALL" || what == "TPOT_detector_occupancy" )
0547   {
0548     iret += draw_detector_occupancy();
0549     ++idraw;
0550   }
0551 
0552   if( what == "ALL" || what == "TPOT_resist_occupancy" )
0553   {
0554     iret += draw_resist_occupancy();
0555     ++idraw;
0556   }
0557 
0558   if (what == "ALL" || what == "TPOT_adc_vs_sample")
0559   {
0560 
0561     // adjust histogram range
0562     auto h_array = get_histograms( "m_adc_sample" );
0563     for( const auto& h:h_array )
0564     {
0565       if( h )
0566       { h->GetXaxis()->SetRangeUser( m_sample_window.first, m_sample_window.second ); }
0567     }
0568 
0569     iret += draw_array("TPOT_adc_vs_sample", h_array, DrawOptions::Colz|DrawOptions::Logz );
0570     auto cv = get_canvas("TPOT_adc_vs_sample");
0571     if( cv )
0572     {
0573       CanvasEditor cv_edit(cv);
0574       cv->Update();
0575 
0576       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0577       {
0578         // draw vertical lines that match sample window
0579         auto&& pad = cv->GetPad(i+1);
0580         pad->cd();
0581         pad->Update();
0582         for( const auto line:{vertical_line( pad, m_sample_window_signal.first ), vertical_line( pad, m_sample_window_signal.second ) } )
0583         {
0584           line->SetLineStyle(2);
0585           line->SetLineColor(2);
0586           line->SetLineWidth(2);
0587           line->Draw();
0588         }
0589 
0590         // also draw horizontal line at average threshold
0591         if( m_mean_thresholds[i] > 0 )
0592         {
0593           auto line = horizontal_line( pad, m_mean_thresholds[i] );
0594           line->SetLineStyle(2);
0595           line->SetLineColor(2);
0596           line->SetLineWidth(2);
0597           line->Draw();
0598         }
0599       }
0600     }
0601     ++idraw;
0602   }
0603 
0604   if (what == "ALL" || what == "TPOT_sample_vs_channel")
0605     {
0606       iret += draw_array("TPOT_sample_vs_channel", get_histograms( "m_sample_channel" ), DrawOptions::Colz);
0607       auto cv = get_canvas("TPOT_sample_vs_channel");
0608       if( cv )
0609     {
0610       CanvasEditor cv_edit(cv);
0611       cv->Update();
0612       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0613         {
0614           auto&& pad = cv->GetPad(i+1);
0615           pad->cd();
0616 
0617               // draw vertical lines that match HV sectors
0618           for( const int& channel:{64, 128, 196} )
0619         {
0620           const auto line = vertical_line( pad, channel );
0621           line->SetLineStyle(2);
0622           line->SetLineColor(2);
0623           line->SetLineWidth(1);
0624           line->Draw();
0625         }
0626         }
0627       {
0628         // mask scoz
0629         auto&& pad = cv->GetPad(9);
0630         pad->cd();
0631         mask_scoz(0.22,0.02,0.58, 0.98);
0632       }
0633     }
0634       ++idraw;
0635     }
0636 
0637   if (what == "ALL" || what == "TPOT_adc_vs_channel")
0638   {
0639     iret += draw_array("TPOT_adc_vs_channel", get_histograms( "m_adc_channel" ), DrawOptions::Colz|DrawOptions::Logz );
0640     auto cv = get_canvas("TPOT_adc_vs_channel");
0641     if( cv )
0642     {
0643       CanvasEditor cv_edit(cv);
0644       cv->Update();
0645       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0646       {
0647 
0648         auto&& pad = cv->GetPad(i+1);
0649         pad->cd();
0650 
0651         // draw threshold
0652         if( m_threshold_histograms[i] )
0653         { m_threshold_histograms[i]->Draw("h same"); }
0654 
0655         // draw vertical lines that match HV sectors
0656         for( const int& channel:{64, 128, 196} )
0657         {
0658           const auto line = vertical_line( pad, channel );
0659           line->SetLineStyle(2);
0660           line->SetLineColor(1);
0661           line->SetLineWidth(1);
0662           line->Draw();
0663         }
0664       }
0665 
0666       {
0667         // mask scoz
0668         auto&& pad = cv->GetPad(9);
0669         pad->cd();
0670         mask_scoz(0.22,0.02,0.58, 0.98);
0671       }
0672 
0673     }
0674     ++idraw;
0675   }
0676 
0677   if (what == "ALL" || what == "TPOT_counts_vs_sample")
0678   {
0679     // adjust histogram range
0680     auto h_array = get_histograms( "m_counts_sample" );
0681     for( const auto& h:h_array )
0682     {
0683       if( h )
0684       {
0685         h->GetXaxis()->SetRangeUser( m_sample_window.first, m_sample_window.second );
0686         h->SetMinimum(0);
0687       }
0688     }
0689 
0690     // iret += draw_array("TPOT_counts_vs_sample", h_array, get_ref_histograms_scaled( "m_counts_sample" ), DrawOptions::MatchRange );
0691     iret += draw_array("TPOT_counts_vs_sample", h_array, get_ref_histograms_scaled( "m_counts_sample" ) );
0692     auto cv = get_canvas("TPOT_counts_vs_sample");
0693     if( cv )
0694     {
0695       CanvasEditor cv_edit(cv);
0696       cv->Update();
0697       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0698       {
0699         // draw vertical lines that match sample window
0700         auto&& pad = cv->GetPad(i+1);
0701         pad->cd();
0702         for( const auto line:{vertical_line( pad, m_sample_window_signal.first ), vertical_line( pad, m_sample_window_signal.second ) } )
0703         {
0704           line->SetLineStyle(2);
0705           line->SetLineColor(2);
0706           line->SetLineWidth(2);
0707           line->Draw();
0708         }
0709       }
0710     }
0711     ++idraw;
0712   }
0713 
0714   if (what == "ALL" || what == "TPOT_hit_charge")
0715   {
0716     iret += draw_array("TPOT_hit_charge", get_histograms( "m_hit_charge" ), get_ref_histograms_scaled( "m_hit_charge" ), DrawOptions::Logy|DrawOptions::MatchRange );
0717     auto cv = get_canvas("TPOT_hit_charge");
0718     if( cv )
0719     {
0720       CanvasEditor cv_edit(cv);
0721       cv->Update();
0722       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0723       {
0724         // draw vertical lines that match sample window
0725         auto&& pad = cv->GetPad(i+1);
0726         pad->cd();
0727         if( m_mean_thresholds[i] > 0 )
0728         {
0729           auto line = vertical_line( pad, m_mean_thresholds[i] );
0730           line->SetLineStyle(2);
0731           line->SetLineColor(2);
0732           line->SetLineWidth(2);
0733           line->Draw();
0734         }
0735       }
0736     }
0737     ++idraw;
0738   }
0739 
0740   if (what == "ALL" || what == "TPOT_hit_multiplicity")
0741   {
0742     iret += draw_array("TPOT_hit_multiplicity", get_histograms( "m_hit_multiplicity" ), get_ref_histograms_scaled( "m_hit_multiplicity" ), DrawOptions::Logy|DrawOptions::MatchRange );
0743     ++idraw;
0744   }
0745 
0746   if (what == "ALL" || what == "TPOT_waveform_vs_channel")
0747   {
0748     iret += draw_array(
0749       "TPOT_waveform_vs_channel",
0750       get_histograms( "m_wf_vs_channel" ),
0751       get_ref_histograms_scaled( "m_wf_vs_channel" ),
0752       DrawOptions::Logy|DrawOptions::MatchRange|DrawOptions::Normalize,
0753       m_triggercnt);
0754     auto cv = get_canvas("TPOT_waveform_vs_channel");
0755     if( cv )
0756     {
0757       CanvasEditor cv_edit(cv);
0758       cv->Update();
0759       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0760       {
0761         // draw vertical lines that match HV sectors
0762         // also set log y
0763         auto&& pad = cv->GetPad(i+1);
0764         pad->cd();
0765         pad->Update();
0766         for( const int& channel:{64, 128, 196} )
0767         {
0768           const auto line = vertical_line( pad, channel );
0769           line->SetLineStyle(2);
0770           line->SetLineColor(1);
0771           line->SetLineWidth(1);
0772           line->Draw();
0773         }
0774       }
0775     }
0776 
0777     ++idraw;
0778   }
0779 
0780   if (what == "ALL" || what == "TPOT_hit_vs_channel")
0781   {
0782     iret += draw_array(
0783       "TPOT_hit_vs_channel",
0784       get_histograms( "m_hit_vs_channel" ),
0785       get_ref_histograms_scaled( "m_hit_vs_channel" ),
0786       DrawOptions::Logy|DrawOptions::MatchRange|DrawOptions::Normalize,
0787       m_triggercnt);
0788     auto cv = get_canvas("TPOT_hit_vs_channel");
0789     if( cv )
0790     {
0791       CanvasEditor cv_edit(cv);
0792       cv->Update();
0793       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0794       {
0795         // draw vertical lines that match HV sectors
0796         // also set log y
0797         auto&& pad = cv->GetPad(i+1);
0798         pad->cd();
0799         pad->Update();
0800         for( const int& channel:{64, 128, 196} )
0801         {
0802           const auto line = vertical_line( pad, channel );
0803           line->SetLineStyle(2);
0804           line->SetLineColor(1);
0805           line->SetLineWidth(1);
0806           line->Draw();
0807         }
0808 
0809       }
0810 
0811       {
0812         // maks scoz
0813         auto&& pad = cv->GetPad(9);
0814         pad->cd();
0815         mask_scoz(0.22,0.02,0.58, 0.98);
0816       }
0817     }
0818 
0819     ++idraw;
0820   }
0821 
0822 
0823   if (what == "ALL" || what == "TPOT_heartbeat_vs_channel")
0824   {
0825     iret += draw_array(
0826       "TPOT_heartbeat_vs_channel",
0827       get_histograms( "m_heartbeat_vs_channel" ),
0828       get_ref_histograms_scaled( "m_heartbeat_vs_channel" ),
0829       DrawOptions::MatchRange|DrawOptions::Normalize,
0830       m_heartbeatcnt);
0831     auto cv = get_canvas("TPOT_heartbeat_vs_channel");
0832     if( cv )
0833     {
0834       CanvasEditor cv_edit(cv);
0835       cv->Update();
0836       for( int i = 0; i < MicromegasDefs::m_nfee; ++i )
0837       {
0838         // draw vertical lines that match HV sectors
0839         // also set log y
0840         auto&& pad = cv->GetPad(i+1);
0841         pad->cd();
0842         pad->Update();
0843         for( const int& channel:{64, 128, 196} )
0844         {
0845           const auto line = vertical_line( pad, channel );
0846           line->SetLineStyle(2);
0847           line->SetLineColor(1);
0848           line->SetLineWidth(1);
0849           line->Draw();
0850         }
0851 
0852       }
0853 
0854     }
0855 
0856     ++idraw;
0857   }
0858 
0859   if ( what == "ALL" || what == "TPOT_server_stats" )
0860   {
0861     iret += draw_server_statistics();
0862     ++idraw;
0863   }
0864 
0865   if (!idraw)
0866   {
0867     std::cout << "TpotMonDraw::Draw - Unimplemented Drawing option: " << what << std::endl;
0868     iret = -1;
0869   }
0870   return iret;
0871 }
0872 
0873 //_______________________________________________________________________________
0874 int TpotMonDraw::SavePlot(const std::string &what, const std::string &type)
0875 {
0876   auto cl = OnlMonClient::instance();
0877   const int iret = Draw(what);
0878   if (iret) return iret;
0879 
0880   for( std::size_t i =0; i < m_canvas.size(); ++i )
0881   {
0882     const auto& cv = m_canvas[i];
0883     if( cv )
0884     {
0885     std::string filename = ThisName + "_" + std::to_string(i+1) + "_" +
0886       std::to_string(cl->RunNumber()) + "." + type;
0887      cl->CanvasToPng(cv, filename);
0888     }
0889   }
0890 
0891   return 0;
0892 }
0893 
0894 //__________________________________________________________________________________
0895 int TpotMonDraw::MakeHtml(const std::string &what)
0896 {
0897   const int iret = Draw(what);
0898   if (iret) return iret;
0899 
0900   auto cl = OnlMonClient::instance();
0901 
0902   // Register the 1st canvas png file to the menu and produces the png file.
0903   for( std::size_t i =0; i < m_canvas.size(); ++i )
0904   {
0905     const auto& cv = m_canvas[i];
0906     if( cv )
0907     {
0908       const auto pngfile = cl->htmlRegisterPage(*this, cv->GetName(), Form("%lu", i+1), "png");
0909       cl->CanvasToPng(cv, pngfile);
0910     }
0911   }
0912 
0913   // log
0914   {
0915     const std::string logfile = cl->htmlRegisterPage(*this, "EXPERTS/Log", "log", "html");
0916     std::ofstream out(logfile.c_str());
0917     out
0918       << "<HTML><HEAD><TITLE>Log file for run " << cl->RunNumber()
0919       << "</TITLE></HEAD>"
0920       << std::endl
0921       << "<P>Some log file output would go here."
0922       << std::endl;
0923     out.close();
0924   }
0925 
0926   // status
0927   {
0928     const std::string status = cl->htmlRegisterPage(*this, "EXPERTS/Status", "status", "html");
0929     std::ofstream out(status.c_str());
0930     out
0931       << "<HTML><HEAD><TITLE>Status file for run " << cl->RunNumber()
0932       << "</TITLE></HEAD>"
0933       << std::endl
0934       << "<P>Some status output would go here."
0935       << std::endl;
0936     out.close();
0937     cl->SaveLogFile(*this);
0938   }
0939 
0940   return 0;
0941 }
0942 
0943 //__________________________________________________________________________________
0944 int TpotMonDraw::draw_counters()
0945 {
0946 
0947   if( Verbosity() ) std::cout << "TpotMonDraw::draw_counters" << std::endl;
0948 
0949   // get histograms
0950   auto m_counters =  get_histogram( "m_counters");
0951   std::unique_ptr<TH1> m_counters_ref( normalize( get_ref_histogram( "m_counters" ), get_ref_scale_factor() ) );
0952 
0953   auto cv = get_canvas("TPOT_counters");
0954   auto transparent = get_transparent_pad( cv, "TPOT_counters");
0955   if( !cv )
0956   {
0957     if( Verbosity() ) std::cout << "TpotMonDraw::draw_counters - no canvas" << std::endl;
0958     return -1;
0959   }
0960 
0961   CanvasEditor cv_edit(cv);
0962 
0963   if( m_counters )
0964   {
0965     m_counters->SetMinimum(0);
0966 
0967     cv->cd(1);
0968     gPad->SetLeftMargin( 0.07 );
0969     gPad->SetRightMargin( 0.15 );
0970     gPad->SetBottomMargin( 0.15 );
0971     m_counters->SetFillStyle(1001);
0972     m_counters->SetFillColor(kYellow );
0973     auto copy = m_counters->DrawCopy( "hist" );
0974     copy->SetStats(false);
0975 
0976     if( m_counters_ref )
0977     {
0978       m_counters_ref->SetLineColor(2);
0979       m_counters_ref->DrawCopy( "hist same" );
0980     }
0981 
0982     draw_time(transparent);
0983     return 0;
0984   } else {
0985 
0986     DrawDeadServer(transparent);
0987     return -1;
0988   }
0989 }
0990 
0991 //__________________________________________________________________________________
0992 int TpotMonDraw::draw_detector_occupancy()
0993 {
0994 
0995   if( Verbosity() ) std::cout << "TpotMonDraw::draw_detector_occupancy" << std::endl;
0996 
0997   // get histograms
0998   auto m_detector_occupancy_phi =  static_cast<TH2Poly*>(get_histogram( "m_detector_occupancy_phi"));
0999   auto m_detector_occupancy_z =  static_cast<TH2Poly*>(get_histogram( "m_detector_occupancy_z"));
1000 
1001   // turn off stat panel
1002   for( const auto& h:{m_detector_occupancy_phi,m_detector_occupancy_z} )
1003   { if(h) h->SetStats(0); }
1004 
1005   auto cv = get_canvas("TPOT_detector_occupancy");
1006   auto transparent = get_transparent_pad( cv, "TPOT_detector_occupancy");
1007   if( !cv )
1008   {
1009     if( Verbosity() ) std::cout << "TpotMonDraw::draw_detector_occupancy - no canvas" << std::endl;
1010     return -1;
1011   }
1012 
1013   CanvasEditor cv_edit(cv);
1014 
1015   if( m_detector_occupancy_phi && m_detector_occupancy_z )
1016   {
1017     cv->cd(1);
1018     gPad->SetLeftMargin( 0.07 );
1019     gPad->SetRightMargin( 0.15 );
1020     auto copy = m_detector_occupancy_z->DrawCopy( "colz" );
1021     copy->SetStats(false);
1022     copy->GetXaxis()->SetTitleOffset(1);
1023     copy->GetYaxis()->SetTitleOffset(0.65);
1024     draw_detnames_sphenix( "Z" );
1025     draw_occupancy( m_detector_occupancy_z );
1026 
1027     cv->cd(2);
1028     gPad->SetLeftMargin( 0.07 );
1029     gPad->SetRightMargin( 0.15 );
1030     copy = m_detector_occupancy_phi->DrawCopy( "colz" );
1031     copy->SetStats(false);
1032     copy->GetXaxis()->SetTitleOffset(1);
1033     copy->GetYaxis()->SetTitleOffset(0.65);
1034     draw_detnames_sphenix( "P" );
1035     draw_occupancy( m_detector_occupancy_phi );
1036 
1037     draw_time(transparent);
1038     return 0;
1039 
1040   } else {
1041 
1042     DrawDeadServer(transparent);
1043     return -1;
1044 
1045   }
1046 }
1047 
1048 //__________________________________________________________________________________
1049 int TpotMonDraw::draw_resist_occupancy()
1050 {
1051 
1052   if( Verbosity() ) std::cout << "TpotMonDraw::draw_resist_occupancy" << std::endl;
1053 
1054   // get histograms
1055   auto m_resist_occupancy_phi =  get_histogram( "m_resist_occupancy_phi");
1056   auto m_resist_occupancy_z =  get_histogram( "m_resist_occupancy_z");
1057 
1058   // turn off stat panel
1059   for( const auto& h:{m_resist_occupancy_phi,m_resist_occupancy_z} )
1060   { if(h) h->SetStats(0); }
1061 
1062   auto cv = get_canvas("TPOT_resist_occupancy");
1063   auto transparent = get_transparent_pad( cv, "TPOT_resist_occupancy");
1064   if( !cv )
1065   {
1066     if( Verbosity() ) std::cout << "TpotMonDraw::draw_resist_occupancy - no canvas" << std::endl;
1067     return -1;
1068   }
1069 
1070   CanvasEditor cv_edit(cv);
1071 
1072   if( m_resist_occupancy_phi && m_resist_occupancy_z )
1073   {
1074     cv->cd(1);
1075     gPad->SetLeftMargin( 0.07 );
1076     gPad->SetRightMargin( 0.15 );
1077     auto copy = m_resist_occupancy_z->DrawCopy( "colz" );
1078     copy->SetStats(false);
1079     copy->GetXaxis()->SetTitleOffset(1);
1080     copy->GetYaxis()->SetTitleOffset(0.65);
1081     draw_detnames_sphenix( "Z" );
1082 
1083     mask_scoz(0.1,0.4,0.18, 0.6);
1084 
1085     cv->cd(2);
1086     gPad->SetLeftMargin( 0.07 );
1087     gPad->SetRightMargin( 0.15 );
1088     copy = m_resist_occupancy_phi->DrawCopy( "colz" );
1089     copy->SetStats(false);
1090     copy->GetXaxis()->SetTitleOffset(1);
1091     copy->GetYaxis()->SetTitleOffset(0.65);
1092     draw_detnames_sphenix( "P" );
1093 
1094     draw_time(transparent);
1095     return 0;
1096 
1097   } else {
1098     DrawDeadServer(transparent);
1099     return -1;
1100   }
1101 }
1102 
1103 
1104 //__________________________________________________________________________________
1105 int TpotMonDraw::draw_server_statistics()
1106 {
1107   auto client = OnlMonClient::instance();
1108   auto cv = get_canvas("TPOT_server_stats");
1109   auto transparent = get_transparent_pad( cv, "TPOT_server_stats");
1110   CanvasEditor cv_edit(cv);
1111   transparent->cd();
1112   transparent->Clear();
1113 
1114   TText PrintRun;
1115   PrintRun.SetTextFont(62);
1116   PrintRun.SetNDC();          // set to normalized coordinates
1117   PrintRun.SetTextAlign(23);  // center/top alignment
1118   PrintRun.SetTextSize(0.04);
1119   PrintRun.SetTextColor(1);
1120   PrintRun.DrawText(0.5, 0.99, "Server Statistics");
1121   PrintRun.SetTextSize(0.02);
1122   const double vdist = 0.05;
1123   double vpos = 0.9;
1124   time_t clienttime = time(nullptr);
1125   for (const auto &server : m_ServerSet)
1126   {
1127     std::ostringstream txt;
1128     auto servermapiter = client->GetServerMap(server);
1129     if (servermapiter == client->GetServerMapEnd())
1130     {
1131       txt << "Server " << server << " is dead ";
1132       PrintRun.SetTextColor(kRed);
1133     } else {
1134       const auto gl1counts = std::get<4>(servermapiter->second);
1135       const time_t currtime = std::get<3>(servermapiter->second);
1136       txt
1137         << "Server " << server
1138         << ", run number " << std::get<1>(servermapiter->second)
1139         << ", event count: " << std::get<2>(servermapiter->second);
1140       if( gl1counts > 0 )
1141       { txt << ", gl1 count: " << gl1counts; }
1142       txt << ", current time " << ctime(&(std::get<3>(servermapiter->second)));
1143 
1144       if (isHtml())
1145       {
1146         // just prevent the font from getting red
1147         clienttime = currtime;
1148       } else {
1149         txt  << ", minutes since last evt: " << (clienttime - currtime)/60;
1150       }
1151 
1152       if (std::get<0>(servermapiter->second) && ((clienttime - currtime)/60) < 10)
1153       {
1154         PrintRun.SetTextColor(kGray + 2);
1155       } else {
1156         PrintRun.SetTextColor(kRed);
1157       }
1158     }
1159 
1160     PrintRun.DrawText(0.5, vpos, txt.str().c_str());
1161     vpos -= vdist;
1162   }
1163 
1164   return 0;
1165 }
1166 
1167 //__________________________________________________________________________________
1168 void TpotMonDraw::draw_detnames_sphenix( const std::string& suffix)
1169 {
1170   gPad->Update();
1171   for( size_t i = 0; i < m_geometry.get_ntiles(); ++i )
1172   {
1173     const auto name = m_geometry.get_detname_sphenix(i)+suffix;
1174     const auto [x,y] = m_geometry.get_tile_center(i);
1175     auto text = new TText();
1176     text->DrawText( x-0.8*m_geometry.m_tile_length/2, y+0.5*m_geometry.m_tile_width/2, name.c_str() );
1177     text->Draw();
1178   }
1179 }
1180 
1181 //__________________________________________________________________________________
1182 void TpotMonDraw::draw_occupancy( TH2Poly* h)
1183 {
1184   gPad->Update();
1185   for( size_t i = 0; i < m_geometry.get_ntiles(); ++i )
1186   {
1187     const auto [x,y] = m_geometry.get_tile_center(i);
1188     const auto bin = h->FindBin(x,y);
1189     const auto value =h->GetBinContent(bin);
1190     // const auto value = std::round(h->GetBinContent( bin )*100)/100;
1191     TText().DrawText( x-0.8*m_geometry.m_tile_length/2, y, Form("%.2f %%",value));
1192   }
1193 }
1194 
1195 //__________________________________________________________________________________
1196 TH1* TpotMonDraw::get_histogram( const std::string& name ) const
1197 {
1198   auto cl = OnlMonClient::instance();
1199   return cl->getHisto("TPOTMON_0", name );
1200 }
1201 
1202 //__________________________________________________________________________________
1203 TpotMonDraw::histogram_array_t TpotMonDraw::get_histograms( const std::string& name ) const
1204 {
1205   histogram_array_t out{{nullptr}};
1206   for( size_t i=0; i<m_detnames_sphenix.size(); ++i)
1207   {
1208     const auto& detector_name=m_detnames_sphenix[i];
1209     const auto hname = name + "_" + detector_name;
1210     out[i] =  get_histogram(  hname );
1211     if( Verbosity() )
1212     { std::cout << "TpotMonDraw::get_histograms - " << hname << (out[i]?" found":" not found" ) << std::endl; }
1213   }
1214 
1215   return out;
1216 }
1217 
1218 //__________________________________________________________________________________
1219 TH1* TpotMonDraw::get_ref_histogram( const std::string& name ) const
1220 { return m_ref_histograms_tfile ? static_cast<TH1*>( m_ref_histograms_tfile->Get( name.c_str() ) ):nullptr; }
1221 
1222 //__________________________________________________________________________________
1223 TpotMonDraw::histogram_array_t TpotMonDraw::get_ref_histograms( const std::string& name ) const
1224 {
1225   histogram_array_t out{{nullptr}};
1226   for( size_t i=0; i<m_detnames_sphenix.size(); ++i)
1227   {
1228     const auto& detector_name=m_detnames_sphenix[i];
1229     const auto hname = name + "_" + detector_name;
1230     out[i] =  get_ref_histogram(  hname );
1231     if( Verbosity() )
1232     { std::cout << "TpotMonDraw::get_ref_histograms - " << hname << (out[i]?" found":" not found" ) << std::endl; }
1233   }
1234 
1235   return out;
1236 }
1237 
1238 //__________________________________________________________________________________
1239 TpotMonDraw::histogram_array_t TpotMonDraw::get_ref_histograms_scaled( const std::string& name ) const
1240 {
1241   histogram_array_t source( get_ref_histograms( name ) );
1242   histogram_array_t out{{nullptr}};
1243 
1244   const double scale = get_ref_scale_factor();
1245   for( size_t i=0; i<source.size(); ++i)
1246   { if( source[i] ) out[i]=normalize( source[i], scale ); }
1247 
1248   return out;
1249 }
1250 
1251 //__________________________________________________________________________________
1252 double TpotMonDraw::get_ref_scale_factor() const
1253 {
1254   if( !m_ref_histograms_tfile ) return 0;
1255   const auto m_counters = get_histogram( "m_counters");
1256   const auto m_counters_ref = get_ref_histogram( "m_counters");
1257   if( !( m_counters && m_counters_ref ) ) return 0;
1258 
1259   const double triggercnt = m_counters->GetBinContent( TpotMonDefs::kTriggerCounter );
1260   const double triggercnt_ref = m_counters_ref->GetBinContent( TpotMonDefs::kTriggerCounter );
1261   return triggercnt_ref > 0 ? triggercnt/triggercnt_ref : 0;
1262 }
1263 
1264 //__________________________________________________________________________________
1265 TH1* TpotMonDraw::normalize( TH1* source, double scale ) const
1266 {
1267   if( !source ) return nullptr;
1268   auto destination = static_cast<TH1*>( source->Clone() );
1269   destination->SetName( TString( source->GetName() )+"_scaled" );
1270   destination->Scale( scale );
1271   return destination;
1272 }
1273 
1274 //__________________________________________________________________________________
1275 int TpotMonDraw::draw_array( const std::string& name, const TpotMonDraw::histogram_array_t& histograms, const TpotMonDraw::histogram_array_t& ref_histograms, unsigned int options, double norm_factor )
1276 {
1277   if( Verbosity() ) std::cout << "TpotMonDraw::draw_array - name: " << name << std::endl;
1278 
1279   auto cv = get_canvas(name);
1280   auto transparent = get_transparent_pad( cv, name);
1281   if( !cv ) return -1;
1282 
1283   bool drawn = false;
1284   CanvasEditor cv_edit(cv);
1285 
1286   // calculate matched maximum
1287   double maximum = 0;
1288   if(options&DrawOptions::MatchRange)
1289   {
1290     for( const auto& h:histograms )
1291     { if( h ) maximum = std::max( maximum, h->GetMaximum() ); }
1292   }
1293 
1294   // also scale by number of triggers if normalization is required
1295   if((options&DrawOptions::Normalize) && (norm_factor>0))
1296   { maximum/=norm_factor; }
1297 
1298   // draw
1299   for( size_t i = 0; i < histograms.size(); ++i )
1300   {
1301     if( histograms[i] )
1302     {
1303       cv->cd(i+1);
1304       TH1* copy = nullptr;
1305       if( options&DrawOptions::Colz )
1306       {
1307         copy = histograms[i]->DrawCopy( "col" );
1308       } else {
1309 
1310         histograms[i]->SetFillStyle(1001);
1311         histograms[i]->SetFillColor(kYellow );
1312         copy = histograms[i]->DrawCopy( "hist" );
1313       }
1314 
1315       if( copy )
1316       {
1317         copy->SetTitle("");
1318         copy->SetStats(false);
1319         copy->GetXaxis()->SetTitleOffset(1.);
1320         copy->GetXaxis()->SetTitleSize( i==12 ? 0.075:0.08 );
1321         copy->GetXaxis()->SetLabelSize( i==12 ? 0.075:0.08 );
1322 
1323         copy->GetYaxis()->SetTitleOffset( i<12 ? 1.4:1.6);
1324         copy->GetYaxis()->SetTitleSize( i<12 ? 0.08:0.07 );
1325         copy->GetYaxis()->SetLabelSize( i<12 ? 0.08:0.07 );
1326 
1327         // normalize
1328         if((options&DrawOptions::Normalize) && (norm_factor>0))
1329         {
1330           copy->Scale( 1./norm_factor );
1331           copy->GetYaxis()->SetTitle("counts/trigger");
1332         }
1333 
1334         // equalize maximum
1335         if(options&DrawOptions::MatchRange)
1336         {
1337           copy->SetMaximum( 1.2*maximum );
1338           copy->SetMinimum(0);
1339         }
1340 
1341       }
1342 
1343       // also draw reference
1344       if( ref_histograms[i] )
1345       {
1346         ref_histograms[i]->SetLineColor(2);
1347 
1348         const auto& ref_copy = ref_histograms[i]->DrawCopy("hist same" );
1349         ref_copy->SetStats(false);
1350 
1351         // normalize
1352         if((options&DrawOptions::Normalize) && (norm_factor>0))
1353         { ref_copy->Scale( 1./norm_factor ); }
1354 
1355       }
1356 
1357       // apply log scales
1358       if( options&DrawOptions::Logx )
1359       { gPad->SetLogx( true ); }
1360 
1361       if( options&DrawOptions::Logy && histograms[i]->GetEntries() > 0 )
1362       {
1363         gPad->SetLogy( true );
1364         if((options&DrawOptions::Normalize) && (norm_factor>0))
1365         {
1366           copy->SetMinimum(1./norm_factor);
1367         } else {
1368           copy->SetMinimum(1);
1369         }
1370       }
1371 
1372       if( options&DrawOptions::Logz )
1373       { gPad->SetLogz( true ); }
1374 
1375       // draw detector name
1376       const auto label = Form( "%s (%02i)", m_detnames_sphenix[i].c_str(), m_mapping.get_fee_id_list()[i]);
1377       draw_text( (i%4) ? 0.5:0.6, 0.9, label, (i%4) ? 0.1:0.094 );
1378       // draw_text( 0.7, 0.9, m_detnames_sphenix[i].c_str(), (i%4) ? 0.1:0.094 );
1379       drawn = true;
1380     }
1381   }
1382 
1383   if( drawn )
1384   {
1385     draw_time(transparent);
1386     return 0;
1387   } else {
1388     DrawDeadServer(transparent);
1389     return -1;
1390   }
1391 
1392   // need to delete reference histograms to avoid leak
1393   for( auto h:ref_histograms ) { delete h; }
1394   return 0;
1395 }