RS232 empfangene Daten mit Regex parsen

  • VB.NET

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

    RS232 empfangene Daten mit Regex parsen

    Hallo zusammen,

    ich weiß, bei Fragen zu Regex rollen einige wahrscheinlich schon mit den Augen :D

    Folgende Situation:
    Ich möchte die Daten von einer Platine, die per RS232 Adapter an den PC angeschlossen ist, in einer Rich Textbox darstellen, es handelt sich dabei quasi um das Menü, mit entsprechend programmierten Buttons wird dann gesteuert.

    Da einige Werte (je nach geöffnetem Menü) in Echtzeit angezeigt werden, wird die Anfrage an die Platine alle 200 Millisekunden gesendet. Daher kommen auch die Daten von der Platine permanent an.

    Nun zum eigentliche Problem: Es gibt zwei Zeilen, und die Platine sendet zwei Datenpakete PRO Zeile, also insgesamt 4. Allerdings haben diese Pakete keine "Ende-Kennung", sondern nur jeweils eine eigene Start-Kennung. Außerdem gibt es alle paar Minuten weiter Daten, die NICHT in der Textbox, sondern in einem Label dargestellt werden sollen.

    Daher wollte ich Regex nutzen, um den Empfang übersichtlich zu machen und zu verarbeiten.

    Ich habe nun folgenden Code, der aber nicht funktioniert: Die Rich-Textbox bleibt leer bzw. wenn ich mauell etwas tippe verschwindet das sofort wieder. Aber Daten werden nicht angezeigt.

    VB.NET-Quellcode

    1. SerialPort1.ReadExisting()
    2. Dim zeinsa As Regex = New Regex("(?<=XX).*(?=YY)")
    3. Dim match As Match = zeinsa.Match(SerialPort1.ReadExisting)
    4. If match.Success Then RichTextBox1.Text = match.Value
    5. Dim zeinsb As Regex = New Regex("(?<=XX).*(?=YY)")
    6. Dim match2 As Match = zeinsb.Match(SerialPort1.ReadExisting)
    7. If match2.Success Then RichTextBox1.Text &= match2.Value
    8. Dim zzweia As Regex = New Regex("(?<=XX).*(?=YY)")
    9. Dim match3 As Match = zzweia.Match(SerialPort1.ReadExisting)
    10. If match3.Success Then RichTextBox1.Text &= match3.Value
    11. Dim zzweib As Regex = New Regex("(?<=XX).*(?=YY)")
    12. Dim match4 As Match = zzweib.Match(SerialPort1.ReadExisting)
    13. If match4.Success Then RichTextBox1.Text &= match4.Value
    14. RichTextBox1.Text = match.Value & match2.Value & match3.Value & match4.Value ' ///nur testweise ausprobiert


    Die Regex Funktion an sich (alles zwischen den Zeichen XX und YY anzeigen) sollte so stimmen. XX und YY habe ich hier der Übersichtlichkeit halber eingesetzt, ist im Code anders.
    "zeinsa, zeinsb, zzweia, zzweib" stehen hier für die Datenpakete der Zeilen.

    Ich habe den Code sowohl in einem Timer, als auch in "SerialPort1.DataReceived", als auch im TextChanged der Textbox selbst ausprobiert...alles ohne Erfolg.
    Hat jemand eine Ahnung was ich falsch mache bzw. an was das liegen kann? Die Aktualisierung soll alle 500 MS erfolgen (testweise erstmal), daher hab ich auch den Timer entsprechend eingestellt.
    Die "Imports" Funktion für RegularExpressions ist natürlich auch da...

    Vielen Dank im Voraus
    @Sbfer Willkommen im Forum.
    Kann es sein, dass die Länge der Datenpakete gleich ist?
    Poste mal ein paar solcher Datenpakete zur Ansicht.
    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!
    Hallo erstmal,

    die Problematik besteht in der Tatsache, dass es kein "Übertragungs-Ende-Signal" gibt.
    Die Daten vom Com-Port können zu dem Zeitpunkt des Abholens evtl. noch gar nicht vollständig übertragen worden sein.
    Kunst kommt von können und nicht von wollen, sonst hieße es Wunst!
    @xmise Genau.
    Deswegen müssen wir wissen, was die Gegenstelle sendet.
    Danach klären wir das Weitere. ;)
    Dazu allerdings muss sich @Sbfer melden.
    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:

    Willkommen im Forum.
    Danke ;)


    xmise schrieb:

    Die Daten vom Com-Port können zu dem Zeitpunkt des Abholens evtl. noch gar nicht vollständig übertragen worden sein.


    Wenn ich mir das so anschaue... da die Abfrage alle 200 Millisekunden erfolgt, antwortet die Platine innerhalb einer Sekunde in der Regel mindestens 2x mit jedem Datenpaket.

    RodFromGermany schrieb:

    Kann es sein, dass die Länge der Datenpakete gleich ist?


    Ja und Nein, die Daten, die für die Textbox bestimmt sind, haben immer die gleiche Länge, also alle Pakete gleich lang. Allerdings sendet die Platine auch noch eine Art Heartbeat mit Daten, die extra weiterverarbeitet werden müssen (dafür ist ein eigener Sub geplant) - diese haben eine andere Länge als alles, was für die Textbox ist. Diese Daten sollen dann nicht in der Textbox erscheinen, enden aber auch mit "04" wie ALLES was die Platine ausspuckt, lediglich eine eigene "Beginn-Kennung" haben diese Daten. Hier werden dann abhängig von dem, was die Platine sendet, bestimmte Antworten erwartet, das regelt dann ein eigener Sub.

    Hier erstmal ein Beispiel für die Datenpakete für die Textbox

    ​01 07 20 20 20 4E 65 74 7A 62 65 04 // "Netzbe" Paket 1 erste Zeile
    01 08 74 72 69 65 62 20 20 20 20 04 01 0a 57 65 69 74 65 72 20 6D 69 04 // "trieb" Paket 2 erste Zeile + zweite Zeile Paket 1
    01 09 74 20 45 6E 74 65 72 20 20 04 // zweite Zeile Paket 2


    Für Regex Zeile 1 Paket A habe ich die 07 (01 07 beginnt erste Zeile) bis 08 (01 08 beginnt nächstes Paket) matchen lassen.
    Für Regex Zeile 2 dann 0a ("zufällig" beginnt mit 01 0a die neue Zeile) bis 09 (mit 01 09 beginnt das letzte Paket der zweiten Zeile).

    Danke...

    Sbfer schrieb:

    Diese Daten sollen dann nicht in der Textbox erscheinen,
    Das ist momentan irrelevant.
    Lass auch das RegEx außen vor.
    Zunächst müssen wir in der Lage sein, die Daten korrekt zu identifizieren.
    Wenn ich Deine Daten anders aufteile:

    Quellcode

    1. 01 07 20 20 20 4E 65 74 7A 62 65 04 // "Netzbe" Paket 1 erste Zeile
    2. 01 08 74 72 69 65 62 20 20 20 20 04
    3. 01 0a 57 65 69 74 65 72 20 6D 69 04 // "trieb" Paket 2 erste Zeile + zweite Zeile Paket 1
    4. 01 09 74 20 45 6E 74 65 72 20 20 04 // zweite Zeile Paket 2
    fangen alle Blöcke mit 01 an und hören mit 04 auf.
    Ist das zu verallgemeinern?
    Wenn da Blöcke intern zwei zusammengehören, setzen wir die hinterher wieder zusammen.
    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
    Die Zerlegung ist richtig. Die Frage ist doch wann eine Nachricht Zuende ist.
    die 01 08 also die ersten beiden Zeichen leiten einen Datensatz ein, während die 04 das "Datenwort" beendet.
    Das sind bisher 12 Byte. Damit kann man die "Steuerzeichen" ausfiltern und die Nutzdaten herausziehen.

    @Sbfer

    Was wird denn noch an Nachrichten gesendet ausser "Netzbetrieb" und "Weiter mit Enter" ?


    Kunst kommt von können und nicht von wollen, sonst hieße es Wunst!

    xmise schrieb:

    ne Idee zum 2. Byte?
    Sieht aus wie eine Befehlskennung.
    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:

    fangen alle Blöcke mit 01 an und hören mit 04 auf.
    Ist das zu verallgemeinern?


    Das ist korrekt, ausnahmslos alles beginnt mit 01 und endet mit 04.

    xmise schrieb:

    Was wird denn noch an Nachrichten gesendet ausser "Netzbetrieb" und "Weiter mit Enter" ?

    Mehrere Hundert Menüs, Texte, Zustände von Ein und Ausgängen...deshalb dachte ich es wäre am einfachsten per RegEx zu sagen "alles zwischen Beginn und Ende matchen" und zeitgleich das zu nutzen um alle anderen Daten, die nicht in die Textbox sollen, weiterzuverarbeiten.

    Eierlein schrieb:

    04 -> EOT (End of Transmission)


    Ja, meinte nur dass leider die Pakete keine eigenen, individuellen Enden haben.

    "ReadLine" und als NewLine die 0a einsetzen, mit der alles für Zeile 2 beginnt, klappt übrigens auch nicht.
    Bei ReadExistring flackern kurz einzelne Teile auf, teilweise Text für Zeile 2 in der ersten Zeile etc.

    Ich sollte noch anmerken: Je nach Menü (z.B. Datum / Uhrzeit) gehören Zeilen 1 und 2 zusammen, die Textbox soll also am Ende immer beide Zeilen zusammen, bestenfalls mindestens 1x pro Sekunde aktualisieren.

    Sbfer schrieb:

    Ja, meinte nur dass leider die Pakete keine eigenen, individuellen Enden haben.
    Du hast eine Property des SerialPorts .NeweLine. Setze ihn auf Convert.ToChar(4).ToString() und Du kannst Deine Kommunikation blockweise organisieren.
    Vergiss RegEx, das bringt bei Deinem Problem nix.
    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:

    Du hast eine Property des SerialPorts .NeweLine. Setze ihn auf Convert.ToChar(4).ToString()


    Eben erledigt - ähnlich wie bei ReadExisting sieht man immer kurz ein halbes Wort...jetzt zwar keine Daten für Zeile 2 in der ersten mehr... aber korrekt angezeigt wird trotzem nichts. Ich vermute das reicht so nicht aus :/
    Und dazu werden die Daten für den "Heartbeat" als "????" auch angezeigt, die ich mit Regex aus der Textbox fernhalten wollte.
    @RodFromGermany
    Vergiss RegEx, das bringt bei Deinem Problem nix.

    stimmt.

    @Sbfer
    Nimm RegEx raus und das Zeichen, welches als "Heartbeat" übertragen wird bei dem Schreibvorgang in die Textbox ausblenden.
    Damit der Text in der Textbox stehen bleibt:


    Quellcode

    1. Textbox.text &= neuerText
    Kunst kommt von können und nicht von wollen, sonst hieße es Wunst!

    xmise schrieb:

    Damit der Text in der Textbox stehen bleibt:QuellcodeTextbox.text &= neuerText


    ibb.co/tZtRCbV sieht jetzt ungefähr so aus... liegt wohl daran dass die Zeilen / Datenpakete durcheinander von der Platine kommen.

    RodFromGermany schrieb:

    Poste mal den kompletten Code.


    Bis auf einen Button der den Port öffnet und die Statusabfrage per Timer sendet sowie Buttons die den Code für Auf/ab etc. senden nur folgendes: (vorher den Regex Code)

    VB.NET-Quellcode

    1. Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    2. SerialPort1.ReadLine()
    3. SerialPort1.NewLine = Convert.ToChar(4).ToString()
    4. RichTextBox1.Text &= SerialPort1.ReadLine
    5. End Sub

    Sbfer schrieb:

    VB.NET-Quellcode

    1. SerialPort1.NewLine = Convert.ToChar(4).ToString()
    Diese Zeile gehört in die Initialisierung, also vor Port.Open().
    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:

    Diese Zeile gehört in die Initialisierung, also vor Port.Open().


    Direkt geändert und getestet - weiterhin erscheinen Teile die für Zeile 1 gedacht sind in der zweiten Zeile und umgekehrt.

    Zudem müsste die Textbox gecleared werden, wenn beide Zeilen voll sind, bevor es weiter geht, das muss ich noch irgendwie machen.

    Eigentlich soll die Textbox immer dann mit neuen Daten gefüllt werden, wenn beide Zeilen vollständig in der Textbox sind, und das möglichst gleichzeitig für beide Zeilen.

    Daher dachte ich, ich sag per Regex "alles zwischen Beginn Zeile 1 und Beginn Zeile 2 matchen und in die obere Zeile und alles danach in die unter" - auch wenn in meinem ersten Post erstmal jedes Datenpaket einzeln geregext wurde.
    Das scheint ja aber keine zielführende Lösung zu sein.

    Ich bin gespannt.
    @Sbfer

    RodFromGermany schrieb:

    Poste mal den kompletten Code.
    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:

    Poste mal den kompletten Code



    VB.NET-Quellcode

    1. ​Imports System.IO.Ports
    2. Imports System.Windows.Forms
    3. Imports System.Text.RegularExpressions
    4. Public Class Form1
    5. Private Sub Button10_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button10.Click
    6. SerialPort1.NewLine = Convert.ToChar(4).ToString()
    7. SerialPort1.Open()
    8. Timer1.Start() 'sendet die Statusanfrage
    9. End Sub
    10. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    11. 'war nur testweise Control.CheckForIllegalCrossThreadCalls = False
    12. SerialPort1.NewLine = Convert.ToChar(4).ToString() 'testweise in FormLoad statt vor den Button der den Port öffnet
    13. End Sub
    14. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    15. Dim statusabfrage() As Byte = {&H1, &H88, &H55, &H4}
    16. SerialPort1.Write(statusabfrage, 0, 4)
    17. End Sub
    18. Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
    19. Dim enter() As Byte = {&H1, &H88, &H22, &H4}
    20. SerialPort1.Write(enter, 0, 4)
    21. End Sub
    22. Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    23. SerialPort1.ReadLine()
    24. ' SerialPort1.NewLine = Convert.ToChar(4).ToString()
    25. RichTextBox1.Text &= SerialPort1.ReadLine
    26. End Sub
    27. Private Sub Button11_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button11.Click
    28. Timer1.Stop()
    29. SerialPort1.Close()
    30. End Sub
    31. Private Sub RichTextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RichTextBox1.TextChanged
    32. End Sub
    33. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    34. Dim auf() As Byte = {&H1, &H88, &H11, &H4}
    35. SerialPort1.Write(auf, 0, 4)
    36. End Sub
    37. Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
    38. Dim links() As Byte = {&H1, &H88, &H66, &H4}
    39. SerialPort1.Write(links, 0, 4)
    40. End Sub
    41. Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
    42. Dim ab() As Byte = {&H1, &H88, &H44, &H4}
    43. SerialPort1.Write(ab, 0, 4)
    44. End Sub
    45. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    46. Dim rechts() As Byte = {&H1, &H88, &H77, &H4}
    47. SerialPort1.Write(rechts, 0, 4)
    48. End Sub
    49. End Class