1

I am attempting to read data through a serial port using SwingWorker. However, upon executing this code I receive no output. By adding the line

JOptionPane.showConfirmDialog(null, "Cancel", "Cancel this task?", JOptionPane.DEFAULT_OPTION);

As suggested in SwingWorker not executing doInBackGround() I only receive the outputs "Reading" /n "Done", and my getPortText method is not invoked. The IF block that is meant to display available ports also does not execute.

The fazecast SerialComm JAR can be found at http://fazecast.github.io/jSerialComm/

All help is greatly appreciated, I've been stuck on this for quite a few days!

import java.util.List;
import java.util.Scanner;

import javax.swing.JOptionPane;
import javax.swing.SwingWorker;

import com.fazecast.jSerialComm.SerialPort;

public class PortWorkerCheck {

public static void main(String[] args) {
    Worker worker = new Worker();
    worker.execute();
}

public static void getPortText(String dir) {
    System.out.println(dir);
}

static class Worker extends SwingWorker<Void, String>{
    boolean portcheck = false;
    SerialPort comPort[] = SerialPort.getCommPorts();
    SerialPort port;
    protected void done() {
        System.out.println("Done");
    }

    @Override
    protected Void doInBackground() throws Exception {
        String data;
     //This if block attempts to list available ports and have the user select one.
        if (portcheck = false) {
            int i = 1;
            System.out.println("Select a port:");
            for(SerialPort ports : comPort) {
                System.out.println(i++ + ". " + ports.getSystemPortName());
            }

            Scanner s = new Scanner(System.in);
            int chosenPort = s.nextInt();
            port = comPort[chosenPort - 1];
            portcheck = true;
            if(port.openPort()) {
                System.out.println("Port opened successfully");
            }
            port.setComPortTimeouts(SerialPort.TIMEOUT_SCANNER, 0, 0);
        }

        publish("Reading");
        Scanner read = new Scanner(port.getInputStream());
        data = read.nextLine();
        publish(data);
        return null;
    }

    //My process should call getPortText, which should then display the received data.
    @Override
    protected void process(List<String> chunks) {
        for(String line : chunks) {
        portcheck = true;
        PortWorkerCheck.getPortText(line);
        }
    }
}

}

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
J Lag
  • 25
  • 4
  • 1
    You don't have a Swing GUI running, and so this code risks exiting when the main method exits. You need to display a Swing GUI for the Swing thread to continue to run. – Hovercraft Full Of Eels Apr 13 '18 at 01:23
  • Also, are you only supposed to read in one line of data? – Hovercraft Full Of Eels Apr 13 '18 at 01:23
  • 2
    .... AND this line is awful: `if (portcheck = false) {`. Never set a boolean like that. Perhaps do `if (portcheck == false) {` or much better `if (!portcheck) {` – Hovercraft Full Of Eels Apr 13 '18 at 01:24
  • The data is streamed to the port as a series of single lines, if you recall the previous question that you helped me on: https://stackoverflow.com/questions/49724665/using-jframe-with-a-continuous-input-stream – J Lag Apr 13 '18 at 01:26
  • Your code only tries to read **one** line though. And again, please do change that improper if block. You're setting the boolean to false with that line -- not good. – Hovercraft Full Of Eels Apr 13 '18 at 01:35
  • I have a more complete code that includes a JFrame display, with the getPortText method changing the state of some fields. I put the worker.execute() into a while(true) loop and nested it into a try/catch block, and fixed that nasty if block. I can create a new question with that code and link it here if you can help me further. – J Lag Apr 13 '18 at 01:46
  • When running it like that, the fields do not update and I don't receive a system.out.println that should represent the data returned from my SwingWorker. The JFrame draws properly and I don't receive an error message, but I don't think the worker is communicating properly to my GUI. – J Lag Apr 13 '18 at 01:48
  • No, no no don't do that – Hovercraft Full Of Eels Apr 13 '18 at 01:48
  • Please see edits to answer. If this doesn't work, then please create and post a valid [mcve], one that creates mock classes that mimic the actions of your SerialPort classes. – Hovercraft Full Of Eels Apr 13 '18 at 02:24

1 Answers1

2

Problems:

  • Your posted code does not create a Swing GUI, and so the Swing event thread is never started. This means that when your main method exits and any other non-daemon threads exit, your program exits. That publish/process method may die before it does anything useful. Solution: don't play with SwingWorkers without a Swing GUI. Create both and connect them.
  • Your code only tries to read one line of data from the port, and then quits.You need a while loop attached to the Scanner that reads the port
  • Your if block boolean test is wrong (as per comments). Never do an if test in that way: if (portcheck = false) {. Instead do it safely: if (!portcheck) {
  • all the code within the if block should not be present. You shouldn't mix Swing event-driven programs with console-based Scanner. The data should be obtained via the Swing GUI.
  • You state: "I put the worker.execute() into a while(true) loop" -- no, don't do this. You don't repeatedly execute the worker but rather execute it only once. It's a use-once then throw out type of object. Instead the while loop goes inside the worker where the Scanner is getting data.

Something like:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.InputStream;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

// not a class that I'm familiar with
import com.fazecast.jSerialComm.SerialPort;

public class PortWorkerCheckPanel extends JPanel {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            PortWorkerCheckPanel portCheckPanel = new PortWorkerCheckPanel();
            JFrame frame = new JFrame("Port Check");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(portCheckPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

    private JTextArea textArea = new JTextArea(20, 50);
    private StartWorkerAction startAction = new StartWorkerAction(this);

    public PortWorkerCheckPanel() {
        textArea.setFocusable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        JPanel btnPanel = new JPanel();
        btnPanel.add(new JButton(startAction));

        setLayout(new BorderLayout());
        add(scrollPane);
        add(btnPanel, BorderLayout.PAGE_END);
    }

    public void appendPortText(String text) {
        textArea.append(text + "\n");
    }

    private static class StartWorkerAction extends AbstractAction {
        private PortWorkerCheckPanel portCheckPanel;

        public StartWorkerAction(PortWorkerCheckPanel portCheckPanel) {
            super("Start Worker");
            this.portCheckPanel = portCheckPanel;
        }

        @Override
        public void actionPerformed(ActionEvent arg0) {
            Worker worker = new Worker(portCheckPanel);
            worker.addPropertyChangeListener(new WorkerListener());
            worker.execute();
        }
    }

    static class WorkerListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                Worker worker = (Worker) evt.getSource();
                try {
                    worker.get();
                } catch (InterruptedException | ExecutionException e) {
                    // TODO handle any exceptions here
                    e.printStackTrace();
                }
            }
        }
    }

    static class Worker extends SwingWorker<Void, String> {
        boolean portcheck = false;

        // Note that I have no idea if this part, the SerialPort code, is
        // correct
        // as I do not use this utility
        SerialPort comPort[] = SerialPort.getCommPorts();
        SerialPort port;
        private PortWorkerCheckPanel portCheckPanel;

        public Worker(PortWorkerCheckPanel portCheckPanel) {
            this.portCheckPanel = portCheckPanel;
        }

        protected void done() {
            System.out.println("Done");
        }

        @Override
        protected Void doInBackground() throws Exception {
            String data;

            // this code in the if block should all be done interactively in the
            // GUI,
            // not using a console-based Scanner the information should then be
            // passed
            // into this worker via a constructor parameter
            if (!portcheck) {
                int i = 1;
                System.out.println("Select a port:");
                for (SerialPort ports : comPort) {
                    System.out.println(i++ + ". " + ports.getSystemPortName());
                }

                Scanner s = new Scanner(System.in);
                int chosenPort = s.nextInt();
                port = comPort[chosenPort - 1];
                portcheck = true;
                if (port.openPort()) {
                    System.out.println("Port opened successfully");
                }
                port.setComPortTimeouts(SerialPort.TIMEOUT_SCANNER, 0, 0);
            }

            publish("Reading");
            Scanner read = new Scanner(port.getInputStream());

            while (read.hasNextLine()) {
                data = read.nextLine();
                publish(data);
            }
            if (read != null) {
                read.close();
            }
            return null;
        }

        // My process should call getPortText, which should then display the
        // received data.
        @Override
        protected void process(List<String> chunks) {
            for (String line : chunks) {
                portcheck = true;
                // PortWorkerCheckPanel.getPortText(line);
                portCheckPanel.appendPortText(line);
            }
        }
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373