Laden...

Daten von Com-Port lesen, bis bestimmte Zeichenfolge gefunden wurde [oder Timeout eintritt]

Erstellt von tipikosch vor 10 Jahren Letzter Beitrag vor 10 Jahren 4.042 Views
T
tipikosch Themenstarter:in
3 Beiträge seit 2013
vor 10 Jahren
Daten von Com-Port lesen, bis bestimmte Zeichenfolge gefunden wurde [oder Timeout eintritt]

Guten Tag,
ich hab ein Code geschrieben bei dem ich daten aus Com Port lesen kann, das Programm soll nach einem string suchen. Wenn gefunden, soll das Programm true zurückgeben, und wenn nicht gefunden, dann soll das Programm nicht unendlich lang laufen. da brauch ich ein stopwatch oä. Ich hab viele beispiele im internet gefunden, aber kein passendes. Kann mir jemand helfen?
hier ist mein code:


public static class Program
    {
        private static string searchString;
        private static bool searchResult;

        static void Main(string[] args)
        {
            bool z = IsMessageInSerialPort("COM1", "Job finished");
        }

        public static bool  IsMessageInSerialPort(string portName, string sString)
        {
            searchString = sString;
            var mySerialPort = new SerialPort(portName)
            {
                BaudRate = 9600,
                Parity = Parity.None,
                StopBits = StopBits.One,
                DataBits = 8,
                Handshake = Handshake.None
            };
            mySerialPort.DataReceived += OnDataReceived;
            mySerialPort.Open();
            Console.ReadKey();
            //mySerialPort.Close();
            return searchResult;
        }

        private static void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            var sp = (SerialPort)sender;
            while (!searchResult && sp.IsOpen)
            {
                string actualString = sp.ReadLine();
                Console.Write(actualString);
                if (actualString.Contains(searchString))
                {
                    sp.Close();
                    searchResult = true;
                }
            }
        }
    }
G
141 Beiträge seit 2011
vor 10 Jahren

Naja ich würde mal sagen du machst dir ersmal eine Stopwatch:


Stopwatch Timer = new Stopwatch
Timer.Start();

In deine schleife baust du dann eine Abfrage:


if (Timer.Elapsed >= *DeineZeitalsInt*)
{
  searchResult = false;
  Timer.Stop();
}

Einfach in die Doku dazu schauen:
MSDN: Stopwatch - Klasse

L
416 Beiträge seit 2008
vor 10 Jahren

Hallo,

alles was du dafür brauchst ist ein Timer

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo tipikosch,

aus meiner Sicht weder Stopwatch noch Timer, sondern ManualResetEvent oder AutoResetEvent.

Das DataReceived-Event wird von alleine ausgelöst, wenn neue Daten kommen. Also musst du nur dafür sorgen, dass das Hauptprogramm solange läuft (genauer: vor der roten Ampel wartet), bis der String gefunden wurde (und du daraufhin die Ampel auf grün setzt), siehe Consolenanwendung mit Timer optimieren [Semaphore/Ampel].

herbivore

T
708 Beiträge seit 2008
vor 10 Jahren

Hallo tipikosch,

die Herangehensweise ist leider grundlegend falsch.
Das OnDataReceived-Event wird aufgerufen, wenn der COM-Port Controller der Meinung ist, die Werte durchzuschleusen. Das hat zum Einen mit der Einrichtung zu tun, zum Anderen wie sehr der Com-Port "gefordert" wird (Cache, usw.).
Soll heißen: Es kann man sein dass die Antwort nur aus einzelnen oder wenigen Zeichen besteht, mal aus dem gesamten Datenfluss bis zum Zeilenumbruch.

Nun erwartest du "Job finished". Das Event wird aufgerufen und liefert "Job" zurück. Die Schleife wird also endlos laufen (Bis die Stopwatch funktioniert).
Beim nächsten Mal gibt es vielleicht sogar nur "J" zurück...

Ich denke, Du verstehst das Problem 😃

Lege eine globale Variable an, der du alle eingehenden Nachrichten aus dem Event per += zuweist und vergleiche das mit deinem searchString.
Dann würde ich einen Timer statt der Stoppwatch verwenden, damit nach dessen Ablauf ebenfalls ein Event aufgerufen wird. Darin beendest Du dann den ComPort, sofern nicht vorher schon ein korrektes Ergebnis eingegangen ist.

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo trib,

da die Menge und Größe der String-Fragmente unbeschränkt ist, ist es keine gute Idee, += zu verwenden: quadratischer Aufwand, Performance-Probleme vorprogrammiert, siehe [Artikel] Strings verketten: Performance-Betrachtung. Besser ist es hier, einen StringBuilder zu verwenden. Oder die Teile gar nicht zusammen zu setzten, sondern sich nur zu merken, bis zu welcher Länge bzw. Position schon eine Übereinstimmung gefunden wurde.

Ich bin weiterhin der Meinung, dass weder Stopwatch noch Timer erforderlich sind.

herbivore

4.221 Beiträge seit 2005
vor 10 Jahren

Ich unterstütze herbivore.

Kapsle doch den COM in einer eigenen Klasse. Im DataReceived sammelst Du in Deiner Klasse alles in einem StringBuilder. Wenn Du dann im StringBuilder den Wert gefunden hast wirfst Du aus der eigenen Klasse einen Event (mit den Daten des StringBuilders).

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

L
416 Beiträge seit 2008
vor 10 Jahren

Ich bin weiterhin der Meinung, dass weder Stopwatch noch Timer erforderlich sind.

Naja ich hatte es so verstanden das quasie ein timeout umgesetzt werden soll weil es vorkommen kann das der String gar nicht empfangen wird.

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo Lennart,

stimmt, da steht, dass es nicht unendlich lange laufen soll. Das "nicht" hatte ich überlesen. Dann braucht man doch einen Timer o.ä. Zumindest sofern nicht sichergestellt werden kann, dass regelmäßig neue Daten eintreffen.

herbivore

T
708 Beiträge seit 2008
vor 10 Jahren

da die Menge und Größe der String-Fragmente unbeschränkt ist, ist es keine gute Idee, += zu verwenden

Hallo herbivore,

das ist vollkommen richtig. Der oben genannte Code wird aber niemals soweit kommen, da es von vornherein eine Endlosschleife gibt. Daher wollte ich erstmal das grundsätzliche Problem erläutern, bevor es ans optimieren geht.

Wenn man es "richtig" machen möchte, sollte man ohnehin mehr Informationen über das angeschlossene Gerät haben.
Werden Start- & Endzeichen übermittelt, kann man die Chunks schonmal genauer abstecken und unnütze Daten verwerfen.
Dann sollte man aus dem Event statt ReadLine() besser ReadExisting() verwenden. So liest man in einem Rutsch alle Zeichen aus dem Port, bis das Event erneut geworfen wird. Und man sperrt sich nicht durch eine Schleife mögliche weitere eingehende Nachrichten.
Wenn der Port nicht alle paar Millisekunden eine Rückmeldung gibt, ist bei heutigen Computern eine simple String-Verkettung das geringste Problem.
(pre optimization is the root of all evil)

Der Timer erledigt sich dann sicherlich von alleine, da sich das Programm auch nicht mehr sperrt. Ist es so wie Lennart vermutet, kommt man aber um einen Timer nicht herum. Möglicherweise reicht es aber schon aus eine Startzeit zu setzen und im DataReceive zu überprüfen ob ein Grenzwert überschritten wird. (_Ist aber auch witzlos, wenn der Port überhauptnicht antwortet _:) )

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo trib,

quadratischen Aufwand zu vermeiden, wenn die Länge (und Häufigkeit) der Eingabe potenziell unbeschränkt ist, es keine premature optimization, sondern dringend geboten. Auch auf heutigen Computern und sogar auf zukünftigen. Zu der Differenzierung, welche Optimierungen angebracht sind und welche nutzlos oder gar gefährlich, findet sich mehr in Mergesort langsamer als Bubblesort? [==> Nein, Messfehler / Aufwandsklasse vs. Mikrooptimierungen].

herbivore

T
tipikosch Themenstarter:in
3 Beiträge seit 2013
vor 10 Jahren

Hallo leute,
danke für die Hilfe. trib du hast recht, ich werde das mit dem stringbuilder versuchen..

Hinweis von herbivore vor 10 Jahren

Threads zusammengefügt, da es weiterhin darum geht "Daten von Com-Port [zu] lesen, bis bestimmte Zeichenfolge gefunden wurde".

T
tipikosch Themenstarter:in
3 Beiträge seit 2013
vor 10 Jahren
Reguläre Ausdrücke

Guten Tag,
ich habe ein kleines Problem und brauche Hilfe.
ich habe ein Programm geschrieben, das Daten (Strings) aus COM-Port auslesen kann. Ich muss nach einem String suchen. Mit meinem Code habe ich es geschafft, aber mir wurde gesagt dass es nicht praktisch ist. Ich soll lieber mit Regex arbeiten. COM-Port schickt mir jede Sekunde 4 Zeilen Strings. Wie kann ich am bestens mit Reguläre Ausdrücke nach einem String suchen?
Hier ist mein Code:

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;

namespace Programm
{
    class Program
    {
        public static void Main()
        {
            SerialPort mySerialPort = new SerialPort("COM15");

            mySerialPort.BaudRate = 115200;
            mySerialPort.Parity = Parity.None;
            mySerialPort.StopBits = StopBits.One;
            mySerialPort.DataBits = 8;
            mySerialPort.Handshake = Handshake.None;

            mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

            mySerialPort.Open();
            Console.ReadKey();
            mySerialPort.Close();
        }

        private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            string SearchAString = "Job Completed";
            SerialPort sp = (SerialPort)sender;
            string NeededString = sp.ReadExisting();
            Console.Write(NeededString);
            bool checkLogFile = NeededString.Contains(SearchAString);
          
                if
                     (checkLogFile == true)
                {
                 System.Diagnostics.Debugger.Break(); }
             }
    }
}

T
415 Beiträge seit 2007
vor 10 Jahren

So wie ich deinen Code deute, checkst du den Input String nur darauf, dass "Job Completed" ankommt, um dann darauf zu reagieren. Dafür ist dein Ansatz meiner Meinung nach okay. Möchtest du den Einstieg in Regex findet, dann empfehle ich dir zunächst dieses Tutorial abzuarbeiten: [Artikel] Regex-Tutorial

1.696 Beiträge seit 2006
vor 10 Jahren

Hallo,

für einfache Suche ist string.Contains schneller/performanter, da RegEx einen ziemlich großen overhead mit sich schleppt. Wenn du nur diesen einen Begriff suchst ist dein Code absolut in Ordnung. Ansonstens das Tut anschauen wenn du mit RegEx in Zukunft einsetzen möchtest.

Grüße

Ich bin verantwortlich für das, was ich sage, nicht für das, was du verstehst.

**:::

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo tipikosch,

zumindest in diesem Thread hat dir keiner geraten, Regex zu verwenden. Und wegen der Thematik, dass der gesuchte String auch auf einer "Kante" von zwei Datenblöcken liegen kann, ist Regex auch nicht zu empfehlen. Genausowenig wie String.Contains. Sondern am besten machst du es in der Art, wie es in "String" im sehr großem Byte-Stream replacen als Pseudocode beschrieben ist.

herbivore

PS: Bitte beachte auch [Hinweis] Wie poste ich richtig? Punkt 7 ==> Superlativ entfernt. Außerdem Punkt 1.2 und 3.