Hallo zusammen,
ich arbeite an einem Projekt, bei dem ich Messwerte eines externen Gerätes auf dem Bildschirm plotten muss. Es gibt dabei folgende Rahmenbedingungen:
es kommen vier Messungen mit jeweils drei Werten pro Sekunde rein - angezeigter Maßstab ist 1cm/Minute
- die Messung kann auch mal ein bis zwei Tage dauern
- es sollte möglich sein flüssig hin und her zu scrollen
- es gibt ein Aktualisierungsintervall, das derzeit bei 6 Sekunden liegt, eventuell später auf 3 reduziert wird
Bisher bin ich wie folgt vorgegangen:
- ich habe einen Thread, der die Messdaten abholt, Intervall 5s, Abholdauer 1s
- ich habe einen weiteren Thread, der die kompletten Messdaten in zwei Bitmaps immer wieder neu zeichnet, sobald eine Aktualisierung der Daten anliegt und die Images auf der Oberfläche aktualisiert
Das läuft soweit so gut. Nur stellt sich heraus, daß das Neuzeichnen keine so gute Idee ist. Ab ca 30 Minuten Messdauer, benötigt das Zeichnen mehr Zeit als das Holen der Daten, und es wird immer langsamer. Also habe ich zumindest da einen Flaschenhals. Ich zeichne wie folgt (vereinfacht und aufs wesentliche reduziert, vermutlich nicht compilierbar):
Delphi-Quellcode:
procedure TCTGGrid.PaintFHR(aBitmap: TBitmap);
var
rowList : TStringList;
i : Integer;
GID1Path : TPathData;
GID2Path : TPathData;
myX, myY : Single;
begin
if aBitmap.Canvas.BeginScene
then
try
GID1Path := TPathData.Create;
GID2Path := TPathData.Create;
try
// In der fValuelist stehen alle Messwerte als Stringliste zur Verfügung. Im Prinzip ist
// ist das eine CSV-Datei, in eine Stringliste eingelesen.
for i := 0
to fValueList.Count - 1
do
begin
// Methoden aufrufen, die aus einem Zeitstempel die X-Koordinate und
// aus einem Messwert die y-Koordinate errechnen. Ich bezweifle, daß die zeitkritisch
// sind. Der Aufruf ist auch nur exemplarisch zu verstehen.
myX := ConvertTimeToXCoord(StrToFloatDef(fvaluelist[i][keyTimeOffset], 0, fs));
myY := ConvertBPMToYCoord(StrToFloatDef(fvaluelist[i][aGID1Key], 0, fs));
GID1Path.LineTo(Pointf(myX, myY));
myY := ConvertBPMToYCoord(StrToFloatDef(fvaluelist[i][aGID2Key], 0, fs));
GID2Path.LineTo(Pointf(myX, myY));
end;
aBitmap.Canvas.Stroke.Dash := TStrokeDash.Solid;
aBitmap.Canvas.Stroke.Color := claBlack;
aBitmap.Canvas.StrokeThickness := 1;
aBitmap.Canvas.DrawPath(GID1Path, 1);
aBitmap.Canvas.Stroke.Color := claBlue;
aBitmap.Canvas.DrawPath(GID2Path, 1);
finally
GID1Path.Free;
GID2Path.Free;
end;
finally
aBitmap.Canvas.EndScene;
end;
Für eine Stunde dauert das mehr als 10 Sekunden... das ist zu langsam für meinen Anwendungsfall.
Frage eins ist jetzt: Ist es möglich hier etwas zu beschleunigen? Was für Optionen gibt es? Kann ich vielleicht ein bestehendes Image verbreitern und ergänzen? So daß also nur meine 6 Sekunden Intervalle neu zu zeichnen sind, was eine Sache von Millisekunden ist.
Frage zwei: Wie überkomme ich die Bitmapgrößenbeschränkung? Die maximale Breite ist schnell erreicht.
Sherlock