Author: [log in to unmask] Date: Thu Mar 5 13:46:25 2015 New Revision: 2273 Log: Added monitoring components for the trigger diagnostics to their new home package. Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTablePanel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTriggerTablePanel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTwoColumnTablePanel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ComponentUtils.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/DiagnosticUpdatable.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/EfficiencyTablePanel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/PairTablePanel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/SinglesTablePanel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TriggerDiagnosticGUIDriver.java Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTablePanel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTablePanel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTablePanel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,249 @@ +package org.hps.monitoring.trigger; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTable; + +import org.hps.analysis.trigger.util.ComponentUtils; + +/** + * Class <code>AbstractTablePanel</code> displays two <code>JTable</code> + * objects side-by-side with headers above them. The left table displays + * statistical data for recent events processed with trigger diagnostics + * while the right table displays the same, but over the course of the + * entire run.<br/> + * <br/> + * This implements the interface <code>DiagnosticUpdatable</code>. + * + * @author Kyle McCarty <[log in to unmask]> + * @see JPanel + * @see DiagnosticUpdatable + */ +public abstract class AbstractTablePanel extends JPanel implements DiagnosticUpdatable { + // Static variables. + private static final long serialVersionUID = 0L; + public static final int ORIENTATION_HORIZONTAL = 0; + public static final int ORIENTATION_VERTICAL = 1; + + // Components. + private JLabel localHeader; + private JLabel globalHeader; + protected final JTable localTable; + protected final JTable globalTable; + + // Component parameters. + private boolean horizontal = true; + private Dimension userPrefSize = null; + private Dimension defaultPrefSize = new Dimension(0, 0); + + /** + * Instantiates an <code>AbstractTablePanel</code>. + * @param args Arguments to be usd when generating the panel tables. + */ + public AbstractTablePanel(Object... args) { + // Initialize the tables. + JTable[] tables = initializeTables(args); + localTable = tables[0]; + globalTable = tables[1]; + add(globalTable); + add(localTable); + + // Set the panels to their null starting values. + updatePanel(null); + + // Define the panel layout. + setLayout(null); + + // Create header labels for the tables. + localHeader = new JLabel("Local Statistics"); + localHeader.setHorizontalAlignment(JLabel.CENTER); + add(localHeader); + + globalHeader = new JLabel("Run Statistics"); + globalHeader.setHorizontalAlignment(JLabel.CENTER); + add(globalHeader); + + // Track when the component changes size and reposition the + // components accordingly. + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { positionComponents(); } + }); + + // Define the component preferred size. + defaultPrefSize.width = localTable.getPreferredSize().width + + ComponentUtils.hinternal + globalTable.getPreferredSize().width; + defaultPrefSize.height = localTable.getPreferredSize().height + + ComponentUtils.vinternal + globalTable.getPreferredSize().height; + } + + @Override + public Dimension getPreferredSize() { + // If there is a user-specified preferred size, return that. + if(userPrefSize == null) { return defaultPrefSize; } + + // Otherwise, return the default calculated preferred size. + else { return userPrefSize; } + } + + @Override + public void setBackground(Color bg) { + // Set the base component background. + super.setBackground(bg); + + // If the components have been initialized, pass the background + // color change to them as appropriate. Note that the tables + // will always retain the same background color. + if(localTable != null) { + // Set the header backgrounds. + localHeader.setBackground(bg); + globalHeader.setBackground(bg); + } + } + + @Override + public void setFont(Font font) { + // Set the base component font. + super.setFont(font); + + // If the components have been initialized, pass the font change + // to them as appropriate. + if(localTable != null) { + // Set the table fonts. + localTable.setFont(font); + globalTable.setFont(font); + + // Set the header fonts. + Font headerFont = font.deriveFont(Font.BOLD, (float) Math.ceil(font.getSize2D() * 1.3)); + localHeader.setFont(headerFont); + globalHeader.setFont(headerFont); + } + } + + @Override + public void setForeground(Color fg) { + // Set the base component foreground. + super.setForeground(fg); + + // If the components have been initialized, pass the foreground + // color change to them as appropriate. Note that the tables + // will always retain the same foreground color. + if(localTable != null) { + // Set the header foregrounds. + localHeader.setForeground(fg); + globalHeader.setForeground(fg); + } + } + + /** + * Sets the orientation of components on the panel. + * @param orientation - The orientation identifier. Identifiers can + * be obtained as static variables from the within the root object + * <code>AbstractTable</code>. + */ + public void setOrientation(int orientation) { + if(orientation == ORIENTATION_HORIZONTAL) { + if(!horizontal) { + horizontal = true; + positionComponents(); + } + } else if(orientation == ORIENTATION_VERTICAL) { + if(horizontal) { + horizontal = false; + positionComponents(); + } + } else { + throw new IllegalArgumentException("Invalid orienation identifier."); + } + } + + @Override + public void setPreferredSize(Dimension preferredSize) { + userPrefSize = preferredSize; + } + + /** + * Generates the two tables that are used by the component. This + * must return an array of size two. + * @param args - Any arguments that should be passed to the method + * for generating tables. + * @return Returns an array of size two, where the first index must + * contain the local table and the second index the global table. + */ + protected abstract JTable[] initializeTables(Object... args); + + /** + * Repositions the components to the correct places on the parent + * <code>JPanel</code>. This should be run whenever the panel + * changes size. + */ + private void positionComponents() { + // Do not update if the components have not been initialized. + if(localHeader == null) { return; } + + // If the components should be position horizontally... + if(horizontal) { + // The local components get the left half of the panel and the + // global components the right. Find half of the panel width, + // accounting for the internal spacing. This is an internal + // component, so it does not employ additional spacing between + // itself and the parent component's edges. + int compWidth = (getWidth() - 10) / 2; + + // If there is any width remaining, it goes to the spacing. + int horizontal = ComponentUtils.hinternal + (getWidth() - 10) % 2; + + // Place the header labels. These are given their preferred + // height. Note that this means a very small panel may cut off + // some of the components. First, get the preferred height of + // the label with the larger preferred height. These should be + // the same thing, but just in case... + int labelHeight = localHeader.getPreferredSize().height; + if(labelHeight < globalHeader.getPreferredSize().height) { + labelHeight = globalHeader.getPreferredSize().height; + } + + // Set the label sizes and positions. + localHeader.setBounds(0, 0, compWidth, labelHeight); + globalHeader.setLocation(ComponentUtils.getNextX(localHeader, horizontal), 0); + globalHeader.setSize(compWidth, labelHeight); + + // The tables go under their respective labels and should fill + // the remainder of the label height. + int tableY = ComponentUtils.getNextY(localHeader, ComponentUtils.vinternal); + localTable.setBounds(0, tableY, compWidth, localTable.getPreferredSize().height); + globalTable.setBounds(globalHeader.getX(), tableY, compWidth, globalTable.getPreferredSize().height); + } + + // Otherwise, position them vertically. + else { + // Place the header labels. These are given their preferred + // height. Note that this means a very small panel may cut off + // some of the components. First, get the preferred height of + // the label with the larger preferred height. These should be + // the same thing, but just in case... + int labelHeight = localHeader.getPreferredSize().height; + if(labelHeight < globalHeader.getPreferredSize().height) { + labelHeight = globalHeader.getPreferredSize().height; + } + + // The local components go first, taking up the entire upper + // width of the panel. + localHeader.setBounds(0, 0, getWidth(), labelHeight); + localTable.setBounds(0, ComponentUtils.getNextY(localHeader, ComponentUtils.vinternal), + getWidth(), localTable.getPreferredSize().height); + + // The global components go immediately below. + globalHeader.setBounds(0, ComponentUtils.getNextY(localTable, ComponentUtils.vinternal), + getWidth(), labelHeight); + globalTable.setBounds(0, ComponentUtils.getNextY(globalHeader, ComponentUtils.vinternal), + getWidth(), globalTable.getPreferredSize().height); + } + } +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTriggerTablePanel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTriggerTablePanel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTriggerTablePanel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,176 @@ +package org.hps.monitoring.trigger; + +import org.hps.analysis.trigger.DiagSnapshot; +import org.hps.analysis.trigger.event.TriggerStatModule; +import org.hps.analysis.trigger.util.ComponentUtils; + +/** + * Abstract class <code>AbstractTriggerTablePanel</code> creates the + * basic framework to display trigger statistics. It is also able to + * update itself from the diagnostic snapshot given certain information, + * which is obtained through the implementation of its abstract methods + * by subclasses. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public abstract class AbstractTriggerTablePanel extends AbstractTwoColumnTablePanel { + // Static variables. + private static final long serialVersionUID = 0L; + + // Internal variables. + private final int numCuts; + + // Reference variables to the default table rows. + protected static final int ROW_RECON_COUNT = 0; + protected static final int ROW_SSP_SIM_COUNT = 1; + protected static final int ROW_SSP_BANK_COUNT = 2; + protected static final int ROW_SSP_EFFICIENCY = 3; + protected static final int ROW_TRIGGER_EFFICIENCY = 4; + protected static final int ROW_EMPTY_SPACE = 5; + protected static final int ROW_CUT_FAILS_TITLE = 6; + protected static final int ROW_FIRST_TRIGGER_CUT = 7; + + /** + * Instantiates an <code>AbstractTriggerTablePanel</code> with the + * indicated cut names. + * @param cutNames + */ + public AbstractTriggerTablePanel(String[] cutNames) { + // Instantiate the superclass. + super(makeTitle(cutNames)); + + // Store the number of cuts. + numCuts = cutNames.length; + updatePanel(null); + } + + @Override + public void updatePanel(DiagSnapshot snapshot) { + // If the snapshot is null, all values should be "N/A." + if(snapshot == null) { + // Output cluster count data. + String scalerNullValue = "---"; + setLocalRowValue(ROW_RECON_COUNT, scalerNullValue); + setLocalRowValue(ROW_SSP_SIM_COUNT, scalerNullValue); + setLocalRowValue(ROW_SSP_BANK_COUNT, scalerNullValue); + setGlobalRowValue(ROW_RECON_COUNT, scalerNullValue); + setGlobalRowValue(ROW_SSP_SIM_COUNT, scalerNullValue); + setGlobalRowValue(ROW_SSP_BANK_COUNT, scalerNullValue); + + // Output the tracked statistical data. + String percentNullValue = "--- / --- (---%)"; + setLocalRowValue(ROW_SSP_EFFICIENCY, percentNullValue); + setLocalRowValue(ROW_TRIGGER_EFFICIENCY, percentNullValue); + setGlobalRowValue(ROW_SSP_EFFICIENCY, percentNullValue); + setGlobalRowValue(ROW_TRIGGER_EFFICIENCY, percentNullValue); + + int ROW_SECOND_TRIGGER_CUT = ROW_FIRST_TRIGGER_CUT + numCuts + 2; + for(int cutRow = 0; cutRow < numCuts; cutRow++) { + setLocalRowValue(cutRow + ROW_FIRST_TRIGGER_CUT, percentNullValue); + setLocalRowValue(cutRow + ROW_SECOND_TRIGGER_CUT, percentNullValue); + setGlobalRowValue(cutRow + ROW_FIRST_TRIGGER_CUT, percentNullValue); + setGlobalRowValue(cutRow + ROW_SECOND_TRIGGER_CUT, percentNullValue); + } + } else { + // Get the local and run trigger statistics from the snapshot. + TriggerStatModule lstat = getLocalModule(snapshot); + TriggerStatModule rstat = getRunModule(snapshot); + + // Determine the most spaces needed to display the values. + // Get the largest number of digits in any of the values. + int mostDigits = ComponentUtils.max(lstat.getReconTriggerCount(), lstat.getSSPBankTriggerCount(), + lstat.getSSPSimTriggerCount(), rstat.getReconTriggerCount(), rstat.getSSPBankTriggerCount(), + rstat.getSSPSimTriggerCount()); + + // Update the single-value counters. + String countFormat = "%" + mostDigits + "d"; + setLocalRowValue(ROW_RECON_COUNT, String.format(countFormat, lstat.getReconTriggerCount())); + setLocalRowValue(ROW_SSP_SIM_COUNT, String.format(countFormat, lstat.getSSPSimTriggerCount())); + setLocalRowValue(ROW_SSP_BANK_COUNT, String.format(countFormat, lstat.getSSPBankTriggerCount())); + setGlobalRowValue(ROW_RECON_COUNT, String.format(countFormat, rstat.getReconTriggerCount())); + setGlobalRowValue(ROW_SSP_SIM_COUNT, String.format(countFormat, rstat.getSSPSimTriggerCount())); + setGlobalRowValue(ROW_SSP_BANK_COUNT, String.format(countFormat, rstat.getSSPBankTriggerCount())); + + // Update the percentage counters. + String percentFormat = "%" + mostDigits + "d / %" + mostDigits + "d (%7.3f)"; + + setLocalRowValue(ROW_SSP_EFFICIENCY, String.format(percentFormat, lstat.getMatchedSSPTriggers(), + lstat.getSSPSimTriggerCount(), (100.0 * lstat.getMatchedSSPTriggers() / lstat.getSSPSimTriggerCount()))); + setLocalRowValue(ROW_TRIGGER_EFFICIENCY, String.format(percentFormat, lstat.getMatchedReconTriggers(), + lstat.getReconTriggerCount(), (100.0 * lstat.getMatchedReconTriggers() / lstat.getReconTriggerCount()))); + setGlobalRowValue(ROW_SSP_EFFICIENCY, String.format(percentFormat, rstat.getMatchedSSPTriggers(), + rstat.getSSPSimTriggerCount(), (100.0 * rstat.getMatchedSSPTriggers() / rstat.getSSPSimTriggerCount()))); + setGlobalRowValue(ROW_TRIGGER_EFFICIENCY, String.format(percentFormat, lstat.getMatchedReconTriggers(), + rstat.getReconTriggerCount(), (100.0 * rstat.getMatchedReconTriggers() / rstat.getReconTriggerCount()))); + + int ROW_SECOND_TRIGGER_CUT = ROW_FIRST_TRIGGER_CUT + numCuts + 2; + int[] total = { lstat.getSSPSimTriggerCount() / 2, rstat.getSSPSimTriggerCount() / 2 }; + for(int cutRow = 0; cutRow < numCuts; cutRow++) { + setLocalRowValue(cutRow + ROW_FIRST_TRIGGER_CUT, String.format(percentFormat, lstat.getCutFailures(0, cutRow), + total[0], (100.0 * lstat.getCutFailures(0, cutRow) / total[0]))); + setLocalRowValue(cutRow + ROW_SECOND_TRIGGER_CUT, String.format(percentFormat, lstat.getCutFailures(1, cutRow), + total[0], (100.0 * lstat.getCutFailures(1, cutRow) / total[0]))); + setGlobalRowValue(cutRow + ROW_FIRST_TRIGGER_CUT, String.format(percentFormat, lstat.getCutFailures(0, cutRow), + total[1], (100.0 * lstat.getCutFailures(0, cutRow) / total[1]))); + setGlobalRowValue(cutRow + ROW_SECOND_TRIGGER_CUT, String.format(percentFormat, lstat.getCutFailures(1, cutRow), + total[1], (100.0 * lstat.getCutFailures(1, cutRow) / total[1]))); + } + } + } + + /** + * Gets the statistical module from which local statistics should + * be drawn. + * @param snapshot - The snapshot containing the modules. + * @return Returns the module containing local statistical data. + */ + protected abstract TriggerStatModule getLocalModule(DiagSnapshot snapshot); + + /** + * Gets the statistical module from which run statistics should + * be drawn. + * @param snapshot - The snapshot containing the modules. + * @return Returns the module containing run statistical data. + */ + protected abstract TriggerStatModule getRunModule(DiagSnapshot snapshot); + + /** + * Creates the table appropriate table rows from the argument cut + * names. + * @param cutNames - An array containing the names of the cuts to + * display. + * @return Returns an array with the default table rows merged in + * with the provided cut names. + */ + private static final String[] makeTitle(String[] cutNames) { + // Make a new array to hold all the text. + String[] mergedArray = new String[cutNames.length + cutNames.length + 9]; + + // Define the default trigger headers. + mergedArray[0] = "Recon Triggers:"; + mergedArray[1] = "SSP Sim Triggers:"; + mergedArray[2] = "SSP Bank Triggers:"; + mergedArray[3] = "SSP Efficiency:"; + mergedArray[4] = "Trigger Efficiency:"; + mergedArray[5] = ""; + mergedArray[6] = "First Trigger Cut Failures"; + + // Insert the cut names for the first trigger. + for(int cutIndex = 0; cutIndex < cutNames.length; cutIndex++) { + mergedArray[7 + cutIndex] = cutNames[cutIndex]; + } + + // Insert the header for the second trigger cut names. + int startIndex = 7 + cutNames.length; + mergedArray[startIndex] = ""; + mergedArray[startIndex + 1] = "Second Trigger Cut Failures"; + + // Insert the next set of cut names. + for(int cutIndex = 0; cutIndex < cutNames.length; cutIndex++) { + mergedArray[startIndex + 2 + cutIndex] = cutNames[cutIndex]; + } + + // Return the resultant array. + return mergedArray; + } +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTwoColumnTablePanel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTwoColumnTablePanel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/AbstractTwoColumnTablePanel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,93 @@ +package org.hps.monitoring.trigger; + +import javax.swing.JTable; + +/** + * Class <code>AbstractTwoColumnTablePanel</code> is an implementation + * of <code>AbstractTablePanel</code> that specifically handles tables + * with two columns where the first column's cells are row headers and + * the second column contains values. + * + * @author Kyle McCarty <[log in to unmask]> + * @see AbstractTablePanel + */ +public abstract class AbstractTwoColumnTablePanel extends AbstractTablePanel { + // Static variables. + private static final long serialVersionUID = 0L; + + // Table models. + private TableTextModel localModel; + private TableTextModel globalModel; + + // Table model mappings. + private static final int COL_TITLE = 0; + private static final int COL_VALUE = 1; + + /** + * Instantiates an <code>AbstractTwoColumnTablePanel</code> object + * with the indicated row names. + * @param rowNames - The names of the rows. + */ + public AbstractTwoColumnTablePanel(String[] rowNames) { + super((Object[]) rowNames); + } + + @Override + protected JTable[] initializeTables(Object... args) { + // The arguments should be a string array. + if(!(args instanceof String[])) { + throw new IllegalArgumentException("Row names must be strings!"); + } + String[] rowNames = (String[]) args; + + // Initialize the table models. They should have two columns + // (one for values and one for headers) and a number of rows + // equal to the number of row names. + localModel = new TableTextModel(rowNames.length, 2); + globalModel = new TableTextModel(rowNames.length, 2); + + // Initialize the titles. + for(int i = 0; i < rowNames.length; i++) { + localModel.setValueAt(rowNames[i], i, COL_TITLE); + globalModel.setValueAt(rowNames[i], i, COL_TITLE); + } + updatePanel(null); + + // Create JTable objects to display the data. + JTable localTable = new JTable(localModel); + localTable.setRowSelectionAllowed(false); + localTable.setColumnSelectionAllowed(false); + localTable.setCellSelectionEnabled(false); + localTable.setShowVerticalLines(false); + + JTable globalTable = new JTable(globalModel); + globalTable.setRowSelectionAllowed(false); + globalTable.setColumnSelectionAllowed(false); + globalTable.setCellSelectionEnabled(false); + globalTable.setShowVerticalLines(false); + + // Return the two tables. + return new JTable[] { localTable, globalTable }; + } + + /** + * Sets the value of the indicated row for the global statistical + * table. + * @param rowIndex - The row. + * @param value - The new value. + */ + protected void setGlobalRowValue(int rowIndex, String value) { + globalModel.setValueAt(value, rowIndex, COL_VALUE); + } + + /** + * Sets the value of the indicated row for the local statistical + * table. + * @param rowIndex - The row. + * @param value - The new value. + */ + protected void setLocalRowValue(int rowIndex, String value) { + localModel.setValueAt(value, rowIndex, COL_VALUE); + } + +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,111 @@ +package org.hps.monitoring.trigger; + +import org.hps.analysis.trigger.DiagSnapshot; +import org.hps.analysis.trigger.event.ClusterStatModule; +import org.hps.analysis.trigger.util.ComponentUtils; + +/** + * Class <code>ClusterTablePanel</code> is an implementation of class + * <code>AbstractTablePanel</code> for cluster statistical data.<br/> + * <br/> + * This implements the interface <code>DiagnosticUpdatable</code>. + * + * @author Kyle McCarty <[log in to unmask]> + * @see AbstractTablePanel + */ +public class ClusterTablePanel extends AbstractTwoColumnTablePanel { + // Static variables. + private static final long serialVersionUID = 0L; + private static final String[] TABLE_TITLES = { "Recon Clusters", "SSP Clusters", "Matched Clusters", + "Failed (Position)", "Failed (Energy)", "Failed (Hit Count)" }; + + // Table model mappings. + private static final int ROW_RECON_COUNT = 0; + private static final int ROW_SSP_COUNT = 1; + private static final int ROW_MATCHED = 2; + private static final int ROW_FAILED_POSITION = 3; + private static final int ROW_FAILED_ENERGY = 4; + private static final int ROW_FAILED_HIT_COUNT = 5; + + /** + * Instantiate a new <code>ClusterTablePanel</code>. + */ + public ClusterTablePanel() { super(TABLE_TITLES); } + + @Override + public void updatePanel(DiagSnapshot snapshot) { + // If the snapshot is null, all values should be "N/A." + if(snapshot == null) { + // Output cluster count data. + String scalerNullValue = "---"; + setLocalRowValue(ROW_RECON_COUNT, scalerNullValue); + setLocalRowValue(ROW_SSP_COUNT, scalerNullValue); + setGlobalRowValue(ROW_RECON_COUNT, scalerNullValue); + setGlobalRowValue(ROW_SSP_COUNT, scalerNullValue); + + // Output the tracked statistical data. + String percentNullValue = "--- / --- (---%)"; + setLocalRowValue(ROW_MATCHED, percentNullValue); + setLocalRowValue(ROW_FAILED_POSITION, percentNullValue); + setLocalRowValue(ROW_FAILED_ENERGY, percentNullValue); + setLocalRowValue(ROW_FAILED_HIT_COUNT, percentNullValue); + setGlobalRowValue(ROW_MATCHED, percentNullValue); + setGlobalRowValue(ROW_FAILED_POSITION, percentNullValue); + setGlobalRowValue(ROW_FAILED_ENERGY, percentNullValue); + setGlobalRowValue(ROW_FAILED_HIT_COUNT, percentNullValue); + } + + // Otherwise, populate the table with the diagnostic data. + else { + // Get the cluster statistical banks. + ClusterStatModule lstat = snapshot.clusterLocalStatistics; + ClusterStatModule rstat = snapshot.clusterRunStatistics; + + // Get the largest number of digits in any of the values. + int mostDigits = ComponentUtils.max(lstat.getReconClusterCount(), lstat.getSSPClusterCount(), lstat.getMatches(), + lstat.getPositionFailures(), lstat.getEnergyFailures(), lstat.getHitCountFailures(), + rstat.getReconClusterCount(), rstat.getSSPClusterCount(), rstat.getMatches(), + rstat.getPositionFailures(), rstat.getEnergyFailures(), rstat.getHitCountFailures()); + + // Put the number of reconstructed and SSP clusters into + // the tables. + int[] clusterValue = { + lstat.getReconClusterCount(), + lstat.getSSPClusterCount(), + rstat.getReconClusterCount(), + rstat.getSSPClusterCount() + }; + String countFormat = "%" + mostDigits + "d"; + setLocalRowValue(ROW_RECON_COUNT, String.format(countFormat, clusterValue[0])); + setLocalRowValue(ROW_SSP_COUNT, String.format(countFormat, clusterValue[1])); + setGlobalRowValue(ROW_RECON_COUNT, String.format(countFormat, clusterValue[2])); + setGlobalRowValue(ROW_SSP_COUNT, String.format(countFormat, clusterValue[3])); + + // Output the tracked statistical data. + int total; + String percentFormat = "%" + mostDigits + "d / %" + mostDigits + "d (%7.3f)"; + int[] statValue = { + lstat.getMatches(), + lstat.getPositionFailures(), + lstat.getEnergyFailures(), + lstat.getHitCountFailures(), + rstat.getMatches(), + rstat.getPositionFailures(), + rstat.getEnergyFailures(), + rstat.getHitCountFailures() + }; + + total = lstat.getReconClusterCount(); + setLocalRowValue(ROW_MATCHED, String.format(percentFormat, statValue[0], total, 100.0 * statValue[0] / total)); + setLocalRowValue(ROW_FAILED_POSITION, String.format(percentFormat, statValue[1], total, 100.0 * statValue[1] / total)); + setLocalRowValue(ROW_FAILED_ENERGY, String.format(percentFormat, statValue[2], total, 100.0 * statValue[2] / total)); + setLocalRowValue(ROW_FAILED_HIT_COUNT, String.format(percentFormat, statValue[3], total, 100.0 * statValue[3] / total)); + + total = rstat.getReconClusterCount(); + setGlobalRowValue(ROW_MATCHED, String.format(percentFormat, statValue[4], total, 100.0 * statValue[4] / total)); + setGlobalRowValue(ROW_FAILED_POSITION, String.format(percentFormat, statValue[5], total, 100.0 * statValue[5] / total)); + setGlobalRowValue(ROW_FAILED_ENERGY, String.format(percentFormat, statValue[6], total, 100.0 * statValue[6] / total)); + setGlobalRowValue(ROW_FAILED_HIT_COUNT, String.format(percentFormat, statValue[7], total, 100.0 * statValue[7] / total)); + } + } +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ComponentUtils.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ComponentUtils.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/ComponentUtils.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,126 @@ +package org.hps.monitoring.trigger; + +import java.awt.Component; + +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; + +/** + * Class <code>ComponentUtils</code> is a list of utility methods used + * by the trigger diagnostic GUI. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class ComponentUtils { + /** The default spacing used between a horizontal edge of one + * component and the horizontal edge of another. */ + static final int hinternal = 10; + /** The default spacing used between a vertical edge of one + * component and the vertical edge of another. */ + static final int vinternal = 10; + /** The default spacing used between a horizontal edge of one + * component and the edge of its parent component. */ + static final int hexternal = 0; + /** The default spacing used between a vertical edge of one + * component and the edge of its parent component. */ + static final int vexternal = 0; + + /** + * Gets a <code>String</code> composed of a number of instances of + * character <code>c</code> equal to <code>number</code>. + * @param c - The character to repeat. + * @param number - The number of repetitions. + * @return Returns the repeated character as a <code>String</code>. + */ + public static final String getChars(char c, int number) { + // Create a buffer to store the characters in. + StringBuffer s = new StringBuffer(); + + // Add the indicated number of instances. + for(int i = 0; i < number; i++) { + s.append(c); + } + + // Return the string. + return s.toString(); + } + + /** + * Gets the number of digits in the base-10 String representation + * of an integer primitive. Negative signs are not included in the + * digit count. + * @param value - The value of which to obtain the length. + * @return Returns the number of digits in the String representation + * of the argument value. + */ + public static final int getDigits(int value) { + return TriggerDiagnosticUtil.getDigits(value); + } + + /** + * Gets the maximum value from a list of values. + * @param values - The values to compare. + * @return Returns the largest of the argument values. + * @throws IllegalArgumentException Occurs if no values are given. + */ + public static final int max(int... values) throws IllegalArgumentException { + // Throw an error if no arguments are provided. + if(values == null || values.length == 0) { + throw new IllegalArgumentException("Can not determine maximum value from a list of 0 values."); + } + + // If there is only one value, return it. + if(values.length == 1) { return values[0]; } + + // Otherwise, get the largest value. + int largest = Integer.MIN_VALUE; + for(int value : values) { + if(value > largest) { largest = value; } + } + + // Return the result. + return largest; + } + + /** + * Gets the x-coordinate immediately to the right of the given + * component. + * @param c - The component of which to find the edge. + * @return Returns the x-coordinate as an <code>int</code> value. + */ + static final int getNextX(Component c) { + return getNextX(c, 0); + } + + /** + * Gets the x-coordinate a given distance to the right edge of the + * argument component. + * @param c - The component of which to find the edge. + * @param spacing - The additional spacing past the edge of the + * component to add. + * @return Returns the x-coordinate as an <code>int</code> value. + */ + static final int getNextX(Component c, int spacing) { + return c.getX() + c.getWidth() + spacing; + } + + /** + * Gets the y-coordinate immediately below the given component. + * @param c - The component of which to find the edge. + * @return Returns the y-coordinate as an <code>int</code> value. + */ + static final int getNextY(Component c) { + return getNextY(c, 0); + } + + /** + * Gets the y-coordinate a given distance below the bottom edge + * of the argument component. + * @param c - The component of which to find the edge. + * @param spacing - The additional spacing past the edge of the + * component to add. + * @return Returns the y-coordinate as an <code>int</code> value. + */ + static final int getNextY(Component c, int spacing) { + return c.getY() + c.getHeight() + spacing; + } +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/DiagnosticUpdatable.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/DiagnosticUpdatable.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/DiagnosticUpdatable.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,22 @@ +package org.hps.monitoring.trigger; + +import org.hps.analysis.trigger.DiagSnapshot; + +/** + * Interface <code>DiagnosticUpdatable</code> defines a class of objects + * that can be updated with information from a trigger diagnostic driver. + * They can take snapshots of the driver results and use this in order to + * alter their displayed or constituent values. + * + * @author Kyle McCarty <[log in to unmask]> + * @see DiagSnapshot + */ +public interface DiagnosticUpdatable { + /** + * Updates the object with information from the trigger diagnostic + * snapshot in the argument. + * @param snapshot - The snapshot containing information with which + * to update the object. + */ + public void updatePanel(DiagSnapshot snapshot); +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/EfficiencyTablePanel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/EfficiencyTablePanel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/EfficiencyTablePanel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,116 @@ +package org.hps.monitoring.trigger; + +import javax.swing.JTable; + +import org.hps.analysis.trigger.DiagSnapshot; +import org.hps.analysis.trigger.event.TriggerEfficiencyModule; +import org.hps.analysis.trigger.util.ComponentUtils; +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; + +public class EfficiencyTablePanel extends AbstractTablePanel implements DiagnosticUpdatable { + // Static variables. + private static final long serialVersionUID = 0L; + + // Table models. + private TableTextModel localModel; + private TableTextModel globalModel; + + /** + * Instantiates a new <code>EfficiencyTablePanel</code>. + */ + public EfficiencyTablePanel() { + // Instantiate the superclass. + super(); + + // Set the orientation to vertical. + setOrientation(ORIENTATION_VERTICAL); + } + + @Override + public void updatePanel(DiagSnapshot snapshot) { + // If there is no snapshot, the tables should all display an + // empty value. + if(snapshot == null) { + for(int eventTriggerID = 0; eventTriggerID < 6; eventTriggerID++) { + for(int seenTriggerID = 0; seenTriggerID < 6; seenTriggerID++) { + localModel.setValueAt("--- / ---", eventTriggerID + 1, seenTriggerID + 1); + globalModel.setValueAt("--- / ---", eventTriggerID + 1, seenTriggerID + 1); + } + } + } + + // Otherwise, update the table cells from the snapshot data. + else { + // Get the efficiency modules. + TriggerEfficiencyModule rmod = snapshot.efficiencyRunStatistics; + TriggerEfficiencyModule lmod = snapshot.efficiencyLocalStatistics; + + // Determine the spacing needed to display the largest numerical + // cell value. + int numWidth = -1; + for(int eventTriggerID = 0; eventTriggerID < 6; eventTriggerID++) { + for(int seenTriggerID = 0; seenTriggerID < 6; seenTriggerID++) { + int rSize = ComponentUtils.getDigits(rmod.getTriggersSeen(eventTriggerID, seenTriggerID)); + int lSize = ComponentUtils.getDigits(lmod.getTriggersSeen(eventTriggerID, seenTriggerID)); + numWidth = ComponentUtils.max(numWidth, rSize, lSize); + } + } + + // Generate the format string for the cells. + String nullText = String.format("%s / %s", ComponentUtils.getChars('-', numWidth), + ComponentUtils.getChars('-', numWidth)); + String format = "%" + numWidth + "d / %" + numWidth + "d"; + + // Update the table. + for(int eventTriggerID = 0; eventTriggerID < 6; eventTriggerID++) { + for(int seenTriggerID = 0; seenTriggerID < 6; seenTriggerID++) { + if(eventTriggerID == seenTriggerID) { + localModel.setValueAt(nullText, eventTriggerID + 1, seenTriggerID + 1); + } else { + localModel.setValueAt(String.format(format, lmod.getTriggersMatched(eventTriggerID, seenTriggerID), + lmod.getTriggersSeen(eventTriggerID, seenTriggerID)), eventTriggerID + 1, seenTriggerID + 1); + globalModel.setValueAt(String.format(format, rmod.getTriggersMatched(eventTriggerID, seenTriggerID), + rmod.getTriggersSeen(eventTriggerID, seenTriggerID)), eventTriggerID + 1, seenTriggerID + 1); + } + } + } + } + } + + @Override + protected JTable[] initializeTables(Object... args) { + // Get a shorter reference to the trigger name list. + String[] triggerNames = TriggerDiagnosticUtil.TRIGGER_NAME; + + // Initialize the table models. There should be one row and + // one column for each type of trigger plus an additional one + // of each for headers. + localModel = new TableTextModel(triggerNames.length + 1, triggerNames.length + 1); + globalModel = new TableTextModel(triggerNames.length + 1, triggerNames.length + 1); + + // Set the column and row headers. + for(int triggerType = 0; triggerType < triggerNames.length; triggerType++) { + localModel.setValueAt(triggerNames[triggerType], triggerType + 1, 0); + localModel.setValueAt(triggerNames[triggerType], 0, triggerType + 1); + globalModel.setValueAt(triggerNames[triggerType], triggerType + 1, 0); + globalModel.setValueAt(triggerNames[triggerType], 0, triggerType + 1); + } + + // Create JTable objects to display the data. + JTable localTable = new JTable(localModel); + localTable.setRowSelectionAllowed(false); + localTable.setColumnSelectionAllowed(false); + localTable.setCellSelectionEnabled(false); + localTable.setShowVerticalLines(false); + + JTable globalTable = new JTable(globalModel); + globalTable.setRowSelectionAllowed(false); + globalTable.setColumnSelectionAllowed(false); + globalTable.setCellSelectionEnabled(false); + globalTable.setShowVerticalLines(false); + + // Return the tables. + return new JTable[] { localTable, globalTable }; + } + +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/PairTablePanel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/PairTablePanel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/PairTablePanel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,33 @@ +package org.hps.monitoring.trigger; + +import org.hps.analysis.trigger.DiagSnapshot; +import org.hps.analysis.trigger.event.TriggerStatModule; + +/** + * Class <code>PairTablePanel</code> displays statistical information + * for trigger pair cuts. + * + * @author Kyle McCarty + */ +public class PairTablePanel extends AbstractTriggerTablePanel { + // Static variables. + private static final long serialVersionUID = 0L; + private static final String[] CUT_NAMES = { " Energy Sum:", + " Energy Difference:", " Energy Slope:", " Coplanarity:" }; + + /** + * Instantiates a <code>PairTablePanel</code>. + */ + public PairTablePanel() { super(CUT_NAMES); } + + @Override + protected TriggerStatModule getLocalModule(DiagSnapshot snapshot) { + return snapshot.pairLocalStatistics; + } + + @Override + protected TriggerStatModule getRunModule(DiagSnapshot snapshot) { + return snapshot.pairRunStatistics; + } + +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/SinglesTablePanel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/SinglesTablePanel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/SinglesTablePanel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,33 @@ +package org.hps.monitoring.trigger; + +import org.hps.analysis.trigger.DiagSnapshot; +import org.hps.analysis.trigger.event.TriggerStatModule; + +/** + * Class <code>SinglesTablePanel</code> displays statistical information + * for trigger singles cuts. + * + * @author Kyle McCarty + */ +public class SinglesTablePanel extends AbstractTriggerTablePanel { + // Static variables. + private static final long serialVersionUID = 0L; + private static final String[] CUT_NAMES = { " Cluster Energy (Low):", + " Cluster Energy (High):", " Hit Count:" }; + + /** + * Instantiates a <code>SinglesTablePanel</code>. + */ + public SinglesTablePanel() { super(CUT_NAMES); } + + @Override + protected TriggerStatModule getLocalModule(DiagSnapshot snapshot) { + return snapshot.singlesLocalStatistics; + } + + @Override + protected TriggerStatModule getRunModule(DiagSnapshot snapshot) { + return snapshot.singlesRunStatistics; + } + +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,103 @@ +package org.hps.monitoring.trigger; + +import javax.swing.table.AbstractTableModel; + +/** + * Class <code>TableTextModel</code> is a simple implementation of + * <code>AbstractTableModel</code> that supports a definable number + * of rows and columns which must be populated with <code>String</code> + * data. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class TableTextModel extends AbstractTableModel { + // Serial UID. + private static final long serialVersionUID = 0L; + + // Stored values. + private final int rows, columns; + private final String[][] values; + + /** + * Instantiates a new <code>TableTextModel</code> with the indicated + * number of rows and columns. + * @param rows - The number of rows. + * @param columns - The number of columns. + */ + public TableTextModel(int rows, int columns) { + // Make sure that the arguments for rows and columns are valid. + if(rows < 1) { + throw new IllegalArgumentException("TableTextModel must have at least one row."); + } else if(columns < 1) { + throw new IllegalArgumentException("TableTextModel must have at least one column."); + } + + // Define the number of rows and columns. + this.rows = rows; + this.columns = columns; + + // Instantiate the data storage array. + values = new String[rows][columns]; + } + + @Override + public int getRowCount() { return rows; } + + @Override + public int getColumnCount() { return columns; } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + // Ensure that the value is within the allowed range. + validateIndex(rowIndex, columnIndex); + + // Return the value. + return values[rowIndex][columnIndex]; + } + + @Override + public void setValueAt(Object value, int rowIndex, int columnIndex) { + // If the object is a string, pass it to the preferred handler. + // This can also be performed if the value is null. + if(value == null || value instanceof String) { + setValueAt((String) value, rowIndex, columnIndex); + } + + // Otherwise, cast the object to a string and use that instead. + else { setValueAt(value.toString(), rowIndex, columnIndex); } + } + + /** + * Sets the text for the indicated column and row of the table. + * @param value - The new text. + * @param rowIndex - The row. + * @param columnIndex - The column. + * @throws IndexOutOfBoundsException Occurs if the row and column + * are not a valid member of table model. + */ + public void setValueAt(String value, int rowIndex, int columnIndex) throws IndexOutOfBoundsException { + // Ensure that the value is within the allowed range. + validateIndex(rowIndex, columnIndex); + + // Set the value. + values[rowIndex][columnIndex] = value; + } + + /** + * Checks to make sure that a given row/column pointer refers to + * an extant position in the data array. In the event that the row + * and column values are not valid, an <code>IndexOutOfBounds</code> + * exception is thrown. + * @param rowIndex - The row index. + * @param columnIndex - The column index. + * @throws IndexOutOfBoundsException Occurs if the row and column + * are not a valid member of the data array. + */ + private void validateIndex(int rowIndex, int columnIndex) throws IndexOutOfBoundsException { + if(rowIndex < 0 || rowIndex >= getRowCount()) { + throw new IndexOutOfBoundsException(String.format("Row index %d is out of bounds.", rowIndex)); + } else if(columnIndex < 0 || columnIndex >= getColumnCount()) { + throw new IndexOutOfBoundsException(String.format("Column index %d is out of bounds.", columnIndex)); + } + } +} Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TriggerDiagnosticGUIDriver.java ============================================================================= --- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TriggerDiagnosticGUIDriver.java (added) +++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/trigger/TriggerDiagnosticGUIDriver.java Thu Mar 5 13:46:25 2015 @@ -0,0 +1,43 @@ +package org.hps.monitoring.trigger; + +import java.util.List; + +import javax.swing.JFrame; + +import org.hps.analysis.trigger.DiagSnapshot; +import org.lcsim.event.EventHeader; +import org.lcsim.util.Driver; + +public class TriggerDiagnosticGUIDriver extends Driver { + private JFrame window = new JFrame(); + private ClusterTablePanel clusterTable = new ClusterTablePanel(); + private String diagnosticCollectionName = "DiagnosticSnapshot"; + + @Override + public void startOfData() { + window.add(clusterTable); + window.setVisible(true); + window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + window.setSize(500, 400); + } + + @Override + public void process(EventHeader event) { + // Updates are only performed if a diagnostic snapshot object + // exists. Otherwise, do nothing. + if(event.hasCollection(DiagSnapshot.class, diagnosticCollectionName)) { + // Get the snapshot collection. + List<DiagSnapshot> snapshotList = event.get(DiagSnapshot.class, diagnosticCollectionName); + + // Get the snapshot. There will only ever be one. + DiagSnapshot snapshot = snapshotList.get(0); + + // Feed it to the table. + clusterTable.updatePanel(snapshot); + } + } + + public void setDiagnosticCollectionName(String name) { + diagnosticCollectionName = name; + } +}