Forum: PC-Programmierung C# Kommunikation mit externem Gerät über Serial Port, wie strukturieren?


von Alexander H. (ill_son)


Lesenswert?

Hallo,

ich habe folgendes Problem. Ich schreibe gerade an einer Anwendung die 
am Ende mit einem extern angeschlossen Gerät (FTDI USB-Seriell) Daten 
austauschen soll. Aktueller Stand ist, dass ich erkennen kann, wenn das 
Gerät angeschlossen oder abgezogen wird und an welchem Port. Das würde 
ich gern beibehalten. Die Kommunikation läuft so, dass ich ein Kommando 
sende und das Gerät entsprechend antwortet und da liegt mein Problem. 
Die Daten können auch schon mal ein paar KByte sein. Ich bin aber 
bestrebt, den Serial Port nur zu öffnen, wenn es sein muss und ihn nach 
dem Senden wieder zu schließen, damit ein Abziehen des Gerätes nicht zu 
undefinierten Zuständen führt. Deshalb weiß ich nicht so recht, wie ich 
die Kommunikation strukturien soll.

Variante 1: Kommando senden, warten, lesen, Port schließen.

Variante 2: Kommando senden, Port offen lassen, auf DatenEvent 
reagieren, dort lesen und Port schließen.

Variante 2 gefällt mir besser, hat aber Unwegbarkeiten. Weil, wenn nix 
kommt, bleibt der Port offen, was Mist ist.

Danke im Voraus für eure Anregungen.

Grüße, Alex

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Alexander H. schrieb:
> Variante 1: Kommando senden, warten, lesen, Port schließen.

würde ich verwenden. Das ganze aber in einem eigenen Thread - damit die 
GUI nicht blockiert. Ein das Readtimeout sollte man setzen, damit das 
lesen nicht ewig blockiert.

> Variante 2: Kommando senden, Port offen lassen, auf DatenEvent
> reagieren, dort lesen und Port schließen.
hier kommt noch dazu das du im schlimmsten fall jedes Byte einzeln 
bekommst. Du musst also sammeln.

von Alexander H. (ill_son)


Lesenswert?

Hallo Peter,

danke für deinen Post. Serial Port läuft eh in einem eigenen Thread, 
soweit ich weiß. Wie ist das mit dem ReadTimeout, gilt das auch für 
ReadExisting oder nur für ReadTo(string)?

Grüße, Alex

von Peter II (Gast)


Lesenswert?

Alexander H. schrieb:
> danke für deinen Post. Serial Port läuft eh in einem eigenen Thread,
> soweit ich weiß.
nein nicht zwingend.

> Wie ist das mit dem ReadTimeout, gilt das auch für
> ReadExisting oder nur für ReadTo(string)?

ReadExisting macht bei direkten lesen (ohne Event) keinen sinn. Ich 
würde nur mir Read arbeiten.

was für Daten bekommst du? woher kennst du die Länge?

von Alexander H. (ill_son)


Lesenswert?

Die Daten sind Strings als Command Response und Binärdaten die bei einer 
Messung entstanden sind. Da ich die Datenrate und die Messzeit etwa 
kenne, kann ich abschätzen, was zusammenkommt, das kann aber variieren. 
Ich müsste also vorher die Länge senden, damit Read weiß, was kommt, 
oder?

Ich will nicht streiten, aber ich dachte

SerialPort port = new SerialPort();

instanziiert einen Port in einem eigenen Thread. Korrigiere mich bitte, 
wenn ich da falsch liege. Wenn ich solche Daten dartstellen wollte, ging 
das nur über Invoke oder es gab 'ne Cross-Thread-Exception.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Alexander H. schrieb:
> Ich will nicht streiten, aber ich dachte
>
> SerialPort port = new SerialPort();
>
> instanziiert einen Port in einem eigenen Thread.

meines wissens nicht


http://msdn.microsoft.com/de-de/library/system.io.ports.serialport.datareceived(v=vs.110).aspx
> Das DataReceived-Ereignis wird in einem sekundären Thread ausgelöst

ich denke nicht das für jeden com-port ein extra Thread geöffnet wird. 
Es wird wohl ein "globalen" sekundären Thread geben der die 
Schnittstelle mit WaitForMultipleObjekts überwacht.

von Alexander H. (ill_son)


Lesenswert?

Ich fahr mal heim und meld mich dann nochmal. Danke erstmal.

von !! Troll Alarm !! (Gast)


Lesenswert?

Was soll es denn bringen den Port immer wieder zu oeffnen und zu 
schliessen?
Es genuegt den Port bei Beenden zu schliessen. Sonst soll ja eh keine 
Applikation mit dem Device reden koennen.

Eine solche Kommunikation sollte man eh zustandslos machen. Dh das 
device muss verschwinden koennen und wieder kommen, ohne was zu 
blockieren. Daher sollte man die Kommunikation auch nicht blockierend 
machen.

Weshalb und woher sollten undefinierte Zustaende resultieren?

von bluppdidupp (Gast)


Lesenswert?

Bei SerialPort.Open() wird intern ein SerialStream-Objekt erzeugt, 
welches via CreateFile() den COM-Port öffnet, ein paar Einstellungen 
setzt und dann einen (Hintergrund-)Thread startet (pro Objekt, nicht 
global für die SerialPort-Klasse)
Im Thread wird praktisch in Schleife WaitCommEvent() aufgerufen.
Sollte ein Event aufgetreten sein, ruft es 
ThreadPool.QueueUserWorkItem(..) auf worin wiederrum das entsprechende 
Event gefeuert wird ;D

D.h. wenn man z.B. im DataReceived-Event ist, hat die SerialPort-Klasse 
sogar 2 Threads am Laufen (den WaitCommEvent-Event Thread und den 
DataReceived-Thread aus dem ThreadPool ;D)

(so sieht es zumindest mit ILSpy für .net 4.0 aus)
---
wenn man einfach SerialPort.Read() aufruft und die Events (DataReceived, 
etc.) nicht nutzt, hat man von diesen Threads natürlich nichts ;D

von Alexander H. (ill_son)


Lesenswert?

@!!Troll Alarm!!

Das Problem tritt auf, wenn man bei geöffnetem Port einfach das Gerät 
abzieht und damit der COM-Port nicht mehr existiert. Ich muss nochmal 
suchen, wo's stand, ich hatte aber gelesen, dass das nicht gut ist, weil 
im Hintergrund Sachen passieren, die nicht bis zu Applikation 
durchkommen. Hatte irgendwas mit dem Stream zu tun.
Kannst Du mir bitte näher eklären, was du mit zustandslos meinst?

Grüße, Alex

von Alexander H. (ill_son)


Lesenswert?

Salut,

ich hab nochmal recherchiert.

1. Wenn ich das richtig verstanden habe, ist meine Kommunikation schon 
zustandlos, weil der Server (das Gerät) sich keine Zustände merkt, 
sondern bei der Anfrage alle Informationen mitgesendet bekommt.

2. Ich bin dabei, die Kommunikation wie folgt zu implementieren:

Der Port bleibt jetzt immer geöffnet. Das Problem mit der 
Undefiniertheit habe ich hoffentlich gelöst. Ich verwende jetzt das 
DataReceivedEvent. Es gibt einen Timer für ein timeout, anhand dessen 
ich detektieren kann, wenn mitten in der Kommunikation was schief läuft 
und der bei jedem Empfang rückgesetzt wird. Weiterhin habe ich eine enum 
Valiable definiert, die mir den Zustand der Kommunikation liefert und 
anhand der dann die empfangenen Daten behandelt werden.

Ich würde euch nochmal um Verbesserungen und Anregungen bitten.

Grüße, Alex

: Bearbeitet durch User
von Alexander H. (ill_son)


Lesenswert?

up ;)

von jibi (Gast)


Lesenswert?

Moin,

das ist doch Kindergarten, mach doch erstmal und frag dann. Die Daten 
von deinem Messgerät werden bestimmt auch terminiert gesendet (CR/LF), 
also ein readline und danach die daten verarbeiten...wo ist das Problem?

Gruß Jonas

von Alexander H. (ill_son)


Lesenswert?

Auch wenn deine Antwort recht unfreundlich rüberkommt: (CR/LF) war der 
entscheidende Hinweis. Ich hatte nähmlich ein anderes 
Terminierungszeichen verwendet, was aber eigentlich nicht sehr clever 
war. Danke.

PS.: Heute ist Freitag, kein Grund, mürrisch zu sein.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.