Author: [log in to unmask] Date: Thu Apr 16 15:53:48 2015 New Revision: 2724 Log: Some fix up to EcalEventDisplay Driver so it behaves better in monitoring app. Modified: java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java Modified: java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java ============================================================================= --- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java (original) +++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java Thu Apr 16 15:53:48 2015 @@ -13,6 +13,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; + +import javax.swing.SwingUtilities; import org.hps.monitoring.ecal.eventdisplay.ui.PDataEventViewer; import org.hps.monitoring.ecal.eventdisplay.ui.PEventViewer; @@ -28,507 +30,93 @@ import org.lcsim.util.aida.AIDA; /** - * Driver <code>EcalEventDisplay</code> generates the histograms shown - * to the user in the fifth tab of the monitoring application. An - * instance of the single event display is employed to allow for the - * selection of specific crystal channels.<br/> - * <br/> - * The implementation is as follows: - * <ul><li>The event display is opened in a separate window</li> - * <li>It is updated regularly, according to the event refresh rate</li> - * <li>If the user clicks on a crystal, the corresponding energy and time - * distributions (both of type <code>IHistogram1D</code>) are shown in - * the last panel of the monitoring application, as well as a 2D histogram - * (hit time vs. hit energy).The fourth panel reports energy for the crystal.</li> - * + * This <code>Driver</code> creates an instance of the ECAL event display viewer and will generate plots in the + * monitoring app for individual channels that are selected. The event display is updated regularly, according to the + * event refresh rate. If the user clicks on a crystal, the corresponding energy and time distributions (both of type + * <code>IHistogram1D</code>) are shown in the last panel of the monitoring application, as well as a 2D histogram (hit + * time vs. hit energy). The fourth panel reports energy for the crystal. + * * @author Andrea Celentano */ public class EcalEventDisplay extends Driver implements CrystalListener, ActionListener { + private static final String CLUSTER_ENERGY_TITLE = "Cluster Energy (GeV)"; + + // private static final String SIGNAL_DATA_STYLE_COLOR = "orange"; + // private static final String RAW_WAVEFORM_TITLE = "Raw Waveform"; + private static final String HIT_ENERGY_TITLE = "Hit Energy (GeV)"; + // private static final String SIGNAL_TIME_TITLE = "Time (ns)"; + private static final String HIT_TIME_TITLE = "Hit Time (ns)"; + // Plot style and title variables. + private static final String NO_TITLE = ""; + // Class variables. private static final int NUM_CHANNELS = 11 * 47; - + private final AIDA aida = AIDA.defaultInstance(); + // Channel plot lists. + private ArrayList<IHistogram1D> channelEnergyPlot; + + private ArrayList<IHistogram1D> channelTimePlot; + // private ArrayList<IHistogram1D> channelRawWaveform; + private ArrayList<IHistogram2D> channelTimeVsEnergyPlot; + private String clusterCollection = "EcalClusters"; + private ArrayList<IHistogram1D> clusterEnergyPlot; + + private String detectorName; + // private static final String SIGNAL_AMPLITUDE_TITLE = "Signal Amplitude (mV)"; + private int eventRefreshRate = 1; // The number of seconds before an update occurs. + // LCIO Collection names. + private String inputCollection = "EcalCalHits"; + private String inputCollectionRaw = "EcalReadoutHits"; + private final boolean[] isFirstRaw = new boolean[NUM_CHANNELS]; // Whether a waveform plot was initiated for each + // channel. + private long lastEventTime = 0; // Tracks the time at which the last event occurred. + private double maxEch = 3500 * EcalUtils.MeV; // The energy scale maximum. + private double minEch = 10 * EcalUtils.MeV; // The energy scale minimum. + private int pedSamples = 10; // // Plotter objects and variables. private IPlotter plotter; + private IPlotterFactory plotterFactory; - private AIDA aida = AIDA.defaultInstance(); - - // LCIO Collection names. - private String inputCollection = "EcalCalHits"; - private String clusterCollection = "EcalClusters"; - private String inputCollectionRaw = "EcalReadoutHits"; - - // Channel plot lists. - private ArrayList<IHistogram1D> channelEnergyPlot; - private ArrayList<IHistogram1D> clusterEnergyPlot; - private ArrayList<IHistogram1D> channelTimePlot; - //private ArrayList<IHistogram1D> channelRawWaveform; - private ArrayList<IHistogram2D> channelTimeVsEnergyPlot; - + private IPlotterStyle pstyle; // The plotter style for all plots. + private boolean resetOnUpdate = true; // Clears the event display on each update. // Internal variables. - private PEventViewer viewer; // Single event display. - private int pedSamples = 10; // - private IPlotterStyle pstyle; // The plotter style for all plots. - private long lastEventTime = 0; // Tracks the time at which the last event occurred. - private int eventRefreshRate = 1; // The number of seconds before an update occurs. - private boolean resetOnUpdate = true; // Clears the event display on each update. - private double minEch = 10 * EcalUtils.MeV; // The energy scale minimum. - private double maxEch = 3500 * EcalUtils.MeV; // The energy scale maximum. - private int[] windowRaw = new int[NUM_CHANNELS]; // The number of samples in a waveform for each channel. - private boolean[] isFirstRaw = new boolean[NUM_CHANNELS]; // Whether a waveform plot was initiated for each channel. - - // Plot style and title variables. - private static final String NO_TITLE = ""; - //private static final String SIGNAL_TIME_TITLE = "Time (ns)"; - private static final String HIT_TIME_TITLE = "Hit Time (ns)"; - //private static final String SIGNAL_DATA_STYLE_COLOR = "orange"; - //private static final String RAW_WAVEFORM_TITLE = "Raw Waveform"; - private static final String HIT_ENERGY_TITLE = "Hit Energy (GeV)"; - private static final String CLUSTER_ENERGY_TITLE = "Cluster Energy (GeV)"; - private String detectorName; - //private static final String SIGNAL_AMPLITUDE_TITLE = "Signal Amplitude (mV)"; - - /** - * Sets the upper bound of the energy scales used by the driver. - * Energy units are in GeV. - * @param maxEch - The energy scale upper bound. - */ - public void setMaxEch(double maxEch) { - this.maxEch = maxEch; - } - - /** - * Sets the lower bound of the energy scales used by the driver. - * Energy units are in GeV. - * @param minEch - The lower energy scale bound. - */ - public void setMinEch(double minEch) { - this.minEch = minEch; - } - - public void setPedSamples(int pedSamples) { - this.pedSamples = pedSamples; - } - /** - * Sets the LCIO collection name for the processed calorimeter hits. - * @param inputCollection - The LCIO collection name. - */ - public void setInputCollection(String inputCollection) { - this.inputCollection = inputCollection; - } - - /** - * Sets the LCIO collection name for the raw waveform hits. - * @param inputCollectionRaw - The LCIO collection name. - */ - public void setInputCollectionRaw(String inputCollectionRaw) { - this.inputCollectionRaw = inputCollectionRaw; - } - - /** - * Sets the LCIO collection name for calorimeter clusters. - * @param inputClusterCollection - The LCIO collection name. - */ - public void setInputClusterCollection(String inputClusterCollection) { - this.clusterCollection = inputClusterCollection; - } - - /** - * Sets the rate at which the GUI updates its elements, - * @param eventRefreshRate - The rate at which the GUI should be - * updated, in seconds. - */ - public void setEventRefreshRate(int eventRefreshRate) { - this.eventRefreshRate = eventRefreshRate; - } - - /** - * Sets whether the event display should be cleared after event - * or whether it should retain the previously displayed results. - * @param resetOnUpdate - <code>true</code> means that the event - * display should be cleared on each update and <code>false</code> - * that it should not. - */ - public void setResetOnUpdate(boolean resetOnUpdate) { - this.resetOnUpdate = resetOnUpdate; - } - - /** - * Initializes the single channel monitoring plots for all crystal - * channels and defines the plotter region that contains them. - */ - @Override - public void detectorChanged(Detector detector) { - // Reset the AIDA tree directory. - aida.tree().cd("/"); - detectorName=detector.getName(); - // Store histograms for the crystals. - channelEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); - channelTimePlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); - //channelRawWaveform = new ArrayList<IHistogram1D>(NUM_CHANNELS); - clusterEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); - channelTimeVsEnergyPlot = new ArrayList<IHistogram2D>(NUM_CHANNELS); - - // Create the histograms for single channel energy and time - // distribution. - for(int ii = 0; ii < NUM_CHANNELS; ii++) { - // The above instruction is a terrible hack, just to fill - // the arrayList with all the elements. They'll be initialized - // properly during the event readout, Since we want to account - // for possibly different raw waveform dimensions! - - //Get the x and y indices for the current channel. - int row = EcalMonitoringUtilities.getRowFromHistoID(ii); - int column = EcalMonitoringUtilities.getColumnFromHistoID(ii); - - // Initialize the histograms for the current crystal channel. - channelEnergyPlot.add(aida.histogram1D(detectorName + " : " - + inputCollection + " : Hit Energy : " + column + " " + row - + ": " + ii, 100, -0.2, maxEch)); - channelTimePlot.add(aida.histogram1D(detectorName + " : " - + inputCollection + " : Hit Time : " + column + " " + row + ": " - + ii, 100, 0, 400)); - channelTimeVsEnergyPlot.add(aida.histogram2D(detectorName - + " : " + inputCollection + " : Hit Time Vs Energy : " + column - + " " + row + ": " + ii, 100, 0, 400, 100, -0.2, maxEch)); - //channelRawWaveform.add(aida.histogram1D(detector.getDetectorName() + " : " - // + inputCollection + " : Hit Energy : " + column + " " + row + ": " + ii)); - clusterEnergyPlot.add(aida.histogram1D(detectorName + " : " - + inputCollection + " : Cluster Energy : " + column + " " + row - + ": " + ii, 100, -0.2, maxEch)); - - // Note that no raw waveform has yet been read for this - // crystal/channel. - windowRaw[ii] = 1; - isFirstRaw[ii] = true; - } - - // Define the plot region that will display the single channel - // plots in the monitoring application. - plotterFactory = aida.analysisFactory().createPlotterFactory("Single Channel"); - plotter = plotterFactory.create("Single Channel"); - pstyle = this.createDefaultStyle(); - plotter.setTitle(""); - plotter.createRegions(2,2); - - // Define the first plot region. - pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); - pstyle.yAxisStyle().setLabel(NO_TITLE); - plotter.region(0).plot(channelEnergyPlot.get(0), pstyle); - - // Define the second plot region. - pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); - pstyle.yAxisStyle().setLabel(NO_TITLE); - plotter.region(1).plot(channelTimePlot.get(0), pstyle); - - // Define the third plot region; this encompasses the time vs. - // energy plots. - pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); - pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE); - plotter.region(2).plot(channelTimeVsEnergyPlot.get(0), pstyle); - - // Define the fourth plot region; this encompasses the cluster - // energy for each channel. - pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE); - pstyle.yAxisStyle().setLabel(NO_TITLE); - plotter.region(3).plot(clusterEnergyPlot.get(0), pstyle); - - /** - // Define the fourth plot region; this encompasses the raw - // wave form plots. - pstyle.xAxisStyle().setLabel(RAW_WAVEFORM_TITLE); - pstyle.yAxisStyle().setLabel(NO_TITLE); - pstyle.dataStyle().fillStyle().setColor(SIGNAL_DATA_STYLE_COLOR); - pstyle.dataStyle().markerStyle().setColor(SIGNAL_DATA_STYLE_COLOR); - pstyle.dataStyle().errorBarStyle().setVisible(false); - plotter.region(3).plot(channelRawWaveform.get(0), pstyle); - **/ - - // Display the plot region. - plotter.show(); - - // Set the time tracker variables. - lastEventTime = 0; - } - - /** - * Initializes the <code>Viewer</code> for the single event display. - * If a configuration file is available, then it is used by the - * <code>Viewer</code> to display hardware configuration mappings. - * Otherwise, this is excluded. - */ - @Override - public void startOfData() { + private final PEventViewer viewer; // Single event display. + private final int[] windowRaw = new int[NUM_CHANNELS]; // The number of samples in a waveform for each channel. + + public EcalEventDisplay() { // Check if the configuration mapping file exists. - File config = new File("ecal-mapping-config.csv"); + final File config = new File("ecal-mapping-config.csv"); // If the file exists, load the viewer that will display it. - if(config.exists() && config.canRead()) { + if (config.exists() && config.canRead()) { // Account for IO read errors. Only load this version if // the data file can be read successfully. - try { viewer = new PDataEventViewer(config.getAbsolutePath()); } - + try { + this.viewer = new PDataEventViewer(config.getAbsolutePath()); + } // Otherwise, open the regular version. - catch (IOException e) { viewer = new PEventViewer(); } - } - - // If the file is not present, then just load the normal version. - else { viewer = new PEventViewer(); } - - // Set the viewer properties. - viewer.setScaleMinimum(minEch); - viewer.setScaleMaximum(maxEch); - viewer.addCrystalListener(this); - - // Make the Viewer object visible. - viewer.setVisible(true); - } - - /** - * Hides the single event display and disposes it from memory. - * Also removes histograms from aida tree. We do not want them in the output aida file, if any.. - */ - @Override - public void endOfData() { - viewer.setVisible(false); - viewer.dispose(); - - int row,column; - String hName; - //System.out.println("EcalEventDisplay endOfData clear histograms"); - for(int ii = 0; ii < NUM_CHANNELS; ii++) { - // The above instruction is a terrible hack, just to fill - // the arrayList with all the elements. They'll be initialized - // properly during the event readout, Since we want to account - // for possibly different raw waveform dimensions! - - //Get the x and y indices for the current channel. - row = EcalMonitoringUtilities.getRowFromHistoID(ii); - column = EcalMonitoringUtilities.getColumnFromHistoID(ii); - hName=detectorName + " : " - + inputCollection + " : Hit Energy : " + column + " " + row - + ": " + ii; - aida.tree().rm(hName); - - hName=detectorName + " : " - + inputCollection + " : Hit Time : " + column + " " + row + ": " - + ii; - aida.tree().rm(hName); - - hName=detectorName+ " : " + inputCollection + " : Hit Time Vs Energy : " + column - + " " + row + ": " + ii; - aida.tree().rm(hName); - - - if (isFirstRaw[ii]==false){ - hName=detectorName+ - " : " + inputCollectionRaw + " : Raw Waveform : " + column + " " - + row + ": " + ii; - aida.tree().rm(hName); - - } - - } - //System.out.println("EcalEventDisplay endOfData clear histograms done"); - } - - @Override - public void process(EventHeader event){ - // Check whether enough time has passed to perform an update - // on the event display. - boolean update = false; - long currentTime = System.currentTimeMillis() / 1000; - if((currentTime - lastEventTime) > eventRefreshRate){ - lastEventTime = currentTime; - update = true; - } - - // If an update should be made, perform the update. - if(update && resetOnUpdate) { viewer.resetDisplay(); } - - // If the event has calorimeter hit objects... - if(event.hasCollection(CalorimeterHit.class, inputCollection)) { - // Get the list of calorimeter hits. - List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputCollection); - - // For each of the calorimeter hits... - for (CalorimeterHit hit : hits) { - // Get the x and y indices for the current hit. - int ix = hit.getIdentifierFieldValue("ix"); - int iy = hit.getIdentifierFieldValue("iy"); - - if (iy != 0 && ix != 0) { - // Get the histogram index for the hit. - int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix); - - // If the hit has energy, populate the plots. - if(hit.getCorrectedEnergy() > 0) { - channelEnergyPlot.get(id).fill(hit.getCorrectedEnergy()); - channelTimePlot.get(id).fill(hit.getTime()); - channelTimeVsEnergyPlot.get(id).fill(hit.getTime(), hit.getCorrectedEnergy()); - } - - // If an update to the event display should be - // performed, give it the hits. - if(update) { viewer.addHit(hit); } - } - } - } - - // If there are clusters in the event... - if (event.hasCollection(Cluster.class, clusterCollection)) { - // Get the list of clusters. - List<Cluster> clusters = event.get(Cluster.class, clusterCollection); - - // Iterate over the clusters and add them to the event - // display if appropriate. - for (Cluster cluster : clusters) { - // Get the ix and iy indices for the seed. - int ix = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"); - int iy = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"); - - // Get the histogram index for the hit. - int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix); - - // Add the cluster energy to the plot. - if(cluster.getEnergy() > 0.0) { - clusterEnergyPlot.get(id).fill(cluster.getEnergy()); - } - - // If an update is needed, add the cluster to the viewer. - if(update) { viewer.addCluster(cluster); } - } - } - - /** - // Plot the raw waveform only if raw tracker hit exist in the - // event. - if (event.hasCollection(RawTrackerHit.class, inputCollectionRaw)){ - // Get the list of raw tracker hits. - List<RawTrackerHit> hits = event.get(RawTrackerHit.class, inputCollectionRaw); - - // Process each raw tracker hit. - for (RawTrackerHit hit : hits) { - // Get the x and y indices for the hit. - int ix = hit.getIdentifierFieldValue("ix"); - int iy = hit.getIdentifierFieldValue("iy"); - - if(iy != 0 && ix != 0) { - if(!ECalUtils.isInHole(iy, ix)) { - // Get the crystal ID for the current hit. - int id = ECalUtils.getHistoIDFromRowColumn(iy, ix); - - // The window is length is not known by default. - // If this is the first hit, read the window - // length and initialize the plot. - if(isFirstRaw[id]) { - // Note that this plot is initialized. - isFirstRaw[id] = false; - - // Set the waveform array. - windowRaw[id] = hit.getADCValues().length; - - // Initialize the waveform plot. - channelRawWaveform.set(id, aida.histogram1D(detector.getDetectorName() - + " : " + inputCollectionRaw + " : Raw Waveform : " + ix + " " - + iy + ": " + id, windowRaw[id], -0.5 * ECalUtils.ecalReadoutPeriod, - (-0.5 + windowRaw[id]) * ECalUtils.ecalReadoutPeriod)); - } - - // If the plot should be updated, do so. - if(update) { - channelRawWaveform.get(id).reset(); - for (int jj = 0; jj < windowRaw[id]; jj++) { - channelRawWaveform.get(id).fill(jj * ECalUtils.ecalReadoutPeriod, - hit.getADCValues()[jj] * ECalUtils.adcResolution * 1000); - } - double[] result = ECalUtils.computeAmplitude(hit.getADCValues(), windowRaw[id], pedSamples); - channelRawWaveform.get(id).setTitle("Ampl: " + String.format("%.2f", result[0]) - + " mV , ped : " + String.format("%.2f", result[1]) + " " - + String.format("%.2f", result[2]) + " ADC counts"); - plotter.region(3).refresh(); - } - } - } - } - } - **/ - - // Update the single event display. - if(update) { viewer.updateDisplay(); } - } - - @Override - public void actionPerformed(ActionEvent ae) { } - - @Override - public void crystalActivated(CrystalEvent e) { } - - @Override - public void crystalDeactivated(CrystalEvent e) { } - - /** - * Updates the monitoring plots for the crystal that was clicked. - */ - @Override - public void crystalClicked(CrystalEvent e) { - // Get the crystal that was clicked in the LCSim coordinate system. - Point ecalPoint = Viewer.toEcalPoint(e.getCrystalID()); - - // Make sure that the clicked crystal is valid. Necessary?? - if((ecalPoint.x != 0) && (ecalPoint.y != 0)) - if (!EcalMonitoringUtilities.isInHole(ecalPoint.y, ecalPoint.x)) { - // Get the crystal ID. - int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(ecalPoint.y, ecalPoint.x); - - // Clear and replot region 0 for the new crystal. - plotter.region(0).clear(); - pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); - pstyle.yAxisStyle().setLabel(NO_TITLE); - plotter.region(0).plot(channelEnergyPlot.get(id), pstyle); - - // Clear and replot region 1 for the new crystal. - plotter.region(1).clear(); - pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); - pstyle.yAxisStyle().setLabel(NO_TITLE); - plotter.region(1).plot(channelTimePlot.get(id), pstyle); - - // Clear and replot region 2 for the new crystal. - plotter.region(2).clear(); - pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); - pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE); - plotter.region(2).plot(channelTimeVsEnergyPlot.get(id), pstyle); - - // Process and plot the region 3 plot. - plotter.region(3).clear(); - pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE); - pstyle.yAxisStyle().setLabel(NO_TITLE); - plotter.region(3).plot(clusterEnergyPlot.get(id), pstyle); - - /** - // Process and plot the region 3 plot. - if(!isFirstRaw[id]) { - pstyle.yAxisStyle().setLabel(SIGNAL_AMPLITUDE_TITLE); - pstyle.xAxisStyle().setLabel(SIGNAL_TIME_TITLE); - pstyle.dataStyle().fillStyle().setColor(SIGNAL_DATA_STYLE_COLOR); - pstyle.dataStyle().markerStyle().setColor(SIGNAL_DATA_STYLE_COLOR); - pstyle.dataStyle().errorBarStyle().setVisible(false); - } - else { - pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); - pstyle.yAxisStyle().setLabel(""); - } - plotter.region(3).plot(channelRawWaveform.get(id), pstyle); - **/ - } + catch (final IOException e) { + // Throw an error if this happens as configuration file should be accessible (checked for it already). + throw new RuntimeException(e); + // this.viewer = new PEventViewer(); + } + } else { + // If the file is not present, then just load the normal version. + this.viewer = new PEventViewer(); + } + } + + @Override + public void actionPerformed(final ActionEvent ae) { } /** * Initializes the default style for plots. - * @return Returns an <code>IPlotterStyle</code> object that - * represents the default style for plots. + * + * @return Returns an <code>IPlotterStyle</code> object that represents the default style for plots. */ public IPlotterStyle createDefaultStyle() { - IPlotterStyle pstyle = plotterFactory.createPlotterStyle(); + final IPlotterStyle pstyle = this.plotterFactory.createPlotterStyle(); // Set the appearance of the axes. pstyle.xAxisStyle().labelStyle().setBold(true); pstyle.yAxisStyle().labelStyle().setBold(true); @@ -553,7 +141,7 @@ pstyle.titleStyle().textStyle().setFontSize(20); // Draw caps on error bars. - pstyle.dataStyle().errorBarStyle().setParameter("errorBarDecoration", (new Float(1.0f)).toString()); + pstyle.dataStyle().errorBarStyle().setParameter("errorBarDecoration", new Float(1.0f).toString()); // Turn off grid lines until explicitly enabled. pstyle.gridStyle().setVisible(false); @@ -561,4 +149,390 @@ // Return the style. return pstyle; } + + @Override + public void crystalActivated(final CrystalEvent e) { + } + + /** + * Updates the monitoring plots for the crystal that was clicked. + */ + @Override + public void crystalClicked(final CrystalEvent e) { + // Get the crystal that was clicked in the LCSim coordinate system. + final Point ecalPoint = Viewer.toEcalPoint(e.getCrystalID()); + + // Make sure that the clicked crystal is valid. Necessary?? + if (ecalPoint.x != 0 && ecalPoint.y != 0) { + if (!EcalMonitoringUtilities.isInHole(ecalPoint.y, ecalPoint.x)) { + // Get the crystal ID. + final int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(ecalPoint.y, ecalPoint.x); + + // Clear and replot region 0 for the new crystal. + this.plotter.region(0).clear(); + this.pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); + this.pstyle.yAxisStyle().setLabel(NO_TITLE); + this.plotter.region(0).plot(this.channelEnergyPlot.get(id), this.pstyle); + + // Clear and replot region 1 for the new crystal. + this.plotter.region(1).clear(); + this.pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + this.pstyle.yAxisStyle().setLabel(NO_TITLE); + this.plotter.region(1).plot(this.channelTimePlot.get(id), this.pstyle); + + // Clear and replot region 2 for the new crystal. + this.plotter.region(2).clear(); + this.pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + this.pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE); + this.plotter.region(2).plot(this.channelTimeVsEnergyPlot.get(id), this.pstyle); + + // Process and plot the region 3 plot. + this.plotter.region(3).clear(); + this.pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE); + this.pstyle.yAxisStyle().setLabel(NO_TITLE); + this.plotter.region(3).plot(this.clusterEnergyPlot.get(id), this.pstyle); + + /** + * // Process and plot the region 3 plot. if(!isFirstRaw[id]) { + * pstyle.yAxisStyle().setLabel(SIGNAL_AMPLITUDE_TITLE); + * pstyle.xAxisStyle().setLabel(SIGNAL_TIME_TITLE); + * pstyle.dataStyle().fillStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + * pstyle.dataStyle().markerStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + * pstyle.dataStyle().errorBarStyle().setVisible(false); } else { + * pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); pstyle.yAxisStyle().setLabel(""); } + * plotter.region(3).plot(channelRawWaveform.get(id), pstyle); + **/ + } + } + } + + @Override + public void crystalDeactivated(final CrystalEvent e) { + } + + /** + * Initializes the single channel monitoring plots for all crystal channels and defines the plotter region that + * contains them. + */ + @Override + public void detectorChanged(final Detector detector) { + // Reset the AIDA tree directory. + this.aida.tree().cd("/"); + this.detectorName = detector.getName(); + // Store histograms for the crystals. + this.channelEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); + this.channelTimePlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); + // channelRawWaveform = new ArrayList<IHistogram1D>(NUM_CHANNELS); + this.clusterEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); + this.channelTimeVsEnergyPlot = new ArrayList<IHistogram2D>(NUM_CHANNELS); + + // Create the histograms for single channel energy and time + // distribution. + for (int ii = 0; ii < NUM_CHANNELS; ii++) { + // The above instruction is a terrible hack, just to fill + // the arrayList with all the elements. They'll be initialized + // properly during the event readout, Since we want to account + // for possibly different raw waveform dimensions! + + // Get the x and y indices for the current channel. + final int row = EcalMonitoringUtilities.getRowFromHistoID(ii); + final int column = EcalMonitoringUtilities.getColumnFromHistoID(ii); + + // Initialize the histograms for the current crystal channel. + this.channelEnergyPlot.add(this.aida.histogram1D(this.detectorName + " : " + this.inputCollection + + " : Hit Energy : " + column + " " + row + ": " + ii, 100, -0.2, this.maxEch)); + this.channelTimePlot.add(this.aida.histogram1D(this.detectorName + " : " + this.inputCollection + + " : Hit Time : " + column + " " + row + ": " + ii, 100, 0, 400)); + this.channelTimeVsEnergyPlot + .add(this.aida.histogram2D(this.detectorName + " : " + this.inputCollection + + " : Hit Time Vs Energy : " + column + " " + row + ": " + ii, 100, 0, 400, 100, -0.2, + this.maxEch)); + // channelRawWaveform.add(aida.histogram1D(detector.getDetectorName() + " : " + // + inputCollection + " : Hit Energy : " + column + " " + row + ": " + ii)); + this.clusterEnergyPlot.add(this.aida.histogram1D(this.detectorName + " : " + this.inputCollection + + " : Cluster Energy : " + column + " " + row + ": " + ii, 100, -0.2, this.maxEch)); + + // Note that no raw waveform has yet been read for this + // crystal/channel. + this.windowRaw[ii] = 1; + this.isFirstRaw[ii] = true; + } + + // Define the plot region that will display the single channel + // plots in the monitoring application. + this.plotterFactory = this.aida.analysisFactory().createPlotterFactory("Single Channel"); + this.plotter = this.plotterFactory.create("Single Channel"); + this.pstyle = this.createDefaultStyle(); + this.plotter.setTitle(""); + this.plotter.createRegions(2, 2); + + // Define the first plot region. + this.pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); + this.pstyle.yAxisStyle().setLabel(NO_TITLE); + this.plotter.region(0).plot(this.channelEnergyPlot.get(0), this.pstyle); + + // Define the second plot region. + this.pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + this.pstyle.yAxisStyle().setLabel(NO_TITLE); + this.plotter.region(1).plot(this.channelTimePlot.get(0), this.pstyle); + + // Define the third plot region; this encompasses the time vs. + // energy plots. + this.pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + this.pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE); + this.plotter.region(2).plot(this.channelTimeVsEnergyPlot.get(0), this.pstyle); + + // Define the fourth plot region; this encompasses the cluster + // energy for each channel. + this.pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE); + this.pstyle.yAxisStyle().setLabel(NO_TITLE); + this.plotter.region(3).plot(this.clusterEnergyPlot.get(0), this.pstyle); + + /** + * // Define the fourth plot region; this encompasses the raw // wave form plots. + * pstyle.xAxisStyle().setLabel(RAW_WAVEFORM_TITLE); pstyle.yAxisStyle().setLabel(NO_TITLE); + * pstyle.dataStyle().fillStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + * pstyle.dataStyle().markerStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + * pstyle.dataStyle().errorBarStyle().setVisible(false); plotter.region(3).plot(channelRawWaveform.get(0), + * pstyle); + **/ + + // Display the plot region. + this.plotter.show(); + + // Set the time tracker variables. + this.lastEventTime = 0; + } + + /** + * Hides the single event display and disposes it from memory. Also removes histograms from aida tree. We do not + * want them in the output aida file, if any.. + */ + @Override + public void endOfData() { + // Disposing of event display window needs to happen on the Swing EDT. + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + System.out.println("turning ECAL event display off"); + EcalEventDisplay.this.viewer.setVisible(false); + System.out.println("disposing ECAL event display ..."); + EcalEventDisplay.this.viewer.dispose(); + System.out.println("done disposing ECAL event display"); + } + }); + // Comment this out for now. It is causing problems. --JM + /* + * int row,column; String hName; //System.out.println("EcalEventDisplay endOfData clear histograms"); for(int ii + * = 0; ii < NUM_CHANNELS; ii++) { // The above instruction is a terrible hack, just to fill // the arrayList + * with all the elements. They'll be initialized // properly during the event readout, Since we want to account + * // for possibly different raw waveform dimensions! //Get the x and y indices for the current channel. row = + * EcalMonitoringUtilities.getRowFromHistoID(ii); column = EcalMonitoringUtilities.getColumnFromHistoID(ii); + * hName=detectorName + " : " + inputCollection + " : Hit Energy : " + column + " " + row + ": " + ii; + * aida.tree().rm(hName); hName=detectorName + " : " + inputCollection + " : Hit Time : " + column + " " + row + + * ": " + ii; aida.tree().rm(hName); hName=detectorName+ " : " + inputCollection + " : Hit Time Vs Energy : " + + * column + " " + row + ": " + ii; aida.tree().rm(hName); if (isFirstRaw[ii]==false){ hName=detectorName+ " : " + * + inputCollectionRaw + " : Raw Waveform : " + column + " " + row + ": " + ii; aida.tree().rm(hName); } } + */ + // System.out.println("EcalEventDisplay endOfData clear histograms done"); + } + + @Override + public void process(final EventHeader event) { + // Check whether enough time has passed to perform an update + // on the event display. + boolean update = false; + final long currentTime = System.currentTimeMillis() / 1000; + if (currentTime - this.lastEventTime > this.eventRefreshRate) { + this.lastEventTime = currentTime; + update = true; + } + + // If an update should be made, perform the update. + if (update && this.resetOnUpdate) { + this.viewer.resetDisplay(); + } + + // If the event has calorimeter hit objects... + if (event.hasCollection(CalorimeterHit.class, this.inputCollection)) { + // Get the list of calorimeter hits. + final List<CalorimeterHit> hits = event.get(CalorimeterHit.class, this.inputCollection); + + // For each of the calorimeter hits... + for (final CalorimeterHit hit : hits) { + // Get the x and y indices for the current hit. + final int ix = hit.getIdentifierFieldValue("ix"); + final int iy = hit.getIdentifierFieldValue("iy"); + + if (iy != 0 && ix != 0) { + // Get the histogram index for the hit. + final int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix); + + // If the hit has energy, populate the plots. + if (hit.getCorrectedEnergy() > 0) { + this.channelEnergyPlot.get(id).fill(hit.getCorrectedEnergy()); + this.channelTimePlot.get(id).fill(hit.getTime()); + this.channelTimeVsEnergyPlot.get(id).fill(hit.getTime(), hit.getCorrectedEnergy()); + } + + // If an update to the event display should be + // performed, give it the hits. + if (update) { + this.viewer.addHit(hit); + } + } + } + } + + // If there are clusters in the event... + if (event.hasCollection(Cluster.class, this.clusterCollection)) { + // Get the list of clusters. + final List<Cluster> clusters = event.get(Cluster.class, this.clusterCollection); + + // Iterate over the clusters and add them to the event + // display if appropriate. + for (final Cluster cluster : clusters) { + // Get the ix and iy indices for the seed. + final int ix = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"); + final int iy = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"); + + // Get the histogram index for the hit. + final int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix); + + // Add the cluster energy to the plot. + if (cluster.getEnergy() > 0.0) { + this.clusterEnergyPlot.get(id).fill(cluster.getEnergy()); + } + + // If an update is needed, add the cluster to the viewer. + if (update) { + this.viewer.addCluster(cluster); + } + } + } + + /** + * // Plot the raw waveform only if raw tracker hit exist in the // event. if + * (event.hasCollection(RawTrackerHit.class, inputCollectionRaw)){ // Get the list of raw tracker hits. + * List<RawTrackerHit> hits = event.get(RawTrackerHit.class, inputCollectionRaw); + * + * // Process each raw tracker hit. for (RawTrackerHit hit : hits) { // Get the x and y indices for the hit. int + * ix = hit.getIdentifierFieldValue("ix"); int iy = hit.getIdentifierFieldValue("iy"); + * + * if(iy != 0 && ix != 0) { if(!ECalUtils.isInHole(iy, ix)) { // Get the crystal ID for the current hit. int id + * = ECalUtils.getHistoIDFromRowColumn(iy, ix); + * + * // The window is length is not known by default. // If this is the first hit, read the window // length and + * initialize the plot. if(isFirstRaw[id]) { // Note that this plot is initialized. isFirstRaw[id] = false; + * + * // Set the waveform array. windowRaw[id] = hit.getADCValues().length; + * + * // Initialize the waveform plot. channelRawWaveform.set(id, aida.histogram1D(detector.getDetectorName() + + * " : " + inputCollectionRaw + " : Raw Waveform : " + ix + " " + iy + ": " + id, windowRaw[id], -0.5 * + * ECalUtils.ecalReadoutPeriod, (-0.5 + windowRaw[id]) * ECalUtils.ecalReadoutPeriod)); } + * + * // If the plot should be updated, do so. if(update) { channelRawWaveform.get(id).reset(); for (int jj = 0; jj + * < windowRaw[id]; jj++) { channelRawWaveform.get(id).fill(jj * ECalUtils.ecalReadoutPeriod, + * hit.getADCValues()[jj] * ECalUtils.adcResolution * 1000); } double[] result = + * ECalUtils.computeAmplitude(hit.getADCValues(), windowRaw[id], pedSamples); + * channelRawWaveform.get(id).setTitle("Ampl: " + String.format("%.2f", result[0]) + " mV , ped : " + + * String.format("%.2f", result[1]) + " " + String.format("%.2f", result[2]) + " ADC counts"); + * plotter.region(3).refresh(); } } } } } + **/ + + // Update the single event display. + if (update) { + this.viewer.updateDisplay(); + } + } + + /** + * Sets the rate at which the GUI updates its elements, + * + * @param eventRefreshRate - The rate at which the GUI should be updated, in seconds. + */ + public void setEventRefreshRate(final int eventRefreshRate) { + this.eventRefreshRate = eventRefreshRate; + } + + /** + * Sets the LCIO collection name for calorimeter clusters. + * + * @param inputClusterCollection - The LCIO collection name. + */ + public void setInputClusterCollection(final String inputClusterCollection) { + this.clusterCollection = inputClusterCollection; + } + + /** + * Sets the LCIO collection name for the processed calorimeter hits. + * + * @param inputCollection - The LCIO collection name. + */ + public void setInputCollection(final String inputCollection) { + this.inputCollection = inputCollection; + } + + /** + * Sets the LCIO collection name for the raw waveform hits. + * + * @param inputCollectionRaw - The LCIO collection name. + */ + public void setInputCollectionRaw(final String inputCollectionRaw) { + this.inputCollectionRaw = inputCollectionRaw; + } + + /** + * Sets the upper bound of the energy scales used by the driver. Energy units are in GeV. + * + * @param maxEch - The energy scale upper bound. + */ + public void setMaxEch(final double maxEch) { + this.maxEch = maxEch; + } + + /** + * Sets the lower bound of the energy scales used by the driver. Energy units are in GeV. + * + * @param minEch - The lower energy scale bound. + */ + public void setMinEch(final double minEch) { + this.minEch = minEch; + } + + public void setPedSamples(final int pedSamples) { + this.pedSamples = pedSamples; + } + + /** + * Sets whether the event display should be cleared after event or whether it should retain the previously displayed + * results. + * + * @param resetOnUpdate - <code>true</code> means that the event display should be cleared on each update and + * <code>false</code> that it should not. + */ + public void setResetOnUpdate(final boolean resetOnUpdate) { + this.resetOnUpdate = resetOnUpdate; + } + + /** + * Initializes the <code>Viewer</code> for the single event display. If a configuration file is available, then it + * is used by the <code>Viewer</code> to display hardware configuration mappings. Otherwise, this is excluded. + */ + @Override + public void startOfData() { + + // Set the viewer properties from the Driver configuration parameters. + this.viewer.setScaleMinimum(this.minEch); + this.viewer.setScaleMaximum(this.maxEch); + this.viewer.addCrystalListener(this); + + // Make the Viewer object visible. Run on the Swing EDT. + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + EcalEventDisplay.this.viewer.setVisible(true); + } + }); + } }