Laden...

Serielle Schnittstelle - DataReceivedEvent ( Daten richtig einlesen und Parsen )

Erstellt von Janiiix3 vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.178 Views
J
Janiiix3 Themenstarter:in
38 Beiträge seit 2015
vor 5 Jahren
Serielle Schnittstelle - DataReceivedEvent ( Daten richtig einlesen und Parsen )

Hallo liebe Community,

ich bin am verzweifeln ( ist ja nichts neues bei mir 😛 )..
Irgendwie bekomme ich es nicht anständig hin, dass ich die eingehenden Daten sammele und verarbeite..
Bei höheren Baudraten ≥ 115200 funktioniert es besser oder gar überhaupt also mit niedrigeren Baudraten.

Vermutlich ist das Probleme, das die Daten bei niedrigeren Baudraten langsamer eintreffen und das Event mehrmals aufgerufen wird.. Bei höheren sind manchmal bei der Auswertung schon alle Daten da..

Das ganze soll nach folgendem Protokoll laufen ->
Das Ende erfahre ich halt an dem "Nachrichtenlänge" Byte..

Hat jemand eine Idee, was ich besser machen könnte?!


public void Client_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    /* Protokoll
     *
     *  Startbyte[0]                    : '-'
     *  Startbyte[1]                    : '+'
     *  Nachrichtenlänge[2]             : 0..255
     *  Anzahl Nutzdaten[3]             : 0..255
     *  Daten Type[4]                   : 0..6
     *  Nachrichten Identifikation[5]   : 0..255
     *  Funktionsrückgabe Code[6]       : 0..255
     *  Checksumme[7]                   : 0..255
     *  Nutzdaten[8..n]                 : 0..255
    */
 
    Thread.Sleep(10);
 
    /*  Empfangene Daten abholen
    */
    try
    {
        Length += Client.Read(buffer, Length, 250);
    }
    catch { }
 
    /*  Kommando Start Parsen
     *  Rückgabewert: - 1 = Kein Start gefunden..
    */
    int index = Parser.ParseStart(buffer);
    if (index != -1)
    {
        /*  Anzahl der zu empfangenen Bytes auslesen
            * HIER WIRD NOCH KEIN CRC BERECHNET!
            * Es könnten Übertragungsfehler nicht erkannt werden..
        */
        BytesToReceive = buffer[index + (byte)Cmd.Communication_Header_Enum.CMD_HEADER_LENGHT];
    }
 
    /*  Wurden alle Bytes empfangen?
        *  Wenn nicht, direkt wieder raus hier!
    */
    if (Length < BytesToReceive)
    {
        return;
    }
    else
    {
        Length = 0;
        BytesToReceive = 0;
    }
 
    /*  Kommando untersuchen..
    */
    Parser.Parse(buffer, ref Cmd.CommandoParsed);
}

16.806 Beiträge seit 2008
vor 5 Jahren

das Thread.Sleep(10) ist extrem unnützlich; davon abgesehen, dass 10ms sowieso unrealistisch sind aufgrund der Auflösung.

Du kannst Dich niemals darauf verlassen, dass alle Daten in einem Event kommen.
Du musst die Daten im Event lesen, die bisher da sind und zwischenspeichern; und die Verarbeitung dann antriggern, sobald Deine Daten nach Deiner Definition vollständig sind.

J
Janiiix3 Themenstarter:in
38 Beiträge seit 2015
vor 5 Jahren

Das mit dem Sleep war ja auch nur zum testen. Es läuft stabiler als ohne.
Na das tue ich doch schon. Ich sammele solange Daten ein bis ich laut meines empfangenen Kommandos fertig bin.

Das läuft ja eben sehr unstabil.

J
Janiiix3 Themenstarter:in
38 Beiträge seit 2015
vor 5 Jahren

Hat sonst noch jemand eine Idee?

W
955 Beiträge seit 2010
vor 5 Jahren

Naja, wie sollen wir dir helfen? Du sagst nicht was fehlerhaft passiert und bekommst es ja selber nicht mit da du die Exception einfach verschluckst.

J
Janiiix3 Themenstarter:in
38 Beiträge seit 2015
vor 5 Jahren

Mein Problem besteht darin, dass ich es nicht wirklich hin bekomme meine Daten in den "Puffer" zu parken.

Ich sende von einem Mikrocontroller zwei Start Bytes ('-' '+') kommen diese beiden Zeichen an, weiß ich das ab hier der Header von meinem Kommando beginnt. Woher weiß ich jetzt wie viele Daten ich mit diesem Kommando noch erwarte?

Das dritte Byte beinhaltet die gesamte Länge des Kommandos incl. Nutzdaten. Der Header an sich ist 8 Bytes groß.

Da ich in diesem Sinne kein "EndOfTransmission" benutze, ist es halt von dem dritten Byte abhängig.
Das ganze Kommando muss so in einen Speicher landen damit ich es später "Pasen" kann. Das gelingt mir nicht.

Die Daten kommen vom Mikrcontroller definitv an. Nur sieht es irgendwie aus als wenn sie durcheinander eintreffen..

C
2.121 Beiträge seit 2010
vor 5 Jahren

Woher weiß ich jetzt wie viele Daten ich mit diesem Kommando noch erwarte?

Hm vielleicht ...

Das dritte Byte beinhaltet die gesamte Länge des Kommandos incl. Nutzdaten.

daher?

Du brauchst eine Datenstruktur die Daten sammelt. Die genügend Platz hat und weiß wie viele Daten bisher angekommen sind. Die nächsten Daten hängt sie dann hinten dran.

Nur sieht es irgendwie aus als wenn sie durcheinander eintreffen.

Das ist nochmal ein anderes Problem, das über die serielle Schnittstelle nicht auftreten darf. Sieht es vielleicht nur so aus weil du einiges verschluckst?

J
Janiiix3 Themenstarter:in
38 Beiträge seit 2015
vor 5 Jahren

Ich vermute mal eher das ich das ganze nicht richtig Händel.
Habe meinen Code Snippet oben gepostet.
Was sagst du zu dem? Der soll das eigentlich machen..

C
2.121 Beiträge seit 2010
vor 5 Jahren

Da fehlt noch einiges an Fehlerhandling. Etwa wenn mehr Bytes ankommen als Platz ist. Da solltest du höchstens so viele Bytes lesen wie in den aktuellen Puffer passen.
Oder wenn mehrere Nachrichten aneinanderhängen, was durchaus passieren könnte.
Oder wenn zuerst aus welchem Grund auch immer Müll ankommt, der den Anfang deines Puffers füllt und die eigentliche Nachricht nicht mehr ganz in den Puffer hineinpasst.

Wo du genauer hinsehen solltest ist das if (Length < BytesToReceive).
Wenn mehr Bytes da sind als benötigt, also wenn (Length > BytesToReceive), gehst du auch zum Parser. Diesen Fall musst du abfangen und dann die überschüssigen Bytes für die nächste Untersuchung aufheben.

Möglicherweise ist das dein Problem. Bei langsamerer Übertragung wäre es denkbar dass das Ende einer Nachricht und der Start der nächsten als gemeinsames Paket ankommen. Dann hast du den Start des neuen Pakets noch mit im vorigen drin, wirfst es mit dem vorigen Datenpaket weg und findest es bei der nächsten Suche nicht mehr.

Genaueres müsstest du mit dem Debugger herausfinden. Lass dir ausgeben wie viele Bytes du gelesen hast und wann du was erkannt hast. Oder wenns nicht zu viele Daten sind schreib alle gelesenen Daten in eine Datei, am besten lesbar als Hex, und dazu die Info wann du was erkannt hast.

K
44 Beiträge seit 2006
vor 5 Jahren

Hallo Janiiix3,

wie erkennst Du den den Anfang deines Datenblocks?
Die ersten beiden bytes sollen die Chars '+' und '-' sein, aber die sind ja dezimal 43 und 45.
Und diese Werte können auch in deinen anderen empfangenen bytes vorkommen.
Wäre zwar Zufall, aber ist nicht ausgeschlossen dass 43 und 45 nebeneinander auftauchen.

Gruß Jürgen

J
Janiiix3 Themenstarter:in
38 Beiträge seit 2015
vor 5 Jahren

Nabend Jürgen,

ich habe jetzt schon von mehreren gehört, das dass eine nicht gerade tolle Idee ist, das so zu machen.
Wie du schon richtig erkannt hast, kann diese Kombination auch in den Nutzdaten vorkommen. Somit hätte ich dann zwei Startbedinungen.

Ich bin immer noch auf der Suche nach einer sauberen Lösung, hast du evtl. eine Idee für mich?

K
44 Beiträge seit 2006
vor 5 Jahren

Hallo Janiiix3,

ich habe einen Arduino von dem ich Daten via comport empfange.
Ich send keine Binärdaten sondern Strings, das ist zwar nicht so kompakt aber problemloser.
Als Endmarke habe ich CR der LF oder beides.
Die Endmarke kannst Du in einer Eigenschaft der Comportklasse angeben.
Die Daten lese ich mit der .Readline() Methode der Comportklasse (ist blockiered) in einem eigenen Thread ein.
Anschließend werden die Strings in eine SyncQueue (von Herbivore hier aus dem Forum) abgelegt.
Diese Strings werden dann im Hauptthread oder einem anderen geparst.
Wenn Du Einfluss auf das Protokoll hast wäre das eine Möglichkeit.

Gruß Jürgen