Blockierendes Lesen, synchrone uns asynchrone Verbindungen...

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.
 
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
 
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...
 
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 ...
 
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...
 
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
 
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"...
 
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
 
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? ;)
 
Zurück