Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

Serielle Schnittstelle - DataReceivedEvent ( Daten richtig einlesen und Parsen )
Janiiix3
myCSharp.de - Member



Dabei seit:
Beiträge: 42
Herkunft: Hannover

Themenstarter:

Serielle Schnittstelle - DataReceivedEvent ( Daten richtig einlesen und Parsen )

beantworten | zitieren | melden

Hallo liebe Community,

ich bin am verzweifeln ( ist ja nichts neues bei mir :-P )..
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);
}
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15510
Herkunft: BW

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Janiiix3
myCSharp.de - Member



Dabei seit:
Beiträge: 42
Herkunft: Hannover

Themenstarter:

beantworten | zitieren | melden

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.
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Janiiix3 am .
private Nachricht | Beiträge des Benutzers
Janiiix3
myCSharp.de - Member



Dabei seit:
Beiträge: 42
Herkunft: Hannover

Themenstarter:

beantworten | zitieren | melden

Hat sonst noch jemand eine Idee?
private Nachricht | Beiträge des Benutzers
witte
myCSharp.de - Member



Dabei seit:
Beiträge: 958

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Janiiix3
myCSharp.de - Member



Dabei seit:
Beiträge: 42
Herkunft: Hannover

Themenstarter:

beantworten | zitieren | melden

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..
private Nachricht | Beiträge des Benutzers
chilic
myCSharp.de - Experte



Dabei seit:
Beiträge: 2111

beantworten | zitieren | melden

Zitat
Woher weiß ich jetzt wie viele Daten ich mit diesem Kommando noch erwarte?
Hm vielleicht ...
Zitat
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.
Zitat
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?
private Nachricht | Beiträge des Benutzers
Janiiix3
myCSharp.de - Member



Dabei seit:
Beiträge: 42
Herkunft: Hannover

Themenstarter:

beantworten | zitieren | melden

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..
private Nachricht | Beiträge des Benutzers
chilic
myCSharp.de - Experte



Dabei seit:
Beiträge: 2111

beantworten | zitieren | melden

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.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von chilic am .
private Nachricht | Beiträge des Benutzers
kuppi
myCSharp.de - Member



Dabei seit:
Beiträge: 44

beantworten | zitieren | melden

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
private Nachricht | Beiträge des Benutzers
Janiiix3
myCSharp.de - Member



Dabei seit:
Beiträge: 42
Herkunft: Hannover

Themenstarter:

beantworten | zitieren | melden

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?
private Nachricht | Beiträge des Benutzers
kuppi
myCSharp.de - Member



Dabei seit:
Beiträge: 44

beantworten | zitieren | melden

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
private Nachricht | Beiträge des Benutzers