Rolling Chart

  • VB.NET

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von nafets.

    Rolling Chart

    Hallo Gemeinde,

    ich möchte Daten die von einem µC über RS232 kommen (10ms Takt, evtl. auch schneller min. 2ms) grafisch in einem rolling Chart darstellen (und in eine txt-Datei schreiben).
    Zur Simulation generiert der Timer gerade Daten.
    Im 1. Schritt habe ich in dem Timer Event alles gemacht. Ergebnis: Chart hat funktioniert, aber die Zeit zwischen den einzelnen Punkten
    war nicht genau die Zeit des Timers, sondern um die Zeit der Abarbeitungszeit in dem Timer Event länger.
    Also z.B. Timer Tick war 20ms --> Abstand Datenpunkte: 20ms + x
    Eigentlich für die geplante Anwendung kein Problem, da ja RS232 benutzt wir. Gestört hat es mich aber trotzdem.

    Dann die Auslagerung mit Backgroundworker.
    Zuerst Chart Aktualisierung im DoWork Sub. Ergebnis: Chart zeigt nach wenigen Punkten ein rotes, Diagonales Kreuz und keine Linien mehr.
    Dann das auf MSDN gelesen:
    DoWork event handler.' data-guid="680180d0b7d73f68e3fd066e114af957">Im DoWork-Ereignishandler dürfen keine Benutzeroberflächenobjekte bearbeitet werden. ProgressChanged and RunWorkerCompleted events.' data-guid="929f2a39d2cc27c359372cfb4cef4c58">Verwenden Sie stattdessen zum Kommunizieren mit der Benutzeroberfläche das ProgressChanged-Ereignis und das RunWorkerCompleted-Ereignis.


    und die Chart Aktualisierung in das RunWorkerCompleted Event gepackt.
    Ergebnis: rotes, diagonal Kreuz kommt immer noch

    Woran liegt es jetzt noch?

    Für jede weitere Anregung ob das Chart Control für mein Vorhaben das Richtige ist bin ich dankbar, da das hier mein erstes VB Programm ist...

    Beste Grüße
    Marcus

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Windows.Forms.DataVisualization.Charting
    3. Imports System.Threading
    4.  
    5.  
    6. Public Class Form1
    7. Dim datenreihe1 As New Series
    8. Dim datarow2 As New Series
    9. 'Dim datenreihetemp As New Series
    10. 'Dim datenreihetemp1 As New Series
    11. Dim queue As New Queue(Of Pointd)
    12. Dim queuetemp As New Queue(Of Pointd)
    13. Dim startzeit As Date = Now
    14. Dim jetzt As Date = Now
    15. Dim delta As New TimeSpan(10000)
    16. Dim zufall As New Random
    17. Dim first As Boolean = True
    18. Dim xachse As Double
    19.  
    20. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    21. Control.CheckForIllegalCrossThreadCalls = False
    22.  
    23. Dim titel As New System.Windows.Forms.DataVisualization.Charting.Title
    24. With Chart1
    25. .Series.Clear()
    26. titel = .Titles.Add("Speedtest")
    27. titel.ForeColor = Color.Black
    28. End With
    29.  
    30.  
    31. datenreihe1.ChartType = SeriesChartType.Line
    32. datarow2.ChartType = SeriesChartType.Line
    33. 'datenreihetemp.ChartType = SeriesChartType.Line
    34. 'datenreihetemp1.ChartType = SeriesChartType.Line
    35. 'Chart1.ChartAreas(0).AxisX.MajorGrid.Interval = 0.2
    36. 'Chart1.ChartAreas(0).AxisX.Interval = 0.1
    37. 'Chart1.ChartAreas(0).AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount
    38. Chart1.ChartAreas(0).AxisY.MajorGrid.Interval = 10
    39. Chart1.ChartAreas(0).AxisY.Interval = 50
    40.  
    41. End Sub
    42. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    43. If BackgroundWorker1.IsBusy Then
    44. MsgBox("Busy")
    45. Else
    46. BackgroundWorker1.RunWorkerAsync()
    47. End If
    48. End Sub
    49. Private Sub btn_stop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_stop.Click
    50. Timer1.Stop()
    51. End Sub
    52. Private Sub btn_start_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_start.Click
    53. Timer1.Start()
    54. End Sub
    55. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    56. Dim liste As IList(Of Series) = New List(Of Series)
    57. Static datenreihetemp As New Series
    58. Static datenreihetemp1 As New Series
    59. datenreihetemp.ChartType = SeriesChartType.Line
    60. datenreihetemp1.ChartType = SeriesChartType.Line
    61.  
    62. If first = True Then
    63. first = False
    64. startzeit = Now
    65. End If
    66. jetzt = Now
    67. delta = jetzt - startzeit
    68. Dim i_dummy As Integer = 0
    69. Dim zaehler As Integer = 0
    70. Static schleifenzaehler As Integer = 0
    71. Dim punkt As New Pointd(0, 0)
    72. Dim y As Double = zufall.Next(0, 300)
    73. Dim x As Double = CType(delta.TotalSeconds, Double)
    74. punkt.x = x
    75. punkt.y = y
    76. queue.Enqueue(punkt)
    77. queuetemp.Enqueue(punkt)
    78. zaehler = queue.Count
    79. schleifenzaehler += 1
    80.  
    81. If zaehler = 300 Then
    82. punkt = queue.Dequeue
    83. datenreihetemp.Points.Clear()
    84. datenreihetemp1.Points.Clear()
    85. For i As Integer = 0 To zaehler - 2 Step 1
    86. punkt = queue.ElementAt(i)
    87. x = punkt.x
    88. y = punkt.y
    89. datenreihetemp.Points.AddXY(x, y)
    90. datenreihetemp1.Points.AddXY(x, zaehler)
    91. Next
    92. Else
    93. datenreihetemp.Points.AddXY(x, y)
    94. datenreihetemp1.Points.AddXY(x, zaehler)
    95. End If
    96. 'datenreihetemp.Name = "qwertz"
    97. 'datenreihe1 = datenreihetemp
    98. 'datenreihetemp1.Name = "affe"
    99. 'datarow2 = datenreihetemp1
    100. 'i_dummy = Chart1.Series.Count
    101. 'Chart1.Series.Clear()
    102. 'i_dummy = Chart1.Series.Count
    103. 'Chart1.Series.Add(datenreihe1)
    104. 'Chart1.Series.Add(datarow2)
    105. 'Chart1.ChartAreas(0).AxisX.Minimum = x - 0.5
    106. 'Timer1.Stop()
    107. liste.Add(datenreihetemp)
    108. liste.Add(datenreihetemp1)
    109. e.Result = liste
    110. xachse = x
    111. End Sub
    112. Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    113. Dim liste1 As List(Of Series) = New List(Of Series)
    114. Static counter As Integer = 0
    115. counter += 1
    116. If counter = 9 Then
    117. counter = 0
    118. End If
    119. liste1 = CType(e.Result(), Global.System.Collections.Generic.List(Of Global.System.Windows.Forms.DataVisualization.Charting.Series)
    120. Chart1.Series.Clear()
    121. datenreihe1.Name = "qwertz"
    122. datenreihe1 = liste1.Item(0)
    123. datarow2.Name = "qwertz1"
    124. datarow2 = liste1.Item(1)
    125. Chart1.Series.Add(datenreihe1)
    126. Chart1.Series.Add(datarow2)
    127. 'Chart1.ChartAreas(0).AxisX.Minimum = xachse - 2
    128. End Sub
    129. End Class
    Willkommen im Forum. :thumbup:

    elnillo schrieb:

    in einem rolling Chart darstellen
    Gugst Du hier.
    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!
    Um die

    elnillo schrieb:

    Performance
    solltest Du Dich erst kümmern, wenn es nach Deinen Vorstellungen läuft.
    Diese Frage solltest Du eig. selbest beantworten können. ;)
    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!
    Das MS-Chart ist ein Koloss, und nur schlecht für Animationen geeignet.
    Winforms ist insgesamt für Animationen schlecht geeignet - die Performance von Rods Oszi ist übrigens überraschend gut.

    Aber das ist im Grunde erstmal egal, denn deine Anwendungs-Architektur taugt nix ( bzw. da ist gar keine ).
    Wenn du einen Daten-Generator codest, dann könnte ich spasseshalber mal gucken, wie man das ins MS-Chart popelt, sodass es sich etwa alle 500ms aktualisiert.
    denn deine Anwendungs-Architektur taugt nix ( bzw. da ist gar keine ).


    es ging nur darum mal zu gucken was das Chart Control so macht, wenn man es alle x ms mit neuen Daten füttert.
    Dafür sollte es doch inkl. recht simplen "Daten-Generator" reichen. Oder nicht? Ich lasse mich gerne belehren. Programmierung unter Windows ist für mich absolutes Neuland.

    Alle 500ms ist viel zu langsam. Auf dem Atmel µC läuft eine Lageregelung die aktuell im 100Hz Takt neu gerechnet wird. Daraus sollen maximal 4 Messwerte mit ja max. 4 Byte , + 2 Byte für Start+Ende + 1 Byte Checksumme gesendet werden.
    @RodFromGermany
    Performance: Sollte man sich da nicht vorher Gedanken drüber machen bevor man sich entscheidet welchen Weg man geht?
    Aber die Aussage von EDR "MS-Chart ist ein Koloss" scheint die Frage zu beantworten.

    elnillo schrieb:

    vorher Gedanken drüber machen bevor
    Jein oder Na :D .
    Manchmal ist es sinnvoll, mehrere Lösungen zu implementieren und sich dann anhand der geforderten Prioritäten zu entscheiden.
    Meine Lösung weiß halt nix von Zeitmarken, natürlich kann man da ganz easy noch ein Gitter drunter legen.
    Aber dann geht die Performance sofort in die Knie.
    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!

    elnillo schrieb:

    Alle 500ms ist viel zu langsam. Auf dem Atmel µC läuft eine Lageregelung die aktuell im 100Hz Takt neu gerechnet wird. Daraus sollen maximal 4 Messwerte mit ja max. 4 Byte , + 2 Byte für Start+Ende + 1 Byte Checksumme gesendet werden.
    Scheinbar willst du einen Chart-Ticker mit 4 Series, oder?
    Wie gesagt: Überleg dir als erstes den Datentyp. Ein Messwert von 4 Bytes wäre etwa ein Integer. Lageberechnung - bedeuten die 4 Messwerte Millimeter oder Winkel oder was?
    Also angenommen die bedeuten x, y und zwei Winkel, dann könnte der Datentyp so aussehen:

    VB.NET-Quellcode

    1. Public Class ComplexPosition
    2. Public X, Y As Integer
    3. Public Alpha, Betha As Double
    4. Public Timestamp As Date
    5. End Class
    Mit solchen Datensätzen kann man was anfangen.
    Die ganze Lageregelung läuft auf dem Atmel und das recht gut inzwischen. Um den dort implementierten PID Regler und noch weitere Parameter zu optimieren möchte ich mir Messwerte ausgeben lassen. Die Einheiten der Messwerte spielt auf der PC Ebene keine Rolle. Messwerte können Rohwerte von der Sensorik, gefilterte Werte, Reglerwerte usw. sein. Was kommt, wird vorher über das Formular eingestellt.

    Datentyp ist schon klar, wobei manche Werte vom Controller auch unsigned kommen. Aber das weiß ich ja vorher und kann darauf entsprechend reagieren.
    Die Klasse so ähnlich ist in dem eigentlichen Programm vorhanden für die Werte
    Das oben gepostete sollte nur mal als Test dienen für das Chart Control. Dabei gab es dann das Problem dass das Chart nach wenigen Werten ein rotes Kreuz zeigt wenn man mit dem Backgroundworker arbeitet.
    macht man das alles im Timer Event klappts.

    elnillo schrieb:

    ... Datentyp ist schon klar,...
    Offensichtlich nicht.
    Du scheinst das Wesen von Datenmodellierung zu verkennen.
    Es geht nicht um Bytes oder Doubles, sondern um ein Modell von dem, was diese Sachen bedeuten.
    Du musst ein Modell der Bedeutung dessen konzipieren, was der µc macht.
    Und am (Daten-) Empfang muss als erstes eine Konvertierung der Roh-Daten in dieses sinnvolle Modell erfolgen, damit die Werte Namen bekommen, die aussagen, was die Werte bedeuten.
    Du scheinst Chart-daten nur als Point verstehen zu können, aber es sind eiglich keine Points, die das Chart anzeigt, sondern es sind (in meim Beispiel) Winkel und Strecken zu Zeitpunkten.

    Aber ich versuche grade, dich auf ein anneres Gleis zu bringen, wo du deine Entwicklung bisserl grundlegender anschaust, bevor du dich in die Details der Präsentation verlierst.
    Und es scheint nicht zu klappen, sonst würdest du meinen Daten-Modell-Versuch mal aufgreifen, und etwas an deine Realität anpassen.
    Die Konvertierung der Empfangenen Daten findet doch statt. Da gibts dann auch eine Klasse die den Namen des Messwertes, seine Länge, signed/unsigned, min/max Wert usw. beinhaltet.
    Natürlich nicht in dem von mir geposteten Code oben.
    Der war doch nur um das Chart Control zu testen.
    Es sollte hier einzig und allein um die Frage gehen warum das Chart Control nach wenigen Aktualisierungen ein rotes Kreuz anzeigt.
    Bis jetzt habe ich die gesendeten Daten einfach mit einem Terminalprogramm aufgezeichnet und in Excel dann aufbereitet zur Anzeige.
    Das wollte ich jetzt mal versuchen mit VB zu machen, um einen Einstieg zu bekommen.

    elnillo schrieb:

    ein rotes Kreuz
    wird angezeigt, wenn intern im Chart eine wie auch immer geartete Exception bei der Darstellung ausgelöst wird.
    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!
    Was brauchst du denn alles für Funktionen? Man kann sowas nämlich mit geringem Aufwand selbst schreiben. Dadurch kriegt man natürlich deutlich bessere Performance hin. Hier mal ein kleines Beispiel von einem von mir programmierten Control (sowas in der Art willst du doch auch haben, oder?):

    Da könnte man ohne weiteres noch ne Skala dazu zeichnen, mehrere Kurven benutzen, den Stil ändern, ... und so ein Ding kriegt locker 1000 Aktualisierungen in der Sekunde hin (ca. 0,1 Millisekunden pro Aktualisierung inklusive Rendering).
    Backgroundworker finde ich ehrlich gesagt nicht so toll: mach lieber einen Thread, welcher einer Klasse die Werte übergibt. Diese Klasse "leert" dann diese gespeicherten Werte X mal die Sekunde per Invoke in das Control. Du könntest das sogar mit nem SyncLock machen, sodass es fast unmöglich ist, dass Fehler auftreten.