PrintWriter ignores sent lines except the last line

368 views Asked by At

I wrote a Java Swing program which listens at the serial port (I use JSerialComm library). The program should display the received data in the console (works already), in a textarea-field (works, too) and in a file (does not work).

The receiving process starts by pressing on a button and end by pressing on another button.

I use the SwingWorker class to put GUI changes in the event dispatch thread. The configuration, opening and reading from the serial port happens in the doInBackground() method. The reading is an endless while-loop which is embedded in another while-loop who tests for isCancelled(). The cancel action is triggert by pressing on a button in the GUI.

The output in the textarea and the console takes place in the process() method. The done() method just do some enabling/disabling GUI elements and stores the PrintWriter close() method.

For writing in the file I use a FileWriter/PrintWriter (in process() method).

The result: I get the last sent line in the file. The previos sent line are ignored (in the file only). E.g. I send "one" "two" "three" through the serial connection via terminal program (line end = LF, CR or CR-LF makes no difference). I just receive the line "three" in the file. On the GUI and console I received the complete 3 strings as expected.

My relevant shortened code (Whole code at GitHub, branch "logfile"):

public class SerialPrinter extends JFrame {

    PrintWriter pw;

    private class SerialReadTask extends SwingWorker<Boolean, String> {
        protected Boolean doInBackground() {
            // [Omitted code] Get values from the GUI an open serial port ....
            // Reading serial data
            Scanner serialScanner = new Scanner(chosenPort.getInputStream());
            while (!isCancelled()) {
                while (serialScanner.hasNextLine()) {
                    String line = serialScanner.nextLine();
                    publish(line);
                }
            }
            return false; // Returns true if cannot open serial port (code not here)
        }

        protected void process(List<String> chunk) {
            try {
                pw = new PrintWriter(new FileWriter(tf_Logfile.getText()));
                for (String line : chunk) {
                    pw.println(line); // save to file
                    ta_VirtualPrint.append(line + "\n"); // display in GUI => WORKS!!!
                    System.out.println(line); // print on console => WORKS!!!
                }
            } catch (IOException ex) {
            }
        }

        protected void done() {
            // [Omitted code] Disabling some GUI elemts. Usage of get()<Boolean>    
            if (pw != null) { // if PrintWriter bw exists
                pw.close(); // Close but just get the last submitted line in the file
            }
        }

        private void btn_OpenPortActionPerformed(ActionEvent e) [
            serialReader.execute()
        }

        private void btn_ClosePortActionPerformed(ActionEvent e) [
            serialReader.cancel()
        }
}

Hope you have an idea. Thank you in advance,

Hani

2

There are 2 answers

2
Arnaud On

You are calling publish for each line :

publish(line);

So each time the file is overwritten with a single line, and you end up with the last read line written.

You may consider populating a List<String> will all read lines, then convert the list to an array, and call publish with this array.

    while (!isCancelled()) {

        List<String> data = new ArrayList<String>();

        while (serialScanner.hasNextLine()) {
            String line = serialScanner.nextLine();
            data.add(line);
        }

        publish(data.toArray(new String[data.size()]));
    }

Alternatively you may keep your code as is, and just create your FileWriter in append mode :

pw = new PrintWriter(new FileWriter(tf_Logfile.getText(), true));
0
Hani On

I put the File/PrintWriter in the background task instead of the EDT (progress()) task, see code below. Added lines are marked with // NEW:

protected Boolean doInBackground() throws IOException { // NEW Exception 
            // [Omitted code] Get values from the GUI an open serial port ....
            // Reading serial data
            pw = new PrintWriter(new FileWriter(tf_Logfile.getText())); // NEW
            Scanner serialScanner = new Scanner(chosenPort.getInputStream());
            while (!isCancelled()) {
                while (serialScanner.hasNextLine()) {
                    String line = serialScanner.nextLine();
                    publish(line);
                    pw.println(line); // NEW
                }
            }
            return false; // Returns true if cannot open serial port (code not here)
        }

The pw.close() happens still in the done() method.

Anyway, as I mentioned before this looks more or less as an workaround, because I haven't understood why the first approach does not work for files but for screen output.

Anyway, after som refactoring => GitHub