Threads Serielle Schnittstelle mit Listener / Aufrufendes GUI

snueffel

Mitglied
Hallo zusammen!

Aufgrund des Interesses an JAVA, welches mein Elektrotechnik-Studium geweckt hat, schreibe ich derzeit eine Software im Rahmen meiner Studienarbeit. Zunächst einmal ein "Danke", denn dieses Forum hat mir bei den Anfängen sehr geholfen, also schön, dass es euch gibt! :toll:

Leider habe ich derzeit ein Problem, was ich offenbar nicht alleine lösen kann: Über einen Button soll ein Thread gestartet werden, der über eine serielle Schnittstelle eine Zeichenkette (von einem Gerät) einliest. Grundsätzlich funktioniert das wunderbar; die gewünschten Zeichen erhalte ich. Ich nutze hierfür jssc.

Da im Vorfeld nicht klar ist, wie viele Zeichen übertragen werden (Das Ende des Strings wird mit einem "ENDE" signalisiert), nutze ich einen Listener, der eine zwischengespeicherte Zeichenkette genau dann erweitert, wenn ein weiteres Zeichen übertragen wurde; hierzu das Codefragment:
Java:
public void run()
    {
	try
	{
	    // Ein Einstellungsstring muss zunächst ausgegeben werden
	    serialPort.openPort();
	    serialPort.setParams(baudrate, databits, stopbits, parity, true, true);
	    char cr = 13;
	    einstellung = einstellung + cr;
	    serialPort.writeBytes(einstellung.getBytes());

	    // Anschließend kann auf Rückgaben gewartet werden
	    int mask = SerialPort.MASK_RXCHAR;
	    serialPort.setEventsMask(mask);
	    new SerialPortReader();
	    serialPort.addEventListener(new SerialPortReader());
	} catch (Exception e)
	{}
    }
    class SerialPortReader implements SerialPortEventListener
    {
	public void serialEvent(SerialPortEvent event)
	{
	    if (event.isRXCHAR())
	    {
		if (event.getEventValue() == 2)
		{
		    // Bytes im input-Buffer Kontrollieren
		    // sobald 2 bytes (1 Zeichen) verfügbar, String erweitern
		    try
		    {
			byte buffer[] = serialPort.readBytes(2);
			String neu = new String(buffer);
			s = s + neu;
			if (s.contains("ENDE") || s.contains("ENDE"))
			    serialPort.closePort();
		    } catch (SerialPortException e)
		    {}
		}
	    }
	}
    }

Wie schon zu erkennen ist, erweitert die Klasse "Thread", damit sich die aufrufende GUI-Klasse nicht "aufhängt". Dieses startet über einen Button den Thread, der natürlich bereits nach wenigen Millisekunden beendet ist (Es arbeitet ja nur noch der Listener). Wie teile ich jedoch dem aufrufenden Programm mit, dass der Port geschlossen wurde und der String abgerufen werden kann? :bahnhof: Weiterhin soll der Vorgang abgebrochen werden, sofern ein entsprechender Button betätigt wurde (Das funktioniert bisher natürlich auch nicht wie gewollt, da der Thread ja schon längst beendet ist, auch wenn noch nichts empfangen wurde). Ich hoffe ihr könnt mir helfen 🙂
 
Hallo 🙂

Du könntest dafür einen eigenen Listener implementieren:

- leg dir ein Interface namens SerialDataListener oder so an, das eine Methode onDataReady(String data) enthält
- implementier dieses Interface in der aufrufenden Klasse oder erzeuge eine anonyme Klasse, die es implementiert
- übergib das implementierende Objekt der Klasse, die den String einliest, und speicher es dort
- wenn der String gelesen wurde, ruf die Methode im Objekt auf, das das Interface implementiert. Dadurch erhält es auch gleichzeitig den String und muss ihn nicht selbst abholen.

Zum Unterbrechen kannst du die Methode interrupt() auf dem Thread aufrufen. Wenn der Thread gerade in einer blockierenden Operation ist, wird dort eine InterruptedException ausgelöst und du weißt dann, dass der Thread beendet werden soll. Ansonsten kannst du auch Thread.interrupted() innerhalb des Threads aufrufen, um zu prüfen, ob von außen unterbrochen wurde und dann alles beenden.

Falls das nicht ausführlich genug war, frag nochmal gezielt nach, dann beschreib ich die Stelle etwas ausführlicher 🙂
 
Sauber! Vielen Dank für die schnelle Antwort, das war genau der "Tritt" in die Richtung, den ich gebraucht habe! :idea: Auf dem Trichter einen Listener zu implementieren, bin ich schon gekommen, allerdings war ich weit von einer konkreten Idee entfernt :bloed:

Solche antworten liebe ich ja: Kein konkreter Code, sondern etwas, dass einen beim Überlegen weiterhilft! :toll:

Und um alles zu vervollständigen, die Lösung mit dem interrupt(): Vorher wurde der Port im Listener geschlossen, jetzt gibt es eine extra Methode, die dies übernimmt. Bei einem manuellen Beenden kann der Port so noch geschlossen werden (Es muss nichtmal zwingend ein Thread gestartet werden, das war mein Denkfehler: Der Thread ist ja schon nach kürzester Zeit abgearbeitet, ich wollte ihn jedoch am Leben halten, bis der String komplett ist. Durch den Listener erübrigt sich dies).
 
Schön, dass du das so schnell umsetzen konntest 🙂 Dann noch zwei kleine Tipps für den restlichen Code:

Du fügst momentan mit dem +-Operator das gelesene Zeichen an den String an. Das ist eine recht langsame Operation, weil dann erst neuer Speicher für den neuen String (alte Länge + angefügte Länge) reserviert werden muss, der alte String vollständig dort hineinkopiert werden muss und schließlich der neue String angefügt werden muss. Mit der Klasse StringBuilder oder StringBuffer kannst du statt + die Append-Methode verwenden, die nur selten neuen Speicher reservieren muss, weil sie genau für solche Operationen gedacht ist. Auf der kannst du am Ende toString aufrufen, um den zusammengebauten String herauszuholen. Siehe auch hier: Galileo Computing :: Java ist auch eine Insel – 4.4 Veränderbare Zeichenketten mit StringBuilder und StringBuffer

Dann verwendest du contains zum Prüfen, ob ENDE vorliegt. Da du das bei jedem empfangenen Byte machst, nehme ich an, dass ENDE nur am ENDE steht - du könntest dann statt contains lieber endsWidth verwenden. Der Vorteil ist, dass nicht bei jedem Byte der ganze String durchsucht werden muss, sondern nur die letzten 4 Zeichen.

Beide Sachen lohnen aber erst, wenn deine Strings ein paar hundert Zeichen überschreiten. Wenn die eh nur 50 Zeichen oder so lang sind, lohnt das evtl. nicht.
 
Zuletzt bearbeitet:
Vielen Dank auch nochmals für die beiden Tipps! 🙂

Der Grund, warum ich den String so zusammengesetzt habe, war, dass ich erstmal sehen wollte, ob ich das Ganze überhaupt so eingelesen bekomme, wie ich möchte (Ich habe vorher noch nie eine serielle Schnittstelle mit JAVA angesprochen). Da ich jedoch maximal 30-40 Zeichen erwarte, werde ich es vermutlich bei der aktuellen Lösung belassen; den Code empfinde ich als einfacher lesbar und der Performancegewinn rechtfertigt eine Änderung kaum.

Bei dem Tip endsWith() zu verwenden sieht das ganze anders aus: Auch hier war das nur provisorisch so implementiert, da ich wusste, dass irgendwann ein "ENDE" kommt. Hieran muss ich zwar noch ein CR anhängen, aber die Überprüfung geht sicherlich besser. Ich muss nämlich leider jedes einzelne Zeichen prüfen, damit ich auch wirklich alle habe.
 

Zurück
Oben