AnalogDelphi

ftComputing : Programme für die fischertechnik-Interfaces und -konstruktionskästen
  
ftComputing.de
Home
Back
Sitemap
Index
Links
Impressum
Mail
 

Anzeige von Analogwerten : Analog

fischertechni Computing Analog Control Aufgabe

Analoge Anzeigen von Werten der A-Ausgänge (EX bzw. Ey) der fischertechnik Interfaces.

Das mit den Werten ist ja einfach : ft.Analogs(ftiAEX) bzw. ftiGetAnalog(0) bei umFishEx. Aber wie kommt man zu einem Analoginstrument. Alternative 1 : Bild besorgen, Zeiger drüber zaubern. Geht, meist sogar mit variabler Größe, aber wie siehts aus mit unterschiedlicher Skalenteilung und Beschriftung ? Das wär dann Alternative 2 : selber zeichnen. Und das wird hier gezeigt. Die Quellen sind in AnaInstr.ZIP zu finden. Zusätzlich sind noch umFishU.ZIP erforderlich.





Die ZeichenRoutine : anaShowDisplay


procedure anaShowDisplay(dsp: TPaintBox; aWert: Integer);

const
kStrich = 5; lStrich = 8; anzStriche = 21; sWinkel = 1.05; iRadius = 75;
zRadius = 40; zStrich = 48; zBereich = 1000; zTeilung = 250;

var
dWinkel, aWinkel: Single; sWert: Integer; dText: string;
x, y: Integer; x1, y1, x2, y2: Single;
i: LongInt;

function xD(x: Single): Integer;
begin
Result := Round(dsp.ClientWidth * (x / 100) + dsp.ClientWidth / 2);
end;

function yD(y: Single): Integer;
begin
Result := dsp.ClientHeight - Round(dsp.ClientHeight * ((y-20) / 80));
end;

begin
dsp.Canvas.Brush.Color := clWhite;
dsp.Canvas.Pen.Color := clWhite;
dsp.Canvas.Pen.Width := 1;
dsp.Canvas.Rectangle(0, 0, dsp.ClientWidth, dsp.ClientHeight);
dsp.Canvas.Pen.Color := clBlack;

// --- Minor Ticks ------------

aWinkel := sWinkel;
dWinkel := sWinkel / (anzStriche - 1);
for i := 1 to anzStriche do
begin
x1 := Cos(aWinkel) * iRadius;
y1 := Sin(aWinkel) * iRadius;
x2 := x1 + Cos(aWinkel) * kStrich;
y2 := y1 + Sin(aWinkel) * kStrich;
dsp.Canvas.MoveTo(xD(x1),yD(y1));
dsp.Canvas.LineTo(xD(x2),yD(y2));
aWinkel := aWinkel + dWinkel;
end;

// --- Major Ticks --------------------

aWinkel := sWinkel;
sWert := zBereich;
dWinkel := sWinkel / ((anzStriche - 1) / 5);
for i := 1 to Round((anzStriche -1)/5+1) do
begin
x1 := Cos(aWinkel) * iRadius;
y1 := Sin(aWinkel) * iRadius;
x2 := x1 + Cos(aWinkel) * lStrich;
y2 := y1 + Sin(aWinkel) * lStrich;
dsp.Canvas.MoveTo(xD(x1),yD(y1));
dsp.Canvas.LineTo(xD(x2),yD(y2));
dsp.Canvas.TextOut(xD(x2)-7,yD(y2)-12,IntToStr(sWert));
aWinkel := aWinkel + dWinkel;
sWert := sWert - zTeilung;
end;

// ----- Analog Wert -------------------------------

dsp.Canvas.Pen.Color := clRed;
aWinkel := sWinkel + ((zBereich-aWert) / zBereich) * sWinkel;
x1 := Cos(aWinkel) * zRadius;
y1 := Sin(aWinkel) * zRadius;
x2 := x1 + Cos(aWinkel) * zStrich;
y2 := y1 + Sin(aWinkel) * zStrich;
dsp.Canvas.MoveTo(xD(x1),yD(y1));
dsp.Canvas.LineTo(xd(x2),yD(y2));

// ----- Digital Wert ----------------------------

dText := 'Wert : ' + IntToStr(aWert);
x := (dsp.ClientWidth div 2) - (dsp.Canvas.TextWidth(dText) div 2);
y := dsp.ClientHeight - 24;
dsp.Canvas.TextOut(x, y, dText);
end;

Auf anhieb und auch wenn man näher hinsieht, kein fischertechnik, aber viel Schule, überall Sin/Cos. So schlimm ist es nun auch wieder nicht (bei Visual Basic ist es aber einfacher). Der Reihe nach :

  1. anaShowDisplay wurde in eine separate Unit gestellt, um die Wiederverwendung zu erleichtern.
  2. Im ersten Teil werden Konstanten definiert, die im eigentlichen Zeichenteil verwendet werden. Grund : die Konstanten können leichter geändert werden (statt 5mal 123 suchen) außerdem können sie bei einer späteren Erweiterung als Variablen herausgereicht werden.
    kStrich/lStrich : Länge des kurzen/lange Skalenstrichs
    anzStrich : Anzahl Skalenstriche
    sWinkel : Skalenwinkel in Radiant (die Skala ist ein Kreissegment)
    iRadius : innerer Radius der Skala
    zRadius : innerer Radius des Zeigers
    zStrich : Länge des Zeigers
    zBereich : Anzeigebereich (größter Wert, Beginn bei 0)
    zTeilung : Teilung der "Major Ticks" (der langen Striche)

    dWinkel : Delta Winkel (Schritt von Skalenstrich zu -strich)
    aWinkel : Anfangswinkel ( !! alle Winkel in Radiant : Pi = 180°)
    sWert : Skalenwert
    dText : Digitaler Anzeigewert als String.
  3. dsp.Canvas.Brush.Color ...
    Gezeichnet wird auf das Canvas (Leinwand) einer PaintBox (AufrufParameter dsp). Die muß erstmal etwas zurecht gerückt werden. (Rectangle löschst sie). Eine Eigenart des Canvas sind seine Koordinaten : Linke obere Ecke ist 0,0 gemessen wird ausschließlich in Pixel. anaShowDisplay geht aber (wie auch die VB-Version) von einer Zeichenfläche -50 / + 50 für x und 20 / 100 für y aus, deswegen sind zum echten Zeichnen auf den Canvas die Umrechnungsfunktionen xD / yD erforderlich.
  4. Minor Ticks
    Zuerst werden die kleinen Striche gezeichnet und zwar von rechts nach links (weil die sin/cos-Werte dann leichter zu handhaben sind) einer nach dem anderen. Und von innen nach außen, zuerst wird die Koordinate innen (x1/y1) bestimmt und dann die äußere durch Addieren des Längenanteils.
    Cos(aWinkel)*iRadius berechnet dabei den x-Wert der inneren Strichkoordinate (bezogen wird sich dabei auf einen auf picAnalog gar nicht mehr darstellbaren Punkt 0,0. .MoveTo geht zum Ausgang des Striches (ohne zu Zeichnen) und LineTo zeichnet.
  5. Major Ticks
    Ist eigentlich das gleiche, nur länger, Jeder fünfte Skalenstrich wird über"gemalt". Außerdem wird er beschriftet. Mit TextOut wird beschriftet.
  6. Analog Wert
    Zeichnen der Zeigerposition mit dem aktuellen Wert. Dazu wird erstmal der Analogwert (aWert) in einen Winkel (Radiant) umgerechnet (prozentualer Anteil von zBereich). Anschließend der Zeigerstrich (wie Skalenstrich, nur rot).
  7. Digital Wert
    Damit die Phantasie in Grenzen bleibt wird zusätzlich noch der aWert als numerischer Wert mit TextOut angezeigt (schön mittig).

Das wars mit anaShowDisplay. Das Besorgen der anzuzeigenden Analogwerte macht man mit links und in einer Endlosschleife (Abbruch : ESC-Taste) :

Die AnzeigeSchleife cmdAction ...

procedure TfrmMain.cmdActionClick(Sender: TObject);
begin
if ftiOpenInterface('COM1') = ftiFehler then begin
MessageDlg('InterfaceProblem : COM1', mtInformation, [mbOK], 0);
exit;
end;
cmdAction.Enabled := false;
cmdEnde.Enabled := false;
lblHinweis.Visible := true;

repeat
anaShowDisplay(Self.picAnalog, ftiGetAnalog(1));
ftiWaitForTime(100);
until ftiFinish(1);

ftiCloseInterface;
lblHinweis.Visible := false;
cmdAction.Enabled := true;
cmdEnde.Enabled := true;
anaShowDisplay(Self.picAnalog, 0);
cmdEnde.SetFocus;
end;

Das Meiste ist zur Verriegelung des Programms während des Betriebs der Abfrageschleife (repeat ... until) erforderlich (deren Abbruch über ESC-Taste oder E1 = true durch Finish). Noch mehr Verriegelung in :


procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if not cmdAction.Enabled then begin
ftiNotHalt := true;
CanClose := false;
end;
end;

CanClose = true lehnt den Ende-Wunsch (Klick auf das x rechts oben) schlicht ab, setzt aber ftiNotHalt = true (Finish kümmert sich auch darum) und leitet ein Schleifenende ein. Beim nächsten Klick aufs x klppst dann.

Wenn die Form verdeckt wurde, wird das AnalogDisplay gelöscht, zur Restauration : 

procedure TfrmMain.FormPaint(Sender: TObject);
begin
anaShowDisplay(picAnalog, 0);
end;

Das Resize

procedure TfrmMain.FormResize(Sender: TObject);
var
WDelta, SDelta: Integer;
begin
if Sperre then exit;
Sperre := true;
if WindowState = wsMinimized then exit;
if Width < WMin then Width := WMin;
if Height < SMin then Height := SMin;
WDelta := ClientWidth - WFest - picAnalog.Width;
SDelta := ClientHeight - SFest - picAnalog.Height;
cmdAction.Left := cmdAction.Left + WDelta;
cmdAction.Top := cmdAction.Top + SDelta;
cmdEnde.Left := cmdEnde.Left + WDelta;
cmdEnde.Top := cmdEnde.Top + SDelta;
lblHinweis.Top := lblHinweis.Top + SDelta;
picAnalog.Width := picAnalog.Width + WDelta;
picAnalog.Height := picAnalog.Height + SDelta;
anaShowDisplay(picAnalog, 0);
Sperre := false;
end;

Wo nun schon anaShowDisplay ein relatives Koordinatensystem bietet, so kann man ja auch mal verschiedene Größen der Form und damit auch der Analoganzeige probieren (es geht bis zum FullScreen). Der Trick beim Resize ist, sich ein (oder auch mehrere) Control auszusuchen, dem die Vergrößerung der Form zugute kommt (oder wos dann weniger wird). Das ist hier natürlich die Analoganzeige picAnalog. Dazu wird die bereinigte Größenänderung WDelta / SDelta gegenüber der vorherigen Anzeige bestimmt. Das geschieht durch Abziehen der alten Werte für die unveränderlichen Controls (WFest / SFest) und der alten Größe von picAnalog von den neuen Formmaßen. Die wird dann auf die alten Maße von picAnalog aufaddiert (wurde die Form kleiner ist .Delta negativ, dann wird abgezogen).

Sperre verhindert einen erneuten Aufruf wenn Resize bei der Arbeit ist, if Width / Height verhindern ein Verkleinern unter ein Mindestmaß.

Die Werte für WFest / SFest werden in FormCreate festgelegt :

procedure TfrmMain.FormCreate(Sender: TObject);
begin
WFest := ClientWidth - picAnalog.Width;
WMin := 200;
SFest := ClientHeight - picAnalog.Height;
SMin := 250;
anaShowDisplay(picAnalog, 0);
end;

Mögliche Erweiterungen

Eher mühsam :

Anzeige der gemessenen Werte (Bereich 0 - 1024) in beliebigen Wertebereichen z.B. 0 - 100° C. Dazu muß dann noch kräftig umgerechnet werden.
Anpassung von Strichstärke und Schriftgröße an besonders große Analoganzeigen.

Eher was für Spezialisten :

Umwandlung in eine Komponente