BufferdInputStream

Hallo Java-Experten!

Ich habe ein Mikrocontrollerboard, das mir mit 57600Baud Daten byteweise über die serielle Schnittstelle sendet. Die serielle Kommunikation mit Java Comm funktioniert bisher problemlos bei 9600Baud. Sobald ich auf 57600Baud gehe, kommt es zu Fehlern.
Die Signale auf dem Kabel sind absolut sauber. Ich vermute, meine Implementierung mittels InputStream ist unter Umständen nicht schnell genug.
Nach allem, was ich bisher gefunden habe, soll es beim häufigen lesen einzelner Bytes günstiger sein, einen InputStream in einem BufferdInputStream zu kapseln. Also habe ich folgendes gemacht:

FileWriter datafile;

try {
 datafile = new FileWriter("recordedData.txt");
 } catch (Exception e) {}

InputStream inputdata;
BufferedInputStream buffereddata = BufferedInputstream(inputdata);

Im SerialPortEventListener steht folgendes:

public void serialEvent(SerialPortEvent event) {
 try {
 i=buffer.read();

 } catch (IOException e) {}

 try {

 datafile.append(Integer.toString(i));
 datafile.append("\r\n");

 } catch (IOException e) {}
 }

Leider funktioniert das nicht. Es werden keine Daten in datafile geschrieben. Wenn ich es ohne ungepuffert versuche, werden zumindest, wenn auch teilweise falsche Daten, geschrieben.

Was mache ich falsch und gibt es einen besseren und saubereren Weg, Daten, die asynchron über die serielle Schnittstelle kommen, in ein File zu schreiben?

Vielen Dank für eure Hilfe!

Gruß

Michael

Moien

Du bufferst das falsche. Das serialEvent kommt bei jedem Byte. Du liest pro Runde ein Byte. Was soll der Buffer da machen ? Der javabuffer holt bei jedem read möglichst viele Byte ab. Er tut aber gar nichts solange man ihn nicht aufruft.

Aber datafile kann man buffern. Das würde ich testweise in ein grosses ByteArray schreiben.

cu

Hallo punpkin!

Danke, du hast Recht, daran habe ich gar nicht gedacht. Dann ist mir aber nicht klar, warum es bei höhren Baudraten regelmäßig zu Problemen kommt. Das Signal habe ich mit dem Scope gemessen, und es war einwandfrei. Würde das Puffern des FileWriter-Objekts etwas bringen? Oder hast du eine Vermutung, was noch ein Problem darstellen könnte? (Ich weiß, dass diese Frage eigentlich ohne Glaskugel nicht zu beantworten ist, aber vielleicht hast du ja zufälligerweise Ideen, wo prinzipiell ein Knackpunkt liegen könnte und was ich ausprobieren könnte)

Gruß

Michael

Moien

Dann
ist mir aber nicht klar, warum es bei höhren Baudraten
regelmäßig zu Problemen kommt.

Solange dein Thread mit lesen/schreiben beschäftigt ist kann kein zweites Byte empfangen werden (Übertrieben, da gibts noch mindestes einen Buffer im UART). Vom Code her braucht das Wegschreiben viel Zeit (Byteweise in Dateien schreiben ist arschlangsam in java), das lesen kann man nicht weiter optimieren.

Dann solltest du aufhören bei jedem Aufruf was zu lesen. Nur wenn das event vom Type DATA_AVAILABLE ist sind auch tatsächlich Daten da. Das muss man abfragen, sonst können dumme Dinge passieren. Haufenweise Exceptions schmeissen ist nicht gut für solchen „zeitkritischen“ Code.

Danach würde ich die 2 try-catchs mit Zählern versehen (pro Durchlauf +1). So sieht man sehr schnell ob was nicht stimmt, hat aber keinen unnötigen Code im Listener.

SerialPort.setSerialPortParams(…) ist korrekt ? notifyOnDataAvailable als einziges Event gesetzt, alles andere abbestellt ? (nicht drauf verlassen dass nur noch solche Events vorbei kommen).

cu

1 Like

Hallo pumkin!

Vielen Dank für deine Geduld. Ich habe hier mal die ReceiveData-Klasse hingeschrieben. Der SerialPortEventListener läuft in einem extra Thread, der vom Hauptprogramm aus gestartet wird.

class ReceiveData extends Thread {

 static CommPortIdentifier portID;
 static SerialPort serialport;

 FileWriter datafile;
 InputStream inputdata;
 Vector storedata = new Vector();

 public ReceiveData(){

 try {
 datafile = new FileWriter("recordedData.txt");
 } catch (Exception e) {}

 try {
 portID = CommPortIdentifier.getPortIdentifier("COM8");
 serialport = (SerialPort)portID.open("data", 2000);
 } catch (Exception e) {}

 try {inputdata = serialport.getInputStream();
 serialport.addEventListener(new SerialEvent());
 } catch (Exception e) {}

 serialport.notifyOnDataAvailable(true);

 try {serialport.setSerialPortParams(57600,
 SerialPort.DATABITS\_8,
 SerialPort.STOPBITS\_1 ,
 SerialPort.PARITY\_NONE);}
 catch (UnsupportedCommOperationException e) {}
 }

 public void run() {
 while(true) {}
 }

 class SerialEvent implements SerialPortEventListener {
 public void serialEvent(SerialPortEvent event) {
 if(event.getEventType() == SerialPortEvent.DATA\_AVAILABLE) {
int i;
 try {
 i=inputdata.read();
 } catch (IOException e) {}

 try {
 datafile.append(storedata.lastElement().toString(i));
 datafile.append("\r\n");
 } catch (IOException e) {}
 }
 }
 }
}

Ich habe im letzten Posting den Code nur auszugsweise hingeschrieben. Es wird somit eingentlich nur gelesen, wenn Daten vorhanden sind.
Wäre es sinnvoller, die Daten in ein Array, statt direkt in das File zu schreiben?

Gruß

Michael

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Moien

Mir ist gerade spontan klar geworden wieso dein Prog so langsam läuft. Du versuchst in der run-Methode das Programm am laufen zu halten. Das klappt auch, lastet die CPU aber zu 100% aus.

 public void run() {
 while(true) {}
 }

Die ebenfalls unsaubere aber wesentlich schmerzlosere Methode wäre this.sleep (Long.MAX_VALUE); anstelle der Endlosschleife.

Wäre es sinnvoller, die Daten in ein Array, statt direkt in
das File zu schreiben?

Es wäre sinnvoll die Datei zu buffern, also:
datafile = new BufferedWriter(new FileWriter(„recordedData.txt“));

cu

1 Like

Hallo pumpkin!

Mir ist gerade spontan klar geworden wieso dein Prog so
langsam läuft. Du versuchst in der run-Methode das Programm am
laufen zu halten. Das klappt auch, lastet die CPU aber zu 100%
aus.

public void run() {
while(true) {}
}

Ok, das leuchtet mir ein.

Die ebenfalls unsaubere aber wesentlich schmerzlosere Methode
wäre this.sleep (Long.MAX_VALUE); anstelle der Endlosschleife.

Warum ist das unsauber, und wie sähe denn eine saubere Methode aus?

Wäre es sinnvoller, die Daten in ein Array, statt direkt in
das File zu schreiben?

Es wäre sinnvoll die Datei zu buffern, also:
datafile = new BufferedWriter(new
FileWriter(„recordedData.txt“));

Ich werde das heute abend ausprobieren.

Vielen Dank und Gruß

Michael

Warum ist das unsauber, und wie sähe denn eine saubere Methode
aus?

eine saubere Implementierung von einer Run Methode schaut so aus, das du auf was abfragst, das tust und wenn nichts zu tun ist wartest.

public void run(){
 while(true){
 byte[] buffer = new byte[1024]{};
 leseBuffer(buffer);
 while(buffer[0] != null){ schreibe(buffer); leseBuffer(buffer) }
 sleep(1000);
 }
}

Nur schnell zusammen getippt, aber ich hoffe es hilft ein bisschen

Moien

Die ebenfalls unsaubere aber wesentlich schmerzlosere Methode
wäre this.sleep (Long.MAX_VALUE); anstelle der Endlosschleife.

Warum ist das unsauber, und wie sähe denn eine saubere Methode
aus?

Du machst in dem Thread gar nix und wartest nur. Das geht z.B. auch mit wait(). Bei wait kann man den Thread später wecken und weiterverwenden.

Oder man spart sich den Thread und spielt mit der Daemoneigenschaft eines anderen Threads rum.

cu

Danke an euch beide!
Hallo ihr beiden!

Zunächst mal vielen Dank für die schnelle Hilfe!

Es lag wohl wie du geschrieben hast, am Schreiben in die Datei. Der Vorgang hat wohl zu lange gedauert. Ich hänge jetzt die Bytes an ein Vector-Objekt an, und kopiere den Inhalt nach Beendigung der Übertragung in das File.
Danach sah es schon viel besser aus, allerdings waren immer noch Fehler im Signal (nur nicht so schlimm wie vorher).
Ich hatte einen RS232 zu USB-Adapter von Hama verwendet (mein Notebook hat keine serielle Schnittstelle (sprich RS232). Irgendwie scheint das aber nicht so gut funktioniert zu haben. Als ich den µController direkt an die RS232 des PC angeschlossen habe, wurden die Daten vollkommen korrekt übertragen.

Vielen Dank!

Gruß

Michael