Laden...

Schnelle Datenübergabe an Steuerelement

Erstellt von BJA-CH vor 2 Jahren Letzter Beitrag vor 2 Jahren 300 Views
B
BJA-CH Themenstarter:in
59 Beiträge seit 2017
vor 2 Jahren
Schnelle Datenübergabe an Steuerelement

Hallo zusammen
Ich bin mir nicht sicher, ob ich hier im richtigen Forum bin, aber ich hoffe man kann mich gleichwohl verstehen.

Ich möchte analoge Daten vom Mikroprozessorsystem "Arduino" einlesen und an einem selbstgebauten Steuerelement, das ich Oszillator" (siehe Bild im Anhang) nenne dynamisch anzeigen lassen.
Ich kann die Daten im Programm aufnehmen. Ich kopiere das Messergebnis in eine kleine Klasse und füge eine fortlaufende Nummer zur Kontrolle dazu. Dann übergebe ich dieses Objekt an das Steuerelement, also ein Messpunkt nach dem anderen.
In einem DependencyProperty übernehme ich dieses Objekt und füge den Messwert in eine ObservableCollection<double>-Liste. Danach kicke ich eine Dispatcher die Darstellungs-Routine an.
Das Ergebnis ist sehr unbefriedigend, denn das angezeigte Signal entspricht in keiner Weise dem Signal des Arduino.

Wenn ich aber die Dateneingänge des Steuerelementes überprüfe, so sehe ich, dass nur ein kleiner Teil der Signale das Steuerelement erreichen, dies obwohl der einlesende Programmteile den Messwert empfängt und an das Steuerelement weiterleitet. Ich weiss, die Darstellung des Signales benötigt viel Rechenaufwand, da jedes Mal die Bildschirmbreite neu gezeichnet wird. Aber selbst, wenn ich die Darstellung des Signales ganz abschalte, werden nicht alle Signale empfangen.

Für mich sieht es aus, als ob das Steuerelement mit einer anderen Priorität arbeitet als das Hauptprogramm.

Nun, und da fängt meine Unkenntnisse an, wie müsste ich ein solches Vorhaben in Prozesse (Threads) aufteilen, damit alles Wichtige möglichst schnell bearbeitet werden kann?
Muss ich das Steuerelement mit dem Einlese-Teil in eine prioritären Thread setzen und wie würde dies geschehen?
Wie muss ich das Zeichnen der Signals vom Einlese-Teil mit einem eigenen Prozess (Thread) trennen, damit das Einlesen möglichst nicht gestört wird?

Hat jemand Erfahrung mit einem solchen Steuerelement? Oder kann mir jemand eine Idee skizzieren, wie ich das hinkriegen könnte?

Besten Dank für eure Beiträge
Jakob

16.807 Beiträge seit 2008
vor 2 Jahren

Von welcher UI Technologie sprichst Du denn?
Aufgrund von ObservableCollection rate ich, dass Du WPF verwendest.

Prinzipiell würde ich das mit Reactive Extensions machen, sodass ich alle einkommenden Daten einfach in eine Subscription-überwachte Liste haue und WPF aktualisiert die UI.
Es könnte gut sein, dass das aber zu langsam ist und statt der WPF Bindung das Zeichnen eines Bildes schneller wäre; aber das wäre wirklich ein seltener Edge Case.

Für mich sieht es aus, als ob das Steuerelement mit einer anderen Priorität arbeitet als das Hauptprogramm.

Das kann technisch prinzipiell nicht sein, weswegen auch deine Folgelösung nicht funktionieren würde.
Alle UI Elemente (des gleichen Prozesses) müssen sich im Hauptthread befinden; Du kannst nur gewisse Teile auslagern, zB. das Zeichnen eines Bildes, oder die ganze Datenverarbeitung.
Bei Steuerelementen und bei WPF-Elementen etc geht das nicht (einfach so).

Wie muss ich das Zeichnen der Signals vom Einlese-Teil mit einem eigenen Prozess (Thread) trennen, damit das Einlesen möglichst nicht gestört wird?

Kurze Grundlagen: ein Prozess ist was anderes als ein Thread, und ein Thread ist wiederum etwas anderes als ein Task (in .NET).
Prozess-Auslagerung macht man aus Isolationsgründen; Thread-Verwaltung kann man sich sparen, wenn man Tasks verwendet.

Ergo Du solltest die ganze Kommunikation in einen Task auslagern (geht super einfach mit async/await und Reactive Extensions) und dann die gesammelten Daten binden.
Oder eben selbst Zeichnen, falls Dir hier WPF zu langsam sein sollte; beachte aber, dass man WPF selbst gut optimieren kann => Optimieren der APP-Leistung - WPF .NET Framework

Hier ist noch ein prinzipielles Beispiel (aber etwas älter), wie man einen Standard Event Pattern implementiert, um eingehende Nachrichten (aka Events) mit Rx zu verarbeiten.
Reactive Extensions in Message Processing – PeteGoo
https://kerry.lothrop.de/seriell-rx/

190 Beiträge seit 2012
vor 2 Jahren

Hallo,
ist das Oszi-Element von dir geschrieben? Ich nutze dieses LiveCharts2. Bin damit sehr zufrieden.

  • Wer lesen kann, ist klar im Vorteil
  • Meistens sitzt der Fehler vorm Monitor
  • "Geht nicht" ist keine Fehlermeldung!
  • "Ich kann programmieren" != "Ich habe den Code bei Google gefunden"

GidF

6.911 Beiträge seit 2009
vor 2 Jahren

Hallo BJA-CH ,

analoge Daten vom Mikroprozessorsystem "Arduino" einlesen

Von welcher Datenmenge (i.e. Datenpunkte pro Sekunde) reden wir?
Sollen die Daten nur angezeigt werden od. dann auch irgendwo archiviert werden?
Willst du alle Daten anzeigen od. nur das aktuellste Fenster mit z.B. der letzten Sekunde und ältere Daten verschwinden aus der Anzeige? Also eine Art Ringpuffer welcher die Daten hält und diese zur Anzeige verwendet werden.

Je nachdem wie die Antworten zu diesen Fragen ausschauen, gibt es praktikablere Wege für eine solche Darstellung.
Lesen der Daten sollte auf jeden Fall in einem eigenen Thread durchgeführt werden, so dass der UI-Thread (= Haupt-Thread) für die Visualisierung genügend Zeit hat.

kopiere das Messergebnis in eine kleine Klasse und füge eine fortlaufende Nummer zur Kontrolle dazu

Bzgl. "Klasse", das ist eine Allokation die der GC später abräumen muss. Daher kann es -- je nach Datenmenge -- sinnvoller sein dazu eine struct zu verwenden. Z.B.


public readonly struct Reading
{
    private static int s_id;

    public int Id       { get; }
    public double Value { get; }

    public Reading(double value)
    {
        // Wenn du sicher sein kannst, dass das Lesen sequentiell passiert reicht das.
        this.Id = s_id++;

        // Sonst muss auch das hier atomar passieren.
        //this.Id = Interlocked.Increment(ref s_id);

        this.Value = value;
    }
}

füge den Messwert in eine ObservableCollection<double>-Liste

Bedenke hier auch dass diese Collection sehr groß werden kann und das für das Datenbindung noch mehr (zeitlicher) Aufwand wird, da O(n).
Fürs Binding sollte nur der jeweilige Datenbereich vorhanden sein, der auch tatsächlich benötigt wird und keine historischen Daten -- siehe oben bei Frage zu Ringpuffer.

Datenbindung in WPF ist schön und praktisch, aber hat auch "Overhead" der je nach Datenmenge die Bremse werden kann.
Oszis werden oft direkt per OpenGL grafikkartennah umgesetzt, da es so kaum Overhead gibt. Od. mit minimalen "Wrapper-Libraries" wie z.B. VTK. Ich denke beides lässt sich wiederum in die WPF-Anwendung einbinden (via Handles, aber da kenn ich mich zu wenig aus).

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

B
BJA-CH Themenstarter:in
59 Beiträge seit 2017
vor 2 Jahren

Hallo,
Ja habe ich selber geschrieben. Wie du siehst, ist das nicht ganz einfach.... aber man kann viel lernen dabei.
Nun, das was "Abt" geantwortet hat hört sich spannend an, denn diese Methoden kenne ich noch gar nicht.

Ich frage mich nun auch, ob ich wirklich mit eintreffen eines Messpunktes das Chart auch wirklich aktualisieren muss, oder ob ich vielleicht besser mit einer Frame-Rate eine kontinuierlicghe Aktualisierung erzwingen soll. Das müsste doch für das Auge auch reichen und würde den Computer sicherlich entlasten....

Danke für deinen Hinweis auf "LiveCharts2", das scheue ich mir gerne mal an.

Gruss, Jakob

6.911 Beiträge seit 2009
vor 2 Jahren

Hallo BJA-CH ,

mit eintreffen eines Messpunktes das Chart auch wirklich aktualisieren muss

Macht eher wenig Sinn. Der Weg via Frame-Rate, etc. ist da schon sinnvoller.
Mach einen Ringbuffer für die Werte und ein Timer nimmt sich dann einen Snapshot dieses Buffers und stellt diese Daten dar. Zeitgleich bzw. unabhängig davon kann der Ringbuffer weiter befüllt werden.
Achte dabei aber auf passende Synchronisierung bzw. eine nimm eine Collection (aus System.Collections.Concurrent).

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"