Search code examples
jfreechart

JFreeChart "real-time" line graph with two lines


I am using JFreeChart to display data gathered from two sensors on an arduino using RXTX in "real-time". There is two values separated by a comma being sent from the arduino. I can successfully plot the data from one sensor on the graph but I am unable to add a second line for the data from the second sensor. Do anyone know how I can add the second line to the graph ?

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import javax.swing.JOptionPane;


public class respiratorytest2 extends javax.swing.JFrame implements SerialPortEventListener {

    public static float[][] C_A_Data = new float[120][2];
    public static int count = 0;
    public static respiratorytest2 demo;
    public static float[ ]dataArray = new float[600];
    public float data1;
    public float data2;
    public static Connection conn;
    public static PreparedStatement stmt = null;
    public static String sql;
    public static int result=0;
    public static int result2;
    public static String gusername;


    /**
     * The number of subplots.
     */
    public static final int SUBPLOT_COUNT = 3;

    /**
     * The datasets.
     */
    private TimeSeriesCollection[] datasets;

    /**
     * The most recent value added to series 1.
     */
    private double[] lastValue = new double[SUBPLOT_COUNT];
    private double[] lastValue2 = new double[SUBPLOT_COUNT];

    SerialPort serialPort;
    /**
     * The port we're normally going to use.
     */
    private static final String PORT_NAMES[] = {
        "/dev/tty.usbserial-A9007UX1", // Mac OS X
        "/dev/ttyUSB0", // Linux
        "COM3", // Windows
    };
    /**
     * A BufferedReader which will be fed by a InputStreamReader converting the
     * bytes into characters making the displayed results codepage independent
     */
    private BufferedReader input;
    /**
     * The output stream to the port
     */
    private OutputStream output;
    /**
     * Milliseconds to block while waiting for port open
     */
    private static final int TIME_OUT = 2000;
    /**
     * Default bits per second for COM port.
     */
    private static final int DATA_RATE = 9600;

    public static void visibility(){
        demo.setVisible(true);
    }

    public respiratorytest2(String title) {
        super(title);
        initialize();

        final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new DateAxis("Time (sec)"));
        this.datasets = new TimeSeriesCollection[SUBPLOT_COUNT];

        this.lastValue[0] = 100.0;
        this.lastValue2[0] = 100.0;
        final TimeSeries series = new TimeSeries("Volume ", Millisecond.class);
        final TimeSeries series2 = new TimeSeries("Volume ", Millisecond.class);
        this.datasets[0] = new TimeSeriesCollection(series);
        this.datasets[1] = new TimeSeriesCollection(series2);

        final NumberAxis rangeAxis = new NumberAxis("Volume");
        rangeAxis.setAutoRangeIncludesZero(false);
        final XYPlot subplot = new XYPlot(
                this.datasets[0], null, rangeAxis, new StandardXYItemRenderer()
        );

        subplot.setBackgroundPaint(Color.lightGray);
        subplot.setDomainGridlinePaint(Color.white);
        subplot.setRangeGridlinePaint(Color.white);
        plot.add(subplot);


        final JFreeChart chart = new JFreeChart("Respiratory Test", plot);
        chart.setBorderPaint(Color.black);
        chart.setBorderVisible(true);
        chart.setBackgroundPaint(Color.white);

        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.white);
        final ValueAxis axis = plot.getDomainAxis();
        axis.setAutoRange(true);
        axis.setFixedAutoRange(60000.0);  // 60 seconds

        final JPanel content = new JPanel(new BorderLayout());

        final ChartPanel chartPanel = new ChartPanel(chart);
        content.add(chartPanel);

        final JPanel buttonPanel = new JPanel(new FlowLayout());

        content.add(buttonPanel, BorderLayout.SOUTH);
        chartPanel.setPreferredSize(new java.awt.Dimension(500, 470));
        chartPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        setContentPane(content);

    }

    public void initialize() {
        CommPortIdentifier portId = null;
        Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

        //First, Find an instance of serial port as set in PORT_NAMES.
        while (portEnum.hasMoreElements()) {
            CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
            for (String portName : PORT_NAMES) {
                if (currPortId.getName().equals(portName)) {
                    portId = currPortId;
                    break;
                }
            }
        }
        if (portId == null) {
            System.out.println("Could not find COM port.");
            return;
        }

        try {
            // open serial port, and use class name for the appName.
            serialPort = (SerialPort) portId.open(this.getClass().getName(),
                    TIME_OUT);

            // set port parameters
            serialPort.setSerialPortParams(DATA_RATE,
                    SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1,
                    SerialPort.PARITY_NONE);

            // open the streams
            input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
            output = serialPort.getOutputStream();

            // add event listeners
            serialPort.addEventListener(this);
            serialPort.notifyOnDataAvailable(true);
        } catch (Exception e) {
            System.err.println("Exception=" + e.toString());
        }
    }

    /**
     * This should be called when you stop using the port. This will prevent
     * port locking on platforms like Linux.
     */
    public synchronized void close() {
        if (serialPort != null) {
            serialPort.removeEventListener();
            serialPort.close();
        }
    }

    /**
     * Handle an event on the serial port. Read the data and print it.
     */
    @Override
    public synchronized void serialEvent(SerialPortEvent oEvent) {
        int i=0,j=0,a,b;
        StringBuffer sq = new StringBuffer();
        java.util.Date utilDate = new java.util.Date();
        java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
        if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            try {
                String inputLine = input.readLine();
                System.out.println(inputLine);
                String[] dArray = inputLine.split(",");
                data1 = Float.valueOf(dArray[0]);
                data2 = Float.valueOf(dArray[1]);
                this.lastValue[0] = new Integer(dArray[0]).intValue();
                this.lastValue2[0] = new Integer(dArray[1]).intValue();
                //this.datasets[0].getSeries(0).add(new Millisecond(), this.lastValue[0]);
                //this.datasets[1].getSeries(0).addOrUpdate(new Millisecond(),this.lastValue2[0]);

            sq.append ("INSERT INTO U_DATA");
            sq.append("(USERNAME, SESSION_NUM, SESSION_DATE, CHEST_DATA, ABDOMINAL_DATA) ");
            sq.append("VALUES ( ");
            sq.append("?, ?, ?, ?, ?");
            sq.append(")");

            System.out.println("Creating statement...");
            try {
                //stmt = conn.createStatement();
                stmt = conn.prepareStatement(sq.toString());
                stmt.setString(1, gusername);
                stmt.setInt(2, result2);
                stmt.setDate(3, sqlDate);
                stmt.setFloat(4, data1);
                stmt.setFloat(5, data2);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
           try {
                result = stmt.executeUpdate();
                count = count + 1;
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
                  //STEP 6: Clean-up environment
           try {
                stmt.close();
            } catch (SQLException e) {
            }

            if (count == 400){
                JOptionPane.showMessageDialog(null,"You Have Successfully Completed The Test");
                conn.close();
                report.main(gusername);
                setVisible(false);
                //dispose();
                close();

                //respiratorytest.successicon();
             }

            } catch (Exception e) {
                System.err.println("Exception serialEvent=" + e.toString());
            }
        }
        // Ignore all the other eventTypes, but you should consider the other ones.
    }

    public static void main(String username) throws Exception {
        gusername = username;
        conn = DBman.main();
        Statement stmt2 = null;
        sql = "SELECT COUNT(DISTINCT SESSION_NUM) AS rnum FROM U_DATA WHERE USERNAME='"+gusername+"'";
        ResultSet rs;
        try {
            stmt2 = conn.createStatement();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            rs = stmt2.executeQuery(sql);
            rs.next();
            result2 = rs.getInt("rnum");
            result2 = result2 + 1;
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        demo = new respiratorytest2("Respiratory Test");
        demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(false);
    }
}

Solution

  • You can add a second series to the chart, as shown here using TimeSeriesCollection:

    image

    or here using DynamicTimeSeriesCollection:

    image

    Use java.swing.Timer to poll the serial port periodically or a SwingWorker to query the serial port in the background, as shown here and here.

    image