# Blockierendes Lesen, synchrone uns asynchrone Verbindungen...



## antimon (13. März 2008)

*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 ... :-/


----------



## TheJadix (13. März 2008)

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 :


```
public void run() {

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

Und über einen Methodenaufruf setzt man das Flag auf false :

```
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


----------



## antimon (13. März 2008)

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...


----------



## TheJadix (13. März 2008)

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


----------



## Thomas Darimont (14. März 2008)

Hallo,

das kannst du wunderbar mit Futures machen.. schau mal hier:
http://www.tutorials.de/forum/java/274056-fragen-threads-und-executorservice.html
http://www.tutorials.de/forum/java/276266-komplizierter-java-queue-mechanismus.html

Gruß Tom


----------



## antimon (15. März 2008)

@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!


----------



## TheJadix (15. März 2008)

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


----------



## antimon (16. März 2008)

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)...


----------



## TheJadix (16. März 2008)

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


----------



## antimon (16. März 2008)

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...


----------



## procurve (16. März 2008)

antimon hat gesagt.:


> 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...



Gab's lange Zeit nicht, aber siet Java 1.4 ist das NIO (New IO) im SDK integriert und das läuft auf Listener-Basis.
Stichworte: SocketChannel, ServerSocketChannel, Selector

Du wirst denke ich nicht alles unter einen nenner bringen können.

Das einzige, was du machen kannst ist, dass du ein abstraktes INterface erstellst, das deinem Programm die benötigte Funktionalität liefertz und du dann die entsprechenden Klassen für USB-, Serial- und Netzwerk-Verbindung dieses Interface implementieren lässt.
Um eine an die jeweilige Verbindung angepasste Klasse kommst du nicht herum, denke ich.


----------



## TheJadix (17. März 2008)

Hallo,

wie procurve es schon vorschlägt :

Wenn du auf Wiederverwertbarkeit deines Codes setzt, dann bastel dir ein Interface,
evtl. noch eine abstrakte Klasse die einen Tiel der Kommunikation implementiert der
für alle Hardwareschnittstellen gleich ist.
Ein zusätzliches Interface für einen eigenen Listener auf dein Kommunikationsobjekt
wäre sicher auch hilfreich, sowie eine eigene Exception die das Objekt werfen könnte.

Alles vor diesem Interface, also GUI und eigentliche Programmlogik wären dann immer
die selbe. Für jede Schnittstellenart gäbe es dann ein XYConnection-Objekt:

Dieses Objekt könnte man dan z.B mit anderen Objekten füttern.
z.B. ein CommInstruction("XYZ",2000,"OK") die ein Befehl XYZ sendet,
maximal 2 Sekunden darauf wartet und ein "OK" auf der Leitung erwartet.
Die Rückantwort könnte dein ConnectionObjekt  über das eigene Listener-
Interface geben. Ein Fehler könnte es mit der Exception qutieren !

Fragen oder Hilfe nötig ? 

Gruß JAdix


----------



## antimon (18. März 2008)

Okay, also die Kommunikation mit dem Gerät funktioniert soweit, danke schonmal für Eure Tips und Hilfe!

Allerdings taucht jetzt das nächste Problem für mich auf:
Der Adapter wird erst initialisiert und in den Monitor-Modus geschaltet, dann kann er Nachrichten empfangen und senden. 

Jetzt kann es aber vorkommen, dass man die Verbindung zum Gerät trennen möchte (beispielsweise wenn das Programm beendet wird), dazu sollte zuerst der Befehl zum Abschalten des Monitormodus gesendet werden, dann eventuell noch weitere "Aufräum"-Befehle und zu guter Letzt das Schließen der Schnittstelle. 

Normalerweise geht es recht fix, die Verbindung zu beenden, aber es kann vielleicht vorkommen dass der Adapter sehr beschäftigt ist und das Kommando zum Beenden des Monitor-Modus einige Zeit verzögert bearbeiten kann - und somit die Antwort auch später schickt. Aber auf die muss ich natürlich warten bevor ich das nächste Kommando sende...

Es kann also sein dass im dümmsten Fall ein paar Sekunden vergehen bis alles fertig ist - was macht man in so einem Fall am besten?

a) Die Befehle im "GUI-Thread" (also ohne extra dafür gestarteten Thread) laufenlassen - das Programm hängt dann evtl. kurz, was natürlich nicht so schön ist.

b) Einen extra Thread starten, der die Befehle abarbeitet und auf Antwort wartet (evtl. über Futures wie oben beschrieben). Der User klickt auf einen Button, der löst die Abarbeitung der Befehle aus und wenn diese das Event auslösen dass sie fertig sind, bekommt der Button das mit und ändert seinen Status (z.B. "befehl disconnect" -> abarbeitung -> buttonstatus "disconnected"). Dabei könnte es aber sein dass der User meint, er hat nicht richtig auf den Button geklickt, klickt mehrmals hintereinander drauf und die Abarbeitung kommt ins Schleudern...?

Was macht man in so einem Fall? Den Button nach dem Klick deaktivieren und erst nach der Abarbeitung wieder aktivieren? Ein Flag setzen, das weitere Events einfach ignoriert? Ganz was anderes?

Hmm ich seh übrigens grad, langsam wirds OT - ich glaub das nächste Mal mach ich nen neuen Thread auf...


----------



## TheJadix (19. März 2008)

Hallo,

am besten man blendet eine beruhigende Fahrstuhlmusik ein, mit einer
sanften Frauenstimme die sagt "Ihr Befehl wurde gesendet, bitte warten !"

Spass beiseite !

Den Button auf disabled setzen ist ne möglichkeit, aber ich finde sie nicht so schön !
Wenn der User die Verbindung eh abbaut hat er sicher in dem Moment nicht noch
grossartig was vor mit der GUI bis dieses erledigt ist.

Ich würde einen Dialog öffnen der darüber Auskunft gibt das die Verbindung abgebaut wird,
mit irgendeiner kleinen Animation die zeigt, es passiert noch was !
Ein bischen rumblinken beruhigt die Generation-Windows ungemein !

Den GUI-Thread würd ich GUI-Thread sein lassen, gibt zig GUTE-schlechte Beispiele
in diesem Forum was das für blöde Nebenwirkungen hat !

Extra Thread ist wahrscheinlich auch nicht nötig ! Man könnte sich sicher dafür in den
Empfangs-Thread einklinken, da man ja eh auf das Ende des Monitor-Modes wartet und
das Ende sicher über die eingehende Kommunikation zu identifizieren ist !

Gruß JAdix

PS : Freu mich auf den nächsten Thread ...


----------



## antimon (21. März 2008)

So, jetzt komm ich endlich wieder zum Programm, die letzten Tage waren bisserl stressig.

Okay, ich denke da muss ich noch ein wenig experimentieren wie ich das mache... eigentlich sollte in den Optionen nach dem Adapter gesucht werden, sobald eine Änderung an der Schnittstelle vorgenommen wurde. Aber ich glaube fast ich mache einen extra Button hin, mit dem man manuell auf einen angeschlossenen Adapter an der aktuellen Schnittstelle suchen kann - und wenn man mit OK aus dem Einstellungs-Dialog rausgeht, kann ich nochmal prüfen und eine Warnmeldung ausgeben, wenn nix gefunden wurde.
Aber ich glaube das wird etwas zu verzwickt, wenn ich bei jeder Änderung der Schnittstelle diese öffne, meinen "Ping" sende, eine Antwort oder ein Timeout abwarte, die Schnittstelle schliesse... wenn ein Benutzer schnell die Einstellungen durchgeht könnte es zum Chaos werden, also beispielsweise kommt das Antwortpaket vom Ping verzögert zurück, hat der Benutzer vielleicht schon eine andere Schnittstelle ausgewählt und das Programm meint, der Ping käme von dieser und nimmt fälschlicherweise an, dass die funktioniert... ne, ich glaub das bringt nix 

Aber wieder zum eigentlichen Thema zurück: Bei meinen Tests habe ich festgestellt, dass irgendwo ein Puffer vollläuft. Und zwar sende ich CAN-Nachrichten über einen gekauften Analysator ins CAN-Netz, empfange die über einen eigens entwickelten uC-Adapter und der schickt die per RS232 weiter an den PC.

Lasse ich nun beispielsweise über den gekauften Analysator jede Milisekunde eine Nachricht raus und stoppe das Senden nach einiger Zeit, so trudeln in meinem Programm noch einige Sekunden später ein Haufen Nachrichten ein... es schaut also so aus als ob irgendwo ein Puffer vollgelaufen ist, der nun langsam wieder geleert wird.

Das Problem hat sich auch relativ schnell gefunden: Nach jedem empfangenen Zeichen habe ich den Thread eine Milisekunde in den Schlaf gelegt. Und klar - kommt jede Milisekunde eine Nachricht rein, gibts da Konflikte denn es geht ja noch Rechenzeit zur Auswertung verloren...

Allerdings: Wenn ich keine Wartezeit einplane, geht natürlich die Prozessorlast auf 100% hoch - nicht gerade das Wahre. Dieses Polling gefällt mir auch absolut nicht, und wenn ich die Empfangsroutine blockieren lasse habe ich das Problem wie oben - ich müsste im Prinzip den Thread von aussen überwachen und abschiessen, aber sehr sauber ist das ja auch nicht - allerdings evtl. doch die sinnvollste Lösung?

Listener wären mir im Prinzip am liebsten, aber da ich einen FT232 (also den Chip USB <-> RS232) ansteuern muss und die verwendete Lib (ftd2xxj) keine Listener unterstützt, bleibt mir wohl nur Polling übrig. Ausser ich finde eine andere Lib für den FT...

Aber egal wie die Daten reinkommen - ich muss diese ja irgendwo erst speichern, auswerten und dann die Events rausschicken. Ich glaube da kann man auch viel falsch machen, wie ist da die sinnvollste Vorgehensweise? Momentan empfange ich Zeichen für Zeichen, nach jedem überprüfe ich ob die empfangene Nachricht vollständig ist und wenn ja schicke ich sie per Event raus. 

Zeichenweise muss ich leider überprüfen, da ich nur ein Startbyte habe und die Länge des Paketes dann vom Pakettyp (das Byte nach dem Startbyte) abhängt. Ich könnte zwar, sobald ich weiss wie lange das Paket wird, warten bis auch so viele Bytes da sind und die dann auswerten, aber da die Pakete nur so 5-10 Byte im Schnitt haben, denke ich dass das nicht so viel bringen würde...

Aber würde es vielleicht etwas bringen im Empfangsthread auf die Auswertung ganz zu verzichten, anstattdessen nur die empfangenen Daten in einen Puffer legen und die Auswertung übernimmt ein eigener Thread? Oder wäre das Overkill? Die Daten kommen auf jeden Fall mit einer maximalen Geschwindigkeit von 1MBit rein, sollten dann aber sauber empfangen werden können.

So, sorry jetzt habe ich extrem viel getextet, das nächste Mal wirds auf jeden Fall weniger, versprochen 

@TheJadix: Was meintest du mit "PS : Freu mich auf den nächsten Thread ..." - ne implizite Aufforderung einen neuen Thread zu eröffnen?  Mach ich, aber das obige denk ich passt doch ganz gut hier rein...


----------



## TheJadix (21. März 2008)

Hi,

klingt als würde der Eingangspuffer der RS232 volllaufen !

Wie wäre das ? :

Du liest die Daten ein, verarbeitest diese (speichern oder was auch immer) und
prüfst unmittelbar danach ob wieder Daten zur verfügung stehen.
Wenn ja lässt du den Sleep aus und fährst direkt mit einem erneuten lesen fort !

Auf was hast du den Timeout der seriellen Schnittstelle stehen ?
Zu kurz würde ich ihn nicht machen !

Gruß JAdix


----------



## antimon (21. März 2008)

Hmm daran habe ich auch schon gedacht... ist die Frage ob ich die Auswertung gleich machen soll oder separat, denn verarbeiten und die Events rausfeuern geht ja im gleichen Thread, oder irre ich mich da? Also wenn ich z.B. von den Listenern eine "packetsReceived()"-Methode aufrufe und dort noch irgendwelche Aktionen ausführe, geht das alles zu Lasten des Empfangs-Threads?

Vielleicht ist mein Denken aber auch etwas zu eingeschränkt, bei Mikrocontrollern sagt man ja, dass die Interrupt-Routinen (also quasi die Events) so wenig Code wie möglich haben sollten, aber die haben auch bedeutend weniger Rechenleistung als heutige PCs...

Das Timeout sollte auf Standard stehen, zumindest habe ich es eigentlich nicht verändert... aber die Doku der RXTX Lib ist leider auch sehr spärlich, z.B. öffne ich die Schnittstelle mit "portIdentifier.open(this.getClass().getName(), 2000);"
Wofür die 2000 allerdings stehen (vielleicht ist das ein Timeout?) sagt die Doku leider nicht, das ist einfach nur eine "ID"...


----------



## TheJadix (21. März 2008)

Hallo

Da was das betrifft, ist die RXTX-Lib identisch ist mit  der "guten alten" javax.comm-Implementation
da kann man die Doku querlesen !

Der Wert beim open(...) bezieht sich nur auf das Timeout beim öffnen des Ports.
Mit enableReceiveTimeout(int) des Ports setzt man den fürs Empfangen gültigen Wert.

Interrupts eines uC's mit Events in einer Multi-Thread-Umgebung zu vergleichen hinkt sicher an der einen
oder anderen Stelle ! Aber der Listener sollte in der Regel so wenig wie möglich den aufrufenden Thread belasten.

Was für Verarbeitungsschritte sind denn unmittelbar nach dem Empfang nötig ?
Oder wird erst nur gespeichert und die CAN-Bus-Signale später analysiert ?

Gruß JAdix


----------



## antimon (21. März 2008)

Ah, ich dachte die RXTX wäre anders aufgebaut als javax.comm... wieder was dazugelernt 

Klar hast du Recht, dass man das nicht vergleichen kann - ich meinte eigentlich auch nur die Tatsache dass ISRs bei Mikrocontrollern so knapp wie möglich gehalten werden sollten.

Ich schildere mal kurz wie der Empfang momentan vonstatten geht...

In einer While-Schleife wird, solange Daten vorhanden sind, ein Byte ausgelesen, an einen String gehängt und dieser String wird an ein "Packet"-Objekt übergeben, das den String analysiert und eine Exception wirft, wenn das Paket unvollständig oder fehlerhaft ist. Ein Packet-Objekt bestimmt übrigens einen einzelnen Frame (Befehl-, Fehler- oder CAN-Frame).

Wenn keine Exception geworfen wird, ist das Paket vollständig und wird an die Listener rausgegeben - danach wird der String geleert und eine Milisekunde gewartet bevor überprüft sind ob neue Daten da sind (das baue ich gleich mal so um dass die Milisekunde nur gewartet wird wenn keine neuen Daten vorhanden sind, wie du geschrieben hast).
Dann geht das ganze Spiel von vorne los...

Die Auswertung der empfangenen Daten erfolgt folgendermaßen: Erst wird das erste Byte überprüft, ob es auch das Startbyte ist, dann wird im zweiten Byte nach dem Frame-Typ geschaut und je nachdem was für ein Typ es ist, ist die Länge des Frames bekannt - wenn diese erreicht wird, wird noch eine Checksum überprüft und dann das Paket in ein Objekt überführt und ist fertig.

Ob das so günstig ist, bei (noch) unvollständigen Frames jedes Mal eine Exception zu werfen weiss ich nicht, aber mir ist leider nichts besseres eingefallen... bin ich damit ansatzweise auf dem richtigen Weg?


----------



## antimon (21. März 2008)

Ach genau, sollte es dich interessieren, die Beschreibung des Protokolls gibt es unter http://wiki.isysbus.org/index.php/CAN-Sniffer_Protokoll


----------



## TheJadix (22. März 2008)

Hallo,

habe mal den Link zum Protokoll angesehen ! Sehr interessant !

Hat es einen bestimmten Grund warum du die Daten beim Empfang in einen
String umwandelst ?
Ich denke mal da wird nicht viel als Klartext zu erkennen sein ?
Also warum kein Array of byte oder int, auch wegen der Bit-Test-Operationen !
Die Maximal länge eines Frames ist ja bekannt (18 Byte richtig ?)
Ich würde sofort beim Empfang mit dem Packet-Objekt arbeiten,
welches dann das Array kapselt !

Synchronisiert sich das Protokoll nur über das Startbyte ?
Und kann im Datenstrom ein weiteres Byte mit dem Wert
des Startbytes erscheinen ?

Gruß JAdix


----------



## antimon (22. März 2008)

Ähm ja... eigentlich hats keinen richtigen Grund, ich gestehe meine Faulheit ein, bei Strings gehts halt recht einfach, was hinzuzufügen etc. - aber du hast Recht, ich kenne die maximale Länge, könnte also einfach mit Arrays arbeiten - werd ich wohl mal bei Gelegenheit umstellen.

Neben dem Startbyte gibt es noch eine zwei Byte breite Checksum, mit der überprüft werden kann ob ein Paket korrekt ist - leider kann das Startbyte auch in den Daten auftreten, aber da  bei Ansteuerung des Adapters maximal 1 MBit/s möglich sind, genau wie auf dem abzuhörenden Bus, so ist jedes eingesparte Bit wichtig...

Hat natürlich den Nachteil dass ich bei jedem eintreffenden Byte, das gleich dem Startbyte ist, schauen muss ob es sich um einen Paketanfang handelt, sonst könnte irgendwann die Synchronisierung verlorengehen und es kommt nur noch Müll an...


----------



## TheJadix (22. März 2008)

Hallo,

genau auf das Sync-Problem zielte meine Frage ab.

Wichtig wäre es also den Code so zu gestallten, dass ein evtl. Verlust
der Synchronisierung nach kürzester Zeit wieder aufgefangen wird.
Sprich nach einem CRC fehler keine Pakete mehr erzeugen bevor
nicht mindestens ein Frame wieder korrekt empfangen wurde !

Nochmal zur Geschwindigkeit :

1MBit also ca. 125kByte also Pi mal Daumen 10000 Pakete in der Sekunde
ist aber Mega-Worst-Case oder ?

Mit welcher Baudrate arbeitet die serielle Schnittstelle dabei ?

Gruß JAdix


----------



## antimon (23. März 2008)

Hmm jetzt glaub ich brauche ich zum Nachvollziehen deines Gedankenganges erst eine Begriffsdefinition...



> keine Pakete mehr erzeugen bevor
> nicht mindestens ein Frame wieder korrekt empfangen wurde



Meinst du mit "Pakete erzeugen" die Objekte, die aus den empfangenen Daten konstruiert wurden? Oder ausgehende Pakete? 
Die Pakete werden nur weitergereicht, wenn alle benötigten Pakete vorhanden sind und die CRC stimmt - allerdings brauche ich da auch noch etwas Feintuning, dass beispielsweise erkannt wird wenn ein Paket zu lang ist und dann das Ganze verworfen wird...

Ja, 1 MBaud ist absoluter worst-case, aber so ein Adapter sollte ja auch unter diesen Bedingungen arbeiten können - nicht dass genau die Nachrichten verloren gehen, die eigentlich interessant wären. Wenn ich wüsste dass Nachrichten einfach so mal verloren gehen könnten, hätte ich ein ungutes Gefühl im Bauch. Leider hilft ein großer Empfangspuffer im Mikrocontroller auch nur über Traffic-Spitzen hinweg, bei Dauerbelastung ist der auch schnell voll (und "großer" Puffer im Mikrocontroller ist bei 256 Byte RAM eh blanker Hohn).

Gut, wenn ich mir so eine "professionelle" Sniffer-Software ansehe, sollte ich mir eigentlich nicht so viel Gedanken machen, die behauptet steif und fest, sie könnte bei einer Busdatenrate von 5 kbit/s jede Milisekunde eine Nachricht von ca. 16 Byte senden... ohne irgendwie Fehlermeldung, selbst der Sendepuffer ist angeblich zu 0% belegt...

Die Baudrate ist übrigens für RS232 genauso wie für den CAN-Bus 1 MBit/s... und du wirst dich vielleicht fragen, wie man bei RS232 auf 1 MBit/s kommt - das geht auch nur, weil vom USB-/RS232 Wandler bis zum Mikrocontroller verdammt wenig Leiterbahnstrecke vorhanden ist...

Wie würdest du denn die Empfangsroutine gestalten, du hast doch da Erfahrung - ich bin mir da leider irgendwie meist recht unsicher, und ausserdem bin ich Perfektionist und momentan mit meiner Lösung überhaupt nicht zufrieden...

Die Überprüfung ob ein Paket korrekt empfangen ist, geschieht im Paket-Objekt selbst - ich erzeuge also erst ein leeres Objekt, übergebe einer "parseString()"-Methode meine empfangenen Daten, die schaut ob die Datenlänge (je nach Frametyp) passt und ob die CRC stimmt - wenn nein wirft sie eine Exception.

Die Daten, die ich der parseString()-Methode übergebe, lese ich byteweise aus und sammle sie, die gesammelten Daten übergebe ich nach jedem empfangenen Zeichen erneut zum Parsen, könnte ja sein dass das Paket mittlerweile vollständig ist. Irgendwann ist dies erreicht, dann wird keine Exception geworfen und das fertige Paket wird zur Verteilung weitergereicht.

Was mir nicht gefällt: Auch wenn noch ansatzweise nicht alle Daten vorhanden sind, wird nach _jedem_ Byte die Vollständigkeit überprüft. Ich weiss dass ich aber _mindestens_ 4 Byte brauche, stellt sich nur die Frage - prüfe ich das am besten bevor ich die Daten zum Parsen ans Packet-Objekt übergebe oder übergeb ich sie und warte die Exception ab, wenn es noch nicht passt?

Und angenommen es tritt folgender (zugegebenermaßen, bzw. hoffentlich) seltener Fall ein: 
Zwei Pakete werden hintereinander gesendet, wobei erstes das Startbyte in seinen Daten enthält und dummerweise ist genau das Startbyte vom ersten Paket verlorengegangen. 
Das heisst, das Byte in den Daten des ersten Pakets wird als Startbyte erkannt, allerdings passt der Rest nicht dazu, bei der CRC sollte es dann nen Fehler geben. Verwerfe ich ab dort die Daten, so sind mindestens zwei Pakete verloren, obwohl das zweite vielleicht korrekt und vollständig ist...
Ich könnte natürlich aber nur ab dem vermeintlich ersten Paket alle Daten verwerfen bis das nächste Startbyte im Datenstrom auftritt und dann erneut mit der Auswertung beginnen. Dann wäre zumindest das zweite Paket nicht verloren, die ganze Auswertungsgeschichte wird aber auch erheblich komplizierter... was ist da wohl sinnvoller?


----------

