Einzelnen Beitrag anzeigen

Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#7

Re: Brauche Objektorientierte Modellierungshilfe :-)

  Alt 19. Jun 2007, 18:27
Zitat von DelphiManiac:
Hätte den Vorteil, dass ich jede Prüfung gleich behandeln kann.

Ich muss aber während meine Prüfablaufs auch eine optische darstellung machen, also z.B.: das Kalibrieren,

der Kalibrator gibt einen Wert vor (sagen wir mal 20mA) und ich muss mit dem Prüfling kommunizieren ob er diese 20mA
erkannt hat und ob der Wert sauber steht, d.h. nicht schwankt um dann den Sensorwert abzuspeichern.

Wie kann ich dass dann in meiner Prüfklasse am Besten bewerkstelligen.
Hoffe ihr versteht was ich meine.
Erstmal: Hoffe ich auch!

Den Ansatz den Hsg hier vorgeschlagen hat, den finde ich schon mal ganz gut! Was Du klar ausnutzen kannst ist die Tatsache, dass Du immer einen gleichen Ablauf hast. Den solltest Du einfach mal ganz abstrakt betrachten.
Ich weiß jetzt nicht genau, wie Prüfling, Prüfplatine usw. mit einander zusammen hängen. Das sind eben die Info's, die schon wichtig für die Modellierung wären. Du hast ja offensichtlich immer ein Tupel aus Prüfling und Kalibrator. Ggf. kannst Du natürlich auch eher das Tripel Prüfplatine, Prüfling und Kalibrator haben (wenn es mehrere Prüflinge auf der gleichen Platine geben kann oder die aus anderen Gründen adressiert werden).
Jedenfalls ist hier eine einfache Möglichkeit, dass Du für das, mit dem Du direkt kommunizierst eine Klasse baust. Diese können (sollten?) von einer Basisklasse abgeleitet werden, in der die Methoden erstmal abstrakt sind. In die Basisklasse kommen dabei Methoden, die für alle Prüfungen benötigt werden. Z.B. setze Wert bei der Prüfplatine und leseWert bei dem Messgerät. Von diesen Basisklassen leitest Du dann einfach die konkreten Klassen (spezielle Prüfplatine und spezielles Messgerät) ab.
Jetzt hast Du zwei getrennte Klassen, eine dient dem setzen eines Werts, die andere dem Auslesen. Eine Messung sieht jetzt aber auch immer sehr gleich aus, ganz allgemein (wie Du es schon aufgeschrieben hast!) geht es um folgendes:
  1. Setzen eines Wertes beim Prüfling
  2. Ggf. Warten bis der Wert stabil anliegt
  3. Lesen des Wertes durch das Messgerät
  4. Speichern des Messwertes
  5. Ggf. 3 und 4 wiederholen
  6. Auswerten der Messwerte (Abweichung von erwarteten Wert)

Soweit sieht das für alle Messungen noch recht gleich aus! Das kannst Du dann wieder einfach ausnutzen, indem Du hier eine eigene Klasse (TMessung) draus machst. Diese macht dann nichts anderes als das, was dort drin steht. Natürlich ist auch diese Klasse erstmal abstrakt und es gibt dann konkrete Ausprägungen. Ich gehe hier einfach mal davon aus, dass es sich bei den verschiedenen Messungen immer darum handelt, dass Du bestimmte Werte als Fließkomazahl setzt und entsprechend wieder ausliest. Sagen wir mal einfach, Du hast eine I/O-Karte als Prüfling, die eine bestimmte Spannung, einen Wiederstand und eine Stromstärke zur Verfügung stellen kann (mir egal ob das Sinn macht!) und dann noch ein Multimeter, dass Dein Messgerät darstellt.
Die Messungen bestehen dann daraus, dass x-Volt, y-Ampere oder z-Ohm "angelegt" und gemessen werden sollen. Anders gesagt gibt es eine Spannungs, eine Stromstärke und eine Wiederstandsprüfung. Nur um es zu sagen, alles hier gesagt lässt sich leicht (hoffe ich) auf verschiedene Messwerte einer Art übertragen!

Gut, diese drei Messungen laufen also im Prinzip gleich ab, man ruft die Methode setzeWert der Prüfplatine auf, wartet eine bestimmte Zeit (auch 0), misst n-mal (n >=1) den anliegenden Wert, speichert die gelesenen Werte zwischen, bildet Mittelwert, Std.Abweichung, Varianz, ... Der Unterschied zwischen den Messungen liegt nur darin, welche Prüfplatine verwendet wird (besser welcher Prüfling) und welches Messgerät. Natürlich kann das Messgerät die ganze Zeit das gleiche Multimeter sein, nur die Art der Messung variiert, aber es kann halt virtuell als echt unterschiedliche Geräte aufgefasst werden. Auch die Zeit die nötig ist, bis eine bestimmte Spannung oder Stromstärke gemessen werden kann ist natürlich variabel. Trotzdem sind dies nur Parameter, die gesetzt werden müssen, der Ablauf ist dann immer gleich.
Hier kannst Du leicht über Dependency-Injection nachdenken. So benötigt jede Messung eben einen Prüfling/eine Prüfplatine und ein (passendes) Messgerät. Beides kannst Du in einer anderen Klasse (z.B. eine Fabrik) erzeugen und an die Messklasse übergeben. Klingt alles etwas kompliziert, ist es aber gar nicht! Mal am Beispiel:

Delphi-Quellcode:
// Erstmal die Prüflinge
type
  TPruefling = class(TObject)
    public
      // Abstrakte Methode, setzt eben einen Wert
      procedure setValue(const Value: Double); virtual; abstract;
  end;

  TSpannungsPruefling = class(TPruefling)
    public
      // implementiert die abstrakte Methode
      // hier also die Kommunikation, die nötig ist damit der Prüfling diesen Wert ausgibt/einstellt/...
      procedure setValue(const Value: Double); override;
  end;

  TWiederstandsPruefling = class(TPruefling)
    public
      // implementiert die abstrakte Methode
      // hier also die Kommunikation, die nötig ist damit der Prüfling diesen Wert ausgibt/einstellt/...
      procedure setValue(const Value: Double); override;
  end;
 
  ...
Damit hast eine abstrakte Prüflingsklasse. Die bietet einfach eine Methode setValue an, die dafür sorgt, dass der Prüfling diesen Wert bereitstellt. Ist der Prüfling eben eine Spannungsquelle, wird der Wert in Volt ausgegeben, als Wiederstand währe es der Wiederstand in Ohn, als Stromquelle der Strom in Ampere. Die Klassen, die von TPruefling abgeleitet sind können natürlich noch jede Menge anderer Methoden und variablen enthalten! Wichtig ist nur, dass alles was ein TPruefling ist über die Methode setValue seinen Wert gesetzt bekommen kann!

Beim Messgerät sieht es ganz analog aus:

Delphi-Quellcode:
type
  TMessgeraet= class(TObject)
    public
      // Abstrakte Methode, liest den aktuellen Wert
      function getValue: Double; virtual; abstract;
  end;

 TSpannungsMessgeraet= class(TMessgeraet)
    public
      // implementiert die abstrakte Methode
      // hier also die Kommunikation, die nötig ist damit das Messgerät den aktuellen Wert liest
      function getValue: Double; override;
  end;

 TWiederstandsMessgeraet= class(TMessgeraet)
    public
      // implementiert die abstrakte Methode
      // hier also die Kommunikation, die nötig ist damit das Messgerät den aktuellen Wert liest
      function getValue: Double; override;
  end;

  ...
Ja, ich denke soweit ist eigentlich noch alles klar. Der eigentlich Trick kommt erst jetzt. Nun kann man die Messklasse erstellen, die eben etwas abstrakter arbeiten kann. Ihre Aufgabe sind die vorhin genannten Punkte, sie hat einen Prüfling und ein Messgerät (die zusammenpassen!) und macht nichts anderes als einen Wert anzulegen, zu warten, den aktuellen Wert (mehrfach) zu messen und dann irgendwas zu berechnen (hier mal einfach Mittelwert + Std. Abweichung).
Das ganze könnte so aussehen:

Delphi-Quellcode:
type
  TTestKlasse = TObject
    private
      FPruefling: TPruefling;
      FMessgeraet: TMessgeraet;
      FWartezeit: Cardinal;
    public
      constructor create(const Pruefling: TPruefling;
                         const Messgeraet: TMessgeraet;
                         const Wartezeit: Cardinal = 0);
      destructor destroy; override;
      
      procedure fuehreMessungAus(const Wert: Double;
                                 const AnzahlMessungen: Cardinal;
                                 const WartezeitZwischenMessungen: Cardinal;
                                 out Mittelwert: Extended;
                                 out StdAbweichung: Extended);
  end;


....

constructor TTestKlasse.create;
begin
  inherited create;

  // speichern der übergebenen Werte/Referenzen
  self.FPruefling := Pruefling;
  self.FMessgeraet := Messgeraet;
  self.FWartezeit := Wartezeit;
end;

denstructor TTestKlasse.destroy;
begin
  // Freigabe
  self.FPruefling.Free;
  self.FMessgeraet.Free;

  inherited;
end;
 
procedure TTestKlasse.fuehreMessungAus(const Wert: Double;
                           const AnzahlMessungen: Cardinal;
                           const WartezeitZwischenMessungen: Cardinal;
                           out Mittelwert: Extended;
                           out StdAbweichung: Extended);
var results: Array of Double;
    i: Cardinal;
begin
  // Anlegen des Wertes an Pruefling
  self.FPruefling.setValue(Wert);

  // Warten bis Wert sicher stabil anliegt (liegen sollte)
  // hier mal ein gaaanz unschöner weg, besser delay aus der DP!
  sleep(self.FWartezeit);

  // Ausführen der Kontrollmessungen
  setLength(results, AnzahlMessungen);
  for i := 0 to anzahlMessungen - 1 do
  begin
    results[i] := self.FMessgeraet.getValue;
    
    // Wartezeit zwischen zwei Messungen abwarten
    // hier mal ein gaaanz unschöner weg, besser delay aus der DP!
    sleep(WartezeitZwischenMessungen);
  end;

  // berechnen von Mittelwert und Std.Abweichung
  // Methode aus Unit Math, sehr flink!
  MeanAndStdDev(results, Mittelwert, stdAbweichung);
end;
Ich hoffe es ist soweit schon klar, was gemacht wird. An sich hier der Hinweis, dass das nicht ganz sauberer Code ist! Das sleep sollte man nicht wirklich verwenden und es wäre auch sinnvoll die Gültigkeit des Wertes zu prüfen, zu schauen ob ein Messgerät und Prüfling übergeben wurden (<> nil/assigned), schauen dass auch überhaupt > 0 Messungen durchgeführt werden sollen, usw.
Hier geht es aber nur um die Idee. Man hat einfach eine Testklasse, die immer die gleichen Schritte für ein beliebiges Tupel aus Messgerät und Prüfling ausführt. Um welchen Prüfling und welches Messgerät es sich dabei genau handelt legt der Konstruktor fest.
Der nächste Schritt besteht dann einfach darin, dass Du eine Klasse schaffst, die entsprechende Objekte erzeugt:

Delphi-Quellcode:
  spannungsmessung := TTestKlasse.create(TSpannungsPruefling.Create,
                                         TSpannungsMessgerät.Create,
                                         0);
  stromstärkenmessung := TTestKlasse.create(TStromstärkenPruefling.Create,
                                            TStromstärkenMessgerät.Create,
                                            10);
  ...
Die Variablen spannungsmessung und stromstärkenmessung sind vom Typ TTestKlasse und haben somit die Methode fuehreMessungAus. Der übergibst Du dann die Spannung oder die Stromstärke, die Du messen möchtest, die jeweilige Anzahl der Messungen usw., den Rest erledigt die für Dich. Dabei wird in der spannungsmessung wirklich eine Spannung angelegt und gemessen, bei der stromstärkenmessung eben eine Stromstärke. Wie gesagt, Du kannst das leicht an nur Spannungen, dafür aber unterschiedliche Werte, anpassen (oder eben später an andere Prüflinge/Messgeräte!).

Und um auf Deine eigentliche Frage zu sprechen zu kommen, natürlich kannst Du auch mehr als eine Methode anbieten oder die mehr als das hier machen lassen. Soll jede Messung auch etwas zeichnen, dann kannst Du auch dafür in der TTestklasse einem Methode vorsehen. Die könnte z.B. noch zusätzlich einen Canvas übergeben bekommen und eine Zeichenklasse. Die Zeichenklasse macht dann nicht mehr als bestimmte Dinge auf den Canvas zu zeichnen. Ist diese abstrakt, kannst Du natürlich für Spannung und Stromstärke wieder konkrete Implementierungen erzeugen, die unterschiedliche Dinge zeichnen. Interessieren Dich nur die Abweichungen (ohne Einheiten) kannst Du natürlich auch auf eine einzelne Zeichenklasse, die immer das gleiche macht zurückgreifen.

Ich hoffe das die Ideen grob klar sind (vielleicht nicht so schön wie bei DUnit, aber hoffentlich trotzdem hilfreich!). Ansonsten einfach weiter nachfragen!

Gruß Der Unwissende
  Mit Zitat antworten Zitat