RS232 empfangene Daten mit Regex parsen

  • VB.NET

Es gibt 41 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    RodFromGermany schrieb:

    Welchen Wert hat Timer1.Interval?


    Angedacht waren 500, da es in der Textbox mindestens 1x pro Sekunde aktualisiert werden sollte.
    Habe aber auch schon 200 und 1500 MS getestet, ändert nichts.

    Gemäß einer Aufzeichnung mittels Serial Port Monitor die ich leider gerade nicht hier habe, sieht es aber so aus als würde die Platine die Datenpakete immer in der richtigen Reihenfolge rausschicken...

    Also Statusabfrage wird gesendet: Erstes Paket kommt, Abfrage wird wieder gesendet, zweites Paket kommt und so weiter... bloß das Gelegentlich schon Teile vom nächsten Paket dranhängen oder in einer Sequenz als "eine Linie" z.B. zweites Paket Zeile 1, dann Paket für Heartbeat, dann erster Teil für Zeile zwei kommen. Natürlich sind die Start und Ende Kennungen aber immer mit dran...
    @Sbfer Du liest die Daten zwei Mal aus. Klar, dass ere sich da verstolpert!
    Da Du sowieso pollst, ist es vielleicht sinnvoll, das Port.DataReceived-Event nicht zu verwenden und das alles im Timer.Tick-Event abzuhandeln.
    Lösche die DataReceived-Prozedur und probier mal:

    VB.NET-Quellcode

    1. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    2. Dim statusabfrage() As Byte = {&H1, &H88, &H55, &H4}
    3. SerialPort1.Write(statusabfrage, 0, 4)
    4. Sleep(100) ' Pause zur Bearbeitung der Anfrage und Datenübertragung, den Wert musst Du ausprobieren
    5. SerialPort1.ReadLine()
    6. RichTextBox1.Text &= SerialPort1.ReadLine
    7. End Sub

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Sleep(100)


    Ich hab das mal getestet... ich vermute es war Thread.Sleep gemeint? Das friert natürlich (gefühlt) die Form ein. Habe daher für

    RodFromGermany schrieb:

    Sleep(100) ' Pause zur Bearbeitung der Anfrage und Datenübertragung, den Wert musst Du ausprobieren
    SerialPort1.ReadLine()
    RichTextBox1.Text &= SerialPort1.ReadLine

    einen eigenen Timer erstellt. Dann friert nichts mehr ein, aber das Ergebnis ist ähnlich... es fängt erstmal mit dem zweiten Teil der ersten Zeile an, dann folgt der letzte Teil der zweiten Zeile, der einfach in der ersten folgt, dann ploppt der erste Teil für Zeile 1 in der zweiten auf etc.

    Zudem soll das alles in der Textbox ja eigentlich relativ zeitgleich aktualisiert werden.

    Wäre es wirklich nicht einfacher und effektiver die Daten irgendwie per ReadExisting zu sammeln (irgendwie ein Puffer oder auf dämliche Art eine invisible Textbox) und per Regex
    alles von Beginn Zeile 1 bis 0a (NewLine und hier ja auch Beginn der neuen Zeile), und dann von 0a bis Ende Zeile 2 zu matchen und immer wenn beides wieder vollständig ist in die eigentliche Textbox zu übergeben? Damit wäre gleichzeitig das Problem gelöst dass die Daten gleichzeitig in der Textbox aktualisiert werden sollen und man könnte einstellen dass die Textbox immer vorm neuen übergeben gecleared bzw. der Text überschrieben wird, damit nicht kurze Zeit die Box leer ist, sondern beim navigieren durch die Menüs nahtlos die Menüpunkte nacheinander angezeigt werden?

    Ich bin am Anfang davon ausgegangen dass mein Code lediglich nicht richtig die Daten einliest oder das übergeben an die Textbox falsch ist, aber es haben ja gleich 2 User geschrieben dass Regex hier nichts nützt - dann glaube ich das mal ;)
    @Sbfer Sorry, da hab ich Dir doch den unkorrigierten Code gepostet.

    VB.NET-Quellcode

    1. Private Async Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    2. Dim statusabfrage() As Byte = {&H1, &H88, &H55, &H4}
    3. SerialPort1.Write(statusabfrage, 0, 4)
    4. await Task.Delay(100) ' Pause zur Bearbeitung der Anfrage und Datenübertragung, den Wert musst Du ausprobieren
    5. Dim txt = SerialPort1.ReadLine()
    6. ' txt von dden Steuerzeichen befreien
    7. RichTextBox1.Text &= txt
    8. End Sub
    Du hast nicht geschrieben, welches Framework Du verwendest. Mit Async und await Task.Delay(100) blockiert die GUI nicht.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    welches Framework Du verwendest.

    4.7.1.

    RodFromGermany schrieb:

    Mit Async und await Task.Delay(100) blockiert die GUI nicht.


    So, mit einem Delay von 700 klappt es soweit, dass die Reihenfolge (meistens....) stimmt und auch entsprechend angezeigt wird. Aber egal ob Delay 100 oder 5000, bei jedem 2-5 Versuch fängt er mit dem Teil für Zeile 2 in der ersten Zeile an... auch das gleichzeitige aktualisieren könnte so wahrscheinlich schwierig werden... aktuell habe ich

    VB.NET-Quellcode

    1. ​.ScrollToCaret
    im TextChanged stehen, damit er ganz nach unten springt..denn die neuen Daten werden einfach angehängt, statt überschrieben (logisch, ist ja auch nicht anders programmiert). Das hat zur Folge das beim "durchlaufen" dann wieder Daten für die obere Zeile (wenn auch vollständig) in der untersten Zeile stehen, dann in die obere rutschen und die Daten für die untere Zeile kommen hinterher etc...so kann man aber nicht flüssig navigieren.

    z.B. wird im Menü angezeigt:
    Zeile A: -Hauptmenü-
    Zeile B: Option 1 - XX
    bei Taste "Auf" folgt Option 2 - YY etc., wobei Zeile A gleich bleibt etc. da rutscht mit der aktuellen Methode spätestens beim navigieren "-Option 1 - XX" in die obere Zeile, "-Hauptmenü" kommt nach und landet in der unteren etc.

    Müsste ich die Daten irgendwie puffern? Und immer in die Richtextbox wenn Puffer voll?

    RodFromGermany schrieb:

    txt von dden Steuerzeichen befreien

    das, und die Pakete für den Heartbeat wollte ich per

    VB.NET-Quellcode

    1. ' Dim theText As String = RichTextBox1.Text
    2. ' Dim Letter As String
    3. ' Dim charactersAllowed As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789-;.:=!"
    4. ' Dim sel_s As Integer = RichTextBox1.SelectionStart
    5. ' Dim did_change As Boolean = False
    6. ' For x As Integer = 0 To RichTextBox1.Text.Length - 1
    7. ' Letter = RichTextBox1.Text.Substring(x, 1)
    8. 'If charactersAllowed.IndexOf(Letter) = False Then
    9. ' theText = theText.Replace(Letter, String.Empty)
    10. ' did_change = True
    11. ' End If
    12. ' Next
    13. ' RichTextBox1.Text = theText
    14. ' If did_change = False Then
    15. ' RichTextBox1.Select(sel_s, 0)
    16. ' Else
    17. 'RichTextBox1.Select(sel_s - 1, 0)
    18. 'End If

    im TextChanged Event rausfiltern...hat noch nicht so ganz geklappt.

    Übrigens: Mein Code vom ersten Post mit Regex geht logischerweise nicht, weil ich da nur die 01 als Beginn etc. eingetragen habe. Natürlich hätte ich das als Bytes im Hex Format machen müssen, ist mir noch so eingefallen...

    Sbfer schrieb:

    im TextChanged Event rausfiltern
    Das ist völlig falsch.
    Mach das genau da, wo ich es markiert habe.

    Sbfer schrieb:

    Das hat zur Folge das beim "durchlaufen" dann wieder Daten für die obere Zeile (wenn auch vollständig) in der untersten Zeile stehen, dann in die obere rutschen und die Daten für die untere Zeile kommen hinterher etc...so kann man aber nicht flüssig navigieren.
    Was willst Du mir damit sagen?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Mach das genau da, wo ich es markiert habe.

    Erledige ich noch.

    RodFromGermany schrieb:

    Was willst Du mir damit sagen?


    Ich wollte...

    Sbfer schrieb:

    Müsste ich die Daten irgendwie puffern? Und immer in die Richtextbox wenn Puffer voll?

    ...darauf hinaus, weil ich mit meinen Kenntnissen nicht wirklich beurteilen kann, ob es mit deiner Methode möglich ist, ohne "durchlaufen" in der Textbox die Darstellung hinzubekommen, sondern dass der Text immer replaced wird, wenn wieder beide Zeilen vollständig angekommen sind, und beim navigieren dann die obere Zeile (im Menü dauerhaft "Hauptmenü") quasi für den User dauerhaft stehen bleibt und sich nur die Optionen (Option 1, 2, 3...) ändern.

    Sbfer schrieb:

    Müsste ich die Daten irgendwie puffern?
    Bisher hatten wir uns mit dem Empfang von Daten von der RS232 befasst.
    Was soll denn mit den Daten passieren?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Was soll denn mit den Daten passieren?

    Ziel wäre wahrscheinlich die Daten nicht direkt in die Textbox zu empfangen, sondern irgendwo zu speichern (Puffer?) -> dass macht

    RodFromGermany schrieb:

    Dim txt = SerialPort1.ReadLine()


    das ja eigentlich.

    So, nun soll die Textbox immer dann aktualisiert werden, wenn beide Zeilen wieder vollständig empfangen wurden, nicht voher oder nacheinander. So dass beide Zeilen dann zeitgleich aktualisiert werden und der Nutzer beim navigieren keine kurzzeitig leere Textbox, oder "halb gefüllte" Textbox sieht.

    Hier wäre mein Ansatz wieder Regex: txt (empfängt ja die Daten vom Port) mit Regex abfragen und Text in der Textbox immer dann überschreiben, wenn sowohl Zeile 1 als auch Zeile 2 vollständig sind, damit beide gleichzeitig in der TextBox aktualisiert werden. Ebenfalls per Regex würde ich dann die Pakete vom Heartbeat zur Weiterverarbeitung in einen eigenen Sub übergeben.
    Nur wie ich das auch codetechnisch umsetze...da würden wohl noch Fragen aufkommen, zumal ihr beide von Regex abgeraten habt.

    Aber sehe ich es richtig, dass deine Idee

    RodFromGermany schrieb:

    SerialPort1.ReadLine()
    RichTextBox1.Text &= SerialPort1.ReadLine


    nicht ausreicht, um zu erreichen dass die Textbox immer nur mit neuen Daten gefüllt wird, wenn wieder beide Zeilen vollständig empfangen wurden (mindestens 1x / Sekunde) ?


    Immerhin der Empfang an sich funktioniert ja schon :rolleyes:

    Sbfer schrieb:

    dass deine Idee
    Das ist Dein Code (Post #16).
    Kannst Du mal ohne zu palavern schreiben, was mit den empfangenen Daten passieren soll?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Daten werden empfangen und zwischengespeichert, immer ERST wenn BEIDE Zeilen vollständig empfangen wurden, werden diese in die Textbox geschrieben -> User sieht keine halbleere Textbox oder fehlende Datenpakete -> mind 1x pro Sekunde.

    Das soll permanent so gehen.

    Die Datenpakete die nicht für die Textbox sind (Heartbeat, hat eigene Startkennung, endet auch mit 04) wird nach Empfang sofort an eigene Sub weitergegeben und dort verarbeitet.

    RodFromGermany schrieb:

    OK. Wo ist das Problem?

    So wie der aktuelle stand ist, das ganze im Timer mit ReadLine und dem NewLine/ConvertToChar erfüllt

    Sbfer schrieb:

    immer ERST wenn BEIDE Zeilen vollständig empfangen wurden, werden diese in die Textbox geschrieben

    das nicht. Da mehrfach von Regex abgeraten wurde, dachte ich, es gäbe einen Alternativvorschlagg, wie ich das am besten löse ;)

    Ich werde es einfach mal so:

    Sbfer schrieb:

    mit Regex abfragen und Text in der Textbox immer dann überschreiben, wenn sowohl Zeile 1 als auch Zeile 2 vollständig sind, damit beide gleichzeitig in der TextBox aktualisiert werden.

    versuche, und nochmal konkreter schreiben wo ich hänge, falls das der Fall sein wird.

    Sbfer schrieb:

    immer ERST wenn BEIDE Zeilen vollständig empfangen wurden
    Kommen IMMER genau zwei Zeilen gepaart an?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Kommen IMMER genau zwei Zeilen gepaart an?

    Rausgeschickt werden die vier Pakete immer in der richtigen Reihenfolge.
    1. Erstes Paket Zeile A
    2. Zweites Paket Zeile A
    3. Erstes Paket Zeile B
    4. Zweites Paket Zeile B

    Angezeigt / aktualisiert werden sollen sie stets zeitgleich.

    Dazwischen kann noch das Paket für den Heartbeat hängen.

    Angezeigt werden die Pakete, egal mit welcher Verzögerung bei Task.Delay, nur meistens in der richtigen Reihenfolge, da beginnt er alle paar Versuche mal mit einem Paket aus der unteren Zeile in der oberen und hängt danach alles dran.
    @Sbfer Wenn Du die Pakete unterscheiden kannst, packst Du einfach ein erstes Paket in eine Variable, und wenn das zweite Paket da ist, hängst Du es an diese Variable und packst beide gemeinsam in die TextBox.
    Dann löschst Du die Variable wieder.
    Feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Soooooo, also:
    da ich schon nicht wusste wie ich diverse Vorschläge "in Code" ausdrücke ,habe ich es nun folgendermaßen gelöst:

    VB.NET-Quellcode

    1. Dim komplettedaten As String = SerialPort1.ReadExisting
    2. Dim zeinsa As Regex = New Regex("(?<=\x07).*(?=\x04)") ' erste Zeile
    3. Dim match As Match = zeinsa.Match(komplettedaten)
    4. If match.Success Then RichTextBox1.Text = match.Value
    5. Dim zeinsb As Regex = New Regex("(?<=\x08).*(?=\x04)") 'zweite Zeile
    6. Dim match2 As Match = zeinsb.Match(komplettedaten)
    7. If match2.Success Then RichTextBox1.Text &= match2.Value & vbNewLine
    8. Dim zzweia As Regex = New Regex("(?<=\x0a).*(?=\x04)") 'dritte
    9. Dim match3 As Match = zzweia.Match(komplettedaten)
    10. If match3.Success Then RichTextBox1.Text &= match3.Value
    11. Dim zzweib As Regex = New Regex("(?<=\x09).*(?=\x03)") 'vierte
    12. Dim match4 As Match = zzweib.Match(komplettedaten)
    13. If match4.Success Then RichTextBox1.Text &= match4.Value



    Nun sind die Daten schön korrekt, und es ist alles richtig formatiert.

    So, nun hätte ich nur noch zwei Fragen...dann komme ich alleine zurecht ;)

    Bei meiner Lösung wird die Textbox immer neu aufgebaut, erste Zeile Teil 1 überschreibt sich immer, man sieht also die "wechselnden Wörter", während für den zweiten Teil der ersten Zeile und beide Teile in der letzten Zeile die Textbox immer kurzzeitig leer ist, und man eine halbe Sekunde sieht, wie die Textteile nacheinander angehängt werden.

    Die Textbox soll aber erst überschrieben werden wenn alle 4 Teile wieder vollständig da sind, dass der Nutzer also nicht sieht, wie die Teile immer wieder angehängt werden. denn so ist die Textbox immer wieder kurz blank (bis auf den ersten Teil der oberen Zeile)

    Ich habe schon einiges getestet, führte aber alles so Chaos und nicht zum Ziel, hat da jemand eine einfache Lösung wo ich meine Regex Methode weiter benutzen kann? Textbox erst überschreiben wenn beide Zeilen komplett sind.

    Letztes Problem:

    Die Daten für den Heartbeat beginnen mit 01 79 und enden auch mit 04 (wie alles andere), hier funktioniert mein Regex Ausdruck nicht: ("(?<=\x79).*(?=\x04)")

    Alles zwischen 01 79 und 04 soll erstmal zwischengespeichert werden (in einem String). Testweise wollte ich diese Daten auch in der Textbox anzeigen lassen, wird aber nicht angezeigt.

    Vermutete Ursache: Als Ascii sind die Heartbeat Daten alle nur irgendwelche Zeichen (z.B. $, ],§) etc. - Kann das der Grund sein, wieso das matchen nicht funktioniert? Müsste ich hier ein anderes Encoding verwenden? Für die Daten in der Textbox funktioniert das alles, nur die Heartbeat Daten sollen direkt als Hex weiterverarbeitet werden.

    Jut, das klappt sicher auch noch irgendwie...

    Sbfer schrieb:

    Müsste ich hier ein anderes Encoding verwenden?
    Was sagt die Beschreibung der Gegenstelle?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Sbfer schreib einfach deine Ergebnisse in den match.Success Klauseln nicht direkt ins Richtext-Fenster sondern in eine String Variable.
    nachdem Du in match4.Success die Varible befüllt hast, Schreibe deren Inhalt ins Richtext-Fenster. Noch die Variable auf leeren String setzen...
    Kunst kommt von können und nicht von wollen, sonst hieße es Wunst!