/*
* (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 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<Object, TimeSeries>();
/** 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;
jSplitPane1 = new javax.swing.JSplitPane();
panGraph = new javax.swing.JPanel();
plotter = new com.fr3ts0n.ecu.gui.application.ObdDataPlotter();
jPanel1 = new javax.swing.JPanel();
jPanel2 = new javax.swing.JPanel();
slGraphTime = new javax.swing.JSlider();
lblHistTime = new javax.swing.JLabel();
btnClearHist = new javax.swing.JButton();
panTable = new javax.swing.JPanel();
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(evt);
}
});
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(evt);
}
});
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(java.awt.event.ActionEvent evt)//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(javax.swing.event.ChangeEvent evt)//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
*/
Object getPvId(ProcessVar pv)
{
return (pv.getKeyValue());
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnClearHist;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JLabel lblHistTime;
private javax.swing.JPanel panGraph;
private javax.swing.JPanel panTable;
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 instanceof PvList)
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.error(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(columnId);
}
/**
* 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());
}
}