1
votes

I'm using the JSSC Serial Port package to exchange a data between my application and a device connected to the serial port. I made a simple application which can send identification message to the device and prints a response. I wanted it to be resistant to connection errors (for example after unplugging the cable during the communication).

Sequence of actions Init -> Open -> IDN -> Close works perfectly. When I unplug the cable after opening the port, I can't send a message (seems logical to me) and close the port (there's nothing to be closed anymore so I also understand that).

At the beginning I was surprised that I couldn't close the port after plugging the cable again but I discovered that after every connection of the device, OS gives it a different number which JSSC library use to handle the port. Fortunately, it turned out that simply opening the port after reconnection solves all the problems, because the application can send messages again.

Unfortunately, problems start when there were more attempts of disconnecting/connecting the cable while the port was opened. After every try the application works slower and slower to end at the state when no messages can be sent anymore. Another thing is that in that state all the serial ports (not only the one I try to communicate with) are blocked and I can't even access them through a terminal-like applications.

So the question is:

How can I make the application to be resistant to connection breaks without blocking ports and loosing performance?

I suppose it might be about some sort of resources, memory management or so but my knowledge of these thing isn't sufficient enough to solve the problem by myself.

Implementation of the communication:

import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;

class Connection implements SerialPortEventListener {
  private static final int SERIAL_PORT_BAUD_RATE = 9600;
  private static final int SERIAL_PORT_DATA_BITS_NUMBER = 8;
  private static final int SERIAL_PORT_STOP_BITS_NUMBER = 1;
  private static final int SERIAL_PORT_PARITY = 0;

  private SerialPort serialPort;
  private String portName;

  public Connection(String portName) {
    this.portName = portName;
  }

  public void open() throws SerialPortException {
    serialPort = new SerialPort(portName);
    serialPort.openPort();
    serialPort.setParams(SERIAL_PORT_BAUD_RATE,
            SERIAL_PORT_DATA_BITS_NUMBER,
            SERIAL_PORT_STOP_BITS_NUMBER,
            SERIAL_PORT_PARITY);
    serialPort.addEventListener(this);
  }

  public void close() throws SerialPortException {
    if (serialPort != null) {
      serialPort.closePort();
    }
  }

  public void simpleMessage() throws SerialPortException {
    if (serialPort != null) {
      serialPort.writeString("IDN");
    }
  }

  @Override
  public void serialEvent(SerialPortEvent serialPortEvent) {
    if (serialPortEvent.getEventType() == SerialPortEvent.RXCHAR) {
      try {
        System.out.println(serialPort.readString());
      } catch (SerialPortException e) {
        System.out.println("Error while reading a string.");
      }
    }
  }
}

Main class:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;

public class MainApplication extends Application{
  public static void main(String [] args) {
    Application.launch();
  }

  @Override
  public void start(Stage primaryStage) throws Exception {
    Pane rootPane = null;
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(MainApplication.class.getResource("front.fxml"));

    try{
      rootPane = loader.load();
    } catch (IOException exception) {
      exception.printStackTrace();
    }

    Scene scene = new Scene(rootPane);
    primaryStage.setScene(scene);
    primaryStage.show();
    primaryStage.setOnCloseRequest(event -> {
      Platform.exit();
      System.exit(0);
    });
  }
}

FXML code:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>

<StackPane fx:id="rootPane"
           xmlns="http://javafx.com/javafx/8.0.60"
           xmlns:fx="http://javafx.com/fxml/1"
           fx:controller="FrontController">
    <VBox>
        <Button text="Init" onAction="#init"/>
        <Button text="Open" onAction="#openPort"/>
        <Button text="Close" onAction="#closePort"/>
        <Button text="IDN" onAction="#idn"/>
        <Button text="Get Ports" onAction="#getPorts"/>
        <Button text="Is opened?" onAction="#isPortOpen"/>
    </VBox>
</StackPane>

Controller:

import javafx.fxml.FXML;
import jssc.SerialPortException;
import jssc.SerialPortList;
import java.util.Arrays;

public class FrontController {

  private Connection connection;

  @FXML
  private void init() {
    String portName = "COM4";
    connection = new Connection(portName);
  }

  @FXML
  private void openPort() {
    try {
      connection.open();
    } catch (SerialPortException e) {
      e.printStackTrace();
    }
  }

  @FXML
  private void closePort() {
    try {
      connection.close();
    } catch (SerialPortException e) {
      e.printStackTrace();
    }
  }

  @FXML
  private void idn() {
    try {
      connection.idnMessage();
    } catch (SerialPortException e) {
      e.printStackTrace();
    }
  }

  @FXML
  private void getPorts() {
    System.out.println(Arrays.toString(SerialPortList.getPortNames()));
  }

  @FXML
  private void isPortOpen() {
    System.out.println(connection.getSerialPort().isOpened());
  }
}
1

1 Answers

0
votes

Basically unplugging device without closing sometimes (infact most times in windows OS) leaves OS in inconsistent state. You need to do clean up when device is removed. Please note it is very important to close port even if device has been removed. We may leave the return value of close. This gives hint to OS that app is also not interested in device so OS has the liberty to release resources which we also want. So in a nutshell we need to create a thread which will monitor if our device is attached to system or not.