417 lines
12 KiB
Java
Executable File
417 lines
12 KiB
Java
Executable File
/*
|
|
* (C) Copyright 2015 by fr3ts0n <erwin.scheuch-heilig@gmx.at>
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*/
|
|
|
|
package com.fr3ts0n.ecu.gui.application;
|
|
|
|
import com.fr3ts0n.ecu.EcuDataPv;
|
|
import com.fr3ts0n.pvs.ProcessVar;
|
|
import com.fr3ts0n.pvs.PvChangeEvent;
|
|
import com.fr3ts0n.pvs.PvChangeListener;
|
|
import com.fr3ts0n.pvs.PvList;
|
|
|
|
import org.jfree.data.time.Second;
|
|
import org.jfree.data.time.TimeSeries;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.logging.Level;
|
|
|
|
import javax.swing.JPanel;
|
|
import javax.swing.event.ListSelectionEvent;
|
|
import javax.swing.event.ListSelectionListener;
|
|
|
|
|
|
/**
|
|
* Panel to combine tabular- and graphical display for OBD data
|
|
*
|
|
* @author erwin
|
|
*/
|
|
public class ObdDataPanel extends JPanel
|
|
implements PvChangeListener, ListSelectionListener
|
|
{
|
|
/**
|
|
*
|
|
*/
|
|
private static final long serialVersionUID = 4441869977997912421L;
|
|
// selectable PID's'
|
|
HashMap<Object, TimeSeries> selPids = new HashMap<>();
|
|
|
|
/** Creates new form ObdGraphPanel */
|
|
public ObdDataPanel()
|
|
{
|
|
initComponents();
|
|
tblPids.setPvModel(new ObdItemTableModel());
|
|
tblPids.getSelectionModel().addListSelectionListener(this);
|
|
tblPids.setDefaultRenderer(EcuDataPv.class, new ObdItemTableRenderer());
|
|
}
|
|
|
|
/**
|
|
* Creates new form ObdGraphPanel
|
|
*
|
|
* @param pvList List of Process vars (PIDS) to display
|
|
*/
|
|
public ObdDataPanel(PvList pvList)
|
|
{
|
|
initComponents();
|
|
tblPids.setPvModel(new ObdItemTableModel());
|
|
tblPids.getSelectionModel().addListSelectionListener(this);
|
|
tblPids.setDefaultRenderer(EcuDataPv.class, new ObdItemTableRenderer());
|
|
setPidPvs(pvList);
|
|
}
|
|
|
|
/**
|
|
* This method is called from within the constructor to
|
|
* initialize the form.
|
|
* WARNING: Do NOT modify this code. The content of this method is
|
|
* always regenerated by the Form Editor.
|
|
*/
|
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
|
private void initComponents()
|
|
{
|
|
java.awt.GridBagConstraints gridBagConstraints;
|
|
|
|
javax.swing.JSplitPane jSplitPane1 = new javax.swing.JSplitPane();
|
|
JPanel panGraph = new JPanel();
|
|
plotter = new com.fr3ts0n.ecu.gui.application.ObdDataPlotter();
|
|
JPanel jPanel1 = new JPanel();
|
|
JPanel jPanel2 = new JPanel();
|
|
slGraphTime = new javax.swing.JSlider();
|
|
lblHistTime = new javax.swing.JLabel();
|
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
|
javax.swing.JButton btnClearHist = new javax.swing.JButton();
|
|
JPanel panTable = new JPanel();
|
|
javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
|
|
tblPids = new com.fr3ts0n.pvs.gui.PvTable();
|
|
|
|
setFont(new java.awt.Font("Dialog", 0, 10));
|
|
setName("DataPanel"); // NOI18N
|
|
setLayout(new java.awt.BorderLayout());
|
|
|
|
jSplitPane1.setDividerLocation(0);
|
|
jSplitPane1.setDividerSize(8);
|
|
jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
|
|
jSplitPane1.setResizeWeight(0.5);
|
|
jSplitPane1.setToolTipText("Move to show graph display");
|
|
jSplitPane1.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
jSplitPane1.setName("splitter"); // NOI18N
|
|
jSplitPane1.setOneTouchExpandable(true);
|
|
|
|
panGraph.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
panGraph.setLayout(new java.awt.BorderLayout());
|
|
|
|
plotter.setToolTipText("Select data items in Table to activate graphing");
|
|
plotter.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
plotter.setName("plotter"); // NOI18N
|
|
panGraph.add(plotter, java.awt.BorderLayout.CENTER);
|
|
|
|
jPanel1.setFont(new java.awt.Font("Dialog", 0, 10)); // NOI18N
|
|
jPanel1.setLayout(new java.awt.BorderLayout());
|
|
|
|
jPanel2.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
|
|
jPanel2.setToolTipText("maximum history time in minutes");
|
|
jPanel2.setLayout(new java.awt.GridBagLayout());
|
|
|
|
slGraphTime.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
slGraphTime.setMaximum(180);
|
|
slGraphTime.setValue(180);
|
|
slGraphTime.addChangeListener(new javax.swing.event.ChangeListener()
|
|
{
|
|
public void stateChanged(javax.swing.event.ChangeEvent evt)
|
|
{
|
|
slGraphTimeStateChanged();
|
|
}
|
|
});
|
|
gridBagConstraints = new java.awt.GridBagConstraints();
|
|
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
|
gridBagConstraints.weightx = 1.0;
|
|
jPanel2.add(slGraphTime, gridBagConstraints);
|
|
|
|
lblHistTime.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
lblHistTime.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
|
lblHistTime.setText(String.valueOf(slGraphTime.getValue()) + " min");
|
|
lblHistTime.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 3, 1, 3));
|
|
lblHistTime.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
|
gridBagConstraints = new java.awt.GridBagConstraints();
|
|
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
|
jPanel2.add(lblHistTime, gridBagConstraints);
|
|
|
|
jPanel1.add(jPanel2, java.awt.BorderLayout.CENTER);
|
|
|
|
btnClearHist.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
btnClearHist.setMnemonic('C');
|
|
btnClearHist.setText("Clear History");
|
|
btnClearHist.setToolTipText("Clear history buffer for graphing");
|
|
btnClearHist.addActionListener(new java.awt.event.ActionListener()
|
|
{
|
|
public void actionPerformed(java.awt.event.ActionEvent evt)
|
|
{
|
|
btnClearHistActionPerformed();
|
|
}
|
|
});
|
|
jPanel1.add(btnClearHist, java.awt.BorderLayout.EAST);
|
|
|
|
panGraph.add(jPanel1, java.awt.BorderLayout.SOUTH);
|
|
|
|
jSplitPane1.setLeftComponent(panGraph);
|
|
|
|
panTable.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
panTable.setLayout(new java.awt.BorderLayout());
|
|
|
|
tblPids.setAutoResizeMode(5);
|
|
tblPids.setFont(new java.awt.Font("Dialog", 0, 10));
|
|
tblPids.setName("DataTable"); // NOI18N
|
|
tblPids.setShowGrid(true);
|
|
tblPids.setToolTipText("Select items to start graphing");
|
|
jScrollPane1.setViewportView(tblPids);
|
|
|
|
panTable.add(jScrollPane1, java.awt.BorderLayout.CENTER);
|
|
|
|
jSplitPane1.setBottomComponent(panTable);
|
|
|
|
add(jSplitPane1, java.awt.BorderLayout.CENTER);
|
|
}// </editor-fold>//GEN-END:initComponents
|
|
|
|
private void btnClearHistActionPerformed()//GEN-FIRST:event_btnClearHistActionPerformed
|
|
{//GEN-HEADEREND:event_btnClearHistActionPerformed
|
|
TimeSeries ts;
|
|
Iterator<TimeSeries> it = selPids.values().iterator();
|
|
while (it.hasNext())
|
|
{
|
|
ts = it.next();
|
|
ts.clear();
|
|
}
|
|
}//GEN-LAST:event_btnClearHistActionPerformed
|
|
|
|
private void slGraphTimeStateChanged()//GEN-FIRST:event_slGraphTimeStateChanged
|
|
{//GEN-HEADEREND:event_slGraphTimeStateChanged
|
|
TimeSeries ts;
|
|
lblHistTime.setText(String.valueOf(slGraphTime.getValue()) + " min");
|
|
if (!slGraphTime.getValueIsAdjusting())
|
|
{
|
|
Iterator<TimeSeries> it = selPids.values().iterator();
|
|
while (it.hasNext())
|
|
{
|
|
ts = it.next();
|
|
ts.setMaximumItemAge(slGraphTime.getValue() * 60);
|
|
}
|
|
}
|
|
}//GEN-LAST:event_slGraphTimeStateChanged
|
|
|
|
/**
|
|
* get readable unique String from PV
|
|
*/
|
|
private Object getPvId(ProcessVar pv)
|
|
{
|
|
return (pv.getKeyValue());
|
|
}
|
|
|
|
|
|
private javax.swing.JLabel lblHistTime;
|
|
private com.fr3ts0n.ecu.gui.application.ObdDataPlotter plotter;
|
|
private javax.swing.JSlider slGraphTime;
|
|
private com.fr3ts0n.pvs.gui.PvTable tblPids;
|
|
// End of variables declaration//GEN-END:variables
|
|
|
|
/**
|
|
* Holds value of property pidPvs.
|
|
*/
|
|
private PvList pidPvs;
|
|
|
|
/**
|
|
* Getter for property pidPvs.
|
|
*
|
|
* @return Value of property pidPvs.
|
|
*/
|
|
public PvList getPidPvs()
|
|
{
|
|
return this.pidPvs;
|
|
}
|
|
|
|
/**
|
|
* Setter for property pidPvs.
|
|
*
|
|
* @param pidPvs New value of property pidPvs.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public void setPidPvs(PvList pidPvs)
|
|
{
|
|
TimeSeries ts;
|
|
// if there is an o previous instance registered, unregister ...
|
|
if (this.pidPvs != null)
|
|
this.pidPvs.removePvChangeListener(this);
|
|
this.pidPvs = pidPvs;
|
|
tblPids.setProcessVar(pidPvs);
|
|
tblPids.setDefaultRenderer(Object.class, new ObdItemTableRenderer());
|
|
pidPvs.addPvChangeListener(this);
|
|
|
|
// update all TimeSeries with PIDs from PV-List
|
|
plotter.dataset.removeAllSeries();
|
|
selPids.clear();
|
|
Iterator<EcuDataPv> it = pidPvs.values().iterator();
|
|
while (it.hasNext())
|
|
{
|
|
EcuDataPv pv = it.next();
|
|
// create new data series
|
|
ts = new TimeSeries(String.valueOf(pv.get(EcuDataPv.FID_DESCRIPT)));
|
|
ts.setDescription(String.valueOf(pv.get(EcuDataPv.FID_DESCRIPT)));
|
|
ts.setRangeDescription(String.valueOf(pv.get(EcuDataPv.FID_UNITS)));
|
|
// set graph time of new element
|
|
ts.setMaximumItemAge(slGraphTime.getValue() * 60);
|
|
// and add to selectable PID list
|
|
selPids.put(getPvId(pv), ts);
|
|
}
|
|
updateColumnWidths();
|
|
}
|
|
|
|
/**
|
|
* set a new graph title
|
|
*
|
|
* @param newTitle new graph title
|
|
*/
|
|
public void setTitle(String newTitle)
|
|
{
|
|
plotter.setTitle(newTitle);
|
|
}
|
|
|
|
/**
|
|
* handle changes in the process var(s)
|
|
*
|
|
* @param event Process var event to be handled
|
|
*/
|
|
public void pvChanged(PvChangeEvent event)
|
|
{
|
|
TimeSeries ts;
|
|
EcuDataPv pv;
|
|
|
|
switch (event.getType())
|
|
{
|
|
case PvChangeEvent.PV_MODIFIED:
|
|
pv = (EcuDataPv) event.getValue();
|
|
if ((ts = selPids.get(getPvId(pv))) != null)
|
|
try
|
|
{
|
|
ts.addOrUpdate(new Second(), ((Number)pv.get(EcuDataPv.FID_VALUE)).floatValue());
|
|
} catch (Exception e)
|
|
{
|
|
ProcessVar.log.log(Level.SEVERE, "", e);
|
|
}
|
|
break;
|
|
|
|
case PvChangeEvent.PV_DELETED:
|
|
// remove from selectable PIDs
|
|
pv = (EcuDataPv) event.getValue();
|
|
selPids.remove(pv);
|
|
break;
|
|
|
|
case PvChangeEvent.PV_CLEARED:
|
|
// remove all from selectable PIDs
|
|
selPids.clear();
|
|
break;
|
|
|
|
case PvChangeEvent.PV_ADDED:
|
|
if ((event.getValue() instanceof EcuDataPv))
|
|
{
|
|
pv = (EcuDataPv) event.getValue();
|
|
addDataSeries(pv);
|
|
} else if ((event.getValue() instanceof Object[]))
|
|
{
|
|
for (Object currPv : (Object[]) event.getValue())
|
|
{
|
|
addDataSeries((ProcessVar) currPv);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// update table column widths
|
|
updateColumnWidths();
|
|
}
|
|
|
|
|
|
private void addDataSeries(ProcessVar pv)
|
|
{
|
|
TimeSeries ts;
|
|
// create new data series
|
|
ts = new TimeSeries(String.valueOf(pv.get(EcuDataPv.FID_DESCRIPT)),
|
|
null,
|
|
String.valueOf(pv.get(EcuDataPv.FID_UNITS)));
|
|
// set graph time of new element
|
|
ts.setMaximumItemAge(slGraphTime.getValue() * 60);
|
|
// and add to selectable PID list
|
|
selPids.put(getPvId(pv), ts);
|
|
}
|
|
|
|
/**
|
|
* update the column widths of data table
|
|
*/
|
|
private void updateColumnWidths()
|
|
{
|
|
if (tblPids.getRowCount() >= 1)
|
|
{
|
|
/** set column sizes here, since this only works with inserted data */
|
|
tblPids.getColumn(EcuDataPv.FIELDS[EcuDataPv.FID_PID]).setPreferredWidth(40);
|
|
tblPids.getColumn(EcuDataPv.FIELDS[EcuDataPv.FID_OFS]).setPreferredWidth(40);
|
|
tblPids.getColumn(EcuDataPv.FIELDS[EcuDataPv.FID_DESCRIPT]).setPreferredWidth(350);
|
|
tblPids.getColumn(EcuDataPv.FIELDS[EcuDataPv.FID_VALUE]).setPreferredWidth(150);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* update specified column of all available table rows
|
|
*
|
|
* @param columnId - column id to be updated
|
|
*/
|
|
public void updateAllTableRows(int columnId)
|
|
{
|
|
((ObdItemTableModel) tblPids.getPvModel()).updateAllRows();
|
|
}
|
|
|
|
/**
|
|
* handle change in table selection
|
|
* activate / deactivate graphing of selected data items
|
|
*/
|
|
public void valueChanged(ListSelectionEvent e)
|
|
{
|
|
TimeSeries ts;
|
|
int[] selIDs;
|
|
|
|
// clean up dataset
|
|
plotter.removeAllSeries();
|
|
// get selected items as list of data
|
|
// rather than array of ID's'
|
|
selIDs = tblPids.getSelectedRows();
|
|
for (int i = 0; i < selIDs.length; i++)
|
|
{
|
|
/* since table model changed, it returns the complete object for any field */
|
|
EcuDataPv currPid = (EcuDataPv) tblPids.getModel().getValueAt(selIDs[i], EcuDataPv.FID_PID);
|
|
ts = selPids.get(currPid.get(EcuDataPv.FID_PID));
|
|
// if Series found
|
|
if (ts != null)
|
|
{
|
|
// add new series to plotter ..
|
|
plotter.addSeries(ts);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString()
|
|
{
|
|
return (this.getName());
|
|
}
|
|
}
|