Blockierendes Lesen, synchrone uns asynchrone Verbindungen...

antimon

Mitglied
Blockierendes Lesen, synchrone und asynchrone Verbindungen...

Hallo,

nachdem ich jetzt tagelang im Internet nach einer Lösung gesucht habe und mir erfolglos den Kopf zerbrochen habe, hoffe ich dass Ihr mir weiterhelfen könnt:

Folgende Problemstellung: Ich möchte ein serielles Gerät ansteuern, das in etwa wie ein Modem anzusprechen ist: Es gibt Befehle um diverse Parameter einzustellen/abzufragen und einen Datenmodus, in dem Daten gesendet und empfangen werden.

Nun ist es so, dass ich in erster Instanz prüfen möchte, ob ein Gerät an der seriellen Schnittstelle angeschlossen ist, dazu sende ich einen Befehl zur Versionsabfrage und warte auf eine Antwort. Da tritt das erste Problem auf - ich muss also eine Antwort senden, eine gewisse Zeit (Timeout ca. 2 Sekunden) warten, ob was zurückkommt und gebe dann die Information über den gefundenen oder nicht gefundenen Adapter aus.

Problem: Während der zwei Sekunden soll das Programm natürlich nicht hängen bleiben, aber der Lesezugriff ist ja blockierend und wenn keine Antwort kommt, hängt alles.
Wenn ich einen Thread für die Abfrage erstelle, müsste ich diesen nach dem eingestellten Timeout killen, also bräuchte ich einen weiteren Überwachungsthread, damit mein Programm beim Warten nicht hängenbleibt? Und wie beende ich den Thread sauber, wenn der Zugriff auf die Schnittstelle ja blockiert? stop() sollte ja nicht mehr verwendet werden, aber bleibt mir dann überhaupt was anderes übrig?


Angenommen diese Hürde ist umschifft, wie wird der Rest dann weiter organisiert? Um den Adapter anzusprechen muss ich also erst ein paar Einstellungen vornehmen, also Befehl senden, Antwort abwarten, nächsten Befehl, wieder Antwort abwarten (also synchron) - sobald ich in den Datenmodus gehe, läuft aber alles asynchron... wie bekomme ich das dann am besten hin?
Also kann man das obere Problem mit dem unteren kombinieren oder muss man synchrone/asynchrone Kommunikation trennen? Und wie sollen die Threads am besten erstellt werden, damit die ganze Kommunikation funktioniert? Hat sowas jemand schon mal gemacht, oder gibts da vielleicht andere, viel bessere Lösungsansätze als meiner?

Ich freu mich auch über Stichpunkte oder kurze Hinweise, aber momentan steh ich total auf dem Schlauch ... :-/
 
Zuletzt bearbeitet:
Hallo,

richtig einen Thread stoppt man nicht mit stop(), sondern indem man ihn aus der run()-Methode herauslaufen lässt.

Dafür muss man natürlich eine Schleife programmieren die sich über ein Flag beenden lässt !

z.B. so :

Code:
public void run() {

    while(doitagain) {
       // tue was !
   }
}

Und über einen Methodenaufruf setzt man das Flag auf false :
Code:
public void dontDoItAgain() {
   doitagain = false;
}

Dann muss nur sichergestellt sein das der Thread nicht irgendwo im while-Block
hängenbleibt ! (In deinem Fall mit Timeout arbeiten damit das Lesen nicht blockt !)

Gruß JAdix
 
Tja das ist leider das Problem... wenn ich blockierend löse, wie kann ich die Blockierung wieder aufheben? Bzw. was für einen Sinn macht eine Blockade wenn ich beispielsweise von einer seriellen Schnittstelle Daten einlesen möchte und jemand zieht plötzlich den Stecker. Dann kommen keine Daten mehr, ergo bleibt das Programm (sofern es nicht gestorben ist... ;) ) immer hängen...
 
Hallo,

ich würde immer mit ein paar Millisekunden als Timeout arbeiten, um eben solchen
Fällen von "ich zieh mal den Stecker" vorzubeugen. enableReceiveTimeout(100)

Ich denke du benötigst einen Thread der eine art Ablaufsteuerung implementiert.
Desweiteren würde ich darüber nachdenken ob es nicht sinnvoll wäre mit dem
SerialPortEventListener zu arbeiten.

Zum Beispiel : Du sendest deinen Befehl, wartest 2 Sekunden in dem du den Thread
schlafen schickst und prüfst danach ob der EventListener in der zwischenzeit
Daten entgegengenommen hat. SerialPortEvent.getEventType() == DATA_AVAILABLE
Wenn ja, prüfst du die Daten. Wenn nicht oder bei falschen Daten gibt es ne Fehlermeldung !

Sonst müsste einen Schleife 20 mal ein read() ausführen um auf (20*100ms Timeout)
2 Sekunden zu kommen. Geht auch und man erhällt ein positives Ergebnis noch vor
ablauf der 2 Sekunden wenn man alle 100ms auf korrekt eingegangene Daten prüft.

So oder so kann man auf falsche oder fehlende Daten mit einem geordneten Programmabruch reagieren !

Gruß JAdix
 
@TheJadix: Meinst du, Senden und Empfangen komplett in jeweils einem eigenen Thread entkoppeln und keine blockierenden Aufrufe starten, sondern pollen lassen? Und quasi in nem extra Thread die Überwachung vornehmen (also der Thread schläft z.B. 2 ms und schaut dann ob vom Receive-Thread mittlerweile was zurückgekommen ist, ansonsten Timeout)?

@Thomas Darimont: Das Future gefällt mir - wäre das quasi so dass man für jedes Paket einen Thread in den Pool legt und der wird entweder abgearbeitet oder kommt mit einem Timeout zurück? Ganz so 100% hab ich das noch nicht überrissen, aber der Link schaut ganz interessant aus:
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html

Danke schon mal für Eure Hilfe, jetzt bin ich schon ein ganzes Stück weiter!
 
Hi,

richtig, kein blockierendes Lesen ! Zum Pollen jein !

Ja wenn du einen seperaten Thread zum lesen verwendest, der mehrfach zu lesen
versucht und die Daten prüft, dann entweder nach x Fehlversuchen aufgibt und
einen Timeoutfehler schmeißt oder aber ein positiven Ergebnis abliefert.

Nein wenn du mit dem PortListener arbeitest. Der Listener würde dann wenn Daten
verfügbar sind diese lesen und verarbeiten und ein Ergebnis abliefern.
Die Timeout-Funktion für die Kommunikation müsste dann von dem Thread erledigt
werden, welcher den Gesamtablauf steuert.

Darf man Fragen was du da für ein Gerät ansprichst ?
Steuert immer das Programm die Kommunikation ?

Gruß JAdix
 
Hmm was mir an der Thread-Geschichte noch nicht so gut gefällt: Die ist recht schwer zu debuggen - besonders wenn ich mehrere Threads drauf ansetze verschiedene Pakete zu empfangen...

Was vielleicht auch ne Idee wäre ist folgendes: Ich habe einen (nicht blockierenden) Empfangs-Thread, der prüft ob Daten da sind, dann 10 ms oder so schläft, dann wieder prüft...
Und ein Sende-Thread bekommt Nachrichten, die er rausschickt, dann mit einem Timestamp versieht und in eine LinkedList reinschmeisst - der Empfangs-Thread überprüft dann regelmäßig diese Liste (viel sollte ja eh nicht drinstehen) auf den Sende-Timestamp und wenn innerhalb einer gewissen (einstellbaren) Zeit keine Antwort auf ein Paket kommt, markiert er es mit einem Timeout und wirft es an die Listener raus... oder habe ich da irgendwo einen Denkfehler? Hätte halt den Vorteil, ich brauche nur einen Thread und habe vielleicht weniger Probleme, weil das Debuggen leichter ist und ich nicht so viele Threads synchronisieren muss?

Konkret geht es bei der Ansteuerung um einen CAN-Sniffer, falls Euch das nichts sagt: CAN ist ein Bussystem, das häufig in der Automobilindustrie verwendet wird und welches wir für ein OpenSource-Hausautomatisierungs-Projekt verwenden (http://www.isysbus.org) Und dieser Sniffer (der aber nicht nur für die Hausautomatisierung verwendbar ist) ist im Prinzip ein Entwicklungswerkzeug für uns (quasi ein "Nebenprodukt").

Die Software, die ich momentan schreibe, greift über die serielle Schnittstelle oder USB auf einen von uns entwickelten Adapter zu, der dann an ein CAN-Netz (Haus, Auto, ...) angeschlossen ist. Und normalerweise ist dieser Adapter passiv, sendet also von sich selbst nichts raus. Den muss ich zuerst initialisieren (CAN-Baudrate sowie weitere Parameter einstellen), das ganze läuft synchron ab, erst Befehl, dann Antwort, usw.

Wenn alles soweit eingestellt ist, schalte ich den Adapter in den Monitormodus und ab dann läuft das ganze asynchron, also bekomme ich Nachrichten vom Bussystem übermittelt, ohne dass ich diese abrufen muss. Deswegen auch dieses "erst synchron, dann asynchron"-Zeug, die Timeouts brauche ich halt besonders dann wenn ich testen will ob ein Adapter angeschlossen ist (sprich auf das "Versionsnummer-Kommando" reagiert)...
 
Hi,

schönes Projekt !
Ich selbst habe verschiedene kleinere Hardwarebastelein auf Basis von AVR-Microcontollern über UART,
also serielle Schnittstelle des PCs, mit Java-Programmen als Frontend angesprochen.

Ein Empfänger-Thread ist sicher ne Lösung, dieser kann ja über ein Flag gesteuert, unterschiedlich
reagieren, jenachdem ob das Programm sich noch im Handshaking- oder schon im Monitor-Modus befindet.

An die PortEvents magst Du so garnicht ran, oder ? Wenn einem schon angeboten wird einen Listener zu registrieren,
dann würde ich das fast immer einem polling vorziehen.

Ob ein zusätzlicher Sende-Thread unbedingt nötig ist ? Nicht unbedingt, denke ich !
Aber einen Thread der die ganze Aktion-Reaktion-Geschichte bei der Kommunikation überwacht (quasi wie ein
Programmwahlschalter einer Waschmaschine) den würde ich einsetzen.

Gruß JAdix
 
Danke für die Blumen :)

Hmm dann kennst du dich auf dem Gebiet ja auch bestens aus :) AVRs sind bei uns auch im Einsatz...

Also eigentlich mag ich Listener lieber als irgendwelche Polling-Geschichten... das Problem ist nur dass ich mich nicht darauf verlassen kann dass ich mit Listenern arbeiten kann - denn die Sniffer-Soft soll unter Windows und unter Linux laufen, ausserdem sowohl über die serielle Schnittstelle als auch über USB und evtl. noch weitere... und da muss ich versuchen, den kleinsten gemeinsamen Nenner zu finden um nicht alles neu schreiben zu müssen und damit zusätzliche Fehlerquellen ins Programm zu bekommen...

Wenn ich mich nicht täusche, gibt es beispielsweise keine Listener wenn man Netzwerksockets verwendet... oder wars was anderes? Is schon länger her dass ich das programmiert habe, korrigier mich bitte wenn ich irgendwas falsches schreibe...

Hmm Überwachungs-Thread, ja sowas werde ich irgendwie bauen, schon allein weil das Laden der RXTX-Lib irgendwie so lange braucht... was das genau ist weiss ich noch nicht, vielleicht habe ich irgendwelche Voreinstellungen vergessen, aber die braucht immer so 2 Sekunden, bis sie initialisiert ist - das wäre natürlich auch super wenn das schon während des Programmstarts im Hintergrund passiert und der Zugriff auf die Schnittstellen sofort möglich ist, wenn man ihn braucht...
 
Zurück