Laden...

RS232: Wird DataRecieved (zuverlässig) gefeuert, wenn das Gerät alle Daten gesendet hat?

Erstellt von Console32 vor 12 Jahren Letzter Beitrag vor 11 Jahren 3.335 Views
C
Console32 Themenstarter:in
258 Beiträge seit 2011
vor 12 Jahren
RS232: Wird DataRecieved (zuverlässig) gefeuert, wenn das Gerät alle Daten gesendet hat?

Hallo Community,

Ich habe folgendes Problem mit einer externen Schnittstelle:
Die externe schnttstelle läuft in einer while(true) schleife mit Sleep,

Ich wollte das ganze nach möglichkeit auf DataRecieved umprogrammieren..
Aber ich habe in der MSDN gelesen das das DataRecieved event nicht bei jedem Byte feuert:

Vorweg die Schnitstelle verwendet Xon/Xoff (keine Ahnung ob das einen Einfluss hat).
Das Problem das ich dabei habe ist das ich auf eine Bestimmte länge warten muss (zB. 243 Bytes) wenn dann das DataRecievedEvent beim 243Byte nicht ausgelöst wird habe ich ein Problem weil dannach der Controller nichts mehr sendet.

Nun ist meine Frage, wird bei dem letzten Byte das Event immer getriggert? oder kann es sein das ich im DataRecieved nichts mache weil 1 Byte fehlt und es dannach nichtmehr getriggert wird?

Wollte nur sichergehen bevor ich etwas Funktionierendes auseinander nehme

185 Beiträge seit 2005
vor 12 Jahren

hast du

Template SerialPort

schon gelesen?

C
Console32 Themenstarter:in
258 Beiträge seit 2011
vor 12 Jahren

ja ich hab mir das Template schonmal angesehen, das template fragt auf einen Environment.NewLine ab.

Bei mir wird die Länge des Telegrams aus bestimmten einstellungen berechnet. Somit müsste ich auf die Länge Prüfen, meine Frage bleibt aber trotzdem:

Kann ich mich darauf verlassen das das Recieve Data Event bei meinem Letzten Byte auch getriggert wird. Ich finde leider gerade den Link nichtmehr bei dem Steht das es passieren kann dass das Event nicht bei jedem Byte getriggert wird

N
191 Beiträge seit 2007
vor 12 Jahren

Ich muß relativ viel mit SerialPort machen, und mir ist nicht aufgefallen dass ich in dem Event schon Pakete verloren habe.

"If you give someone a
program, you will frustrate them
for a day; if you teach them how to
program, you will frustrate them
for a lifetime." :evil:

M
78 Beiträge seit 2007
vor 12 Jahren

In meinen Anwendungen nutze ich auch schon lange die SerialPort-Klasse für diverse Hardwareansteuerungen (teils 8 Stunden Dauerbetrieb). Bisher hatte ich mit dem DataReceived-Event noch keine Probleme und alle Bytes sind angekommen.
Da die Bytes blockweise ankommen, lese ich diese aus und füge sie in eine Liste/Queue (Instanzvariable) hinzu. Danach mache ich die Auswertung, sodass die ankommenden Daten als Nachricht interpretiert werden.

Entscheidend ist natürlich auch das Protokoll/die Befehlsbeschreibung. Meist hatte ich das Glück, dass ein eindeutiger Delimiter/Separator (z.B. 0xFF) definiert ist und z.B. das nachfolgende Byte die Länge der Datenbytes angibt. So geht die Auswertung wunderbar, selbst wenn von einer Nachricht erstmal nur ein paar Bytes ankommen.

Auf eine bestimmte Länge warten. Hmmm... sowas ähnliches hatte ich neulich auch mal.
Da war das Protokoll sehr simpel aufgebaut. Es legte nur fest, dass eine Nachricht 13 Bytes lang ist. D.h. also im DataReceived-Event wiederum mit einer Liste/Queue (Instanzvariable) arbeiten. Bei der Auswertung dann jeweils 13 Bytes als Nachricht interpretieren, solange die Listenlänge ≥13 ist.
Das war auch ein Gerät, das ungefragt Nachrichten an den PC sendet (also kein Polling bzw. Request/Response).

Eine Sorge habe ich allerdings noch heute: Bei Geräten (RS232 bzw. USB via VCP) sind vermutlich diverse Puffer im Hintergrund.
Solch eine Auswertung mit dem Zählen von Bytes würde total ins Hemd gehen, wenn nach serialPort.Open() im Empfangspuffer noch "Müll-Bytes" z.B. von der letzten Verbindung bestehen. Dann kommt das Zählen total durcheinander. Mir ist das noch nicht passiert und auch andere Kollegen konnten mir keine Auskunft darüber geben.
Jemand hat mir mal empfohlen nach dem serialPort.Open() einfach mit Read() den Empfangspuffer zu leeren. Meiner Ansicht nach, kann das aber auch nach hinten losgehen, wenn da das Gerät schon mit dem Senden angefangen hat und man womöglich relevante Bytes ins Nirwana wirft.

C
Console32 Themenstarter:in
258 Beiträge seit 2011
vor 11 Jahren

Hallo,

Danke für die Rückmeldungen, klingt ja recht gut.
Somit werd ichs einfach versuchen.

T
381 Beiträge seit 2009
vor 11 Jahren

Es gibt 2 Bedingungen damit das Event gefeuert wird.

  1. eine konfigurierte Anzahl Bytes wurde empfangen
  2. es sind Daten vorhanden und eine konfigurierte Zeit ist vergangen

Diese beiden Werte können in der SerialPort Klasse eingestellt werden. Paramternamen habe ich nicht im Kopf.

In Zeitkritischen Anwendungen und in Multithreadanwendungen kann dieses Verhalten durchaus Probleme verursachen. In den meisten Anwendungen wird es aber ausreichen.

Der sicherste Weg wäre ein eigener Thread der auf neue Daten pollt. Dann hat man alles was ankommt und in welchen Threads es verarbeitet wird selbst in der Hand.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Tarion,

wenn man fürs Polling einen Timer (oder Thread.Sleep) verwendet, damit die CPU-Last des Kerns nicht auf 100% geht, und dabei die Zeitspanne wählt, die man auch bei SerialPort angibt, gewinnt man durch das manuelle Polling allerdings nichts.

herbivore

T
381 Beiträge seit 2009
vor 11 Jahren

Ich habe selbst wenig Erfahrungen, vor Kurzem habe ich aber von einem Kollegen der sich tiefgehender mit dem Thema beschäftigt hat das für spezielle Anwendungen das Event nicht mehr ausreicht. Aber angeblich sind ihm durch die Events in bestimmten fällen daten verloren gegangen oder in der falschen Reihenfolge angekommen. Das hängt aber auch von der Peripherie ab, wie USB/Seriell Wandler, etc. und ist kein reines C# Problem gewesen.

C
Console32 Themenstarter:in
258 Beiträge seit 2011
vor 11 Jahren

Da die Kommunikation als Request/Response abläuft habe ich mit einem ManualResetEvent einfach den Thread stillgelegt bis die gewünschte anzahl an daten vorhanden ist, oder eine gewisse Zeit keine Daten ankommen.


while (true)
                {
                    this.waitingForData.Reset();
                    inputBufferLength = this.Read();
                    if (inputBufferLength >= 6)
                    {
                        header.BufferToHeader(this.inputBuffer);

                        if (inputBufferLength >= (header.wBlockSize + 8))
                            break;
                    }
                    if (!this.waitingForData.WaitOne(timeout))
                        throw new TimeOutExpection("Connection timed out");
                }

Soweit verliere ich auch keine Daten und hab keine Probleme mit Puffern von USB / Serial Convertern, Danke für die Rückmeldungen

C
Console32 Themenstarter:in
258 Beiträge seit 2011
vor 11 Jahren

Hallo,

Da ich letzte Woche den oben Genannten Code auf ein Paar Systemen getestet habe musste ich feststellen das er bei älteren System / hoher Porzessorauslastung nicht mehr funktioniert. Ein tatsächlicher Hardwarefehlern kann ausgeschlossen werden.

Das Problem ist das die serialPort.BytesToRead Property manchmal (sehr sporadisch) nichtmehr die gewünschten Byte enthält --> es wird dann eine TimeOutException ausgelöst. (Obwohl die Bytes definitv vom Controller gesendet werden).

Selbst wenn ich an der stelle der eigentlichen Exception im Debugger nocheinmal die BytesToRead Property einsehe stimmen diese Bytes nicht!

Habe ich irgendwo eine Race-Condition übersehen?

Der alte Code den ich "verschlimbessert" habe funktioniert bei allen Systemen gleich "schlecht" aber er funktioniert. Der alte Code war in etwa so (Pseudo Code):


while(true)
{
   rxLen = Read();
   if(rxLen >= 6)
      .... auswertung etc.

   else
      wiviel Zeit ist seit beginn der Aktion vergangen  
      wenn diese Zeit > timeout
         throw new TimoutException
   Thread.Sleep(50);
}
C
Console32 Themenstarter:in
258 Beiträge seit 2011
vor 11 Jahren

Ich bump den Thread mal leider besteht das Problem immer noch.

U
1.688 Beiträge seit 2007
vor 11 Jahren

Da die Kommunikation als Request/Response abläuft

Dann kannst Du doch den ReadTimeout vom Serialport benutzen - einfach nur "Read" aufrufen und schauen, ob danach Deine Daten komplett sind oder eben eine TimeoutException ausgelöst wurde. Natürlich ohne DataReceived-Event.
Edit: Ggf. musst Du Deine Daten auch "sammeln". Es kann ja sein, dass 6 Bytes in "Häppchen" von 4 und 2 Bytes (oder anders) ankommen.

Hast Du Deine Kommunikation auch schon mal mit einem SerialPort-Sniffer überprüft?

C
Console32 Themenstarter:in
258 Beiträge seit 2011
vor 11 Jahren

Danke mit dem SerialPort.ReadTimeout funktioniert es einwandfrei auch auf älteren System.

Die Kommunikation habe ich natürlich mit einem Sniffer überwacht 😉