Laden...

Alle 100ms ein Kommando, was der User auch via Button frei triggern kann, geht nur manchmal

Erstellt von Janiiix3 vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.714 Views
J
Janiiix3 Themenstarter:in
38 Beiträge seit 2015
vor 5 Jahren
Alle 100ms ein Kommando, was der User auch via Button frei triggern kann, geht nur manchmal

Nabend,

in einem Timer Event ( das alle 100ms) auftritt, sende ich Kommandos über die Serielle Schnittstelle und das ganze permanent.
Nun kann es vorkommen das der User während dies geschieht einen Button drücken kann um was anderes über die Serielle zu senden. Ab und zu klappt das auch.
Ab und zu geht es aber auch in die Hose.

Meine Frage jetzt. Was genau passiert wenn Daten gerade aus dem Timer Event geschickt werden und dann der User einen Button drückt? Wird das Timer Event dann direkt unterbrochen und bei dem Button Event weiter gemacht oder führt der Timer erst den kompletten Code aus?

4.938 Beiträge seit 2008
vor 5 Jahren

Das kommt auf den Timer drauf an. Wenn du den Windows.Forms.Timer benutzt, dann läuft dieser selber im UI-Thread, d.h. Timer-Code und Button-Code werden nacheinander ausgeführt. Bei den System-Timern kann jedoch aufgrund von Multithreading jeder der Methoden vorzeitig unterbrochen werden und sich die Ausgaben (über die Serielle Schnittstelle) mischen - du müßtest diese also dann selber synchronisieren.

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

Okay dann muss das Problem an einer anderen Stelle lauern.
In dem Seriellen Daten Empfangs Event, sammel ich solange die eingehenden Daten ein bis ich ein Kommando ganz empfangen habe. Klappt auch alles wunderbar.
Wird auch alles mit CRC auf Gültigkeit geprüft.
Drücke ich jetzt während der Timer läuft ( ist übrigens ein Forms Timer) empfange ich dann wohl ab und zu die Antwort nicht oder übersehe sie???
Wenn es am Ablauf nicht liegen kann muss ich diese Nachricht wohl nicht mitbekommen oder irgendwie verwerfen..
Vill. hat ja jemand einen Tipp für mich?


       int length = 0;
        byte bytesToReceive = 0;
        byte[] buffer = new byte[100];

        private void Client_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            /*  Empfangene Daten abholen
            */
            try
            {
                length += Client.Read(buffer, length, 50 );
            }
            catch { }
            

            /*  Kommando Start Parsen
             *  Rückgabewert: - 1 = Kein Start gefunden..
            */
            int index = Cmd.ParseCommandoStart(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..
            */
            Cmd.ParseCommando( buffer, ref Cmd.CommandoParsed );

            manualResetEvent.Set();
        }


4.938 Beiträge seit 2008
vor 5 Jahren

Wo wartest du denn auf den manualResetEvent? Dieses Warten blockiert natürlich den entsprechenden Thread.

T
111 Beiträge seit 2005
vor 5 Jahren

Hallo

stell Deine Befehle die Du senden möchtest (Timer, User) in eine Queue und arbeite diese sequentiell ab. Und zwar immer erst dann, wenn eine Antwort vollständig empfangen wurde. Ich vermute mal, dass Deine Peripherie durcheinander kommt, wenn es beim Senden eine neue Nachricht bekommt.

Thomas

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

Okay. Ist das so eine Art Ringpuffer?

4.938 Beiträge seit 2008
vor 5 Jahren

Nicht unbedingt, eine Warteschlange (Queue) arbeitet generell ersteinmal nach dem FIFO-Prinzip (ob der Speicher dabei "unendlich" ist oder limitiert, liegt an der entsprechenden Implementierung).

Im .NET-Framework gibt es die threadsicheren Container im System.Collections.Concurrent Namespace, d.h. nutze also die ConcurrentQueue<T>, damit 'Producer' und 'Consumer' aus verschiedenen Threads sicher darauf zugreifen können.

16.828 Beiträge seit 2008
vor 5 Jahren

Ich empfehle an dieser Stelle zB https://github.com/jbogard/MediatR, der genau das macht und einem die gesamte Infrastruktur abnimmt.

Das bedeutet, dass man würde in diesem Fall einen Command via Send in die Queue schreiben und ein entsprechender Handler würde das Abarbeiten durchführen.

Das entspricht in etwa dem CQS bzw CQRS Pattern - nur alles InProcess.