Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Problem mit Klassenerstellung (https://www.delphipraxis.net/191143-problem-mit-klassenerstellung.html)

Smeik 13. Dez 2016 13:16

Problem mit Klassenerstellung
 
Hallo,
ich möchte eine Sensor-Steuerung per OOP abbilden.
Eine Steuerung kann unterschiedliche Art und Anzahl von Sensoren (NTC-Sensor, Druck-Sensor) und Aktoren (Relais) besitzen .
Für diese habe ich jeweils eigene Klassen erstellt, die dann in eine Steuerung eingebunden werden.
Die einzelnen Sensoren besitzen widerum Schwellwerte die einstellbar sind und bei Überschreiten und unterschreiten Fehler auslösen. Die Aktoren widerum können nur Ein/Aus geschalten werden. Wie kann ich nun in der Klasse TSteuerung die Verknüfung zwischen meinen Sensoren und Aktoren am besten herstellen bzw. konfigurierbar machen. Bsp. Die Steuerung besitzt 2 Ntc-Sensoren mit Minimal-/Maximaltemperaturüberwachung und 1 Drucksensor mit entspr. Minimal/Maximaldrucküberwachung und 2 Relais die bei einem Fehler zugeschalten werden können. Wie kann ich nun in der Klasse TSteuerung am besten die Beziehung zwischen Sensor und Aktor konfigurierbar machen. Ich stelle mir eine Art Matrix (Array) vor in der die Fehlerzustände der Sensoren mit den Eingängen der Aktoren verknüft werden können. Damit möchte ich dann mittels GUI zb. den SensNTC[1].MaxError und SensDruck[1].MinError mit Relais[2] und den SensDruck[1].MaxError mit Relais[1] verknüpfen. Das ganze habe ich in der Steuerung momentan mit dynamischen Arrays aufgebaut um flexibel die Anzahl der Sensoren und Aktoren festlegen zu können.

Fragen:
1. Wie verküpfe ich einen Sensor.Alarm mit einm Aktor.Eingang?
2. Sollte man die Sensoren lieber von einer Klasse TSensor ableiten und das Relais von einer Klasse TAktor um dann ein mehrdimensionales Array (TAlarmConfig) zu erstellen? Hier fehlt mir leider noch die zündende Idee bzw. die Erfahrung mit OOP.


Delphi-Quellcode:
type
  TSensNtc = class(Tobject)
  private
    FTmpIs: single;
    FErrMin: boolean;
    FErrMax: boolean;
    FLimitMax: smallint;
    FLimitMin: smallint;
    procedure SetErrMin(const Value: boolean);
    procedure SetErrMax(const Value: boolean);
    procedure SetLimitMax(const Value: smallint);
    procedure SetLimitMin(const Value: smallint);
    procedure SetTmpIst(const Value: single);
  public
    property TmpIst: single read FTmpIs write SetTmpIst; // IsValue
    property ErrMin: boolean read FErrMin write SetErrMin; // IsValue
    property ErrMax: boolean read FErrMax write SetErrMax; // IsValue
    property LimitMax: smallint read FLimitMax write SetLimitMax;
    // SetValue
    property TmpLimitMin: smallint read FLimitMin write SetLimitMin;
    // SetValue
    constructor Create;
    destructor Destroy; override;
  end;

type
  TSensPress = class(Tobject)
  private
    FErrMin: boolean;
    FPressureIst: single;
    FLimitMax: smallint;
    FLimitMin: smallint;
    FErrMax: boolean;
    procedure SetErrMax(const Value: boolean);
    procedure SetErrMin(const Value: boolean);
    procedure SetLimitMax(const Value: smallint);
    procedure SetLimitMin(const Value: smallint);
    procedure SetPressureIst(const Value: single);
  public
    property PressureIst: single read FPressureIst write SetPressureIst;
    // IsValue
    property ErrMin: boolean read FErrMin write SetErrMin; // IsValue
    property ErrMax: boolean read FErrMax write SetErrMax; // IsValue
    property LimitMax: smallint read FLimitMax write SetLimitMax;
    // SetValue
    property LimitMin: smallint read FLimitMin write SetLimitMin;
    // SetValue
    constructor Create;
    destructor Destroy; override;
  end;

type
  TRelay = class(TObject)
  private
    FSetOn: boolean;
    procedure SetSetOn(const Value: boolean);
  public
    property SetOn: boolean read FSetOn write SetSetOn;
  end;

type
  TSteuerung = class(Tobject)
  private
    FSensNtcCount: integer;
    FSensPCount: integer;
    FRelayCount: integer;
    FSensNtc: array of TSensNtc;
    FSensP: array of TSensPress;
    FRelay: array of TRelay;
  public
    constructor Create;
    destructor Destroy; override;
  end;

constructor TSteuerung.Create;
var
  i: integer;
begin
  inherited;
  FSensNtcCount := 2;
  FSensPCount := 3;
  FRelayCount := 2;
  for i := 0 to FSensNtcCount - 1 do
    FSensNtc[i] := TSensNtc.Create;
  for i := 0 to FSensPCount - 1 do
    FSensP[i] := TSensPress.Create;
  for i := 0 to FRelayCount - 1 do
    FRelay[i] := TRelay.Create;
end;

Blup 14. Dez 2016 11:25

AW: Problem mit Klassenerstellung
 
Ich würde folgende Strukur vorschlagen:
Delphi-Quellcode:
type
TCustomSensor = class;
TSignalGeber = class;
TSignal = class;
TCustomAktor = class;

// Jeder Sensor liefert erst mal nur einen Messwert und löst ein Ereignis beim SignalGeber aus, wenn sich der Messwert ändert.

TCustomSensor = class()
private
  FSignalGeber: TSignalGeber;
publiv
  property Value: Float;
  property SignalGeber;
end;

// Der Signalgeber reagiert auf das Ereignis eines Sensors. Dazu verfügt er über eine Liste von frei definierten Signalen. Der Signalgeber leited das Ereignis an alle Signale weiter.

TSignalGeber = class()
protected
  FSignalList: TObjectList; // TSignal
puplic
  procedure DoOnSensorChange(Sender: TCustomSensor);
  procedure AddSignal(AItem: TSignal);
  procedure RemoveSignal(AItem: TSignal);
end;

//Das Signal reagiert auf das Ereignis in dem es den Wert des Sensors mit dem hinterlegten Wertebereich vergleicht und den neuen Zustand ermittelt. Ändert sich dabei der Zustand (False/True) meldet es dies dem zugeordneten Aktor.

TSignal = class()
puplic
  procedure DoOnSensorChange(Sender: TCustomSensor);
  property Value: Float;
published
  property MinValue: Float;
  property MaxValue: Float;
  property Aktor: TCustomAktor;
end;

TCustomAktor = class()
puplic
  procedure DoOnSignalChange(Sender: TSignal);
end;
Der Vorteil ist, konkrete Implementierungen müssen nur von TCustomSensor oder TCustomAktor abgeleited werden und sind relativ einfach gehalten. TSignal und TSignalGeber müssen nicht mehr verändert werden, da diese weitgehend frei von Abhängigkeiten implementiert sind. Ein Fehler ist hier auch nur ein frei definiertes Signal, das durch einen Aktor zu verarbeiten ist. Ein Aktor könnte z.B. mit dem Anschalten einer Warnlampe oder einem Protokolleintrag reagieren.

Smeik 14. Dez 2016 13:53

AW: Problem mit Klassenerstellung
 
Hallo Blup, deine Idee finde ich gut.
Der Signalgeber in Kombination mit dem Signal ist sozusagen das Bindeglied zwischen Sensor und Aktor. Der Aktor sollte ja eigentlich nicht den Sensor kennen und umgekehrt.
Ich werde mal versuchen Sie so umzusetzen.

Danke :)

Smeik 15. Dez 2016 10:08

AW: Problem mit Klassenerstellung
 
Ich habe deinen Vorschlag mal umgesetzt und er funktioniert sehr gut. Jetzt wollte ich die verschiedenen Klassen in einzelnen Units (uSiganl, uAktor, uSensor) auslagern und habe das Problem der zirkulären Unit-Referenzen. Meine Idee war eine unit uSignal zu erstellen in der zum einem TSignal und davon abgeleitet TSignalMax TSignalMin und weitere stehen. Das selbe mit TSensor (TSensorNtc, ...) und mit TAktor (TAktorRelay, TAktorLed). Das hat leider so nicht geklappt.
Sollte ich lieber die drei Urklassen TSignal, TCustomSensor, TCustomAktor und TSignalgeber in einer Unit stehen lassen und die Ableitung der Urklassen dann in einzelne Units auslagern? Was wäre hier der richtige Weg?

Blup 15. Dez 2016 20:35

AW: Problem mit Klassenerstellung
 
Es handelt sich um weitgehend abstrakte Basisklassen, die in enger Beziehung zueinander stehn. Ich würde alle zusammen in einer Unit lassen. Die Implementierung eines konkreten Sensors oder Aktors gehört dagegen in eine eigene Unit, die je nach technischer Umsetzung wieder andere Abhängigkeiten hat.

IYuky 20. Dez 2016 08:17

AW: Problem mit Klassenerstellung
 
Ich würde noch vorschlagen mit Interfaces zu arbeiten

nahpets 20. Dez 2016 21:08

AW: Problem mit Klassenerstellung
 
Zitat:

Zitat von Smeik (Beitrag 1356222)
... und habe das Problem der zirkulären Unit-Referenzen.

Wo hast Du die Units in der Uses-Anweisung eingetragen?

interface oder implementation?

Das Problem lässt sich oft (aber nicht immer) beheben, wenn man die Uses-Anweisung hinter implementation nutzt.

Units schreib ich eigentlich nur dann hinter interface, wenn sie bereits vor implementation zwingend benötigt werden.

Eventuell hast Du ja damit Glück.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz