Ich habe ein simples serielles Logging Programm mit GUI geschrieben, dass derzeit auf Windows zuverlässig läuft.
Als serielle Bibliothek verwende ich JSerialComm 1.3.11.
Codebeispiel w.u. und Java Datei im Anhang. Zum Lesen verwende ich den thread-sicheren BufferedReader.
Das Problem:
Derzeit lasse ich den BufferedReader nicht explizit schließen, da dann alle Plattformen beim BufferedReader.close() hängen bleiben, auch Windows.
macOS hängt beim Abbruch des Loggens (Button CLOSE => Task.cancel(true)) beim BufferedReader.close(), oder wenn auskommentiert im portClose(). Bis zum Abbruch loggt es die Daten aber ohne Probleme.
GNU/Linux loggt beim ersten Durchlauf und auskommentierten BufferdReader.close() alle Daten wie gewünscht. Es verschluckt aber (beim auskommentierten BufferedReader.close()) beim zweiten Durchlauf (Port schließen und wieder öffnen ohne das Programm zwischenzeitlich zu beenden) die erste empfangene Zeile, die nachfolgenden Zeilen werden korrekt geloggt . Das passiert auch bei allen weiteren Durchläufen. Schließt man das Programm und öffnet es wieder, läuft der erste Loggerdurchlauf wieder ohne Probleme und bei allen weiteren wird die erste Zeile verschluckt.
Fehlervermutung:
Ich glaube es ist ein Threadingproblem mit dem SwingWorker und dem BufferedReader. Wenn ich den Reader in "done()" schließen will, bleiben alle Plattformen dort hängen. Sie laufen erst weiter, wenn ich von der anderen Seite mindestens ein Zeichen an den Logger sende. Dann sind alle Probleme beseitigt: macOS schließt den Port ordentlich und GNU/Linux verschluckt ab dem 2ten Durchlauf die erste Zeile nicht mehr!
Auch wenn ich versuche im SwingWorker-Thread in "finally" den Reader zu schließen, funktioniert das nicht. Der Code wird dort gar nicht ausgeführt. Das geschieht erst, wenn ich auf CLOSE geklickt habe (sofern das überhaupt möglich ist -> macOS) und wieder auf OPEN klicke! Der Worker Thread läuft trotz cancel(true) und isCancelled()-Abfrage weiter. Soweit ich das sehe wird nach isCancelled() sofort die done()-Methode aufgerufen und nicht aus der while-Schleife gesprungen, denn Code nach der while-Schleife (vor return null) wird beim Abbruch auch nicht ausgeführt.
Es kann auch mit dem Blocking in ".setComPortTimeouts(SerialPort.TIMEOUT_SCANNER,.." zusammenhängen. Habe auch schon bei JSerialComm vor 2 Wochen beim Autor einen Frage/Bugreport eingereicht. Bisher ohne Rückmeldung.
Code:
Ich habe eine stark vereinfachte Version meines SerialLoggers (PortClose) geschrieben, um damit zu experimentieren, siehe auch Anhang. Der zeigt die geloggten Daten in der Konsole an.
Hier mein SwingWorker:
Ich glaube, wenn ich den BufferdReader irgendwie geschlossen bekomme, werden die Probleme bei macOS und GNU/Linux verschwinden. Ich weiß nur nicht wie. Habe schon 2 Wochen gegoogled und alles mögliche ausprobiert.
Vielleicht kann ich auch eine andere Klasse als BufferedReader verwenden. BTW: Scanner funktioniert noch weniger zuverlässig. Ich vermute alle StreamReader bleiben dort hängen.
Für alle Ideen dankbar.
Hani
Als serielle Bibliothek verwende ich JSerialComm 1.3.11.
Codebeispiel w.u. und Java Datei im Anhang. Zum Lesen verwende ich den thread-sicheren BufferedReader.
Das Problem:
Derzeit lasse ich den BufferedReader nicht explizit schließen, da dann alle Plattformen beim BufferedReader.close() hängen bleiben, auch Windows.
macOS hängt beim Abbruch des Loggens (Button CLOSE => Task.cancel(true)) beim BufferedReader.close(), oder wenn auskommentiert im portClose(). Bis zum Abbruch loggt es die Daten aber ohne Probleme.
GNU/Linux loggt beim ersten Durchlauf und auskommentierten BufferdReader.close() alle Daten wie gewünscht. Es verschluckt aber (beim auskommentierten BufferedReader.close()) beim zweiten Durchlauf (Port schließen und wieder öffnen ohne das Programm zwischenzeitlich zu beenden) die erste empfangene Zeile, die nachfolgenden Zeilen werden korrekt geloggt . Das passiert auch bei allen weiteren Durchläufen. Schließt man das Programm und öffnet es wieder, läuft der erste Loggerdurchlauf wieder ohne Probleme und bei allen weiteren wird die erste Zeile verschluckt.
Fehlervermutung:
Ich glaube es ist ein Threadingproblem mit dem SwingWorker und dem BufferedReader. Wenn ich den Reader in "done()" schließen will, bleiben alle Plattformen dort hängen. Sie laufen erst weiter, wenn ich von der anderen Seite mindestens ein Zeichen an den Logger sende. Dann sind alle Probleme beseitigt: macOS schließt den Port ordentlich und GNU/Linux verschluckt ab dem 2ten Durchlauf die erste Zeile nicht mehr!
Auch wenn ich versuche im SwingWorker-Thread in "finally" den Reader zu schließen, funktioniert das nicht. Der Code wird dort gar nicht ausgeführt. Das geschieht erst, wenn ich auf CLOSE geklickt habe (sofern das überhaupt möglich ist -> macOS) und wieder auf OPEN klicke! Der Worker Thread läuft trotz cancel(true) und isCancelled()-Abfrage weiter. Soweit ich das sehe wird nach isCancelled() sofort die done()-Methode aufgerufen und nicht aus der while-Schleife gesprungen, denn Code nach der while-Schleife (vor return null) wird beim Abbruch auch nicht ausgeführt.
Es kann auch mit dem Blocking in ".setComPortTimeouts(SerialPort.TIMEOUT_SCANNER,.." zusammenhängen. Habe auch schon bei JSerialComm vor 2 Wochen beim Autor einen Frage/Bugreport eingereicht. Bisher ohne Rückmeldung.
Code:
Ich habe eine stark vereinfachte Version meines SerialLoggers (PortClose) geschrieben, um damit zu experimentieren, siehe auch Anhang. Der zeigt die geloggten Daten in der Konsole an.
Hier mein SwingWorker:
Java:
private class Task extends SwingWorker<Void, String> {
private BufferedReader serialReader;
private String portName;
@Override
protected Void doInBackground() {
portName = tf_Port.getText();
chosenPort = SerialPort.getCommPort(portName);
chosenPort.setComPortParameters(9600, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
chosenPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
chosenPort.setComPortTimeouts(SerialPort.TIMEOUT_SCANNER, 0, 0);
if (chosenPort.openPort()) {
try {
System.out.println("Port " + portName + " open. Waiting for serial data:");
serialReader = new BufferedReader(new InputStreamReader(chosenPort.getInputStream()));
String line = null;
while ((line = serialReader.readLine()) != null && !isCancelled()) {
publish(line);
}
} catch (IOException ex) {
Logger.getLogger(PortClose.class.getName()).log(Level.SEVERE, "BufferedReader.readLine() error", ex);
} finally {
// Dieser Code wird beim Abbruch des Workerthreads nicht ausgeführt. Erst beim Schließen
// und erneuten Öffnen des Ports.
//
// try {
// System.out.println("Finally");
// serialReader.close();
// } catch (IOException ex) {
// Logger.getLogger(PortClose.class.getName()).log(Level.SEVERE, null, ex);
// }
}
} else {
System.err.println("Open port " + portName + " failed");
}
return null;
}
@Override
protected void done() {
// try {
// serialReader.close(); // Alle Plattformen bleiben hier hängen
// } catch (IOException ex) {
// Logger.getLogger(PortClose.class.getName()).log(Level.SEVERE, null, ex);
// }
if (chosenPort.closePort()) { // Wenn serialReader.close() auskommentiert ist, hängt hier macOS
System.out.println("Port " + portName + " closed");
} else {
System.err.println("Port " + portName + " could not be closed");
}
bt_Open.setEnabled(true);
bt_Close.setEnabled(false);
tf_Port.setEnabled(true);
}
@Override
protected void process(java.util.List<String> chunk) {
for (String line : chunk) {
System.out.println(line);
}
}
}
Ich glaube, wenn ich den BufferdReader irgendwie geschlossen bekomme, werden die Probleme bei macOS und GNU/Linux verschwinden. Ich weiß nur nicht wie. Habe schon 2 Wochen gegoogled und alles mögliche ausprobiert.
Vielleicht kann ich auch eine andere Klasse als BufferedReader verwenden. BTW: Scanner funktioniert noch weniger zuverlässig. Ich vermute alle StreamReader bleiben dort hängen.
Für alle Ideen dankbar.
Hani