diff --git a/.classpath b/.classpath index 20de1a4..10d54e9 100644 --- a/.classpath +++ b/.classpath @@ -1,11 +1,13 @@ + + diff --git a/MafScaling.jar b/MafScaling.jar index be84870..13bf800 100644 Binary files a/MafScaling.jar and b/MafScaling.jar differ diff --git a/lib/jmathplot.jar b/lib/jmathplot.jar new file mode 100644 index 0000000..d809cf1 Binary files /dev/null and b/lib/jmathplot.jar differ diff --git a/log4j.properties b/log4j.properties index ce5985f..79aa9c4 100644 --- a/log4j.properties +++ b/log4j.properties @@ -2,11 +2,10 @@ log4j.rootLogger=info, stdout, file log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%-5r %-5p [%t] - %m%n +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %F:%L - %m%n log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=./maf_scaling.log log4j.appender.file.MaxBackupIndex=1 log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%-5r %-5p [%t] - %m%n - +log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %F:%L - %m%n diff --git a/resources/arrow.jpg b/resources/arrow.jpg new file mode 100644 index 0000000..c9c697e Binary files /dev/null and b/resources/arrow.jpg differ diff --git a/resources/chart.jpg b/resources/chart.jpg new file mode 100644 index 0000000..1340481 Binary files /dev/null and b/resources/chart.jpg differ diff --git a/resources/table.jpg b/resources/table.jpg new file mode 100644 index 0000000..939cf72 Binary files /dev/null and b/resources/table.jpg differ diff --git a/src/com/vgi/mafscaling/BgColorFormatRenderer.java b/src/com/vgi/mafscaling/BgColorFormatRenderer.java index 9b9fb2d..f641ae3 100644 --- a/src/com/vgi/mafscaling/BgColorFormatRenderer.java +++ b/src/com/vgi/mafscaling/BgColorFormatRenderer.java @@ -1,3 +1,21 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + package com.vgi.mafscaling; import java.awt.Color; @@ -7,7 +25,7 @@ public class BgColorFormatRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = -2283383526525346419L; - Color bgColor = getBackground(); + private Color bgColor = getBackground(); private Color[][] colors = null; /** diff --git a/src/com/vgi/mafscaling/ClosedLoop.java b/src/com/vgi/mafscaling/ClosedLoop.java index 4fdb310..10cee6c 100644 --- a/src/com/vgi/mafscaling/ClosedLoop.java +++ b/src/com/vgi/mafscaling/ClosedLoop.java @@ -52,20 +52,17 @@ import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; -import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextPane; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; -import javax.swing.SpinnerNumberModel; import javax.swing.border.LineBorder; import javax.swing.border.TitledBorder; import javax.swing.table.DefaultTableColumnModel; @@ -99,34 +96,34 @@ public class ClosedLoop extends JTabbedPane implements ActionListener, IMafChart private static final long serialVersionUID = 2988105467764335997L; private static final Logger logger = Logger.getLogger(ClosedLoop.class); - private final static String SaveDataFileHeader = "[closed_loop run data]"; - private final static String MafTableName = "Current MAF Scaling"; - private final static String Afr1TableName = "AFR Average"; - private final static String Afr2TableName = "AFR Cell Hit Count"; - private final static String XAxisName = "MAF Sensor (Voltage)"; - private final static String Y1AxisName = "Mass Airflow (g/s)"; - private final static String Y2AxisName = "Total Correction (%)"; - private final static String dvdtAxisName = "dV / dt"; - private final static String iatAxisName = "IAT"; - private final static String trpmAxisName = "Trims / Rpm"; - private final static String mnmdAxisName = "Mean / Mode"; - private final static String mnmd2AxisName = "Trims / MafV"; - private final static String timeAxisName = "Time"; - private final static String rpmAxisName = "RPM"; + private static final String SaveDataFileHeader = "[closed_loop run data]"; + private static final String MafTableName = "Current MAF Scaling"; + private static final String Afr1TableName = "AFR Average"; + private static final String Afr2TableName = "AFR Cell Hit Count"; + private static final String XAxisName = "MAF Sensor (Voltage)"; + private static final String Y1AxisName = "Mass Airflow (g/s)"; + private static final String Y2AxisName = "Total Correction (%)"; + private static final String dvdtAxisName = "dV / dt"; + private static final String iatAxisName = "IAT"; + private static final String trpmAxisName = "Trims / Rpm"; + private static final String mnmdAxisName = "Mean / Mode"; + private static final String mnmd2AxisName = "Trims / MafV"; + private static final String timeAxisName = "Time"; + private static final String rpmAxisName = "RPM"; private static final String totalCorrectionDataName = "Total Correction"; private static final String currentDataName = "Current"; private static final String correctedDataName = "Corrected"; private static final String smoothedDataName = "Smoothed"; - private static final String mafCurveDataName = "Maf Curve"; + private static final String mafCurveDataName = "Smoothed Maf Curve"; private static final String currentSlopeDataName = "Current Maf Slope"; private static final String smoothedSlopeDataName = "Smoothed Maf Slope"; - private final static int MinimumDataSampleCount = 30; - private final static int ColumnWidth = 50; - private final static int ColumnCount = 9; - private int MafTableColumnCount = 50; - private int AfrTableColumnCount = 15; - private int AfrTableRowCount = 25; - private int LogDataRowCount = 200; + private static final int MinimumDataSampleCount = 30; + private static final int ColumnWidth = 50; + private static final int ColumnCount = 9; + private static final int MafTableColumnCount = 50; + private static final int AfrTableColumnCount = 15; + private static final int AfrTableRowCount = 25; + private static final int LogDataRowCount = 200; private int clValue = Config.getClOlStatusValue(); private double afrMin = Config.getAfrMinimumValue(); private double afrMax = Config.getAfrMaximumValue(); @@ -161,6 +158,7 @@ public class ClosedLoop extends JTabbedPane implements ActionListener, IMafChart private JCheckBox checkBoxSmoothedMaf = null; private JCheckBox checkBoxSmoothing = null; private JComboBox smoothComboBox = null; + private JButton btnCompareButton = null; private JButton btnSmoothButton = null; private JButton btnResetSmoothButton = null; private JButton btnPlusButton = null; @@ -186,12 +184,13 @@ public class ClosedLoop extends JTabbedPane implements ActionListener, IMafChart private final XYSeries currMafData = new XYSeries(currentDataName); private final XYSeries corrMafData = new XYSeries(correctedDataName); private final XYSeries smoothMafData = new XYSeries(smoothedDataName); - private PrimaryOpenLoopFuelingTable polfTable = null; + private MafCompare mafCompare = null; - public ClosedLoop(int tabPlacement, PrimaryOpenLoopFuelingTable table) { + public ClosedLoop(int tabPlacement, PrimaryOpenLoopFuelingTable table, MafCompare comparer) { super(tabPlacement); polfTable = table; + mafCompare = comparer; initialize(); } @@ -214,7 +213,7 @@ private void createDataTab() { gbl_dataPanel.columnWidths = new int[] {0, 0}; gbl_dataPanel.rowHeights = new int[] {0, 0, 0, 0}; gbl_dataPanel.columnWeights = new double[]{0.0, 0.0}; - gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 0,0, 1.0}; + gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 0.0, 1.0}; dataPanel.setLayout(gbl_dataPanel); JPanel cntlPanel = new JPanel(); @@ -633,14 +632,16 @@ private void createGraghTab() { checkBoxSmoothedMaf.addActionListener(this); cntlPanel.add(checkBoxSmoothedMaf, gbc_checkBoxSmoothedMaf); - Component horizontalGlue = Box.createHorizontalGlue(); - GridBagConstraints gbc_horizontalGlue = new GridBagConstraints(); - gbc_horizontalGlue.weightx = 1.0; - gbc_horizontalGlue.fill = GridBagConstraints.HORIZONTAL; - gbc_horizontalGlue.insets = new Insets(0, 0, 3, 3); - gbc_horizontalGlue.gridx = 8; - gbc_horizontalGlue.gridy = 0; - cntlPanel.add(horizontalGlue, gbc_horizontalGlue); + btnCompareButton = new JButton("Compare"); + GridBagConstraints gbc_btnCompareButton = new GridBagConstraints(); + gbc_btnCompareButton.anchor = GridBagConstraints.CENTER; + gbc_btnCompareButton.insets = new Insets(0, 0, 3, 3); + gbc_btnCompareButton.weightx = 1.0; + gbc_btnCompareButton.gridx = 8; + gbc_btnCompareButton.gridy = 0; + btnCompareButton.setActionCommand("compare"); + btnCompareButton.addActionListener(this); + cntlPanel.add(btnCompareButton, gbc_btnCompareButton); checkBoxSmoothing = new JCheckBox("Smoothing:"); GridBagConstraints gbc_checkBoxSmoothing = new GridBagConstraints(); @@ -685,6 +686,7 @@ private void createGraghTab() { cntlPanel.add(btnResetSmoothButton, gbc_btnResetSmoothButton); JFreeChart chart = ChartFactory.createScatterPlot(null, null, null, null, PlotOrientation.VERTICAL, false, true, false); + chart.setBorderVisible(true); mafChartPanel = new MafChartPanel(chart, this); GridBagConstraints gbl_chartPanel = new GridBagConstraints(); @@ -718,6 +720,7 @@ private void createGraghTab() { lineRenderer.setSeriesShape(0, ShapeUtilities.createDiamond((float) 2.5)); lineRenderer.setSeriesShape(1, ShapeUtilities.createUpTriangle((float) 2.5)); lineRenderer.setSeriesShape(2, ShapeUtilities.createDownTriangle((float) 2.5)); + mafChartPanel.enablePointsDrag(0); lineRenderer.setLegendItemLabelGenerator( new StandardXYSeriesLabelGenerator() { @@ -878,9 +881,11 @@ public String generateLabel(XYDataset dataset, int series) { private void createUsageTab() { JTextPane usageTextArea = new JTextPane(); + usageTextArea.setMargin(new Insets(10, 10, 10, 10)); usageTextArea.setContentType("text/html"); usageTextArea.setText(usage()); usageTextArea.setEditable(false); + usageTextArea.setCaretPosition(0); JScrollPane textScrollPane = new JScrollPane(usageTextArea); textScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); @@ -916,6 +921,9 @@ else if ("save".equals(e.getActionCommand())) { else if ("go".equals(e.getActionCommand())) { calculateMafScaling(); } + else if ("compare".equals(e.getActionCommand())) { + mafCompare.setVisible(true); + } else if ("smooth".equals(e.getActionCommand())) { smoothCurve(); } @@ -1180,13 +1188,13 @@ private void calculateMafScaling() { catch (Exception e) { e.printStackTrace(); logger.error(e); - JOptionPane.showMessageDialog(null, "Error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Error: " + e, "Error", JOptionPane.ERROR_MESSAGE); } finally { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } - + private void calculateCorrectedGS() { double time; double load; @@ -1325,14 +1333,23 @@ private void calculateCorrectedGS() { Utils.removeRow(size, afr2Table); Utils.colorTable(afr1Table); Utils.colorTable(afr2Table); + int firstCorrIndex = 0; + double firstCorr = 1; for (i = 0; i < correctionMeanArray.size(); ++i) { corr = 1; - if (temp.get(i) > MinimumDataSampleCount) + if (temp.get(i) > MinimumDataSampleCount) { corr = 1.0 + (correctionMeanArray.get(i) + correctionModeArray.get(i)) / 200.00; + if (firstCorrIndex == 0) { + firstCorrIndex = i; + firstCorr = corr; + } + } gsCorrected.add(i, gsArray.get(i) * corr); } + for (i = firstCorrIndex - 1; i > 0; --i) + gsCorrected.set(i, gsArray.get(i) * firstCorr); } - + private boolean getMafTableData(ArrayList voltArray, ArrayList gsArray) { String value; for (int i = 0; i < mafTable.getColumnCount(); ++i) { @@ -1505,6 +1522,9 @@ else if (checkBoxSmoothing.isSelected()) { paddingY = maxY * 0.05; plot.getDomainAxis(0).setRange(smoothMafData.getMinX() - paddingX, smoothMafData.getMaxX() + paddingX); plot.getRangeAxis(0).setRange(minY - paddingY, maxY + paddingY); + corrMafData.setDescription(mafCurveDataName); + currMafData.setDescription(currentSlopeDataName); + smoothMafData.setDescription(smoothedSlopeDataName); } else if ((checkBoxCurrentMaf.isSelected() && checkBoxCurrentMaf.isEnabled()) || (checkBoxCorrectedMaf.isSelected() && checkBoxCorrectedMaf.isEnabled()) || @@ -1540,7 +1560,7 @@ private void plotSmoothingLineSlopes() { setXYTable(mafSmoothingTable, voltArray, smoothGsArray); } - public void onMovePoint(int itemIndex, double valueY) { + public void onMovePoint(int itemIndex, double valueX, double valueY) { ArrayList xarr = voltArray; ArrayList yarr = smoothGsArray; XYSeries series = smoothMafData; @@ -1853,6 +1873,46 @@ private int getLogTableEmptyRow() { return 0; } + private boolean getColumnsFilters(String[] elements) { + boolean ret = true; + ArrayList columns = new ArrayList(Arrays.asList(elements)); + String logClOlStatusColName = Config.getClOlStatusColumnName(); + String logAfLearningColName = Config.getAfLearningColumnName(); + String logAfCorrectionColName = Config.getAfCorrectionColumnName(); + String logAfrColName = Config.getAfrColumnName(); + String logRpmColName = Config.getRpmColumnName(); + String logLoadColName = Config.getLoadColumnName(); + String logTimeColName = Config.getTimeColumnName(); + String logMafvColName = Config.getMafVoltageColumnName(); + String logIatColName = Config.getIatColumnName(); + logClOlStatusColIdx = columns.indexOf(logClOlStatusColName); + logAfLearningColIdx = columns.indexOf(logAfLearningColName); + logAfCorrectionColIdx = columns.indexOf(logAfCorrectionColName); + logAfrColIdx = columns.indexOf(logAfrColName); + logRpmColIdx = columns.indexOf(logRpmColName); + logLoadColIdx = columns.indexOf(logLoadColName); + logTimeColIdx = columns.indexOf(logTimeColName); + logMafvColIdx = columns.indexOf(logMafvColName); + logIatColIdx = columns.indexOf(logIatColName); + if (logClOlStatusColIdx == -1) { Config.setClOlStatusColumnName(Config.NO_NAME); ret = false; } + if (logAfLearningColIdx == -1) { Config.setAfLearningColumnName(Config.NO_NAME); ret = false; } + if (logAfCorrectionColIdx == -1) { Config.setAfCorrectionColumnName(Config.NO_NAME); ret = false; } + if (logAfrColIdx == -1) { Config.setAfrColumnName(Config.NO_NAME); ret = false; } + if (logRpmColIdx == -1) { Config.setRpmColumnName(Config.NO_NAME); ret = false; } + if (logLoadColIdx == -1) { Config.setLoadColumnName(Config.NO_NAME); ret = false; } + if (logTimeColIdx == -1) { Config.setTimeColumnName(Config.NO_NAME); ret = false; } + if (logMafvColIdx == -1) { Config.setMafVoltageColumnName(Config.NO_NAME); ret = false; } + if (logIatColIdx == -1) { Config.setIatColumnName(Config.NO_NAME); ret = false; } + clValue = Config.getClOlStatusValue(); + afrMin = Config.getAfrMinimumValue(); + afrMax = Config.getAfrMaximumValue(); + minLoad = Config.getLoadMinimumValue(); + maxDvDt = Config.getDvDtMaximumValue(); + maxMafV = Config.getMafVMaximumValue(); + maxIat = Config.getIatMaximumValue(); + return ret; + } + private void loadLogFile() { if (JFileChooser.APPROVE_OPTION != fileChooser.showOpenDialog(this)) return; @@ -1863,274 +1923,21 @@ private void loadLogFile() { String line = br.readLine(); if (line != null) { String [] elements = line.split(",", -1); + getColumnsFilters(elements); - ArrayList columns = new ArrayList(Arrays.asList(elements)); - String logClOlStatusColName = Config.getClOlStatusColumnName(); - String logAfLearningColName = Config.getAfLearningColumnName(); - String logAfCorrectionColName = Config.getAfCorrectionColumnName(); - String logAfrColName = Config.getAfrColumnName(); - String logRpmColName = Config.getRpmColumnName(); - String logLoadColName = Config.getLoadColumnName(); - String logTimeColName = Config.getTimeColumnName(); - String logMafvColName = Config.getMafVoltageColumnName(); - String logIatColName = Config.getIatColumnName(); - logClOlStatusColIdx = columns.indexOf(logClOlStatusColName); - logAfLearningColIdx = columns.indexOf(logAfLearningColName); - logAfCorrectionColIdx = columns.indexOf(logAfCorrectionColName); - logAfrColIdx = columns.indexOf(logAfrColName); - logRpmColIdx = columns.indexOf(logRpmColName); - logLoadColIdx = columns.indexOf(logLoadColName); - logTimeColIdx = columns.indexOf(logTimeColName); - logMafvColIdx = columns.indexOf(logMafvColName); - logIatColIdx = columns.indexOf(logIatColName); boolean resetColumns = false; - - if (logClOlStatusColIdx >= 0 || - logAfLearningColIdx >= 0 || - logAfCorrectionColIdx >= 0 || - logAfrColIdx >= 0 || - logRpmColIdx >= 0 || - logLoadColIdx >=0 || - logTimeColIdx >=0 || - logMafvColIdx >= 0 || - logIatColIdx >= 0 ) { + if (logClOlStatusColIdx >= 0 || logAfLearningColIdx >= 0 || logAfCorrectionColIdx >= 0 || logAfrColIdx >= 0 || + logRpmColIdx >= 0 || logLoadColIdx >=0 || logTimeColIdx >=0 || logMafvColIdx >= 0 || logIatColIdx >= 0 ) { if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, "Would you like to reset column names or filter values?", "Columns/Filters Reset", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE)) resetColumns = true; } - - JTable table = new JTable() { - private static final long serialVersionUID = 2L; - public boolean isCellEditable(int row, int column) { return false; }; - }; - table.setColumnSelectionAllowed(false); - table.setCellSelectionEnabled(true); - table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - table.setBorder(new LineBorder(new Color(0, 0, 0))); - table.setTableHeader(null); - table.setModel(new DefaultTableModel(elements.length, 1)); - for (int i = 0; i < elements.length; ++i) - table.setValueAt(elements[i], i, 0); - JLabel spinnerLabel = new JLabel("CL/OL Status value for CL"); - JSpinner spinner = new JSpinner(new SpinnerNumberModel(clValue, -1, 10, 1)); - JLabel lblMin = new JLabel("AFR Filter - valid minimum"); - NumberFormat doubleFmt = NumberFormat.getNumberInstance(); - doubleFmt.setMaximumFractionDigits(2); - JFormattedTextField minTextField = new JFormattedTextField(doubleFmt); - minTextField.setValue(new Double(afrMin)); - minTextField.setColumns(10); - JLabel lblMax = new JLabel("AFR Filter - valid maximum"); - JFormattedTextField maxTextField = new JFormattedTextField(doubleFmt); - maxTextField.setValue(new Double(afrMax)); - maxTextField.setColumns(10); - lblMin.setVisible(false); - minTextField.setVisible(false); - lblMax.setVisible(false); - maxTextField.setVisible(false); - - JComponent[] inputs = new JComponent[] { new JScrollPane(table), spinnerLabel, spinner, lblMin, minTextField, lblMax, maxTextField }; - - if (logClOlStatusColIdx >= 0) - table.changeSelection(logClOlStatusColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logClOlStatusColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select CL/OL Status Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logClOlStatusColIdx = table.getSelectedRow(); - if (logClOlStatusColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid CL/OL Status Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - clValue = Integer.valueOf(spinner.getValue().toString()); - if (clValue == -1) { - JOptionPane.showMessageDialog(null, "Invalid CL/OL Status value for closed loop", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setClOlStatusColumnName(table.getValueAt(logClOlStatusColIdx, 0).toString()); - Config.setClOlStatusValue(clValue); - } - spinnerLabel.setVisible(false); - spinner.setVisible(false); - - lblMax.setVisible(true); - maxTextField.setVisible(true); - lblMin.setVisible(true); - minTextField.setVisible(true); - if (logAfrColIdx >= 0) - table.changeSelection(logAfrColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logAfrColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select AFR (Stock) Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logAfrColIdx = table.getSelectedRow(); - if (logAfrColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid AFR Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - if (!Pattern.matches(Utils.fpRegex, minTextField.getText())) { - JOptionPane.showMessageDialog(null, "Invalid AFR Filter minimum value", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - afrMin = Double.valueOf(minTextField.getText()); - if (!Pattern.matches(Utils.fpRegex, maxTextField.getText())) { - JOptionPane.showMessageDialog(null, "Invalid AFR Filter maximum value", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - afrMax = Double.valueOf(maxTextField.getText()); - Config.setAfrColumnName(table.getValueAt(logAfrColIdx, 0).toString()); - Config.setAfrMinimumValue(afrMin); - Config.setAfrMaximumValue(afrMax); - } - lblMax.setVisible(false); - maxTextField.setVisible(false); - lblMin.setVisible(false); - minTextField.setVisible(false); - - if (logAfLearningColIdx >= 0) - table.changeSelection(logAfLearningColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logAfLearningColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select AFR Learning (LTFT) Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logAfLearningColIdx = table.getSelectedRow(); - if (logAfLearningColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid AFR Learning (LTFT) Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setAfLearningColumnName(table.getValueAt(logAfLearningColIdx, 0).toString()); - } - - if (logAfCorrectionColIdx >= 0) - table.changeSelection(logAfCorrectionColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logAfCorrectionColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select AFR Correction (STFT) Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logAfCorrectionColIdx = table.getSelectedRow(); - if (logAfCorrectionColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid AFR Correction (STFT) Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setAfCorrectionColumnName(table.getValueAt(logAfCorrectionColIdx, 0).toString()); - } - - if (logRpmColIdx >= 0) - table.changeSelection(logRpmColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logRpmColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Engine Speed Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logRpmColIdx = table.getSelectedRow(); - if (logRpmColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Engine Speed Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setRpmColumnName(table.getValueAt(logRpmColIdx, 0).toString()); - } - - lblMin.setText("Engine Load Filter - minimum value"); - minTextField.setText(String.valueOf(minLoad)); - lblMin.setVisible(true); - minTextField.setVisible(true); - if (logLoadColIdx >= 0) - table.changeSelection(logLoadColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logLoadColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Engine Load Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logLoadColIdx = table.getSelectedRow(); - if (logLoadColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Engine Load Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - if (!Pattern.matches(Utils.fpRegex, minTextField.getText())) { - JOptionPane.showMessageDialog(null, "Invalid Engine Load Filter minimum value", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - minLoad = Double.valueOf(minTextField.getText()); - Config.setLoadColumnName(table.getValueAt(logLoadColIdx, 0).toString()); - Config.setLoadMinimumValue(minLoad); - } - lblMin.setVisible(false); - minTextField.setVisible(false); - - lblMax.setText("dV/dt Filter - maximum value"); - maxTextField.setText(String.valueOf(maxDvDt)); - lblMax.setVisible(true); - maxTextField.setVisible(true); - if (logTimeColIdx >= 0) - table.changeSelection(logTimeColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logTimeColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Time Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logTimeColIdx = table.getSelectedRow(); - if (logTimeColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Time Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - if (!Pattern.matches(Utils.fpRegex, maxTextField.getText())) { - JOptionPane.showMessageDialog(null, "Invalid dV/dt Filter maximum value", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - maxDvDt = Double.valueOf(maxTextField.getText()); - Config.setTimeColumnName(table.getValueAt(logTimeColIdx, 0).toString()); - Config.setDvDtMaximumValue(maxDvDt); - } - lblMax.setVisible(false); - maxTextField.setVisible(false); - - lblMax.setText("MafV Filter - maximum value"); - maxTextField.setText(String.valueOf(maxMafV)); - lblMax.setVisible(true); - maxTextField.setVisible(true); - if (logMafvColIdx >= 0) - table.changeSelection(logMafvColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logMafvColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Maf Voltage Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logMafvColIdx = table.getSelectedRow(); - if (logMafvColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Maf Voltage Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - maxMafV = Double.valueOf(maxTextField.getText()); - Config.setMafVoltageColumnName(table.getValueAt(logMafvColIdx, 0).toString()); - Config.setMafVMaximumValue(maxMafV); - } - lblMax.setVisible(false); - maxTextField.setVisible(false); - lblMax.setText("IAT Filter - maximum value"); - maxTextField.setText(String.valueOf(maxIat)); - lblMax.setVisible(true); - maxTextField.setVisible(true); - if (logIatColIdx >= 0) - table.changeSelection(logIatColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logIatColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select IAT Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logIatColIdx = table.getSelectedRow(); - if (logIatColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid IAT Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - maxIat = Double.valueOf(maxTextField.getText()); - Config.setIatColumnName(table.getValueAt(logIatColIdx, 0).toString()); - Config.setIatMaximumValue(maxIat); + if (resetColumns || logClOlStatusColIdx < 0 || logAfLearningColIdx < 0 || logAfCorrectionColIdx < 0 || logAfrColIdx < 0 || + logRpmColIdx < 0 || logLoadColIdx < 0 || logTimeColIdx < 0 || logMafvColIdx < 0 || logIatColIdx < 0 ) { + ColumnsFiltersSelection selectionWindow = new ColumnsFiltersSelection(ColumnsFiltersSelection.Loop.CLOSED_LOOP, polfTable.isSet()); + if (!selectionWindow.getUserSettings(elements) || !getColumnsFilters(elements)) + return; } - lblMax.setVisible(false); - maxTextField.setVisible(false); String[] flds; line = br.readLine(); @@ -2188,7 +1995,7 @@ private void loadLogFile() { } catch (NumberFormatException e) { logger.error(e); - JOptionPane.showMessageDialog(null, "Error parsing number at line " + i + ": " + e.getMessage(), "Error processing file", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Error parsing number at line " + i + ": " + e, "Error processing file", JOptionPane.ERROR_MESSAGE); return; } line = br.readLine(); @@ -2202,7 +2009,7 @@ private void loadLogFile() { } catch (Exception e) { logger.error(e); - JOptionPane.showMessageDialog(null, e.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, e, "Error opening file", JOptionPane.ERROR_MESSAGE); } finally { if (br != null) { diff --git a/src/com/vgi/mafscaling/ColumnsFiltersSelection.java b/src/com/vgi/mafscaling/ColumnsFiltersSelection.java new file mode 100644 index 0000000..cc9c841 --- /dev/null +++ b/src/com/vgi/mafscaling/ColumnsFiltersSelection.java @@ -0,0 +1,1116 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.vgi.mafscaling; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.NumberFormat; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.table.DefaultTableModel; + +public class ColumnsFiltersSelection implements ActionListener { + public enum Loop { + OPEN_LOOP, + CLOSED_LOOP + } + private static final String rpmLabelText = "Engine Speed"; + private static final String loadLabelText = "Engine Load"; + private static final String afLearningLabelText = "AFR Learning (LTFT)"; + private static final String afCorrectionLabelText = "AFR Correction (STFT)"; + private static final String mafVLabelText = "MAF Voltage"; + private static final String wbAfrLabelText = "Wideband AFR"; + private static final String thtlAngleLabelText = "Throttle Angle %"; + private static final String commAfrLabelText = "Commanded AFR"; + private static final String stockAfrLabelText = "Stock AFR"; + private static final String clolStatusLabelText = "CL/OL Status"; + private static final String timeLabelText = "Time"; + private static final String iatLabelText = "Intake Air Temperature"; + private static final String minMafVLabelText = "MAF Voltage Minimum"; + private static final String maxMafVLabelText = "MAF Voltage Maximum"; + private static final String maxIatLabelText = "IAT Maximum"; + private static final String maxAfrLabelText = "AFR Maximum"; + private static final String minAfrLabelText = "AFR Minimum"; + private static final String maxDvdtLabelText = "dV/dt Maximum"; + private static final String minEngineLoadLabelText = "Engine Load Minimum"; + private static final String wotStationaryLabelText = "WOT stationary point (Angle %)"; + private static final String afrErrorLabelText = "AFR Error +/- % value"; + private boolean isOpenLoop; + private boolean isPolfTableSet; + private JTable columnsTable = null; + private JTextField thtlAngleName = null; + private JTextField afLearningName = null; + private JTextField afCorrectionName = null; + private JTextField mafVName = null; + private JTextField wbAfrName = null; + private JTextField stockAfrName = null; + private JTextField rpmName = null; + private JTextField loadName = null; + private JTextField commAfrName = null; + private JTextField clolStatusName = null; + private JTextField timeName = null; + private JTextField iatName = null; + private JFormattedTextField minMafVFilter = null; + private JFormattedTextField maxMafVFilter = null; + private JFormattedTextField minEngineLoadFilter = null; + private JFormattedTextField afrErrorFilter = null; + private JFormattedTextField maxAfrFilter = null; + private JFormattedTextField minAfrFilter = null; + private JFormattedTextField maxIatFilter = null; + private JFormattedTextField maxDvdtFilter = null; + private JSpinner wotStationaryPointFilter = null; + private JSpinner clolStatusFilter = null; + + public ColumnsFiltersSelection(Loop loop, boolean isPolfTableSet) { + if (loop == Loop.OPEN_LOOP) + isOpenLoop = true; + else + isOpenLoop = false; + this.isPolfTableSet = isPolfTableSet; + } + + public boolean getUserSettings(String[] columns) { + JPanel selectionPanel = new JPanel(); + GridBagLayout gbl_dataPanel = new GridBagLayout(); + gbl_dataPanel.columnWidths = new int[]{0, 0, 0, 0}; + gbl_dataPanel.rowHeights = new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + gbl_dataPanel.columnWeights = new double[]{0.0, 0.0, 0.0, 1.0}; + gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0}; + selectionPanel.setLayout(gbl_dataPanel); + + Dimension minTextDimension = new Dimension(200, 16); + Dimension minFilterDimension = new Dimension(80, 16); + Insets insets0 = new Insets(0, 0, 0, 0); + Insets insets1 = new Insets(1, 1, 1, 1); + Insets insets3 = new Insets(3, 3, 3, 3); + NumberFormat doubleFmt = NumberFormat.getNumberInstance(); + doubleFmt.setMaximumFractionDigits(2); + ImageIcon arrowImage = new ImageIcon(getClass().getResource("/arrow.jpg")); + + int row = 0; + // Optional Note + JLabel optNoteLabel = new JLabel("NOTE: Fields marked with asterisk (*) are optional"); + optNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_optNoteLabel = new GridBagConstraints(); + gbc_optNoteLabel.anchor = GridBagConstraints.WEST; + gbc_optNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_optNoteLabel.insets = new Insets(5, 50, 5, 5); + gbc_optNoteLabel.gridx = 0; + gbc_optNoteLabel.gridy = row; + gbc_optNoteLabel.gridwidth = 4; + selectionPanel.add(optNoteLabel, gbc_optNoteLabel); + + row += 1; + // columns note + JLabel colNoteLabel = new JLabel("Columns Selection - use blank row to clear optional columns"); + GridBagConstraints gbc_colNoteLabel = new GridBagConstraints(); + gbc_colNoteLabel.anchor = GridBagConstraints.WEST; + gbc_colNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_colNoteLabel.insets = new Insets(5, 5, 5, 5); + gbc_colNoteLabel.gridx = 0; + gbc_colNoteLabel.gridy = row; + gbc_colNoteLabel.gridwidth = 4; + selectionPanel.add(colNoteLabel, gbc_colNoteLabel); + + row += 1; + // RPM + JLabel rpmLabel = new JLabel(rpmLabelText); + GridBagConstraints gbc_rpmLabel = new GridBagConstraints(); + gbc_rpmLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_rpmLabel.insets = insets3; + gbc_rpmLabel.gridx = 0; + gbc_rpmLabel.gridy = row; + selectionPanel.add(rpmLabel, gbc_rpmLabel); + + rpmName = new JTextField(isEmpty(Config.getRpmColumnName())); + rpmName.setMinimumSize(minTextDimension); + rpmName.setEditable(false); + rpmName.setBackground(Color.WHITE); + GridBagConstraints gbc_rpmName = new GridBagConstraints(); + gbc_rpmName.anchor = GridBagConstraints.NORTHWEST; + gbc_rpmName.insets = insets3; + gbc_rpmName.gridx = 1; + gbc_rpmName.gridy = row; + selectionPanel.add(rpmName, gbc_rpmName); + + JButton rpmButton = new JButton("", arrowImage); + rpmButton.setMargin(insets0); + rpmButton.setBorderPainted(false); + rpmButton.setContentAreaFilled(false); + GridBagConstraints gbc_rpmButton = new GridBagConstraints(); + gbc_rpmButton.anchor = GridBagConstraints.CENTER; + gbc_rpmButton.insets = insets1; + gbc_rpmButton.gridx = 2; + gbc_rpmButton.gridy = row; + rpmButton.setActionCommand("rpm"); + rpmButton.addActionListener(this); + selectionPanel.add(rpmButton, gbc_rpmButton); + + row += 1; + // Load + JLabel loadLabel = new JLabel(loadLabelText); + GridBagConstraints gbc_loadLabel = new GridBagConstraints(); + gbc_loadLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_loadLabel.insets = insets3; + gbc_loadLabel.gridx = 0; + gbc_loadLabel.gridy = row; + selectionPanel.add(loadLabel, gbc_loadLabel); + + loadName = new JTextField(isEmpty(Config.getLoadColumnName())); + loadName.setMinimumSize(minTextDimension); + loadName.setEditable(false); + loadName.setBackground(Color.WHITE); + GridBagConstraints gbc_loadName = new GridBagConstraints(); + gbc_loadName.anchor = GridBagConstraints.NORTHWEST; + gbc_loadName.insets = insets3; + gbc_loadName.gridx = 1; + gbc_loadName.gridy = row; + selectionPanel.add(loadName, gbc_loadName); + + JButton loadButton = new JButton("", arrowImage); + loadButton.setMargin(insets0); + loadButton.setBorderPainted(false); + loadButton.setContentAreaFilled(false); + GridBagConstraints gbc_loadButton = new GridBagConstraints(); + gbc_loadButton.anchor = GridBagConstraints.CENTER; + gbc_loadButton.insets = insets1; + gbc_loadButton.gridx = 2; + gbc_loadButton.gridy = row; + loadButton.setActionCommand("load"); + loadButton.addActionListener(this); + selectionPanel.add(loadButton, gbc_loadButton); + + row += 1; + // LTFT + JLabel afLearningLabel = new JLabel(afLearningLabelText); + GridBagConstraints gbc_afLearningLabel = new GridBagConstraints(); + gbc_afLearningLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_afLearningLabel.insets = insets3; + gbc_afLearningLabel.gridx = 0; + gbc_afLearningLabel.gridy = row; + selectionPanel.add(afLearningLabel, gbc_afLearningLabel); + + afLearningName = new JTextField(isEmpty(Config.getAfLearningColumnName())); + afLearningName.setMinimumSize(minTextDimension); + afLearningName.setEditable(false); + afLearningName.setBackground(Color.WHITE); + GridBagConstraints gbc_afLearningName = new GridBagConstraints(); + gbc_afLearningName.anchor = GridBagConstraints.NORTHWEST; + gbc_afLearningName.insets = insets3; + gbc_afLearningName.gridx = 1; + gbc_afLearningName.gridy = row; + selectionPanel.add(afLearningName, gbc_afLearningName); + + JButton afLearningButton = new JButton("", arrowImage); + afLearningButton.setMargin(insets0); + afLearningButton.setBorderPainted(false); + afLearningButton.setContentAreaFilled(false); + GridBagConstraints gbc_afLearningButton = new GridBagConstraints(); + gbc_afLearningButton.anchor = GridBagConstraints.CENTER; + gbc_afLearningButton.insets = insets1; + gbc_afLearningButton.gridx = 2; + gbc_afLearningButton.gridy = row; + afLearningButton.setActionCommand("afrlearn"); + afLearningButton.addActionListener(this); + selectionPanel.add(afLearningButton, gbc_afLearningButton); + + row += 1; + // STFT + JLabel afCorrectionLabel = new JLabel(afCorrectionLabelText); + GridBagConstraints gbc_afCorrectionLabel = new GridBagConstraints(); + gbc_afCorrectionLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_afCorrectionLabel.insets = insets3; + gbc_afCorrectionLabel.gridx = 0; + gbc_afCorrectionLabel.gridy = row; + selectionPanel.add(afCorrectionLabel, gbc_afCorrectionLabel); + + afCorrectionName = new JTextField(isEmpty(Config.getAfCorrectionColumnName())); + afCorrectionName.setMinimumSize(minTextDimension); + afCorrectionName.setEditable(false); + afCorrectionName.setBackground(Color.WHITE); + GridBagConstraints gbc_afCorrectionName = new GridBagConstraints(); + gbc_afCorrectionName.anchor = GridBagConstraints.NORTHWEST; + gbc_afCorrectionName.insets = insets3; + gbc_afCorrectionName.gridx = 1; + gbc_afCorrectionName.gridy = row; + selectionPanel.add(afCorrectionName, gbc_afCorrectionName); + + JButton afCorrectionButton = new JButton("", arrowImage); + afCorrectionButton.setMargin(insets0); + afCorrectionButton.setBorderPainted(false); + afCorrectionButton.setContentAreaFilled(false); + GridBagConstraints gbc_afCorrectionButton = new GridBagConstraints(); + gbc_afCorrectionButton.anchor = GridBagConstraints.CENTER; + gbc_afCorrectionButton.insets = insets1; + gbc_afCorrectionButton.gridx = 2; + gbc_afCorrectionButton.gridy = row; + afCorrectionButton.setActionCommand("afrcorr"); + afCorrectionButton.addActionListener(this); + selectionPanel.add(afCorrectionButton, gbc_afCorrectionButton); + + row += 1; + // MAF Voltage + JLabel mafVLabel = new JLabel(mafVLabelText); + GridBagConstraints gbc_mafVLabel = new GridBagConstraints(); + gbc_mafVLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_mafVLabel.insets = insets3; + gbc_mafVLabel.gridx = 0; + gbc_mafVLabel.gridy = row; + selectionPanel.add(mafVLabel, gbc_mafVLabel); + + mafVName = new JTextField(isEmpty(Config.getMafVoltageColumnName())); + mafVName.setMinimumSize(minTextDimension); + mafVName.setEditable(false); + mafVName.setBackground(Color.WHITE); + GridBagConstraints gbc_mafVName = new GridBagConstraints(); + gbc_mafVName.anchor = GridBagConstraints.NORTHWEST; + gbc_mafVName.insets = insets3; + gbc_mafVName.gridx = 1; + gbc_mafVName.gridy = row; + selectionPanel.add(mafVName, gbc_mafVName); + + JButton mafVButton = new JButton("", arrowImage); + mafVButton.setMargin(insets0); + mafVButton.setBorderPainted(false); + mafVButton.setContentAreaFilled(false); + GridBagConstraints gbc_mafVButton = new GridBagConstraints(); + gbc_mafVButton.anchor = GridBagConstraints.CENTER; + gbc_mafVButton.insets = insets1; + gbc_mafVButton.gridx = 2; + gbc_mafVButton.gridy = row; + mafVButton.setActionCommand("mafv"); + mafVButton.addActionListener(this); + selectionPanel.add(mafVButton, gbc_mafVButton); + + if (isOpenLoop) { + row += 1; + // Wideband AFR + JLabel wbAfrLabel = new JLabel(wbAfrLabelText); + GridBagConstraints gbc_wbAfrLabel = new GridBagConstraints(); + gbc_wbAfrLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_wbAfrLabel.insets = insets3; + gbc_wbAfrLabel.gridx = 0; + gbc_wbAfrLabel.gridy = row; + selectionPanel.add(wbAfrLabel, gbc_wbAfrLabel); + + wbAfrName = new JTextField(isEmpty(Config.getWidebandAfrColumnName())); + wbAfrName.setMinimumSize(minTextDimension); + wbAfrName.setEditable(false); + wbAfrName.setBackground(Color.WHITE); + GridBagConstraints gbc_wbAfrName = new GridBagConstraints(); + gbc_wbAfrName.anchor = GridBagConstraints.NORTHWEST; + gbc_wbAfrName.insets = insets3; + gbc_wbAfrName.gridx = 1; + gbc_wbAfrName.gridy = row; + selectionPanel.add(wbAfrName, gbc_wbAfrName); + + JButton wbAfrButton = new JButton("", arrowImage); + wbAfrButton.setMargin(insets0); + wbAfrButton.setBorderPainted(false); + wbAfrButton.setContentAreaFilled(false); + GridBagConstraints gbc_wbAfrButton = new GridBagConstraints(); + gbc_wbAfrButton.anchor = GridBagConstraints.CENTER; + gbc_wbAfrButton.insets = insets1; + gbc_wbAfrButton.gridx = 2; + gbc_wbAfrButton.gridy = row; + wbAfrButton.setActionCommand("wbafr"); + wbAfrButton.addActionListener(this); + selectionPanel.add(wbAfrButton, gbc_wbAfrButton); + + row += 1; + // Throttle Angle + JLabel thtlAngleLabel = new JLabel(thtlAngleLabelText); + GridBagConstraints gbc_thtlAngleLabel = new GridBagConstraints(); + gbc_thtlAngleLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_thtlAngleLabel.insets = insets3; + gbc_thtlAngleLabel.gridx = 0; + gbc_thtlAngleLabel.gridy = row; + selectionPanel.add(thtlAngleLabel, gbc_thtlAngleLabel); + + thtlAngleName = new JTextField(isEmpty(Config.getThrottleAngleColumnName())); + thtlAngleName.setMinimumSize(minTextDimension); + thtlAngleName.setEditable(false); + thtlAngleName.setBackground(Color.WHITE); + GridBagConstraints gbc_thtlAngleName= new GridBagConstraints(); + gbc_thtlAngleName.anchor = GridBagConstraints.NORTHWEST; + gbc_thtlAngleName.insets = insets3; + gbc_thtlAngleName.gridx = 1; + gbc_thtlAngleName.gridy = row; + selectionPanel.add(thtlAngleName, gbc_thtlAngleName); + + JButton thtlAngleButton = new JButton("", arrowImage); + thtlAngleButton.setMargin(insets0); + thtlAngleButton.setBorderPainted(false); + thtlAngleButton.setContentAreaFilled(false); + GridBagConstraints gbc_thtlAngleButton = new GridBagConstraints(); + gbc_thtlAngleButton.anchor = GridBagConstraints.CENTER; + gbc_thtlAngleButton.insets = insets1; + gbc_thtlAngleButton.gridx = 2; + gbc_thtlAngleButton.gridy = row; + thtlAngleButton.setActionCommand("thtlAngle"); + thtlAngleButton.addActionListener(this); + selectionPanel.add(thtlAngleButton, gbc_thtlAngleButton); + + row += 1; + // Commanded AFR + JLabel commAfrLabel = new JLabel(commAfrLabelText + (isPolfTableSet ? " *" : "")); + GridBagConstraints gbc_commAfrLabel = new GridBagConstraints(); + gbc_commAfrLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_commAfrLabel.insets = insets3; + gbc_commAfrLabel.gridx = 0; + gbc_commAfrLabel.gridy = row; + selectionPanel.add(commAfrLabel, gbc_commAfrLabel); + + commAfrName = new JTextField(isEmpty(Config.getCommandedAfrColumnName())); + commAfrName.setMinimumSize(minTextDimension); + commAfrName.setEditable(false); + commAfrName.setBackground(Color.WHITE); + GridBagConstraints gbc_commAfrName = new GridBagConstraints(); + gbc_commAfrName.anchor = GridBagConstraints.NORTHWEST; + gbc_commAfrName.insets = insets3; + gbc_commAfrName.gridx = 1; + gbc_commAfrName.gridy = row; + selectionPanel.add(commAfrName, gbc_commAfrName); + + JButton commAfrButton = new JButton("", arrowImage); + commAfrButton.setMargin(insets0); + commAfrButton.setBorderPainted(false); + commAfrButton.setContentAreaFilled(false); + GridBagConstraints gbc_commAfrButton = new GridBagConstraints(); + gbc_commAfrButton.anchor = GridBagConstraints.CENTER; + gbc_commAfrButton.insets = insets1; + gbc_commAfrButton.gridx = 2; + gbc_commAfrButton.gridy = row; + commAfrButton.setActionCommand("cmdafr"); + commAfrButton.addActionListener(this); + selectionPanel.add(commAfrButton, gbc_commAfrButton); + } + else { + row += 1; + // Stock AFR + JLabel stockAfrLabel = new JLabel(stockAfrLabelText); + GridBagConstraints gbc_stockAfrLabel = new GridBagConstraints(); + gbc_stockAfrLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_stockAfrLabel.insets = insets3; + gbc_stockAfrLabel.gridx = 0; + gbc_stockAfrLabel.gridy = row; + selectionPanel.add(stockAfrLabel, gbc_stockAfrLabel); + + stockAfrName = new JTextField(isEmpty(Config.getAfrColumnName())); + stockAfrName.setMinimumSize(minTextDimension); + stockAfrName.setEditable(false); + stockAfrName.setBackground(Color.WHITE); + GridBagConstraints gbc_stockAfrName = new GridBagConstraints(); + gbc_stockAfrName.anchor = GridBagConstraints.NORTHWEST; + gbc_stockAfrName.insets = insets3; + gbc_stockAfrName.gridx = 1; + gbc_stockAfrName.gridy = row; + selectionPanel.add(stockAfrName, gbc_stockAfrName); + + JButton stockAfrButton = new JButton("", arrowImage); + stockAfrButton.setMargin(insets0); + stockAfrButton.setBorderPainted(false); + stockAfrButton.setContentAreaFilled(false); + GridBagConstraints gbc_stockAfrButton = new GridBagConstraints(); + gbc_stockAfrButton.anchor = GridBagConstraints.CENTER; + gbc_stockAfrButton.insets = insets1; + gbc_stockAfrButton.gridx = 2; + gbc_stockAfrButton.gridy = row; + stockAfrButton.setActionCommand("afr"); + stockAfrButton.addActionListener(this); + selectionPanel.add(stockAfrButton, gbc_stockAfrButton); + + row += 1; + // Closed/Open Loop Status + JLabel clolStatusLabel = new JLabel(clolStatusLabelText); + GridBagConstraints gbc_clolStatusLabel = new GridBagConstraints(); + gbc_clolStatusLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_clolStatusLabel.insets = insets3; + gbc_clolStatusLabel.gridx = 0; + gbc_clolStatusLabel.gridy = row; + selectionPanel.add(clolStatusLabel, gbc_clolStatusLabel); + + clolStatusName = new JTextField(isEmpty(Config.getClOlStatusColumnName())); + clolStatusName.setMinimumSize(minTextDimension); + clolStatusName.setEditable(false); + clolStatusName.setBackground(Color.WHITE); + GridBagConstraints gbc_clolStatusName = new GridBagConstraints(); + gbc_clolStatusName.anchor = GridBagConstraints.NORTHWEST; + gbc_clolStatusName.insets = insets3; + gbc_clolStatusName.gridx = 1; + gbc_clolStatusName.gridy = row; + selectionPanel.add(clolStatusName, gbc_clolStatusName); + + JButton clolStatusButton = new JButton("", arrowImage); + clolStatusButton.setMargin(insets0); + clolStatusButton.setBorderPainted(false); + clolStatusButton.setContentAreaFilled(false); + GridBagConstraints gbc_clolStatusButton = new GridBagConstraints(); + gbc_clolStatusButton.anchor = GridBagConstraints.CENTER; + gbc_clolStatusButton.insets = insets1; + gbc_clolStatusButton.gridx = 2; + gbc_clolStatusButton.gridy = row; + clolStatusButton.setActionCommand("clolstat"); + clolStatusButton.addActionListener(this); + selectionPanel.add(clolStatusButton, gbc_clolStatusButton); + + row += 1; + // Time + JLabel timeLabel = new JLabel(timeLabelText); + GridBagConstraints gbc_timeLabel = new GridBagConstraints(); + gbc_timeLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_timeLabel.insets = insets3; + gbc_timeLabel.gridx = 0; + gbc_timeLabel.gridy = row; + selectionPanel.add(timeLabel, gbc_timeLabel); + + timeName = new JTextField(isEmpty(Config.getTimeColumnName())); + timeName.setMinimumSize(minTextDimension); + timeName.setEditable(false); + timeName.setBackground(Color.WHITE); + GridBagConstraints gbc_timeName = new GridBagConstraints(); + gbc_timeName.anchor = GridBagConstraints.NORTHWEST; + gbc_timeName.insets = insets3; + gbc_timeName.gridx = 1; + gbc_timeName.gridy = row; + selectionPanel.add(timeName, gbc_timeName); + + JButton timeButton = new JButton("", arrowImage); + timeButton.setMargin(insets0); + timeButton.setBorderPainted(false); + timeButton.setContentAreaFilled(false); + GridBagConstraints gbc_timeButton = new GridBagConstraints(); + gbc_timeButton.anchor = GridBagConstraints.CENTER; + gbc_timeButton.insets = insets1; + gbc_timeButton.gridx = 2; + gbc_timeButton.gridy = row; + timeButton.setActionCommand("time"); + timeButton.addActionListener(this); + selectionPanel.add(timeButton, gbc_timeButton); + + row += 1; + // IAT + JLabel iatLabel = new JLabel(iatLabelText); + GridBagConstraints gbc_iatLabel = new GridBagConstraints(); + gbc_iatLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_iatLabel.insets = insets3; + gbc_iatLabel.gridx = 0; + gbc_iatLabel.gridy = row; + selectionPanel.add(iatLabel, gbc_iatLabel); + + iatName = new JTextField(isEmpty(Config.getIatColumnName())); + iatName.setMinimumSize(minTextDimension); + iatName.setEditable(false); + iatName.setBackground(Color.WHITE); + GridBagConstraints gbc_iatName = new GridBagConstraints(); + gbc_iatName.anchor = GridBagConstraints.NORTHWEST; + gbc_iatName.insets = insets3; + gbc_iatName.gridx = 1; + gbc_iatName.gridy = row; + selectionPanel.add(iatName, gbc_iatName); + + JButton iatButton = new JButton("", arrowImage); + iatButton.setMargin(insets0); + iatButton.setBorderPainted(false); + iatButton.setContentAreaFilled(false); + GridBagConstraints gbc_iatButton = new GridBagConstraints(); + gbc_iatButton.anchor = GridBagConstraints.CENTER; + gbc_iatButton.insets = insets1; + gbc_iatButton.gridx = 2; + gbc_iatButton.gridy = row; + iatButton.setActionCommand("iat"); + iatButton.addActionListener(this); + selectionPanel.add(iatButton, gbc_iatButton); + } + + // Columns selection table + columnsTable = new JTable() { + private static final long serialVersionUID = 1L; + public boolean isCellEditable(int row, int column) { return false; }; + }; + columnsTable.setColumnSelectionAllowed(false); + columnsTable.setCellSelectionEnabled(true); + columnsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + columnsTable.setTableHeader(null); + columnsTable.setModel(new DefaultTableModel(columns.length + 1, 1)); + columnsTable.setValueAt("", 0, 0); + for (int i = 0; i < columns.length; ++i) + columnsTable.setValueAt(columns[i], i + 1, 0); + + GridBagConstraints gbc_selectionPanel = new GridBagConstraints(); + gbc_selectionPanel.insets = insets3; + gbc_selectionPanel.anchor = GridBagConstraints.SOUTH; + gbc_selectionPanel.fill = GridBagConstraints.BOTH; + gbc_selectionPanel.gridx = 3; + gbc_selectionPanel.gridy = 2; + gbc_selectionPanel.gridheight = (isOpenLoop? 8 : 9); + JScrollPane scrollPane = new JScrollPane(columnsTable); + selectionPanel.add(scrollPane, gbc_selectionPanel); + + row += 1; + // filters note + JLabel filtNoteLabel = new JLabel("Filters Selection - use large/small values out of range to disable a filter"); + GridBagConstraints gbc_filtNoteLabel = new GridBagConstraints(); + gbc_filtNoteLabel.anchor = GridBagConstraints.WEST; + gbc_filtNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_filtNoteLabel.insets = new Insets(5, 5, 5, 5); + gbc_filtNoteLabel.gridx = 0; + gbc_filtNoteLabel.gridy = row; + gbc_filtNoteLabel.gridwidth = 4; + selectionPanel.add(filtNoteLabel, gbc_filtNoteLabel); + + if (isOpenLoop) { + row += 1; + // MAF Voltage Minimum Note + JLabel minMafVNoteLabel = new JLabel("Set this filter to process just Open Loop part of MAF curve data"); + minMafVNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_minMafVNoteLabel = new GridBagConstraints(); + gbc_minMafVNoteLabel.anchor = GridBagConstraints.WEST; + gbc_minMafVNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_minMafVNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_minMafVNoteLabel.gridx = 0; + gbc_minMafVNoteLabel.gridy = row; + gbc_minMafVNoteLabel.gridwidth = 4; + selectionPanel.add(minMafVNoteLabel, gbc_minMafVNoteLabel); + + row += 1; + JLabel minMafVLabel = new JLabel(minMafVLabelText); + GridBagConstraints gbc_minMafVLabel = new GridBagConstraints(); + gbc_minMafVLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_minMafVLabel.insets = insets3; + gbc_minMafVLabel.gridx = 0; + gbc_minMafVLabel.gridy = row; + selectionPanel.add(minMafVLabel, gbc_minMafVLabel); + + minMafVFilter = new JFormattedTextField(doubleFmt); + minMafVFilter.setText(String.valueOf(Config.getMafVMinimumValue())); + minMafVFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_minMafVFilter = new GridBagConstraints(); + gbc_minMafVFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_minMafVFilter.insets = insets3; + gbc_minMafVFilter.gridx = 1; + gbc_minMafVFilter.gridy = row; + selectionPanel.add(minMafVFilter, gbc_minMafVFilter); + + row += 1; + // WOT Stationary Point Note + JLabel wotPointNoteLabel = new JLabel("CL/OL transition. Use \"Throttle Angle %\" but could use \"Accel Pedal Angle %\" instead"); + wotPointNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_wotPointNoteLabel = new GridBagConstraints(); + gbc_wotPointNoteLabel.anchor = GridBagConstraints.WEST; + gbc_wotPointNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_wotPointNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_wotPointNoteLabel.gridx = 0; + gbc_wotPointNoteLabel.gridy = row; + gbc_wotPointNoteLabel.gridwidth = 4; + selectionPanel.add(wotPointNoteLabel, gbc_wotPointNoteLabel); + + row += 1; + JLabel wotStationaryLabel = new JLabel(wotStationaryLabelText); + GridBagConstraints gbc_wotStationaryLabel = new GridBagConstraints(); + gbc_wotStationaryLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_wotStationaryLabel.insets = insets3; + gbc_wotStationaryLabel.gridx = 0; + gbc_wotStationaryLabel.gridy = row; + selectionPanel.add(wotStationaryLabel, gbc_wotStationaryLabel); + + wotStationaryPointFilter = new JSpinner(new SpinnerNumberModel(Config.getWotStationaryPointValue(), 50, 100, 5)); + wotStationaryPointFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_wotStationaryPointFilter = new GridBagConstraints(); + gbc_wotStationaryPointFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_wotStationaryPointFilter.insets = insets3; + gbc_wotStationaryPointFilter.gridx = 1; + gbc_wotStationaryPointFilter.gridy = row; + selectionPanel.add(wotStationaryPointFilter, gbc_wotStationaryPointFilter); + + row += 1; + // AFR Error Percent Note + JLabel afrErrorNoteLabel = new JLabel("Remove data where \"AFR Error %\" exceeds desired change %"); + afrErrorNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_afrErrorNoteLabel = new GridBagConstraints(); + gbc_afrErrorNoteLabel.anchor = GridBagConstraints.WEST; + gbc_afrErrorNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_afrErrorNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_afrErrorNoteLabel.gridx = 0; + gbc_afrErrorNoteLabel.gridy = row; + gbc_afrErrorNoteLabel.gridwidth = 4; + selectionPanel.add(afrErrorNoteLabel, gbc_afrErrorNoteLabel); + + row += 1; + JLabel afrErrorLabel = new JLabel(afrErrorLabelText); + GridBagConstraints gbc_afrErrorLabel = new GridBagConstraints(); + gbc_afrErrorLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_afrErrorLabel.insets = insets3; + gbc_afrErrorLabel.gridx = 0; + gbc_afrErrorLabel.gridy = row; + selectionPanel.add(afrErrorLabel, gbc_afrErrorLabel); + + afrErrorFilter = new JFormattedTextField(doubleFmt); + afrErrorFilter.setText(String.valueOf(Config.getWidebandAfrErrorPercentValue())); + afrErrorFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_afrErrorFilter = new GridBagConstraints(); + gbc_afrErrorFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_afrErrorFilter.insets = insets3; + gbc_afrErrorFilter.gridx = 1; + gbc_afrErrorFilter.gridy = row; + selectionPanel.add(afrErrorFilter, gbc_afrErrorFilter); + } + else { + row += 1; + // CL/OL Status value for CL Note + JLabel clolStatusNoteLabel = new JLabel("Set this filter to filter out Open Loop data using logged OL/CL status"); + clolStatusNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_clolStatusNoteLabel = new GridBagConstraints(); + gbc_clolStatusNoteLabel.anchor = GridBagConstraints.WEST; + gbc_clolStatusNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_clolStatusNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_clolStatusNoteLabel.gridx = 0; + gbc_clolStatusNoteLabel.gridy = row; + gbc_clolStatusNoteLabel.gridwidth = 4; + selectionPanel.add(clolStatusNoteLabel, gbc_clolStatusNoteLabel); + + row += 1; + JLabel clolStatusLabel = new JLabel(clolStatusLabelText); + GridBagConstraints gbc_clolStatusLabel = new GridBagConstraints(); + gbc_clolStatusLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_clolStatusLabel.insets = insets3; + gbc_clolStatusLabel.gridx = 0; + gbc_clolStatusLabel.gridy = row; + selectionPanel.add(clolStatusLabel, gbc_clolStatusLabel); + + clolStatusFilter = new JSpinner(new SpinnerNumberModel(Config.getClOlStatusValue(), -1, 10, 1)); + clolStatusFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_clolStatusFilter = new GridBagConstraints(); + gbc_clolStatusFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_clolStatusFilter.insets = insets3; + gbc_clolStatusFilter.gridx = 1; + gbc_clolStatusFilter.gridy = row; + selectionPanel.add(clolStatusFilter, gbc_clolStatusFilter); + + row += 1; + // MAF Voltage Maximum Note + JLabel maxMafVNoteLabel = new JLabel("Set this filter to process just Closed Loop part of MAF curve data"); + maxMafVNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_maxMafVNoteLabel = new GridBagConstraints(); + gbc_maxMafVNoteLabel.anchor = GridBagConstraints.WEST; + gbc_maxMafVNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_maxMafVNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_maxMafVNoteLabel.gridx = 0; + gbc_maxMafVNoteLabel.gridy = row; + gbc_maxMafVNoteLabel.gridwidth = 4; + selectionPanel.add(maxMafVNoteLabel, gbc_maxMafVNoteLabel); + + row += 1; + JLabel maxMafVLabel = new JLabel(maxMafVLabelText); + GridBagConstraints gbc_maxMafVLabel = new GridBagConstraints(); + gbc_maxMafVLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_maxMafVLabel.insets = insets3; + gbc_maxMafVLabel.gridx = 0; + gbc_maxMafVLabel.gridy = row; + selectionPanel.add(maxMafVLabel, gbc_maxMafVLabel); + + maxMafVFilter = new JFormattedTextField(doubleFmt); + maxMafVFilter.setText(String.valueOf(Config.getMafVMaximumValue())); + maxMafVFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_maxMafVFilter = new GridBagConstraints(); + gbc_maxMafVFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_maxMafVFilter.insets = insets3; + gbc_maxMafVFilter.gridx = 1; + gbc_maxMafVFilter.gridy = row; + selectionPanel.add(maxMafVFilter, gbc_maxMafVFilter); + + row += 1; + // Engine Load Minimum Note + JLabel minEngineLoadNoteLabel = new JLabel("Set this filter to process data above specific Engine Load (eg filter out idle)"); + minEngineLoadNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_minEngineLoadNoteLabel = new GridBagConstraints(); + gbc_minEngineLoadNoteLabel.anchor = GridBagConstraints.WEST; + gbc_minEngineLoadNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_minEngineLoadNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_minEngineLoadNoteLabel.gridx = 0; + gbc_minEngineLoadNoteLabel.gridy = row; + gbc_minEngineLoadNoteLabel.gridwidth = 4; + selectionPanel.add(minEngineLoadNoteLabel, gbc_minEngineLoadNoteLabel); + + row += 1; + JLabel minEngineLoadLabel = new JLabel(minEngineLoadLabelText); + GridBagConstraints gbc_minEngineLoadLabel = new GridBagConstraints(); + gbc_minEngineLoadLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_minEngineLoadLabel.insets = insets3; + gbc_minEngineLoadLabel.gridx = 0; + gbc_minEngineLoadLabel.gridy = row; + selectionPanel.add(minEngineLoadLabel, gbc_minEngineLoadLabel); + + minEngineLoadFilter = new JFormattedTextField(doubleFmt); + minEngineLoadFilter.setText(String.valueOf(Config.getLoadMinimumValue())); + minEngineLoadFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_minEngineLoadFilter = new GridBagConstraints(); + gbc_minEngineLoadFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_minEngineLoadFilter.insets = insets3; + gbc_minEngineLoadFilter.gridx = 1; + gbc_minEngineLoadFilter.gridy = row; + selectionPanel.add(minEngineLoadFilter, gbc_minEngineLoadFilter); + + row += 1; + // Intake Air Temperature Maximum Note + JLabel maxIatNoteLabel = new JLabel("Set this filter to filter out data with high Intake Air Temperature"); + maxIatNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_maxIatNoteLabel = new GridBagConstraints(); + gbc_maxIatNoteLabel.anchor = GridBagConstraints.WEST; + gbc_maxIatNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_maxIatNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_maxIatNoteLabel.gridx = 0; + gbc_maxIatNoteLabel.gridy = row; + gbc_maxIatNoteLabel.gridwidth = 4; + selectionPanel.add(maxIatNoteLabel, gbc_maxIatNoteLabel); + + row += 1; + JLabel maxIatLabel = new JLabel(maxIatLabelText); + GridBagConstraints gbc_maxIatLabel = new GridBagConstraints(); + gbc_maxIatLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_maxIatLabel.insets = insets3; + gbc_maxIatLabel.gridx = 0; + gbc_maxIatLabel.gridy = row; + selectionPanel.add(maxIatLabel, gbc_maxIatLabel); + + maxIatFilter = new JFormattedTextField(doubleFmt); + maxIatFilter.setText(String.valueOf(Config.getIatMaximumValue())); + maxIatFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_maxIatFilter = new GridBagConstraints(); + gbc_maxIatFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_maxIatFilter.insets = insets3; + gbc_maxIatFilter.gridx = 1; + gbc_maxIatFilter.gridy = row; + selectionPanel.add(maxIatFilter, gbc_maxIatFilter); + + row += 1; + // AFR Maximum Note + JLabel maxAfrNoteLabel = new JLabel("Remove data where \"AFR\" is above the specified maximum"); + maxAfrNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_maxAfrNoteLabel = new GridBagConstraints(); + gbc_maxAfrNoteLabel.anchor = GridBagConstraints.WEST; + gbc_maxAfrNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_maxAfrNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_maxAfrNoteLabel.gridx = 0; + gbc_maxAfrNoteLabel.gridy = row; + gbc_maxAfrNoteLabel.gridwidth = 4; + selectionPanel.add(maxAfrNoteLabel, gbc_maxAfrNoteLabel); + + row += 1; + JLabel maxAfrLabel = new JLabel(maxAfrLabelText); + GridBagConstraints gbc_maxAfrLabel = new GridBagConstraints(); + gbc_maxAfrLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_maxAfrLabel.insets = insets3; + gbc_maxAfrLabel.gridx = 0; + gbc_maxAfrLabel.gridy = row; + selectionPanel.add(maxAfrLabel, gbc_maxAfrLabel); + + maxAfrFilter = new JFormattedTextField(doubleFmt); + maxAfrFilter.setText(String.valueOf(Config.getAfrMaximumValue())); + maxAfrFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_maxAfrFilter = new GridBagConstraints(); + gbc_maxAfrFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_maxAfrFilter.insets = insets3; + gbc_maxAfrFilter.gridx = 1; + gbc_maxAfrFilter.gridy = row; + selectionPanel.add(maxAfrFilter, gbc_maxAfrFilter); + + row += 1; + // AFR Minimum Note + JLabel minAfrNoteLabel = new JLabel("Remove data where \"AFR\" is below the specified minimum"); + minAfrNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_minAfrNoteLabel = new GridBagConstraints(); + gbc_minAfrNoteLabel.anchor = GridBagConstraints.WEST; + gbc_minAfrNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_minAfrNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_minAfrNoteLabel.gridx = 0; + gbc_minAfrNoteLabel.gridy = row; + gbc_minAfrNoteLabel.gridwidth = 4; + selectionPanel.add(minAfrNoteLabel, gbc_minAfrNoteLabel); + + row += 1; + JLabel minAfrLabel = new JLabel(minAfrLabelText); + GridBagConstraints gbc_minAfrLabel = new GridBagConstraints(); + gbc_minAfrLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_minAfrLabel.insets = insets3; + gbc_minAfrLabel.gridx = 0; + gbc_minAfrLabel.gridy = row; + selectionPanel.add(minAfrLabel, gbc_minAfrLabel); + + minAfrFilter = new JFormattedTextField(doubleFmt); + minAfrFilter.setText(String.valueOf(Config.getAfrMinimumValue())); + minAfrFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_minAfrFilter = new GridBagConstraints(); + gbc_minAfrFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_minAfrFilter.insets = insets3; + gbc_minAfrFilter.gridx = 1; + gbc_minAfrFilter.gridy = row; + selectionPanel.add(minAfrFilter, gbc_minAfrFilter); + + row += 1; + // dVdt Maximum Note + JLabel maxDvdtNoteLabel = new JLabel("Remove data where \"dV/dt\" is above the specified maximum"); + maxDvdtNoteLabel.setForeground(Color.BLUE); + GridBagConstraints gbc_maxDvdtNoteLabel = new GridBagConstraints(); + gbc_maxDvdtNoteLabel.anchor = GridBagConstraints.WEST; + gbc_maxDvdtNoteLabel.fill = GridBagConstraints.HORIZONTAL; + gbc_maxDvdtNoteLabel.insets = new Insets(5, 10, 5, 5); + gbc_maxDvdtNoteLabel.gridx = 0; + gbc_maxDvdtNoteLabel.gridy = row; + gbc_maxDvdtNoteLabel.gridwidth = 4; + selectionPanel.add(maxDvdtNoteLabel, gbc_maxDvdtNoteLabel); + + row += 1; + JLabel maxDvdtLabel = new JLabel(maxDvdtLabelText); + GridBagConstraints gbc_maxDvdtLabel = new GridBagConstraints(); + gbc_maxDvdtLabel.anchor = GridBagConstraints.NORTHEAST; + gbc_maxDvdtLabel.insets = insets3; + gbc_maxDvdtLabel.gridx = 0; + gbc_maxDvdtLabel.gridy = row; + selectionPanel.add(maxDvdtLabel, gbc_maxDvdtLabel); + + maxDvdtFilter = new JFormattedTextField(doubleFmt); + maxDvdtFilter.setText(String.valueOf(Config.getDvDtMaximumValue())); + maxDvdtFilter.setMinimumSize(minFilterDimension); + GridBagConstraints gbc_maxDvdtFilter = new GridBagConstraints(); + gbc_maxDvdtFilter.anchor = GridBagConstraints.NORTHWEST; + gbc_maxDvdtFilter.insets = insets3; + gbc_maxDvdtFilter.gridx = 1; + gbc_maxDvdtFilter.gridy = row; + selectionPanel.add(maxDvdtFilter, gbc_maxDvdtFilter); + } + + // Set window params + selectionPanel.setPreferredSize(new Dimension(650, (isOpenLoop ? 400 : 600))); + JComponent[] inputs = new JComponent[] { selectionPanel }; + do { + if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Columns / Filters Settings", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) + return false; + } + while (!validate()); + + return true; + } + + public String isEmpty(String current) { + if (current.equals(Config.NO_NAME)) + return ""; + return current; + } + + private boolean validate() { + boolean ret = true; + String error = ""; + String value; + String colName; + + // Engine Speed + value = rpmName.getText().trim(); + colName = rpmLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setRpmColumnName(value); + // Engine Load + value = loadName.getText().trim(); + colName = loadLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setLoadColumnName(value); + // AFR Learning + value = afLearningName.getText().trim(); + colName = afLearningLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setAfLearningColumnName(value); + // AFR Correction + value = afCorrectionName.getText().trim(); + colName = afCorrectionLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setAfCorrectionColumnName(value); + // Maf Voltage + value = mafVName.getText().trim(); + colName = mafVLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setMafVoltageColumnName(value); + if (isOpenLoop) { + // Wideband AFR + value = wbAfrName.getText().trim(); + colName = wbAfrLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setWidebandAfrColumnName(value); + // Throttle Angle + value = thtlAngleName.getText().trim(); + colName = thtlAngleLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setThrottleAngleColumnName(value); + // Commanded AFR + value = commAfrName.getText().trim(); + colName = commAfrLabelText; + if (isPolfTableSet) { + if (value.isEmpty()) + value = Config.NO_NAME; + Config.setCommandedAfrColumnName(value); + } + else { + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified if \"Primary Open Loop Fueling\" table is not set.\n"; + } + else + Config.setCommandedAfrColumnName(value); + } + // Min MAF Voltage filter + Config.setMafVMinimumValue(Double.valueOf(minMafVFilter.getText())); + // WOT Stationary point + Config.setWotStationaryPointValue(Integer.valueOf(wotStationaryPointFilter.getValue().toString())); + // Afr Error filter + Config.setWidebandAfrErrorPercentValue(Double.valueOf(afrErrorFilter.getText())); + } + else { + // Stock AFR + value = stockAfrName.getText().trim(); + colName = stockAfrLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setAfrColumnName(value); + // CL/OL Status + value = clolStatusName.getText().trim(); + colName = clolStatusLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setClOlStatusColumnName(value); + // Time + value = timeName.getText().trim(); + colName = timeLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setTimeColumnName(value); + // Intake Air Temperature + value = iatName.getText().trim(); + colName = iatLabelText; + if (value.isEmpty()) { + ret = false; + error += "\"" + colName + "\" column must be specified\n"; + } + else + Config.setIatColumnName(value); + // CL/OL Status + Config.setClOlStatusValue(Integer.valueOf(clolStatusFilter.getValue().toString())); + // Max MAF Voltage filter + Config.setMafVMaximumValue(Double.valueOf(maxMafVFilter.getText())); + // Engine Load filter + Config.setLoadMinimumValue(Double.valueOf(minEngineLoadFilter.getText())); + // IAT filter + Config.setIatMaximumValue(Double.valueOf(maxIatFilter.getText())); + // AFR filters + Config.setAfrMaximumValue(Double.valueOf(maxAfrFilter.getText())); + Config.setAfrMinimumValue(Double.valueOf(minAfrFilter.getText())); + // dV/dt filter + Config.setDvDtMaximumValue(Double.valueOf(maxDvdtFilter.getText())); + } + if (!ret) + JOptionPane.showMessageDialog(null, error, "Error", JOptionPane.ERROR_MESSAGE); + return ret; + } + + @Override + public void actionPerformed(ActionEvent e) { + int row = columnsTable.getSelectedRow(); + if (row < 0) { + JOptionPane.showMessageDialog(null, "Please select column name from the list", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + String value = columnsTable.getValueAt(row, 0).toString(); + if ("thtlAngle".equals(e.getActionCommand())) + thtlAngleName.setText(value); + else if ("afrlearn".equals(e.getActionCommand())) + afLearningName.setText(value); + else if ("afrcorr".equals(e.getActionCommand())) + afCorrectionName.setText(value); + else if ("mafv".equals(e.getActionCommand())) + mafVName.setText(value); + else if ("wbafr".equals(e.getActionCommand())) + wbAfrName.setText(value); + else if ("rpm".equals(e.getActionCommand())) + rpmName.setText(value); + else if ("load".equals(e.getActionCommand())) + loadName.setText(value); + else if ("cmdafr".equals(e.getActionCommand())) + commAfrName.setText(value); + else if ("afr".equals(e.getActionCommand())) + stockAfrName.setText(value); + else if ("clolstat".equals(e.getActionCommand())) + clolStatusName.setText(value); + else if ("time".equals(e.getActionCommand())) + timeName.setText(value); + else if ("iat".equals(e.getActionCommand())) + iatName.setText(value); + } +} diff --git a/src/com/vgi/mafscaling/Config.java b/src/com/vgi/mafscaling/Config.java index 21aa8d2..94da695 100644 --- a/src/com/vgi/mafscaling/Config.java +++ b/src/com/vgi/mafscaling/Config.java @@ -1,3 +1,21 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + package com.vgi.mafscaling; import java.awt.Dimension; @@ -15,7 +33,20 @@ public class Config { private static final Logger logger = Logger.getLogger(Config.class); private static final String CFG_FILE = "config.xml"; + public static final String NO_NAME = "#$#"; private static Properties props = new Properties(); + + public static String getProperty(String name) { + return props.getProperty(name, ""); + } + + public static void setProperty(String name, String prop) { + props.setProperty(name, prop); + } + + public static void removeProperty(String name) { + props.remove(name); + } public static Dimension getWindowSize() { return new Dimension(Integer.parseInt(props.getProperty("WindowWidth", "300")), Integer.parseInt(props.getProperty("WindowHeight", "200"))); @@ -35,6 +66,24 @@ public static void setWindowLocation(Point p) { props.setProperty("WindowPositionY", Integer.toString(p.y)); } + public static Dimension getCompWindowSize() { + return new Dimension(Integer.parseInt(props.getProperty("CompareWindowWidth", "300")), Integer.parseInt(props.getProperty("WindowHeight", "140"))); + } + + public static void setCompWindowSize(Dimension d) { + props.setProperty("CompareWindowWidth", Integer.toString(d.width)); + props.setProperty("CompareWindowHeight", Integer.toString(d.height)); + } + + public static Point getCompWindowLocation() { + return new Point(Integer.parseInt(props.getProperty("CompareWindowPositionX", "50")), Integer.parseInt(props.getProperty("WindowPositionY", "50"))); + } + + public static void setCompWindowLocation(Point p) { + props.setProperty("CompareWindowPositionX", Integer.toString(p.x)); + props.setProperty("CompareWindowPositionY", Integer.toString(p.y)); + } + public static String getThrottleAngleColumnName() { return props.getProperty("ThrottleAngleColumnName", "#$#"); } @@ -234,6 +283,22 @@ public static String getPOLFuelingFiles() { public static void setPOLFuelingFiles(String s) { props.setProperty("POLFuelingFiles", s); } + + public static String getXAxisTemplates() { + return props.getProperty("XAxisTemplates", ","); + } + + public static void setXAxisTemplates(String s) { + props.setProperty("XAxisTemplates", s); + } + + public static String getYAxisTemplates() { + return props.getProperty("YAxisTemplates", ","); + } + + public static void setYAxisTemplates(String s) { + props.setProperty("YAxisTemplates", s); + } public static void load() { InputStream is = null; diff --git a/src/com/vgi/mafscaling/ExcelAdapter.java b/src/com/vgi/mafscaling/ExcelAdapter.java index a86d811..e0e614f 100644 --- a/src/com/vgi/mafscaling/ExcelAdapter.java +++ b/src/com/vgi/mafscaling/ExcelAdapter.java @@ -108,7 +108,7 @@ public void addTable(JTable table, boolean extendRows, boolean extendCols) { * @param extendRows, if true will automatically add rows to the table to be able to paste all data * @param extendCols, if true will automatically add columns to the table to be able to paste all data */ - private void addTable(JTable table, boolean disableCopy, boolean disableCut, boolean disablePaste, boolean disableClear, boolean extendRows, boolean extendCols) { + public void addTable(JTable table, boolean disableCopy, boolean disableCut, boolean disablePaste, boolean disableClear, boolean extendRows, boolean extendCols) { addTable(table, disableCopy, disableCut, disablePaste, disableClear, true, true, true, extendRows, extendCols); } @@ -254,7 +254,7 @@ else if (e.getActionCommand().equals("Paste Vertical")) onPasteVertical(tableHolder.getTable(), tableHolder.getExtendRows(), tableHolder.getExtendCols()); } - private void onClearSelection(JTable table) { + protected void onClearSelection(JTable table) { // Check to ensure we have selected only a contiguous block of cells int numcols = table.getSelectedColumnCount(); int numrows = table.getSelectedRowCount(); @@ -282,11 +282,11 @@ private void onClearSelection(JTable table) { } } - private void onSelectAll(JTable table) { + protected void onSelectAll(JTable table) { table.selectAll(); } - private void onCopy(JTable table) { + protected void onCopy(JTable table) { StringBuffer sbf = new StringBuffer(); // Check to ensure we have selected only a contiguous block of cells int numcols = table.getSelectedColumnCount(); @@ -315,7 +315,7 @@ private void onCopy(JTable table) { system.setContents(stsel, stsel); } - private void onCopyVertical(JTable table) { + protected void onCopyVertical(JTable table) { StringBuffer sbf = new StringBuffer(); // Check to ensure we have selected only a contiguous block of cells int numcols = table.getSelectedColumnCount(); @@ -344,7 +344,7 @@ private void onCopyVertical(JTable table) { system.setContents(stsel, stsel); } - private void onCopyRR(JTable table) { + protected void onCopyRR(JTable table) { StringBuffer sbf = new StringBuffer("[Table2D]" + eol); // Check to ensure we have selected only a contiguous block of cells int numcols = table.getSelectedColumnCount(); @@ -373,7 +373,7 @@ private void onCopyRR(JTable table) { system.setContents(stsel, stsel); } - private void onPaste(JTable table, boolean extendRows, boolean extendCols) { + protected void onPaste(JTable table, boolean extendRows, boolean extendCols) { if (table.getSelectedRows() == null || table.getSelectedRows().length == 0 || table.getSelectedColumns() == null || table.getSelectedColumns().length == 0) return; @@ -393,16 +393,26 @@ private void onPaste(JTable table, boolean extendRows, boolean extendCols) { int colCount = entries.length; if (extendCols && startCol + colCount > table.getColumnCount()) Utils.ensureColumnCount(startCol + colCount, table); - // populate cells with the data - for (int i = 0; i < rowCount; ++i) { - if (i > 0) - rowstring = lines[i]; - entries = rowstring.split("\t", -1); - for (int j = 0; j < entries.length; ++j) { - value = entries[j]; - if (startRow + i < table.getRowCount() && startCol + j< table.getColumnCount()) - table.setValueAt(value, startRow + i, startCol + j); - } + if (rowCount == 1 && colCount == 1) { + int[] rows = table.getSelectedRows(); + int[] cols = table.getSelectedColumns(); + // populate cells with the data + for (int i = 0; i < rows.length; ++i) { + for (int j = 0; j < cols.length; ++j) + table.setValueAt(entries[0], rows[i], cols[j]); + } + } + else { + for (int i = 0; i < rowCount; ++i) { + if (i > 0) + rowstring = lines[i]; + entries = rowstring.split("\t", -1); + for (int j = 0; j < entries.length; ++j) { + value = entries[j]; + if (startRow + i < table.getRowCount() && startCol + j< table.getColumnCount()) + table.setValueAt(value, startRow + i, startCol + j); + } + } } } } @@ -412,7 +422,7 @@ private void onPaste(JTable table, boolean extendRows, boolean extendCols) { } } - private void onPasteVertical(JTable table, boolean extendRows, boolean extendCols) { + protected void onPasteVertical(JTable table, boolean extendRows, boolean extendCols) { if (table.getSelectedRows() == null || table.getSelectedRows().length == 0 || table.getSelectedColumns() == null || table.getSelectedColumns().length == 0) return; diff --git a/src/com/vgi/mafscaling/IMafChartHolder.java b/src/com/vgi/mafscaling/IMafChartHolder.java index 863a5c8..a6e267f 100644 --- a/src/com/vgi/mafscaling/IMafChartHolder.java +++ b/src/com/vgi/mafscaling/IMafChartHolder.java @@ -1,8 +1,26 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + package com.vgi.mafscaling; /** * Interface that defines required function for MafChartPanel holders */ public interface IMafChartHolder { - public void onMovePoint(int itemIndex, double valueY); + public void onMovePoint(int itemIndex, double valueX, double valueY); } diff --git a/src/com/vgi/mafscaling/LogStats.java b/src/com/vgi/mafscaling/LogStats.java new file mode 100644 index 0000000..03e87da --- /dev/null +++ b/src/com/vgi/mafscaling/LogStats.java @@ -0,0 +1,974 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.vgi.mafscaling; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.Format; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.ListSelectionModel; +import javax.swing.border.LineBorder; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import org.apache.log4j.Logger; +import org.math.plot.Plot3DPanel; + +public class LogStats extends JTabbedPane implements ActionListener { + private enum Statistics {COUNT, MINIMUM, MAXIMUM, MEAN, MEDIAN, MODE, RANGE, VARIANCE, STDDEV}; + private enum Plot3D {GRID, BAR, LINE, SCATTER}; + private enum DataFilter {NONE, LESS, EQUAL, GREATER}; + private static final long serialVersionUID = -7486851151646396168L; + private static final Logger logger = Logger.getLogger(LogStats.class); + private static final int ColumnWidth = 50; + private static final int DataTableRowCount = 50; + private static final int DataTableColumnCount = 25; + + private JFileChooser fileChooser = new JFileChooser(); + private File logFile = null; + private JComboBox xAxisColumn = null; + private JComboBox yAxisColumn = null; + private JComboBox dataColumn = null; + private JComboBox statistics = null; + private JComboBox filters = null; + private JFormattedTextField xAxisRoundTextBox = null; + private JFormattedTextField yAxisRoundTextBox = null; + private JTable dataTable = null; + private ExcelAdapter excelAdapter = null; + private HashMap>> xData = null; + private Plot3DPanel plot = null; + private ButtonGroup rbGroup = new ButtonGroup(); + private JRadioButton rbGridPlot = null; + private JRadioButton rbBarPlot = null; + private JRadioButton rbLinePlot = null; + private JRadioButton rbScatterPlot = null; + private ArrayList xAxisArray; + private ArrayList yAxisArray; + private DataFilter dataFilterType = DataFilter.NONE; + private double dataFilter = Double.NaN; + private int dataRounding = 0; + + public LogStats(int tabPlacement) { + super(tabPlacement); + initialize(); + } + + private void initialize() { + fileChooser.setCurrentDirectory(new File(".")); + excelAdapter = new ExcelAdapter(); + xAxisArray = new ArrayList(); + yAxisArray = new ArrayList(); + createDataTab(); + createGraghTab(); + createUsageTab(); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // DATA TAB + ////////////////////////////////////////////////////////////////////////////////////// + + private void createDataTab() { + JPanel dataPanel = new JPanel(); + add(dataPanel, "
D
a
t
a
"); + GridBagLayout gbl_dataPanel = new GridBagLayout(); + gbl_dataPanel.columnWidths = new int[] {0}; + gbl_dataPanel.rowHeights = new int[] {0, 0}; + gbl_dataPanel.columnWeights = new double[]{0.0}; + gbl_dataPanel.rowWeights = new double[]{0.0, 1.0}; + dataPanel.setLayout(gbl_dataPanel); + + createControlPanel(dataPanel); + createDataPanel(dataPanel); + } + + private void createControlPanel(JPanel dataPanel) { + NumberFormat doubleFmt = NumberFormat.getNumberInstance(); + doubleFmt.setGroupingUsed(false); + doubleFmt.setMaximumFractionDigits(2); + doubleFmt.setMinimumFractionDigits(1); + doubleFmt.setRoundingMode(RoundingMode.HALF_UP); + + JPanel cntlPanel = new JPanel(); + GridBagConstraints gbc_ctrlPanel = new GridBagConstraints(); + gbc_ctrlPanel.insets = new Insets(3, 3, 3, 3); + gbc_ctrlPanel.anchor = GridBagConstraints.PAGE_START; + gbc_ctrlPanel.fill = GridBagConstraints.HORIZONTAL; + gbc_ctrlPanel.weightx = 1.0; + gbc_ctrlPanel.gridx = 0; + gbc_ctrlPanel.gridy = 0; + dataPanel.add(cntlPanel, gbc_ctrlPanel); + + GridBagLayout gbl_cntlPanel = new GridBagLayout(); + gbl_cntlPanel.columnWidths = new int[]{0, 0, 0, 0, 0, 0, 0, 0}; + gbl_cntlPanel.rowHeights = new int[]{0, 0, 0}; + gbl_cntlPanel.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0}; + gbl_cntlPanel.rowWeights = new double[]{0.0, 0.0, 0.0}; + cntlPanel.setLayout(gbl_cntlPanel); + + JButton selectLogButton = new JButton("Select Log"); + GridBagConstraints gbc_selectLogButton = new GridBagConstraints(); + gbc_selectLogButton.anchor = GridBagConstraints.PAGE_START; + gbc_selectLogButton.insets = new Insets(3, 3, 3, 3); + gbc_selectLogButton.gridx = 0; + gbc_selectLogButton.gridy = 0; + gbc_selectLogButton.gridheight = 3; + selectLogButton.setActionCommand("selectlog"); + selectLogButton.addActionListener(this); + cntlPanel.add(selectLogButton, gbc_selectLogButton); + + JLabel xAxisLabel = new JLabel("X-Axis"); + xAxisLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_xAxisLabel = new GridBagConstraints(); + gbc_xAxisLabel.anchor = GridBagConstraints.EAST; + gbc_xAxisLabel.insets = new Insets(3, 3, 3, 0); + gbc_xAxisLabel.gridx = 1; + gbc_xAxisLabel.gridy = 0; + cntlPanel.add(xAxisLabel, gbc_xAxisLabel); + + xAxisColumn = new JComboBox(); + GridBagConstraints gbc_xAxisColumn = new GridBagConstraints(); + gbc_xAxisColumn.anchor = GridBagConstraints.PAGE_START; + gbc_xAxisColumn.insets = new Insets(3, 3, 3, 3); + gbc_xAxisColumn.gridx = 2; + gbc_xAxisColumn.gridy = 0; + xAxisColumn.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + cntlPanel.add(xAxisColumn, gbc_xAxisColumn); + + JLabel xAxisScalingLabel = new JLabel("X-Axis Step"); + xAxisScalingLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_xAxisScalingLabel = new GridBagConstraints(); + gbc_xAxisScalingLabel.anchor = GridBagConstraints.EAST; + gbc_xAxisScalingLabel.insets = new Insets(3, 3, 3, 0); + gbc_xAxisScalingLabel.gridx = 3; + gbc_xAxisScalingLabel.gridy = 0; + cntlPanel.add(xAxisScalingLabel, gbc_xAxisScalingLabel); + + xAxisRoundTextBox = new JFormattedTextField(doubleFmt); + xAxisRoundTextBox.setPreferredSize(new Dimension(65, 20)); + GridBagConstraints gbc_xAxisRoundTextBox = new GridBagConstraints(); + gbc_xAxisRoundTextBox.anchor = GridBagConstraints.PAGE_START; + gbc_xAxisRoundTextBox.fill = GridBagConstraints.HORIZONTAL; + gbc_xAxisRoundTextBox.insets = new Insets(3, 3, 3, 3); + gbc_xAxisRoundTextBox.gridx = 4; + gbc_xAxisRoundTextBox.gridy = 0; + cntlPanel.add(xAxisRoundTextBox, gbc_xAxisRoundTextBox); + + JLabel yAxisLabel = new JLabel("Y-Axis"); + yAxisLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_yAxisLabel = new GridBagConstraints(); + gbc_yAxisLabel.anchor = GridBagConstraints.EAST; + gbc_yAxisLabel.insets = new Insets(3, 3, 3, 0); + gbc_yAxisLabel.gridx = 1; + gbc_yAxisLabel.gridy = 1; + cntlPanel.add(yAxisLabel, gbc_yAxisLabel); + + yAxisColumn = new JComboBox(); + GridBagConstraints gbc_yAxisColumn = new GridBagConstraints(); + gbc_yAxisColumn.anchor = GridBagConstraints.PAGE_START; + gbc_yAxisColumn.insets = new Insets(3, 3, 3, 3); + gbc_yAxisColumn.gridx = 2; + gbc_yAxisColumn.gridy = 1; + yAxisColumn.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + cntlPanel.add(yAxisColumn, gbc_yAxisColumn); + + JLabel yAxisScalingLabel = new JLabel("Y-Axis Step"); + yAxisScalingLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_yAxisScalingLabel = new GridBagConstraints(); + gbc_yAxisScalingLabel.anchor = GridBagConstraints.EAST; + gbc_yAxisScalingLabel.insets = new Insets(3, 3, 3, 0); + gbc_yAxisScalingLabel.gridx = 3; + gbc_yAxisScalingLabel.gridy = 1; + cntlPanel.add(yAxisScalingLabel, gbc_yAxisScalingLabel); + + yAxisRoundTextBox = new JFormattedTextField(doubleFmt); + yAxisRoundTextBox.setPreferredSize(new Dimension(65, 20)); + GridBagConstraints gbc_yAxisRoundTextBox = new GridBagConstraints(); + gbc_yAxisRoundTextBox.anchor = GridBagConstraints.PAGE_START; + gbc_yAxisRoundTextBox.fill = GridBagConstraints.HORIZONTAL; + gbc_yAxisRoundTextBox.insets = new Insets(3, 3, 3, 3); + gbc_yAxisRoundTextBox.gridx = 4; + gbc_yAxisRoundTextBox.gridy = 1; + cntlPanel.add(yAxisRoundTextBox, gbc_yAxisRoundTextBox); + + JLabel datasLabel = new JLabel("Data"); + datasLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_datasLabel = new GridBagConstraints(); + gbc_datasLabel.anchor = GridBagConstraints.EAST; + gbc_datasLabel.insets = new Insets(3, 3, 3, 0); + gbc_datasLabel.gridx = 1; + gbc_datasLabel.gridy = 2; + cntlPanel.add(datasLabel, gbc_datasLabel); + + dataColumn = new JComboBox(); + GridBagConstraints gbc_dataColumn = new GridBagConstraints(); + gbc_dataColumn.anchor = GridBagConstraints.PAGE_START; + gbc_dataColumn.insets = new Insets(3, 3, 3, 3); + gbc_dataColumn.gridx = 2; + gbc_dataColumn.gridy = 2; + dataColumn.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + cntlPanel.add(dataColumn, gbc_dataColumn); + + JLabel statisticsLabel = new JLabel("Statistics"); + statisticsLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_statisticsLabel = new GridBagConstraints(); + gbc_statisticsLabel.anchor = GridBagConstraints.EAST; + gbc_statisticsLabel.insets = new Insets(3, 3, 3, 0); + gbc_statisticsLabel.gridx = 3; + gbc_statisticsLabel.gridy = 2; + cntlPanel.add(statisticsLabel, gbc_statisticsLabel); + + statistics = new JComboBox(new String[] {"Count", "Minimum", "Maximum", "Mean", "Median", "Mode", "Range", "Variance", "Std Deviation"}); + GridBagConstraints gbc_statistics = new GridBagConstraints(); + gbc_statistics.anchor = GridBagConstraints.PAGE_START; + gbc_statistics.insets = new Insets(3, 3, 3, 3); + gbc_statistics.gridx = 4; + gbc_statistics.gridy = 2; + cntlPanel.add(statistics, gbc_statistics); + + JLabel orLabel = new JLabel("or"); + statisticsLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_orLabel = new GridBagConstraints(); + gbc_orLabel.anchor = GridBagConstraints.CENTER; + gbc_orLabel.insets = new Insets(3, 3, 3, 0); + gbc_orLabel.gridx = 5; + gbc_orLabel.gridy = 0; + gbc_orLabel.gridheight = 2; + cntlPanel.add(orLabel, gbc_orLabel); + + JButton btnSetAxisButton = new JButton("Set Axis"); + GridBagConstraints gbc_btnSetAxisButton = new GridBagConstraints(); + gbc_btnSetAxisButton.anchor = GridBagConstraints.CENTER; + gbc_btnSetAxisButton.insets = new Insets(3, 3, 3, 3); + gbc_btnSetAxisButton.gridx = 6; + gbc_btnSetAxisButton.gridy = 0; + gbc_btnSetAxisButton.gridheight = 2; + btnSetAxisButton.setActionCommand("setaxis"); + btnSetAxisButton.addActionListener(this); + cntlPanel.add(btnSetAxisButton, gbc_btnSetAxisButton); + + filters = new JComboBox(new String[] {"", "Less", "Equal", "Greater"}); + GridBagConstraints gbc_filters = new GridBagConstraints(); + gbc_filters.anchor = GridBagConstraints.PAGE_START; + gbc_filters.fill = GridBagConstraints.HORIZONTAL; + gbc_filters.insets = new Insets(3, 3, 3, 3); + gbc_filters.gridx = 6; + gbc_filters.gridy = 2; + filters.setActionCommand("filter"); + filters.addActionListener(this); + cntlPanel.add(filters, gbc_filters); + + JButton btnGoButton = new JButton("GO"); + GridBagConstraints gbc_btnGoButton = new GridBagConstraints(); + gbc_btnGoButton.anchor = GridBagConstraints.EAST; + gbc_btnGoButton.insets = new Insets(3, 3, 3, 3); + gbc_btnGoButton.weightx = 1.0; + gbc_btnGoButton.gridx = 7; + gbc_btnGoButton.gridy = 0; + gbc_btnGoButton.gridheight = 3; + btnGoButton.setActionCommand("go"); + btnGoButton.addActionListener(this); + cntlPanel.add(btnGoButton, gbc_btnGoButton); + } + + private void createDataPanel(JPanel dataPanel) { + TableColumnModel dataTableModel = new DefaultTableColumnModel(); + dataTableModel.addColumn(new TableColumn(0, 250)); + + dataTable = new JTable() { + private static final long serialVersionUID = 6526901361175099297L; + public boolean isCellEditable(int row, int column) { return false; }; + }; + dataTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + dataTable.setColumnSelectionAllowed(true); + dataTable.setCellSelectionEnabled(true); + dataTable.setBorder(new LineBorder(new Color(0, 0, 0))); + dataTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + dataTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + dataTable.setModel(new DefaultTableModel(DataTableRowCount, DataTableColumnCount)); + dataTable.setTableHeader(null); + Utils.initializeTable(dataTable, ColumnWidth); + + Format[][] formatMatrix = { { new DecimalFormat("0.00"), new DecimalFormat("0.00") } }; + NumberFormatRenderer renderer = (NumberFormatRenderer)dataTable.getDefaultRenderer(Object.class); + renderer.setFormats(formatMatrix); + + GridBagConstraints gbc_dataTable = new GridBagConstraints(); + gbc_dataTable.insets = new Insets(3, 3, 3, 3); + gbc_dataTable.anchor = GridBagConstraints.PAGE_START; + gbc_dataTable.fill = GridBagConstraints.BOTH; + gbc_dataTable.weightx = 1.0; + gbc_dataTable.weighty = 1.0; + gbc_dataTable.gridx = 0; + gbc_dataTable.gridy = 1; + gbc_dataTable.gridwidth = 14; + + JScrollPane scrollPane = new JScrollPane(dataTable); + dataPanel.add(scrollPane, gbc_dataTable); + excelAdapter.addTable(dataTable, false, true, true, true, true, true, true, true, true); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // CREATE CHART TAB + ////////////////////////////////////////////////////////////////////////////////////// + + private void createGraghTab() { + JPanel plotPanel = new JPanel(); + add(plotPanel, "
C
h
a
r
t
"); + GridBagLayout gbl_plotPanel = new GridBagLayout(); + gbl_plotPanel.columnWidths = new int[] {0}; + gbl_plotPanel.rowHeights = new int[] {0, 0}; + gbl_plotPanel.columnWeights = new double[]{1.0}; + gbl_plotPanel.rowWeights = new double[]{0.0, 1.0}; + plotPanel.setLayout(gbl_plotPanel); + + JPanel cntlPanel = new JPanel(); + GridBagConstraints gbl_ctrlPanel = new GridBagConstraints(); + gbl_ctrlPanel.insets = new Insets(3, 3, 3, 3); + gbl_ctrlPanel.anchor = GridBagConstraints.NORTH; + gbl_ctrlPanel.weightx = 1.0; + gbl_ctrlPanel.fill = GridBagConstraints.HORIZONTAL; + gbl_ctrlPanel.gridx = 0; + gbl_ctrlPanel.gridy = 0; + plotPanel.add(cntlPanel, gbl_ctrlPanel); + + GridBagLayout gbl_cntlPanel = new GridBagLayout(); + gbl_cntlPanel.columnWidths = new int[]{0, 0, 0, 0, 0}; + gbl_cntlPanel.rowHeights = new int[]{0}; + gbl_cntlPanel.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, 1.0}; + gbl_cntlPanel.rowWeights = new double[]{0}; + cntlPanel.setLayout(gbl_cntlPanel); + + rbGridPlot = new JRadioButton("Grid"); + GridBagConstraints gbc_rbGridPlot = new GridBagConstraints(); + gbc_rbGridPlot.anchor = GridBagConstraints.WEST; + gbc_rbGridPlot.insets = new Insets(0, 0, 3, 3); + gbc_rbGridPlot.gridx = 0; + gbc_rbGridPlot.gridy = 0; + rbGridPlot.setActionCommand("grid"); + rbGridPlot.addActionListener(this); + rbGroup.add(rbGridPlot); + cntlPanel.add(rbGridPlot, gbc_rbGridPlot); + + rbBarPlot = new JRadioButton("Bar"); + GridBagConstraints gbc_rbBarPlot = new GridBagConstraints(); + gbc_rbBarPlot.anchor = GridBagConstraints.WEST; + gbc_rbBarPlot.insets = new Insets(0, 0, 3, 3); + gbc_rbBarPlot.gridx = 1; + gbc_rbBarPlot.gridy = 0; + rbBarPlot.setActionCommand("bar"); + rbBarPlot.addActionListener(this); + rbGroup.add(rbBarPlot); + cntlPanel.add(rbBarPlot, gbc_rbBarPlot); + + rbLinePlot = new JRadioButton("Line"); + GridBagConstraints gbc_rbLinePlot = new GridBagConstraints(); + gbc_rbLinePlot.anchor = GridBagConstraints.WEST; + gbc_rbLinePlot.insets = new Insets(0, 0, 3, 3); + gbc_rbLinePlot.gridx = 2; + gbc_rbLinePlot.gridy = 0; + rbLinePlot.setActionCommand("line"); + rbLinePlot.addActionListener(this); + rbGroup.add(rbLinePlot); + cntlPanel.add(rbLinePlot, gbc_rbLinePlot); + + rbScatterPlot = new JRadioButton("Scatter"); + GridBagConstraints gbc_rbScatterPlot = new GridBagConstraints(); + gbc_rbScatterPlot.anchor = GridBagConstraints.WEST; + gbc_rbScatterPlot.insets = new Insets(0, 0, 3, 3); + gbc_rbScatterPlot.gridx = 3; + gbc_rbScatterPlot.gridy = 0; + rbScatterPlot.setActionCommand("scatter"); + rbScatterPlot.addActionListener(this); + rbGroup.add(rbScatterPlot); + cntlPanel.add(rbScatterPlot, gbc_rbScatterPlot); + + plot = new Plot3DPanel("NORTH") { + private static final long serialVersionUID = 7914951068593204419L; + public void addPlotToolBar(String location) { + super.addPlotToolBar(location); + super.plotToolBar.remove(4); + super.plotToolBar.remove(5); + } + }; + plot.setAutoBounds(); + plot.setAutoscrolls(true); + plot.setEditable(false); + plot.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + plot.setForeground(Color.BLACK); + plot.getAxis(0).setColor(Color.BLACK); + plot.getAxis(1).setColor(Color.BLACK); + plot.getAxis(2).setColor(Color.BLACK); + + GridBagConstraints gbl_chartPanel = new GridBagConstraints(); + gbl_chartPanel.anchor = GridBagConstraints.CENTER; + gbl_chartPanel.insets = new Insets(3, 3, 3, 3); + gbl_chartPanel.weightx = 1.0; + gbl_chartPanel.weighty = 1.0; + gbl_chartPanel.fill = GridBagConstraints.BOTH; + gbl_chartPanel.gridx = 0; + gbl_chartPanel.gridy = 1; + plotPanel.add(plot, gbl_chartPanel); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // CREATE USAGE TAB + ////////////////////////////////////////////////////////////////////////////////////// + + private void createUsageTab() { + JTextPane usageTextArea = new JTextPane(); + usageTextArea.setMargin(new Insets(10, 10, 10, 10)); + usageTextArea.setContentType("text/html"); + usageTextArea.setText(usage()); + usageTextArea.setEditable(false); + usageTextArea.setCaretPosition(0); + + JScrollPane textScrollPane = new JScrollPane(usageTextArea); + textScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + textScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + add(textScrollPane, "
U
s
a
g
e
"); + } + + private String usage() { + ResourceBundle bundle; + bundle = ResourceBundle.getBundle("com.vgi.mafscaling.logstats"); + return bundle.getString("usage"); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // WORK FUNCTIONS + ////////////////////////////////////////////////////////////////////////////////////// + + private void setAxis() { + new LogStatsFixedAxis(xAxisArray, yAxisArray); + if (xAxisArray.size() > 0) + xAxisRoundTextBox.setValue(null); + if (yAxisArray.size() > 0) + yAxisRoundTextBox.setValue(null); + } + + private void getLogColumns() { + if (JFileChooser.APPROVE_OPTION != fileChooser.showOpenDialog(this)) + return; + xAxisColumn.removeAllItems(); + yAxisColumn.removeAllItems(); + dataColumn.removeAllItems(); + logFile = fileChooser.getSelectedFile(); + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(logFile.getAbsoluteFile())); + String line = br.readLine(); + if (line != null) { + String [] elements = line.split(",", -1); + for (String item : elements) { + xAxisColumn.addItem(item); + yAxisColumn.addItem(item); + dataColumn.addItem(item); + } + } + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error opening file", JOptionPane.ERROR_MESSAGE); + } + finally { + if (br != null) { + try { + br.close(); + } + catch (IOException e) { + logger.error(e); + } + } + } + } + + private Statistics getStatId() { + if (statistics.getSelectedItem() == null) + return Statistics.MEAN; + String name = (String)statistics.getSelectedItem(); + if ("Count".equals(name)) + return Statistics.COUNT; + if ("Minimum".equals(name)) + return Statistics.MINIMUM; + if ("Maximum".equals(name)) + return Statistics.MAXIMUM; + if ("Mean".equals(name)) + return Statistics.MEAN; + if ("Median".equals(name)) + return Statistics.MEDIAN; + if ("Mode".equals(name)) + return Statistics.MODE; + if ("Range".equals(name)) + return Statistics.RANGE; + if ("Variance".equals(name)) + return Statistics.VARIANCE; + if ("Std Deviation".equals(name)) + return Statistics.STDDEV; + return Statistics.MEAN; + } + + private DataFilter getType() { + if (filters.getSelectedItem() == null) + return DataFilter.NONE; + String name = (String)filters.getSelectedItem(); + if ("Less".equals(name)) + return DataFilter.LESS; + if ("Equal".equals(name)) + return DataFilter.EQUAL; + if ("Greater".equals(name)) + return DataFilter.GREATER; + return DataFilter.NONE; + } + + private void processLog() { + Utils.clearTable(dataTable); + if (xAxisColumn.getSelectedItem() == null || yAxisColumn.getSelectedItem() == null || dataColumn.getSelectedItem() == null) + return; + if (xAxisRoundTextBox.getValue() == null && xAxisArray.size() == 0) { + JOptionPane.showMessageDialog(null, "X-Axis scaling is not set. Please set 'Step' or X-Axis values.", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + if (yAxisRoundTextBox.getValue() == null && yAxisArray.size() == 0) { + JOptionPane.showMessageDialog(null, "Y-Axis scaling is not set. Please set 'Step' or Y-Axis values.", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + double xRound = Double.NaN; + if (xAxisRoundTextBox.getValue() != null) { + xRound = (((Number)xAxisRoundTextBox.getValue()).doubleValue()); + if (xRound < 0.01) { + JOptionPane.showMessageDialog(null, "Incorrect X-Axis scaling, minimum allowed is 0.01", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + } + double yRound = Double.NaN; + if (yAxisRoundTextBox.getValue() != null) { + yRound = (((Number)yAxisRoundTextBox.getValue()).doubleValue()); + if (yRound < 0.01) { + JOptionPane.showMessageDialog(null, "Incorrect Y-Axis scaling, minimum allowed is 0.01", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + } + setCursor(new Cursor(Cursor.WAIT_CURSOR)); + Statistics statid = getStatId(); + String xAxisColName = (String)xAxisColumn.getSelectedItem(); + String yAxisColName = (String)yAxisColumn.getSelectedItem(); + String dataColName = (String)dataColumn.getSelectedItem(); + + BufferedReader br = null; + try { + String [] elements; + br = new BufferedReader(new FileReader(logFile.getAbsoluteFile())); + String line = br.readLine(); + if (line != null) { + elements = line.split(",", -1); + ArrayList columns = new ArrayList(Arrays.asList(elements)); + int xColIdx = columns.indexOf(xAxisColName); + int yColIdx = columns.indexOf(yAxisColName); + int vColIdx = columns.indexOf(dataColName); + double x, y, val, roundedVal; + int i = 2; + // data struct where first hash set is X-Axis containing second hash set which is Y-Axis containing array of values + xData = new HashMap>>(); + HashMap> yData; + ArrayList data; + try { + line = br.readLine(); + boolean proceed = true; + while (line != null) { + elements = line.split(",", -1); + try { + proceed = true; + if (Double.isNaN(xRound)) + x = xAxisArray.get(Utils.closestValueIndex(Double.valueOf(elements[xColIdx]), xAxisArray)); + else + x = Utils.round(Double.valueOf(elements[xColIdx]), xRound); + if (Double.isNaN(yRound)) + y = yAxisArray.get(Utils.closestValueIndex(Double.valueOf(elements[yColIdx]), yAxisArray)); + else + y = Utils.round(Double.valueOf(elements[yColIdx]), yRound); + val = Double.valueOf(elements[vColIdx]); + if (dataFilterType != DataFilter.NONE && !Double.isNaN(dataFilter)) { + switch (dataFilterType) { + case LESS: + if (val > dataFilter) + proceed = false; + break; + case EQUAL: + roundedVal = val; + if (dataRounding > 0) + roundedVal = Math.round(val * dataRounding * 10.0) / (dataRounding * 10.0); + else + roundedVal = Math.round(val); + if (roundedVal != dataFilter) + proceed = false; + break; + case GREATER: + if (val < dataFilter) + proceed = false; + break; + default: + proceed = true; + } + } + if (proceed) { + yData = xData.get(x); + if (yData == null) { + yData = new HashMap>(); + xData.put(x, yData); + } + data = yData.get(y); + if (data == null) { + data = new ArrayList(); + yData.put(y, data); + } + data.add(val); + } + } + catch (NumberFormatException e) { + logger.error(e); + JOptionPane.showMessageDialog(null, "Error parsing number at line " + i + ": " + e, "Error processing file", JOptionPane.ERROR_MESSAGE); + return; + } + line = br.readLine(); + i += 1; + } + processData(statid); + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error opening file", JOptionPane.ERROR_MESSAGE); + } + finally { + setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + if (br != null) { + try { + br.close(); + } + catch (IOException e) { + logger.error(e); + } + } + } + } + + public void processData(Statistics id) { + HashMap> yData; + ArrayList data; + try { + TreeMap xAxisMap = new TreeMap(); + TreeMap yAxisMap = new TreeMap(); + for (Map.Entry>> xentry : xData.entrySet()) { + xAxisMap.put(xentry.getKey(), 0); + for (Map.Entry> yentry : xentry.getValue().entrySet()) + yAxisMap.put(yentry.getKey(), 0); + } + Utils.ensureColumnCount(xAxisMap.size() + 1, dataTable); + int i = 0; + for (Map.Entry entry : xAxisMap.entrySet()) { + entry.setValue(++i); + dataTable.setValueAt(entry.getKey(), 0, i); + } + Utils.ensureRowCount(yAxisMap.size() + 1, dataTable); + i = 0; + for (Map.Entry entry : yAxisMap.entrySet()) { + entry.setValue(++i); + dataTable.setValueAt(entry.getKey(), i, 0); + } + double x, y, val; + for (Map.Entry>> xentry : xData.entrySet()) { + x = xentry.getKey(); + yData = xentry.getValue(); + val = 0; + for (Map.Entry> yentry : yData.entrySet()) { + y = yentry.getKey(); + data = yentry.getValue(); + switch (id) { + case COUNT: + val = data.size(); + break; + case MINIMUM: + val = Collections.min(data); + break; + case MAXIMUM: + val = Collections.max(data); + break; + case MEAN: + val = Utils.mean(data); + break; + case MEDIAN: + val = Utils.median(data); + break; + case MODE: + val = Utils.mode(data); + break; + case RANGE: + val = Utils.range(data); + break; + case VARIANCE: + val = Utils.variance(data); + break; + case STDDEV: + val = Utils.standardDeviation(data); + break; + } + dataTable.setValueAt(val, yAxisMap.get(y), xAxisMap.get(x)); + } + } + if (xData.size() > 0) { + // remove extra rows + for (i = dataTable.getRowCount() - 1; i >= 0 && dataTable.getValueAt(i, 0).toString().equals(""); --i) + Utils.removeRow(i, dataTable); + // remove extra columns + for (i = dataTable.getColumnCount() - 1; i >= 0 && dataTable.getValueAt(0, i).toString().equals(""); --i) + Utils.removeColumn(i, dataTable); + Utils.colorTable(dataTable); + } + else { + Utils.clearTable(dataTable); + } + rbGridPlot.setSelected(true); + display3D(Plot3D.GRID); + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE); + } + } + + public double[][] doubleZArray(double[] x, double[] y) { + double[][] z = new double[y.length][x.length]; + for (int i = 0; i < x.length; ++i) { + for (int j = 0; j < y.length; ++j) { + if (!dataTable.getValueAt(j + 1, i + 1).toString().isEmpty()) + z[j][i] = Double.valueOf(dataTable.getValueAt(j + 1, i + 1).toString()); + } + } + return z; + } + + private void addGridPlot() { + if (xData == null || xData.size() == 0) + return; + TreeMap xAxisMap = new TreeMap(); + TreeMap yAxisMap = new TreeMap(); + for (Map.Entry>> xentry : xData.entrySet()) { + xAxisMap.put(xentry.getKey(), 0); + for (Map.Entry> yentry : xentry.getValue().entrySet()) + yAxisMap.put(yentry.getKey(), 0); + } + double[] x = new double[xAxisMap.size()]; + int i = 0; + for (Double key : xAxisMap.keySet()) + x[i++] = key; + double[] y = new double[yAxisMap.size()]; + i = 0; + for (Double key : yAxisMap.keySet()) + y[i++] = key; + double[][] z = doubleZArray(x, y); + plot.addGridPlot(dataColumn.getSelectedItem().toString() + " " + statistics.getSelectedItem().toString(), x, y, z); + } + + private void addBarLineScatterPlot(Plot3D type) { + if (xData == null || xData.size() == 0) + return; + ArrayList xAxisArray = new ArrayList(); + ArrayList yAxisArray = new ArrayList(); + ArrayList zAxisArray = new ArrayList(); + String val; + double X, Y; + for (int i = 1; i < dataTable.getColumnCount(); ++i) { + val = dataTable.getValueAt(0, i).toString(); + if (val.isEmpty()) + break; + X = Double.valueOf(val.toString()); + for (int j = 1; j < dataTable.getRowCount(); ++j) { + val = dataTable.getValueAt(j, 0).toString(); + if (val.isEmpty()) + break; + Y = Double.valueOf(val.toString()); + val = dataTable.getValueAt(j, i).toString(); + if (!val.isEmpty()) { + xAxisArray.add(X); + yAxisArray.add(Y); + zAxisArray.add(Double.valueOf(val)); + } + } + } + double[] x = new double[xAxisArray.size()]; + int i = 0; + for (Double v : xAxisArray) + x[i++] = v; + double[] y = new double[yAxisArray.size()]; + i = 0; + for (Double v : yAxisArray) + y[i++] = v; + double[] z = new double[zAxisArray.size()]; + i = 0; + for (Double v : zAxisArray) + z[i++] = v; + switch (type) { + case BAR: + plot.addBarPlot(dataColumn.getSelectedItem().toString() + " " + statistics.getSelectedItem().toString(), x, y, z); + break; + case LINE: + plot.addLinePlot(dataColumn.getSelectedItem().toString() + " " + statistics.getSelectedItem().toString(), x, y, z); + break; + case SCATTER: + plot.addScatterPlot(dataColumn.getSelectedItem().toString() + " " + statistics.getSelectedItem().toString(), x, y, z); + default: + break; + } + } + + private void display3D(Plot3D type) { + plot.removeAllPlots(); + switch (type) { + case GRID: + addGridPlot(); + break; + case BAR: + addBarLineScatterPlot(type); + break; + case LINE: + addBarLineScatterPlot(type); + break; + case SCATTER: + addBarLineScatterPlot(type); + break; + default: + return; + } + plot.setAxisLabel(0, xAxisColumn.getSelectedItem().toString()); + plot.setAxisLabel(1, yAxisColumn.getSelectedItem().toString()); + plot.setAxisLabel(2, dataColumn.getSelectedItem().toString()); + } + + private void getFilter() { + dataFilterType = getType(); + if (dataFilterType != DataFilter.NONE) { + String temp = ""; + DecimalFormat dblFmt = new DecimalFormat("#.######"); + JTextField filterTextField = new JTextField(""); + if (!Double.isNaN(dataFilter)) + filterTextField.setText(dblFmt.format(dataFilter)); + JComponent[] inputs = new JComponent[] { + new JLabel("Set data filter value (Double)"), + filterTextField + }; + boolean done = false; + do { + if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Data Filter", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) + return; + temp = filterTextField.getText().trim(); + if (Pattern.matches(Utils.fpRegex, temp)) { + dataFilter = Double.valueOf(temp); + done = true; + dataRounding = 0; + if (temp.indexOf('.') != -1) { + temp = temp.substring(temp.indexOf('.')); + dataRounding = temp.length() - 1; + } + } + else { + if (!Double.isNaN(dataFilter)) + dblFmt.format(dataFilter); + else + filterTextField.setText(""); + } + } + while (!done); + } + else + dataFilter = Double.NaN; + } + + @Override + public void actionPerformed(ActionEvent e) { + if ("selectlog".equals(e.getActionCommand())) + getLogColumns(); + else if ("go".equals(e.getActionCommand())) + processLog(); + else if ("setaxis".equals(e.getActionCommand())) + setAxis(); + else if ("grid".equals(e.getActionCommand())) { + if (xData != null) + display3D(Plot3D.GRID); + } + else if ("bar".equals(e.getActionCommand())) { + if (xData != null) + display3D(Plot3D.BAR); + } + else if ("line".equals(e.getActionCommand())) { + if (xData != null) + display3D(Plot3D.LINE); + } + else if ("scatter".equals(e.getActionCommand())) { + if (xData != null) + display3D(Plot3D.SCATTER); + } + else if ("filter".equals(e.getActionCommand())) { + getFilter(); + } + } +} diff --git a/src/com/vgi/mafscaling/LogStatsFixedAxis.java b/src/com/vgi/mafscaling/LogStatsFixedAxis.java new file mode 100644 index 0000000..a47d9ed --- /dev/null +++ b/src/com/vgi/mafscaling/LogStatsFixedAxis.java @@ -0,0 +1,708 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.vgi.mafscaling; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.DecimalFormat; +import java.text.Format; +import java.util.ArrayList; +import java.util.Collections; +import java.util.regex.Pattern; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.LineBorder; +import javax.swing.border.TitledBorder; +import javax.swing.table.DefaultTableModel; + +import org.apache.log4j.Logger; + +public class LogStatsFixedAxis implements ActionListener { + private static final Logger logger = Logger.getLogger(LogStatsFixedAxis.class); + private enum Axis {XAXIS, YAXIS}; + private final static int ColumnWidth = 40; + private final static int AxisColumnCount = 15; + private ExcelAdapter excelAdapter = new ExcelAdapter(); + private JTable xAxisTable = null; + private JTable yAxisTable = null; + private JTable templateTable = null; + private JComboBox xAxisList = null; + private JComboBox yAxisList = null; + private ArrayList xAxisArray; + private ArrayList yAxisArray; + + public LogStatsFixedAxis(ArrayList xAxis, ArrayList yAxis) { + xAxisArray = xAxis; + yAxisArray = yAxis; + initialize(); + } + + private void initialize() { + JPanel dataPanel = new JPanel(); + GridBagLayout gbl_dataPanel = new GridBagLayout(); + gbl_dataPanel.columnWidths = new int[]{0}; + gbl_dataPanel.rowHeights = new int[] {0, 0, 0, 0, 0, 0}; + gbl_dataPanel.columnWeights = new double[]{0.0}; + gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 1.0}; + dataPanel.setLayout(gbl_dataPanel); + dataPanel.setPreferredSize(new Dimension(620, 500)); + + JLabel note1Label = new JLabel("You can manually enter axis values or paste them into tables below"); + note1Label.setForeground(Color.BLUE); + GridBagConstraints gbc_note1Label = new GridBagConstraints(); + gbc_note1Label.anchor = GridBagConstraints.WEST; + gbc_note1Label.fill = GridBagConstraints.HORIZONTAL; + gbc_note1Label.insets = new Insets(3, 3, 3, 3); + gbc_note1Label.gridx = 0; + gbc_note1Label.gridy = 0; + dataPanel.add(note1Label, gbc_note1Label); + + createAxisPanel(dataPanel); + + JLabel note2Label = new JLabel("You can select existing axis if you have saved them before"); + note2Label.setForeground(Color.BLUE); + GridBagConstraints gbc_note2Label = new GridBagConstraints(); + gbc_note2Label.anchor = GridBagConstraints.WEST; + gbc_note2Label.fill = GridBagConstraints.HORIZONTAL; + gbc_note2Label.insets = new Insets(3, 3, 3, 3); + gbc_note2Label.gridx = 0; + gbc_note2Label.gridy = 2; + dataPanel.add(note2Label, gbc_note2Label); + + createControlPanel(dataPanel); + + JLabel note3Label = new JLabel("You can paste a table below and first row and first column will be used as axis"); + note3Label.setForeground(Color.BLUE); + GridBagConstraints gbc_note3Label = new GridBagConstraints(); + gbc_note3Label.anchor = GridBagConstraints.WEST; + gbc_note3Label.fill = GridBagConstraints.HORIZONTAL; + gbc_note3Label.insets = new Insets(3, 3, 3, 3); + gbc_note3Label.gridx = 0; + gbc_note3Label.gridy = 4; + dataPanel.add(note3Label, gbc_note3Label); + + createDataTablePanel(dataPanel); + + setAxisTables(); + + JComponent[] inputs = new JComponent[] { dataPanel }; + do { + if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Fixed size axis", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) + return; + } + while (!validateAxisData()); + } + + private void createAxisPanel(JPanel dataPanel) { + JPanel axisPanel = new JPanel(); + GridBagConstraints gbc_axisPanel = new GridBagConstraints(); + gbc_axisPanel.insets = new Insets(3, 3, 3, 3); + gbc_axisPanel.anchor = GridBagConstraints.NORTHWEST; + gbc_axisPanel.fill = GridBagConstraints.HORIZONTAL; + gbc_axisPanel.weightx = 1.0; + gbc_axisPanel.gridx = 0; + gbc_axisPanel.gridy = 1; + dataPanel.add(axisPanel, gbc_axisPanel); + + GridBagLayout gbl_cntlPanel = new GridBagLayout(); + gbl_cntlPanel.columnWidths = new int[]{0, 0, 0}; + gbl_cntlPanel.rowHeights = new int[]{0, 0, 0}; + gbl_cntlPanel.columnWeights = new double[]{0.0, 0.0, 0.0}; + gbl_cntlPanel.rowWeights = new double[]{0.0, 0.0, 1.0}; + axisPanel.setLayout(gbl_cntlPanel); + + xAxisTable = new JTable(); + xAxisTable.setColumnSelectionAllowed(true); + xAxisTable.setCellSelectionEnabled(true); + xAxisTable.setBorder(new LineBorder(Color.GRAY)); + xAxisTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + xAxisTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + xAxisTable.setModel(new DefaultTableModel(1, AxisColumnCount)); + xAxisTable.setTableHeader(null); + Utils.initializeTable(xAxisTable, ColumnWidth); + excelAdapter.addTable(xAxisTable, false, false, false, false, true, false, true, false, true); + + JScrollPane xAxisScrollPane = new JScrollPane(xAxisTable); + xAxisScrollPane.setViewportBorder(new TitledBorder(null, "X-Axis", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + xAxisScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + xAxisScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + GridBagConstraints gbc_xAxisScrollPane = new GridBagConstraints(); + gbc_xAxisScrollPane.insets = new Insets(0, 0, 0, 0); + gbc_xAxisScrollPane.anchor = GridBagConstraints.NORTH; + gbc_xAxisScrollPane.fill = GridBagConstraints.HORIZONTAL; + gbc_xAxisScrollPane.ipady = 17; + gbc_xAxisScrollPane.weightx = 1.0; + gbc_xAxisScrollPane.gridx = 0; + gbc_xAxisScrollPane.gridy = 0; + axisPanel.add(xAxisScrollPane, gbc_xAxisScrollPane); + + JButton btnClearXAxis = new JButton("Clear"); + GridBagConstraints gbc_btnClearXAxis = new GridBagConstraints(); + gbc_btnClearXAxis.anchor = GridBagConstraints.EAST; + gbc_btnClearXAxis.insets = new Insets(1, 5, 1, 1); + gbc_btnClearXAxis.gridx = 1; + gbc_btnClearXAxis.gridy = 0; + btnClearXAxis.setActionCommand("clearxaxis"); + btnClearXAxis.addActionListener(this); + axisPanel.add(btnClearXAxis, gbc_btnClearXAxis); + + JButton btnSaveXAxis = new JButton("Save"); + GridBagConstraints gbc_btnSaveXAxis = new GridBagConstraints(); + gbc_btnSaveXAxis.anchor = GridBagConstraints.EAST; + gbc_btnSaveXAxis.insets = new Insets(1, 5, 1, 1); + gbc_btnSaveXAxis.gridx = 2; + gbc_btnSaveXAxis.gridy = 0; + btnSaveXAxis.setActionCommand("savexaxis"); + btnSaveXAxis.addActionListener(this); + axisPanel.add(btnSaveXAxis, gbc_btnSaveXAxis); + + yAxisTable = new JTable(); + yAxisTable.setColumnSelectionAllowed(true); + yAxisTable.setCellSelectionEnabled(true); + yAxisTable.setBorder(new LineBorder(Color.GRAY)); + yAxisTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + yAxisTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + yAxisTable.setModel(new DefaultTableModel(1, AxisColumnCount)); + yAxisTable.setTableHeader(null); + Utils.initializeTable(yAxisTable, ColumnWidth); + excelAdapter.addTable(yAxisTable, false, false, false, false, true, false, true, false, true); + + JScrollPane yAxisScrollPane = new JScrollPane(yAxisTable); + yAxisScrollPane.setViewportBorder(new TitledBorder(null, "Y-Axis", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + yAxisScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + yAxisScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + GridBagConstraints gbc_yAxisScrollPane = new GridBagConstraints(); + gbc_yAxisScrollPane.anchor = GridBagConstraints.PAGE_START; + gbc_yAxisScrollPane.weightx = 1.0; + gbc_yAxisScrollPane.insets = new Insets(0, 0, 0, 0); + gbc_yAxisScrollPane.fill = GridBagConstraints.HORIZONTAL; + gbc_yAxisScrollPane.ipady = 17; + gbc_yAxisScrollPane.gridx = 0; + gbc_yAxisScrollPane.gridy = 1; + axisPanel.add(yAxisScrollPane, gbc_yAxisScrollPane); + + JButton btnClearYAxis = new JButton("Clear"); + GridBagConstraints gbc_btnClearYAxis = new GridBagConstraints(); + gbc_btnClearYAxis.anchor = GridBagConstraints.EAST; + gbc_btnClearYAxis.insets = new Insets(1, 5, 1, 1); + gbc_btnClearYAxis.gridx = 1; + gbc_btnClearYAxis.gridy = 1; + btnClearYAxis.setActionCommand("clearyaxis"); + btnClearYAxis.addActionListener(this); + axisPanel.add(btnClearYAxis, gbc_btnClearYAxis); + + JButton btnSaveYAxis = new JButton("Save"); + GridBagConstraints gbc_btnSaveYAxis = new GridBagConstraints(); + gbc_btnSaveYAxis.anchor = GridBagConstraints.EAST; + gbc_btnSaveYAxis.insets = new Insets(1, 5, 1, 1); + gbc_btnSaveYAxis.gridx = 2; + gbc_btnSaveYAxis.gridy = 1; + btnSaveYAxis.setActionCommand("saveyaxis"); + btnSaveYAxis.addActionListener(this); + axisPanel.add(btnSaveYAxis, gbc_btnSaveYAxis); + } + + private void createControlPanel(JPanel dataPanel) { + String templNames; + JPanel cntlPanel = new JPanel(); + GridBagConstraints gbc_ctrlPanel = new GridBagConstraints(); + gbc_ctrlPanel.insets = new Insets(3, 3, 3, 3); + gbc_ctrlPanel.anchor = GridBagConstraints.WEST; + gbc_ctrlPanel.fill = GridBagConstraints.HORIZONTAL; + gbc_ctrlPanel.gridx = 0; + gbc_ctrlPanel.gridy = 3; + dataPanel.add(cntlPanel, gbc_ctrlPanel); + + GridBagLayout gbl_cntlPanel = new GridBagLayout(); + gbl_cntlPanel.columnWidths = new int[]{0, 0, 0, 0, 0, 0}; + gbl_cntlPanel.rowHeights = new int[]{0}; + gbl_cntlPanel.columnWeights = new double[]{0.0, 0.0, 0.0, 1.0, 0.0, 0.0}; + gbl_cntlPanel.rowWeights = new double[]{0.0}; + cntlPanel.setLayout(gbl_cntlPanel); + + JLabel xAxisLabel = new JLabel("Saved X-Axis"); + GridBagConstraints gbc_xAxisLabel = new GridBagConstraints(); + gbc_xAxisLabel.anchor = GridBagConstraints.WEST; + gbc_xAxisLabel.insets = new Insets(3, 0, 3, 0); + gbc_xAxisLabel.gridx = 0; + gbc_xAxisLabel.gridy = 0; + cntlPanel.add(xAxisLabel, gbc_xAxisLabel); + + templNames = Config.getXAxisTemplates(); + if (templNames.isEmpty()) + templNames = ","; + xAxisList = new JComboBox(templNames.split(",")); + GridBagConstraints gbc_xAxisList = new GridBagConstraints(); + gbc_xAxisList.anchor = GridBagConstraints.WEST; + gbc_xAxisList.insets = new Insets(1, 5, 1, 1); + gbc_xAxisList.gridx = 1; + gbc_xAxisList.gridy = 0; + xAxisList.setActionCommand("xaxis"); + xAxisList.addActionListener(this); + xAxisList.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXX"); + cntlPanel.add(xAxisList, gbc_xAxisList); + + JButton btnRemXAxisTempl = new JButton("Remove"); + GridBagConstraints gbc_btnRemXAxisTempl = new GridBagConstraints(); + gbc_btnRemXAxisTempl.anchor = GridBagConstraints.EAST; + gbc_btnRemXAxisTempl.insets = new Insets(1, 5, 1, 1); + gbc_btnRemXAxisTempl.gridx = 2; + gbc_btnRemXAxisTempl.gridy = 0; + btnRemXAxisTempl.setActionCommand("remxtempl"); + btnRemXAxisTempl.addActionListener(this); + cntlPanel.add(btnRemXAxisTempl, gbc_btnRemXAxisTempl); + + JLabel yAxisLabel = new JLabel("Saved Y-Axis"); + GridBagConstraints gbc_yAxisLabel = new GridBagConstraints(); + gbc_yAxisLabel.anchor = GridBagConstraints.EAST; + gbc_yAxisLabel.insets = new Insets(3, 3, 3, 0); + gbc_yAxisLabel.gridx = 3; + gbc_yAxisLabel.gridy = 0; + cntlPanel.add(yAxisLabel, gbc_yAxisLabel); + + templNames = Config.getYAxisTemplates(); + if (templNames.isEmpty()) + templNames = ","; + yAxisList = new JComboBox(templNames.split(",")); + GridBagConstraints gbc_yAxisList = new GridBagConstraints(); + gbc_yAxisList.anchor = GridBagConstraints.WEST; + gbc_yAxisList.insets = new Insets(1, 5, 1, 1); + gbc_yAxisList.gridx = 4; + gbc_yAxisList.gridy = 0; + yAxisList.setActionCommand("yaxis"); + yAxisList.addActionListener(this); + yAxisList.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXX"); + cntlPanel.add(yAxisList, gbc_yAxisList); + + JButton btnRemYAxisTempl = new JButton("Remove"); + GridBagConstraints gbc_btnRemYAxisTempl = new GridBagConstraints(); + gbc_btnRemYAxisTempl.anchor = GridBagConstraints.EAST; + gbc_btnRemYAxisTempl.insets = new Insets(1, 5, 1, 1); + gbc_btnRemYAxisTempl.gridx = 5; + gbc_btnRemYAxisTempl.gridy = 0; + btnRemYAxisTempl.setActionCommand("remytempl"); + btnRemYAxisTempl.addActionListener(this); + cntlPanel.add(btnRemYAxisTempl, gbc_btnRemYAxisTempl); + } + + public void createDataTablePanel(JPanel dataPanel) { + JPanel tblPanel = new JPanel(); + GridBagConstraints gbc_tblPanel = new GridBagConstraints(); + gbc_tblPanel.insets = new Insets(3, 3, 3, 3); + gbc_tblPanel.anchor = GridBagConstraints.NORTHWEST; + gbc_tblPanel.fill = GridBagConstraints.BOTH; + gbc_tblPanel.gridx = 0; + gbc_tblPanel.gridy = 5; + dataPanel.add(tblPanel, gbc_tblPanel); + + GridBagLayout gbl_cntlPanel = new GridBagLayout(); + gbl_cntlPanel.columnWidths = new int[]{0,0}; + gbl_cntlPanel.rowHeights = new int[]{0, 0}; + gbl_cntlPanel.columnWeights = new double[]{1.0,0.0}; + gbl_cntlPanel.rowWeights = new double[]{0.0, 1.0}; + tblPanel.setLayout(gbl_cntlPanel); + + JButton btnClearData = new JButton("Clear"); + GridBagConstraints gbc_btnClearData = new GridBagConstraints(); + gbc_btnClearData.anchor = GridBagConstraints.EAST; + gbc_btnClearData.insets = new Insets(1, 5, 1, 1); + gbc_btnClearData.gridx = 0; + gbc_btnClearData.gridy = 0; + btnClearData.setActionCommand("cleartempl"); + btnClearData.addActionListener(this); + tblPanel.add(btnClearData, gbc_btnClearData); + + JButton btnValidateData = new JButton("Validate"); + GridBagConstraints gbc_btnValidateData = new GridBagConstraints(); + gbc_btnValidateData.anchor = GridBagConstraints.EAST; + gbc_btnValidateData.insets = new Insets(1, 5, 1, 1); + gbc_btnValidateData.gridx = 1; + gbc_btnValidateData.gridy = 0; + btnValidateData.setActionCommand("validate"); + btnValidateData.addActionListener(this); + tblPanel.add(btnValidateData, gbc_btnValidateData); + + templateTable = new JTable(); + templateTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + templateTable.setColumnSelectionAllowed(true); + templateTable.setCellSelectionEnabled(true); + templateTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + templateTable.setTableHeader(null); + templateTable.setModel(new DefaultTableModel(20, 15)); + Utils.initializeTable(templateTable, ColumnWidth); + Format[][] formatMatrix = { { new DecimalFormat("#"), new DecimalFormat("0.00") } }; + NumberFormatRenderer renderer = (NumberFormatRenderer)templateTable.getDefaultRenderer(Object.class); + renderer.setFormats(formatMatrix); + excelAdapter.addTable(templateTable, true, true); + + GridBagConstraints gbc_templTable = new GridBagConstraints(); + gbc_templTable.insets = new Insets(5, 0, 0, 0); + gbc_templTable.fill = GridBagConstraints.BOTH; + gbc_templTable.weightx = 1.0; + gbc_templTable.weighty = 1.0; + gbc_templTable.gridx = 0; + gbc_templTable.gridy = 1; + gbc_templTable.gridwidth = 2; + + JScrollPane scrollPane = new JScrollPane(templateTable); + tblPanel.add(scrollPane, gbc_templTable); + } + + /** + * Methods sets the axis table from axis array if they have been set before + */ + private void setAxisTables() { + try { + if (xAxisArray.size() > 0) { + Utils.ensureColumnCount(xAxisArray.size(), xAxisTable); + Utils.clearTable(xAxisTable); + for (int i = 0; i < xAxisArray.size(); ++i) + xAxisTable.setValueAt(xAxisArray.get(i), 0, i); + for (int i = xAxisTable.getColumnCount() - 1; i >= 0 && xAxisTable.getValueAt(0, i).toString().equals(""); --i) + Utils.removeColumn(i, xAxisTable); + } + if (yAxisArray.size() > 0) { + Utils.ensureColumnCount(yAxisArray.size(), yAxisTable); + Utils.clearTable(yAxisTable); + for (int i = 0; i < yAxisArray.size(); ++i) + yAxisTable.setValueAt(yAxisArray.get(i), 0, i); + for (int i = yAxisTable.getColumnCount() - 1; i >= 0 && yAxisTable.getValueAt(0, i).toString().equals(""); --i) + Utils.removeColumn(i, yAxisTable); + } + } + catch (Exception e) { + logger.error(e); + } + } + + /** + * Method validates that all data is populated and valid. + * @param fuelingTable, table to be checked + * @return + */ + private boolean validateTemplateData() { + try { + // check if table is empty + if (Utils.isTableEmpty(templateTable)) + return true; + // check paste format + if (!templateTable.getValueAt(0, 0).toString().equalsIgnoreCase("[table3d]") && + !((templateTable.getValueAt(0, 0).toString().equals("")) && + Pattern.matches(Utils.fpRegex, templateTable.getValueAt(0, 1).toString()) && + Pattern.matches(Utils.fpRegex, templateTable.getValueAt(1, 0).toString()))) { + JOptionPane.showMessageDialog(null, "Invalid data in table.\n\nPlease post data into first cell", "Error", JOptionPane.ERROR_MESSAGE); + return false; + } + if (templateTable.getValueAt(0, 0).toString().equalsIgnoreCase("[table3d]")) { + // realign if paste is from RomRaider + if (templateTable.getValueAt(0, 1).toString().equals("")) { + Utils.removeRow(0, templateTable); + for (int i = templateTable.getColumnCount() - 2; i >= 0; --i) + templateTable.setValueAt(templateTable.getValueAt(0, i), 0, i + 1); + templateTable.setValueAt("", 0, 0); + } + // paste is probably from excel, just blank out the first cell + else + templateTable.setValueAt("", 0, 0); + } + // remove extra rows + for (int i = templateTable.getRowCount() - 1; i >= 0 && templateTable.getValueAt(i, 0).toString().equals(""); --i) + Utils.removeRow(i, templateTable); + // remove extra columns + for (int i = templateTable.getColumnCount() - 1; i >= 0 && templateTable.getValueAt(0, i).toString().equals(""); --i) + Utils.removeColumn(i, templateTable); + // validate row/column headers cells are numeric + for (int i = 1; i < templateTable.getRowCount(); ++i) { + if (!Pattern.matches(Utils.fpRegex, templateTable.getValueAt(i, 0).toString())) { + JOptionPane.showMessageDialog(null, "Invalid value at row " + (i + 1) + " column 1", "Error", JOptionPane.ERROR_MESSAGE); + return false; + } + } + for (int i = 1; i < templateTable.getColumnCount(); ++i) { + if (!Pattern.matches(Utils.fpRegex, templateTable.getValueAt(0, i).toString())) { + JOptionPane.showMessageDialog(null, "Invalid value at row 1 column " + (i + 1), "Error", JOptionPane.ERROR_MESSAGE); + return false; + } + } + Utils.colorTableHeaders(templateTable); + // copy axis + Utils.clearTable(xAxisTable); + Utils.clearTable(yAxisTable); + Utils.ensureColumnCount(templateTable.getColumnCount() - 1, xAxisTable); + Utils.ensureColumnCount(templateTable.getRowCount() - 1, yAxisTable); + for (int i = 1; i < templateTable.getColumnCount(); ++i) + xAxisTable.setValueAt(templateTable.getValueAt(0, i), 0, i - 1); + for (int i = 1; i < templateTable.getRowCount(); ++i) + yAxisTable.setValueAt(templateTable.getValueAt(i, 0), 0, i - 1); + // remove extra columns + for (int i = xAxisTable.getColumnCount() - 1; i >= 0 && xAxisTable.getValueAt(0, i).toString().equals(""); --i) + Utils.removeColumn(i, xAxisTable); + for (int i = yAxisTable.getColumnCount() - 1; i >= 0 && yAxisTable.getValueAt(0, i).toString().equals(""); --i) + Utils.removeColumn(i, yAxisTable); + return true; + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error validation table data", JOptionPane.ERROR_MESSAGE); + } + return false; + } + + /** + * Methods saves axis table into config and adds a menu item in the template dropdown + * @param type + */ + private void save(Axis type) { + try { + JTable axisTable = xAxisTable; + JComboBox axisList = xAxisList; + if (type == Axis.YAXIS) { + axisTable = yAxisTable; + axisList = yAxisList; + } + if (Utils.isTableEmpty(axisTable)) { + JOptionPane.showMessageDialog(null, "Table is empty or not properly populated", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + String axisName = ""; + JTextField nameTextField = new JTextField(); + JComponent[] inputs = new JComponent[] { new JLabel("Set unique name"), nameTextField }; + do { + if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Axis template name", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) + return; + axisName = nameTextField.getText().trim(); + boolean found = false; + for (int i = 0; i < xAxisList.getItemCount() && !found; ++i) { + if (xAxisList.getItemAt(i).toString().equals(axisName)) + found = true; + } + if (found) { + JOptionPane.showMessageDialog(null, "This name is being used for X-Axis template", "Error", JOptionPane.ERROR_MESSAGE); + axisName = ""; + } + else { + for (int i = 0; i < yAxisList.getItemCount() && !found; ++i) { + if (yAxisList.getItemAt(i).toString().equals(axisName)) + found = true; + } + if (found) { + JOptionPane.showMessageDialog(null, "This name is being used for Y-Axis template", "Error", JOptionPane.ERROR_MESSAGE); + axisName = ""; + } + } + } + while (axisName.isEmpty()); + + String axisValues = ""; + for (int i = 0; i < axisTable.getColumnCount(); ++i) { + if (!Pattern.matches(Utils.fpRegex, axisTable.getValueAt(0, i).toString())) { + JOptionPane.showMessageDialog(null, "Invalid data in table, column " + (i + 1), "Error", JOptionPane.ERROR_MESSAGE); + return; + } + if (i > 0) + axisValues += ","; + axisValues += axisTable.getValueAt(0, i).toString(); + } + if (type == Axis.XAXIS) { + String s = Config.getXAxisTemplates(); + if (s.endsWith(",")) + Config.setXAxisTemplates(s + axisName); + else + Config.setXAxisTemplates(s + "," + axisName); + } + else { + String s = Config.getYAxisTemplates(); + if (s.endsWith(",")) + Config.setYAxisTemplates(s + axisName); + else + Config.setYAxisTemplates(s + "," + axisName); + } + Config.setProperty(axisName, axisValues); + if (axisList.getItemCount() == 0) + axisList.addItem(""); + axisList.addItem(axisName); + axisList.setSelectedItem(axisName); + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error saving axis", JOptionPane.ERROR_MESSAGE); + } + } + + /** + * Method removes current axis template from templates dropdown and config + * @param type + */ + private void remove(Axis type) { + try { + String newNames = ""; + String[] axisNames = Config.getXAxisTemplates().split(","); + JComboBox axisList = xAxisList; + if (type == Axis.YAXIS) { + axisList = yAxisList; + axisNames = Config.getYAxisTemplates().split(","); + } + if (axisList.getSelectedItem() == null) + return; + String axisName = axisList.getSelectedItem().toString(); + if (axisName.isEmpty()) + return; + if (axisList.getItemCount() == 0) + axisList.addItem(""); + axisList.setSelectedItem(""); + axisList.removeItem(axisName); + Config.removeProperty(axisName); + for (String s : axisNames) { + if (s.isEmpty() || s.equals(axisName)) + continue; + newNames += ("," + s); + } + if (newNames.isEmpty()) + newNames = ","; + if (type == Axis.XAXIS) + Config.setXAxisTemplates(newNames); + else + Config.setYAxisTemplates(newNames); + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error removing axis template", JOptionPane.ERROR_MESSAGE); + } + } + + /** + * Method loads from config selected axis template into axis table + * @param type + */ + private void load(Axis type) { + try { + JTable axisTable = xAxisTable; + JComboBox axisList = xAxisList; + if (type == Axis.YAXIS) { + axisTable = yAxisTable; + axisList = yAxisList; + } + if (axisList.getSelectedItem() == null) + return; + String axisName = axisList.getSelectedItem().toString(); + if (axisName.isEmpty()) + return; + String[] values = Config.getProperty(axisName).split(","); + Utils.ensureColumnCount(values.length, axisTable); + Utils.clearTable(axisTable); + for (int i = 0; i < values.length; ++i) + axisTable.setValueAt(values[i], 0, i); + for (int i = axisTable.getColumnCount() - 1; i >= 0 && axisTable.getValueAt(0, i).toString().equals(""); --i) + Utils.removeColumn(i, axisTable); + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error load axis template", JOptionPane.ERROR_MESSAGE); + } + } + + /** + * Methods validates axis table and makes sure at least one axes is specified + * @return + */ + private boolean validateAxisData() { + try { + if (Utils.isTableEmpty(xAxisTable) && Utils.isTableEmpty(yAxisTable)) { + JOptionPane.showMessageDialog(null, "Both axis tables are either empty or not properly populated", "Error", JOptionPane.ERROR_MESSAGE); + return false; + } + ArrayList tempArray = new ArrayList(); + if (!Utils.isTableEmpty(xAxisTable)) { + for (int i = 0; i < xAxisTable.getColumnCount(); ++i) { + if (!Pattern.matches(Utils.fpRegex, xAxisTable.getValueAt(0, i).toString())) { + JOptionPane.showMessageDialog(null, "Invalid data in X-Axis table, column " + (i + 1), "Error", JOptionPane.ERROR_MESSAGE); + return false; + } + tempArray.add(Double.valueOf(xAxisTable.getValueAt(0, i).toString())); + } + xAxisArray.clear(); + for (Double d : tempArray) + xAxisArray.add(d); + Collections.sort(xAxisArray); + } + tempArray.clear(); + if (!Utils.isTableEmpty(yAxisTable)) { + for (int i = 0; i < yAxisTable.getColumnCount(); ++i) { + if (!Pattern.matches(Utils.fpRegex, yAxisTable.getValueAt(0, i).toString())) { + JOptionPane.showMessageDialog(null, "Invalid data in Y-Axis table, column " + (i + 1), "Error", JOptionPane.ERROR_MESSAGE); + return false; + } + tempArray.add(Double.valueOf(yAxisTable.getValueAt(0, i).toString())); + } + yAxisArray.clear(); + for (Double d : tempArray) + yAxisArray.add(d); + Collections.sort(yAxisArray); + } + return true; + } + catch (Exception e) { + logger.error(e); + JOptionPane.showMessageDialog(null, e, "Error setting axis", JOptionPane.ERROR_MESSAGE); + } + return false; + } + + @Override + public void actionPerformed(ActionEvent e) { + if ("clearxaxis".equals(e.getActionCommand())) { + Utils.clearTable(xAxisTable); + xAxisList.setSelectedIndex(-1); + } + else if ("clearyaxis".equals(e.getActionCommand())) { + Utils.clearTable(yAxisTable); + yAxisList.setSelectedIndex(-1); + } + else if ("cleartempl".equals(e.getActionCommand())) + Utils.clearTable(templateTable); + else if ("validate".equals(e.getActionCommand())) + validateTemplateData(); + else if ("savexaxis".equals(e.getActionCommand())) + save(Axis.XAXIS); + else if ("saveyaxis".equals(e.getActionCommand())) + save(Axis.YAXIS); + else if ("remxtempl".equals(e.getActionCommand())) + remove(Axis.XAXIS); + else if ("remytempl".equals(e.getActionCommand())) + remove(Axis.YAXIS); + else if ("xaxis".equals(e.getActionCommand())) + load(Axis.XAXIS); + else if ("yaxis".equals(e.getActionCommand())) + load(Axis.YAXIS); + } +} diff --git a/src/com/vgi/mafscaling/MafChartPanel.java b/src/com/vgi/mafscaling/MafChartPanel.java index 36fa542..97a50a7 100644 --- a/src/com/vgi/mafscaling/MafChartPanel.java +++ b/src/com/vgi/mafscaling/MafChartPanel.java @@ -1,3 +1,21 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + package com.vgi.mafscaling; import java.awt.Cursor; @@ -9,6 +27,7 @@ import java.awt.event.MouseWheelListener; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.util.HashSet; import org.apache.log4j.Logger; import org.jfree.chart.ChartPanel; @@ -21,14 +40,16 @@ public class MafChartPanel implements MouseListener, MouseMotionListener, MouseWheelListener { private static final Logger logger = Logger.getLogger(MafChartPanel.class); - ChartPanel chartPanel = null; - IMafChartHolder chartHolder = null; + private ChartPanel chartPanel = null; + private IMafChartHolder chartHolder = null; private XYItemEntity xyItemEntity = null; + private HashSet pointDraggableSet = null; private boolean AllowPointMove = true; private boolean IsMovable = false; private double initialMovePointY = 0; public MafChartPanel(JFreeChart chart, IMafChartHolder holder) { + pointDraggableSet = new HashSet(); chartPanel = new ChartPanel(chart, true, true, true, true, true); chartHolder = holder; chartPanel.addMouseMotionListener(this); @@ -38,6 +59,10 @@ public MafChartPanel(JFreeChart chart, IMafChartHolder holder) { chartPanel.setMouseZoomable(false); } + public void enablePointsDrag(int seriesIndex) { + pointDraggableSet.add(seriesIndex); + } + public ChartPanel getChartPanel() { return chartPanel; } @@ -47,7 +72,7 @@ public void movePoint(MouseEvent event) { if (IsMovable) { int itemIndex = xyItemEntity.getItem(); int seriesIndex = xyItemEntity.getSeriesIndex(); - if (seriesIndex != 0) + if (!pointDraggableSet.contains(seriesIndex)) return; XYSeries series = ((XYSeriesCollection)xyItemEntity.getDataset()).getSeries(seriesIndex); XYPlot plot = chartPanel.getChart().getXYPlot(); @@ -59,7 +84,7 @@ public void movePoint(MouseEvent event) { series.getY(itemIndex).doubleValue() + difference < 0.0) initialMovePointY = finalMovePointY; series.updateByIndex(itemIndex, series.getY(itemIndex).doubleValue() + difference); - chartHolder.onMovePoint(itemIndex, series.getY(itemIndex).doubleValue()); + chartHolder.onMovePoint(itemIndex, series.getX(itemIndex).doubleValue(), series.getY(itemIndex).doubleValue()); chartPanel.getChart().fireChartChanged(); chartPanel.updateUI(); initialMovePointY = finalMovePointY; diff --git a/src/com/vgi/mafscaling/MafCompare.java b/src/com/vgi/mafscaling/MafCompare.java new file mode 100644 index 0000000..56890ee --- /dev/null +++ b/src/com/vgi/mafscaling/MafCompare.java @@ -0,0 +1,384 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.vgi.mafscaling; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.regex.Pattern; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.LineBorder; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableModel; +import org.apache.log4j.Logger; + +public class MafCompare extends JFrame { + private static final long serialVersionUID = 3380903505904186441L; + private static final Logger logger = Logger.getLogger(MafCompare.class); + private static final String Title = "MAF Compare / Modify"; + private static final int WindowHeight = 145; + private static final int RowHeight = 17; + private static final int ColumnWidth = 50; + private static final int MafTableColumnCount = 50; + private ExcelAdapter excelAdapter = new ExcelAdapter(); + private ExcelAdapter compExcelAdapter = null; + private TableCellListener compMafCellListener = null; + private JTable origMafTable = null; + private JTable newMafTable = null; + private JTable compMafTable = null; + + public MafCompare() { + compExcelAdapter = new ExcelAdapter() { + //@override + protected void onPaste(JTable table, boolean extendRows, boolean extendCols) { + super.onPaste(table, extendRows, extendCols); + int colCount = compMafTable.getColumnCount(); + Utils.ensureColumnCount(colCount, origMafTable); + Utils.ensureColumnCount(colCount, newMafTable); + ArrayList values = new ArrayList(); + int i; + for (i = (table.getSelectedColumns())[0]; i < colCount; ++i) + values.add(compMafTable.getValueAt(0, i)); + double corr; + int j = 0; + for (i = (table.getSelectedColumns())[0]; i < colCount; ++i, ++j) { + if (Pattern.matches(Utils.fpRegex, values.get(j).toString()) && + Pattern.matches(Utils.fpRegex, origMafTable.getValueAt(1, i).toString())) { + corr = Double.valueOf(values.get(j).toString()) / 100.0 + 1.0; + newMafTable.setValueAt(Double.valueOf(origMafTable.getValueAt(1, i).toString()) * corr, 1, i); + } + else + break; + } + } + }; + initialize(); + } + + /** + * Initialize the contents of the frame. + */ + private void initialize() { + try { + ImageIcon tableImage = new ImageIcon(getClass().getResource("/table.jpg")); + setTitle(Title); + setIconImage(tableImage.getImage()); + setBounds(100, 100, 621, 372); + setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + setSize(Config.getCompWindowSize()); + setLocation(Config.getCompWindowLocation()); + setMaximumSize(new Dimension(1600, WindowHeight)); + setMinimumSize(new Dimension(300, WindowHeight)); + setLocationRelativeTo(null); + setVisible(false); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + Utils.clearTable(origMafTable); + Utils.clearTable(newMafTable); + Utils.clearTable(compMafTable); + Config.setCompWindowSize(getSize()); + Config.setCompWindowLocation(getLocation()); + } + }); + + JPanel dataPanel = new JPanel(); + GridBagLayout gbl_dataPanel = new GridBagLayout(); + gbl_dataPanel.columnWidths = new int[] {0, 0, 0}; + gbl_dataPanel.rowHeights = new int[] {RowHeight, RowHeight, RowHeight, RowHeight, RowHeight}; + gbl_dataPanel.columnWeights = new double[]{0.0, 0.0, 0.0}; + gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0}; + dataPanel.setLayout(gbl_dataPanel); + getContentPane().add(dataPanel); + + JLabel origLabel = new JLabel("Original"); + GridBagConstraints gbc_origLabel = new GridBagConstraints(); + gbc_origLabel.anchor = GridBagConstraints.PAGE_START; + gbc_origLabel.insets = new Insets(1, 1, 1, 5); + gbc_origLabel.weightx = 0; + gbc_origLabel.weighty = 0; + gbc_origLabel.gridx = 0; + gbc_origLabel.gridy = 0; + gbc_origLabel.gridheight = 2; + dataPanel.add(origLabel, gbc_origLabel); + + JLabel newLabel = new JLabel("New"); + GridBagConstraints gbc_newLabel = new GridBagConstraints(); + gbc_newLabel.anchor = GridBagConstraints.PAGE_START; + gbc_newLabel.insets = new Insets(1, 1, 1, 5); + gbc_newLabel.weightx = 0; + gbc_newLabel.weighty = 0; + gbc_newLabel.gridx = 0; + gbc_newLabel.gridy = 2; + gbc_newLabel.gridheight = 2; + dataPanel.add(newLabel, gbc_newLabel); + + JLabel compLabel = new JLabel("Change"); + GridBagConstraints gbc_compLabel = new GridBagConstraints(); + gbc_compLabel.anchor = GridBagConstraints.PAGE_START; + gbc_compLabel.insets = new Insets(1, 1, 1, 5); + gbc_compLabel.weightx = 0; + gbc_compLabel.weighty = 0; + gbc_compLabel.gridx = 0; + gbc_compLabel.gridy = 4; + dataPanel.add(compLabel, gbc_compLabel); + + JLabel origVoltLabel = new JLabel("volt"); + GridBagConstraints gbc_origVoltLabel = new GridBagConstraints(); + gbc_origVoltLabel.anchor = GridBagConstraints.PAGE_START; + gbc_origVoltLabel.insets = new Insets(1, 1, 1, 5); + gbc_origVoltLabel.weightx = 0; + gbc_origVoltLabel.weighty = 0; + gbc_origVoltLabel.gridx = 1; + gbc_origVoltLabel.gridy = 0; + dataPanel.add(origVoltLabel, gbc_origVoltLabel); + + JLabel origGsLabel = new JLabel(" g/s"); + GridBagConstraints gbc_origGsLabel = new GridBagConstraints(); + gbc_origGsLabel.anchor = GridBagConstraints.PAGE_START; + gbc_origGsLabel.insets = new Insets(1, 1, 1, 5); + gbc_origGsLabel.weightx = 0; + gbc_origGsLabel.weighty = 0; + gbc_origGsLabel.gridx = 1; + gbc_origGsLabel.gridy = 1; + dataPanel.add(origGsLabel, gbc_origGsLabel); + + JLabel newVoltLabel = new JLabel("volt"); + GridBagConstraints gbc_newVoltLabel = new GridBagConstraints(); + gbc_newVoltLabel.anchor = GridBagConstraints.PAGE_START; + gbc_newVoltLabel.insets = new Insets(1, 1, 1, 5); + gbc_newVoltLabel.weightx = 0; + gbc_newVoltLabel.weighty = 0; + gbc_newVoltLabel.gridx = 1; + gbc_newVoltLabel.gridy = 2; + dataPanel.add(newVoltLabel, gbc_newVoltLabel); + + JLabel newGsLabel = new JLabel(" g/s"); + GridBagConstraints gbc_newGsLabel = new GridBagConstraints(); + gbc_newGsLabel.anchor = GridBagConstraints.PAGE_START; + gbc_newGsLabel.insets = new Insets(1, 1, 1, 5); + gbc_newGsLabel.weightx = 0; + gbc_newGsLabel.weighty = 0; + gbc_newGsLabel.gridx = 1; + gbc_newGsLabel.gridy = 3; + dataPanel.add(newGsLabel, gbc_newGsLabel); + + JLabel compPctLabel = new JLabel(" % "); + GridBagConstraints gbc_compPctLabel = new GridBagConstraints(); + gbc_compPctLabel.anchor = GridBagConstraints.PAGE_START; + gbc_compPctLabel.insets = new Insets(1, 1, 1, 5); + gbc_compPctLabel.weightx = 0; + gbc_compPctLabel.weighty = 0; + gbc_compPctLabel.gridx = 1; + gbc_compPctLabel.gridy = 4; + dataPanel.add(compPctLabel, gbc_compPctLabel); + + JPanel tablesPanel = new JPanel(); + GridBagLayout gbl_tablesPanel = new GridBagLayout(); + gbl_tablesPanel.columnWidths = new int[]{0}; + gbl_tablesPanel.rowHeights = new int[] {0, 0, 0}; + gbl_tablesPanel.columnWeights = new double[]{0.0}; + gbl_tablesPanel.rowWeights = new double[]{0.0, 0.0, 1.0}; + tablesPanel.setLayout(gbl_tablesPanel); + + JScrollPane mafScrollPane = new JScrollPane(tablesPanel); + mafScrollPane.setMinimumSize(new Dimension(1600, 105)); + mafScrollPane.getHorizontalScrollBar().setMaximumSize(new Dimension(20, 20)); + mafScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + mafScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + GridBagConstraints gbc_mafScrollPane = new GridBagConstraints(); + gbc_mafScrollPane.weightx = 1.0; + gbc_mafScrollPane.weighty = 1.0; + gbc_mafScrollPane.anchor = GridBagConstraints.PAGE_START; + gbc_mafScrollPane.fill = GridBagConstraints.HORIZONTAL; + gbc_mafScrollPane.gridx = 2; + gbc_mafScrollPane.gridy = 0; + gbc_mafScrollPane.gridheight = 5; + dataPanel.add(mafScrollPane, gbc_mafScrollPane); + + origMafTable = new JTable(); + origMafTable.setColumnSelectionAllowed(true); + origMafTable.setCellSelectionEnabled(true); + origMafTable.setBorder(new LineBorder(new Color(0, 0, 0))); + origMafTable.setRowHeight(RowHeight); + origMafTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + origMafTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + origMafTable.setModel(new DefaultTableModel(2, MafTableColumnCount)); + origMafTable.setTableHeader(null); + Utils.initializeTable(origMafTable, ColumnWidth); + GridBagConstraints gbc_origMafTable = new GridBagConstraints(); + gbc_origMafTable.anchor = GridBagConstraints.PAGE_START; + gbc_origMafTable.insets = new Insets(0, 0, 0, 0); + gbc_origMafTable.fill = GridBagConstraints.BOTH; + gbc_origMafTable.weightx = 1.0; + gbc_origMafTable.weighty = 0; + gbc_origMafTable.gridx = 0; + gbc_origMafTable.gridy = 0; + tablesPanel.add(origMafTable, gbc_origMafTable); + excelAdapter.addTable(origMafTable, false, false, false, false, true, false, true, false, true); + + newMafTable = new JTable(); + newMafTable.setColumnSelectionAllowed(true); + newMafTable.setCellSelectionEnabled(true); + newMafTable.setBorder(new LineBorder(new Color(0, 0, 0))); + newMafTable.setRowHeight(RowHeight); + newMafTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + newMafTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + newMafTable.setModel(new DefaultTableModel(2, MafTableColumnCount)); + newMafTable.setTableHeader(null); + Utils.initializeTable(newMafTable, ColumnWidth); + GridBagConstraints gbc_newMafTable = new GridBagConstraints(); + gbc_newMafTable.anchor = GridBagConstraints.PAGE_START; + gbc_newMafTable.insets = new Insets(0, 0, 0, 0); + gbc_newMafTable.fill = GridBagConstraints.BOTH; + gbc_newMafTable.weightx = 1.0; + gbc_newMafTable.weighty = 0; + gbc_newMafTable.gridx = 0; + gbc_newMafTable.gridy = 1; + tablesPanel.add(newMafTable, gbc_newMafTable); + excelAdapter.addTable(newMafTable, false, false, false, false, false, false, false, false, true); + + compMafTable = new JTable(); + compMafTable.setColumnSelectionAllowed(true); + compMafTable.setCellSelectionEnabled(true); + compMafTable.setBorder(new LineBorder(new Color(0, 0, 0))); + compMafTable.setRowHeight(RowHeight); + compMafTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + compMafTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + compMafTable.setModel(new DefaultTableModel(1, MafTableColumnCount)); + compMafTable.setTableHeader(null); + Utils.initializeTable(compMafTable, ColumnWidth); + NumberFormatRenderer numericRenderer = new NumberFormatRenderer(); + numericRenderer.setFormatter(new DecimalFormat("0.000")); + compMafTable.setDefaultRenderer(Object.class, numericRenderer); + GridBagConstraints gbc_compMafTable = new GridBagConstraints(); + gbc_compMafTable.anchor = GridBagConstraints.PAGE_START; + gbc_compMafTable.insets = new Insets(0, 0, 0, 0); + gbc_compMafTable.fill = GridBagConstraints.BOTH; + gbc_compMafTable.weightx = 1.0; + gbc_compMafTable.weighty = 0; + gbc_compMafTable.gridx = 0; + gbc_compMafTable.gridy = 2; + tablesPanel.add(compMafTable, gbc_compMafTable); + compExcelAdapter.addTable(compMafTable, false, true, false, false, false, true, true, false, true); + + TableModelListener origTableListener = new TableModelListener() { + public void tableChanged(TableModelEvent tme) { + if (tme.getType() == TableModelEvent.UPDATE) { + int colCount = origMafTable.getColumnCount(); + Utils.ensureColumnCount(colCount, newMafTable); + Utils.ensureColumnCount(colCount, compMafTable); + for (int i = 0; i < colCount; ++i) { + if (Pattern.matches(Utils.fpRegex, origMafTable.getValueAt(1, i).toString()) && + Pattern.matches(Utils.fpRegex, newMafTable.getValueAt(1, i).toString())) { + compMafTable.setValueAt(((Double.valueOf(newMafTable.getValueAt(1, i).toString()) / Double.valueOf(origMafTable.getValueAt(1, i).toString())) - 1.0) * 100.0, 0, i); + } + else + break; + } + } + } + }; + + TableModelListener newTableListener = new TableModelListener() { + public void tableChanged(TableModelEvent tme) { + if (tme.getType() == TableModelEvent.UPDATE) { + int colCount = newMafTable.getColumnCount(); + Utils.ensureColumnCount(colCount, origMafTable); + Utils.ensureColumnCount(colCount, compMafTable); + for (int i = 0; i < colCount; ++i) { + if (Pattern.matches(Utils.fpRegex, newMafTable.getValueAt(1, i).toString()) && + Pattern.matches(Utils.fpRegex, origMafTable.getValueAt(1, i).toString())) { + compMafTable.setValueAt(((Double.valueOf(newMafTable.getValueAt(1, i).toString()) / Double.valueOf(origMafTable.getValueAt(1, i).toString())) - 1.0) * 100.0, 0, i); + } + else + break; + } + } + } + }; + + origMafTable.getModel().addTableModelListener(origTableListener); + newMafTable.getModel().addTableModelListener(newTableListener); + + Action action = new AbstractAction() { + private static final long serialVersionUID = 8148393537657380215L; + public void actionPerformed(ActionEvent e) { + TableCellListener tcl = (TableCellListener)e.getSource(); + if (Pattern.matches(Utils.fpRegex, compMafTable.getValueAt(0, tcl.getColumn()).toString())) { + if (Pattern.matches(Utils.fpRegex, origMafTable.getValueAt(1, tcl.getColumn()).toString())) { + double corr = Double.valueOf(compMafTable.getValueAt(0, tcl.getColumn()).toString()) / 100.0 + 1.0; + newMafTable.setValueAt(Double.valueOf(origMafTable.getValueAt(1, tcl.getColumn()).toString()) * corr, 1, tcl.getColumn()); + } + } + else + compMafTable.setValueAt(tcl.getOldValue(), 0, tcl.getColumn()); + } + }; + + setCompMafCellListener(new TableCellListener(compMafTable, action)); + } + catch (Exception e) { + logger.error(e); + } + } + + @Override + public void paint(Graphics g) { + Dimension d = getSize(); + Dimension m = getMaximumSize(); + boolean resize = d.width > m.width || d.height > m.height; + d.width = Math.min(m.width, d.width); + d.height = Math.min(m.height, d.height); + if (resize) { + Point p = getLocation(); + setVisible(false); + setSize(d); + setLocation(p); + setVisible(true); + } + super.paint(g); + } + + public TableCellListener getCompMafCellListener() { + return compMafCellListener; + } + + public void setCompMafCellListener(TableCellListener compMafCellListener) { + this.compMafCellListener = compMafCellListener; + } +} diff --git a/src/com/vgi/mafscaling/MafScaling.java b/src/com/vgi/mafscaling/MafScaling.java index 6005e9b..bbf12c5 100644 --- a/src/com/vgi/mafscaling/MafScaling.java +++ b/src/com/vgi/mafscaling/MafScaling.java @@ -1,3 +1,21 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + package com.vgi.mafscaling; import java.awt.BorderLayout; @@ -5,6 +23,7 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JTabbedPane; import javax.swing.UIManager; @@ -12,9 +31,10 @@ public class MafScaling { private static final Logger logger = Logger.getLogger(MafScaling.class); - private static final String Title = "MAF Scaling - v1.2.0"; + private static final String Title = "MAF Scaling - v1.3.0"; private static final String OLTabName = "Open Loop"; private static final String CLTabName = "Closed Loop"; + private static final String RTabName = "Rescale"; private JFrame frame; /** @@ -55,6 +75,9 @@ public MafScaling() { */ private void initialize() { PrimaryOpenLoopFuelingTable pofFuelingTable = new PrimaryOpenLoopFuelingTable(); + MafCompare mafCompare = new MafCompare(); + + ImageIcon chartImage = new ImageIcon(getClass().getResource("/chart.jpg")); frame = new JFrame(); frame.setTitle(Title); @@ -62,6 +85,7 @@ private void initialize() { frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(Config.getWindowSize()); frame.setLocation(Config.getWindowLocation()); + frame.setIconImage(chartImage.getImage()); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { Config.setWindowSize(frame.getSize()); @@ -74,12 +98,21 @@ public void windowClosing(WindowEvent e) { tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); frame.getContentPane().add(tabbedPane, BorderLayout.CENTER); - JTabbedPane ol = new OpenLoop(JTabbedPane.LEFT, pofFuelingTable); + JTabbedPane ol = new OpenLoop(JTabbedPane.LEFT, pofFuelingTable, mafCompare); ol.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); tabbedPane.add(ol, OLTabName); - JTabbedPane cl = new ClosedLoop(JTabbedPane.LEFT, pofFuelingTable); + JTabbedPane cl = new ClosedLoop(JTabbedPane.LEFT, pofFuelingTable, mafCompare); cl.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); tabbedPane.add(cl, CLTabName); + + JTabbedPane r = new Rescale(JTabbedPane.LEFT); + r.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + tabbedPane.add(r, RTabName); + + JTabbedPane l = new LogStats(JTabbedPane.LEFT); + l.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + tabbedPane.add(l, "Log Stats"); + } } diff --git a/src/com/vgi/mafscaling/OpenLoop.java b/src/com/vgi/mafscaling/OpenLoop.java index b90562d..4ed2458 100644 --- a/src/com/vgi/mafscaling/OpenLoop.java +++ b/src/com/vgi/mafscaling/OpenLoop.java @@ -48,20 +48,17 @@ import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; -import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextPane; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; -import javax.swing.SpinnerNumberModel; import javax.swing.border.LineBorder; import javax.swing.border.TitledBorder; import javax.swing.table.DefaultTableModel; @@ -88,27 +85,27 @@ public class OpenLoop extends JTabbedPane implements ActionListener, IMafChartHolder { private static final long serialVersionUID = 2988105467764335997L; private static final Logger logger = Logger.getLogger(OpenLoop.class); - private final static String SaveDataFileHeader = "[open_loop run data]"; - private final static String MafTableName = "Current MAF Scaling"; - private final static String RunTableName = "Run "; - private final static String XAxisName = "MAF Sensor (Voltage)"; - private final static String Y1AxisName = "Mass Airflow (g/s)"; - private final static String Y2AxisName = "AFR Error (%)"; - private final static String rpmAxisName = "RPM"; + private static final String SaveDataFileHeader = "[open_loop run data]"; + private static final String MafTableName = "Current MAF Scaling"; + private static final String RunTableName = "Run "; + private static final String XAxisName = "MAF Sensor (Voltage)"; + private static final String Y1AxisName = "Mass Airflow (g/s)"; + private static final String Y2AxisName = "AFR Error (%)"; + private static final String rpmAxisName = "RPM"; private static final String runDataName = "AFR Error"; private static final String currentDataName = "Current"; private static final String correctedDataName = "Corrected"; private static final String smoothedDataName = "Smoothed"; - private static final String mafCurveDataName = "Maf Curve"; + private static final String mafCurveDataName = "Smoothed Maf Curve"; private static final String currentSlopeDataName = "Current Maf Slope"; private static final String smoothedSlopeDataName = "Smoothed Maf Slope"; - private final static int ColumnWidth = 50; - private final static int RunCount = 12; - private int MafTableColumnCount = 50; - private int RunRowsCount = 200; - private int wotPoint = Config.getWotStationaryPointValue(); + private static final int ColumnWidth = 50; + private static final int RunCount = 12; + private static final int MafTableColumnCount = 50; + private static final int RunRowsCount = 200; private double minMafV = Config.getMafVMinimumValue(); - private Double afrErrPrct = Config.getWidebandAfrErrorPercentValue(); + private double afrErrPrct = Config.getWidebandAfrErrorPercentValue(); + private int wotPoint = Config.getWotStationaryPointValue(); private int logThtlAngleColIdx = -1; private int logAfLearningColIdx = -1; private int logAfCorrectionColIdx = -1; @@ -128,6 +125,7 @@ public class OpenLoop extends JTabbedPane implements ActionListener, IMafChartHo private JCheckBox checkBoxSmoothedMaf = null; private JCheckBox checkBoxSmoothing = null; private JComboBox smoothComboBox = null; + private JButton btnCompareButton = null; private JButton btnSmoothButton = null; private JButton btnResetSmoothButton = null; private JButton btnPlusButton = null; @@ -151,10 +149,12 @@ public class OpenLoop extends JTabbedPane implements ActionListener, IMafChartHo private final XYSeries corrMafData = new XYSeries(correctedDataName); private final XYSeries smoothMafData = new XYSeries(smoothedDataName); private PrimaryOpenLoopFuelingTable polfTable = null; + private MafCompare mafCompare = null; - public OpenLoop(int tabPlacement, PrimaryOpenLoopFuelingTable table) { + public OpenLoop(int tabPlacement, PrimaryOpenLoopFuelingTable table, MafCompare comparer) { super(tabPlacement); polfTable = table; + mafCompare = comparer; initialize(); } @@ -177,7 +177,7 @@ private void createDataTab() { gbl_dataPanel.columnWidths = new int[] {0}; gbl_dataPanel.rowHeights = new int[] {0, 0, 0, 0}; gbl_dataPanel.columnWeights = new double[]{1.0}; - gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 0,0, 1.0}; + gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 0.0, 1.0}; dataPanel.setLayout(gbl_dataPanel); JPanel cntlPanel = new JPanel(); @@ -476,14 +476,16 @@ private void createGraghTab() { checkBoxSmoothedMaf.addActionListener(this); cntlPanel.add(checkBoxSmoothedMaf, gbc_checkBoxSmoothedMaf); - Component horizontalGlue = Box.createHorizontalGlue(); - GridBagConstraints gbc_horizontalGlue = new GridBagConstraints(); - gbc_horizontalGlue.weightx = 1.0; - gbc_horizontalGlue.fill = GridBagConstraints.HORIZONTAL; - gbc_horizontalGlue.insets = new Insets(0, 0, 3, 3); - gbc_horizontalGlue.gridx = 5; - gbc_horizontalGlue.gridy = 0; - cntlPanel.add(horizontalGlue, gbc_horizontalGlue); + btnCompareButton = new JButton("Compare"); + GridBagConstraints gbc_btnCompareButton = new GridBagConstraints(); + gbc_btnCompareButton.anchor = GridBagConstraints.CENTER; + gbc_btnCompareButton.insets = new Insets(0, 0, 3, 3); + gbc_btnCompareButton.weightx = 1.0; + gbc_btnCompareButton.gridx = 5; + gbc_btnCompareButton.gridy = 0; + btnCompareButton.setActionCommand("compare"); + btnCompareButton.addActionListener(this); + cntlPanel.add(btnCompareButton, gbc_btnCompareButton); checkBoxSmoothing = new JCheckBox("Smoothing:"); GridBagConstraints gbc_checkBoxSmoothing = new GridBagConstraints(); @@ -528,6 +530,7 @@ private void createGraghTab() { cntlPanel.add(btnResetSmoothButton, gbc_btnResetSmoothButton); JFreeChart chart = ChartFactory.createScatterPlot(null, null, null, null, PlotOrientation.VERTICAL, false, true, false); + chart.setBorderVisible(true); mafChartPanel = new MafChartPanel(chart, this); GridBagConstraints gbl_chartPanel = new GridBagConstraints(); @@ -561,6 +564,7 @@ private void createGraghTab() { lineRenderer.setSeriesShape(0, ShapeUtilities.createDiamond((float) 2.5)); lineRenderer.setSeriesShape(1, ShapeUtilities.createUpTriangle((float) 2.5)); lineRenderer.setSeriesShape(2, ShapeUtilities.createDownTriangle((float) 2.5)); + mafChartPanel.enablePointsDrag(0); lineRenderer.setLegendItemLabelGenerator( new StandardXYSeriesLabelGenerator() { @@ -721,9 +725,11 @@ public String generateLabel(XYDataset dataset, int series) { private void createUsageTab() { JTextPane usageTextArea = new JTextPane(); + usageTextArea.setMargin(new Insets(10, 10, 10, 10)); usageTextArea.setContentType("text/html"); usageTextArea.setText(usage()); usageTextArea.setEditable(false); + usageTextArea.setCaretPosition(0); JScrollPane textScrollPane = new JScrollPane(usageTextArea); textScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); @@ -757,6 +763,9 @@ else if ("save".equals(e.getActionCommand())) { else if ("go".equals(e.getActionCommand())) { calculateMafScaling(); } + else if ("compare".equals(e.getActionCommand())) { + mafCompare.setVisible(true); + } else if ("smooth".equals(e.getActionCommand())) { smoothCurve(); } @@ -948,7 +957,7 @@ private void calculateMafScaling() { catch (Exception e) { e.printStackTrace(); logger.error(e); - JOptionPane.showMessageDialog(null, "Error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Error: " + e, "Error", JOptionPane.ERROR_MESSAGE); } finally { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); @@ -997,10 +1006,12 @@ private boolean sortRunData(TreeMap> result) { private void calculateCorrectedGS(TreeMap> result) { ArrayList closestVolatageArray; - double gs; - double avgError; + double gs = 0; + double avgError = 0; + int lastErrIndex = 0; + int i; gsCorrected.addAll(gsArray); - for (int i = 0; i < gsCorrected.size(); ++i) { + for (i = 0; i < gsCorrected.size(); ++i) { gs = gsCorrected.get(i); avgError = 0; closestVolatageArray = result.get(i); @@ -1008,11 +1019,23 @@ private void calculateCorrectedGS(TreeMap> result) { for (int j = 0; j < closestVolatageArray.size(); ++j) avgError += closestVolatageArray.get(j); avgError /= closestVolatageArray.size(); + lastErrIndex = i; } gsCorrected.set(i, gs +(gs * 0.01 * avgError)); } + avgError = 0; + ArrayList sortedAfrArray = result.get(lastErrIndex); + Collections.sort(sortedAfrArray, Collections.reverseOrder()); + for (i = 0; i < 10 && i < sortedAfrArray.size(); ++i) + avgError += sortedAfrArray.get(i); + if (i > 0) + avgError /= i; + for (i = lastErrIndex + 1; i < gsCorrected.size(); ++i) { + gs = gsCorrected.get(i); + gsCorrected.set(i, gs +(gs * 0.01 * avgError)); + } } - + private boolean getMafTableData(ArrayList voltArray, ArrayList gsArray) { String value; for (int i = 0; i < mafTable.getColumnCount(); ++i) { @@ -1149,7 +1172,7 @@ private void plotSmoothingLineSlopes() { setXYTable(mafSmoothingTable, voltArray, smoothGsArray); } - public void onMovePoint(int itemIndex, double valueY) { + public void onMovePoint(int itemIndex, double valueX, double valueY) { ArrayList xarr = voltArray; ArrayList yarr = smoothGsArray; XYSeries series = smoothMafData; @@ -1438,6 +1461,39 @@ public void loadData() { } } + private boolean getColumnsFilters(String[] elements) { + boolean ret = true; + ArrayList columns = new ArrayList(Arrays.asList(elements)); + String logThtlAngleColName = Config.getThrottleAngleColumnName(); + String logAfLearningColName = Config.getAfLearningColumnName(); + String logAfCorrectionColName = Config.getAfCorrectionColumnName(); + String logMafvColName = Config.getMafVoltageColumnName(); + String logAfrColName = Config.getWidebandAfrColumnName(); + String logRpmColName = Config.getRpmColumnName(); + String logLoadColName = Config.getLoadColumnName(); + String logCommandedAfrColName = Config.getCommandedAfrColumnName(); + logThtlAngleColIdx = columns.indexOf(logThtlAngleColName); + logAfLearningColIdx = columns.indexOf(logAfLearningColName); + logAfCorrectionColIdx = columns.indexOf(logAfCorrectionColName); + logMafvColIdx = columns.indexOf(logMafvColName); + logAfrColIdx = columns.indexOf(logAfrColName); + logRpmColIdx = columns.indexOf(logRpmColName); + logLoadColIdx = columns.indexOf(logLoadColName); + logCommandedAfrCol = columns.indexOf(logCommandedAfrColName); + if (logThtlAngleColIdx == -1) { Config.setThrottleAngleColumnName(Config.NO_NAME); ret = false; } + if (logAfLearningColIdx == -1) { Config.setAfLearningColumnName(Config.NO_NAME); ret = false; } + if (logAfCorrectionColIdx == -1) { Config.setAfCorrectionColumnName(Config.NO_NAME); ret = false; } + if (logMafvColIdx == -1) { Config.setMafVoltageColumnName(Config.NO_NAME); ret = false; } + if (logAfrColIdx == -1) { Config.setWidebandAfrColumnName(Config.NO_NAME); ret = false; } + if (logRpmColIdx == -1) { Config.setRpmColumnName(Config.NO_NAME); ret = false; } + if (logLoadColIdx == -1) { Config.setLoadColumnName(Config.NO_NAME); ret = false; } + if (logCommandedAfrCol == -1) { Config.setCommandedAfrColumnName(Config.NO_NAME); ret = false; } + wotPoint = Config.getWotStationaryPointValue(); + minMafV = Config.getMafVMinimumValue(); + afrErrPrct = Config.getWidebandAfrErrorPercentValue(); + return ret; + } + private void loadLogFile() { if (JFileChooser.APPROVE_OPTION != fileChooser.showOpenDialog(this)) return; @@ -1448,202 +1504,20 @@ private void loadLogFile() { String line = br.readLine(); if (line != null) { String [] elements = line.split(",", -1); - ArrayList columns = new ArrayList(Arrays.asList(elements)); - String logThtlAngleColName = Config.getThrottleAngleColumnName(); - String logAfLearningColName = Config.getAfLearningColumnName(); - String logAfCorrectionColName = Config.getAfCorrectionColumnName(); - String logMafvColName = Config.getMafVoltageColumnName(); - String logAfrColName = Config.getWidebandAfrColumnName(); - String logRpmColName = Config.getRpmColumnName(); - String logLoadColName = Config.getLoadColumnName(); - String logCommandedAfrColName = Config.getCommandedAfrColumnName(); - logThtlAngleColIdx = columns.indexOf(logThtlAngleColName); - logAfLearningColIdx = columns.indexOf(logAfLearningColName); - logAfCorrectionColIdx = columns.indexOf(logAfCorrectionColName); - logMafvColIdx = columns.indexOf(logMafvColName); - logAfrColIdx = columns.indexOf(logAfrColName); - logRpmColIdx = columns.indexOf(logRpmColName); - logLoadColIdx = columns.indexOf(logLoadColName); - logCommandedAfrCol = columns.indexOf(logCommandedAfrColName); + getColumnsFilters(elements); + boolean resetColumns = false; - - if (logThtlAngleColIdx >= 0 || - logAfLearningColIdx >= 0 || - logAfCorrectionColIdx >= 0 || - logMafvColIdx >= 0 || - logAfrColIdx >= 0 || - logRpmColIdx >= 0 || - logLoadColIdx >=0 || - logCommandedAfrCol >=0) { + if (logThtlAngleColIdx >= 0 || logAfLearningColIdx >= 0 || logAfCorrectionColIdx >= 0 || logMafvColIdx >= 0 || + logAfrColIdx >= 0 || logRpmColIdx >= 0 || logLoadColIdx >= 0 || logCommandedAfrCol >= 0) { if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, "Would you like to reset column names or filter values?", "Columns/Filters Reset", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE)) resetColumns = true; } - JTable table = new JTable() { - private static final long serialVersionUID = 1L; - public boolean isCellEditable(int row, int column) { return false; }; - }; - table.setColumnSelectionAllowed(false); - table.setCellSelectionEnabled(true); - table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - table.setBorder(new LineBorder(new Color(0, 0, 0))); - table.setTableHeader(null); - table.setModel(new DefaultTableModel(elements.length, 1)); - for (int i = 0; i < elements.length; ++i) - table.setValueAt(elements[i], i, 0); - JLabel noteLabel1 = new JLabel("Note: please check your log file if Throttle Angle % reaches 100%."); - JLabel noteLabel2 = new JLabel("If not, you could change WOT stationary point below or use Accel Pedal Angle."); - JLabel spinnerLabel = new JLabel("WOT stationary point (%)"); - JSpinner spinner = new JSpinner(new SpinnerNumberModel(wotPoint, 50, 100, 5)); - NumberFormat doubleFmt = NumberFormat.getNumberInstance(); - doubleFmt.setMaximumFractionDigits(2); - JFormattedTextField valueField = new JFormattedTextField(doubleFmt); - valueField.setColumns(10); - valueField.setVisible(false); - JComponent[] inputs = new JComponent[] { new JScrollPane(table), noteLabel1, noteLabel2, spinnerLabel, spinner, valueField }; - - if (logThtlAngleColIdx >= 0) - table.changeSelection(logThtlAngleColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logThtlAngleColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Throttle Angle % Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logThtlAngleColIdx = table.getSelectedRow(); - if (logThtlAngleColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Throttle Angle % Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - wotPoint = Integer.valueOf(spinner.getValue().toString()); - Config.setThrottleAngleColumnName(table.getValueAt(logThtlAngleColIdx, 0).toString()); - Config.setWotStationaryPointValue(wotPoint); - } - noteLabel1.setVisible(false); - noteLabel2.setVisible(false); - spinnerLabel.setVisible(false); - spinner.setVisible(false); - - if (logAfLearningColIdx >= 0) - table.changeSelection(logAfLearningColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logAfLearningColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select AFR Learning (LTFT) Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logAfLearningColIdx = table.getSelectedRow(); - if (logAfLearningColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid AFR Learning (LTFT) Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setAfLearningColumnName(table.getValueAt(logAfLearningColIdx, 0).toString()); - } - - if (logAfCorrectionColIdx >= 0) - table.changeSelection(logAfCorrectionColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logAfCorrectionColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select AFR Correction (STFT) Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logAfCorrectionColIdx = table.getSelectedRow(); - if (logAfCorrectionColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid AFR Correction (STFT) Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setAfCorrectionColumnName(table.getValueAt(logAfCorrectionColIdx, 0).toString()); - } - - noteLabel1.setText("MafV Filter - minimum value"); - valueField.setText(String.valueOf(minMafV)); - noteLabel1.setVisible(true); - valueField.setVisible(true); - if (logMafvColIdx >= 0) - table.changeSelection(logMafvColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logMafvColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Maf Voltage Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logMafvColIdx = table.getSelectedRow(); - if (logMafvColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Maf Voltage Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - minMafV = Double.valueOf(valueField.getText()); - Config.setMafVoltageColumnName(table.getValueAt(logMafvColIdx, 0).toString()); - Config.setMafVMinimumValue(minMafV); - } - noteLabel1.setVisible(false); - valueField.setVisible(false); - - noteLabel1.setText("Afr Error Filter +/- % value"); - valueField.setText(String.valueOf(afrErrPrct)); - noteLabel1.setVisible(true); - valueField.setVisible(true); - if (logAfrColIdx >= 0) - table.changeSelection(logAfrColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logAfrColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Wideband AFR Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logAfrColIdx = table.getSelectedRow(); - if (logAfrColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid AFR Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - afrErrPrct = Double.valueOf(valueField.getText()); - Config.setWidebandAfrColumnName(table.getValueAt(logAfrColIdx, 0).toString()); - Config.setWidebandAfrErrorPercentValue(afrErrPrct); - } - noteLabel1.setVisible(false); - valueField.setVisible(false); - - if (logRpmColIdx >= 0) - table.changeSelection(logRpmColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logRpmColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Engine Speed Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logRpmColIdx = table.getSelectedRow(); - if (logRpmColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Engine Speed Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setRpmColumnName(table.getValueAt(logRpmColIdx, 0).toString()); - } - - if (logLoadColIdx >= 0) - table.changeSelection(logLoadColIdx, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logLoadColIdx < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Engine Load Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logLoadColIdx = table.getSelectedRow(); - if (logLoadColIdx == -1) { - JOptionPane.showMessageDialog(null, "Invalid Engine Load Column selection", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setLoadColumnName(table.getValueAt(logLoadColIdx, 0).toString()); - } - - if (!polfTable.isSet()) { - if (logCommandedAfrCol >= 0) - table.changeSelection(logCommandedAfrCol, 0, false, false); - else - table.clearSelection(); - if (resetColumns == true || logCommandedAfrCol < 0) { - if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(null, inputs, "Select Commanded Afr Column", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) - return; - logCommandedAfrCol = table.getSelectedRow(); - if (logCommandedAfrCol == -1) { - JOptionPane.showMessageDialog(null, "Invalid Commanded Afr Column selection. Please select Commanded Afr or set the Primary Open Loop Fueling table.", "Invalid selection", JOptionPane.ERROR_MESSAGE); - return; - } - Config.setCommandedAfrColumnName(table.getValueAt(logCommandedAfrCol, 0).toString()); - } + if (resetColumns || logThtlAngleColIdx < 0 || logAfLearningColIdx < 0 || logAfCorrectionColIdx < 0 || logMafvColIdx < 0 || + logAfrColIdx < 0 || logRpmColIdx < 0 || logLoadColIdx < 0 || (logCommandedAfrCol < 0 && !polfTable.isSet())) { + ColumnsFiltersSelection selectionWindow = new ColumnsFiltersSelection(ColumnsFiltersSelection.Loop.OPEN_LOOP, polfTable.isSet()); + if (!selectionWindow.getUserSettings(elements) || !getColumnsFilters(elements)) + return; } String[] flds; @@ -1658,7 +1532,7 @@ private void loadLogFile() { double rpm; double load; double mafv; - double afrErr; + double afrErr = 0; int i = 0; for (; i < runTables.length; ++i) { if (runTables[i].getValueAt(0, 0).toString().isEmpty()) @@ -1701,12 +1575,14 @@ private void loadLogFile() { afr = afr / ((100.0 - (ltft + stft)) / 100.0); - if (!polfTable.isSet() && logCommandedAfrCol >= 0) { + if (logCommandedAfrCol >= 0) { cmdafr = Double.valueOf(flds[logCommandedAfrCol]); afrErr = (afr - cmdafr) / cmdafr * 100.0; } - else + else if (polfTable.isSet()) afrErr = calculateAfrError(rpm, load, afr); + else + JOptionPane.showMessageDialog(null, "Please set either \"Commanded AFR\" column or \"Primary Open Loop Fueling\" table", "Error", JOptionPane.ERROR_MESSAGE); if (Math.abs(afrErr) <= afrErrPrct) { runTables[i].setValueAt(rpm, j, 0); @@ -1729,7 +1605,7 @@ private void loadLogFile() { } catch (Exception e) { logger.error(e); - JOptionPane.showMessageDialog(null, e.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, e, "Error opening file", JOptionPane.ERROR_MESSAGE); } finally { if (br != null) { diff --git a/src/com/vgi/mafscaling/PrimaryOpenLoopFuelingTable.java b/src/com/vgi/mafscaling/PrimaryOpenLoopFuelingTable.java index 01e0c1e..b77e213 100644 --- a/src/com/vgi/mafscaling/PrimaryOpenLoopFuelingTable.java +++ b/src/com/vgi/mafscaling/PrimaryOpenLoopFuelingTable.java @@ -1,3 +1,21 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + package com.vgi.mafscaling; import java.awt.Component; @@ -57,7 +75,7 @@ public boolean isRoot(File file) { return false; } } - private static final Logger logger = Logger.getLogger(ClosedLoop.class); + private static final Logger logger = Logger.getLogger(PrimaryOpenLoopFuelingTable.class); private final static int ColumnWidth = 40; private ExcelAdapter excelAdapter = new ExcelAdapter(); private JFileChooser fileChooser = null; @@ -78,8 +96,14 @@ public PrimaryOpenLoopFuelingTable() { fuelingTable = loadPolFueling(fuelingTable, fileName); if (fuelingTable == null) { Config.setDefaultPOLFueling(""); - String files = Config.getPOLFuelingFiles(); - Config.setPOLFuelingFiles(files.replaceAll("," + fileName + "\\b", "")); + String[] files = Config.getPOLFuelingFiles().split(","); + String fs = ""; + for (String fn : files) { + if (fn.equals(fileName) || fn.isEmpty()) + continue; + fs += ("," + fn); + } + Config.setPOLFuelingFiles(fs); fileName = ""; } else { @@ -87,6 +111,8 @@ public PrimaryOpenLoopFuelingTable() { btnSetDefault.setText("Unset Default"); } } + else if (Config.getDefaultPOLFueling().equals(fileName)) + btnSetDefault.setText("Unset Default"); } public boolean getSetUserFueling() { @@ -104,11 +130,10 @@ public boolean getSetUserFueling() { gbc_loadList.insets = new Insets(1, 5, 1, 1); gbc_loadList.gridx = 0; gbc_loadList.gridy = 0; - if (!fileName.isEmpty()) - loadList.setSelectedItem(fileName); + loadList.setSelectedItem(fileName); loadList.setActionCommand("polselected"); loadList.addActionListener(this); - loadList.setPreferredSize(new Dimension(150, 25)); + loadList.setPreferredSize(new Dimension(150, 23)); loadList.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); fuelingPanel.add(loadList, gbc_loadList); @@ -449,8 +474,8 @@ private void setDefault(JTable fuelingTable) { else { if (!tempFileName.isEmpty()) save(fuelingTable); - if (!tempFileName.isEmpty()) - Config.setDefaultPOLFueling(tempFileName); + Config.setDefaultPOLFueling(tempFileName); + btnSetDefault.setText("Unset Default"); } } @@ -486,8 +511,15 @@ private void save(JTable fuelingTable) { } } } - String files = Config.getPOLFuelingFiles(); - Config.setPOLFuelingFiles(files.replaceAll("," + fileName + "\\b", "") + "," + tempFileName); + String[] files = Config.getPOLFuelingFiles().split(","); + String fs = ""; + for (String fn : files) { + if (fn.equals(fileName) || fn.isEmpty()) + continue; + fs += ("," + fn); + } + fs += ("," + tempFileName); + Config.setPOLFuelingFiles(fs); loadList.addItem(tempFileName); loadList.setSelectedItem(tempFileName); } @@ -510,13 +542,21 @@ private void load() { } else { loadList.removeItem(fileName); - loadList.setSelectedIndex(0); + loadList.setSelectedItem(""); if (!fileName.isEmpty()) { - String files = Config.getPOLFuelingFiles(); - Config.setPOLFuelingFiles(files.replaceAll("," + fileName + "\\b", "")); + String[] files = Config.getPOLFuelingFiles().split(","); + String fs = ""; + for (String fn : files) { + if (fn.equals(fileName) || fn.isEmpty()) + continue; + fs += ("," + fn); + } + Config.setPOLFuelingFiles(fs); } } } + else if (Config.getDefaultPOLFueling().equals(fileName)) + btnSetDefault.setText("Unset Default"); } @Override diff --git a/src/com/vgi/mafscaling/Rescale.java b/src/com/vgi/mafscaling/Rescale.java new file mode 100644 index 0000000..b33c19c --- /dev/null +++ b/src/com/vgi/mafscaling/Rescale.java @@ -0,0 +1,832 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.vgi.mafscaling; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Stroke; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JTextPane; +import javax.swing.ListSelectionModel; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.LineBorder; +import javax.swing.border.TitledBorder; +import javax.swing.table.DefaultTableModel; + +import org.apache.log4j.Logger; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.labels.StandardXYToolTipGenerator; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.SeriesRenderingOrder; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYSplineRenderer; +import org.jfree.chart.title.LegendTitle; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.ui.RectangleEdge; +import org.jfree.util.ShapeUtilities; + +public class Rescale extends JTabbedPane implements IMafChartHolder, ActionListener { + private static final long serialVersionUID = -3803091816206090707L; + private static final Logger logger = Logger.getLogger(Rescale.class); + private static final int MafTableColumnCount = 50; + private static final int ColumnWidth = 50; + private static final int CellsPerSection = 2; + private static final String OrigMafTableName = "Original MAF Scaling"; + private static final String NewMafTableName = "New MAF Scaling"; + private static final String XAxisName = "MAF Sensor (Voltage)"; + private static final String YAxisName = "Mass Airflow (g/s)"; + private static final String currentDataName = "Original"; + private static final String correctedDataName = "Rescaled"; + private final XYSeries currMafData = new XYSeries(currentDataName); + private final XYSeries corrMafData = new XYSeries(correctedDataName); + private JTable origMafTable = null; + private JTable newMafTable = null; + private TableCellListener newMafTableCellListener = null; + private JFormattedTextField newMaxVFmtTextBox = null; + private JFormattedTextField maxVUnchangedFmtTextBox = null; + private JFormattedTextField minVFmtTextBox = null; + private JFormattedTextField modeDeltaVFmtTextBox = null; + + private MafChartPanel mafChartPanel = null; + private ExcelAdapter excelAdapter = null; + private ExcelAdapter newMafExcelAdapter = null; + private ArrayList origVoltArray = null; + private ArrayList origGsArray = null; + private ArrayList deltaVoltArray = null; + private double modeDeltaV; + + public Rescale(int tabPlacement) { + super(tabPlacement); + excelAdapter = new ExcelAdapter() { + protected void onPaste(JTable table, boolean extendRows, boolean extendCols) { + super.onPaste(table, extendRows, extendCols); + calculateModeDeltaV(); + } + protected void onPasteVertical(JTable table, boolean extendRows, boolean extendCols) { + super.onPasteVertical(table, extendRows, extendCols); + calculateModeDeltaV(); + } + protected void onClearSelection(JTable table) { + super.onClearSelection(table); + calculateModeDeltaV(); + updateNewMafScale(); + } + }; + newMafExcelAdapter = new ExcelAdapter() { + protected void onPaste(JTable table, boolean extendRows, boolean extendCols) { + super.onPaste(table, extendRows, extendCols); + recalculateNewGs(); + } + protected void onPasteVertical(JTable table, boolean extendRows, boolean extendCols) { + super.onPasteVertical(table, extendRows, extendCols); + recalculateNewGs(); + } + }; + initialize(); + } + + private void initialize() { + createDataTab(); + createUsageTab(); + } + + public TableCellListener getNewMafTableCellListenerListener() { + return newMafTableCellListener; + } + + public void setNewMafTableCellListenerListener(TableCellListener listener) { + newMafTableCellListener = listener; + } + + ////////////////////////////////////////////////////////////////////////////////////// + // DATA TAB + ////////////////////////////////////////////////////////////////////////////////////// + + private void createDataTab() { + JPanel dataPanel = new JPanel(); + add(dataPanel, "
D
a
t
a
"); + GridBagLayout gbl_dataPanel = new GridBagLayout(); + gbl_dataPanel.columnWidths = new int[] {0}; + gbl_dataPanel.rowHeights = new int[] {0, 0, 0}; + gbl_dataPanel.columnWeights = new double[]{0.0}; + gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 1.0}; + dataPanel.setLayout(gbl_dataPanel); + + createControlPanel(dataPanel); + createMafScalesScrollPane(dataPanel); + createGraghPanel(dataPanel); + } + + private void createControlPanel(JPanel dataPanel) { + JPanel cntlPanel = new JPanel(); + GridBagConstraints gbl_ctrlPanel = new GridBagConstraints(); + gbl_ctrlPanel.insets = new Insets(3, 3, 3, 3); + gbl_ctrlPanel.anchor = GridBagConstraints.PAGE_START; + gbl_ctrlPanel.fill = GridBagConstraints.HORIZONTAL; + gbl_ctrlPanel.weightx = 1.0; + gbl_ctrlPanel.gridx = 0; + gbl_ctrlPanel.gridy = 0; + dataPanel.add(cntlPanel, gbl_ctrlPanel); + + GridBagLayout gbl_cntlPanel = new GridBagLayout(); + gbl_cntlPanel.columnWidths = new int[]{0, 0, 0, 0, 0, 0, 0, 0}; + gbl_cntlPanel.rowHeights = new int[]{0, 0}; + gbl_cntlPanel.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0}; + gbl_cntlPanel.rowWeights = new double[]{0}; + cntlPanel.setLayout(gbl_cntlPanel); + + NumberFormat doubleFmt = NumberFormat.getNumberInstance(); + doubleFmt.setGroupingUsed(false); + doubleFmt.setMaximumIntegerDigits(1); + doubleFmt.setMinimumIntegerDigits(1); + doubleFmt.setMaximumFractionDigits(3); + doubleFmt.setMinimumFractionDigits(1); + doubleFmt.setRoundingMode(RoundingMode.HALF_UP); + + NumberFormat scaleDoubleFmt = NumberFormat.getNumberInstance(); + scaleDoubleFmt.setGroupingUsed(false); + scaleDoubleFmt.setMaximumIntegerDigits(1); + scaleDoubleFmt.setMinimumIntegerDigits(1); + scaleDoubleFmt.setMaximumFractionDigits(8); + scaleDoubleFmt.setMinimumFractionDigits(1); + scaleDoubleFmt.setRoundingMode(RoundingMode.HALF_UP); + + JLabel newMaxVLabel = new JLabel("New Max V"); + newMaxVLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_newMaxVLabel = new GridBagConstraints(); + gbc_newMaxVLabel.anchor = GridBagConstraints.EAST; + gbc_newMaxVLabel.insets = new Insets(3, 3, 3, 0); + gbc_newMaxVLabel.gridx = 0; + gbc_newMaxVLabel.gridy = 0; + cntlPanel.add(newMaxVLabel, gbc_newMaxVLabel); + + newMaxVFmtTextBox = new JFormattedTextField(doubleFmt); + newMaxVFmtTextBox.setPreferredSize(new Dimension(50, 18)); + newMaxVFmtTextBox.addPropertyChangeListener("value", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + Object source = e.getSource(); + if (source == newMaxVFmtTextBox) + updateNewMafScale(); + } + }); + GridBagConstraints gbc_newMaxV = new GridBagConstraints(); + gbc_newMaxV.anchor = GridBagConstraints.WEST; + gbc_newMaxV.insets = new Insets(3, 3, 3, 3); + gbc_newMaxV.gridx = 1; + gbc_newMaxV.gridy = 0; + cntlPanel.add(newMaxVFmtTextBox, gbc_newMaxV); + + JLabel minVLabel = new JLabel("Min V"); + minVLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_minVLabel = new GridBagConstraints(); + gbc_minVLabel.anchor = GridBagConstraints.EAST; + gbc_minVLabel.insets = new Insets(3, 3, 3, 0); + gbc_minVLabel.gridx = 2; + gbc_minVLabel.gridy = 0; + cntlPanel.add(minVLabel, gbc_minVLabel); + + minVFmtTextBox = new JFormattedTextField(doubleFmt); + minVFmtTextBox.setPreferredSize(new Dimension(50, 18)); + minVFmtTextBox.addPropertyChangeListener("value", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + Object source = e.getSource(); + if (source == minVFmtTextBox) + updateNewMafScale(); + } + }); + GridBagConstraints gbc_minVFmtTextBox = new GridBagConstraints(); + gbc_minVFmtTextBox.anchor = GridBagConstraints.WEST; + gbc_minVFmtTextBox.insets = new Insets(3, 3, 3, 3); + gbc_minVFmtTextBox.gridx = 3; + gbc_minVFmtTextBox.gridy = 0; + cntlPanel.add(minVFmtTextBox, gbc_minVFmtTextBox); + + JLabel maxVUnchangedLabel = new JLabel("Max Unchanged"); + maxVUnchangedLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_maxVUnchangedLabel = new GridBagConstraints(); + gbc_maxVUnchangedLabel.anchor = GridBagConstraints.EAST; + gbc_maxVUnchangedLabel.insets = new Insets(3, 3, 3, 0); + gbc_maxVUnchangedLabel.gridx = 4; + gbc_maxVUnchangedLabel.gridy = 0; + cntlPanel.add(maxVUnchangedLabel, gbc_maxVUnchangedLabel); + + maxVUnchangedFmtTextBox = new JFormattedTextField(doubleFmt); + maxVUnchangedFmtTextBox.setPreferredSize(new Dimension(50, 18)); + maxVUnchangedFmtTextBox.addPropertyChangeListener("value", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + Object source = e.getSource(); + if (source == maxVUnchangedFmtTextBox) + updateNewMafScale(); + } + }); + GridBagConstraints gbc_maxVUnchangedFmtTextBox = new GridBagConstraints(); + gbc_maxVUnchangedFmtTextBox.anchor = GridBagConstraints.WEST; + gbc_maxVUnchangedFmtTextBox.insets = new Insets(3, 3, 3, 3); + gbc_maxVUnchangedFmtTextBox.gridx = 5; + gbc_maxVUnchangedFmtTextBox.gridy = 0; + cntlPanel.add(maxVUnchangedFmtTextBox, gbc_maxVUnchangedFmtTextBox); + + JLabel modeDeltaVChangeLabel = new JLabel("Mode deltaV"); + modeDeltaVChangeLabel.setHorizontalAlignment(LEFT); + GridBagConstraints gbc_modeDeltaVChangeLabel = new GridBagConstraints(); + gbc_modeDeltaVChangeLabel.anchor = GridBagConstraints.EAST; + gbc_modeDeltaVChangeLabel.insets = new Insets(3, 3, 3, 0); + gbc_modeDeltaVChangeLabel.gridx = 6; + gbc_modeDeltaVChangeLabel.gridy = 0; + cntlPanel.add(modeDeltaVChangeLabel, gbc_modeDeltaVChangeLabel); + + modeDeltaVFmtTextBox = new JFormattedTextField(scaleDoubleFmt); + modeDeltaVFmtTextBox.setPreferredSize(new Dimension(80, 18)); + modeDeltaVFmtTextBox.setEditable(false); + modeDeltaVFmtTextBox.setBackground(new Color(210,210,210)); + GridBagConstraints gbc_modeDeltaVFmtTextBox = new GridBagConstraints(); + gbc_modeDeltaVFmtTextBox.anchor = GridBagConstraints.WEST; + gbc_modeDeltaVFmtTextBox.insets = new Insets(3, 3, 3, 3); + gbc_modeDeltaVFmtTextBox.gridx = 7; + gbc_modeDeltaVFmtTextBox.gridy = 0; + cntlPanel.add(modeDeltaVFmtTextBox, gbc_modeDeltaVFmtTextBox); + } + + private void createMafScalesScrollPane(JPanel dataPanel) { + JPanel mafPanel = new JPanel(); + + JScrollPane mafScrollPane = new JScrollPane(mafPanel); + mafScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + mafScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + GridBagConstraints gbl_mafScrollPane = new GridBagConstraints(); + gbl_mafScrollPane.insets = new Insets(3, 3, 3, 3); + gbl_mafScrollPane.anchor = GridBagConstraints.PAGE_START; + gbl_mafScrollPane.fill = GridBagConstraints.HORIZONTAL; + gbl_mafScrollPane.ipady = 110; + gbl_mafScrollPane.weightx = 1.0; + gbl_mafScrollPane.gridx = 0; + gbl_mafScrollPane.gridy = 1; + + dataPanel.add(mafScrollPane, gbl_mafScrollPane); + + GridBagLayout gbl_mafPanelLayout = new GridBagLayout(); + gbl_mafPanelLayout.columnWidths = new int[]{0}; + gbl_mafPanelLayout.rowHeights = new int[]{0, 0}; + gbl_mafPanelLayout.columnWeights = new double[]{0.0, 1.0}; + gbl_mafPanelLayout.rowWeights = new double[]{0.0, 1.0}; + mafPanel.setLayout(gbl_mafPanelLayout); + + JScrollPane origMafScrollPane = new JScrollPane(); + origMafScrollPane.setBorder(BorderFactory.createEmptyBorder()); + origMafScrollPane.setViewportBorder(new TitledBorder(null, OrigMafTableName, TitledBorder.LEADING, TitledBorder.TOP, null, null)); + origMafScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + origMafScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + GridBagConstraints gbc_origMafScrollPane = new GridBagConstraints(); + gbc_origMafScrollPane.anchor = GridBagConstraints.PAGE_START; + gbc_origMafScrollPane.weightx = 1.0; + gbc_origMafScrollPane.insets = new Insets(0, 0, 0, 0); + gbc_origMafScrollPane.fill = GridBagConstraints.HORIZONTAL; + gbc_origMafScrollPane.gridx = 0; + gbc_origMafScrollPane.gridy = 0; + mafPanel.add(origMafScrollPane, gbc_origMafScrollPane); + + JPanel origDataMafPanel = new JPanel(); + origMafScrollPane.setViewportView(origDataMafPanel); + GridBagLayout gbl_origDataMafPanel = new GridBagLayout(); + gbl_origDataMafPanel.columnWidths = new int[]{0, 0, 0}; + gbl_origDataMafPanel.rowHeights = new int[] {0, 0}; + gbl_origDataMafPanel.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE}; + gbl_origDataMafPanel.rowWeights = new double[]{0.0, 0.0}; + origDataMafPanel.setLayout(gbl_origDataMafPanel); + + JLabel origVoltLabel = new JLabel("volt"); + GridBagConstraints gbc_origVoltLabel = new GridBagConstraints(); + gbc_origVoltLabel.anchor = GridBagConstraints.PAGE_START; + gbc_origVoltLabel.insets = new Insets(1, 1, 1, 5); + gbc_origVoltLabel.weightx = 0; + gbc_origVoltLabel.weighty = 0; + gbc_origVoltLabel.gridx = 0; + gbc_origVoltLabel.gridy = 0; + origDataMafPanel.add(origVoltLabel, gbc_origVoltLabel); + + JLabel origGSLabel = new JLabel(" g/s"); + GridBagConstraints gbc_origGSLabel = new GridBagConstraints(); + gbc_origGSLabel.anchor = GridBagConstraints.PAGE_START; + gbc_origGSLabel.insets = new Insets(1, 1, 1, 5); + gbc_origGSLabel.weightx = 0; + gbc_origGSLabel.weighty = 0; + gbc_origGSLabel.gridx = 0; + gbc_origGSLabel.gridy = 1; + origDataMafPanel.add(origGSLabel, gbc_origGSLabel); + + origMafTable = new JTable(); + origMafTable.setColumnSelectionAllowed(true); + origMafTable.setCellSelectionEnabled(true); + origMafTable.setBorder(new LineBorder(new Color(0, 0, 0))); + origMafTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + origMafTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + origMafTable.setModel(new DefaultTableModel(2, MafTableColumnCount) { + private static final long serialVersionUID = -2179977830999030022L; + public boolean isCellEditable(int row, int column) { return false; }; + }); + origMafTable.setTableHeader(null); + Utils.initializeTable(origMafTable, ColumnWidth); + GridBagConstraints gbc_origMafTable = new GridBagConstraints(); + gbc_origMafTable.insets = new Insets(0, 0, 0, 0); + gbc_origMafTable.fill = GridBagConstraints.BOTH; + gbc_origMafTable.weightx = 1.0; + gbc_origMafTable.weighty = 1.0; + gbc_origMafTable.gridx = 1; + gbc_origMafTable.gridy = 0; + gbc_origMafTable.gridheight = 2; + origDataMafPanel.add(origMafTable, gbc_origMafTable); + excelAdapter.addTable(origMafTable, false, false, false, false, false, false, false, false, true); + + JScrollPane newMafScrollPane = new JScrollPane(); + newMafScrollPane.setBorder(BorderFactory.createEmptyBorder()); + newMafScrollPane.setViewportBorder(new TitledBorder(null, NewMafTableName, TitledBorder.LEADING, TitledBorder.TOP, null, null)); + newMafScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + newMafScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + GridBagConstraints gbc_newMafScrollPane = new GridBagConstraints(); + gbc_newMafScrollPane.anchor = GridBagConstraints.PAGE_START; + gbc_newMafScrollPane.weightx = 1.0; + gbc_newMafScrollPane.insets = new Insets(0, 0, 0, 0); + gbc_newMafScrollPane.fill = GridBagConstraints.HORIZONTAL; + gbc_newMafScrollPane.gridx = 0; + gbc_newMafScrollPane.gridy = 1; + mafPanel.add(newMafScrollPane, gbc_newMafScrollPane); + + JPanel newDataMafPanel = new JPanel(); + newMafScrollPane.setViewportView(newDataMafPanel); + GridBagLayout gbl_newDataMafPanel = new GridBagLayout(); + gbl_newDataMafPanel.columnWidths = new int[]{0, 0, 0}; + gbl_newDataMafPanel.rowHeights = new int[] {0, 0}; + gbl_newDataMafPanel.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE}; + gbl_newDataMafPanel.rowWeights = new double[]{0.0, 0.0}; + newDataMafPanel.setLayout(gbl_newDataMafPanel); + + JLabel newVoltLabel = new JLabel("volt"); + GridBagConstraints gbc_newVoltLabel = new GridBagConstraints(); + gbc_newVoltLabel.anchor = GridBagConstraints.PAGE_START; + gbc_newVoltLabel.insets = new Insets(1, 1, 1, 5); + gbc_newVoltLabel.weightx = 0; + gbc_newVoltLabel.weighty = 0; + gbc_newVoltLabel.gridx = 0; + gbc_newVoltLabel.gridy = 0; + newDataMafPanel.add(newVoltLabel, gbc_newVoltLabel); + + JLabel newGSLabel = new JLabel(" g/s"); + GridBagConstraints gbc_newGSLabel = new GridBagConstraints(); + gbc_newGSLabel.anchor = GridBagConstraints.PAGE_START; + gbc_newGSLabel.insets = new Insets(1, 1, 1, 5); + gbc_newGSLabel.weightx = 0; + gbc_newGSLabel.weighty = 0; + gbc_newGSLabel.gridx = 0; + gbc_newGSLabel.gridy = 1; + newDataMafPanel.add(newGSLabel, gbc_newGSLabel); + + newMafTable = new JTable() { + private static final long serialVersionUID = 7749582128758153892L; + public boolean isCellEditable(int row, int column) { if (row == 1) return false; return true; }; + }; + newMafTable.setColumnSelectionAllowed(true); + newMafTable.setCellSelectionEnabled(true); + newMafTable.setBorder(new LineBorder(new Color(0, 0, 0))); + newMafTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + newMafTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + newMafTable.setModel(new DefaultTableModel(2, MafTableColumnCount)); + newMafTable.setTableHeader(null); + Utils.initializeTable(newMafTable, ColumnWidth); + GridBagConstraints gbc_newMafTable = new GridBagConstraints(); + gbc_newMafTable.insets = new Insets(0, 0, 0, 0); + gbc_newMafTable.fill = GridBagConstraints.BOTH; + gbc_newMafTable.weightx = 1.0; + gbc_newMafTable.weighty = 1.0; + gbc_newMafTable.gridx = 1; + gbc_newMafTable.gridy = 0; + gbc_newMafTable.gridheight = 2; + newDataMafPanel.add(newMafTable, gbc_newMafTable); + newMafExcelAdapter.addTable(newMafTable, false, false, false, false, false, false, false, false, true); +/* + TableModelListener newTableListener = new TableModelListener() { + public void tableChanged(TableModelEvent tme) { + if (tme.getType() == TableModelEvent.UPDATE) { + int colCount = newMafTable.getColumnCount(); + corrMafData.clear(); + for (int i = 0; i < colCount; ++i) { + if (Pattern.matches(Utils.fpRegex, newMafTable.getValueAt(0, i).toString()) && + Pattern.matches(Utils.fpRegex, newMafTable.getValueAt(1, i).toString())) { + corrMafData.add(Double.valueOf(newMafTable.getValueAt(0, i).toString()), Double.valueOf(newMafTable.getValueAt(1, i).toString())); + } + else + break; + } + if (colCount != corrMafData.getItemCount()) + corrMafData.clear(); + corrMafData.fireSeriesChanged(); + } + } + }; + newMafTable.getModel().addTableModelListener(newTableListener); +*/ + + Action action = new AbstractAction() { + private static final long serialVersionUID = 1L; + public void actionPerformed(ActionEvent e) { + recalculateNewGs(); + } + }; + + setNewMafTableCellListenerListener(new TableCellListener(newMafTable, action)); + } + + private void createGraghPanel(JPanel dataPanel) { + JFreeChart chart = ChartFactory.createScatterPlot(null, null, null, null, PlotOrientation.VERTICAL, false, true, false); + chart.setBorderVisible(true); + mafChartPanel = new MafChartPanel(chart, this); + + GridBagConstraints gbl_chartPanel = new GridBagConstraints(); + gbl_chartPanel.anchor = GridBagConstraints.PAGE_START; + gbl_chartPanel.insets = new Insets(3, 3, 3, 3); + gbl_chartPanel.fill = GridBagConstraints.BOTH; + gbl_chartPanel.weightx = 1.0; + gbl_chartPanel.weighty = 1.0; + gbl_chartPanel.gridx = 0; + gbl_chartPanel.gridy = 2; + dataPanel.add(mafChartPanel.getChartPanel(), gbl_chartPanel); + + XYSplineRenderer lineRenderer = new XYSplineRenderer(3); + lineRenderer.setUseFillPaint(true); + lineRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator( + StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, + new DecimalFormat("0.00"), new DecimalFormat("0.00"))); + + Stroke stroke = new BasicStroke(2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f, null, 0.0f); + lineRenderer.setSeriesStroke(0, stroke); + lineRenderer.setSeriesStroke(1, stroke); + lineRenderer.setSeriesPaint(0, new Color(201, 0, 0)); + lineRenderer.setSeriesPaint(1, new Color(0, 0, 255)); + lineRenderer.setSeriesShape(0, ShapeUtilities.createDiamond((float) 2.5)); + lineRenderer.setSeriesShape(1, ShapeUtilities.createUpTriangle((float) 2.5)); + + ValueAxis mafvDomain = new NumberAxis(XAxisName); + ValueAxis mafgsRange = new NumberAxis(YAxisName); + + XYSeriesCollection lineDataset = new XYSeriesCollection(); + + lineDataset.addSeries(currMafData); + lineDataset.addSeries(corrMafData); + + XYPlot plot = chart.getXYPlot(); + plot.setRangePannable(true); + plot.setDomainPannable(true); + plot.setDomainGridlinePaint(Color.DARK_GRAY); + plot.setRangeGridlinePaint(Color.DARK_GRAY); + plot.setBackgroundPaint(new Color(224, 224, 224)); + plot.setSeriesRenderingOrder(SeriesRenderingOrder.FORWARD); + + plot.setDataset(0, lineDataset); + plot.setRenderer(0, lineRenderer); + plot.setDomainAxis(0, mafvDomain); + plot.setRangeAxis(0, mafgsRange); + plot.mapDatasetToDomainAxis(0, 0); + plot.mapDatasetToRangeAxis(0, 0); + + + LegendTitle legend = new LegendTitle(plot.getRenderer()); + legend.setItemFont(new Font("Arial", 0, 10)); + legend.setPosition(RectangleEdge.TOP); + chart.addLegend(legend); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // CREATE USAGE TAB + ////////////////////////////////////////////////////////////////////////////////////// + + private void createUsageTab() { + JTextPane usageTextArea = new JTextPane(); + usageTextArea.setMargin(new Insets(10, 10, 10, 10)); + usageTextArea.setContentType("text/html"); + usageTextArea.setText(usage()); + usageTextArea.setEditable(false); + usageTextArea.setCaretPosition(0); + + JScrollPane textScrollPane = new JScrollPane(usageTextArea); + textScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + textScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + add(textScrollPane, "
U
s
a
g
e
"); + } + + private String usage() { + ResourceBundle bundle; + bundle = ResourceBundle.getBundle("com.vgi.mafscaling.rescale"); + return bundle.getString("usage"); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // WORK FUNCTIONS + ////////////////////////////////////////////////////////////////////////////////////// + + private boolean getMafTableData(JTable mafTable, ArrayList voltArray, ArrayList gsArray) { + String value; + for (int i = 0; i < mafTable.getColumnCount(); ++i) { + for (int j = 0; j < mafTable.getRowCount(); ++j) { + value = mafTable.getValueAt(j, i).toString(); + if (value.isEmpty()) + return true; + if (!Utils.validateDouble(value, j, i, mafTable.getName())) + return false; + } + voltArray.add(Double.parseDouble(mafTable.getValueAt(0, i).toString())); + gsArray.add(Double.parseDouble(mafTable.getValueAt(1, i).toString())); + } + if (voltArray.size() != gsArray.size()) { + JOptionPane.showMessageDialog(null, "Data sets (volt/gs) in " + mafTable.getName() + " have different length", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + private void calculateModeDeltaV() { + origVoltArray = new ArrayList(); + origGsArray = new ArrayList(); + if (!getMafTableData(origMafTable, origVoltArray, origGsArray) || origVoltArray.size() == 0) + return; + deltaVoltArray = new ArrayList(); + deltaVoltArray.add(0.0); + int i; + for (i = 1; i < origVoltArray.size(); ++i) + deltaVoltArray.add(origVoltArray.get(i) - origVoltArray.get(i - 1)); + modeDeltaV = Utils.mode(deltaVoltArray); + modeDeltaVFmtTextBox.setValue(modeDeltaV); + for (i = deltaVoltArray.size() - 1; i > 0; --i) { + if (modeDeltaV == deltaVoltArray.get(i)) { + // hack as if value being set is the same then change even is not sent and updateNewMafScale() is not triggered + maxVUnchangedFmtTextBox.setValue(null); + maxVUnchangedFmtTextBox.setValue(origVoltArray.get(i)); + return; + } + } + maxVUnchangedFmtTextBox.setValue(null); + } + + private void calculateNewGs(ArrayList newVoltArray, ArrayList newGsArray) { + TreeMap vgsTree = new TreeMap(); + for (int i = origVoltArray.size() - 1; i >= 0; --i) + vgsTree.put(origVoltArray.get(i), i); + Map.Entry kv; + double x0, y0, x, y, x1, y1; + for (int i = 1; i < newVoltArray.size(); ++i) { + x = newVoltArray.get(i); + kv = vgsTree.floorEntry(x); + if (kv == null) { + newGsArray.add(0.0); + continue; + } + x0 = kv.getKey(); + if (x0 == x) { + newGsArray.add(origGsArray.get(kv.getValue())); + continue; + } + y0 = origGsArray.get(kv.getValue()); + kv = vgsTree.ceilingEntry(x); + if (kv == null) { + newGsArray.add(0.0); + continue; + } + x1 = kv.getKey(); + y1 = origGsArray.get(kv.getValue()); + y = Utils.linearInterpolation(x, x0, x1, y0, y1); + newGsArray.add(y); + } + } + + private void recalculateNewGs() { + try { + if (origVoltArray.size() == 0 || origVoltArray.size() != origGsArray.size()) + return; + ArrayList newVoltArray = new ArrayList(); + ArrayList newGsArray = new ArrayList(); + newGsArray.add(origGsArray.get(0)); + for (int i = 0; i < newMafTable.getColumnCount(); ++i) { + if (Pattern.matches(Utils.fpRegex, newMafTable.getValueAt(0, i).toString())) + newVoltArray.add(Double.valueOf(newMafTable.getValueAt(0, i).toString())); + else + break; + } + if (newVoltArray.size() != origVoltArray.size()) + return; + calculateNewGs(newVoltArray, newGsArray); + for (int i = 0; i < newVoltArray.size(); ++i) + newMafTable.setValueAt(newGsArray.get(i), 1, i); + corrMafData.clear(); + setXYSeries(corrMafData, newVoltArray, newGsArray); + } + catch (Exception e) { + e.printStackTrace(); + logger.error(e); + } + } + + private void updateNewMafScale() { + try { + corrMafData.clear(); + currMafData.clear(); + Utils.clearTable(newMafTable); + + if (newMaxVFmtTextBox.getValue() == null || + maxVUnchangedFmtTextBox.getValue() == null || + minVFmtTextBox.getValue() == null || + origMafTable.getValueAt(0, 0).toString().isEmpty()) + return; + + if (origVoltArray.size() == 0 || origVoltArray.size() != origGsArray.size()) + return; + + if (origVoltArray.size() < 10) { + JOptionPane.showMessageDialog(null, "It looks like you have only partial original MAF scale table", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return; + } + + double newMafV = (((Number)newMaxVFmtTextBox.getValue()).doubleValue()); + if (newMafV < origVoltArray.get(0)) { + JOptionPane.showMessageDialog(null, "New Max V [" + newMafV + "] can't be lower than first MAF table value", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return; + } + if (newMafV > origVoltArray.get(origVoltArray.size() - 1)) { + JOptionPane.showMessageDialog(null, "New Max V [" + newMafV + "] can't be higher than last MAF table value", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return; + } + + double minV = (((Number)minVFmtTextBox.getValue()).doubleValue()); + if (minV <= origVoltArray.get(1)) { + JOptionPane.showMessageDialog(null, "Min V [" + minV + "] must be higher than second MAF table value", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return; + } + if (minV > newMafV) { + JOptionPane.showMessageDialog(null, "Min V [" + minV + "] can't be higher than new MAF V value", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return; + } + + double maxVUnch = (((Number)maxVUnchangedFmtTextBox.getValue()).doubleValue()); + if (maxVUnch <= minV) { + JOptionPane.showMessageDialog(null, "Max Unchanged [" + maxVUnch + "] must be higher than Min V value", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return; + } + if (maxVUnch > newMafV) { + JOptionPane.showMessageDialog(null, "Max Unchanged [" + maxVUnch + "] can't be higher than new MAF V value", "Invalid Data", JOptionPane.ERROR_MESSAGE); + return; + } + + int i, j, z; + ArrayList newVoltArray = new ArrayList(); + ArrayList newGsArray = new ArrayList(); + newVoltArray.add(origVoltArray.get(0)); + newGsArray.add(origGsArray.get(0)); + + // Find first value greater than MinV from original scale, + // calculate mid-point and add them as second and third values to the new array. + // After that simply copy all original values up until Max Unchanged value. + boolean minFound = false; + double val; + for (i = 2; i < origVoltArray.size(); ++i) { + val = origVoltArray.get(i); + if (minFound) { + if (val <= maxVUnch) + newVoltArray.add(val); + else + break; + } + else if (minV <= val) { + newVoltArray.add((val - origVoltArray.get(0)) / 2.0 + origVoltArray.get(0)); + newVoltArray.add(val); + minFound = true; + } + } + int newMaxUnchIdx = newVoltArray.size() - 1; + + // Find avg % change per section in the original scale but for the same number of points as new scale + double pointsCount = origVoltArray.size() - newVoltArray.size(); + int sectionCount = (int)Math.ceil((double)pointsCount / (double)CellsPerSection); + List modDeltaList = deltaVoltArray.subList(newMaxUnchIdx, deltaVoltArray.size()); + double avgDelta = Utils.mean(modDeltaList); + double maxDelta = Collections.max(modDeltaList); + double minDelta = Collections.min(modDeltaList); + double avgSectionChange = (maxDelta - minDelta) / sectionCount; + double changePercent = (maxDelta - minDelta) / avgSectionChange; + + // Calculate delta per section + double delta; + ArrayList adj = new ArrayList(); + for (i = 0; i < sectionCount; ++i) + adj.add(avgDelta); + int end = (int) Math.floor(sectionCount / 2.0); + for (i = 0, j = sectionCount - 1; i < j; ++i, --j) { + delta = avgDelta / 100.00 * changePercent * (end - i); + adj.set(i, avgDelta - delta); + adj.set(j, avgDelta + delta); + } + // Apply diff for each cell of each section + for (i = newMaxUnchIdx + 1, j = 0, z = 0; i < origVoltArray.size(); ++i, ++j) { + double diff = adj.get(z); + if (j >= CellsPerSection) { + j = 0; + ++z; + diff = adj.get(z); + } + newVoltArray.add(newVoltArray.get(i - 1) + diff); + } + // Since the above diffs are based of the original scale change simply adjust the new values to fit the new scale + double corr = (newMafV - newVoltArray.get(newVoltArray.size() - 1)) / pointsCount; + for (i = newMaxUnchIdx + 1, j = 1; i < newVoltArray.size(); ++i, ++j) + newVoltArray.set(i, newVoltArray.get(i) + j * corr); + + calculateNewGs(newVoltArray, newGsArray); + + Utils.ensureColumnCount(newVoltArray.size(), newMafTable); + for (i = 0; i < newVoltArray.size(); ++i) { + newMafTable.setValueAt(newVoltArray.get(i), 0, i); + newMafTable.setValueAt(newGsArray.get(i), 1, i); + } + setXYSeries(currMafData, origVoltArray, origGsArray); + setXYSeries(corrMafData, newVoltArray, newGsArray); + setRanges(mafChartPanel); + } + catch (Exception e) { + logger.error(e); + } + } + + private boolean setXYSeries(XYSeries series, ArrayList xarr, ArrayList yarr) { + if (xarr.size() == 0 || xarr.size() != yarr.size()) + return false; + series.clear(); + for (int i = 0; i < xarr.size(); ++i) + series.add(xarr.get(i), yarr.get(i), false); + series.fireSeriesChanged(); + return true; + } + + private void setRanges(MafChartPanel chartPanel) { + double paddingX = currMafData.getMaxX() * 0.05; + double paddingY = currMafData.getMaxY() * 0.05; + chartPanel.getChartPanel().getChart().getXYPlot().getDomainAxis(0).setRange(currMafData.getMinX() - paddingX, currMafData.getMaxX() + paddingX); + chartPanel.getChartPanel().getChart().getXYPlot().getRangeAxis(0).setRange(currMafData.getMinY() - paddingY, currMafData.getMaxY() + paddingY); + } + + @Override + public void onMovePoint(int itemIndex, double valueX, double valueY) { + newMafTable.setValueAt(valueX, 0, itemIndex); + newMafTable.setValueAt(valueY, 1, itemIndex); + } + + @Override + public void actionPerformed(ActionEvent e) { + } + +} diff --git a/src/com/vgi/mafscaling/TableCellListener.java b/src/com/vgi/mafscaling/TableCellListener.java new file mode 100644 index 0000000..64a3616 --- /dev/null +++ b/src/com/vgi/mafscaling/TableCellListener.java @@ -0,0 +1,171 @@ +/* +* Open-Source tuning tools +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.vgi.mafscaling; + +import java.awt.event.*; +import javax.swing.*; +import java.beans.*; + +/* + * This class listens for changes made to the data in the table via the + * TableCellEditor. When editing is started, the value of the cell is saved + * When editing is stopped the new value is saved. When the oold and new + * values are different, then the provided Action is invoked. + * + * The source of the Action is a TableCellListener instance. + */ +public class TableCellListener implements PropertyChangeListener, Runnable +{ + private JTable table; + private Action action; + private int row; + private int column; + private Object oldValue; + private Object newValue; + + /** + * Create a TableCellListener. + * @param table the table to be monitored for data changes + * @param action the Action to invoke when cell data is changed + */ + public TableCellListener(JTable table, Action action) + { + this.table = table; + this.action = action; + table.addPropertyChangeListener(this); + } + + /** + * Create a TableCellListener with a copy of all the data relevant to + * the change of data for a given cell. + * @param row the row of the changed cell + * @param column the column of the changed cell + * @param oldValue the old data of the changed cell + * @param newValue the new data of the changed cell + */ + private TableCellListener(JTable table, int row, int column, Object oldValue, Object newValue) + { + this.table = table; + this.row = row; + this.column = column; + this.oldValue = oldValue; + this.newValue = newValue; + } + + /** + * Get the column that was last edited + * @return the column that was edited + */ + public int getColumn() + { + return column; + } + + /** + * Get the new value in the cell + * @return the new value in the cell + */ + public Object getNewValue() + { + return newValue; + } + + /** + * Get the old value of the cell + * @return the old value of the cell + */ + public Object getOldValue() + { + return oldValue; + } + + /** + * Get the row that was last edited + * @return the row that was edited + */ + public int getRow() + { + return row; + } + + /** + * Get the table of the cell that was changed + * @return the table of the cell that was changed + */ + public JTable getTable() + { + return table; + } + + /** + * Implement the PropertyChangeListener interface + */ + @Override + public void propertyChange(PropertyChangeEvent e) + { + // A cell has started/stopped editing + if ("tableCellEditor".equals(e.getPropertyName())) + { + if (table.isEditing()) + processEditingStarted(); + else + processEditingStopped(); + } + } + + /** + * Save information of the cell about to be edited + */ + private void processEditingStarted() + { + /* The invokeLater is necessary because the editing row and editing column of the table have + * not been set when the "tableCellEditor" PropertyChangeEvent is fired. + * This results in the "run" method being invoked + */ + SwingUtilities.invokeLater(this); + } + + /** + * Override for the above. + */ + @Override + public void run() + { + row = table.convertRowIndexToModel(table.getEditingRow()); + column = table.convertColumnIndexToModel(table.getEditingColumn()); + oldValue = table.getModel().getValueAt(row, column); + newValue = null; + } + + /** + * Update the Cell history when necessary + */ + private void processEditingStopped() + { + newValue = table.getModel().getValueAt(row, column); + // The data has changed, invoke the supplied Action + if (!newValue.equals(oldValue)) + { + // Make a copy of the data in case another cell starts editing while processing this change + TableCellListener tcl = new TableCellListener(getTable(), getRow(), getColumn(), getOldValue(), getNewValue()); + ActionEvent event = new ActionEvent(tcl, ActionEvent.ACTION_PERFORMED, ""); + action.actionPerformed(event); + } + } +} diff --git a/src/com/vgi/mafscaling/Utils.java b/src/com/vgi/mafscaling/Utils.java index df3fdd3..e2a1ac7 100644 --- a/src/com/vgi/mafscaling/Utils.java +++ b/src/com/vgi/mafscaling/Utils.java @@ -22,7 +22,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.TreeSet; import java.util.regex.Pattern; @@ -59,10 +61,14 @@ public static Color[] getColorArray(int numColors) { * @return array of unique gradient colors */ public static Color[] getColorArray(Color begin, Color end, int numColors) { - Color[] gradient = new Color[numColors]; + Color[] gradient; + if (numColors == 1) + gradient = new Color[3]; + else + gradient = new Color[numColors]; float[] hsv1 = Color.RGBtoHSB(begin.getRed(), begin.getGreen(), begin.getBlue(), null); float[] hsv2 = Color.RGBtoHSB(end.getRed(), end.getGreen(), end.getBlue(), null); - int a1 = begin.getAlpha() ; + int a1 = begin.getAlpha(); float h1 = hsv1[0] ; float s1 = hsv1[1]; float v1 = hsv1[2]; @@ -70,12 +76,14 @@ public static Color[] getColorArray(Color begin, Color end, int numColors) { float dh = hsv2[0] - h1; float ds = hsv2[1]- s1; float dv = hsv2[2] - v1; - for (int i = 0; i < gradient.length; ++i) { + for (int i = 0; i < gradient.length; ++i) { float rel = i / (float)(gradient.length - 1); - int rgb = Color.HSBtoRGB(h1 + dh * rel, s1 + ds * rel, v1 + dv * rel) ; - rgb +=(((int)(a1 + da * rel)) << 24) ; + int rgb = Color.HSBtoRGB(h1 + dh * rel, s1 + ds * rel, v1 + dv * rel); + rgb +=(((int)(a1 + da * rel)) << 24); gradient[i] = new Color(rgb); } + if (numColors == 1) + gradient = new Color[] { gradient[1] }; return gradient; } @@ -142,6 +150,25 @@ public static void colorTable(JTable table) { ((DefaultTableModel)table.getModel()).fireTableDataChanged(); } + /** + * Method sets gradient background color for first row and first column assuming those are headers. + * The table must have default renderer set as BgColorFormatRenderer + * @param table + */ + public static void colorTableHeaders(JTable table) { + Color[][] colorMatrix = new Color[table.getRowCount()][table.getColumnCount()]; + if (colorMatrix != null) { + for (int i = 0; i < colorMatrix.length; ++i) + colorMatrix[i][0] = Color.LIGHT_GRAY; + for (int i = 0; i < colorMatrix[0].length; ++i) + colorMatrix[0][i] = Color.LIGHT_GRAY; + BgColorFormatRenderer renderer = (BgColorFormatRenderer)table.getDefaultRenderer(Object.class); + if (renderer != null) + renderer.setColors(colorMatrix); + } + ((DefaultTableModel)table.getModel()).fireTableDataChanged(); + } + /** * Method clears the table cells and sets default value - an empty string * @param table @@ -184,7 +211,7 @@ public static void ensureColumnCount(int count, JTable table) { int[] minwidth = new int[count]; int[] maxwidth = new int[count]; int[] prefwidth = new int[count]; - int i; + int i, j; for (i = 0; i < table.getColumnCount(); ++i) { minwidth[i] = table.getColumnModel().getColumn(i).getMinWidth(); maxwidth[i] = table.getColumnModel().getColumn(i).getMaxWidth(); @@ -196,7 +223,7 @@ public static void ensureColumnCount(int count, JTable table) { minwidth[i] = minwidth[i - 1]; maxwidth[i] = maxwidth[i - 1]; prefwidth[i] = prefwidth[i - 1]; - for (int j = 0; j < table.getRowCount(); ++j) + for (j = 0; j < table.getRowCount(); ++j) table.setValueAt("", j, i); } for (i = 0; i < count; ++i) { @@ -213,8 +240,12 @@ public static void ensureColumnCount(int count, JTable table) { */ public static void ensureRowCount(int count, JTable table) { DefaultTableModel model = (DefaultTableModel)table.getModel(); - while (count > table.getRowCount()) + int i, j; + for (i = table.getRowCount(); i < count; ++i) { model.addRow(new Object[table.getColumnCount()]); + for (j = 0; j < table.getColumnCount(); ++j) + table.setValueAt("", i, j); + } } /** @@ -324,4 +355,116 @@ public static int closestValueIndex(double val, ArrayList list) { return index; } + /** + * Method returns a linearly interpolated value + * @param x is X value you want to interpolate at + * @param x1 is X value for previous point + * @param x2 is X value for following point + * @param y1 is Y value for previous point + * @param y2 is Y value for following point + * @return Y interpolated value + */ + public static double linearInterpolation(double x, double x1, double x2, double y1, double y2) { + return (x1 == x2) ? 0.0 : (y1 + (x - x1) * (y2 - y1) / (x2 - x1)); + } + + /** + * Method rounds input number by a specific step + * @param input + * @param step + * @return + */ + public static double round(double input, double step) { + return ((Math.round(input / step)) * step); + } + + /** + * Calculate mean of the array + * @param data + * @return + */ + public static double mean(List data) { + double val = 0; + for (int i = 0; i < data.size(); ++i) + val += data.get(i); + return val / data.size(); + } + + /** + * Calculate median of the array + * @param data + * @return + */ + public static double median(List data) { + double val = 0; + Collections.sort(data); + int mid = data.size() / 2; + if (data.size() % 2 == 1) + val = data.get(mid); + else + val = (data.get(mid - 1) + data.get(mid)) / 2; + return val; + } + + /** + * Calculate mode of the array + * @param data + * @return + */ + public static double mode(List data) { + ArrayList modes = new ArrayList(); + HashMap countMap = new HashMap(); + int max = -1; + Integer count; + for (Double n : data) { + count = countMap.get(n); + if (count == null) + count = 0; + count += 1; + countMap.put(n, count); + if (count > max) + max = count; + } + for (Map.Entry entry : countMap.entrySet()) { + if (entry.getValue() == max) + modes.add(entry.getKey()); + } + Collections.sort(modes); + return modes.get(modes.size() / 2); + } + + /** + * Calculate range of the array + * @param data + * @return + */ + public static double range(List data) { + return Collections.max(data) - Collections.min(data); + } + + /** + * Calculate variance of the array + * @param data + * @return + */ + public static double variance(List data) { + double mean = mean(data); + double sum = 0; + double diff = 0; + for (Double d : data) { + diff = d - mean; + sum += (diff * diff); + } + return sum / data.size(); + } + + /** + * Calculate standard deviation of the array + * @param data + * @return + */ + public static double standardDeviation(List data) { + return Math.sqrt(variance(data)); + } + } diff --git a/src/com/vgi/mafscaling/closed_loop.properties b/src/com/vgi/mafscaling/closed_loop.properties index 180ce57..ce0cd54 100644 --- a/src/com/vgi/mafscaling/closed_loop.properties +++ b/src/com/vgi/mafscaling/closed_loop.properties @@ -9,6 +9,7 @@ The usage guide is based on wrxsti-l's post. Big thanks to all who have been act

The Total Correction % is then calculated for each cell as average of mean and mode of Correction Error values.
\

The correction will only be applied to cells where count of corrections is more than 30.
\

The corrected g/sec is calculated from the Total Correction % and the current g/sec as current_gsec * ((tot_corr% / 100) + 1).
\ +

Also, please note that collected data and correction applied may be greater than the full MAF scaling voltage and thus several of the first table cell would be uncorrected. In this case the first calculated Total Correction % value is applied to all cells prior to it except the first cell.\
\

Usage:

\ You won't nail the calibration in one shot, so rinse and repeat until you're happy with the scaling. Also, it is VERY important that you collect data at a constant temperature. If you collect data at 30F one day and then 60F next week, it will totally screw up the process.

\ @@ -33,13 +34,13 @@ The usage guide is based on wrxsti-l's post. Big thanks to all who have been act
  • Open your tune in RomRaider.
  • \
  • Expand "Fueling - Primary Open Loop" tables group and copy "Primary Open Loop Fueling" table by clicking "Edit"->"Copy Table" into the first cell of "Primary Open Loop Fueling" table on the tool.
  • \
  • Click "Validate" button to make sure all is fine (validation is done on setting the table as default and saving it or on submitting the table so it's not required).
  • \ -
  • Most likely the Primary Open Loop Fueling table will stay the same, so for autoloading next time you can click on "Set Default" and save this table in a new file. If you do have another tune with a different Primary Open Loop Fueling table, you can click on "Clear" to clear out table, then paste data from second Primary Open Loop Fueling table, click on "Set Default" and save this table in a new file. To switch between different Primary Open Loop Fueling tables saved in files click on "Clear" to clear table, then click on "Load" to load data from file, then click on "Set Default" so that next time it will be loaded automatically.
  • \ +
  • Most likely the Primary Open Loop Fueling table will stay the same, so for auto-loading next time you can click on "Set Default" and save this table in a new file. If you do have another tune with a different Primary Open Loop Fueling table, you can click on "Clear" to clear out table, then paste data from second Primary Open Loop Fueling table, click on "Set Default" and save this table in a new file. To switch between different Primary Open Loop Fueling tables saved in files click on "Clear" to clear table, then click on "Load" to load data from file, then click on "Set Default" so that next time it will be loaded automatically.
  • \ \
  • Now you have three ways to proceed:
    \
      \
    • If you have used external tools (eg some spreadsheet) and have filtered Closed Loop data you can simply copy/paste that data into corresponding columns' first cell.
    • \
    • If you have saved data from previous run and would like to see the graphs or modify some data manually you can load that run using "Load" button.
    • \ -
    • If you just ecu log file(s) then click on "Load Log" button, select your log file, select asked columns from log file AND set desired filters values when prompted. Please pay attention to filters on some columns to filter out noisy data. Once the log file is processed you should see closed loop data populated in the table. Repeat this procedure to load more log files - the data will be appended to the table.
    • \ +
    • If you just log file(s) then click on "Load Log" button, select your log file, select asked columns from log file AND set desired filters values when prompted. Please pay attention to filters on some columns to filter out noisy data. Once the log file is processed you should see closed loop data populated in the table. Repeat this procedure to load more log files - the data will be appended to the table.
    • \
    \
  • \
  • At this point you can save all the input data for future reference or re-scaling by clicking on "Save" button at the top bar.
  • \ @@ -67,17 +68,20 @@ The usage guide is based on wrxsti-l's post. Big thanks to all who have been act
      \
    • quick - straight values copy
      \
        \ -
      1. Simply copy the newly generated "Airflow (g/s)" values from results table into the "MAF Sensor Scaling" table in your tuning tool (eg RommRaider).
      2. \ +
      3. Simply copy the newly generated "Airflow (g/s)" values from results table into the "MAF Sensor Scaling" table in your tuning tool (eg RomRaider).
      4. \
      \
    • \ -
    • slow - apply smoothing or manual changes. To apply smoothing to the Corrected Maf Scaling curve or to make manual changes (only for people who really know what they are doing!!!) click on "Smoothing" checkbox. The chart view will display smoothed MAF curve together with current MAF curve slope and the smoothed MAF curve slope. At the bottom of the screen there is resulting MAF scaling table with controls that allow making changes to the table (and thus the MAF curve). Row 1 of the result table corresponds to "MAF Sensor (volts)" and row 2 of the "Smoothing" tab corresponds to "Airflow (g/s)".
      \ +
    • slow (optional) - apply smoothing or manual changes. To apply smoothing to the Corrected Maf Scaling curve or to make manual changes (only for people who really know what they are doing!!!) click on "Smoothing" checkbox. The chart view will display Smoothed Maf curve(blue) together with Current MAF curve slope line(red) and the Smoothed MAF curve slope line(green). At the bottom of the screen there is resulting MAF scaling table with controls that allow making changes to the table (and thus the MAF curve). Row 1 of the result table corresponds to "MAF Sensor (volts)" and row 2 of the "Smoothing" tab corresponds to "Airflow (g/s)".\ +

      Slope line is a line of MAF Curve slope values. Slope value is a number which is used to indicate the steepness of a curve at a particular point. Slope value for MAF Curve is calculated between each 2 points of the curve as: \ +

      Code:
      slope = (y2 - y1) / (x2 - x1)
      Since the MAF curve is non-linear you see that the slope of the curve changes as you move along it.

      \
        \
      1. All smoothing changes are immediate and can be seen on the smoothing graph.
      2. \
      3. If you have overdone smoothing, simply click on "Reset" button to set the smoothed curve back to corrected curve.
      4. \
      5. You can apply smoothing by selecting the cells in MAF scaling table and the smoothing degree and then clicking "Apply" button. Smoothing degree is simply a moving window size where 3 - uses 1 cell to left and right, 5 - uses 2 cells to left and right, and 7 - uses 3 cells to left and right.
      6. \
      7. Please note that due to how smoothing algorithm works one, two, or three (depending on smoothing degree you chose) the first cells and last cell will not get modified. You can raise/lower those manually if needed by the same value the cell after or cell before was modified.
      8. \
      9. BE CAREFULL not to smooth too much or you will end up making your MAF scaling worse. General suggestion is to apply either degree "3" or "5" once.
      10. \ -
      11. In addition, you can manually smoothen values by either dragging the smoothed curve's point or by applying +/-0.10 to the highlighted cells using buttons on the left from the result table. Keep in mind that selection all cells will raise or lower the whole curve. Thus you can raise certain parts of the curve by selecting appropriate cells.
      12. \ +
      13. In addition, you can manually smooth values by either dragging the smoothed curve slope line's point or by applying custom +/- value to the highlighted cells using buttons on the left from the result table. Keep in mind that selection of all cells will raise or lower the whole curve. Thus you can raise certain parts of the curve by selecting appropriate cells.
      14. \ +
      15. Finally, there is a compare screen. You copy original MAF scaling and the new MAF scaling and see change % difference. You can also manually alter change % difference if you feel it's needed and it will modify you new MAF scaling.
      16. \
      17. Once you have smoothed the data to your liking, copy the data to your "MAF Sensor Scaling" table.
      18. \
      \
    • \ diff --git a/src/com/vgi/mafscaling/logstats.properties b/src/com/vgi/mafscaling/logstats.properties new file mode 100644 index 0000000..52e1810 --- /dev/null +++ b/src/com/vgi/mafscaling/logstats.properties @@ -0,0 +1,21 @@ +usage=\ +\ +

      Credits:

      \ +This tool is similar to BrzEdit Log Viewer.\ +

      Logic:

      \ +

      The tool is supposed to help in analyzing the \ +log data and visualizing data distribution by applying different statistics methods and filters.\ +

      Usage:

      \ +
        \ +
      1. Select log file to be analyzed.
      2. \ +
      3. Select X-Axis column from available columns in log file (eg Engine Load).
      4. \ +
      5. Select Y-Axis column from available columns in log file (eg Engine Speed).
      6. \ +
      7. Select Data column to be analyzed from available columns in log file (eg AFR).
      8. \ +
      9. Set X-Axis rounding - 'step' (eg 0.1 for Engine Load) or set the fixed scaling values for X-Axis column (please note that setting step takes priority over fixed scale values).
      10. \ +
      11. Set Y-Axis rounding - 'step' (eg 200 for Engine Speed) or set the fixed scaling values for Y-Axis column (please note that setting step takes priority over fixed scale values).
      12. \ +
      13. Select statistics method for analyzes (eg Mean).
      14. \ +
      15. Optionally set the filter comparison method and value to be compared against the Data column values (eg AFR < 16). Please note that for equality the Data column values are rounded \ +to the same number of decimal points as the filter input value.
      16. \ +
      17. Click on 'GO' button.
      18. \ +
      \ +\ diff --git a/src/com/vgi/mafscaling/open_loop.properties b/src/com/vgi/mafscaling/open_loop.properties index 4c5c6c2..fad78ef 100644 --- a/src/com/vgi/mafscaling/open_loop.properties +++ b/src/com/vgi/mafscaling/open_loop.properties @@ -5,7 +5,7 @@ This tool is originally based on BadNoodle's spreadsheet, however changes were m The usage guide is based on wrxsti-l's post. Big thanks to all who have been active in MAF scaling discussions and providing methodology - williaty, Rene2.5RS, and others.

      A special 'thank you' to Td-d and Kodename47 for providing feedback.
      \

      Logic:

      \

      Logged MAF voltage and AFR % error are fitted to your current MAF scaling and new scaling is suggested.
      \ -

      The raw AFR is calculated as wideband AFR / ((100 - (LTFT + STFT)) / 100). The AFR % error is calculated as (raw AFR - interpolated AFR) / interpolated AFR * 100), where interpolated AFR is interpolated from "Primary Open Loop Fueling" table or (raw AFR - commanded AFR) / commanded AFR * 100) if you log "Commanded Afr" and don't set "Primary Open Loop Fueling" table in the program.
      \ +

      The raw AFR is calculated as wideband AFR / ((100 - (LTFT + STFT)) / 100). The AFR % error is calculated as (raw AFR - interpolated AFR) / interpolated AFR * 100), where interpolated AFR is interpolated from "Primary Open Loop Fueling" table or (raw AFR - commanded AFR) / commanded AFR * 100) if you log "Commanded Afr" and set the column in settings.
      \

      The 'advantage' is that since a lot of the data points are between cells, it distributes the % error between cells based on how far your data point is from the cell value.
      \

      Say your maf was scaled like:
      \

      \ @@ -20,6 +20,7 @@ The idea is by distribution the data between cells you get a smoother scaling.Code:
      \
      MAFv: 0   1   2  3 4 5
      g/s: 0 2.5 7.5 0 0 0
      \ \ +Also, please note that collected data and correction applied may be less than the full MAF scaling voltage. In this case error correction is also applied to those table cells after the one corrected. The % error for those correction is calculated as average of 10 highest correction for the last cell. \
      \

      Usage:

      \ You really need to nail CL portion of your MAF scaling first!

      \ @@ -38,15 +39,15 @@ The idea is by distribution the data between cells you get a smoother scaling.Open your tune in RomRaider.\
    • Expand "Fueling - Primary Open Loop" tables group and copy "Primary Open Loop Fueling" table by clicking "Edit"->"Copy Table" into the first cell of "Primary Open Loop Fueling" table on the tool.
    • \
    • Click "Validate" button to make sure all is fine (validation is done on setting the table as default and saving it or on submitting the table so it's not required).
    • \ -
    • Most likely the "Primary Open Loop Fueling" table will stay the same, so for autoloading next time you can click on "Set Default" and save this table in a new file. If you do have another tune with a different "Primary Open Loop Fueling" table, you can click on "Clear" to clear out table, then paste data from second "Primary Open Loop Fueling" table, click on "Set Default" and save this table in a new file. To switch between different Primary Open Loop Fueling tables saved in files click on "Clear" to clear table, then click on "Load" to load data from file, then click on "Set Default" so that next time it will be loaded automatically.
    • \ +
    • Most likely the "Primary Open Loop Fueling" table will stay the same, so for auto-loading next time you can click on "Set Default" and save this table in a new file. If you do have another tune with a different "Primary Open Loop Fueling" table, you can click on "Clear" to clear out table, then paste data from second "Primary Open Loop Fueling" table, click on "Set Default" and save this table in a new file. To switch between different Primary Open Loop Fueling tables saved in files click on "Clear" to clear table, then click on "Load" to load data from file, then click on "Set Default" so that next time it will be loaded automatically.
    • \
    \ \ -
  • \If you did log "Commanded Afr" then you must clear "Primary Open Loop Fueling" table by clicking "POL Fueling" button and making sure that the table is empty. If it is not empty and can click on "Clear" button, and then on "OK" button. If you are logging "Commanded Afr" then also make sure that you don't have a default file set. Otherwise the "Primary Open Loop Fueling" table and interpolated values are used by default.
  • \ +
  • \If you did log "Commanded Afr" then you must set the column name in settings screen while loading log file. If that column is empty then "Primary Open Loop Fueling" table is used and values are interpolated from table (much slower).
  • \
  • Now you have three ways to proceed:
    \
      \
    • If you have WOT "MAF voltage" and "AFR Error %" from Airboy's spreadsheet then simply copy/paste "MAF voltage" and "AFR Error %" data into corresponding columns' first cell. You can go up to 12 WOT pulls.
    • \
    • If you have saved data from previous run and would like to see the graphs or modify some data manually you can load that run using "Load" button.
    • \ -
    • If you just have ecu log file(s) with WOT pulls, then click on "Load Log" button, select your log file with WOT runs, select asked columns from log file AND set desired filters values when prompted. Please pay attention to filters on some columns to filter out noisy data. Once the log file is processed you should see WOT pull tables on the tool populated with data. If you have more log files just repeat this procedure until all 12 tables are loaded.
    • \ +
    • If you just have log file(s) with WOT pulls, then click on "Load Log" button, select your log file with WOT runs, select asked columns from log file AND set desired filters values when prompted. Please pay attention to filters on some columns to filter out noisy data. Once the log file is processed you should see WOT pull tables on the tool populated with data. If you have more log files just repeat this procedure until all 12 tables are loaded.
    • \
    \
  • \
  • At this point you can save all the input data for future reference or re-scaling by clicking on "Save" button at the top bar.
  • \ @@ -70,17 +71,20 @@ The idea is by distribution the data between cells you get a smoother scaling.\
  • quick - straight values copy
    \
      \ -
    1. Simply copy the newly generated "Airflow (g/s)" values from results table into the "MAF Sensor Scaling" table in your tuning tool (eg RommRaider).
    2. \ +
    3. Simply copy the newly generated "Airflow (g/s)" values from results table into the "MAF Sensor Scaling" table in your tuning tool (eg RomRaider).
    4. \
    \
  • \ -
  • slow - apply smoothing or manual changes. To apply smoothing to the Corrected Maf Scaling curve or to make manual changes (only for people who really know what they are doing!!!) click on "Smoothing" checkbox. The chart view will display Smoothed Maf curve together with current MAF curve slope versus the smoothed MAF curve slope. At the bottom of the screen there is resulting MAF scaling table with controls that allow making changes to the table (and thus the MAF curve). Row 1 of the result table corresponds to "MAF Sensor (volts)" and row 2 of the "Smoothing" tab corresponds to "Airflow (g/s)".
    \ +
  • slow (optional) - apply smoothing or manual changes. To apply smoothing to the Corrected Maf Scaling curve or to make manual changes (only for people who really know what they are doing!!!) click on "Smoothing" checkbox. The chart view will display Smoothed Maf curve(blue) together with Current MAF curve slope line(red) and the Smoothed MAF curve slope line(green). At the bottom of the screen there is resulting MAF scaling table with controls that allow making changes to the table (and thus the MAF curve). Row 1 of the result table corresponds to "MAF Sensor (volts)" and row 2 of the "Smoothing" tab corresponds to "Airflow (g/s)".
    \ +

    Slope line is a line of MAF Curve slope values. Slope value is a number which is used to indicate the steepness of a curve at a particular point. Slope value for MAF Curve is calculated between each 2 points of the curve as: \ +

    Code:
    slope = (y2 - y1) / (x2 - x1)
    Since the MAF curve is non-linear you see that the slope of the curve changes as you move along it.

    \
      \
    1. All smoothing changes are immediate and can be seen on the smoothing graph.
    2. \
    3. If you have overdone smoothing, simply click on "Reset" button to set the smoothed curve back to corrected curve.
    4. \
    5. You can apply smoothing by selecting the cells in MAF scaling table and the smoothing degree and then clicking "Apply" button. Smoothing degree is simply a moving window size where 3 - uses 1 cell to left and right, 5 - uses 2 cells to left and right, and 7 - uses 3 cells to left and right.
    6. \
    7. Please note that due to how smoothing algorithm works one, two, or three (depending on smoothing degree you chose) the first cells and last cell will not get modified. You can raise/lower those manually if needed by the same value the cell after or cell before was modified.
    8. \
    9. BE CAREFULL not to smooth too much or you will end up making your MAF scaling worse. General suggestion is to apply either degree "3" or "5" once.
    10. \ -
    11. In addition, you can manually smoothen values by either dragging the smoothed curve's point or by applying +/-0.10 to the highlighted cells using buttons on the left from the result table. Keep in mind that selection all cells will raise or lower the whole curve. Thus you can raise certain parts of the curve by selecting appropriate cells.
    12. \ +
    13. In addition, you can manually smooth values by either dragging the smoothed curve slope line's point or by applying custom +/- value to the highlighted cells using buttons on the left from the result table. Keep in mind that selection of all cells will raise or lower the whole curve. Thus you can raise certain parts of the curve by selecting appropriate cells.
    14. \ +
    15. Finally, there is a compare screen. You copy original MAF scaling and the new MAF scaling and see change % difference. You can also manually alter change % difference if you feel it's needed and it will modify you new MAF scaling.
    16. \
    17. Once you have smoothed the data to your liking, copy the data to your "MAF Sensor Scaling" table.
    18. \
    \
  • \ diff --git a/src/com/vgi/mafscaling/rescale.properties b/src/com/vgi/mafscaling/rescale.properties new file mode 100644 index 0000000..5c4c362 --- /dev/null +++ b/src/com/vgi/mafscaling/rescale.properties @@ -0,0 +1,32 @@ +usage=\ +\ +

    Credits:

    \ +This tool development has been driven by Kodename47.\ +

    Logic:

    \ +

    The tool is about changing the MAF scale but keeping the curve the same. As with all mapping it's a resolution trade off. \ +The larger the scale, the less accurate that it is. If you're NA, the likelihood that the maximum voltage you are logging \ +at max RPM is little over 4.1/4.2v. Therefore the scale going all the way to 5v is a waste of potentially better resolution. \ +The same applies to several points at the start of the curve. You can only use the same amount of data points so by closing \ +the gaps, you're improving the accuracy due the way the ECU interpolates the cells. So by lowering maximum V value and removing \ +some unused V down low the new scale increases resolution for those cell where difference between points starts increasing and \ +therefore much more accurate.\ +

    The logic is as follow:
    \ +

    First the point grater or equal to Min V is found and mid point is calculated between that point and the first point - reference point. \ +This is done in case your MAFv drops into the low range of the MAF scaling, the three points should provide a fair resolution.\ +

    Mode of spacing between points is calculated and Max Unchanged value determined as the point after which spacing starts increasing from Mode. \ +The Max Unchanged value though can be reset from suggested by the user.\ +

    Once the original MAF scale is pasted the new MAF voltage scale is calculated where first point is unchanged, second point is the mid point between first and third point, \ +third point is point greater or equal to Min V, then original values are copied until Max Unchanged point, and finally rescaled points calculated based on original \ +scaling % increase between sections of two points.\ +

    After the new MAF Voltage values are calculated, for each MAF Voltage cell a new GS value is interpolated from the original curve for the new MafV using linear interpolation.\ +

    Usage:

    \ +
      \ +
    1. Put the new maximum desired voltage into the text box.
    2. \ +
    3. Put the new minimum voltage into the text box.
    4. \ +
    5. Paste original MAF scale into the top table.
    6. \ +
    7. Modify Unchanged MaxV if desired or paste different MafV values into new MAF scaling or modify manually to adjust the curve if needed.
    8. \ +
    9. Copy the data to your "MAF Sensor Scaling" table.
    10. \ +
    11. Once you have copied your new MAF scaling values from the result table to your ROM, save a new ROM with the changes made.
    12. \ +
    13. Flash the modified ROM to your ECU.
    14. \ +
    \ +\