From f0c017f35adc1ee6e7dce7159e4956a561e1003a Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 1 May 2018 12:24:10 -0400 Subject: [PATCH] Added checkboxes for showing less data and hiding the trend line. Removed code to graph the min and max values since it's unlikely to be used. --- .../healthmonitor/HealthMonitorDashboard.java | 54 +++++- .../healthmonitor/TimingMetricGraphPanel.java | 159 +++++------------- 2 files changed, 94 insertions(+), 119 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java index 706680b591..292a38eae8 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java @@ -64,6 +64,8 @@ public class HealthMonitorDashboard { private JComboBox dateComboBox = null; private JComboBox hostComboBox = null; private JCheckBox hostCheckBox = null; + private JCheckBox showTrendLineCheckBox = null; + private JCheckBox skipOutliersCheckBox = null; private JPanel graphPanel = null; private JDialog dialog = null; private final Container parentWindow; @@ -200,7 +202,9 @@ public class HealthMonitorDashboard { * @return the control panel */ @NbBundle.Messages({"HealthMonitorDashboard.createTimingControlPanel.filterByHost=Filter by host", - "HealthMonitorDashboard.createTimingControlPanel.maxDays=Max days to display"}) + "HealthMonitorDashboard.createTimingControlPanel.maxDays=Max days to display", + "HealthMonitorDashboard.createTimingControlPanel.skipOutliers=Do not plot outliers", + "HealthMonitorDashboard.createTimingControlPanel.showTrendLine=Show trend line"}) private JPanel createTimingControlPanel() { JPanel timingControlPanel = new JPanel(); @@ -251,7 +255,7 @@ public class HealthMonitorDashboard { } }); - // Create the checkbox + // Create the host checkbox hostCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_filterByHost()); hostCheckBox.setSelected(false); hostComboBox.setEnabled(false); @@ -269,6 +273,38 @@ public class HealthMonitorDashboard { } }); + // Create the checkbox for showing the trend line + showTrendLineCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_showTrendLine()); + showTrendLineCheckBox.setSelected(true); + + // Set up the listener on the checkbox + showTrendLineCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + try { + updateTimingMetricGraphs(); + } catch (HealthMonitorException ex) { + logger.log(Level.SEVERE, "Error populating timing metric panel", ex); + } + } + }); + + // Create the checkbox for omitting outliers + skipOutliersCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_skipOutliers()); + skipOutliersCheckBox.setSelected(false); + + // Set up the listener on the checkbox + skipOutliersCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + try { + updateTimingMetricGraphs(); + } catch (HealthMonitorException ex) { + logger.log(Level.SEVERE, "Error populating timing metric panel", ex); + } + } + }); + // Add the date range combo box and label to the panel timingControlPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createTimingControlPanel_maxDays())); timingControlPanel.add(dateComboBox); @@ -280,6 +316,18 @@ public class HealthMonitorDashboard { timingControlPanel.add(hostCheckBox); timingControlPanel.add(hostComboBox); + // Put some space between the elements + timingControlPanel.add(Box.createHorizontalStrut(100)); + + // Add the skip outliers checkbox + timingControlPanel.add(this.showTrendLineCheckBox); + + // Put some space between the elements + timingControlPanel.add(Box.createHorizontalStrut(100)); + + // Add the skip outliers checkbox + timingControlPanel.add(this.skipOutliersCheckBox); + return timingControlPanel; } @@ -327,7 +375,7 @@ public class HealthMonitorDashboard { // Generate the graph TimingMetricGraphPanel singleTimingGraphPanel = new TimingMetricGraphPanel(intermediateTimingDataForDisplay, - TimingMetricGraphPanel.TimingMetricType.AVERAGE, hostToDisplay, true, metricName); + hostToDisplay, true, metricName, skipOutliersCheckBox.isSelected(), showTrendLineCheckBox.isSelected()); singleTimingGraphPanel.setPreferredSize(new Dimension(700,200)); graphPanel.add(singleTimingGraphPanel); diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java index 0f39a553de..5b6083146a 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java @@ -58,9 +58,10 @@ class TimingMetricGraphPanel extends JPanel { private int pointWidth = 4; private int numberYDivisions = 10; private List timingResults; - private TimingMetricType timingMetricType; private String metricName; private boolean doLineGraph; + private boolean skipOutliers; + private boolean showTrendLine; private String yUnitString; private TrendLine trendLine; private final long MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24; @@ -70,11 +71,12 @@ class TimingMetricGraphPanel extends JPanel { private double maxMetricTime; private double minMetricTime; - TimingMetricGraphPanel(List timingResultsFull, TimingMetricType timingMetricType, - String hostName, boolean doLineGraph, String metricName) { + TimingMetricGraphPanel(List timingResultsFull, + String hostName, boolean doLineGraph, String metricName, boolean skipOutliers, boolean showTrendLine) { - this.timingMetricType = timingMetricType; this.doLineGraph = doLineGraph; + this.skipOutliers = skipOutliers; + this.showTrendLine = showTrendLine; this.metricName = metricName; if(hostName == null || hostName.isEmpty()) { timingResults = timingResultsFull; @@ -84,89 +86,48 @@ class TimingMetricGraphPanel extends JPanel { .collect(Collectors.toList()); } - try { - trendLine = new TrendLine(timingResults, timingMetricType); - } catch (HealthMonitorException ex) { - // Log it, set trendLine to null and continue on - logger.log(Level.WARNING, "Can not generate a trend line on empty data set"); - trendLine = null; + if(showTrendLine) { + try { + trendLine = new TrendLine(timingResults); + } catch (HealthMonitorException ex) { + // Log it, set trendLine to null and continue on + logger.log(Level.WARNING, "Can not generate a trend line on empty data set"); + trendLine = null; + } } // Calculate these using the full data set, to make it easier to compare the results for - // individual hosts - calcMaxTimestamp(timingResultsFull); - calcMinTimestamp(timingResultsFull); - calcMaxMetricTime(timingResultsFull); - calcMinMetricTime(timingResultsFull); - } - - /** - * Set the highest metric time for the given type - */ - private void calcMaxMetricTime(List timingResultsFull) { - // Find the highest of the values being graphed + // individual hosts. Calculate the average at the same time. maxMetricTime = Double.MIN_VALUE; - for (DatabaseTimingResult score : timingResultsFull) { - // Use only the data we're graphing to determing the max - switch (timingMetricType) { - case MAX: - maxMetricTime = Math.max(maxMetricTime, score.getMax()); - break; - case MIN: - maxMetricTime = Math.max(maxMetricTime, score.getMin()); - break; - case AVERAGE: - default: - maxMetricTime = Math.max(maxMetricTime, score.getAverage()); - break; - } - } - } - - /** - * Set the lowest metric time for the given type - */ - private void calcMinMetricTime(List timingResultsFull) { - // Find the lowest of the values being graphed minMetricTime = Double.MAX_VALUE; - for (DatabaseTimingResult result : timingResultsFull) { - // Use only the data we're graphing to determing the min - switch (timingMetricType) { - case MAX: - minMetricTime = Math.min(minMetricTime, result.getMax()); - break; - case MIN: - minMetricTime = Math.min(minMetricTime, result.getMin()); - break; - case AVERAGE: - default: - minMetricTime = Math.min(minMetricTime, result.getAverage()); - break; - } - } - } - - /** - * Set the largest timestamp in the data collection - */ - private void calcMaxTimestamp(List timingResultsFull) { maxTimestamp = Long.MIN_VALUE; - for (DatabaseTimingResult score : timingResultsFull) { - maxTimestamp = Math.max(maxTimestamp, score.getTimestamp()); - } - } - - /** - * Set the smallest timestamp in the data collection - */ - private void calcMinTimestamp(List timingResultsFull) { minTimestamp = Long.MAX_VALUE; - for (DatabaseTimingResult score : timingResultsFull) { - minTimestamp = Math.min(minTimestamp, score.getTimestamp()); + double averageMetricTime = 0.0; + for (DatabaseTimingResult result : timingResultsFull) { + + maxMetricTime = Math.max(maxMetricTime, result.getAverage()); + minMetricTime = Math.min(minMetricTime, result.getAverage()); + + maxTimestamp = Math.max(maxTimestamp, result.getTimestamp()); + minTimestamp = Math.min(minTimestamp, result.getTimestamp()); + + averageMetricTime += result.getAverage(); + } + averageMetricTime = averageMetricTime / timingResultsFull.size(); + + // If we're omitting outliers, we may use a different maxMetricTime. + // If the max time is reasonably close to the average, do nothing + if (this.skipOutliers && (maxMetricTime > (averageMetricTime * 5))) { + // Calculate the standard deviation + double intermediateValue = 0.0; + for (DatabaseTimingResult result : timingResultsFull) { + double diff = result.getAverage() - averageMetricTime; + intermediateValue += diff * diff; + } + double standardDeviation = Math.sqrt(intermediateValue / timingResultsFull.size()); + maxMetricTime = averageMetricTime + standardDeviation; } } - - /** * Setup of the graphics panel: @@ -353,20 +314,7 @@ class TimingMetricGraphPanel extends JPanel { // Create the points to plot List graphPoints = new ArrayList<>(); for (int i = 0; i < timingResults.size(); i++) { - double metricTime; - switch (timingMetricType) { - case MAX: - metricTime = timingResults.get(i).getMax(); - break; - case MIN: - metricTime = timingResults.get(i).getMin(); - break; - case AVERAGE: - default: - metricTime = timingResults.get(i).getAverage(); - break; - - } + double metricTime = timingResults.get(i).getAverage(); int x1 = (int) ((timingResults.get(i).getTimestamp() - minValueOnXAxis) * xScale + leftGraphPadding); int y1 = (int) ((maxValueOnYAxis - metricTime) * yScale + topGraphPadding); @@ -410,7 +358,7 @@ class TimingMetricGraphPanel extends JPanel { // Draw the trend line. // Don't draw anything if we don't have at least two data points. - if(trendLine != null && (timingResults.size() > 1)) { + if(showTrendLine && (trendLine != null) && (timingResults.size() > 1)) { double x0value = minValueOnXAxis; double y0value = trendLine.getExpectedValueAt(x0value); if (y0value < minValueOnYAxis) { @@ -482,15 +430,6 @@ class TimingMetricGraphPanel extends JPanel { g2.drawString(titleStr, positionForMetricNameLabel, padding); } - /** - * The metric field we want to graph - */ - enum TimingMetricType { - AVERAGE, - MAX, - MIN; - } - /** * Class to generate a linear trend line from timing metric data. * @@ -507,7 +446,7 @@ class TimingMetricGraphPanel extends JPanel { double slope; double yInt; - TrendLine(List timingResults, TimingMetricGraphPanel.TimingMetricType timingMetricType) throws HealthMonitorException { + TrendLine(List timingResults) throws HealthMonitorException { if((timingResults == null) || timingResults.isEmpty()) { throw new HealthMonitorException("Can not generate trend line for empty/null data set"); @@ -521,19 +460,7 @@ class TimingMetricGraphPanel extends JPanel { double sumXsquared = 0; for(int i = 0;i < n;i++) { double x = timingResults.get(i).getTimestamp(); - double y; - switch (timingMetricType) { - case MAX: - y = timingResults.get(i).getMax(); - break; - case MIN: - y = timingResults.get(i).getMin(); - break; - case AVERAGE: - default: - y = timingResults.get(i).getAverage(); - break; - } + double y = timingResults.get(i).getAverage(); sumX += x; sumY += y;