/* * (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()); } }