# Zwei Pumpenpaare abwechselnd + 1 Pumpe bei hohen Wasseraufkommen



## mista (27 November 2020)

Hallo liebe Leute,

ich stehe mal wieder vor einer Herausforderung.

Ich muss eine Pumpensteuerung entwickeln die folgende Kriterien Erfüllen muss:


2 Pumpenpaare mit je 2 Pumpen
Behälter wird ab einem Niveau geleert, ein Pumpenpaar lauft an
ist ein Anderes Niveau unterschritten gehen die Pumpen des Pumpenpaar aus
Ist das Wasservorkommen zu hoch und Niveau 3 wird erreicht lauft eine weitere Pumpe des jeweiligen anderen Pumpenpaares an, welche die weinigsten Betriebsstunden aufweist.
Bis ein Niveau erreicht wird wo die Dritte Pumpe ausschaltet.
Wenn eine Pumpe aus geht, darf sie erst wieder anspringen nach einer einstellbaren Zeit wieder anlaufen.
Eine Störumschaltung der Pumpenpaare soll natürlich auch implementiert werden.
Tia 15 Update 4
Vorzugsweise in FUP wenn nicht geht in SCL, KEIN AWL.

In SCL würde ich mir das zusammen frickeln können, aber nicht in FUP.
Da es nicht zusammen gefrickelt werden soll, sondern auch gut Wartbar hoffe ich, auf kreativen Input hier, denn ich weiß nicht so recht wie ich anfangen soll.

Klar Nach Betriebsstunden, sortieren, und wenn eine Pumpe gesperrt ist eine hohe Zahl eintragen in dem Sortierarray. Aber das fühlt sich für mich nicht nach leicht wartbar an.
Pumpenbausteine muss ich auch selber Programmieren.

Vielleicht könnt ihr mir ja ein wenig Helfen, oder Links geben, wie eine Pumpenfolge zu Programmieren ist.

Vielen Dank, und bleibt gesund.


----------



## GUNSAMS (27 November 2020)

Na, vielleicht solltest du uns erst einmal weiterhelfen.

In dem du uns erst einmal mitteilst, welcher Steuerungstyp und welche Software.


----------



## mista (27 November 2020)

Wie oben beschrieben, TIA 15 Update 4 (Kundenwunsch)
Und Steuerung S7 1511 PN.


----------



## GLT (28 November 2020)

mista schrieb:


> Ich muss eine Pumpensteuerung entwickeln die folgende Kriterien Erfüllen muss:
> 
> 
> 2 Pumpenpaare mit je 2 Pumpen
> ...


Kenne die Anlage ja nicht, scheint mir logisch aber nicht schlüssig.

Sind die Pumpen(paare) in ihrer Leistung identisch?
Zumindest scheinen sie wohl einzeln ansteuerbar zu sein - warum soll dann immer gleich ein Pumpenpaar eingeschaltet werden, anstatt kontinuierlich zuschaltend? Oder sind das Doppelpumpen?

Wir haben das bislang so gelöst, dass für jede Pumpe die Betriebsstunden erfasst wurden; bei Tageswechsel wurde die Priorität der Pumpen in Abhängigkeit der Betriebsstunden neu verteilt - was Pumpe 1,2 usw. ist. Pumpen mit aktiver Störung wurden dann zwar in die Reihe einsortiert, jedoch in der Ansteuerung in der Priorität "übersprungen" - war die Störung behoben, lief die Pumpe in der angedachten Sortierung mit.

Wenn man die Wahl zwischen FUP und SCL hat, ist ein SCL-Code meiner Ansicht für dergleichen wesentlich wartbarer als FUP.


----------



## hucki (28 November 2020)

Für die Zu-/Abschaltung nach Betriebsdauer hat Zotos mal ein FUP-Beispiel für Classic gepostet -> Motorenpendel
Eine Abwandlung dieses Beispiels mit zusätzlicher Laufzeitüberwachung und Wiederanlaufsperre für TIA SCL findest Du hier -> Pumpenpendel

Dazu dann noch die Zu-/Abschaltung nach Niveau (Anzahl der benötigten Pumpen).
Die sollte ja nicht mehr den riesen Aufwand darstellen.


----------



## mista (29 November 2020)

Das wäre dann mein Erster Versuch:


```
REGION Initialisierung
    // Niveuau Reinigungsbetrieb
    IF #Reinigungsbetrieb THEN
        #tempNiveau1 := #einstellungen.Niveaus.Reinigungsbetrieb.Niveau_1;
        #tempNiveau2 := #einstellungen.Niveaus.Reinigungsbetrieb.Niveau_2;
        #tempNiveau3 := #einstellungen.Niveaus.Reinigungsbetrieb.Niveau_3;
        #tempNiveau4 := #einstellungen.Niveaus.Reinigungsbetrieb.Niveau_4;
        #tempNiveau5 := #einstellungen.Niveaus.Reinigungsbetrieb.Niveau_5;
    ELSE
        #tempNiveau1 := #einstellungen.Niveaus.Normalbetrieb.Niveau_1;
        #tempNiveau2 := #einstellungen.Niveaus.Normalbetrieb.Niveau_2;
        #tempNiveau3 := #einstellungen.Niveaus.Normalbetrieb.Niveau_3;
        #tempNiveau4 := #einstellungen.Niveaus.Normalbetrieb.Niveau_4;
        #tempNiveau5 := #einstellungen.Niveaus.Normalbetrieb.Niveau_5;
    END_IF;
END_REGION


REGION Freigaben nach Niveau
    // Niveau Freigaben
    IF #Füllstand > #tempNiveau1 THEN
        #tempAnforderungNormal := true;
    END_IF;
    
    IF #Füllstand < #tempNiveau1 THEN
        #tempAnforderungNormal := false;
    END_IF;
    
    // Anforderung dritte Pumpe
    IF NOT #Reinigungsbetrieb AND #Füllstand > #tempNiveau4 THEN
        #tempDrittePumpe := true;
    END_IF;
    
    IF #tempDrittePumpe AND #Füllstand < #tempNiveau2 THEN
        #tempDrittePumpe := false;
    END_IF;
END_REGION


REGION Automatikbetrieb
    // Einschalten wenn keine Pumpenpaar läuft und Anforderung
    IF #tempAnforderungNormal THEN
        IF NOT #statPumpenpaar1EIN AND NOT #statPumpenpaar2EIN THEN
            IF #statPumpenpaar1EIN OR (NOT #Pumpen[1].Status.Aktiv OR NOT #Pumpen[2].Status.Aktiv) THEN
                #statPumpenpaar2EIN := TRUE;
                #stat_LetztesPumpenpaar := 2;
                
            ELSIF #stat_LetztesPumpenpaar = 2 OR (NOT #Pumpen[3].Status.Aktiv OR NOT #Pumpen[4].Status.Aktiv) THEN
                #statPumpenpaar1EIN := TRUE;
                #stat_LetztesPumpenpaar :=1;
            END_IF;
        END_IF;
    ELSE
        #statPumpenpaar1EIN := FALSE;
        #statPumpenpaar2EIN := FALSE;
    END_IF;
    
    IF #statPumpenpaar1EIN THEN
        #Pumpen[1].Status.Lauft := true;
        #Pumpen[2].Status.Lauft := true;
        #Pumpen[3].Status.Lauft := false;
        #Pumpen[4].Status.Lauft := false;
        #statStart := 3;
        #statEnd := 4;
    ELSIF #statPumpenpaar2EIN THEN
        #Pumpen[3].Status.Lauft := true;
        #Pumpen[4].Status.Lauft := True;
        #Pumpen[1].Status.Lauft := FALSE;
        #Pumpen[2].Status.Lauft := FALSE;
        #statStart := 1;
        #statEnd := 2;
    END_IF;
    
    IF #tempDrittePumpe THEN
        IF NOT #statDrittePumpeEIN THEN
            FOR #temp_i := #statStart TO #statEnd BY 1 DO
                IF #Pumpen[#temp_i].Status.Modus = 2 // Modus Auto
                    AND #Pumpen[#temp_i].Betriebstunden < #tempAktuelleLaufzeit
                THEN
                    #tempAktuelleLaufzeit := #Pumpen[#temp_i].Betriebstunden;
                    #tempSelekiertePumpe := INT_TO_USINT(#temp_i);
                END_IF;
            END_FOR;
            IF #tempSelekiertePumpe >= 1 THEN
                #Pumpen[#tempSelekiertePumpe].Status.Lauft := true;
                #statDrittePumpeEIN := true;
            ELSE
                //Fehler keine Pumpe verfügbar!
                ;
            END_IF;
        END_IF;
    ELSE
        #statDrittePumpeEIN := FALSE;
        #Pumpen[#statStart].Status.Lauft := false;
        #Pumpen[#statEnd].Status.Lauft := false;
    END_IF;
    
END_REGION
```

Nicht so dynamisch wie das Pumpenpendel und simpler


----------



## hucki (30 November 2020)

Warum sind die ganzen Niveauvorgaben z.B. eigentlich keine Arrays?
Würde doch noch Einiges vereinfachen:

```
REGION Initialisierung
     // Niveau Reinigungsbetrieb

     #tempNiveau := SEL(G := #Reinigungsbetrieb, IN0 := #einstellungen.Niveaus.Normalbetrieb; IN1 := #einstellungen.Niveaus.Reinigungsbetrieb);

END_REGION

...
```


----------



## mista (30 November 2020)

Na weil ich die SEL Funktion vorher nicht kannte.

Die Zykluszeit über RT_INFO(); benutze ich um die Sperrzeit abzuziehen. Doch in der TIA Simulation kommen mir Sekunden Fast wie Minuten?
Beim Pumpenpendel nutzt du den für die Betriebsstunden, kann ich das so übernehmen?
Also die Zykluszeit addieren auf die Gesamtlaufzeit solange ich eine Betriebsrückmeldung der Pumpe bekomme und dann irgendwie (x 3600000000) in Stunden Umrechen?


----------



## hucki (30 November 2020)

hucki schrieb:


> Warum sind die ganzen Niveauvorgaben z.B. eigentlich keine Arrays?





mista schrieb:


> Na weil ich die SEL Funktion vorher nicht kannte.


Die Arrays haben ja nix mit der SEL-Funktion zu tun. Die Zuweisungen funktionieren mit IF genauso.
Man kann halt das komplette Array auch an einem Stück übergeben, statt jedes Feld einzeln.
Das trifft auch für gleich aufgebaute STRUCTs/UDTs zu.


Das SEL ist dann eher so 'ne "Marotte" von mir, für nur eine Auswahlzuweisung kein IF...THEN...ELSE zu verwenden, weil ich persönlich es mit dieser einzelnen direkten Zuweisung lesbarer finde.
Der kompilierte Code wird dann vermutlich eh' wieder für beide Varianten sehr ähnlich aussehen.


----------



## hucki (30 November 2020)

mista schrieb:


> Die Zykluszeit über RT_INFO(); benutze ich um die Sperrzeit abzuziehen. Doch in der TIA Simulation kommen mir Sekunden Fast wie Minuten?
> Beim Pumpenpendel nutzt du den für die Betriebsstunden, kann ich das so übernehmen?
> Also die Zykluszeit addieren auf die Gesamtlaufzeit solange ich eine Betriebsrückmeldung der Pumpe bekomme und dann irgendwie (x 3600000000) in Stunden Umrechen?


Welchen MODE und OB gibst Du für RT_INFO an?

Wenn ich mich richtig erinnere, hatte ich das damals auf 'ner realen S7-1500 laufen lassen.
Deswegen hatte ich für den MODE auch die 25 verwendet, obwohl in der Hilfe eigentlich was von 1 stand. Mit der 1 hab' ich komischerweise keine Zeiten erhalten.

Bei mir funktionierte der Betriebszähler jedenfalls so.
LTIME hatte ich damals der Einfachheit halber gewählt.
Für Anzeigen gibt's dann ja noch Konverter, die aus der LTIME was für Menschen Lesbares machen.


----------



## mista (30 November 2020)

Mode 25 für letzte Zykluszeit und OB 1 für die Abfrage.

Ja genau so einen Konverter, wie machst du den?

Ich hab ein weg mit ms aber nicht mit ns.


----------



## hucki (30 November 2020)

T_CONV hat einiges an Möglichkeiten.

Ich persönlich ziehe mir meist zum Anzeigen auch nur die vollen Stunden aus der LTIME, also LTIME_TO_LINT und dann Division durch 3.600.000.000.000 (1h = 60m x 60s x 1.000ms x 1.000µs x 1.000ns).



PS: 
Also in meiner Simu laufen die Sekunden (Division nur durch 1.000.000.000) recht "normal" und nicht wie Minuten.
Und mein Tablet ist bereits über 6 Jahre alt.


----------



## mista (1 Dezember 2020)

Für die BEtriebsstunden mache ich das so:


```
IF #Rückmeldung_betrieb THEN    #statGesamtLaufzeit += #Zykluszeit;
    #tempTimeToINT := LTIME_TO_LINT(#statGesamtLaufzeit);
    #HMI_Pumpe.Betriebsdaten.Betriebstunden := LINT_TO_REAL(#tempTimeToINT / 3600000000000);
END_IF;
```

Für die Sperrzeit so:


----------



## hucki (1 Dezember 2020)

Wozu brauchst Du denn bei den Betriebsstunden einen REAL?
DINT oder sogar INT sollten doch vollkommen ausreichen.
(REAL ist übrigens auch noch ungenauer als DINT, weil 8 Bits für die Kommaverschiebung weggehen.)


----------



## mista (15 Januar 2021)

Und Monatlich grüßt das Murmeltier...

Neue Aufgabe,

und wieder stehe ich da mit lauter ???? in meinem Kopf.

Bitte sagt mir, wie man an solchen Pumpensteuerung rann geht. Am besten so einfach wie möglich zu verstehen, wenn ein anderer über den Code ließt. Und einfach umzusetzen.

Folgendes Szenario, 2 Motoren, 3 Motoren oder 2x2 Motoren

in den ersten 2 Fällen: 

läuft bei einer Bedingung 1 Motor bis Abschaltbedingung kommt oder durch die Laufzeitkontrolle abschaltet.
Bei einer andere Bedingung startet zusätzlich ein weiter Motor, dann gibt es für beide keine Laufzeitkontrolle.
die 2. Pumpe läuft bis eine Abschaltbedingung erfüllt ist.

Beim 2x2 ist es so, dass es 2 Paare gibt. 

dabei schalte bei Anforderung, P1 ein nach einer Verzögerung P2,
beim Ausschalten erst P2 dann P1.
Beim nächsten Start soll P3 und P4 starten.
Auch hier soll es eine Laufzeitkontrolle geben.

Generell gilt: Bei Abschaltung durch Laufzeit, soll es eine Pause geben, wann erst der nächste Motor oder Motorpaar einschalten darf.

Und um das noch ein wenig zu verkomplizieren, soll es auch nach X Anlaufen für eine Y Zeit in den Linkslauf gehen. und das einstellbar nach einem Motorlauf oder davor wenn die Bedinung wieder erfüllt ist. 

Natürlich soll es auch Stör Umschaltungen geben etc.   

Ja ich habe mir das Motorpendel angeschaut auch das Beispiel von @hucki.

Ich habe ein Laufendes Programm allerdings werden Freigaben und Bedingungen so oft geschrieben, dass es unübersichtlich ist wo was passiert. 
Aktuell ist es so, das nach der Initialisierung der Zeiten die Zustände kommen, welche aber weiter unten erst definiert werden. Also für mich persönlich nicht leserlich und Wartungsfreundlich.

Ich möchte auch kein fertiges Programm nur einen Pseudo Code, für die Aufteilung wo ihr was machen würdet, bzw. wie ihr das aufteilen würdet. Würdet ihr drei Funktionen machen oder Alle drei Fälle in einer Funktion unterkriegen. Fall 1 und 2 kann durch dynamische Anzahl von Motoren Array leicht zusammen gefasst werden. Beim dritten Fall würde ich mit Modulo arbeiten weil es immer ein Paar sein muss.

Den Trick mit der Zykluszeit für die Betriebsstunden finde ich genial, allerdings benötige ich eine Fließkommazahl, Also kein DINT mehr sondern, Real oder LReal wenn man von LTIME konvertiert?

Wie würdet ihr euch ds aufschreiben, skizieren um es dann zu Programmieren? gibt es im Netz Tutorial wo man die Herangehensweise lernt? ich bin für alles offen auch für Privatstuden über SKYPE etc.
Gibt es da vielleicht Desingpattern die man immer wieder anwendet?


----------



## Larry Laffer (15 Januar 2021)

Hallo,
es hört sich für mich erstmal grundsätzlich nach einem Ablauf an - denk hier doch mal, gerade wegen Nachvollziehbarkeit, über eine Schrittkette nach.
Ich habe aber dein erstes Scenario nicht wirklich verstanden ...

Gruß
Larry


----------



## mista (15 Januar 2021)

Szeneario bei 2 bis 3 Motoren.

1. Bedingung erfüllt dann  // Startbedining für 1. Motor

Starte Motor 1

2. Bedingung erfüllt dann // Startbeding für den Parallelbetrieb

Starte den zweiten Motor

2. Abschaltbeding erfüllt dann
 Stoppe 2. Motor

1. Abschaltbeding erfüllt dann

stoppe ersten Motor

Und dann nach dem Prinzip 
1,2,1,2 bzw. 1+2 im Parallelbetrieb
oder 1,2,3, bzw. 1+2, 2+3, 3+1 im Parallelbetrieb

1. Bedingung erffült dann // Startbedining für 1. Motor

starte 2. Motor

usw.


----------



## Larry Laffer (15 Januar 2021)

Na ... da hast du doch dann schon deine Schrittkette ... 8)
Und so ähnlich würde es dann für das 2. Szenario ja auch aussehen ...

Weißt du das umzusetzen ...?

Gruß
Larry


----------



## mista (15 Januar 2021)

Meinst Du echt eine Schrittkette? 

Was wäre der Initialschritt? Wie würdest du das durchwechseln der Motoren gewährleisten?

Den Linkslauf und das Automatische Abschalten nach einer Zeit und dann auch noch die Pausenzeit.

Schrittketten habe ich eigentlich bei Fördertechnik gedacht nicht bei Verfahrenstechnik. Nur so klar ist es viel Strukturierter...

Ansonsten Ja hab mal was mit Schritten gemacht und würde es jetzt so machen:



```
Intialschritt = Keine Ahnung

Wenn Bedingung Erfüllt, dann                                                                              möglicher Paralleler Schrittketten Strang:                                      Wenn 2. Motor angefodert (und Schritt 1 ist aber schon Zurück gesetzt )

Schritt 1 = Starte Motor der an der Reihe ist.                                                                            Schritt 3 = Starte 2.Motor

Wenn Ausshaltbedinngung erreicht worden, und Schritt 1 aktiv ist dann:                                     Wenn Ausschaltbedinung erreicht worden und Schritt 3 dann 

Schritt 2 = Rücksetze Schritt 1, Starte Nachlaufzeit                                               2. Motor aus                                                 Schalte Motor 2 aus und Rücksetze Schritt 3
```

IRgendiwie So..

Jetzt habe ich durch google gesehen, dass Du hier mal eine Schrittkette mit Hilfe von INT und ein SWITCH CASE Anweisung empfohlen hast.


----------



## Larry Laffer (15 Januar 2021)

Na klar - Schrittkette ...!!!
Du musst dir nur klar darüber sein was wann passieren soll.
Ne Schritt kette ist nicht grundsätzlich nur für Fördertechnik - es ist eigentlich immer dann sinnig wenn es ein ABLAUF ist und der möglichst mehr als 2 Schritte hat. In deinem Fall, bei dem ich allerdings die Zuschalt- und Abschaltbedingungen für die Motore noch nicht so richtig verinnerlicht habe :

Initialschritt = hier warte die Schrittkette (auch nach dem Start der SPS oder der Betriebsart) auf deren Start bzw. Freigabe

Versuch einfach noch einmal deinen Ablauf zu beschreiben - ich bastel dir dann daraus die Schrittkette ...

Gruß
Larry


----------



## Larry Laffer (15 Januar 2021)

mista schrieb:


> Jetzt habe ich durch google gesehen, dass Du hier mal eine Schrittkette mit Hilfe von INT und ein SWITCH CASE Anweisung empfohlen hast.



Das wäre dann SCL - kommt aber auf das Gleiche heraus - egal ob INT- oder Merker-Schrittkette. Wenn dir SCL mehr liegt dann gerne auch so ...


----------



## mista (15 Januar 2021)

JA wir Arbeiten immer in SCL bei komplexeren Aufgaben

Also ich Versuche das mal auch für mich ein wenig runter zu schreiben:

Als erstes muss ermittelt werden Welcher Anlagen Typ verwendet wird, 2, 3 oder 2x2 Motoren.
Dann muss sortiert werden bzw. je nach welcher Motor oder 2 Motoren als letztes drann war/en den Nächsten/ die Anderen auswählen.


```
Initialschitt

Dann wenn eine Bedingung erreicht 
Schritt 1: Starte den einen Motor oder bei Analgentyp 2x2 den ersten Motor des Paares

Wenn eine Verzögerung abgelaufen ist
Schritt 2: Schalte den zweiten Motor des Paares ( NUR Bei Anlagentyp 2x2)

   Wenn zwischenzeitlich eine Andere Bedingung
   Parallelschritt 1: Starte weiteren Motor (NUR bei Analgentyp mit 2 oder 3 Motoren

   Wenn Abschaltbedining zusatzmotor dann 
   Parallelschritt 2: Schalte zusätlichen Motor ab

Wenn die Abschaltbediingung erreicht ist oder die MAX Laufzeit ist abgelaufen
Schritt 3: Schalte die Nachlaufzeit oder bei anlagentyp 2x2 Stoppe den 2. Motor des Paares

Wenn die Nachlaufzeit beendet ist, dann 
Schritt 4 Stoppe den Ersten Motor oder bei Anlagentyp 2x2 den zweiten Motor des Paares
```

Sollte die MAX Laufzeit ablaufen oder eine Störung ders Laufendes Motors (oder eines Paares), muss zwingend die Bedingung für Schritt 1erfüllt werden, damit der andere Motor, Paar einspringt.

Zusätzlich soll nach gewissen Startzyklen einstellbar nach dem Ausschalten der Motoren oder vor dem einschalten der Motoren ein Linkslauf erfolgen für z.b. 5 Sek. erfolgen


----------



## Larry Laffer (15 Januar 2021)

So wie ich das sehe benötigst du für jeden Motor eine eigene Schrittkette. Damit ist dann der Parallelbetrieb machbar und zwar auch so, dass sie unabhängig voneinander sind.
Welcher Motor anläuft kannst du darüber machen, dass du dir merkst welches der letzte Antrieb war - wenn Last_Motor = 1 dann muss als nächstes dann 2 anlaufen. Wenn Last_Motor = 2 und 3 Motoren vorhanden dann wäre der nächste Motor 3 sonst eben 1.
Mal unabhängig, dass ich ein großer SCL-Fan bin kann man eigentlich hier schon lesen, dass du damit auch bei SCL wahrscheinlich am Besten aufgehoben bist.
Ich denke aber mal, dass wir noch ein paar Mal hin- und herschreiben bis das finale Konzept so richtig steht ...

Gruß
Larry


----------



## mista (15 Januar 2021)

Larry Laffer schrieb:


> So wie ich das sehe benötigst du für jeden Motor eine eigene Schrittkette. Damit ist dann der Parallelbetrieb machbar und zwar auch so, dass sie unabhängig voneinander sind.
> ...
> Gruß
> Larry



Das Verstehe ich nicht, ich könnt mir nur Vorstellen 2 Schrittkette für einmal bis zu 3 Motoren und eine für den Paarbetrieb.

Kann man für den Parallelbetrieb nicht eine Art Leerschritt machen und die Aktion nur ausführen wenn Parallelbetrieb aktiv in der einen Schritt kette genauso ein Schritt für den Linkslauf vor und nach dem Vorgang und je nachdem was aktiviert ist, dann auch den Linkslauf einschalten?

Vor allem möchte ich auch alles möglich nur einmal schreiben müssen und verwenden wie Methoden aufrufen...


----------



## mista (15 Januar 2021)

Dann wollen wir mal im Psuedo Code starten

Haupt Schrittkette:


```
// Schritt 1
IF Einschaltpunkt THEN
   Schritt_1 := TRUE;
   Motor : = TRUE;
END_IF;

// Shritt 2 leerschritt
IF Schritt_1 AND NOT Parallelbetrieb_aktiviert THEN
   Schritt_1 := FALSE;
   Schritt_2 := TRUE;
END_IF;

//Alternativweg wenn Parallebetrieb aktiviert
IF (Schritt_1 AND Parallelbetrieb_aktiviert AND Einschaltbedinung_Parallel ) OR SPRUNG THEN
   Schritt_1 := FALSE;
   Schritt_2a := TRUE;
   Zusätlicher_Motor := NOT SPRUNG;
END_IF;

// Parallelbetrieb ausschalten
IF (Schritt 2a AND Ausschaltbedinung) OR SPRUNG THEN
   Schritt_2a := FALSE;
   Schritt_3 := TRUE;
   Zusätzlicher_Motor := FALSE;
   IF Linkslauf_Zusätzliher_Motor AND NOT SPRUNG THEN
      Zusätzlicher_Motor_Links := Schritt_6;
      TOF_Linkslauf_Zusatz(IN:= Zusätzlicher_Motor_links, PT: t#5s);
   END_IF;
END_IF;

// Jetzt kommt meine Frage, Parallelbetrieb ist Möglich muss aber nicht vorkommen, 
// wie kann man jetzt von Schritt 2a und oder 3 zu Schritt 4 Springen wenn eine Bedinnung erfolgt?

// Schritt 4
IF Schritt_2 OR Schritt_2a OR SPRUNG THEN
   Schritt_3 := FALSE;
   Schritt_2 := FALSE;
   Schritt_4 := TRUE;
   Starte_Nahchlauf();
END_IF;

// Schritt 5
IF Schritt_4 AND Nachlauf_abgelaufen THEN
   Schritt_4 := FALSE;   
   Schritt_5 := TRUE;   
   Motor  := FALSE;
END_IF;

// Schritt 6 leeschritt
IF Schritt_5 AND NOT Linkslaufbedining THEN
   Schritt_5 := FALSE;
   Schritt_6: = TRUE;
END_IF;

// Schriit_6a Linkslauf
IF Schritt 5 AND Linkslaufbedining THEN
   Schritt_5 := FALSE;
   Schritt_6 := TRUE;
   Motor_Links := Schritt_6;
   TON_Linkslauf(IN:= Motor_links, PT: t#5s);
END_IF;

IF Schritt_6 OR (Schritt_6a AND TON_Linkslauf.Q) THEN
   Schritt_6 := FALSE;
   Schritt_6a := FALSE;    
   INITIALSCHITT := TRUE;
END_IF;
```

Das Wäre jetzt erstmal für 2 bis 3 Motoren der Pseudo code. Wie ich jetzt einen Linkslauf() vor Schritt 1 oder nach Schritt 5 Umsetze weiß ich nicht genau, ich habe es mal probiert nach dem Motorlauf jeweils, des 1. und 2. angefoerderten Motors.

EDIT: Sprung selber versucht umzusetzen...


----------



## Larry Laffer (16 Januar 2021)

Ich dachte, du wolltest eine SELECT-Schrittkette machen ... nun ist ja doch eine Merker-Schrittkette ... 8) 
Egal !
Ich gehe eigentlich so vor, dass ich mir die Schrittkette immer erst graphisch erstelle. So runterschreiben klappt bei mir meißt nur bei relativ einfachen Abläufen.

Was soll "Sprung" darstellen ?
Beschreib mir doch mal genau, was so passieren soll in welcher Reihenfolge ...

Gruß
Larry


----------



## mista (16 Januar 2021)

Ja von mir aus auch eine Select Schrittkette, nur habe ich sowas nicht nur gemacht.

Sprung soll eine Bedingung sein, wenn der Parallelbetrieb zwar möglich ist aber nicht immer aktiviert werden muss weil die Ausschalt Bedingung des 1. Motors greift. Welche die Transition für das beginnen der Nachlaufzeit ist. 

Soll heißen dass der Parallelbetrieb nur benötigt wird, wenn der 1. Motor nicht reicht.

Prinzipiell Frage ich mich auch wie ich den Linkslauf beliebig vor dem Vorgang oder nach dem Vorgang mit einer Methode einfach Aufrufe.

So langsam bin ich immer mehr verwirrt 😕

Danke für deine Unterstützung


----------



## Larry Laffer (16 Januar 2021)

Ich beschreibe mal was ich verstanden habe :
- du hast 2 (oder 3) Motoren, die erstmal im kontinuierlichen Wechsel laufen sollen (damit ihre Auslastung insgesamt ungefähr gleich ist)
- wenn ein bestimmter Pegel erreicht wird dann schaltet sich die Pumpe, die als nächstes dran wäre ein
  - diese Pumpe schaltet sich ab, wenn der Pegel wieder unterschritten wurde
  - wenn eine maximale (vorgegebene) Laufzeit überschritten wurde - dann jedoch soll die nächste Pumpe weitermachen
  - außer es ist 2-Pumpen-Modus angewählt - dann machen beide weiter bis Pegel wieder unterschritten
  - wenn die nächste Pumpe übernimmt dann verfährt die genauso wie die  vorhergehende ? ... oder macht die dann bis Pegel wieder OK ?  (Ich  fände es witziger wenn die sich so verhalten würde wie die vorherige)

was ich hier nicht verstanden habe ist die Geschichte mit der Rückwärtsfahrt (Linkslauf) - wofür ist das gedacht ? ... und wann soll das passieren ?  (wahrscheinlich wenn überhaupt dann wenn keine Pumpe mehr aktiv ist ?)

Das 2x2-Szenario wäre einfacher (aber ein eigenständiger Zweig der Schrittkette).
S0 = warten
- wenn Pegel erreicht und nicht Ablauf 1-2 dann gehe nach S1
- wenn Pegel erreicht und Ablauf 1-2 dann gehe nach S5

S1 = Pumpe 1 Ein // Start Timer // setze Ablauf 1-2
- wenn Timer abgelaufen dann
S2 = Pumpe 2 Ein
- wenn Pegel unterschritten dann
S3 = Pumpe 1 Aus // Start Timer
- wenn Timer abgelaufen dann
S4 = Pumpe 2 Aus // Start Timer
- wenn Timer abgelaufen dann gehe nach S0

S5 = Pumpe 3 Ein // Start Timer // rücksetze Ablauf 1-2
- wenn Timer abgelaufen dann
S6 = Pumpe 4 Ein
- wenn Pegel unterschritten dann
S7 = Pumpe 3 Aus // Start Timer
- wenn Timer abgelaufen dann
S8 = Pumpe 4 Aus // Start Timer
- wenn Timer abgelaufen dann gehe nach S0

Aber :  welche Funktion hat bzw. wo greift hier die Laufzeitkontrolle ? Was ist hier mit dem Linkslauf ?

Jetzt bist du wieder dran ... 8)

Gruß
Larry


----------



## mista (16 Januar 2021)

Erstmal vielen vielen Dank .

Ich versuche es Mal zu beschreiben wobei ich da nicht der beste bin, jedoch Übung macht den Meister.

Also, je nach der Hydraulik ist ein Parallellauf möglich oder nicht. Und das auch nur wenn 2 oder 3 Pumpen gibt.

Machen wir es simple für den Anfang und gehen davon aus , dass Parallelbetrieb möglich ist und es nur 2 pumpen in der Anlage gibt. Dann gibt es 3 Pegel:

Einschalt, Ausschalt und parallel.

Normalerweise ist es so, dass Parallelbetrieb sehr selten vorkommt.

Also wird abwechselnd eingeschaltet und wieder ausgeschaltet. Beim Ausschalten greift eine Nachlaufzeit.

Erreicht die Pumpe ihre max Laufzeit wird diese abgeschaltet und eine Pausenzeit zählt runter. 

Damit die nächste Pumpe starten kann muss zwingend der einschaltpegel erreicht werden und die Pausenzeit ablaufen.

Steigt beim normalen Vorgang der Pegel bis zum Parallelpunkt, dann startet die zweite Pumpe hinzu. Bis der Pegel des Einschaltpunjtes unterschritten ist. Es kann aber sein, dass der Pegel für den Parallelbetrieb nie eintrifft also müssen die Schritte eben übersprungen werden weil die eine Pumpe den Pegel zum ausschaltpunkt gesenkt hat. Die Bedingung für den Sprung wäre hier, dass der ausschaltpunkt erreicht ist. Also in diesem Vorgang kein Parallelbetrieb nötig war

Der Linkslauf ist auch optional und soll entweder nach oder vor einem Durchgang passieren, je nachdem nach eingestellten Zahl von Startzyklen.

So die Punkte des pegels bekomme ich aus einem Füllstandbaustein. Welches ich als Interface über inout einlese.

Die Zustände der pumpen auch über Inout Array of Pumps udt.

Jetzt will ich es so programmieren, dass es 1. Einfach zu warten ist, 
2. Einfach erweitern.
3. Einfach zu lesen ist.

Am besten modular aufrufen zum Beispiel , Linkslauf()
Das Abfragen welche Pumpe dann ist bzw. Hinzuschaltet.
Etc.

Eine andere Frage ist, gibt es eine schnelle Möglichkeit nur diesen Teil vom Programm zu testen ohne es in den Programm selber zu testen sondern, in eine Art von Testprogramm. 

Und wenn ja wie geht ihr da vor? Teste ihr mit den forcieren der Werte oder baut ihr Simulations Bausteine? Oder gibt es eine andere smarte Simulationsumgebung in tia?


----------



## Larry Laffer (16 Januar 2021)

Nach dieser Beschreibung würde ich jetzt auf jeden Fall jeder Pumpe einen eigenen Ablauf spendieren ...
Die Wahl, welche Pumpe sich zunächst zuständig fühlt kannst du über eine Variable machen, die speichert (z.B.) welche Pumpe zuletzt aktiv war.
Im Falle, dass du jeder Pumpe einen eigenen Ablauf spendierst kannst du das so machen, dass dann auch jede Pumpe einen eigenen Baustein hat - eine neue Instanz desselben FB's z.B. So ist es dann vollkommen egal, wieviele Pumpen du an den Start bringen willst. Du instanzierst halt einfach noch eine weitere dazu ...

Wie gut es zu warten, erweitern und lesen ist hängt schwer davon ab, wieviel Mühe du dir mit der Doku gibst - aber auch wie klar die Abläufe dargestellt sind.

Das Testen (auch im "Trockenen") sollte bei den wenigen Signalen, die du hier hast, nicht so das Problem sein - das solltest du schon mit PLCSim hinbekommen ... Es geht aber auch mit ein paar Schaltern und Lämpchen - ganz nach Geschmack ... oder du baust dir ein Äquivalent dazu auf deiner Visu auf - ist vielleicht auch ganz nett ...

Gruß
Larry


----------



## mista (16 Januar 2021)

Also was ich nicht versteh wie du das meinst, dass jeder Pumpe einen eigenen Ablauf hat.

Wie gesagt, je nachdem wie viele pumpen es gibt, erstelle ich ein Instanzen für eine Pumpe . Dieser Baustein kümmert sich jedoch nicht für die Steuerung sondern bekommt Eingänge für den Automatik Anforderung, Hand, Störungen etc. Und setzt diese um. 

Die Pumpe kann entweder geregelt sein oder ein Direktantrieb mit einfacher Freigabe.

Und sollte dann auch für andere Zwecke verwendet werden können.

Daher binde habe ich vor im DB der Pumpensteuerung das udt als inout an und steuerue die jeweilige Pumpe ob sie rechts, links oder in Pause ist. Etc 

Innherlb der Pumpensteuerung FB möchte aber den oben beschrieben Vorgang steuern können.


----------



## Larry Laffer (16 Januar 2021)

mista schrieb:


> Wie gesagt, je nachdem wie viele pumpen es gibt, erstelle ich ein Instanzen für eine Pumpe . Dieser Baustein kümmert sich jedoch nicht für die Steuerung sondern bekommt Eingänge für den Automatik Anforderung, Hand, Störungen etc. Und setzt diese um.
> 
> Die Pumpe kann entweder geregelt sein oder ein Direktantrieb mit einfacher Freigabe.



Das ist vollkommen OK so und macht für mich auch Sinn ...



mista schrieb:


> Innherlb der Pumpensteuerung FB möchte aber den oben beschrieben Vorgang steuern können.



Was spricht nun aber dagegen, die Abläufe auch einzeln zu betrachten ?
Es läuft ja erstmal (so wie du es beschrieben hast) jede Pumpe für sich. 
Arbeitet nun z.B. Pumpe 1 UND es wird der 3. Pegel erreicht dann sollte Pumpe 2 mit ins Rennen kommen - dies wäre dann also eine mögliche Startbedingung für den Ablauf von Pumpe 2, der ja, so wie aussieht, ansonsten identisch mit dem vom Pumpe 1 ist.
Die Weiterschaltbedingung vom Initialschritt des Ablaufs von Pumpe 2 könnte also z.B. sein :

```
U -Pumpe_2_ist_dran_mit_arbeiten
U -Einschaltpegel_erreicht
O
U -Pumpe_1_arbeitet_schon
U -Zuschaltpegel_erreicht
```
Genauso würde es bei jeder anderen Pumpe ja aussehen. Wäre nun dein Ablauf ein FB dann könnten diese einzelnen Bits IN-Parameter von dem Baustein sein ...

Kannst du mir folgen ?

Gruß
Larry


----------



## mista (16 Januar 2021)

Also meinst du mit eigenen Strang ein Pumpenvorgang (Start, Ende) einer Pumpe. Ok das verstehe ich!.

Was ich nicht verstehe ist, wie du in den vorherigen Posts geschrieben hast von Schritt 0 Entwerder zu S1 oder S2 spingst, meinst Du da einen alternativen Pfad?

Mir ist aufgefallen, dass ich keinen alternativen Pfad/Strang sondern einen Parallelen brauche. 
Denn wenn die Einschaltbedingung für 1 Pumpe kommt kann ja der Strang für die zusätzliche Pumpe auch aktiv werden mit einem Leerschritt. 
Dann warte ich einfach auf die Bedingung für die zweite Pumpe im paralleln Strang und wenn die nicht kommt dann springe ich raus.

Nur dieses Springen im CASE wüsste ich nicht wie ich es machen sollte. Ich würde mir einen Abrrechen, mit verscheiden Merkern in dem DB.

Ich schreib das jetzt mal runter:

Transitionen:
Wenn S0 und Pegel 2 
dann S1 und -S1a (falls Parallelbetrieb eingestellt)

Wenn (S1  oder S2a )und Pegel 1 
dann S2

Wenn (S2 und 5s abgelaufen) oder (MAX Laufzeit und nicht Parallelaktiv)
dann S3

Wenn Anforderung Linkslauf nach Vorgang
dann S4

Wenn kein Linkslauf
dann S0

Wenn S4 und 5s abgelaufen
dann S0

Wenn S1a und Pegel 3
dann S2a

Aktionen:
S0 = Initialschritt
S1 = Starte erste Pumpe; starte Laufzeit,
S2 = Starte Nachlaufzeit, rucksetze Laufzeit
S3 = Stoppe erste (und wenn Parallelbetrieb die zweite Pumpe), rücksetze parallelaktiv
S4 = Linkslauf

S1a = Starte Leeschritt parallel
S2a = Starte zweite Pumpe, setze Parallelaktiv

so in der Art.

Sollte linkslauf angefordert werden vor S1 oder S2a dann muss nach dem Linkslauf eine Pausenzeit aktiv werden.
eine Pausenzeit muss auch aktiv werden wenn eine Pumpe ihre MAX Laufzeit erreicht hat.

Wie Ihr seht, habe ich schon einen Plan weiß aber nicht wie man OPTIONALE Schritte oder Schrittketten (Linkslauf und Parallelbetrieb) in einem GRAFCET darstellt und in SCL Programmiert ohne sich ein abzubrechen mit 1000 von Merkern.

Eine Idee die ich noch gerade in dem Sinn gekommen ist, zumindest bei Linkslauf den die Kette nicht als option aber einfach den Laufzeit für den Linkslauf t#0s zu setzen wenn der Linkslauf nicht angefordert wird. 

Gruß 
Mike


----------



## Larry Laffer (16 Januar 2021)

So langsam kommen wir uns näher 

Du brauchst keinen parallelen Pfad - es reicht ein alternativer Pfad. In dem einen Fall startest du nur die eine Pumpe (kein Parallelbetrieb) im anderen Fall gehst du in den Alternativschritt und startest dort beide Pumpen.

Wenn du irgendwann einmal auf eine SELECT-Schrittkette gehen willst dann solltest du das mit dem S1a lassen - ein "a" kannst du mit einer INT-Variablen nicht wirklich darstellen - das muss dann auch wieder eine Zahl werden. Warum nicht S1 und als Alternative S2 (statt S1a) ?

Zur Schrittkette (das ist jetzt aber quick-and-dirty) :

```
SELECT CASE Schritt_aktiv
   0:
      IF Pegel_2 AND NOT Parallelbetrieb then Schritt_aktiv := 1 ; END_IF ;
      IF Pegel_2 AND Parallelbetrieb then Schritt_aktiv := 2 ; END_IF ;
   1:
      IF Pegel_1 AND NOT Pegel_2 then Schritt_aktiv := 3 ; END_IF ;
   2:
      IF Pegel_1 AND NOT Pegel_2 then Schritt_aktiv := 3 ; END_IF ;


// usw.
END_SELECT;
```
Die Aktionen selbst würde ich nicht innerhalb der Schrittkette umsetzen sondern dahinter ... also ungefähr so :

```
Start_Pumpe_1 := (Schritt_aktiv = 1) ;
Start_Pumpe_2 := (Schritt_aktiv = 1) or (Schritt_aktiv = 2) ;
```

Hilft dir das weiter ?


----------



## mista (16 Januar 2021)

Achso, also nicht mit Setzen der Pumpenfreigaben sondern nur Freigeben solange ein Schritt aktiv ist? 

Das Problem ist, dass der Parallelbetrieb nach dem  Einschalten der ersten Pumpe erfolgen kann (nicht muss) und nicht beide Pumpen gleich Starten. 
Sondern erst eine, dann wenn die nicht ausreicht die zweite.

Wie werden denn optionale Schritte realisiert?


----------



## Larry Laffer (17 Januar 2021)

mista schrieb:


> Wie werden denn optionale Schritte realisiert?


Ich würde an jedem Schritt, an dem das möglicherweise geschehen kann, eine Parallel-Verzweigung machen - immer in Abhängigkeit mit dem Parallelbetrieb ...
Du solltest dieses Konstrukt dann aber AUF JEDEN FALL auch graphisch dokumentieren (es muss jetzt nicht GRAPH sein), weil es dir ja wichtig war, dass man auch später da noch durchsteigen kann.
(Ich habe meine Schrittketten immer mit einem ECad-Programm gezeichnet)

Gruß
Larry


----------



## mista (17 Januar 2021)

So erstmal vielen vielen Dank.. 

Ich werde die switch Case Anweisung nehmen.  Alles in einer..

Benötige ich am Anfang einen links Lauf springe ich von Schritt 0 zu 1

Wenn nicht dann da wo der normale Vorgang begibt z.b. Schritt 4

Ist Schritt 4 aktiv abpumpvorgang und eventuellen Parallelbetrieb aktivieren.

Dann gehe ich weiter bis zum Schluss  und wenn da ein Linkslauf gefordert wird durchlaufe ich die Kette weiter sonst springe ich zu Schritt 0.

So ist alles zentralisiert und modular aufgebaut. Weil ich für zum Beispiel den Linkslauf in einer eigenen Funktion aufrufen kann und per inout sogar die Pumpe, die linksfahren soll übergeben.

So langsam wird ein Schuh raus!


----------



## Heinileini (17 Januar 2021)

mista schrieb:


> Ist das Wasservorkommen zu hoch und Niveau 3 wird erreicht lauft eine weitere Pumpe des jeweiligen anderen Pumpenpaares an, welche die weinigsten Betriebsstunden aufweist.
> Eine Störumschaltung der Pumpenpaare soll natürlich auch implementiert werden.


Wie stark soll die "PaarBindung" denn sein und was bezwecken? Ist sie überhaupt sinnvoll/nötig?

Wenn Niveau 3 erreicht wird, wird sie ja ohnehin gelockert: "... läuft 1 weitere Pumpe des anderen PumpenPaares ...", also nicht das komplette andere Paar.

Laut Beitrag #6 sind aus 3 Niveaus plötzlich 10 geworden, 5 für NormalBetrieb und 5 für ReinigungsBetrieb.
Hast Du 10 SchwimmerSchalter oder 1 analoge PegelMessung (ggfs kombiniert mit SchwimmerSchaltern)?

StörUmschaltung: Wodurch? Meldekontakte von Motorschutzschaltern? StörMeldungen von Umrichtern? 
Evtl. noch manuelle Vorgaben angedacht, um einen Motor stilllegen zu können?

RichtungsUmkehr: WendeSchützSchaltung? Umrichter? Digitale oder analoge Schnittstelle zum Antrieb?



mista schrieb:


> Die Pumpe kann entweder geregelt sein oder ein Direktantrieb mit einfacher Freigabe.
> 
> Und sollte dann auch für andere Zwecke verwendet werden können.


Für andere Zwecke??? Z.B.? Pumpe als "Turbine" und PumpenMotor als Generator?

Welche Strategie soll verfolgt werden, wenn der Einsatz von Pumpen gefordert wird, die (vorübergehend) nicht einsatzfähig sind?
So richtig nennenswerte Reserve für "NotFälle" scheint bisher nicht eingeplant zu sein. In der Variante "paarweiser Betrieb von Pumpen" sind die AusweichMögklichkeiten zudem für meinen Geschmack zu sehr und unnötig eingeschränkt.

Wozu dienen die "ZwangsPausen" zwischen den BetriebsZeiten verschiedener Pumpen? 
In welcher GrössenOrdnung liegen diese PausenZeiten?
Wie kritisch ist das Einhalten der vorgegebenen Pegel? Werden die Pausen zu einem unerwünschten/vermeidbaren Überschwingen führen?

Bevor Du Dir Gedanken über Details der eierlegenden WollMilchPumpenSchrittketten machst, solltest Du einmal abklopfen, über welche Schnittstellen das Werk mit der "AussenWelt" verbunden werden soll.
Werden die Pegel (Niveaus) über digitale und/oder analoge Werte gemeldet?
Welche Informationen/RückMeldungen stehen zur Verfügung, z.B. bezüglich der EinsatzFähigkeit der einzelnen Pumpen?
Werden die PumpenMotoren über digitale und/oder analoge Werte angesteuert (Links- und RechtsLauf berücksichtigen)?

Und abklopfen, welche TeilAufgaben die Software bewältigen soll und wie die Schnittstellen zwischen diesen TeilAufgaben aussehen sollten.
Ich sehe folgende TeilAufgaben:
- Bearbeitung der ZeitWerte (BetriebsStunden, aktuelle LaufZeiten, Zeit seit dem letzten Abschalten einer Pumpe, ... , falls keine Timer verwendet werden, hier auch den TimerErsatz verwalten).
- Auswertung der NiveauMeldung[en]. Ermittlung, wieviele Pumpen laufen müssen (für PumpenPaare evtl. die minimale Anzahl Pumpen von 1 auf 2 umbiegen).
- Vergleich des Soll- und Istwertes der Anzahl laufenden Pumpen. Muss eine Pumpe hinzugeschaltet oder abgeschaltet werden? 
Der IstWert muss "manipuliert" werden: ist für eine Pumpe ein Einsatz vorgegeben, pausiert sie aber gerade, so muss sie trotzdem als laufend erfasst werden.
- Entscheidung, welche Pumpe hinzugeschaltet/abgeschaltet wird.
- Ansteuerung einer Pumpe. Muss/darf eine LinksLaufPeriode eingefügt werden? Ist eine Einschalt-/AusschaltVerzögerung erforderlich (z.B. für PumpenPaar)?

Eine Anwendung von SchrittKetten sehe ich allenfalls bei entweder der letzten oder vorletzten TeilAufgabe, jenachdem, wo LinksLauf bzw. PausenZeiten realisiert werden. 
Auf jeden Fall würde ich es vermeiden, auch solche TeilAufgaben mit Gewalt in die SchrittKetten hineinzuzerren, die dort nichts zu suchen haben und unnötig die Komplexität aufblähen würden.  

Eine "StörUmschaltung" als solche separat zu realisieren, halte ich für überflüssig. Diese wird "automatisch" wirksam, wenn durch Ausfall einer BetriebsBereitschaft der Vergleich zwischen Ist- und SollAnzahl der fördernden Pumpen plötzlich verändert wird.
Allenfalls für den Betrieb von PumpenPaaren wird es etwas komplex - darum nochmal die Frage: muss das wirklich sein? Das bringt nur mehr Aufwand für weniger (Ausweich-)Möglichkeiten.


----------



## mista (17 Januar 2021)

Verdammt!!!

Ab Beitrag 15 ist es ein anderes Projekt!!!

Vielleicht sollte da ein Moderator zwei Threads raus machen?


----------



## mista (17 Januar 2021)

So sieht Sie jetzt aus die Schrittkette



```
REGION Schrittkette
    CASE #statAktuellerSchritt OF
        0:  // Schritt 0 = Grundstellung
            IF #Einstellungen.Anlagentyp < 2 THEN                          // Anlagentyp 2 bis 3 Pumpen
                #statAktuellerSchritt := 1;
            ELSE                                            // Anlagentyp 2x2 Pumpen
                #statAktuellerSchritt := 200;
                ;
            END_IF;
            
        1:  // Linkslauf falls Anforderung
            IF Einschaltunkt erreicht THEN
                IF Linkslauf THEN
                    // Aktiviere Linkslauf
                    #statAktuellerSchritt := 2;
                ELSE
                    // Springe zum normalen Vorgang
                    #statAktuellerSchritt := 4;
                END_IF;
            END_IF;
        2: // Starte Linkslauf
            IF Timer Beendet THEN
                #statAktuellerSchritt := 3;
            END_IF;
        3: // Pause Linkslauf
            IF Pause Beendet AND einschaltpunkt erreicht
            THEN
                #statAktuellerSchritt := 4;
            END_IF;
        4: //  Pumpvorgang      
            IF MAX Abgelaufen THEN
                #statAktuellerSchritt := 5;
            ELSIF Ausschaltbedingung THEN
                #statAktuellerSchritt := 6;
            ELSIF Parallelbedingung THEN
                #statAktuellerSchritt := 70;
            END_IF;
            
        70: // parallel
            IF Abschaltbedingung THEN
                #statAktuellerSchritt := 6;
            END_IF;
        5: // PAUSE Nach Abschaltung
            IF Pause Beendet THEN
                #statAktuellerSchritt := 0;
            END_IF;
            
        6: // Nachlaufzeit
            IF Nachlaufzeit Beendet THEN
                IF Reversion THEN
                    #statAktuellerSchritt := 7;
                ELSE
                    #statAktuellerSchritt := 0;
                END_IF;
            END_IF;
        7: // Pausenzeit VOR Revision
            IF Pause vor Revision durch THEN
                #statAktuellerSchritt := 8;
            END_IF;
        8:
            //Revision
            IF Revision Zeit durch THEN
                #statAktuellerSchritt := 0;
            END_IF
            ;
        ELSE  // Statement section ELSE
            #statAktuellerSchritt := 0;
    END_CASE;
END_REGION
```


----------



## Larry Laffer (17 Januar 2021)

mista schrieb:


> Verdammt!!!
> 
> Ab Beitrag 15 ist es ein anderes Projekt!!!
> 
> Vielleicht sollte da ein Moderator zwei Threads raus machen?


Das ist jetzt eigentlich auch egal ... im Grunde passt es ja noch "ein bisschen" zum Ursprungsthema und vor Allem aber zur Überschrift ... 8)


----------



## Larry Laffer (17 Januar 2021)

Zu deiner Schrittkette :
Ob es so gut oder nicht gut ist kann ich jetzt so nicht beurteilen - das wird dann aber sicherlich dein Test ergeben.
Was du dir ggf. noch überlegen solltest ist, die Schrittnummern nicht im 1er-Schritt zu vergeben - lieber so, wie für bei den "guten alten" Basic-Programmen - also 10er oder ggf. 5er Schritte. Der Vorteil ist, dass du einfacher einen eventuell mal notwendigen Zwischenschritt an der richtigen Position einfügen kannst (da hast du dann ggf. mal einen 1er Schritt).
Mit den Timern wirst du sicherlich mit IEC-Timern arbeiten (?) - wäre jedenfalls sinnig. Denk dran, dass deinen Bools, die du an den Q des Timers hängst auch statische Variablen sein müssen ...

Gruß
Larry


----------



## mista (17 Januar 2021)

Durch das tolle Forum und Google bin ich auf ein Thread gestoßen wo die Schrittnummern als Konstanten hinterlegt ist, 
so kann man immer recht Simple ändern. In der Tat habe ich den Parallellauf Nachträglich eingeführt daher die Schrittnummer 70.

Genau die Frage habe ich mit gestellt, soll ich in den Timern TON oder IEC als statische Variable instanziieren. 
Also IEC Timer vielen Dank.

Ich verstehe nicht was du mit Bools die an den Ausgängen des Timers angehengt werden, meinst.


----------



## Larry Laffer (17 Januar 2021)

mista schrieb:


> Ich verstehe nicht was du mit Bools die an den Ausgängen des Timers angehengt werden, meinst.


Naja ... du weißt ja sicherlich, dass die IEC-Timer ganz generell nicht bedingt aufgerufen werden wollen (also nicht in den SELECT einbauen oder eine IF-Abfrage. Nun musst (oder willst) du die Meldung, dass der Timer abgelaufen ist ja irgendwie an die Schrittkette übergeben ... das wäre dann z.B. dein "Revision_Zeit" - das sollte dann ein STAT am Timer.Q sein ...

Das mit den Schrittnummern als Konstanten (vor Allem wenn die dann nicht "Schritt_xyz" oder so heissen) ist natürlich auch sinnig. Leider ist Siemens hier noch nicht so weit wie z.B. Beckhoff - da geht das noch ein bisschen schöner ...


----------



## mista (18 Januar 2021)

Alles klar vielen Dank, jetzt fehlt mir nur noch das setzen der richtigen Pumpen, Also welche Pumpe jetzt dran ist. 
Irgendwie sehe ich nicht den Wald vor lauter Bäumen nicht mehr...


----------



## Larry Laffer (18 Januar 2021)

Dann musst du mir jetzt mal helfen - wo genau sitzt du fest ?


----------



## Heinileini (18 Januar 2021)

mista schrieb:


> Irgendwie sehe ich nicht den Wald vor lauter Bäumen nicht mehr...


Dann beschreib uns doch mal Deine Bäume so, dass wir einen klareren Blick darauf erhalten können.
"Welche Pumpe jetzt dran ist" hat ja zwei Bedeutungen:
- welche Pumpe ist dran mit eingeschaltet werden 
und, wenn mehrere Pumpen laufen,
- welche Pumpe ist dran mit abgeschaltet werden.

Welche Kriterien hast Du (in Deiner "PumpenTabelle" also in Deinem array of struct) dafür, welche Pumpe ...
- nicht zur Verfügung steht
- bevorzugt eingeschaltet werden soll
- bevorzugt abgeschaltet werden soll
- ggfs mit welcher anderen Pumpe "verheiratet" ist
?

Allgemeiner Tipp:
Tabelle entsprechend den Prioritäten und AusschlussKriterien sortieren. Weil nach unterschiedlichen Kriterien unterschiedlich sortiert werden soll, belässt man die Tabelle unsortiert und fügt Spalten hinzu, die Indizes auf die Zeilen der "festen" TabellenSpalten enthalten. Nur diese IndexSpalten werden dann sortiert.


----------



## mista (18 Januar 2021)

ICh hab das jetzt mal so gemacht:

Für Kritik und Verbesserung bin ich offen.


```
REGION Pumpen Schalten
    
    REGION Nächster Pumpensatz
        // Nächsten Pumpensatz bestimmen
        #tempNächsterSatz := #statAktiverSatz;
        FOR #statLaufvariable := 1 TO #tempAnzahlPumpen DO
            
            // inkrementieren
            #tempNächsterSatz += 1;
            
            // Limitieren
            IF #tempNächsterSatz > #tempAnzahlPumpen THEN
                #tempNächsterSatz := 1;
            END_IF;
            
            IF #pumpen[#tempNächsterSatz].status.bereit THEN
                // wenn Pumpe bereit, dann als nächste bzw. für den Parallelbetrieb vormerken und Schleife verlassen 
                #statNächsterSatz := #tempNächsterSatz;
                EXIT;
            END_IF;
        END_FOR;
    END_REGION 
    
    REGION Satzwechsel 
        // Positive Flanke generieren, nach der Nachlaufzeit oder dem Reversieren für den Satzwelsel
        #statFTriggVorgang(CLK := (#statSchritkette.Schritte.nachlaufzeit AND NOT #tempAnforderungReversierung)
                           OR (#statSchritkette.Schritte.reversionNachAbpumpen AND #tempAnforderungReversierung));
        
        // Negative Flanke nach der Abschaltung durch Laufzeit oder Fehler
        #statRTriggAbschaltung(CLK := #statSchritkette.Schritte.pauseNachAbschaltung);
        
        // Anforderung nächster Satz setzen
        #statAnforderungNächsterSatz := #statRTriggAbschaltung.Q OR #statFTriggVorgang.Q;
        
        // Satzwechsel
        IF #statAnforderungNächsterSatz THEN
            #statAktiverSatz := #statNächsterSatz;
        END_IF;
    END_REGION ;
    
    REGION Pumpenfreigaben
        // erste Pumpe einschalten
        #pumpen[#statAktiverSatz].autoEin := #statSchritkette.Schritte.abpumpen
        OR #statSchritkette.Schritte.parallelbetrieb
        OR #statSchritkette.Schritte.nachlaufzeit;
        
        IF #Einstellungen.Parallelbetrieb THEN
            // zusätzliche Pumpe einschalten
            #statParallSatz := #statNächsterSatz;
            #pumpen[#statParallSatz].autoEin := #statSchritkette.Schritte.parallelbetrieb OR #statSchritkette.Schritte.nachlaufzeit;
        END_IF;
        
        // erste Pumpe reversieren
        #pumpen[#statAktiverSatz].autoRev := #statSchritkette.Schritte.reversionVorAbpumpen OR #statSchritkette.Schritte.reversionNachAbpumpen;
    END_REGION
END_REGION
```


----------



## Larry Laffer (18 Januar 2021)

Warum eine Schleife ?
Damit inkrementierst du doch TempNächsterSatz einfach nur durch - die Schleife brauchst du da m.E. überhaupt nicht ...

Gruß
Larry


----------



## mista (18 Januar 2021)

Weil, es ja mehr als 2 Pumpen sein können, und sagen wir Pumpe 1 ist aktiver Satz und Pumpe 2 nicht bereit, weil gewartet wird oder gestört ist. 
Dann muss ja Pumpe 3 einsetzen. wie ich das realisier ohne eine Schleife keine Ahnung...


----------



## Larry Laffer (18 Januar 2021)

So, wie du aktuell die Schleife hast, läuft sie immer. Schau dir deinen Code-Schnipsel mal genau an ...

Was du beschreibst ist ja eine Suche nach der nächsten Pumpe - das darf nur einmal und auf Anforderung geschehen. Wenn du aber tatsächlich nur max. 3 Pumpen an den Start bringst dann kannst du das genauso über einen IF-ELSE_IF-Abfrage machen - das ist dann auch nicht mehr Aufwand (und für dich vielleicht auch einfacher umzusetzen ...


----------



## mista (18 Januar 2021)

Die Schleife läuft immer aber ist das nicht egal?

Denn der aktive Satz wird nur beim Triggeraufruf mit dem nächsten Satz überschrieben.


----------



## Larry Laffer (18 Januar 2021)

... dann macht es doch erst recht keinen Sinn ...
Schau dir den Code noch einmal genau an ... und was mit deinem aktivenSatz passiert ...


----------



## mista (18 Januar 2021)

Und der nächste Satz ist auch für den Parallelbetrieb. 

ich finde aber nicht den Fehler den du meinst?

der Aktive Satz wird doch nur bei hier gesetzt:


```
// Satzwechsel
        IF #statAnforderungNächsterSatz THEN
            #statAktiverSatz := #statNächsterSatz;
        END_IF;
```


----------



## Larry Laffer (18 Januar 2021)

Ich meinte das hier :
	
	



```
FOR #statLaufvariable := 1 TO #tempAnzahlPumpen DO
            
            // inkrementieren
            #tempNächsterSatz += 1;   [COLOR=#ff0000]// <= hier wird munter durch-inkrementiert, und zwar sogar die aktuelle Nummer wird dabei nicht ausgespart ...[/COLOR]
            
            // Limitieren
            IF #tempNächsterSatz > #tempAnzahlPumpen THEN
                #tempNächsterSatz := 1;
            END_IF;
            
            [COLOR=#ff0000]// dieser Teil macht vielleicht Sinn - aber ggf, auch eher so, wie ich geschrieben habe ...[/COLOR]
            IF #pumpen[#tempNächsterSatz].status.bereit THEN
                // wenn Pumpe bereit, dann als nächste bzw. für den Parallelbetrieb vormerken und Schleife verlassen 
                #statNächsterSatz := #tempNächsterSatz;
                EXIT;
            END_IF;
        END_FOR;
```


----------



## mista (18 Januar 2021)

Aber die aktuelle Nummer ist doch tempNächsterSatz, die wird im 1. Durchlauf inkrementiert, damit die aktuelle Pumpe nicht ausgewählt wird.

Die erste, welche gefunden wird gesetzt als nächste Pumpe. Die aktuelle Pumpe hat den Status bereit nicht! Also kann diese auch nicht ausgewählt werden als nächste Pumpe.


----------



## Larry Laffer (18 Januar 2021)

nein ... die wird IMMER inkrementiert - in jedem Durchlauf und in JEDEM Zyklus der SPS ...
Und sie hat am Ende der Schleife IMMER denselben Wert ...


----------



## mista (18 Januar 2021)

Sorry ich raff das nicht, 

vor der Schleife wird die Temp Variable zum druchlaufen der Schleife doch mit der Zahl des aktiven Satzes initialisiert. Muss ja auch jeder zyklus, da es doch eine Temp Variable ist. 


```
// Nächsten Pumpensatz bestimmen
        #tempNächsterSatz := #statAktiverSatz;
        FOR #statLaufvariable := 1 TO #tempAnzahlPumpen DO
```

Ich glaube ich stehe auf dem Schlauch


----------



## Larry Laffer (18 Januar 2021)

Wie lang ist der geschätze Zyklus deiner SPS ? 10 ms ? 20 ms ?
In dieser Zeit läuft deine Schleife ein Mal durch. Die Bereit-Meldung, auf die du reagierst, wird sich nicht während des Durchlaufs der Schleife verändern sondern nur davor (oder danach - egal wie du es siehst). Denk immer dran : Zyklische Programmbearbeitung ...! Der Durchlauf, den du da skizzierst hast, wird also im Zyklus stattfinden und somit immer das gleiche (oder kein) Ergebnis bringen. 
Aber du kannst es ja auch einfach ausprobieren ...


----------



## mista (18 Januar 2021)

Wie würdest du es denn anders machen? Oder anders gefragt, wie würdest du sicher stellen, dass die Schleife nur einmal ausgeführt wird? Das auch in den Trigger Bedingung für die Anforderung?

Alle meine Programme laufen die schleifen jeden Zyklus. Was ist wenn sich der bereit Status ändert wie kann man das abfangen und durch iterrieren?

Mit if else wusste ich nicht wie ich ein dynamisches Array Durchlauf


----------



## Heinileini (18 Januar 2021)

mista schrieb:


> // wenn Pumpe bereit, dann als nächste bzw. für den Parallelbetrieb vormerken ...


Mich beschäftigt jetzt schon länger, was genau hinter den Begriffen "ParallelBetrieb" und "PumpenPaar[en]" steckt.
Ist etwa mit beiden dasselbe gemeint? 
Legt z.B. der Bediener fest, welche Pumpen jeweils ein Paar bilden und bleibt diese Zuordnung bestehen, bis er sie anders festlegt?
Oder soll das Programm jedesmal aktuell ein Paar anhand der "bevorzugt-einschalten-Kriterien" neu "ausgucken", sobald sich der AnforderungsZustand von "alle Pumpen aus" in "Pumpe[n] einschalten" ändert?

Eine weitere Frage: ist es wichtig, dass bei einem PumpenPaar diejenige Pumpe zuerst deaktiviert wird, die zuletzt aktiviert wurde?


----------



## mista (18 Januar 2021)

Also das soll eine Steuerung sein, die für 3 Anlagen Typen sein soll. Einmal mit 2 pumpen die sich abwechseln . 

Einmal 3 Pumpen die sich abwechseln

Und einmal 2 pumpen paaere die sich abwechseln.

Bei jedem Anlagentyp kann optional auch der Parallelbetrieb aktiviert werden dann laufen ab einem bestimmten Pegel 2 pumpen, bzw. Beide Pumpenpaare.

Die ersten beiden Amalgamtypen habe ich mit meiner Schritt Kette versucht abzubilden. Wenn das steht wird die Kette um den dritten Anlagentyp erweitert. Welche mit der Schrittnummer 200 eingeleitet wird.


----------



## Larry Laffer (19 Januar 2021)

mista schrieb:


> Wie würdest du es denn anders machen? Oder anders gefragt, wie würdest du sicher stellen, dass die Schleife nur einmal ausgeführt wird? Das auch in den Trigger Bedingung für die Anforderung?



Ich bin auch "ein bisschen" Hochsprachen-Programmierer - von daher wäre hier mein Ansatz eine Funktion/Methode zu erstellen "GetNächstePumpe" oder so ...
Das heißt, ich würde das ermitteln lassen wenn Bedarf besteht. Das kann natürlich auch eine Schleife sein - macht aber bei 3 Pumpen nicht wirklich Sinn ... In deinem Fall ist es aber sowieso so, dass du (wegen des Aufbaus der Funktion) immer das gleiche Ergebnis bekommst - möglicherweise sogar die Nummer der Pumpe, die du sowieso schon benutzt.
In deinem Fall ist das mit den Schleifen sicherlich nicht so kritisch - ich bin aber grundsätzlich kein Fan davon, etwas ständig laufen zu lassen was nicht sein muss - egal welche Ergebnisse es bringt. Stell dir jetzt mal vor, deine Schleife hätte nicht 3 sondern 300 Elemente ständig durchzuackern - das würde dann irgendwann schon auf deine Zykluszeit gehen ...
Aber wie gesagt :  für mich machte die Routine selbst so keinen Sinn ...

Gruß
Larry


----------



## Heinileini (19 Januar 2021)

Larry Laffer schrieb:


> ... In deinem Fall ist es aber sowieso so, dass du (wegen des Aufbaus der Funktion) immer das gleiche Ergebnis bekommst ...


Na ja, ganz so langweilig wird's schon nicht werden. Es könnte mal eine Pumpe ausfallen. Und bei nur 3 Pumpen wäre das schon eine drastische Änderung der Situation.
Wenn nur ein Teil der Pumpen läuft, dann ändern sich auch die Laufzeiten der Pumpen unterschiedlich und wirken sich damit auf die Auswahl des nächsten Kandidaten aus.

Das ändert aber nichts daran, dass man nicht in jedem Zyklus den aktuell nächsten Kandidaten ermitteln muss.
Die Länge der Zykluszeit muss man selbstverständlich im Auge behalten ... aber auch die Stärke der Schwankungen der Zykluszeit! Es kommt auch auf eine gute Balance zwischen beiden Aspekten an. Das sollte man schonmal im Blick haben, wenn z.B. eine spätere Erweiterung auf mehr ArrayElemente/SchleifenDurchläufe abzusehen ist.


----------



## mista (19 Januar 2021)

Also ich hab das jetzt so umgebaut..dass ich in die Schleife rein springe nur wenn die Anforderung zum Satzwechsel kommt (wo ich dann auch den aktuellen Satz mit dem nächsten schreibe), oder wenn not Pumpe[nächsterSatz].Status.bereit ist.

Also nicht mehr jeden Zyklus. So noch einmal die Frage in die Runde.. wie geht man an solchen Pumpensteuerungen generell rann..ich muss mir eine Routine bzw. Ein Baukasten erstellen..damit ich nicht immer vor dem gleichen Herausforderungen stehe..


----------



## hucki (20 Januar 2021)

mista schrieb:


> .. wie geht man an solchen Pumpensteuerungen generell ran.


Ich versuche, die Aufgabe in separate Hauptpunkte und diese in möglichst einfache, universelle Abläufe zu zerlegen.
Deine Aufgabe hier würde ich in etwa so umsetzen...


Für mich gibt es 3 Hauptpunkte ->


*Motorenanforderung:*
Anzahl notwendiger Motoren (z.B. nach Niveau) bestimmen
Motorenanforderung an die notwendigen Motoren anpassen
Die Anforderung bestimmt nur, wieviele Motoren benötigt werden, aber nicht welche(r).
Die Anpassung der angeforderten Motoren kann z.B. auch zeitlich hoch/runter gesteuert werden, so dass nicht alle notwendigen Motoren zeitgleich ein- (oder aus-) geschaltet werden, auch wenn das Niveau das eigentlich erfordern würde. 
Laufzeitkontrolle der einzelnen Motoren u.ä. spielen bei der reinen Anforderung auch keine Rolle.



*Motorenpendel:*
Motorenzählungen (Anzahl in Dauer-EIN, in AUTO und in BETRIEB)
Auswahl zur Erhöhung bzw. Verringerung der Anzahl von AUTO-Motoren in BETRIEB je nach Anforderung (EIN-Motoren + AUTO-Motoren größer/kleiner Anforderung)
Die Anzahl der Motoren wird durch das angelegte Array bestimmt. Durch Dauer-AUS kann man Motore aus dem Pendel komplett heraus nehmen.

Beim Erhöhen wird jeweils der freigegebene Motor mit der höchsten Priorität als Nächstes ausgewählt, beim Verringern der mit der niedrigsten.
Die Priorität kann z.B. bestimmt werden durch die Gesamtlaufzeit (Beispiel-Link weiter vorn im Thread), den letzten EIN-/AUS-Schaltpunkt oder einer Rangliste der Motoren.
Mit Letzterem kann man z.B. einen täglichen, wöchentlichen ... Wechsel der Priorität erwirken. 



*(Einzel-) Motorensteuerung:*
Steuerung Betriebs-Modus (Dauer-AUS, -EIN, AUTO)
Ablaufsteuerung (Schrittkette) beim BETRIEB-AKTIV-Schalten und beim BETRIEB-INAKTIV-Schalten durch die Laufzeitüberwachung.
Datenerfassung wie totale/aktuelle Laufzeit, Anzahl Starts, letzter Ausschalt-Zeitpunkt u.ä.
ggf. Laufzeitüberwachung (mit BETRIEBssperre bei Überschreitung)
Jeder Motor hat seine eigene (aber für alle die gleiche) Steuerung, unabhängig von den Zuständen und Abläufen der anderen Motoren (Aktivierung/Deaktivierung der AUTO-Motoren macht das Pendel). 
Dadurch braucht es keine speziellen Parallelschritte oder -ketten, insbesondere wenn sich die Motorenanzahl (= Größe des Motorenarrays) ändert.
Störungen wirken wie ein Dauer-AUS des jeweiligen Motors.

Motorenpaare werden unterschieden nach Haupt- und Nebenmotor(en). 
Nur die Hauptmotoren (kein Verweis auf einen anderen Motor) werden beim Paarmodus vom Pendel gezählt und ggf. ein-/ausgeschaltet. 
Nebenmotoren (Verweis auf einen anderen Motor) folgen im Paarmodus mit Verzögerung dem BETRIEBszustand des Hauptmotors. Das bewirkt zwar eine Umkehr der Abschaltreihenfolge gegenüber Deiner Beschreibung, dafür kann die Ablaufsteuerung aber immer gleich und separat für jeden Motor bleiben und man benötigt nur ein einziges Ablaufszenarium (ich vermute mal, das aus diesem Grund auch Heinilein hier am Ende danach gefragt hat). 
Und dadurch kann man dann mit entsprechender Anzahl von Motoren auch ohne weitere Programmänderung bei Bedarf auch 3er, 4er ... Gruppen bilden. Der 3. Motor folgt dann einfach dem 2. usw..


----------



## Heinileini (20 Januar 2021)

hucki schrieb:


> (ich vermute mal, das aus diesem Grund auch Heinilein hier am Ende danach gefragt hat)


Ach hucki, wenigstens einer, der mich versteht!  DANKE!

Deine Unterscheidung in HauptMotor und NebenMotor gefällt mir. Auch der "Trick", die Verknüpfung / den Verweis "unkonventionellerweise"(?) nur und ausgerechnet beim NebenMotor einzutragen.
Aber wie löst man damit die Aufgabe, in der ersten Stufe 2 Motoren zu aktivieren, in der zweiten Stufe jedoch nur 1 Motor des anderen Paares?
In der ersten Stufe den NebenMotor anfordern und der HauptMotor wird dann anhand des Verweises auf den HauptMotor mitgezogen?
Und in der zweiten Stufe den HauptMotor anfordern und der weiss mangels Verweises auf den NebenMotor nicht, dass er "nur" ein halbes Paar ist?

Wie siehst Du das mit der Unterscheidung zwischen PumpenPaaren und dem ParallelBetrieb?

Gruss, Heinileini


----------



## hucki (20 Januar 2021)

Heinileini schrieb:


> Aber wie löst man damit die Aufgabe, in der ersten Stufe 2 Motoren zu aktivieren, in der zweiten Stufe jedoch nur 1 Motor des anderen Paares?
> In der ersten Stufe den NebenMotor anfordern und der HauptMotor wird dann anhand des Verweises auf den HauptMotor mitgezogen?
> Und in der zweiten Stufe den HauptMotor anfordern und der weiss mangels Verweises auf den NebenMotor nicht, dass er "nur" ein halbes Paar ist?


(Noch) keine Ahnung. 
Diese Aufgabe ist bis dato so völlig an mir vorbei gegangen. 

Ich hab' mich nur an dem hier orientiert:


mista schrieb:


> Folgendes Szenario, 2 Motoren, 3 Motoren oder 2x2 Motoren
> 
> in den ersten 2 Fällen:
> 
> ...


Könnte mir aber auf die Schnelle vorstellen, dass z.B. jeder Nebenmotor anhand der bereits laufenden Hauptmotoren (werden ja im Pendel eh' ständig gezählt) selbst entscheidet, ob er (noch) mit anspringen muss oder nicht. Durch die Verzögerung sollte auch mehr als genügend Zeit für diese Entscheidung sein.






Heinileini schrieb:


> Wie siehst Du das mit der Unterscheidung zwischen PumpenPaaren und dem ParallelBetrieb?


Ich hätte an ein Flag gedacht, das anzeigt ob im Parallel- oder Paarbetrieb gefahren werden soll.
Im Parallel-Betrieb wird dann nicht zwischen Haupt- und Nebenmotor unterschieden bzw. sind dann einfach alles Hauptmotoren.


----------



## hucki (20 Januar 2021)

Heinileini schrieb:


> Auch der "Trick", die Verknüpfung / den Verweis "unkonventionellerweise"(?) nur und ausgerechnet beim NebenMotor einzutragen.


Keine Ahnung, ob das unkonventionell ist.
Meine Gedanken waren, dass sich jeder Motor möglichst nur um seinen eigenen Betrieb kümmert und dabei einfach nur rüber schielt, was sein "Chef" (so denn er einen hat) so macht.

Und ich bin mit immer nur einer Partnerangabe IMHO trotzdem flexibler:
Ich kann z.B. Motor 2 sagen, er soll der 1 folgen. Dann kann ich z.B. der 3 sagen, er soll widerrum der 2 folgen. Alternativ könnte ich aber die 3 auch direkt der 1 folgen lassen.
Umgekehrt müsste ich im letzten Fall die 1 schon über 2 Follower informieren.


----------



## mista (22 Januar 2021)

Larry Laffer schrieb:


> ...
> 
> Was du beschreibst ist ja eine Suche nach der nächsten Pumpe - das darf nur einmal und auf Anforderung geschehen. Wenn du aber tatsächlich nur max. 3 Pumpen an den Start bringst dann kannst du das genauso über einen IF-ELSE_IF-Abfrage machen - das ist dann auch nicht mehr Aufwand (und für dich vielleicht auch einfacher umzusetzen ...



Magst du mir ein Beispiel zeigen?

Denn es kann ja sein ,dass Pumpe 1 aktiv ist aber Pumpe 2 nicht bereit, dann müsste ja die Pumpe 3 als nächste gewählt werden.


----------



## Larry Laffer (23 Januar 2021)

mista schrieb:


> Magst du mir ein Beispiel zeigen?
> 
> Denn es kann ja sein ,dass Pumpe 1 aktiv ist aber Pumpe 2 nicht bereit, dann müsste ja die Pumpe 3 als nächste gewählt werden.



Klar ... du könntest dir für die aktibe Pumnpe einen "Merker" setzen oder in eine INT-Variable deren Nummer hineinschreiben. Das benutzt du dann als Anwahl - ich nehme mal die INT-Variante ... das könnte dann vielleicht so aussehen :

```
zweite_Pumpe := 0 ;
if (aktuelle_Pumpe = 1) then
   if Pumpe_2_bereit then
      zweite_Pumpe := 2 ;
   els_if Pumpe_3_bereit then
      zweite_Pumpe := 3 ;
   end_if ;
els_if (aktuelle_Pumpe = 2) then
   // ... entsprechend wie oben ...
els_if (aktuelle_Pumpe = 3) then
   // ... entsprechend wie oben ...
End_if ;
```
Enthält nun die Variable zweite_Pumpe einen Zahlenwert ungleich 0 dann würdest du die dazuschalten ...

Gruß
Larry


----------



## mista (23 Januar 2021)

Ja statAktiverSatz und statNächsterSatz sind ja beide int Variablen. OK das kann man dann wirklich so machen. 

Dachte aber dass so ein Code unnötig aufgebläht wird.

Das mit dem getNachstePumpe oder Revierbetrieb könnten man ja auch als Sprung im Case dann machen..also wenn die Anforderung für den reversierbetrieb da ist, setze aktuellen Schritt z.B. = 1000 und dann noch einen  vorherigen als statische Variable um zum letzen Schritt zu springen um dann die Ablaufkette zum Beispiel mit statAktuellerScheitt := StatLetzterSchritt + 1 oder mit if then else arbeiten und je nachdem mit der Konstante dea Schrittes setzten.

Hast du vielleicht eine gute Lektüre als Tipp für mich wo man entweder, typische Algorithmen für SPS oder Herangehensweise zum nachschlagen hat?

Aktuell habe ich das Buch, SPS Theorie und Praxis. Was ich nicht sehr gut finde..


----------



## Larry Laffer (24 Januar 2021)

mista schrieb:


> Hast du vielleicht eine gute Lektüre als Tipp für mich wo man entweder, typische Algorithmen für SPS oder Herangehensweise zum nachschlagen hat?



Nach meiner Meinung gibt es nicht DAS Nachschlagewerk - wie sollte es auch. Viele Dinge sind doch spezifisch - auch wenn sie sich wiederholen ...
Ich würde dir empfehlen, hier generell mitzulesen - auch bei Themen, die dich im Augenblick vielleicht nicht betreffen. Hier bekommst du auf die Weise dann viel mit ... und im Zeifel kannst du dann ja sogar nachfragen ...

Gruß
Larry


----------



## Heinileini (24 Januar 2021)

Larry Laffer schrieb:


> mista schrieb:
> 
> 
> > Hast du vielleicht eine gute Lektüre als Tipp für mich wo man entweder, typische Algorithmen für SPS oder Herangehensweise zum nachschlagen hat?
> ...


Wie sollte es auch aussehen und was sollte es alles beinhalten?
Und auf welche verschiedenen Typen von Interessenten/Lesern mit welchen Vorkenntnissen und Fähigkeiten soll/könnte "DAS Nachschlagewerk" zugeschnitten sein?

Ich sehe eine Bandbreite von Leuten mit extrem schlechtem Gedächtnis bis hin zu Leuten mit extrem gutem Gedächtnis.
Ich gehöre zu denen mit schlechtem Gedächtnis. Was ich nicht verstanden habe, kann ich mir nicht merken. Was ich verstanden habe, muss ich mir nicht merken - das ist für mich irgendwie selbstverständlich und zur Not kann ich es mir wieder herleiten.
Beneidet habe ich Leute mit gutem Gedächtnis durchaus, musste aber hin und wieder feststellen, dass ihnen manchmal das gute Gedächtnis bei ProblemLösungen nicht geholfen hat, weil sie gelegentlich hilflos waren, das Gelernte in die Praxis umzusetzen.

Selber [mit-]denken war für mich immer sehr wichtig. Auch, keine Scheu davor zu haben, das Rad (oder nur ein kleines, unbedeutendes Rädchen) mal neu zu erfinden, hat mir geholfen. Gelesenes zu hinterfragen und nicht einfach nur "abzunicken" ist auch nicht verkehrt.
Wenn man über eine fertige Lösung stolpert, auch mal überlegen, wie hätte ich das (Teil-)Problem angepackt/gelöst und dann mal vergleichen, welche Vor- und Nachteile sich da gegenüberstehen.

Logisch denken sollte man können und man muss erkennen und sich eingestehen, wenn man in zu grossen/groben Schritten (zu sprunghaft) denkt.
ComputerProgramme erfordern es, dass man die Anweisungen fein säuberlich Schrittchen für Schrittchen durchdenkt/aufbereitet.

Programme bzw. ProgrammStückchen testen. Verhält sich der verwendete Befehl oder die Funktion wirklich so, wie ich glaube, die Beschreibung verstanden zu haben? 

Bei SPS-Programmen die zyklische Bearbeitung akzeptieren, verstehen und verinnerlichen. Verstehen, dass das gesamte zyklische Programm in einer "EndlosSchleife" vom BetriebsSystem aufgerufen, aber nicht als Schleife sichtbar wird.

Verstehen, dass die Aufteilung in verschiedene Tasks (GrundProgramm, Alarme/ZeitAlarme/WeckAlarme und wie sie noch so genannt werden) manchmal nötig und/oder hilfreich ist, aber oft zu weiteren "Problemchen" führt, spätestens wenn sie Einfluss auf die DatenKonsistenz oder die effektive ZyklusZeit hat.

Verstehen, dass viele Funktionalitäten (z.B. Datei öffnen, lesen/schreiben, schliessen oder "Streams", also DatenAustausch mit anderen Geräten) i.A. mehrere bis viele Zyklen benötigen, bis sie fertig und somit auswertbar oder wieder benutzbar sind.

Den Umgang mit Schrittketten verstehen. Sie dienen dazu, im Wesentlichen WarteZeiten auf Reaktionen zu "verbraten" und gelegentlich zur nächsten TeilAufgabe einer Aufgabe weiterzuschalten. Nutzlos Wartezeiten "verbraten" tun sie aber nicht, sie erlauben der CPU, sich während dieser Wartezeiten mit anderen Aufgaben zu beschäftigen.

Das Thema FlankenErkennung finde ich so wichtig, dass man sie m.E. zunächst "zu Fuss" programmieren sollte, bis man sie wirklich verstanden hat.
Oft hat man es mit FlankenErkennungen zu tun, die man nicht unbedingt als solche wahrnimmt, z.B. bei Timern, Zählern und auch anderen Bausteinen, die flankengetriggert reagieren. 

Die korrekte Verarbeitung von sog. "A/B-Signalen" liegt mir auch sehr am Herzen. Es ist so simpel und trotzdem findet man erschreckend oft Beispiele (sogar in "guten" Büchern und Lehrgängen) dafür, wie man es keinesfalls machen sollte, wenn man sich nicht unnötige (weil leicht vermeidbare) Probleme einfangen will.

Das Rätsel der HexadezimalDarstellung von Zahlen. Wird überwiegend in der Dokumentation von seriellen Schnittstellen verwendet und führt immer wieder zu der Frage, "wie wandele ich denn Format x in hexadezimal bzw. umgekehrt?". Meistens lautet die Antwort "[am besten] gar nicht!". Erst, wenn es darum geht, eine in ASCII vorliegende oder zu erzeugende Zeichenfolge zu interpretieren oder bilden, wird es etwas aufwändiger.

Der eigentlich triviale Hinweis, dass die IF-Selektion nicht grundsätzlich überflüssig ist, oft aber doch ... und dann produziert sie Unübersichtlichkeit allein durch die überflüssige Menge an zu schreibendem Code. Z.B.:


```
IF MotorAn = TRUE THEN
    MotorAktiv := TRUE ;
ELSE
    MotorAktiv := FALSE ;    
END_IF ;
```
Wie überflüssig dieser Aufwand ist, sieht man gut, wenn man die einfache Variante als Einzeiler gegenüberstellt:


```
MotorAktiv := MotorAn ;
```
Warum die beiden Alternativen auseinanderpfriemeln, um sie dann sofort wieder zusammenzuführen?
Übrigens sind die Vergleiche '= TRUE' oder '<> FALSE' oder 'NOT (...) = FALSE' immer überflüssig.

Sich an die Verwendung von Arrays heranwagen!
Zunächst bzw. bevorzugt erstmal auf 1-dimensionale Arrays beschränken. 
Sucht man nach einem Array, das unterschiedliche DatenTypen enthält, besteht die Lösung nicht darin, eine oder mehrere Dimensionen hinzuzufügen!
Man verwendet dann ein 'Array Of Struct' und erhält damit eine (quasi "Excel"-)Tabelle.
Der Index des Array entspricht der ZeilenNr und alle Zeilen haben denselben, allerdings einen zusammengebastelten, "benutzerdefinierten" DatenTyp, in dem man für jede Spalte den jeweils benötigten DatenTyp festgelegt hat.
Benötigt man eine 2. oder auch 3. Dimension, um z.B. in den Daten das Abbild eines Regals mit seinen in Zeilen und Spalten angeordneten Fächern und dann noch mehrere Reihen solcher RegalWände zu schaffen, dann bleibt die Angelegenheit nachvollziehbar und durchschaubar.
Aber Vorsicht! Wenn man plötzlich feststellt, dass man zu wenig Platz im Array eingeplant hat, lässt man sich allzu leicht dazu verleiten, auf die Schnelle einfach eine weitere Dimension hinzuzufügen. Man sollte sich dann sehr gut überlegen, ob diese Massnahme wirklich den gewünschten Zweck erfüllt oder nur den benötigten SpeicherPlatz verschwendet oder "explodieren" lässt.

Weitere FingerÜbungen für Arrays bzw. Arrays Of Struct: 
- Sortieren von Listen/Tabellen (für "Fortgeschrittene": Sortierung über einen Index, ohne die "eigentlichen" Daten(-Sätze) im Speicher umzuräumen; Sortierung nach verschiedenen Kriterien; mehrstufiges Sortieren). 
- FIFOs und LIFOs.

Hierfür findet man meistens die verschiedensten vorgefertigten Lösungen. Aber das SichBeschäftigen mit eigenen Ansätzen und kleinen Experimenten kann nicht schaden.
Selbst, wenn die gesammelten Erfahrungen "nur" dazu dienen, aus den verschiedenen vorgefertigten Lösungen die geeigneteste herauszupicken.
Nicht zuletzt, wenn es um Arrays mit sehr vielen Elementen geht und die ZyklusZeit akut bedroht ist, kann eine massgeschneiderte (selbst realisierte) Lösung
schon mal in Betracht gezogen werden!


----------



## Larry Laffer (24 Januar 2021)

*ACK*
Ich weiß nicht ob es dem TE hilft - es ist aber gut geschrieben und zeigt ja auch in die richtige Richtung (und zwar sogar an ganz ganz vielen Stellen).


----------



## mista (25 Januar 2021)

Ich finde ja Arrays auch sehr gut, doch leider sind diese schlecht zu debuggen, Also wenn ich eine For Schleife habe, ist es schwierig diese zu debuggen, oder ich raff das nicht wie man die Haltepunkte bei Siemens benutzt für SCL. Bei anderen Hochsprachen bzw. Skriptsprachen wie C#, Java oder javaScript ist es viel besser gelöst. 

Gerade in der Simulation bei PLC Sim würde ich gerne sehen, was in der Schleife passiert, das hat beim Letzten Projekt echt zu Try and Error geführt, daher denke ich, dass wenn ich nur 5 Pumpen habe wohl ab jetzt nur noch mit IF THEN ELSE arbeiten werde.

Es sei denn man erklärt mir, wie man ein einer FOR Schleife vernünftig Debuggen kann.


----------



## Larry Laffer (25 Januar 2021)

Kannst du meines Wisserns nach nicht ...
Du musst hier verstehen, dass ein SPS-Programm ständig zyklisch abgearbeitet wird. Du kannst es nicht an einer Stelle "anhalten". Das ist z.B. bei .Net komplett anders - hier ist der Ablauf erstmal ereignisgesteuert ... und das Ereignis kannst du unterbinden ... oder das Programm auch so einfach "festhalten".
Hältst du ein SPS-Programm irgendwo an so würdest du sofort eine Zykluszeit-Überschreitung bekommen da die CPU im Hintergrund eine maximale Zykluszeit sicherzustellen versucht - du musst immer dran denken, dass du ja eigentlich eine Maschine steuern willst und z.B. ein Abschalten von irgendetwas sicherstellen willst / musst ...

Gruß
Larry


----------



## mista (25 Januar 2021)

Deshalb die Frage ob es geht zumindest in PLC Sim. Aber Haltepunkte Setzen geht, auf jeden Fall! Nur innerhalb einer Schleife bekomme ich das nicht hin. Aber anhalten und zeilenweise den Code durchgehen funktioniert komischer weise.


----------



## Larry Laffer (25 Januar 2021)

Nicht wirklich und nicht 100%ig zuverlässig.
Hier wird im Prinzip versucht, das Ergebnis an einer bestimmten Stelle im Programm abzufragen - der Zyklus läuft deswegen trotzdem "ganz normal" weiter ...


----------



## mista (25 Januar 2021)

Deshalb die Frage ob es geht zumindest in PLC Sim. Aber Haltepunkte Setzen geht, auf jeden Fall! Nur innerhalb einer Schleife bekomme ich das nicht hin. Aber anhalten und zeilenweise den Code durchgehen funktioniert komischer weise.


----------



## Larry Laffer (25 Januar 2021)

Das habe ich dir aber, so glaube ich, eben erklärt :
Im "normalen" Programm gibt es in jeder Zeile des Programms (bildlich gesprochen) nur ein mögliches Ergebnis.
Innerhalb einer Schleife hast du mehrere unterschiedliche Ergebnisse in derselben Zeile.
Und ... das SPS-Programm wird NICHT angehalten - warum hatte ich, glaube ich, auch beiläufig erwähnt ...
Wenn du Zwischenergebnisse in deiner Schleife sehen willst so geht das nur indem du die Schleife eben keine Schleife sein läßt - also Beobachten für den festen Index 1, danach für den festen Index 2 ... usw.


----------



## Windoze (25 Januar 2021)

Sorry, aber das stimmt so nicht!

Bei Haltepunkten wird die SPS tatsächlich angehalten, mit entsprechenden Auswirkungen auf die Anlage.

Zitat aus der TIA Hilfe:


> Haftungsausschluss für die Verwendung von Haltepunkten  bei SIMATIC S7-1500
> Die SIMATIC S7-1500 unterstützt die Verwendung von Haltepunkten zum gezielten  Anhalten des Programmablaufs bei der Fehlersuche im Anwenderprogramm. Das  Erreichen eines gesetzten Haltepunkts durch das Anwenderprogramm führt dazu,  dass der Verarbeitungszyklus der SIMATIC S7-1500 an diesen Haltepunkten  angehalten wird. Dabei werden die Ausgangswerte für die angeschlossen  Ausgangsbaugruppen in ihrem jeweiligen Zustand gehalten und (im Gegensatz zur  SIMATIC S7-300/S7-400) weder zurückgesetzt noch auf die projektierten  Ersatzwerte gesetzt werden. Darüber hinaus kann die SIMATIC S7-1500 in diesem  Zustand nicht mehr auf Eingangswerte von Eingangsbaugruppen reagieren.


----------



## hucki (25 Januar 2021)

Larry Laffer schrieb:


> Wenn du Zwischenergebnisse in deiner Schleife sehen willst so geht das nur indem du die Schleife eben keine Schleife sein läßt - also Beobachten für den festen Index 1, danach für den festen Index 2 ... usw.


Oder man klickt mit rechts auf den Code und aktiviert das Kästchen "Schleifen beobachten":


			
				TIA-Hilfe schrieb:
			
		

> *Schleifen beobachten*
> 
> Durch die Anzeige des Programmstatus können Sie den Programmablauf innerhalb eines Bausteins beobachten. Dadurch erhalten Sie eine Übersicht über die Werte der einzelnen Operanden und der Verknüpfungsergebnisse und können feststellen, ob die Komponenten des Automatisierungssystems korrekt gesteuert werden.
> Sind die Werte in einer helleren Schrift mit geringerer Sättigung dargestellt, so stammen diese Werte nicht aus dem aktuellen Zyklus.
> ...


----------



## Heinileini (25 Januar 2021)

hucki schrieb:


> Oder man klickt mit rechts auf den Code und aktiviert das Kästchen "Schleifen beobachten":


Und wenn man das Kästchen "Schleifen beobachten" nicht finden kann, tut's vielleicht auch ein WorkAround ...

```
// Beispiel für FOR-Schleife. 
// Achtung, PseudoCode! Ist als "AbsichtsErklärung" zu verstehen!
// Und nochmal Achtung! Vorzeitige SchleifenAbbrüche (z.B. durch EXIT) sind nicht eingeplant!
// Und immer noch Achtung! Bei geschachtelten Schleifen nur für die äusserste Schleife geeignet!

FOR x := Anfang TO Ende DO
    // zu testende Befehlsfolge mit x := Anfang..Ende ;
END_FOR ;    

// ========================================================================================
// zum Testen umschreiben in (sollte mit Copy/Paste ohne ausufernden Aufwand möglich sein):
// ========================================================================================

Test := MIN(MAX(x, Anfang), Ende) ; // mit x den zu beobachtenden "SchleifenDurchlauf" wählen

IF Test > Anfang THEN
    FOR x := Anfang TO Test - 1 DO
        // nicht zu testende Befehlsfolge mit x := Anfang..Test-1 ;
    END_FOR ;    
END_IF ;

x := Test ; 
// zu testende Befehlsfolge >>> NUR HIER <<< ausserhalb der Schleife[n] beobachten

IF Test < Ende THEN
    FOR x := Test + 1 TO Ende DO
        // nicht zu testende Befehlsfolge mit x := Test+1..Ende ;
    END_FOR ;
END_IF ;
```


----------



## Larry Laffer (26 Januar 2021)

Windoze schrieb:


> Bei Haltepunkten wird die SPS tatsächlich angehalten, mit entsprechenden Auswirkungen auf die Anlage.



Das war mir so nicht bekannt ...
Vielleicht ja auch deshalb nicht weil das nun nicht unbedingt etwas ist, dass man praktikabel (also in der Praxis) einsetzen kann oder sollte ... 

Gruß
Larry


----------



## Windoze (26 Januar 2021)

Larry Laffer schrieb:


> Das war mir so nicht bekannt ...
> Vielleicht ja auch deshalb nicht weil das nun nicht unbedingt etwas ist, dass man praktikabel (also in der Praxis) einsetzen kann oder sollte ...
> 
> Gruß
> Larry



Ja, die einzige Sinnvolle Verwendung ist eigentlich in PLCSIM.
Wir haben fast immer F-CPUs da würde ein Haltepunkt Not-Halt auslösen...


----------

