Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Ampelschaltung mit 4 Ampeln auf einem Timer


DerCaptain - Di 20.11.12 19:23
Titel: Ampelschaltung mit 4 Ampeln auf einem Timer
Hallo,
Habe Probleme mit meinem Delphi-Code.
Muss eine Ampelschaltung für eine Kreuzung mit einem if-then-else-if code machen und dabei eine RotGelb-Phase einbringen, die allerdings nur nach dem Rot-Signal ausgelöst wird.
Dabei soll die Rot/Grün-Phase von der waagerecht verlaufenden Strasse 10Sek. betragen und die der vertikal verlaufenden 5Sek.
Die Nebenphasen sollen jeweils 1Sek dauern.
Mein Code bis jetzt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
procedure TForm1.tiAmpelschaltungTimer(Sender: TObject);
begin
  if imAmpelGruen4.Visible = true
    then begin
           imAmpelGelb1.Visible := false;
           imAmpelGelb3.Visible := false;
           imAmpelGruen2.Visible := false;
           imAmpelGruen4.Visible := false;
           imAmpelRot1.Visible := true;
           imAmpelRot3.Visible := true;
           imAmpelGelb2.Visible := true;
           imAmpelGelb4.Visible := true;
           tiAmpelschaltung.Interval := 5000;
         end
    else if imAmpelGelb4.Visible = true
           then begin
                  imAmpelRot1.Visible := false;
                  imAmpelRot3.Visible := false;
                  imAmpelGelb2.Visible := false;
                  imAmpelGelb4.Visible := false;
                  imAmpelRotGelb1.Visible := true;
                  imAmpelRotGelb3.Visible := true;
                  imAmpelRot2.Visible := true;
                  imAmpelRot4.Visible := true;
                  tiAmpelschaltung.Interval := 10000;
                end
            else if imAmpelRot4.Visible = true
                   then begin
                          imAmpelGelb1.Visible := true;
                          imAmpelGelb3.Visible := true;
                          imAmpelRotGelb2.Visible := true;
                          imAmpelRotGelb4.Visible := true;
                          imAmpelRot2.Visible := false;
                          imAmpelRot4.Visible := false;
                          imAmpelRotGelb1.Visible := false;
                          imAmpelRotGelb3.Visible := false;
                          tiAmpelschaltung.Interval := 1000
                        end
                    else if imAmpelRotGelb4.Visible = true
                           then begin
                                  imAmpelGruen1.Visible := true;
                                  imAmpelGruen3.Visible := true;
                                  imAmpelGelb2.Visible := true;
                                  imAmpelGelb4.Visible := true;
                                  imAmpelRotGelb2.Visible := false;
                                  imAmpelRotGelb4.Visible := false;
                                  imAmpelGelb1.Visible := false;
                                  imAmpelGelb3.Visible := false;
                                  tiAmpelschaltung.Interval := 10000;
                                end

Könnte mir irgendjemand helfen?
Komme mit dem Timing noch nicht so ganz klar und manchmal wird noch immer eine Ampelphase übersprungen.


ub60 - Di 20.11.12 19:31

Mein Tipp:
Stelle das Timer-Intervall auf eine Sekunde und zähle im OnTimer-Event einen Zähler jeweils um 1 höher.
Frage dann den erhaltenen Zähler in der Art ab:

Delphi-Quelltext
1:
2:
3:
4:
5:
if Zaehler=10
  then ...
  else if Zahler=12 {oder was immer Deine Werte sind}
    then ...
    else

Besser geht das Ganze noch mit case .. of ...
Zeichne Dir auf alle Fälle eine "Timeline", wann die Ampel welche Zustände haben bzw. umschalten soll.

ub60


platzwart - Di 20.11.12 19:39

Außerdem...


Delphi-Quelltext
1:
if imAmpelGruen4.Visible = true then ...                    


...muss korrekt lauten:


Delphi-Quelltext
1:
 if imAmpelGruen4.Visible then ...                    


Niemals auf true oder false prüfen (bei false dann not verwenden).


DerCaptain - Di 20.11.12 20:24

Habe einen neuen Quelltext aufgesetzt,
bei dem ich diesmal die If-Bedingungen mit den Timer-Werten erstellt habe und alles genau nach plan gemacht habe.
Diesmal allerdings Springen Ampelreihe1 und Ampelreihe4 von Gelb auf Rot auf Rotgelb auf Gelb auf Rot und auch bei der Ampelreihe 2 und 3 wird grün übersprungen.
Code:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
procedure TfmKreuzung.tiAmpelschaltungTimer(Sender: TObject);
begin
   if tiAmpelschaltung.Interval = 1000
     then begin
            imAmpelGruen2.Visible := false;
            imAmpelGruen3.Visible := false;
            imAmpelGelb2.Visible := true;
            imAmpelGelb3.Visible := true;
            imAmpelGelb1.Visible := false;
            imAmpelGelb4.Visible := false;
            imAmpelRot1.Visible := true;
            imAmpelRot4.Visible := true;
            tiAmpelschaltung.Interval := 5000
          end
     else if tiAmpelschaltung.Interval = 5000
            then begin
                   imAmpelGelb2.Visible := false;
                   imAmpelGelb3.Visible := false;
                   imAmpelRot2.Visible := true;
                   imAmpelRot3.Visible := true;
                   imAmpelRot1.Visible := false;
                   imAmpelRot4.Visible := false;
                   imAmpelRotGelb1.Visible := true;
                   imAmpelRotGelb4.Visible := true;
                   tiAmpelschaltung.Interval := 10000;
                 end
            else if tiAmpelschaltung.Interval = 10000
                   then begin
                          imAmpelRot2.Visible := false;
                          imAmpelRot3.Visible := false;
                          imAmpelRotGelb2.Visible := true;
                          imAmpelRotGelb3.Visible := true;
                          imAmpelRotGelb1.Visible := false;
                          imAmpelRotGelb4.Visible := false;
                          imAmpelGelb1.Visible := true;
                          imAmpelGelb4.Visible := true;
                          tiAmpelschaltung.Interval := 1000;
                        end
                   else if tiAmpelschaltung.Interval = 1000
                          then begin
                                 imAmpelRotGelb2.Visible := false;
                                 imAmpelRotGelb3.Visible := false;
                                 imAmpelGelb2.Visible := true;
                                 imAmpelGelb3.Visible := true;
                                 imAmpelGelb1.Visible := false;
                                 imAmpelGelb4.Visible := false;
                                 imAmpelGruen1.Visible := true;
                                 imAmpelGruen4.Visible := true;
                                 tiAmpelschaltung.Interval := 5000;
                               end



Edit: Mir ist gerade aufgefallen, dass das eventuell 'was damit zutun haben könnte, dass ich ganz am Ende nochmal die Interval-Eigenschaft verändere?
Werde zunächst den Tipp des Vorposters ausprobieren.

Edit2: Keine Veränderung der Lage.
Besagte Phasen werden immernoch übersprungen.

Moderiert von user profile iconNarses: Beiträge zusammengefasst

Habe es geschafft.
Der Fehler lag darin, dass Delphi immer beim letzten Schritt das die erste Klammerung ausgeführt hat.
Doch ich konnte das Problem lösen indem ich einfach ein Objekt erstellt habe desses Eigenschaften eingebracht habe, um Bedingungen zu erzielen, die sich unterscheiden.


Narses - Di 20.11.12 23:37

Moin und :welcome: in der EE!

Hast du Interesse das vernünftig zu machen? Oder möchtest du weiter mit dem if-then-else-Intervall rumhampeln? :nixweiss:

Wenn du das ordentlich machen möchtest, sag bescheid, dann ziehen wir das eben in ein paar Minuten durch. ;)

cu
Narses


Narses - Do 22.11.12 12:43

Moin!

Nunja, Beratungsresistenz ist halt eine Anfänger-Krankheit... :| Egal, dann halt für die Nachwelt die Musterlösung:

Zunächstmal machen wir uns einen Plan, wann welche Lampe leuchten soll:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
          R1  Y1  G1  R2  Y2  G2  Dauer
Phase 0   X           X            1
Phase 1   X   X       X            1
Phase 2           X   X           15
Phase 3       X       X            1
Phase 4   X           X            1
Phase 5   X           X   X        1
Phase 6   X                   X   10
Phase 7   X               X        1
Hier steht Y für Gelb, sonst kann man das ja nicht auseinanderhalten. ;)

Dann legen wir ein Demo-Projekt an:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
type
  TForm1 = class(TForm)
    imAmpelRot1: TImage;
    imAmpelGelb1: TImage;
    imAmpelGruen1: TImage;
    imAmpelRot2: TImage;
    imAmpelGelb2: TImage;
    imAmpelGruen2: TImage;
    Timer1: TTimer;
  private
    FPhase: Integer;
    FTakt: Integer;
Wie man sieht, werden 6 Images gebraucht, anlegen und passend benennen, so dass das zwei Ampeln ergibt. Die beiden Klassen-Eigenschaften brauchen wir, um uns die aktuelle Phase und den Takt innerhalb der Phase zu merken.

Hier jetzt der Kern der Sache, der Timer-Ereignishandler:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.Timer1Timer(Sender: TObject);
  const
    TaktSoll: array[0..7of Integer = (
      1115111101
    );
begin
  Inc(FTakt); // nächster Takt
  if (FTakt = TaktSoll[FPhase]) then begin // Laufzeit erreicht?
    FPhase := (FPhase +1mod 8// nächste Phase (8->0)
    FTakt := 0// aber erster Takt in dieser Phase
    // pro Lampe aufzählen, wann sie leuchten soll:
    imAmpelRot1.Visible   := FPhase in [014567];
    imAmpelGelb1.Visible  := FPhase in [13];
    imAmpelGruen1.Visible := FPhase in [2];
    imAmpelRot2.Visible   := FPhase in [012345];
    imAmpelGelb2.Visible  := FPhase in [57];
    imAmpelGruen2.Visible := FPhase in [6];
  end;
Wir zählen in FTakt die Anzahl Timer-Ereignisse. Wenn die Anzahl der Vorgabe für diese Phase entspricht, dann wird zur nächsten Phase geschaltet. Beim Phasenübergang ist dann die Sichtbarkeit der Lampen anzupassen. Dazu liest man die Tabelle einfach Spaltenweise für die entsprechende Lampe ab. :idea:

Und noch etwas Kosmetik für die Initialisierung:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.FormCreate(Sender: TObject);

  procedure SetColor(AImage: TImage; const AColor: TColor; const AVisible: Boolean = TRUE);
  begin
    AImage.Picture.Bitmap.Canvas.Brush.Color := AColor;
    AImage.Picture.Bitmap.Width := AImage.Width;
    AImage.Picture.Bitmap.Height := AImage.Height;
    AImage.Picture.Bitmap.Canvas.Rectangle(AImage.Canvas.ClipRect);
    AImage.Visible := AVisible;
  end;

begin
  SetColor(imAmpelRot1, clRed);
  SetColor(imAmpelRot2, clRed);
  SetColor(imAmpelGelb1, clYellow, FALSE);
  SetColor(imAmpelGelb2, clYellow, FALSE);
  SetColor(imAmpelGruen1, clLime, FALSE);
  SetColor(imAmpelGruen2, clLime, FALSE);

Bei Fragen - fragen! ;)

cu
Narses