# Reihenfolge per Zufallsgenerator bestimmen



## Ratoncito (25 November 2020)

Hallo,

ich möchte die Reihenfolge von 7 Aktivitäten per Zufallsgenerator bestimmen. Den Ablauf stelle ich mir so vor:

Nehmen wir mal an die Aktivitäten wären von A bis G bezeichnet.
Zu einer bestimmten Bedingung werden diesen Aktivitäten Zahlen von 1 bis 7 zugeordnet und anschließend der Reihe nach abgearbeitet.

Ich möchte, dass diese Aktivitäten jedesmal in einer anderen Reihenfolge ablaufen.

Für Eure Hilfe im Voraus vielen Dank


----------



## JSEngineering (25 November 2020)

Hallo Wolfgang,

mal in OSCAT geschaut? Da gibt es RND-Funktionen, die den Timer nutzen, um Pseudo-Zufallszahlen zu erzeugen. Und dann kannst Du z.B. nach Größe der Zahlen sortieren, um die Reihenfolge festzulegen.

Gruß
   Jens


----------



## Heinileini (25 November 2020)

Hier ganz viel gänzlich ungetesteter PseudoCode:

Verwendete Variablen:

```
giA       globales INT Array [0..6]
giP       globale  INT Variable
grR       globale  REAL Variable
    
tbEnd     temporäre BOOL Variable
    
tiBis     temporäre INT Variable
tiIdx1    temporäre INT Variable
tiIdx2    temporäre INT Variable
tiTmp     temporäre INT Variable
    
trR       temporäre REAL Variable
```

Init-Routine:

```
// = = = = = < Init: "im Hochlauf der PLC" > = = = = =

// - - - < Array 0..6 initialisieren mit 1..7 > - - -
tiTmp := giA[0] ;
For tiIdx1 := 0 To 6 Do
    giA[tiIdx1] := (tiIdx1 + tiTmp) Mod 7 + 1 ;
End_For ;

// - - - < Startwert für grR bilden > - - -
If giP < 2 OR  giP > 32740 Then
    giP := 2 ;
Else
    Repeat
        giP = giP + 1 + giP Mod 2 ;
        tiBis := Real_To_Int(Sqrt(Int_To_Real(giP))) ;
        tbEnd := True ;
        For tiIdx1 := 3 To tiBis By 2 Do
            If giP Mod tiIdx1 = 0 Then 
                tbEnd := False ;
                Exit ;
            End_If ;     
        End_For ;
    Until tbEnd
    End_Repeat ;
End_If ;
trR := Sqrt(Int_To_Real(giP)) ;
grR := trR - Int_To_Real(Real_To_Int(trR)) ;
```

Nächste Array-Belegung bilden:

```
// = = = = = < Next: "Verwirbeln des Array" > = = = = =

// - - - < Array rotieren > - - -
tiTmp:= giA[0] ;
For tiIdx1 := 0 To 5 Do
    giA[tiIdx1] := giA[tiIdx1 + 1] ;
End_For ;
giA[6] := tiTmp ;

// - - - < Idx1 bilden > - - -
trR := grR * 997.0 ;
grR := trR - Int_To_Real(Real_To_Int(trR)) ;
tiIdx1 := Real_To_Int(grR * 7.0) ;

// - - - < Idx2 bilden > - - -
trR := grR * 997.0 ;
grR := trR - Int_To_Real(Real_To_Int(trR)) ;
tiIdx2 := Real_To_Int(grR * 7.0) ;

// - - - < ggfs Idx2 korrigieren > - - -
If tiIdx1 = tiIdx2 Then 
    tiIdx2 := (tiIdx2 + 3) Mod 7 ;
End_If ;

// - - - < 2 Elemente tauschen > - - -
tiTmp:= giA[tiIdx1] ; 
giA[tiIdx1] := giA[tiIdx2] ;
giA[tiIdx2] := tiTmp ;
```


----------



## Ratoncito (25 November 2020)

Hallo,

@Heinilein
Morgen werde ich mich damit beschäftigen und sicherlich mehr als nur eine Frage haben.


@Jens
Bei Oscat habe ich einige Stunden rumgesucht, aber nichts gefunden. Eventuell nicht an der richtigen Stelle gesucht, war heute eh nicht mein Tag.


Vielen Dank und noch einen schönen Abend.


----------



## JSEngineering (25 November 2020)

Ich glaube, dass findest Du unter den mathematischen Funktionen... Such mal nach RND.


----------



## Ratoncito (25 November 2020)

Hallo,

ich habe sicherlich auch danach gesucht. Ich habe auch einige Beiträge zu Zufallsgenerator gefunden, aber die Beiträge waren recht alt und eventuell vorhandener Code gelöscht. Unter den Downloads bin ich auch nicht fündig geworden.

Aber wie schon bemerkt, heute war nicht mein Tag.


----------



## Heinileini (25 November 2020)

JSEngineering schrieb:


> Ich glaube, dass findest Du unter den mathematischen Funktionen... Such mal nach RND.


Besser nach RDM bzw. RDM2.



Ratoncito schrieb:


> Aber wie schon bemerkt, heute war nicht mein Tag.


Doch! Du weisst es nur noch nicht! 

RND ist runden (ROUND). Das Zauberwort lautet RANDOM. Man kann nach dem kompletten Wort suchen (und u.a. viele unpassende Dinge finden) oder nach (z.B.) RDM.

PS:
In #3 benutze ich zu Fuss gemachte ZufallsZahlen. Prinzip: Multiplikation mit 997 und plattmachen der Vorkommastellen.


----------



## JSEngineering (25 November 2020)

Heinileini schrieb:


> Besser nach RDM bzw. RDM2.



Klar, vollkommen richtig... 
Die Krux mit Abkürzungen, man verwechselt sie 

Danke für die Korrektur...


----------



## Ratoncito (26 November 2020)

Hallo,



> > Zitat von *Ratoncito*
> >
> >
> > Aber wie schon bemerkt, heute war nicht mein Tag.
> ...



Dann wollen wir mal hoffen...

Eigentlich kann es heute nur besser werden 

Trotz dem gestrigen Debakel werde ich mich heute zuerst noch mal mit der Rechnerei um Zeit und Datum beschäftigen.


----------



## Ratoncito (29 November 2020)

Hallo Heinileini,

den Code aus Deinem Beitrag#3 habe ich in einen Baustein eingefügt. Ja es funktioniert, ich sitze vor dem Laptop und sehe staunend viele sich ständig ändernde Zahlen.

Nur, wie könnte ich nun meine 7 Variablen, ich nenne sie mal A ... G in zufälliger Reihenfolge starten?

Ich bin mal wieder ahnungslos.


----------



## JSEngineering (29 November 2020)

Du erzeugst pro Buchstaben A..G eine Zahl. Am Ende sortierst Du sie nach Größe, schon hast Du eine zufällige Reihenfolge.


----------



## Ratoncito (29 November 2020)

Hallo,



JSEngineering schrieb:


> Du erzeugst pro Buchstaben A..G eine Zahl. Am Ende sortierst Du sie nach Größe, schon hast Du eine zufällige Reihenfolge.



ja, so habe ich mir das auch gedacht. 

Die Frage ist nur wie?

Der Zufallsgenerator aus Beitrag#3 scheint ja zu funktionieren, aber wie erhalte ich nun die Zahlen für A ... G, und wie sortiere ich das.


----------



## Onkel Dagobert (29 November 2020)

Was willst du eigentlich sortieren? Erst möchtest du eine zufällige Reihenfolge und dann willst du wieder sortieren? Grundsätzlich ordnest du doch deinen Aktionen A..G die Zahlen 1..7 fest zu, oder nicht? Also A bekommt fest den Wert 1, B den Wert 2 usw. Jetzt ziehst du die erste Zahl und arbeitest die entsprechende Aktion ab. Dann ziehst du solange eine nächste Zahl, bis diese ungleich der schon gezogenen ist und arbeitest die nächste Aktion ab. Das machst du so lange, bis jede Zahl einmal gezogen wurde.

Übrigens, man muss es nicht verkomplizieren  .
Wenn man eine genügend große Ganzzahl hernimmt, welche man bis in die letzten Stellen unmöglich beeinflussen kann (z.Bsp. die Tageszeit in Millisekunden, einen Timer, die Systemzeit etc.), und dann mit MOD 7 (für den Bereich 1..7) arbeitet, erhält man auch ein zufälliges Ergebnis. Ich habe das vor langer Zeit mal getestet, die Ergebnisse gezählt und für gut befunden.


----------



## JSEngineering (29 November 2020)

Onkel Dagobert schrieb:


> Was willst du eigentlich sortieren? Erst möchtest du eine zufällige Reihenfolge und dann willst du wieder sortieren? Grundsätzlich ordnest du doch deinen Aktionen A..G die Zahlen 1..7 fest zu, oder nicht? Also A bekommt fest den Wert 1, B den Wert 2 usw. Jetzt ziehst du die erste Zahl und arbeitest die entsprechende Aktion ab. Dann ziehst du solange eine nächste Zahl, bis diese ungleich der schon gezogenen ist und arbeitest die nächste Aktion ab. Das machst du so lange, bis jede Zahl einmal gezogen wurde.



Da komme ich jetzt nicht mit....
Die Zahlen 1..7 sind ja Zufallszahlen. Er möchte A..G in zufälliger Reihenfolge aufrufen... Wenn ich also die Zufallszahlen der Größe nach sortiere, bekomme ich eine zufällige Reihenfolge... daher das Sortieren.

Ich denke, wie immer, gibt es mehrere Lösungswege:
Du nimmst ein zweidimensionales Array. In der ersten Dimension speicherst Du fest A..G, in der zweiten dynamisch die Zufallszahlen und sortierst dann nach den Zufallszahlen. Danach gehst Du von oben nach unten durch, die Zufallszahlen interessieren jetzt in der zweiten Dimension nicht mehr, jetzt guckst Du nur noch in die erste und liest aus, welche Aktion A..G zu startest.

Eine weitere ist, die Zufallszahlen in zwei Arrays zu speichern: Quasi eine Abwandlung von der ersten Lösung und vielleicht das, was Dagobert meint. Im ersten Array hast Du jetzt über den Arrayindex die Zuordnung der Zahlen zu A..G. Das zweite Array sortierst Du und "ziehst" jetzt die Zahlen nach und nach. Suchst die dann im ersten Array und hast über den Index die Aktion, die Du starten willst.

Du kannst auch Zahlen zwischen 1..7 generieren über den Zufallsgenerator und nimmst dann die Aktion A..G, die Du ausführen willst. Hierbei mußt Du nur aufpassen, daß Du eine Aktion nicht mehrfach ausführst. Taucht eine Zahl mehrfach auf, mußt Du den Zufallsgenerator ggf. mehrfach anschmeißen.

Array sortieren geht z.B. über _ARRAY_SORT aus Oscat.

Du kannst auch versuchen, ein Array 1..7 anzulegen und dieses in jedem Durchgang über _ARRAY_SHUFFLE aus Oscat durcheinanderzuwürfeln. Damit kommst Du eigentlich am Schnellsten zum Ergebnis. Hab ich gerade erst gesehen, daß es diese Funktion gibt...


----------



## Ratoncito (29 November 2020)

Hallo,

mal wieder nicht richtig ausgedrückt 

A ... G nach Zufall unterschiedliche Zahlen zuweisen und dann in Reihenfolge der zugewiesenen Zahl ausführen.

PS 
Bedenkt bitte, ich bin Erstklässler - bilde die Quadratwurzel von... 
Je einfacher - je besser


----------



## JSEngineering (29 November 2020)

Ratoncito schrieb:


> Je einfacher - je besser



Dann mache Dir ein festes Array, in dem die Zahlen 1..7 (A..G) stehen.
Schicke dieses Array in jedem Durchlauf durch die Funktion _ARRAY_SHUFFLE.
Danach gehst Du durch das Array und führst dann die Aktion 1..7 aus, die jetzt ja in zufälliger Reihenfolge im Array stehen.


----------



## JSEngineering (29 November 2020)

```
Reihenfolge : Array[1..7] of Real := 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0;
Dummy : BOOL;
x : INT;

Dummy := _ARRAY_SHUFFLE(pt:=ADR(Reihenfolge), size:=7);

for x:=1 to 7 do
    case REAL_TO_INT(Reihenfolge[x]) of
             1: //Anweisung für A
             2: //Anweisung für B
             3: //Anweisung für C
             ...
     end_case;
end_for;
```


----------



## Heinileini (29 November 2020)

Moin Wolfgang,
die zufällige Reihenfolge steht in den 7 Elementen des Array.
Du schaust nacheinander giA[0] .. giA[6] an und führst das aus, was "drinsteht". Ist natürlich als Zahl kodiert (1..7) und nur Du weisst, was die Zahlen bedeuten.
Im folgenden mache ich die Zuordnung 1 entspricht Aktion A, 2 entspricht Aktion B, u.s.w..

```
IF giAct > 6 THEN // irgendwo Sequenz starten durch 'giAct := 0 ;'
        gbActDone := TRUE
        // hier sonst nix tun, d.h. warten, bis giAct im Bereich 0..6 liegt
    ELSIF gbActDone THEN // wenn gbActDone, dann nächste Aktion starten
        gbActDone := FALSE ;
        giAct := giAct MOD 7 ;
        CASE giA[giAct] OF
        1:  
            //  hier Kriterium für Start Aktion A setzen!
            IF "Aktion A fertig" THEN gbActDone := TRUE ; END_IF ; // statt "Aktion A fertig" etwas "sinnvolles" programmieren!
        2:  
            // hier Kriterium für Start der Aktion B setzen!
            IF "Aktion B fertig" THEN gbActDone := TRUE ; END_IF ;
        3:  
            // hier Kriterium für Start der Aktion C setzen!
            IF "Aktion C fertig" THEN gbActDone := TRUE ; END_IF ;
        4:  
            // hier Kriterium für Start der Aktion D setzen!
            IF "Aktion D fertig" THEN gbActDone := TRUE ; END_IF ;
        5:  
            // hier Kriterium für Start der Aktion E setzen!
            IF "Aktion E fertig" THEN gbActDone := TRUE ; END_IF ;
        6:  
            // hier Kriterium für Start der Aktion F setzen!
            IF "Aktion F fertig" THEN gbActDone := TRUE ; END_IF ;
        ELSE:  
            // hier Kriterium für Start der Aktion G setzen!
            IF "Aktion G fertig" THEN gbActDone := TRUE ; END_IF ;
        END_CASE ; 
    IF gbActDone THEN giAct := giAct + 1 ; END_IF ;
    END_IF ;
```
Das mag ungeschickt für Deine Anwendung sein, aber da kann ich nur raten ...

Gruss, Heinileini


----------



## Ratoncito (29 November 2020)

Sorry, aber die Zeile

Dummy := _ARRAY_SHUFFLE(pt:=ADR(Reihenfolge), size:=7);

meldet einen Fehler, den ich nicht beseitigen kann.

Programmname, Funktion oder Funktionsbausteininstanz an Stelle von '_ARRAY_SHUFFLE [...]

???


----------



## hucki (29 November 2020)

Ratoncito schrieb:


> Sorry, aber die Zeile
> 
> Dummy := _ARRAY_SHUFFLE(pt:=ADR(Reihenfolge), size:=7);
> 
> meldet einen Fehler, den ich nicht beseitigen kann.


Ich tippe mal auf eine OSCAT-Funktion, die Du erst einbinden musst.


----------



## JSEngineering (29 November 2020)

JSEngineering schrieb:


> Du kannst auch versuchen, ein Array 1..7 anzulegen und dieses in jedem Durchgang über *_ARRAY_SHUFFLE aus Oscat* durcheinanderzuwürfeln. Damit kommst Du eigentlich am Schnellsten zum Ergebnis. Hab ich gerade erst gesehen, daß es diese Funktion gibt...



Richtig....


----------



## Heinileini (29 November 2020)

JSEngineering schrieb:


> Du kannst auch versuchen, ein Array 1..7 anzulegen und dieses in jedem Durchgang über _ARRAY_SHUFFLE aus Oscat durcheinanderzuwürfeln. Damit kommst Du eigentlich am Schnellsten zum Ergebnis.


Das ist doch genau das, was das Progrämmle in meinem Beitrag tut, allerdings mit einem Array 0..6 und ohne Oscat! Es "shufflet" die 7 Elemente durcheinander, gesteuert durch 2 Zufallszahlen.
Wie man die Zahlen 1..7 den Aktionen A .. G zuordnen könnte (per CASE), das habe ich vorhin nachgereicht.
Da ich nichts über diese Aktionen weiss, habe ich allerdings ziemlich viel offen gelassen.
Je nach dem, was sich hinter den Aktionen A .. G verbirgt, könnte man natürlich ein zweites Array oder eine zweite Dimension oder ein Array of Struct bemühen.
Oder ein array of character, gefüllt mit den Buchstaben A .. G, gründlich durchshufflen.
ABER für das Sortieren sehe ich absolut keinen Anlass! Warum erst Chaos schaffen, nur um es dann wieder fein säuberlich zu ordnen??? 

@Dagobert
Na, sooo aufwändig finde ich das Ermitteln der nächsten Zufallszahl durch 1-mal Multiplikation mit Primzahl 997 und 1-mal MOD 1 auch wieder nicht!
(Dieses Verfahren habe ich übrigens aus einem Büchlein zum hp97 geklaut).


----------



## JSEngineering (29 November 2020)

Sorry Heinileini,

hatte mir Deinen Code nicht angesehen :-(

Mein erster Ansatz war ein Anderer: Die Zufallszahl legt die Reihenfolge fest. Daher das Sortieren: Der Buchstabe mit der größten (alternativ kleinsten) Zufallszahl wird zuerst ausgeführt.

Schlußendlich sind wir dann aber wohl beide beim gleichen Vorgehen gelandet... habe eben einen Umweg genommen ;o)

Ich hoffe, Du vergibst mir...


----------



## Ratoncito (29 November 2020)

Hallo,

sorry, wenn ich hier so viel Unruhe stifte...

Ich hatte mich heute morgen an der Version von Heinilein versucht, und bin mal wieder gestrauchelt. Daher meine Fragen dazu.

Dann kam der verlockende übersichtliche Code aus #17, dem ich nicht widerstehen konnte...
Dass ich dort einen Oscat-Baustein brauche, war nicht zu erkennen.
(Ich würde es lieber ohne diesen Baustein machen, bei Oscat habe ich noch nie das gefunden, wonach ich gesucht habe - egal)

Ich würde jetzt gerne die Version von Heinilein bis zum Ende durchziehen, egal wie kompliziert es wird.
Immer wenn ich auf die Nase falle, lerne ich auch was.

Nochmal eine kurze Beschreibung was ich machen möchte:
Einmal am Tag (könnte man durch die Uhrzeit machen) möchte ich die Reihenfolge von 7 Aktionen (A ... G) per Zufall festlegen.
Die erste Aktion wird dann ausgeführt und startet am Ende nach einer Wartezeit die Aktion 2, diese dann 3 usw.


----------



## Heinileini (29 November 2020)

JSEngineering schrieb:


> Schlußendlich sind wir dann aber wohl beide beim gleichen Vorgehen gelandet... habe eben einen Umweg genommen ;o)


Da gibt es doch nichts zu verzeihen!
Wir beide haben lediglich unterschiedliche Umwege genommen: Du den Umweg über Oscat und ich den Umweg über DIY (= Selbermachen). 

Übrigens: den Umweg über Selbermachen finde ich gar nicht sooo verkehrt und sooo umständlich, zumal das, was ich unserem "PLC-Erstklässler" bisher zugemutet habe, noch recht gut nachvollziehbar sein dürfte. Er ist ja nicht dumm und er hat recht konkrete Verstellungen, was er wie in Angriff nehmen möchte. 
Ein Kollege sagte mal, "Die PLC hat für alles mögliche Befehle, aber die paar Befehle, die ich brauche, die gibt es nicht!". Aber das, was er eigentlich wollte, das liess sich alles mit den paar, mickrigen Befehlen der PLC und ein klein wenig Gehirnschmalz problemlos realisieren. 
Ich finde, PLC-Erstklässler sollten nicht ausschliesslich und nicht vorrangig darauf gedrillt werden, die passende Bibliothek und darin die passende Funktion zu finden. Das können sie sich dann immer noch aneignen, wenn sie die Möglichkeiten exemplarisch erstmal durchgekaut haben und verstehen, was sie da tun bzw. tun können/könnten. Z.B. eine FlankenErkennung sollte man unbedingt zuerst selber machen und verstehen, bevor man dazu übergeht, vorgefertigte FlankenErkennungen zu benutzen. 

PS:


> Immer wenn ich auf die Nase falle, lerne ich auch was.



Genau das meine ich doch, Wolfgang!


----------



## hucki (29 November 2020)

Heinileini schrieb:


> Das ist doch genau das, was das Progrämmle in meinem Beitrag tut,...


Mehr Kommentare in Deinem Code wären IMHO gut, um sich Deinen Code intensiver anzusehen und Deinen Gedankengängen folgen zu können.
Und auch die Auswahl der Variablennamen als Abkürzungen empfinde ich beim Lesen des Codes nicht gerade als selbst redend.

Es steht halt nicht jeder auf Deinem Programmierniveau nach >40? Berufsjahren. 
Ich persönlich z.B. komme mir da immer vor, als hätte ich gerade erst das boolsche AND kennen gelernt.


----------



## Ratoncito (29 November 2020)

Hallo,



> > Immer wenn ich auf die Nase falle, lerne ich auch was.
> 
> 
> Genau das meine ich doch, Wolfgang!



Im Moment schmerzt mein zartes Näschen aber schon ein wenig 



> Mehr Kommentare in Deinem Code wären IMHO gut, um sich Deinen Code  intensiver anzusehen und Deinen Gedankengängen folgen zu können.
> Und auch die Auswahl der Variablennamen als Abkürzungen empfinde ich beim Lesen des Codes nicht gerade als selbst redend.



Ja, das stimmt schon. Aber wenn es funktioniert brösel ich so etwas auf, füge meine Bezeichnungen ein und dann folgt oft ein Aha-Effekt.



> Übrigens: den Umweg über Selbermachen finde ich gar nicht sooo verkehrt  und sooo umständlich, zumal das, was ich unserem "PLC-Erstklässler"  bisher zugemutet habe, noch recht gut nachvollziehbar sein dürfte. Er  ist ja nicht dumm und er hat recht konkrete Verstellungen, was er wie in  Angriff nehmen möchte.



Freut mich zu hören. Leider verrenne ich mich oft in zu komplizierte Ansätze.
Codesys ist wirklich komplett neu für mich, hat aber Ähnlichkeiten zu anderen Programmiersprachen. Was ich besonders vermisse sind gescheite Tutorials zu den Grundlagen. Und eine PLC hat nochmal so ihre ganz besonderen Eigenarten, die zumindest bei mir immer wieder für Überraschungen beim Programmablauf sorgen.

Danke für Eure Geduld


----------



## Heinileini (29 November 2020)

hucki schrieb:


> Mehr Kommentare in Deinem Code wären IMHO gut, um sich Deinen Code intensiver anzusehen und Deinen Gedankengängen folgen zu können.
> Und auch die Auswahl der Variablennamen als Abkürzungen empfinde ich beim Lesen des Codes nicht gerade als selbst redend.


Danke für die konstruktive Kritik, hucki!

Ich bin zwar nur zugereister Lipper ("Immigrant"), aber trotzdem (zu) "sparsam". Das ist mir zwar bewusst, aber in dem Moment, in dem ich es hinschreibe (oder auch nicht), stecke ich gedanklich noch zu gut drin, um überhaupt zu merken, welcher Hinweis noch nützlich sein könnte für jemanden, der sich einliest.
Aber jeden einzelnen Befehl zu kommentieren, z.B. 'iIdx := iIdx + 1 ; // Index vom Typ INT inkrementieren', werde ich mir wohl auch weiterhin verkneifen. 
Gilt übrigens auch für iIdx += 1 ; 

Bezüglich der selbst redenden VariablenNamen bevorzuge ich (leider?) kurze Namen und "kryptische" Abkürzungen. Lange Namen mag benutzen wer will, mich halten sie eher auf bzw. davon ab, die "Struktur" dessen, was dort steht, zu erfassen. Aber ich werde versuchen, etwas spendabler damit umzugehen.

Rückfragen und auch Kritik und VerbesserungsVorschläge sind aber weiterhin und jederzeit willkommen!

Gruss, Heinileini


----------



## Onkel Dagobert (29 November 2020)

Ratoncito schrieb:


> .. Aber wenn es funktioniert brösel ich so etwas auf, füge meine Bezeichnungen ein und dann folgt oft ein Aha-Effekt...


Ich bin davon überzeugt, dass Heinileinis Code funktionieren wird. Statt des Aha-Effekts kommt bei mir aber immer nur der Oooh-Effekt. Ich bin mal auf deine Interpretation gespannt.


----------



## Heinileini (29 November 2020)

Moin Wolfgang,
ich denke, ich habe Dein Anliegen verstanden, obwohl die Variablen bzw. Aktionen A .. G noch etwas nebulös sind.

Kannst Du denn etwas dazu sagen, wie die Schnittstelle zu diesen A .. G Dingern aussieht bzw. angedacht ist, dann könnte man etwas konkreter darauf eingehen.
Ich gehe z.Z. noch davon aus, dass nacheinander mit eingestreuten Pausen und in zufälliger Reihenfolge alle 7 Aktionen jeweils nur 1-mal angestossen werden sollen.
Und davon, dass Du z.B. nur einmalig pro Tag diese zufällige Sequenz (z.B. Öffnen der Rollläden) anstossen willst. 
(Willst Du evtl. auch die eingestreuten Pausen in zufälliger Länge variieren?)

Mit dem Stichwort "Pause" bestätigst Du mich in der Annahme, dass das alles über viele Zyklen verstreut angeleiert werden und passieren soll.
Deshalb findest Du in meinem Vorschlag die CASE-Selektion nicht in einer FOR-Schleife.
Die Schleife ist die EndlosSchleife des BetriebsSystems, die zyklisch dafür sorgt, dass das PLC-Programm immer wieder durchlaufen wird.
Der SchleifenIndex wird also "zu Fuss" gemacht.

Generell: Trau Dich! Dumme Fragen gibt es nicht!

PS:



Onkel Dagobert schrieb:


> Statt des Aha-Effekts kommt bei mir aber immer nur der Oooh-Effekt.


Du darfst Dich auch trauen, Dagobert! 
Du hättest ruhig sagen können, der "Oooh-oooh-oooh-Effekt!


----------



## hucki (29 November 2020)

Onkel Dagobert schrieb:


> Ich bin davon überzeugt, dass Heinileinis Code funktionieren wird. Statt des Aha-Effekts kommt bei mir aber immer nur der Oooh-Effekt.


Da hab' ich auch so gar keine Zweifel.
:TOOL:






Heinileini schrieb:


> Aber jeden einzelnen Befehl zu kommentieren, z.B. 'iIdx := iIdx + 1 ; // Index vom Typ INT inkrementieren', werde ich mir wohl auch weiterhin verkneifen.
> Gilt übrigens auch für iIdx += 1 ;



Bei den Kommentaren geht es doch oft weniger um das WAS als vielmehr um das WARUM, oder?

Hier z.B.:


Heinileini schrieb:


> ```
> ...
> 
> // - - - < Idx1 bilden > - - -
> ...


Was Du da machst, sehe ich.
Aber warum Du das machst und wofür Idx1 oder die Zwischenvariablen benötigt werden, erschließt sich mir so nicht direkt.



PS: 
Ich hab' mich allerdings auch noch nicht wirklich mit Deinem Code beschäftigt. Halt nur drüber weg geschaut. 
Aber genau so ergeht es mir auch oft mit eigenem Code und zeitlichem Abstand zur Erstellung, wenn ich nicht "kleinlich" kommentiere.


----------



## Ratoncito (29 November 2020)

Hallo Heinileini,

Ich habe das Ding gerade mal ans Laufen bekommen. Es funktioniert, aber nicht so, wie ich mir das vorgestellt habe. Es läuft, glaube ich, in eine falsche Richtung.

Bemühe Dich bitte nicht, ich melde mich mit konkreten Fragen.

Danke - wird etwas dauern. Es ist Kaffeezeit


----------



## Heinileini (29 November 2020)

hucki schrieb:


> Aber warum Du das machst und wofür Idx1 oder die Zwischenvariablen benötigt werden, erschließt sich mir so nicht direkt.


Stimmt schon, hucki, das ist zu kurz gekommen.
grR ist das Ergebnis der hier realisierten Random-Funktion.
grR soll Werte m Bereich > 0.0 und < 1.0 annehmen.
Um den nächsten Random-Wert zu erhalten, wird mit 997 mulitpliziert.
Aber dadurch entstehen auch "VorkommaStellen", die wieder plattgemacht werden sollen.
Beim hp97 gab es dafür die Funktion 'Frac'.
'grR := FRAC(grR * 997.0)' ist das, was ich eigentlich tun möchte. Notwendigkeit für ZwischenVariable: Null,Nix.

In CodeSys habe ich nichts Entsprechendes gefunden. Auch nicht die INT-Funktion, die die NachkommaStellen plattmacht und die man von diversen Sprachen her gewohnt ist.
Auf diesem Umweg hätte man 'Frac := rX - INT(rX)' realisieren können. Also von der kompletten Zahl mit Vorkomma- und Nachkomma-Stellen die um die NachkommaStellen beschnittene Zahl subtrahieren. Bleiben die NachkommaStellen.

Habe, um INT zu "workarounden", also die NachkommaStellen abzuschneiden, GleitkommaZahl in Ganzzahl und dann zurück Ganzzahl im GleitkommaZahl missbraucht (in der Hoffnung, dass CodeSys dabei - anders als die LOGO! - nicht auf die Idee kommt, zu runden).

Wenn es denn die MOD-Funktion auch für REAL-Zahlen gäbe, hätte ich 'grR := (grG * 997.0) MOD 1.0' geschrieben. Hier auch kein Bedarf für eine ZwischenVariable.

Ja, warum die ZwischenVariable. Hätte ich's ohne ZwischenVariable programmiert ('grR := grR * 997.0 - Int_To_Real(Real_To_Int(grR * 997.0))') , wäre bestimmt die Frage gekommen, warum ohne ZwischenVariable! 
Meine Argumente für die ZwischenVariable:
- weil das Ergebnis der Multiplikation anschliessend mehr als einmal (nämlich zweimal) benötigt wird
- weil man beim Testen manchmal auch ein ZwischenErgebniss sehen/kontrollieren möchte.

Zu den beiden Indizes. Diese werden anschliessend benötigt, um zwei zufällig ausgeguckte Elemente des Array zu tauschen. 
grR hat, wie gesagt, den WerteBereich > 0.0 bis < 1.0 und für die Indizes benötigen wir die Zahlen 0 .. 6. Die dritte Zeile skaliert die ZufallsZahl dementsprechend.

Wird für beide Indizes zufällig derselbe Wert geliefert, so wird auf den ersten IndexWert eine 3 addiert und das Ergebnis per MOD 7 wieder in den zulässigen Bereich 0 .. 6 "umgebogen".
Es ist auf diesem Wege nicht nötig, solange Zufallszahlen zu bilden, bis mal eine kommt, die für Index2 eine andere Zahl liefert, als Index1 zugewiesen wurde.

Es würde aber auch nichts schlimmes passieren, wenn beide Indizes mal denselben Wert hätten. Dann würde überflüssigerweise ein ArrayElement gegen sich selbst ausgetauscht.

Ja, das passiert mir auch: wenn ich nach einiger Zeit versuche, meinen Code zu enträtseln, dann merke ich natürlich auch, wo überall Kommentare fehlen.


----------



## Thomas_v2.1 (29 November 2020)

Heinileini schrieb:


> Hier ganz viel gänzlich ungetesteter PseudoCode:



Ich bin gerade schwer am rätseln, was zum Teufel du dir da ausgedacht hast. Vor allem warum du da mit Real-Zahlen herumrechnest. Nur zu denken "das sieht aber wild aus" führt nicht unbedingt zu Zufall, oder zumindest Pseudozufall.


----------



## Onkel Dagobert (29 November 2020)

Oooh-oooh-oooh  !


----------



## Thomas_v2.1 (29 November 2020)

Vielleicht habe ich da ja was falsch programmiert, aber an Index 0 kommt damit immer die Folge 5, 6, 7, 1, an Index 1 immer die Folge 2, 3, 4 usw. Muss ja jetzt nicht kryptografisch sicher sein, aber für den Aufwand könnte da ein bisschen mehr Zufall kommen ;-)


----------



## Heinileini (30 November 2020)

Thomas_v2.1 schrieb:


> Muss ja jetzt nicht kryptografisch sicher sein, aber für den Aufwand könnte da ein bisschen mehr Zufall kommen ;-)


Ich hab's in CodeSys nicht getestet, Thomas, aber jetzt in VBA versucht, nachzuvollziehen.
Hier die ersten 1000 Ergebnisse, wobei 77 Ergebnisse wiederholt auftraten. Der Startwert waren die Nachkommastellen von Wurzel aus 2. 

```
Die ersten 1000 Ergebnisse: 2345176 6451732 4516327 1563274 5672341 2763415 6734152 7143526 3415267 4162573 6125734 3257146 2471563 4725631 7254316 2534167 5314672 6143725 1537246 5342761 3421675 6214753 2157436 4571362 5743621 7536214 4362157 5621374 3216745 2467153 4671235 3712654 7146523 1463257 7632541 7325416 3254761 2567413 5674231 6742351 6423517 4235671 7356214 3462157 6421573 4125736 7251364 5213647 2536471 4365712 3657421 6174253 1746532 7465231 1652347 6123475 1734256 7342516 3427165 4276153 2361574 3415762 4257613 2571634 5736142 1367425 3671254 6732541 7523416 5234176 2345761 3475612 6754123 7541263 5214637 7146325 1463527 4653271 4532716 5327614 3256147 2531476 5413762 2137645 1346752 3267541 3675412 6753124 7513246 5137462 1324675 2346751 3167542 1673425 7352461 5324617 3245176 2651743 6217435 2176354 1764532 7642351 6523417 5634172 2341765 3517642 5671423 6754231 7642315 3426157 4271563 2714635 7641352 5413627 4536271 5162734 1624375 6743251 7234516 2346157 3561472 5416723 4176235 1762453 762
3541 6235471 2345716 3457612 1576423 4765231 7452316 4526137 1265374 2635741 6537412 5374621 7346215 3621754 2617543 1675432 5764321 7613245 6134257 1352476 3542761 5627413 6374125 5741236 7462315 4723156 7231654 2317546 3175426 1734265 7312654 3146527 1462573 5624731 3247615 6472153 4726531 7365214 5632147 6371425 3714265 3142657 1326574 3265471 2657413 6534172 5342716 3724165 6241753 2714536 4175362 1723654 4236571 2165734 7651342 1563427 5624371 1436257 4762531 5627314 5273146 2761435 7624351 6342517 3427156 4172563 1527634 5273641 6732415 7524136 5246317 2763145 7361452 3624517 6245137 2453176 4531267 5321674 3716245 7162354 1623457 5234671 2346517 1465372 3654721 4567213 3672154 7621543 6125437 4251376 2517364 5173624 7136245 1367452 5674321 6143275 1632754 6321547 1235476 2147653 1576432 6754321 7543261 4532617 5623174 6231475 2413756 4173562 1765324 7654231 6542713 5426137 4261357 2653174 6541732 5714326 7243165 2341657 3516472 5264713 2746135 7416352 4165327 3651274 7512643 7261345 2653417 6134572 13
54726 3147265 1452673 3526741 5261473 2615734 6127345 6273451 2743516 7345162 3541627 5476213 4162735 1657324 3576241 2765413 7456132 4567321 5613274 6134725 1374256 3247561 2475163 7451632 4513627 5134276 1352764 3427651 4276315 4631752 6327514 4275136 2715364 7152643 1536427 5374261 3745612 7451623 4316257 3762514 6725143 7261435 2654317 6543127 5413276 4632715 6327145 3471256 4752163 7251634 2513647 5163472 1635724 6357142 6571423 5716234 1627543 6215437 2754316 7534162 6341527 2415376 4135762 6375241 6752413 5724136 7241356 2453167 4571632 5316724 1427653 4576231 5742316 7432165 5321647 3261475 2614735 6137452 6374521 3645217 7526431 5264137 7641325 6713254 7532146 1325467 3754621 6547213 5372146 3726415 7264135 5641327 6413572 4137526 2375164 4751632 3516724 5147263 1473625 4763251 7642513 6725134 1257346 2543761 5432617 4321675 3612754 6172543 1625437 7546213 5461237 4216375 2164753 1347562 7435621 4352617 3516274 3162745 1627435 6254371 6435172 4153726 3517264 2715436 7153462 1537624 5376142 2314756 5
147362 1463725 4537261 4372615 4265173 1652734 6547321 5374216 4732165 7351624 2516347 5173462 6734125 1347256 3471562 4713625 7132654 1426537 4562371 5613724 1637245 2534716 5347126 3475261 2574136 5241367 2431675 4613752 6132574 1325647 3756421 5463127 4731265 1372654 3762541 7125463 6254137 4521376 5216734 1267345 1673452 6714523 7345216 3254167 2514673 5641732 6417235 4152376 1523746 7235461 2154637 3546172 2461753 4617235 6132754 1527346 5723461 7234651 2436517 4361572 7615324 6152347 1527436 5271364 2731645 7326451 3214567 2175643 1756234 7542361 5243617 2435176 4352761 5176342 1763245 7236451 6324517 3145276 1452736 4523761 1237654 2736541 3657214 6571243 5742136 3421765 4217563 2135674 1354762 4537621 5176234 1765342 7613425 5134267 3142675 1726453 3264571 2145763 1467532 3675421 6714253 6142537 5421376 2413765 4157632 1573624 6735241 7152436 1534267 5243671 4365127 2651374 7513642 5436127 4351276 4512763 5476321 4762315 7523164 5234617 7346125 3416257 1325674 3156742 1564723 5617234 6372145 3751426 
2514763 5142637 1436275 3627514 6275413 2754631 7146352 1364527 3546271 5462731 4527316 5723164 7213645 6132457 1342576 3245761 6457213 3572146 5761423 7615234 6172345 7263541 6235417 2754136 7546312 5423167 4271635 6712354 4123576 1253764 2637541 6475312 4753621 6537214 5672143 6731425 4317256 3127564 1375642 6753421 7532416 5327164 3274615 2746513 7465312 4653721 6137254 1362547 2635471 6254713 2517436 5473621 3746215 7462351 4326517 5641372 5413726 2137465 1347652 3416527 3165274 1672543 6725341 7352416 3521467 5214376 2743165 7421653 5216437 2163475 1674352 7632514 3275416 5724163 7341625 4263571 4635712 6354127 7541236 5421367 4213657 4136572 7653142 6531472 5314627 3126475 1462753 4327561 1275634 2716345 7463152 4621537 3215674 2136745 3654271 6542317 5432176 3716254 5162743 5627431 5274316 2743615 4361527 2156347 2563471 6741325 6413257 2134576 3546721 5367214 3472165 4521673 5216374 7163245 1532467 6245713 2457316 4576132 5461327 4316275 1372546 3725416 7154263 1245637 2546371 5643712 7321456 5214367
 1426573 6245731 6457312 4753126 7531624 5613247 6172435 1764352 7463521 2635417 3541726 5467213 2674135 4761352 7316524 3465217 4562173 5321764 2163457 1654372 6523741 5437216 4672135 6721453 4217536 2165374 1635742 6327451 3264517 2645713 2457136 4517362 5137624 1346275 3562741 5637412 6375124 2751346 7543162 5231647 2361475 3641752 5417623 6174235 1724356 7241563 2451637 4516732 5147326 4173265 2316547 1562743 5127436 1275364 2735641 7326415 3164257 1632574 2365741 2574613 5647132 6475321 4153276 6527341 5372416 6724135 7251346 2573461 5437612 4356127 7561234 5612374 5234716 2367145 3617452 6574123 5241736 2417356 4176532 1745326 1453267 4132675 1376254 3672541 6325417 3154276 1342765 3472651 4725613 6541327 5423176 4132765 6327154 3571246 5612473 6421735 4153762 1567324 5673214 6132745 1627453 6275431 2154376 4513762 5147623 6471235 4762351 6723514 7235416 2314567 3145627 1456237 2564371 1643752 6417523 4175263 1762534 7625431 1254367 2743651 7436215 4162357 1624573 6345721 4357216 3574162 2741653 721653
4 2765341 7613452 6135427 1254376 2541763 5617432 6174352 1473526 4735162 2351674 1627534 6245371 2153746 4537162 2371654 4716532 7164325 7643251 6452317 7523146 5237461 2371645 5716432 5164327 1647235 6721534 3215746 2154763 1247635 2576341 6257341 2173456 6745321 4753216 7542163 5421736 4271365 2713564 5137642 1375426 3764251 2516374 5173642 1637425 6473251 4736512 1365724 3675241 6452713 4327156 3217564 7125643 1253467 5146372 1263745 1637452 4376521 3265714 2654173 6541237 5712346 7123456 1264537 4625371 6253174 7531246 5316427 3174265 1642753 6472531 4735216 7342165 1423657 4236517 2365714 3457162 5614237 1526743 1267435 2671354 5713642 7136452 7364521 3625417 6234175 6417352 4163527 1635247 3524167 5142673 1246735 4672531 6125374 6253741 5341762 2417635 4576312 5713624 1467352 3745216 7652143 7234156 2371564 4715632 7456321 5463217 7621354 4213567 5132674 5264731 2617345 6573412 5736124 7351246 3542167 1425673 4256713 2547136 5471326 4716235 7162345 2613457 1634572 7345621 3654217 6542371 5463712 46317
25 1367254 3652741 3527416 5214763 2147536 1675342 6723451 7264513 2635147 4351672 5764213 7645132 6351427 3514267 5146273 5637241 3761452 7416523 6752314 6523147 3251476 6514723 5146237 1472365 4623751 7236514 2365417 3645172 6421753 4237516 4375162 


77 Wiederholungen bei den Ergebnissen: 95 139 184 227 254 287 315 323 331 342 372 384 388 393 399 417 421 429 472 482 511 518 525 535 554 585 590 591 592 609 611 615 623 636 640 646 648 651 655 663 669 676 679 690 699 732 734 743 748 758 776 784 827 828 844 850 851 854 864 865 866 879 903 905 914 918 922 926 931 933 936 942 946 976 982 984 987
```
Dabei sind die Inhalte der 7 ArrayElemente jeweils zu einer 7-stelligen Zahl zusammen gefasst.

Was ich mir bei den RealZahlen gedacht habe? Eigentlich nichts (Böses). Ich weiss doch, dass man bei vielen PLC-Anwendungen mit Ganzzahlen besser bedient ist. Aber bei meiner Beschäftigung mit Oscat ist mir aufgefallen, dass ich nicht der einzige bin, der RealZahlen nicht grundsätzlich aus dem Wege geht. 

Gruss, Heinileini

PS:
Ich glaube, ich weiss jetzt, was schief gelaufen ist und den Zufall zu Fall gebracht hat, Thomas! 
Der Inhalt der Variable grR muss von der einen bis zur nächsten ZufallsZahlenBildung erhalten bleiben (static oder global oder ...) und "vernünftig" vorbesetzt worden sein.
Sonst kommt es zu kurzen WiederholZyklen, ohne die Bildung von ZufallsZahlen. Das Rotieren des Array sorgt dann zwar noch für ein wenig "Bewegung" und das Tauschen immer derselben ArrayElemente bringt dann auch keinen nennenswerten Schub an Dynamik!
Mir war schon immer ein Rätsel, wie sich bei CodeSys die Deklarationen für Static und Temp unterscheiden und bin mal belehrt worden, dass alle statisch sind und es temporäre im Sinne von Simatic nicht geben soll ... ?


----------



## Ratoncito (30 November 2020)

Hallo,

was habe ich nur angerichtet? 
Nach den Erklärungen in #33 kann ich in etwa nachvollziehen, was gemacht wird. Ohne die Erklärungen hätte das nichts gegeben. Und an eine Umsetzung in einen funktionierenden Code brauche ich garnicht zu denken. 

Den größten Fehler habe ich schon bei der Frage mit dem Wort Zufallsgenerator gemacht. Bevor ich hier eine Frage stelle, geistert mir die Aufgabe lange durch den Kopf und ich suche nach Lösungen in der Richtung, die ich für zielführend halte. So läuft es dann manchmal ein wenig aus dem Ruder.

Daher nochmal eine Beschreibung was ich machen möchte:
Einmal am Tag soll eine Reihe von Aufgaben (A ... G oder 1 bis 7) in zufälliger Reihenfolge ausgeführt werden. 
Zwischen dem Start einer Aufgabe und der nächsten sollten 1 bis 5 Minuten Wartezeit liegen (das ist mir gerade noch eingefallen).

Das Durcheinanderbringen der Reihenfolge ist möglicherweise anders leichter zu bewerkstelligen als über Zufallszahlen. Es tut mir Leid, wenn ich Euch auf einen falschen Weg gelockt habe.

Ich wünsche allen einen guten Start in die neue Woche.


----------



## Heinileini (30 November 2020)

Ratoncito schrieb:


> Daher nochmal eine Beschreibung was ich machen möchte:
> Einmal am Tag soll eine Reihe von Aufgaben (A ... G oder 1 bis 7) in zufälliger Reihenfolge ausgeführt werden.
> Zwischen dem Start einer Aufgabe und der nächsten sollten 1 bis 5 Minuten Wartezeit liegen (das ist mir gerade noch eingefallen).


Moin Wolfgang,

wer bestimmt die Länge der Pausen 1 .. 5 Minuten? Auch der Zufall? Oder folgt auf eine Aufgabe immer eine Pause von derjenigen Länge, die der Aufgabe festzugeordnet ist?

Du hast jetzt ein Array mit 7 Elementen, die die Zahlen 1 .. 7 in sich ändernden Reihenfolgen enthalten. Wo genau liegt Dein Problem, daraus etwas zu machen?
Die CASE-Selektion ist Dir nicht nur von mir als HilfsMittel nahegelegt worden (kann man aber auch ohne CASE mit IF ... ELSIF ... u.s.w. machen und evtl. auch mit einem weiteren Array).
Wie sieht es mit den 7 Aufgaben aus? Wie kann man sie anstossen/starten? Wie kann man abfragen, ob/wann sie fertig geworden sind? Kannst Du dazu etwas sagen oder wartest Du auf unsere Vorschläge?
Willst Du evtl. die 7 Aufgaben (und die Pausen dazwischen?) in die CASE-Selektion integrieren oder sollen diese Aufgaben zusätzlich auch anderweitig (z.B. manuell) gestartet werden können?

Dir auch einen guten Start in die neue Woche!

Gruss, Heinileini


----------



## Thomas_v2.1 (30 November 2020)

Heinileini schrieb:


> Was ich mir bei den RealZahlen gedacht habe? Eigentlich nichts (Böses). Ich weiss doch, dass man bei vielen PLC-Anwendungen mit Ganzzahlen besser bedient ist. Aber bei meiner Beschäftigung mit Oscat ist mir aufgefallen, dass ich nicht der einzige bin, der RealZahlen nicht grundsätzlich aus dem Wege geht.



Die einfachsten Zufallsgeneratoren sind absolut simpel von der Berechnung her und benötigen nur eine Variable als Speicher, siehe: https://en.wikipedia.org/wiki/Linear_congruential_generator
Ich dachte bei dir schon fast du hast da einen Mersenne-Twister gebaut, wohl eines der besten Pseudozufallsgeneratoren.


----------



## Ratoncito (30 November 2020)

Hallo Heinileini,

vielen Dank für Deine Geduld.

Damit nicht immer wieder Zwischenfragen nötig sind sind schreibe ich jetzt mal einen Roman.

Zur Aufgabe
Ich möchte die Rolladen in täglich unterschiedlicher Reihenfolge öffnen und schließen. Der Start ist die Uhrzeit, die aus dem Thema "Startzeit an die Jahreszeit anpassen" stammt. Da Du konkret danach fragst, die Pausen zwischen den Aufgaben sollten zwischen 60 und 300 Sekunden sein. Immer unterschiedlich wäre super.

Nun zum Rest
Sicherlich ist das alles nicht unbedingt nötig, und über sinnvoll oder nicht kann man unterschiedliche Ansichten haben. Wie ich schon geschrieben habe, dient die neue Steuerung als Ersatz, für eine alte Micro, die nicht mehr mit mir kommunizieren wollte. (Nur am Rande, jetzt wo sie abgebaut ist, hat sie es sich anders überlegt, und ich kann sie wieder aufrufen.)
Dort hatte ich damals einen Wust integriert, der ein paar Lampen einschaltete und die Rolladen in unterschiedlicher Reihenfolge herunterfuhr. Das hat, wenn wir nicht zuhause waren immer den Eindruck erweckt, das das Haus nicht unbewohnt war. (Unser Haus war mal sieben Jahre gänzlich unbewohnt)
Da ich damals statt strukturierter Programmierung eher nach dem Motto "ein Genie beherscht das Chaos" etwas zusammengezimmert habe, blicke ich da eh nicht mehr durch.

Da ich die neue Steuerung innerhalb kurzer Zeit in Betrieb genommen habe, möchte ich, nachdem die Grundfunktionen laufen, diese um solche Annehmlichkeiten erweitern.

Dabei versuche ich zuerst eine eigene Lösung zu finden und probiere vieles aus. Leider werden mir meistens schnell meine Grenzen aufgezeigt. Durch meine ungenauen Fragen läuft es dann oft in die falsche Richtung.

Die Aufgabe habe ich oben nochmal konkret beschrieben. 
Nun gibt es immer viele Wege, die zum Ziel führen. Wenn ich das richtig sehe, sind es zwei unterschiedliche Dinge, einmal die Reihenfolge, die täglich durcheinander gebracht werden müsste, und einmal die Werte für die Pausen erzeugen.

Wenn es zwei unterschiedliche Dinge sind habe ich eine Bitte. Ich möchte mit der Reihenfolge beginnen, die mit der Variablen Start=True beginnt und die Aufgaben mit A ... G in eine beliebige Reihenfolge bringt. Wenn es geschickter ist, beides zu kombinieren, dann bitte zusammen.

Danke für Euer Verständnis


----------



## Heinileini (30 November 2020)

Thomas_v2.1 schrieb:


> Die einfachsten Zufallsgeneratoren sind absolut simpel von der Berechnung her und benötigen nur eine Variable als Speicher, ...


Ja, genau, Thomas. Woher dann die riesen Aufregung?
"Mein" (wie bereits gesagt: von hewlett-packard "geklauter") ZufallsZahlenGenerator ist doch absolut simpel und benötigt nur 1 (i.W.: eine) Variable als Speicher!
In der VBA-Schreiweise:

```
xG# = xG# * 977
    xG# = xG# - Int(xG#)
```
Und eine mögliche Initialisierung ("randomize") dazu, ebenfalls in VBA:

```
xG# = 2 ^ 0.5
    xG# = xG# - Int(xG#)
```
Wobei der Anteil 'xG# = xG# - Int(xG#)' durchaus verzichtbar ist und mehr der "Schönheit" dient. Verzichtbar, weil folgende ZufallsZahlenBildungen trotzdem funktionieren. Schönheit insofern, als bereits hier das ZahlenFormat "ohne VorkommaStellen" produziert wird, wie es nach dem Durchlaufen einer ZufallsZahlenBildung auch vorliegt.

Dass meine Workaround-Verwurstelung der in CodeSys von mir nicht gefundenen INT-Funktion recht unschön aussieht, bitte ich zu verzeihen - in CodeSys bin ich wahrscheinlich nicht einmal Erstklässler. 

Sooo, nun zurück zum eigentlichen Thema. 
Wir wollen nicht vordergründig eine möglichst zufällige Folge der Ziffern 1 .. 7 bilden, sondern Kombinationen aus den Ziffern 1 .. 7.
Diese Kombinationen sollen jede dieser Ziffern genau 1-mal enthalten und die gefundenen Kombinationen sollen sich von Mal zu Mal unterscheiden und uns nach Möglichkeit mit Wiederholungen verschonen. Allerdings können Wiederholungen trotz und wegen aller Zufälligkeit dennoch auftreten. Das ist nicht per se ein Makel.
Mein Ansatz war, ein Array mit 7 Elementen vorzugeben (und ja, dieses ganze Array zu speichern) und zu "shufflen", wofür ich eine Rotation der Elemente um jeweils 1 Platz vorgesehen habe und
ein Tauschen von zwei Elementen. Hier kommen die ZufallsZahlen wieder ins Spiel: sie bestimmen, welches der Elemente gegen welches andere Element getauscht wird.

Um Wolfgang ein wenig Abwechslung zu gönnen, habe ich bei der Vorgabe des Startwertes für den ZufallsZahlenGenerator ein wenig Luxus eingebaut (und nur für diesen Zweck eine weitere Variable spendiert, die gespeichert werden muss). Mit dieser Mimik werden der Initialisierung ("Randomize") wechselnde PrimZahlen (2 .. 32749) vorgegeben, aus der sie die QuadratWurzel zieht ...

Gruss, Heinileini


----------



## Heinileini (30 November 2020)

Ratoncito schrieb:


> Zur Aufgabe
> Ich möchte die Rolladen in täglich unterschiedlicher Reihenfolge öffnen und schließen. Der Start ist die Uhrzeit, die aus dem Thema "Startzeit an die Jahreszeit anpassen" stammt. Da Du konkret danach fragst, die Pausen zwischen den Aufgaben sollten zwischen 60 und 300 Sekunden sein. Immer unterschiedlich wäre super.


Sooo, jetzt wieder zu Dir, Wolfgang!

Vor lauter Zufallerei bin ich ja gar nicht mehr dazu gekommen, auf Dich einzugehen. 
Wie wär's, wenn wir die Aktivierung eines Rollladen und die Wartezeit gleichzeitig starten? Die Wartezeit (60..300 s) dürfte dürfte wohl lang genug sein, so dass der Rolladen mit seiner Aktion längst fertig ist, bevor die Zeit abgelaufen ist. Notfalls verlängern wir die Wartezeiten, die wir jetzt nicht mehr als Wartezeit nach dem vollendeten Schliessen bzw. Öffnen interpretieren, sondern als Wartezeit nach dem Starten des Schliessens oder Öffnens, um z.B. 30 s.
Dann müssen wir gar keine Rückmeldungen von 7 Rollläden haben, sondern nur die Rückmeldung von 1 Timer - wir benutzen für die 6 (oder 7) Wartezeiten einfach immer denselben Timer.
Ja, beschlossen und verkündet. Gefällt mir. 
Auf dieser Basis werde ich mal weiterknobeln ...

Gruss, Heinileini


----------



## Ratoncito (30 November 2020)

Hallo,

ich bin garnicht glücklich über das von mir verursachte Durcheinander.



> Dann müssen wir gar keine Rückmeldungen von 7 Rollläden haben, sondern  nur die Rückmeldung von 1 Timer - wir benutzen für die 6 (oder 7)  Wartezeiten einfach immer denselben Timer.
> Ja, beschlossen und verkündet. Gefällt mir.
> Auf dieser Basis werde ich mal weiterknobeln ...



Na ja, dann werde ich das mal wohlwollend Abnicken 

Das sind genau die Kleinigkeiten, die mir, wenn überhaupt, erst zum Ende hin auffallen und den benötigten Aufwand reduzieren.

Danke im Voraus und noch einen schönen Abend


----------



## Heinileini (2 Dezember 2020)

Wie immer, absolut ungetestet:

```
FUNCTION_BLOCK UpsAndDowns

    Pause         : TOF  ; // TOF-Instanz deklarieren - ist das so richtig?

VAR_INPUT
    itMax         : TIME ; // OberGrenze  für Wartezeit [ms] (inklusive Ausführungszeit)
    itMin         : TIME ; // UnterGrenze für Wartezeit [ms] (inklusive Ausführungszeit)
END_VAR

VAR_IN_OUT
    iobBlindsDown : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge schliessen"
    iobBlindsUp   : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge öffnen"
END_VAR

VAR_OUTPUT
    obBlind1Down  : BOOL ; // schliesst Rollladen 1
    obBlind1Up    : BOOL ; // öffnet    Rollladen 1
    obBlind2Down  : BOOL ; // schliesst Rollladen 2
    obBlind2Up    : BOOL ; // öffnet    Rollladen 2
    obBlind3Down  : BOOL ; // schliesst Rollladen 3
    obBlind3Up    : BOOL ; // öffnet    Rollladen 3
    obBlind4Down  : BOOL ; // schliesst Rollladen 4
    obBlind4Up    : BOOL ; // öffnet    Rollladen 4
    obBlind5Down  : BOOL ; // schliesst Rollladen 5
    obBlind5Up    : BOOL ; // öffnet    Rollladen 5
    obBlind6Down  : BOOL ; // schliesst Rollladen 6
    obBlind6Up    : BOOL ; // öffnet    Rollladen 6
    obBlind7Down  : BOOL ; // schliesst Rollladen 7
    obBlind7Up    : BOOL ; // öffnet    Rollladen 7
END_VAR

VAR
//  giA[0..6]     : INT  ; // (Globales?!) Array mit 7 zufällig angeordneten Zahlen 1..7
//  grR           : REAL ; // (Globale?!)  0.0 < ZufallsZahl < 1.0
    iAct          : INT := 8 ; // 0..6: Index für giA[]; 7: letzte Aufgabe der Sequenz; 8: Sequenz beendet
    bTimerIN      : BOOL ; // StartImpuls für Pausen-TOF 
    tTimerPT      : TIME ; // Dauer für Pausen-TOF im Bereich laut itMin .. itMax (z.B. 60..300 s)
END_VAR

    iobBlindsUp := iobBlindsUp AND NOT iobBlindsDown ; // BlindsUp rücksetzen, wenn BlindsDown
    IF iAct > 7 THEN // Sequenz inaktiv
        IF iobBlindsUp OR iobBlindsDown THEN
            iAct := 0 ; // Sequenz inaktiv und neue Aufgabe, dann Sequenz auf Anfang setzen
        END_IF ;
    ELSIF NOT Pause.Q THEN // Sequenz aktiv, warten auf PausenEnde, dann Aufgabe beenden und ggfs nächste starten
        obBlind1Up   := FALSE ; obBlind1Down := FALSE ;
        obBlind2Up   := FALSE ; obBlind2Down := FALSE ;
        obBlind3Up   := FALSE ; obBlind3Down := FALSE ;
        obBlind4Up   := FALSE ; obBlind4Down := FALSE ;
        obBlind5Up   := FALSE ; obBlind5Down := FALSE ;
        obBlind6Up   := FALSE ; obBlind6Down := FALSE ;
        obBlind7Up   := FALSE ; obBlind7Down := FALSE ;
        IF iAct < 7 THEN // AuftragsGenerierung aktiv
            CASE giA[iAct] OF // Rollladen-Nr
            1:  // Rollladen 1
                obBlind1Up := iobBlindsUp ; obBlind1Down := iobBlindsDown ;
            2:  // Rollladen 2
                obBlind2Up := iobBlindsUp ; obBlind2Down := iobBlindsDown ;
            3:  // Rollladen 3
                obBlind3Up := iobBlindsUp ; obBlind3Down := iobBlindsDown ;
            4:  // Rollladen 4
                obBlind4Up := iobBlindsUp ; obBlind4Down := iobBlindsDown ;
            5:  // Rollladen 5
                obBlind5Up := iobBlindsUp ; obBlind5Down := iobBlindsDown ;
            6:  // Rollladen 6
                obBlind6Up := iobBlindsUp ; obBlind6Down := iobBlindsDown ;
            ELSE // Rollladen 7
                obBlind7Up := iobBlindsUp ; obBlind7Down := iobBlindsDown ;
            END_CASE ; 
            bTimerIN := iobBlindsUp OR iobBlindsDown ; // TimerStart vorbereiten ...
            IF bTimerIN THEN // falls Aufgabe, dann Pausenzeit (Zufall!) festlegen 
                grR := grR * 997.0 - INT_TO_REAL (REAL_TO_INT (grR * 997.0)) ; // neue ZufallsZahl
                tTimerPT := REAL_TO_TIME((grR * TIME_TO_REAL(itMax - itMin)) + TIME_TO_REAL(itMin)) ; // Wartezeit in ms
            END_IF ;
        END_IF ;
        iAct := iAct + 1 ; // ... und Vorlage der nächsten Aufgabe vorbereiten
    END_IF ;
    Pause(bTimerIN, tTimerPT) ; // ? ? ?
    bTimerIN := FALSE ; // TimerStart rücksetzen
    IF iAct > 7 THEN // wenn Sequenz komplett durchlaufen, dann AuftragsMerker rücksetzen ;
        iobBlindsUp := FALSE ; iobBlindsDown := FALSE ;
    END_IF ;
END_FUNCTION_BLOCK
```


----------



## Ratoncito (3 Dezember 2020)

Hallo Heinileini,

vielen Dank.

Der Zu-fall ist gefallen und hat mich fast erschlagen.

Ein paar rot markierte Stellen gab es im Code, die sind nun weg. Ich habe es auch geschafft, das Ding ans Laufen zu bekommen. Was dann noch fehlte war die zufällige Reihenfolge im Array.

Dafür habe ich den Code aus dem Beitrag#3 eingefügt. Leider nicht so ganz mit dem gewünschten Erfolg. Ich erhalten lediglich einen anderen Startpunkt der Sequenz von 1 bis 7.

Nun habe ich soviel hin und her geschoben und ausprobiert, bis es im Chaos geendet ist. Ich werde alles nochmal neu machen. Das bringt ein wenig Übung 

Nur eine Frage
Der Code aus Beitrag #3 erzeugt eine beliebige Zahlenfolge von 7 Zahlen?

Dann werde ich nochmal versuchen den Code zur Erzeugen des Arrays einzubauen.


----------



## Heinileini (3 Dezember 2020)

Ratoncito schrieb:


> 1. Ein paar rot markierte Stellen gab es im Code, die sind nun weg. Ich habe es auch geschafft, das Ding ans Laufen zu bekommen.
> 2. Was dann noch fehlte war die zufällige Reihenfolge im Array.
> Dafür habe ich den Code aus dem Beitrag#3 eingefügt. Leider nicht so ganz mit dem gewünschten Erfolg. Ich erhalten lediglich einen anderen Startpunkt der Sequenz von 1 bis 7.
> 3.Der Code aus Beitrag #3 erzeugt eine beliebige Zahlenfolge von 7 Zahlen?


Zu 1.: 
Worum ging es im einzelnen? Was wurde bemeckert? Was hast Du getan, dass es jetzt weg ist? An's Laufen bekommen heisst was? Keine FehlerMeldungen mehr oder der FB gibt schon erste "Lebenzeichen" von sich? Welche?  Es ist für mich schwierig, mitzudenken, wenn ich nicht ein Bisschen mehr über die aufgetretenen Probleme und deren Beseitigungen erfahre.
Zu 2.:
Der Code aus #3 besteht aus 2 Teilen:
a) die Initialisierung des Array *giA[]*. Die Elemente giA[0]..giA[6] werden mit Zahlen 1..7 gefüllt - jede der Zahlen nur 1-mal) und 
der Speicher für die ZufallsZahl bzw. den ZufallsZahlenGenerator *grR* (REAL Zahl >0.0 und < 1.0). 
Die InitialisierungsRoutine hinterlässt in *giP *ausserdem eine Primzahl im Bereich 2..32749, die sie benutzt hat, um daraus den AnfangsWert für grR zu bilden.
Wird die InitialisierungsRoutine wieder durchlaufen, so wird die nächste Primzahl ermittelt und in giP gespeichert und daraus der Wert in grR gebildet.
Normalerweise genügt es, diese Prozedur 1-mal zu Anfang zu durchlaufen. Darf aber auch "gelegentlich" zwischendurch aufgerufen werden.
b) das DurcheinanderWürfeln des Array mit zweimaliger Benutzung des ZufallsZahlenGenerators. 
Hier werden die Inhalte von giA[0]..giA[6] rotiert, d.h. giA[0] erhält den Wert von giA[1], giA[1] den von giA[2] u.s.w. bis giA[5] den von giA[6] und schliesslich giA[6] den zu Anfang geretteten Wert von giA[0]. Jetzt werden noch zwei der Elemente untereinander getauscht. Die beiden TauschKandidaten werden durch ZufallsZahlen (aus grR durch Skalieren in den Bereich 0..6 gebracht) festgelegt.
Den Teil b musst Du immer dann aufrufen/durchlaufen, wenn Du die Belegung des Array giA[] ändern willst! Z.B. täglich oder vor jeder "alle-Rollläden-in-zufälliger-Reihenfolge-öffnen-Aktion" und/oder der "alle-Rollläden-in-zufälliger-Reihenfolge-schliessen-Aktion".
Die Inhalte des Array giA[] und des ZufallsWerts grR und des PrimzahlSpeichers giP sollen erhalten bleiben, bis das Programm "irgendwann" wieder darauf zurückgreift.
Darum habe ich vorgesehen, dass Du sie global deklarierst.
Wenn mit der Bildung der ZufallsZahlen oder der Veränderung der Array-Belegung etwas nicht mehr zu klappen scheint, bitte nachsehen, was in giA[0]..giA[6], in giP und in grR drinsteht.
giP und grR dürfen nicht 0 sein und in giA[0]..giA[6] darf jede der Zahlen 1..7 nur 1-mal vorkommen, sonst kann die Mimik nicht so funktionieren, wie beabsichtigt.
Ggfs Teil a (die Initialisierung) wiederholen, dann werden brauchbare Inhalte vorbesetzt. Das sollte aber normalerweise nicht nötig sein.
Zu 3.:
Siehe "zu 2", also JAIN.
Teil a erzeugt eine kaum bis gar nicht zufallsabhängige, aber "brauchbare" Belegung des Array.
Teil b ändert die Belegung "zufallsabhängig", setzt aber voraus, dass das Array bereits einen "brauchbaren" Inhalt hat.

Ähnlich ist es mit der ZufallsZahl grR. 
Es muss schon etwas "Brauchbares" drin stehen, dann kann man die nächste ZufallsZahl damit bilden. Siehe z.B. im Code von #45 in der Zeile

```
[COLOR=#333333][FONT=Courier]grR := grR * 997.0 - INT_TO_REAL (REAL_TO_INT (grR * 997.0)) ; // neue ZufallsZahl[/FONT][/COLOR]
```


----------



## Ratoncito (3 Dezember 2020)

Hallo Heinileini,

vielen Dank für die ausführliche Beschreibung.

Bemeckert wurden hauptsächlich ein paar Dinge um den Timer für die Pause, aber darauf hattest Du ja schon hingewiesen. Was da noch so war? Wenn es Dich interessiert kopiere ich den Code nochmal in einen neuen POU und schau nach. Mache ich morgen und poste das was bemängelt wird, und was ich gemacht habe. 

Es wurmt mich schon ein wenig, dass ich es nicht hinbekomme. Ich werde es mit der obigen Beschreibung nochmal versuchen.

Vielen Dank, noch einen schönen Abend und bis morgen.


----------



## Ratoncito (4 Dezember 2020)

Hallo Heinileini,

so, jetzt alles was rot war


```
//Von mir zugefügt bzw geändert
    giA: ARRAY[0..6] OF INT;
    grR: REAL;    
    //Pause         : TOF  ; // TOF-Instanz deklarieren - ist das so richtig?
    TOF_Pause         : TOF  ; // TOF-Instanz deklarieren - ist das so richtig?
    A_Down : BOOL;     //zum Start Down
    A_Up : BOOL;         //zum Start Up

   // Pause(bTimerIN, tTimerPT) ; // ? ? ?
   TOF_Pause (IN := bTimerIN, PT := tTimerPT) ;

   //ELSIF NOT Pause.Q THEN // Sequenz aktiv, warten auf PausenEnde, dann Aufgabe beenden und ggfs nächste starten
    ELSIF NOT TOF_Pause.Q THEN
```

Wie schon geschrieben ging es um die Deklaration von TOF, auf die Du schon hingewiesen hast, und auf die beiden Variablen, die aber schon im Code aus Beitrag #3 deklariert wurden.

Weiter habe ich für die Ausgänge Up_1 ... Up_7 und Down_1 ... Down_7 zugefügt. Und A_Down und A_Up für den Start.

Nun den Code aus Beitrag#3
Dauert etwas, bin nicht der Schnellste.


----------



## Ratoncito (4 Dezember 2020)

Hallo Heinileini,

zwischenzeitlich wähnte ich mich auf der Zielgerade, aber dann bin ich mal wieder falsch abgebogen.

Nach dem Start wird das Array laufend mit Zahlen gefüllt. Ich habe versucht, das nur einmal anzustossen und wenn das Array gefüllt ist Rolladen zu schließen. Irgendwann ging es mal, aber nur einmal nach dem Start (Warum auch immer). Dann viel mir auf, dass es manchmal gleiche Zahlen im Array gab. Vermutlich habe ich an der falschen Stelle gestoppt. Meine Versuche habe ich alle wieder verworfen und dann total den Faden verloren.

Ich poste mal den Code. Könntest Du dort bitte Start und Stop für das einmalige Füllen des Arrays einfügen? An dem nächsten Schritt (Start der Rolladen) würde ich mir dann gerne selbst nochmal die Zähne ausbeissen.


```
FUNCTION_BLOCK UpsAndDowns

VAR_INPUT
    itMax         : TIME := T#8S ; // OberGrenze  für Wartezeit [ms] (inklusive Ausführungszeit)
    itMin         : TIME := T#2S ; // UnterGrenze für Wartezeit [ms] (inklusive Ausführungszeit)
END_VAR

VAR_IN_OUT
    iobBlindsDown : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge schliessen"
    iobBlindsUp   : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge öffnen"
END_VAR

VAR_OUTPUT
    obBlind1Down  : BOOL ; // schliesst Rollladen 1
    obBlind1Up    : BOOL ; // öffnet    Rollladen 1
    obBlind2Down  : BOOL ; // schliesst Rollladen 2
    obBlind2Up    : BOOL ; // öffnet    Rollladen 2
    obBlind3Down  : BOOL ; // schliesst Rollladen 3
    obBlind3Up    : BOOL ; // öffnet    Rollladen 3
    obBlind4Down  : BOOL ; // schliesst Rollladen 4
    obBlind4Up    : BOOL ; // öffnet    Rollladen 4
    obBlind5Down  : BOOL ; // schliesst Rollladen 5
    obBlind5Up    : BOOL ; // öffnet    Rollladen 5
    obBlind6Down  : BOOL ; // schliesst Rollladen 6
    obBlind6Up    : BOOL ; // öffnet    Rollladen 6
    obBlind7Down  : BOOL ; // schliesst Rollladen 7
    obBlind7Up    : BOOL ; // öffnet    Rollladen 7
END_VAR

VAR
//  giA[0..6]     : INT  ; // (Globales?!) Array mit 7 zufällig angeordneten Zahlen 1..7
//  grR           : REAL ; // (Globale?!)  0.0 < ZufallsZahl < 1.0
    iAct          : INT := 8 ; // 0..6: Index für giA[]; 7: letzte Aufgabe der Sequenz; 8: Sequenz beendet
    bTimerIN      : BOOL ; // StartImpuls für Pausen-TOF 
    tTimerPT      : TIME ; // Dauer für Pausen-TOF im Bereich laut itMin .. itMax (z.B. 60..300 s)
    //Von mir zugefügt
    giA: ARRAY[0..6] OF INT;
    grR: REAL;    
    //Pause         : TOF  ; // TOF-Instanz deklarieren - ist das so richtig?
    TOF_Pause         : TOF  ; // TOF-Instanz deklarieren - ist das so richtig?
    // zum Starten AB
    A_Down : BOOL;
    // zum Starten AUF
    A_Up : BOOL;
END_VAR
//Betrag#3
VAR
giP : INT;    
tbEnd : BOOL;    
tiBis : INT;
tiIdx1 : INT;
tiIdx2 : INT;
tiTmp : INT;    
trR : REAL;
    RTrig_Start: R_TRIG;
    ArrayBerechnen: BOOL;
END_VAR
```


```
Merker.Start_AB := A_Down;
    Merker.Start_AUF :=A_Up;
    
    //Ab hier Beitrag#3
    // = = = = = < Init: "im Hochlauf der PLC" > = = = = =
// - - - < Array 0..6 initialisieren mit 1..7 > - - -
tiTmp := giA[0] ;
For tiIdx1 := 0 To 6 Do
    giA[tiIdx1] := (tiIdx1 + tiTmp) Mod 7 + 1 ;
END_FOR ;

// - - - < Startwert für grR bilden > - - -
If giP < 2 OR  giP > 32740 Then
    giP := 2 ;
Else
    Repeat
        //giP = giP + 1 + giP MOD 2 ;
        giP := giP + 1 + giP MOD 2 ;
        tiBis := Real_To_Int(Sqrt(Int_To_Real(giP))) ;
        tbEnd := True ;
        For tiIdx1 := 3 To tiBis By 2 Do
            If giP Mod tiIdx1 = 0 Then 
                tbEnd := False ;
                Exit ;
            End_If ;     
        End_For ;
    Until tbEnd
    End_Repeat ;
End_If ;
trR := Sqrt(Int_To_Real(giP)) ;
grR := trR - INT_TO_REAL(REAL_TO_INT(trR)) ;

// = = = = = < Next: "Verwirbeln des Array" > = = = = =

// - - - < Array rotieren > - - -
tiTmp:= giA[0] ;
For tiIdx1 := 0 To 5 Do
    giA[tiIdx1] := giA[tiIdx1 + 1] ;
End_For ;
giA[6] := tiTmp ;

// - - - < Idx1 bilden > - - -
trR := grR * 997.0 ;
grR := trR - Int_To_Real(Real_To_Int(trR)) ;
tiIdx1 := Real_To_Int(grR * 7.0) ;

// - - - < Idx2 bilden > - - -
trR := grR * 997.0 ;
grR := trR - Int_To_Real(Real_To_Int(trR)) ;
tiIdx2 := Real_To_Int(grR * 7.0) ;

// - - - < ggfs Idx2 korrigieren > - - -
If tiIdx1 = tiIdx2 Then 
    tiIdx2 := (tiIdx2 + 3) Mod 7 ;
End_If ;

// - - - < 2 Elemente tauschen > - - -
tiTmp:= giA[tiIdx1] ; 
giA[tiIdx1] := giA[tiIdx2] ;
giA[tiIdx2] := tiTmp ;
//Bis hier Beitrag#3

 iobBlindsUp := iobBlindsUp AND NOT iobBlindsDown ; // BlindsUp rücksetzen, wenn BlindsDown
    IF iAct > 7 THEN // Sequenz inaktiv
        IF iobBlindsUp OR iobBlindsDown THEN
            iAct := 0 ; // Sequenz inaktiv und neue Aufgabe, dann Sequenz auf Anfang setzen
        END_IF ;
    //ELSIF NOT Pause.Q THEN // Sequenz aktiv, warten auf PausenEnde, dann Aufgabe beenden und ggfs nächste starten
    ELSIF NOT TOF_Pause.Q THEN 
        obBlind1Up   := FALSE ; obBlind1Down := FALSE ;
        obBlind2Up   := FALSE ; obBlind2Down := FALSE ;
        obBlind3Up   := FALSE ; obBlind3Down := FALSE ;
        obBlind4Up   := FALSE ; obBlind4Down := FALSE ;
        obBlind5Up   := FALSE ; obBlind5Down := FALSE ;
        obBlind6Up   := FALSE ; obBlind6Down := FALSE ;
        obBlind7Up   := FALSE ; obBlind7Down := FALSE ;
        IF iAct < 7 THEN // AuftragsGenerierung aktiv
            CASE giA[iAct] OF // Rollladen-Nr
            1:  // Rollladen 1
                obBlind1Up := iobBlindsUp ; obBlind1Down := iobBlindsDown ;
            2:  // Rollladen 2
                obBlind2Up := iobBlindsUp ; obBlind2Down := iobBlindsDown ;
            3:  // Rollladen 3
                obBlind3Up := iobBlindsUp ; obBlind3Down := iobBlindsDown ;
            4:  // Rollladen 4
                obBlind4Up := iobBlindsUp ; obBlind4Down := iobBlindsDown ;
            5:  // Rollladen 5
                obBlind5Up := iobBlindsUp ; obBlind5Down := iobBlindsDown ;
            6:  // Rollladen 6
                obBlind6Up := iobBlindsUp ; obBlind6Down := iobBlindsDown ;
            ELSE // Rollladen 7
                obBlind7Up := iobBlindsUp ; obBlind7Down := iobBlindsDown ;
            END_CASE ; 
            bTimerIN := iobBlindsUp OR iobBlindsDown ; // TimerStart vorbereiten ...
            IF bTimerIN THEN // falls Aufgabe, dann Pausenzeit (Zufall!) festlegen 
                grR := grR * 997.0 - INT_TO_REAL (REAL_TO_INT (grR * 997.0)) ; // neue ZufallsZahl
                tTimerPT := REAL_TO_TIME((grR * TIME_TO_REAL(itMax - itMin)) + TIME_TO_REAL(itMin)) ; // Wartezeit in ms
            END_IF ;
        END_IF ;
        iAct := iAct + 1 ; // ... und Vorlage der nächsten Aufgabe vorbereiten
    END_IF ;
   // Pause(bTimerIN, tTimerPT) ; // ? ? ?
   TOF_Pause (IN := bTimerIN, PT := tTimerPT) ;
    bTimerIN := FALSE ; // TimerStart rücksetzen
    IF iAct > 7 THEN // wenn Sequenz komplett durchlaufen, dann AuftragsMerker rücksetzen ;
        iobBlindsUp := FALSE ; iobBlindsDown := FALSE ;
    END_IF ;
```

Und nur der Vollständigkeithalber und weil Du danach gefragt hast - Zeile 17 und 18 war noch etwas


----------



## Heinileini (4 Dezember 2020)

Ratoncito schrieb:


> Und nur der Vollständigkeithalber und weil Du danach gefragt hast - Zeile 17 und 18 war noch etwas


Ich nehme an, Du meinst ...

```
//giP = giP + 1 + giP MOD 2 ;
        giP := giP + 1 + giP MOD 2 ;
```
Was war damit? Meckert der Compiler oder fehlt Dir mein Kommentar?
Hier soll der Inhalt von giP ...
- um 1 erhöht werden, wenn giP gerade ist (der DivisionsRest ist dann 0) bzw.
- um 2 erhöht werden, wenn giP ungerade ist (der DivisionsRest ist dann 1).

Ansonsten ... diesmal muss ich Deine Geduld strapazieren - ich bin auch nicht sooo schnell. 
Wo es lang gehen soll, ist zwar klar, aber wie sage ich's dem CodeSys?

Versuch Du bitte alldieweil zu ergründen, wie und wo im Projekt man die globalen Variablen giA[], giP und grR deklarieren muss. Irgendwo ganz zu Anfang und ausserhalb von allen FBs - unterstelle ich mal.

Gruss, Heinileini

PS:
Der von Dir eingeschlagene Weg ist wahrscheinlich gar nicht so schlecht. 
Ich werde, denke ich, auf diesem Wege weiter überlegen, dann brauchen wir die globalen Dinger nicht.


----------



## Ratoncito (4 Dezember 2020)

Kurz auf die Schnelle,

Du wolltest wissen, wo der Compiler gemeckert hat. Daher habe ich alles vermerkt.

Ich glaube, dass ich alles deklariert habe. Damit scheint es keine Probleme zu geben, oder ich habe da noch etwas nicht richtig verstanden.

Irgendwie habe ich Probleme das Array einmal richtig zu füllen und dann zu stoppen. Wenn Du mir sagst: "hier starten und wenn alles gefüllt ist hier wieder beenden. Array = True und am Ende False", das würde mir helfen.

Ich hatte teilweise Zahlen doppelt oder mit Minus, oder immer gleiche Reihenfolge. Wenn  ich trotz dem richtigen Start- und Endpunkt nicht weiter komme melde ich mich noch einmal.

Danke und noch einen schönen Abend.


----------



## Heinileini (4 Dezember 2020)

Hab da schnell mal was reingeflickt (hoffentlich nicht zu schnell):

```
Merker.Start_AB := A_Down;
    Merker.Start_AUF :=A_Up;

VAR_INPUT              //    < < < < < eingefügt > > > > >
    ibInit    : BOOL ; //    < < < < < eingefügt > > > > >
    ibNext    : BOOL ; //    < < < < < eingefügt > > > > >
END_VAR                //    < < < < < eingefügt > > > > >

VAR                    //    < < < < < eingefügt > > > > >
    sbInitPre : BOOL ; //    < < < < < eingefügt > > > > >
    sbNextPre : BOOL ; //    < < < < < eingefügt > > > > >
END_VAR                //    < < < < < eingefügt > > > > >
    
    // = = = = = < Init: "im Hochlauf der PLC" > = = = = =
IF ibInit AND NOT sbInitPre THEN //    < < < < < eingefügt > > > > >
    //Ab hier Beitrag#3
    // - - - < Array 0..6 initialisieren mit 1..7 > - - -
    tiTmp := giA[0] ;
    For tiIdx1 := 0 To 6 Do
        giA[tiIdx1] := (tiIdx1 + tiTmp) Mod 7 + 1 ;
    END_FOR ;
    
    // - - - < Startwert für grR bilden > - - -
    If giP < 2 OR  giP > 32740 Then
        giP := 2 ;
    Else
        Repeat
            //giP = giP + 1 + giP MOD 2 ;
            giP := giP + 1 + giP MOD 2 ;
            tiBis := Real_To_Int(Sqrt(Int_To_Real(giP))) ;
            tbEnd := True ;
            For tiIdx1 := 3 To tiBis By 2 Do
                If giP Mod tiIdx1 = 0 Then 
                    tbEnd := False ;
                    Exit ;
                End_If ;     
            End_For ;
        Until tbEnd
        End_Repeat ;
    End_If ;
    trR := Sqrt(Int_To_Real(giP)) ;
    grR := trR - INT_TO_REAL(REAL_TO_INT(trR)) ;
END_IF ;              //    < < < < < eingefügt > > > > >
sbInitPre := ibInit ; //    < < < < < eingefügt > > > > >

// = = = = = < Next: "Verwirbeln des Array" > = = = = =

IF ibNext AND NOT sbNextPre THEN //    < < < < < eingefügt > > > > >
    // - - - < Array rotieren > - - -
    tiTmp:= giA[0] ;
    For tiIdx1 := 0 To 5 Do
        giA[tiIdx1] := giA[tiIdx1 + 1] ;
    End_For ;
    giA[6] := tiTmp ;
    
    // - - - < Idx1 bilden > - - -
    trR := grR * 997.0 ;
    grR := trR - Int_To_Real(Real_To_Int(trR)) ;
    tiIdx1 := Real_To_Int(grR * 7.0) ;
    
    // - - - < Idx2 bilden > - - -
    trR := grR * 997.0 ;
    grR := trR - Int_To_Real(Real_To_Int(trR)) ;
    tiIdx2 := Real_To_Int(grR * 7.0) ;
    
    // - - - < ggfs Idx2 korrigieren > - - -
    If tiIdx1 = tiIdx2 Then 
        tiIdx2 := (tiIdx2 + 3) Mod 7 ;
    End_If ;
    
    // - - - < 2 Elemente tauschen > - - -
    tiTmp:= giA[tiIdx1] ; 
    giA[tiIdx1] := giA[tiIdx2] ;
    giA[tiIdx2] := tiTmp ;
//Bis hier Beitrag#3
END_IF ;             //    < < < < < eingefügt > > > > >
sbNextPre := ibNex ; //    < < < < < eingefügt > > > > >

    iobBlindsUp := iobBlindsUp AND NOT iobBlindsDown ; // BlindsUp rücksetzen, wenn BlindsDown
    IF iAct > 7 THEN // Sequenz inaktiv
        IF iobBlindsUp OR iobBlindsDown THEN
            iAct := 0 ; // Sequenz inaktiv und neue Aufgabe, dann Sequenz auf Anfang setzen
        END_IF ;
    //ELSIF NOT Pause.Q THEN // Sequenz aktiv, warten auf PausenEnde, dann Aufgabe beenden und ggfs nächste starten
    ELSIF NOT TOF_Pause.Q THEN 
        obBlind1Up   := FALSE ; obBlind1Down := FALSE ;
        obBlind2Up   := FALSE ; obBlind2Down := FALSE ;
        obBlind3Up   := FALSE ; obBlind3Down := FALSE ;
        obBlind4Up   := FALSE ; obBlind4Down := FALSE ;
        obBlind5Up   := FALSE ; obBlind5Down := FALSE ;
        obBlind6Up   := FALSE ; obBlind6Down := FALSE ;
        obBlind7Up   := FALSE ; obBlind7Down := FALSE ;
        IF iAct < 7 THEN // AuftragsGenerierung aktiv
            CASE giA[iAct] OF // Rollladen-Nr
            1:  // Rollladen 1
                obBlind1Up := iobBlindsUp ; obBlind1Down := iobBlindsDown ;
            2:  // Rollladen 2
                obBlind2Up := iobBlindsUp ; obBlind2Down := iobBlindsDown ;
            3:  // Rollladen 3
                obBlind3Up := iobBlindsUp ; obBlind3Down := iobBlindsDown ;
            4:  // Rollladen 4
                obBlind4Up := iobBlindsUp ; obBlind4Down := iobBlindsDown ;
            5:  // Rollladen 5
                obBlind5Up := iobBlindsUp ; obBlind5Down := iobBlindsDown ;
            6:  // Rollladen 6
                obBlind6Up := iobBlindsUp ; obBlind6Down := iobBlindsDown ;
            ELSE // Rollladen 7
                obBlind7Up := iobBlindsUp ; obBlind7Down := iobBlindsDown ;
            END_CASE ; 
            bTimerIN := iobBlindsUp OR iobBlindsDown ; // TimerStart vorbereiten ...
            IF bTimerIN THEN // falls Aufgabe, dann Pausenzeit (Zufall!) festlegen 
                grR := grR * 997.0 - INT_TO_REAL (REAL_TO_INT (grR * 997.0)) ; // neue ZufallsZahl
                tTimerPT := REAL_TO_TIME((grR * TIME_TO_REAL(itMax - itMin)) + TIME_TO_REAL(itMin)) ; // Wartezeit in ms
            END_IF ;
        END_IF ;
        iAct := iAct + 1 ; // ... und Vorlage der nächsten Aufgabe vorbereiten
    END_IF ;
    // Pause(bTimerIN, tTimerPT) ; // ? ? ?
    TOF_Pause (IN := bTimerIN, PT := tTimerPT) ;
    bTimerIN := FALSE ; // TimerStart rücksetzen
    IF iAct > 7 THEN // wenn Sequenz komplett durchlaufen, dann AuftragsMerker rücksetzen ;
        iobBlindsUp := FALSE ; iobBlindsDown := FALSE ;
    END_IF ;
```


----------



## Ratoncito (5 Dezember 2020)

Hallo Heinileini,

vielen Dank.

Mach Dir bitte im Moment keine weitere Mühe, außer Daumen drücken. Wenn ich Glück habe, dann ist bei mir soeben der Groschen (wenn auch in Pfennigen) gefallen.

Allen ein schönes Wochenende


----------



## Ratoncito (7 Dezember 2020)

Hallo Heinileini,

zurück aus der Versenkung Leider ist der Groschen nur in Pfennigen gefallen, und ein paar sind irgendwo hängen geblieben 

Nochmal ein paar Grundgedanken
Muss man einen Funktionsbaustein bauen? Habe doch eh schon keine Ahnung. Eigentlich möchte ich die Routine zweimal am Tag aufrufen - Rolladen AUF und AB.

Das Array ist vom letzten Aufruf noch gefüllt und müsste nur noch in eine andere Reihenfolge gebracht werden. Einmal Rotieren und dann ein- oder zweimal mit Zufallszahlen Plätze tauschen sollte vollkommen ausreichen.
Nun noch die Routine starten und mit zufälligen Pausenzeiten ausführen.
Feierabend bis zum nächsten Aufruf.

Leider gibt es da so einige Verständnisprobleme, was wann und wofür gemacht wird. Daher habe ich die Variablen so deklariert, dass es (für mich) aussagefähigere Namen sind. Somit jetzt die einzelnen Teile

```
// - - - < Array 0..6 initialisieren mit 1..7 > - - -
tiTmp := giA[0] ;
For Zahl_1 := 0 To 6 Do
    giA[Zahl_1] := (Zahl_1 + tiTmp) Mod 7 + 1 ;
END_FOR
```

Füllen des Array, funktioniert. Könnte aber entfallen, wenn man für den ersten Start einen Initialisierungswert vorgibt. Im weiteren Verlauf bleibt das Array mit der letzten Reihenfolge gefüllt.


```
// - - - < Array rotieren > - - -
tiTmp:= giA[0] ;
For Zahl_1 := 0 To 5 Do
    giA[Zahl_1] := giA[Zahl_1 + 1] ;
End_For ;
giA[6] := tiTmp ;
```

Funktioniert. Müsste im geeigneten Moment einmal gestartet werden.


```
// - - - < Startwert für X_Zuf bilden > - - -
If X_Prim < 2 OR  X_Prim > 32740 Then
    X_Prim := 2 ;
Else
    Repeat
        X_Prim := X_Prim + 1 + X_Prim MOD 2 ;
        tiBis := Real_To_Int(Sqrt(Int_To_Real(X_Prim))) ;
        tbEnd := True ;
        FOR tiIdx1 := 3 TO tiBis BY 2 DO
            IF X_Prim MOD tiIdx1 = 0 THEN
                tbEnd := False ;
                Exit ;
            End_If ;    
        End_For ;
    Until tbEnd
    End_Repeat ;
End_If ;
trR := Sqrt(Int_To_Real(X_Prim)) ;
X_Zuf := trR - INT_TO_REAL(REAL_TO_INT(trR)) ;
```

Funktioniert. Wenn es benötigt wird, müsste es für eine Weile Laufen um einen x-beliebigen Startwert zu erzeugen.
Hier erkenne ich die Funktion von tiBis und tbEnd nicht. Was hat es damit auf sich?


```
// - - - < Zufallszahl 1 zum Verschieben bilden > - - -
trR := X_Zuf * 997.0 ;
X_Zuf := trR - Int_To_Real(Real_To_Int(trR)) ;
Zahl_1 := REAL_TO_INT(X_Zuf * 7.0) ;
IF Zahl_1 < 0 THEN
    Zahl_1 := Zahl_1 *(-1);
END_IF
// - - - < Zufallszahl 2 bilden > - - -
trR := X_Zuf * 997.0 ;
X_Zuf := trR - Int_To_Real(Real_To_Int(trR)) ;
Zahl_2 := REAL_TO_INT(X_Zuf * 7.0) ;
IF Zahl_2 < 0 THEN
    Zahl_2 := Zahl_2 *(-1);
END_IF
// - - - < ggfs Zufallszahl 2 korrigieren > - - -
IF Zahl_1 = Zahl_2 THEN
    Zahl_2 := (Zahl_2 + 3) Mod 7 ;
END_IF ;
```

Wenn ich es richtig sehe, sollen hier 2 unterschiedliche Zufallszahlen zum Tauschen von 2 Plätzen im Array erzeugt werden?
Hierbei werden manchmal negative Zahlen erzeugt, für den Fall habe ich die Multiplikation mit -1 eingefügt.

Zum Beginn habe ich versucht, den Start nur für die Funktion AUF und mit fester Wartezeit auszuführen.


```
TP_Pause (IN := PauseIN, PT := PauseMax) ;                        //PT noch mit zufälliger Zeit füllen
   Pause := TP_Pause.Q;
   
IF AUF_Start = TRUE THEN
    IF Y_WR = FALSE THEN                                                        // noch ändern um einmalig iAct auf 0 zu setzen
        iAct := 0;
        PauseIN := TRUE;
        Y_WR := TRUE;    
    END_IF                                                                        //
END_IF

IF iAct < 8 THEN
    CASE giA[iAct] OF
        1: Auf_1 := TRUE;
            PauseIN := TRUE;
        2: Auf_2 := TRUE;
            PauseIN := TRUE;
        3: Auf_3 := TRUE;
            PauseIN := TRUE;
        4: Auf_4 := TRUE;
            PauseIN := TRUE;
        5: Auf_5 := TRUE;
            PauseIN := TRUE;
        6: Auf_6 := TRUE;
            PauseIN := TRUE;
        7: Auf_7 := TRUE;
            PauseIN := TRUE;
    END_CASE
    PauseIN := FALSE;
    IF Pause = FALSE THEN
        iAct:= iAct + 1;
        PauseIN := TRUE;
    END_IF
ELSE 
    PauseIN := FALSE;
        AUF_1 :=FALSE;
        AUF_2 :=FALSE;
        AUF_3 :=FALSE;
        AUF_4 :=FALSE;
        AUF_5 :=FALSE;
        AUF_6 :=FALSE;
        AUF_7 :=FALSE;
    AUF_Start := FALSE;
    Y_WR := FALSE;
END_IF
```

Problem: zu Beginn werden 2 Aufgaben ohne Wartezeit dazwischen gestartet.
Sicherlich gibt es eine elegantere Lösung?

Wenn dieser Teil okay ist, würde ich gerne versuchen als nächsten Schritt die variable Pausenzeit einzubauen.

Einen guten Start in die neue Woche - Wolfgang


----------



## Heinileini (7 Dezember 2020)

1) Die negativen Zahlen wurmen mich. Versuch bitte zunächst mal mit 2 Funktionen (ABS und TRUNC) aus der Standard-Lib:

```
// - - - < Zufallszahl für Index 1 zum Tauschen bilden > - - -
X_Zuf := ABS(X_Zuf * 997.0 - Int_To_Real(TRUNC(X_Zuf * 997.0))) ; // nächste ZufallsZahl
Zahl_1 := REAL_TO_INT(X_Zuf * 7.0) ;             // > 0.0 .. < 1.0 skalieren auf 0..6
// - - - < Zufallszahl für Index 2 zum Tauschen bilden > - - -
X_Zuf := ABS(X_Zuf * 997.0 - Int_To_Real(TRUNC(X_Zuf * 997.0))) ; // nächste ZufalssZahl
Zahl_2 := REAL_TO_INT(X_Zuf * 7.0) ;             // > 0.0 .. < 1.0 skalieren auf 0..6
// - - - < ggfs Zufallszahl 2 korrigieren > - - -
IF Zahl_1 = Zahl_2 THEN
    Zahl_2 := (Zahl_2 + 3) Mod 7 ;
END_IF ;
```
Die ZufallsZahlenBildung habe ich jetzt in einen EinZeiler geändert, ohne die temporäre ZwischenVariable - ist wahrscheinlich übersichtlicher.
TRUNC schneidet die NachKommaStellen ab. Der ErgebnisTyp soll INT sein laut Beschreibung - ich nehme es mal so hin.
ABS bildet den Betrag, d.h. eliminiert das negative Vorzeichen. Sollte für mein Verständnis überflüssig sein, aber Du hast ja schon die Erfahrung gemacht, dass das VorZeichen plötzlich negativ werden kann.
Habe eben gesehen, dass die Funktion SQRT (die Du schon verwendest) ebenfalls aus der Standard-Lib ist.

2) Nachtrag 1:

```
// - - - < Startwert für X_Zuf bilden > - - -
If X_Prim < 2 OR  X_Prim > 32740 Then // wenn nötig, PrimZahl 2 vorbesetzen
    X_Prim := 2 ;
Else // sonst nächstgrössere PrimZahl suchen
    Repeat
        X_Prim := X_Prim + 1 + X_Prim MOD 2 ; // +1, wenn gerade Zahl, sonst +2
        tiBis := Real_To_Int(SQRT(Int_To_Real(X_Prim))) ; // Obergrenze für zu testende Divisoren
        tbEnd := True ; // Merker: Zahl nicht restlos teilbar
        FOR tiIdx1 := 3 TO tiBis BY 2 DO
            IF X_Prim MOD tiIdx1 = 0 THEN
                tbEnd := False ; // SchleifenbAbbruch, wenn restlos teilbar
                Exit ;
            End_If ;    
        End_For ;
    Until tbEnd
    End_Repeat ;
End_If ;
trR := SQRT(Int_To_Real(X_Prim)) ; // QuadratWurzel aus PrimZahl -> NachkommaStellen!
X_Zuf := ABS(trR - Int_To_Real(TRUNC(trR))) ; // VorkommaStellen löschen
```
Letzte Zeile geändert - in Anlehnung an 1)! Ansonsten nur Kommentare ergänzt.

3) Nachtrag 2:


Ratoncito schrieb:


> Nochmal ein paar Grundgedanken
> Muss man einen Funktionsbaustein bauen? Habe doch eh schon keine Ahnung. Eigentlich möchte ich die Routine zweimal am Tag aufrufen - Rolladen AUF und AB.


So ist der Plan, FB bauen.
Ob nötig oder nicht, das wirst Du eines Tages besser beurteilen können als ich. 

Jetzt haben wir es so, dass die Kapitel 
- Rollläden in zufälliger Reihenfolge schliessen
- Rollläden in zufälliger Reihenfolge öffnen
- die dazu erforderliche Bildung der zufälligen Reihenfolge
- die Bildung der zufälligen PausenZeiten
unter einem Hut im FB zusammengefasst sind.
Der FB wird zyklisch aufgerufen.
Er prüft, ob es etwas zu tun gibt, wo und was. Gibt es nix zu tun, so tut er auch nicht viel mehr, als eben nur zu prüfen.
Der FB wird von aussen gesteuert
- ein Bit für "Initialisierung"
- ein Bit für "nächste Array-Belegung"
- ein Bit für "alle Rollläden öffnen"
- ein Bit für "alle Rollläden schliessen" 
und er steuert nach aussen die Aktionen
- "Rollladen 1 öffnen"
- "Rollladen 1 schliessen"
- u.s.w. das gleiche für die anderen 6 Rollläden.
Die Bits, die als IN_OUT oder OUTPUT deklariert sind, setzt er auch wieder zurück.
Die Bits, die als INPUT deklariert sind, darfst Du länger anstehen lassen - musst Du aber nicht, weil er nur die positiven Flanken auswertet. Aber Du musst sie auch selbst rücksetzten, bevor Du sie wieder setzen kannst, um einen neuen Auftrag auszulösen.
D.h., Du musst einiges drum herum basteln, aber das willst Du ja schliesslich auch. Du willst bestimmen, wann alle Rollläden geöffnet/geschlossen werden sollen.
Du willst bestimmen, wie Dein Rollladen auf das Kommando öffnen/schliessen reagieren soll. Der FB gibt Dir nur vor, wann welcher womit dran ist und welche Pause eingelegt wird. 

Ich poste dies schon mal, damit Du nicht unnötig lange auf eine erste Reaktion warten musst ...


----------



## Ratoncito (7 Dezember 2020)

Hallo Heinileini,

vielen Dank.

Der Compiler meckert über

Int_To_Real(TRUNC(trR)
Typ DINT kann nicht in Typ INT konvertiert werden.


----------



## Heinileini (7 Dezember 2020)

Ratoncito schrieb:


> Der Compiler meckert über
> 
> Int_To_Real(TRUNC(trR)
> Typ DINT kann nicht in Typ INT konvertiert werden.


Dann ändern wir in ...

```
X_Zuf := ABS(X_Zuf * 997.0 - [COLOR=#0000cd][B]Dint[/B][/COLOR]_To_Real(TRUNC(X_Zuf * 997.0))) ; // nächste ZufallsZahl
```
... an zwei Stellen im Programm und in ...

```
X_Zuf := ABS(trR - [COLOR=#0000cd][B]Dint[/B][/COLOR]_To_Real(TRUNC(trR))) ; // VorkommaStellen löschen
```
... an einer anderen Stelle und ...
rauchen die CodeSys-Beschreibung in der Pfeiffe:


> TRUNC
> Konvertierung vom Typ REAL zum Typ *INT*. Es wird nur der Betrag des ganzzahligen Anteils der Zahl genommen.


Da steht sowieso schon Unsinn: "der Betrag" passt schon nicht zu einem der Beispiele:


> Beispiele in ST:
> i:=TRUNC(1.9); (* Ergebnis ist 1 *)
> i:=TRUNC(-1.4); (* Ergebnis ist *-*1 *) *<===<<<*


Ich hoffe Du findest die Stellen (ich meine die in Deinem Programm) wieder. 

Bitte um Rückmeldung! Die CompilerMeldung ist mir nicht wirklich klar. Vielleicht wäre Weglassen von DINT_TO_REAL noch besser? Wenn ja, warum???


----------



## Ratoncito (7 Dezember 2020)

Hallo Heinileini,



Heinileini schrieb:


> Bitte um Rückmeldung! Die CompilerMeldung ist mir nicht wirklich klar. Vielleicht wäre Weglassen von DINT_TO_REAL noch besser? Wenn ja, warum???



Dann bin ich wenigstens nicht alleine 

Ich hatte da schon ein wenig rumprobiert, bin mir aber nicht sicher was ich da eigentlich mache.

X_Zuf := ABS(trR - (TRUNC(trR))) ; lefert
Implizierte Konvertierung von DINT nach REAL: Möglicher Datenverlust

Den Rest probiere ich morgen, vielen Dank und gute Nacht


----------



## Heinileini (7 Dezember 2020)

Ratoncito schrieb:


> X_Zuf := ABS(trR - (TRUNC(trR))) ; liefert
> Implizierte Konvertierung von DINT nach REAL: Möglicher Datenverlust


Das dürfte aber nur eine Warnung sein ...


----------



## Ratoncito (8 Dezember 2020)

Hallo Heinileini,

vielen Dank.

Ja, es ist nur eine Warnung.

Ich habe den Code entsprechend geändert und getestet. Ich denke, dass alles in Ordnung ist.

Ein FB ist sicherlich die bessere Lösung. Bereitete mir aber ein Problem beim Aufruf im PLC_PRG, da alle Ein- und Ausgänge belegt werden müssen. Da ich mich aber zuerst mit dem ganzen "Zufallsgemüse" beschäftigen wollte (um wenigstens halbwegs zu verstehen, was da abläuft) habe ich den ganzen Rest weggelassen. Simulieren kann ich das dann nur in einem POU.

Nun habe ich begonnen einfach nur die Funktion AUF mit fester Zeit einzufügen. Wenn das funktioniert kommt der nächste Schritt dazu...

Am Ende können wir das gerne in einen FB packen.

Es ist sicherlich ein längerer Weg, und bequemer wäre es, einen von Dir gebauten FB zu verwenden. Aber andersherum ist der Lerneffekt größer.

In Beitrag#55 ist mein aktuelles Problem beschrieben: zu Beginn werden 2 Aufgaben gleichzeitig aufgerufen.

Ich würde mich freuen, wenn jemand diesen Fehler behebt, oder noch besser, mir zeigt, wie man diesen Teil besser und effizienter programmiert.

Ich freue mich über Kritiken und Verbesserungen.


----------



## holgermaik (8 Dezember 2020)

> ... an einer anderen Stelle und ...
> rauchen die CodeSys-Beschreibung in der Pfeiffe:


Hallo Heinileini

Die Hilfe ist bei Codesys sehr spezifisch.
ich nutze meistens https://help.codesys.com/
Wolfgang  nutzt e!cockpit Firmware 17. Das entspricht als Basis Codesys 3.5.14.  Auf der o.g.S. kann man die Version explizit einstellen.


> Der IEC-Operator dient der  Konvertierung vom Typ REAL zum Typ DINT. CODESYS nimmt nur den ganzzahligen Anteil der Zahl.




```
X_Zuf := ABS(trR - (TRUNC(trR))) ;
X_Zuf := ABS(trR - DINT_TO_REAL(TRUNC(trR))) ; // VorkommaStellen löschen
```
Das die erste Zeile eine Warnung wirft ist erstmal völlig i.o. Die 2. Zeile läuft ohne Anmerkung des Compilers durch.

Holger


----------



## Ratoncito (8 Dezember 2020)

Hallo,

nur mal nebenbei

Der IEC-Operator dient der  Konvertierung vom Typ REAL zum Typ DINT. CODESYS nimmt *nur* den ganzzahligen Anteil der Zahl.

diVar := TRUNC (-1.4) ; (* Result: -1 *)


Das Ergebnis enthält aber den ganzzahligen Teil mit dem Vorzeichen, und nicht *nur* den ganzzahligen Anteil der Zahl.


Der von Heinileini geänderte Code ist okay.


----------



## Heinileini (8 Dezember 2020)

Ratoncito schrieb:


> In Beitrag#55 ist mein aktuelles Problem beschrieben: zu Beginn werden 2 Aufgaben gleichzeitig aufgerufen.


Während die PausenZeit läuft, muss der "AufgabenVerteiler" stillgelegt, also nicht durchlaufen werden. 
IF NOT Pause THEN 
... "AufgabenVerteiler" ...
END_IF ;

@Holger
Danke! Offensichtlich (zumindest in Deinem Link) wurde TRUNC geändert von INT in DINT und die Beschreibung korrigiert, was den "Betrag" betrifft (jetzt "ganzzahliger Anteil").

Gruss, Heinileini


----------



## Ratoncito (9 Dezember 2020)

Hallo Heinileini,

Dein Code zur Abarbeitung der Aufgaben ist sicherlich strukturierter programmiert als meine Ansätze, aber ich habe ihn nicht ans Laufen bekommen. Warum auch immer. Daher habe ich mir Gedanken gemacht und nach einer Lösung gesucht.
Dieser Ansatz funktioniert nun, daher möchte ich gerne darauf aufbauen und die weiteren Schritte zufügen. Mir fällt es so leichter den Ablauf zu verstehen.

Das ist mein Ansatz für AUF mit zufälliger Pausenzeit:

```
IF AUF_Start = TRUE THEN                                                    // nur für Test
    IF Y_WR = FALSE THEN                                                        // nur für Test
        iAct := 0;
        PauseIN := TRUE;
        Y_WR := TRUE;    
    END_IF                                                                        //
END_IF

   TP_Pause (IN := PauseIN, PT := PausePT) ;            
   Pause := TP_Pause.Q;
   
IF iAct < 7 THEN
    IF Pause = FALSE THEN
    CASE giA[iAct] OF
        1: Auf_1 := TRUE;
            PauseIN := TRUE;
        2: Auf_2 := TRUE;
            PauseIN := TRUE;
        3: Auf_3 := TRUE;
            PauseIN := TRUE;
        4: Auf_4 := TRUE;
            PauseIN := TRUE;
        5: Auf_5 := TRUE;
            PauseIN := TRUE;
        6: Auf_6 := TRUE;
            PauseIN := TRUE;
        7: Auf_7 := TRUE;
            PauseIN := TRUE;
    END_CASE        
    END_IF
    PauseIN := FALSE;
    IF Pause = FALSE THEN
        Y_Startwert := FALSE;                                                            //Funktion für Zafallszahl stoppen und anschließend Pausenzeit berechnen
        PausePT := REAL_TO_TIME((X_Zuf * TIME_TO_REAL(PauseMax - PauseMin)) + TIME_TO_REAL(PauseMin)) ;  // Wartezeit in ms
        iAct:= iAct + 1;
        PauseIN := TRUE;
        Y_Startwert := TRUE;                                                                //Funktion für Zufallszahl starten
    END_IF
ELSE 
    PauseIN := FALSE;
        Auf_1 :=FALSE;
        Auf_2 :=FALSE;
        Auf_3 :=FALSE;
        Auf_4 :=FALSE;
        Auf_5 :=FALSE;
        Auf_6 :=FALSE;
        Auf_7 :=FALSE;
    AUF_Start := FALSE;                                                                    //nur zum Test, später anpassen
    Y_WR := FALSE;                                                                            //nur zum Test, später anpassen
    Y_Startwert := FALSE;                                                    
END_IF
```

Nein, an mir ist sicherlich kein Programmierer verloren gegangen. Daher möchte ich Euch bitten, diesen Code zu verbessern, oder mir zu zeigen, wie man dies richtig macht.

Im Voraus vielen Dank für Eure Hilfe


----------



## Heinileini (9 Dezember 2020)

Moin Wolfgang,

habe jetzt alles komplett (hoffentlich!?) im FB zusammengeschrieben und auch zaghaft versucht, auf Deine VariablenNamen zu ändern (letzteres aber wieder abgebrochen, um DoppeltGemoppeltes zu vermeiden).
Der FB sollte jetzt selbständig in der Lage sein, ...
- die Initialisierung und
- die nächste ArrayBelegung
anzuleiern.
Wenn diese beiden Aktionen auch noch bzw. nicht mehr von extern (über die Inputs) gestartet werden sollen: siehe Zeilen mit '(optional)' im Kommentar.

Das Thema 'globale Variablen' ist jetzt vom Tisch. Sollte jetzt so funktionieren, wenn nur noch der FB darauf zugreift.

Verzeih mir, wenn ich nicht auf Deine Bitte eingegangen bin, aber ich blicke auch schon so doch kaum noch durch! 

Gruss, Heinileini

```
FUNCTION_BLOCK UpsAndDowns

    TOF_Pause     : TOF  ; // Timer für (variable) Pausenzeit

VAR_INPUT
    ibInit        : BOOL ; // pos. Flanke: Initialisierung des ZufallszahlenGenerators und des Array (optional)
    ibNext        : BOOL ; // pos. Flanke: nächste ArrayBelegung (optional)
    itMax         : TIME ; // OberGrenze  für Wartezeit [ms] (inklusive Ausführungszeit)
    itMin         : TIME ; // UnterGrenze für Wartezeit [ms] (inklusive Ausführungszeit)
END_VAR

VAR_IN_OUT
    iobBlindsDown : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge schliessen"
    iobBlindsUp   : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge öffnen"
END_VAR

VAR_OUTPUT
    obBlind1Down  : BOOL ; // schliesst Rollladen 1
    obBlind1Up    : BOOL ; // öffnet    Rollladen 1
    obBlind2Down  : BOOL ; // schliesst Rollladen 2
    obBlind2Up    : BOOL ; // öffnet    Rollladen 2
    obBlind3Down  : BOOL ; // schliesst Rollladen 3
    obBlind3Up    : BOOL ; // öffnet    Rollladen 3
    obBlind4Down  : BOOL ; // schliesst Rollladen 4
    obBlind4Up    : BOOL ; // öffnet    Rollladen 4
    obBlind5Down  : BOOL ; // schliesst Rollladen 5
    obBlind5Up    : BOOL ; // öffnet    Rollladen 5
    obBlind6Down  : BOOL ; // schliesst Rollladen 6
    obBlind6Up    : BOOL ; // öffnet    Rollladen 6
    obBlind7Down  : BOOL ; // schliesst Rollladen 7
    obBlind7Up    : BOOL ; // öffnet    Rollladen 7
END_VAR

VAR
    giA : ARRAY[0..6] OF INT; // Array mit 7 zufällig angeordneten Zahlen 1..7
    X_Zuf         : REAL ; // 0.0 < ZufallsZahl < 1.0
    X_Prim        : INT  ; // Primzahl 2..32749
    iAct          : INT := 8 ; // 0..6: Index für giA[]; 7: letzte Aufgabe der Sequenz; 8: Sequenz beendet
    trR           : REAL ; // temporär
    bTimerIN      : BOOL ; // StartImpuls für Pausen-TOF 
    sbInitAut     : BOOL ; // FlankenMerker automatische Initialisierung  
    sbNextAut     : BOOL ; // FlankenMerker automatische nächste ArrayBelegung 
    sbInitPre     : BOOL ; // FlankenMerker Initialisierung von extern (optional)
    sbNextPre     : BOOL ; // FlankenMerker nächste ArrayBelegung von extern (optional)
    tiTmp         : INT  ; // temporär 
    tiIdx1        : INT  ; // temporär
    tiIdx2        : INT  ; // temporär
    tTimerPT      : TIME ; // Dauer für Pausen-TOF im Bereich laut itMin .. itMax (z.B. 60..300 s)
END_VAR

// Prüfung, ob "AUF" und "ZU" gleichzeitg. Wenn ja, dann "AUF" löschen
iobBlindsUp := iobBlindsUp AND NOT iobBlindsDown ; 
// Prüfung, ob Init erforderlich
If X_Prim < 2 OR X_Prim > 32740 OR giA[0] = 0 OR X_Zuf <= 0.0 OR X_Zuf >= 1.0 Then 
    sbInitAut := FALSE ;
END_IF ;  
// = = = = = < Initialisierung ZufallsZahl und Array > = = = = =
// IF (iobBlindsUp OR iobBlindsDown) AND NOT sbInitAut OR ibInit AND NOT sbInitPre THEN 
IF (iobBlindsUp OR iobBlindsDown) AND NOT sbInitAut THEN 
    // - - - < Array 0..6 initialisieren mit 1..7 > - - -
    tiTmp := giA[0] ;
    For tiIdx1 := 0 To 6 Do
        giA[tiIdx1] := (tiIdx1 + tiTmp) Mod 7 + 1 ;
    END_FOR ;
    // - - - < Startwert für X_Zuf bilden > - - -
    If X_Prim < 2 OR X_Prim > 32740 Then // X_Prim im gültigen Bereich?
        X_Prim := 2 ; // nein: mit Primzahl 2 vorbesetzen
    Else
        Repeat // ja: nächste Primzahl suchen
            X_Prim := X_Prim + 1 + X_Prim MOD 2 ; // +1 wenn gerade, sonst +2
            tiBis := DINT_TO_INT(TRUNC(SQRT(Int_To_Real(X_Prim)))) ; // Obergrenze für möglichen Faktor festlegen
            tbEnd := True ;
            For tiIdx1 := 3 To tiBis By 2 Do
                If X_Prim Mod tiIdx1 = 0 Then 
                    tbEnd := False ;
                    Exit ; // Prüfung abbrechen, wenn restlos teilbar
                End_If ;     
            End_For ;
        Until tbEnd
        End_Repeat ; // Suche beenden, wenn nicht restlos teilbar
    End_If ;
    trR := SQRT(Int_To_Real(X_Prim)) ; // Wurzel aus Primzahl - gebraucht werden die Nachkommastellen
    X_Zuf := ABS(trR - DINT_TO_REAL(TRUNC(trR))) ; // Vorkommastellen löschen
    sbInitAut := TRUE ;
END_IF ;  
sbInitPre := ibInit ; // FlankenMerker (optional)
// = = = = = < Ende der Initialisierung von ZufallsZahl und Array > = = = = =

// = = = = = < Array-Shuffle > = = = = =
// IF (iobBlindsUp OR iobBlindsDown) AND NOT sbNextAut OR ibNext AND NOT sbNextPre THEN // (optional)
IF (iobBlindsUp OR iobBlindsDown) AND NOT sbNextAut THEN
    // - - - < Array rotieren > - - -
    tiTmp:= giA[0] ;
    For tiIdx1 := 0 To 5 Do
        giA[tiIdx1] := giA[tiIdx1 + 1] ;
    End_For ;
    giA[6] := tiTmp ;
    
    // - - - < Idx1 bilden > - - -
    X_Zuf := ABS(X_Zuf * 997.0 - Dint_To_Real(TRUNC(X_Zuf * 997.0))) ; // nächste ZufallsZahl, Vorkommastellen löschen
    tiIdx1 := Real_To_Int(X_Zuf * 7.0) ; // sklaieren in den Bereich 0..6
    
    // - - - < Idx2 bilden > - - -
    X_Zuf := ABS(X_Zuf * 997.0 - Dint_To_Real(TRUNC(X_Zuf * 997.0))) ; // nächste ZufallsZahl, Vorkommastellen löschen
    tiIdx2 := Real_To_Int(X_Zuf * 7.0) ; // sklaieren in den Bereich 0..6
    
    // - - - < ggfs Idx2 korrigieren > - - -
    If tiIdx1 = tiIdx2 Then 
        tiIdx2 := (tiIdx2 + 3) Mod 7 ;
    End_If ;
    
    // - - - < 2 zufällig festgelegte Elemente des Array tauschen > - - -
    tiTmp:= giA[tiIdx1] ; 
    giA[tiIdx1] := giA[tiIdx2] ;
    giA[tiIdx2] := tiTmp ;
END_IF ; 
sbNextPre := ibNext ; // FlankenMerker (optional)
sbNextAut := iobBlindsUp OR iobBlindsDown ; // FlankenMerker
// = = = = = < Ende von Array-Shuffle > = = = = =

// = = = = = < Rollläden und Pausen steuern > = = = = =
    IF iAct > 7 THEN // Sequenz inaktiv
        IF iobBlindsUp OR iobBlindsDown THEN
            iAct := 0 ; // Sequenz inaktiv und neue Aufgabe, dann Sequenz auf Anfang setzen
        END_IF ;
    ELSIF NOT TOF_Pause.Q THEN // Sequenz aktiv, warten auf PausenEnde, dann Aufgabe beenden und ggfs nächste starten
        obBlind1Up   := FALSE ; obBlind1Down := FALSE ;
        obBlind2Up   := FALSE ; obBlind2Down := FALSE ;
        obBlind3Up   := FALSE ; obBlind3Down := FALSE ;
        obBlind4Up   := FALSE ; obBlind4Down := FALSE ;
        obBlind5Up   := FALSE ; obBlind5Down := FALSE ;
        obBlind6Up   := FALSE ; obBlind6Down := FALSE ;
        obBlind7Up   := FALSE ; obBlind7Down := FALSE ;
        IF iAct < 7 THEN // AuftragsGenerierung aktiv
            CASE giA[iAct] OF // Rollladen-Nr
            1:  // Rollladen 1
                obBlind1Up := iobBlindsUp ; obBlind1Down := iobBlindsDown ;
            2:  // Rollladen 2
                obBlind2Up := iobBlindsUp ; obBlind2Down := iobBlindsDown ;
            3:  // Rollladen 3
                obBlind3Up := iobBlindsUp ; obBlind3Down := iobBlindsDown ;
            4:  // Rollladen 4
                obBlind4Up := iobBlindsUp ; obBlind4Down := iobBlindsDown ;
            5:  // Rollladen 5
                obBlind5Up := iobBlindsUp ; obBlind5Down := iobBlindsDown ;
            6:  // Rollladen 6
                obBlind6Up := iobBlindsUp ; obBlind6Down := iobBlindsDown ;
            ELSE // Rollladen 7
                obBlind7Up := iobBlindsUp ; obBlind7Down := iobBlindsDown ;
            END_CASE ; 
            bTimerIN := iobBlindsUp OR iobBlindsDown ; // TimerStart vorbereiten 
            IF bTimerIN THEN // falls Aufgabe, dann Pausenzeit (Zufall!) festlegen 
                X_Zuf := ABS(X_Zuf * 997.0 - DINT_TO_REAL(TRUNC(X_Zuf * 997.0))) ; // neue ZufallsZahl, Vorkommastellen löschen
                tTimerPT := REAL_TO_TIME((X_Zuf * TIME_TO_REAL(itMax - itMin)) + TIME_TO_REAL(itMin)) ; // skalieren, Wartezeit in ms
            END_IF ;
        END_IF ;
        iAct := iAct + 1 ; // ... und Vorlage der nächsten Aufgabe vorbereiten
    END_IF ;

    TOF_Pause (IN := bTimerIN, PT := tTimerPT) ; // Pausen-Timer
    bTimerIN := FALSE ; // TimerStart rücksetzen
    IF iAct > 7 THEN // wenn Sequenz komplett durchlaufen, dann AuftragsMerker rücksetzen ;
        iobBlindsUp := FALSE ; iobBlindsDown := FALSE ; sbNextAut := FALSE ;
    END_IF ;
// = = = = = < Ende von Rollläden und Pausen steuern > = = = = =
END_FUNCTION_BLOCK
```


----------



## Ratoncito (9 Dezember 2020)

Hallo Heinileini,



Heinileini schrieb:


> Verzeih mir, wenn ich nicht auf Deine Bitte eingegangen bin, aber ich blicke auch schon so doch kaum noch durch!



Nein, das verzeihe ich nicht 
Wenn Du angeblich kaum noch durchblickst, was soll ich dann sagen?

Nun gut, ich gebe mich geschlagen und habe es ausprobiert - im Prinzip läufts. Ich habe es einige Male AUF und AB laufen lassen. Dabei sind zwei mal doppelte Zahlen im Array aufgetaucht. Werde mir das morgen noch in Ruhe anschauen. Brauche mal wieder eine Weile, bis ich den Ablauf durchschaue.

Vielen lieben Dank für Deine Hilfe und noch einen schönen Abend

PS Danke für die vielen Kommentare 
@ Mache Dir bitte keine Mühe, ich teste alles nochmal in aller Ruhe.


----------



## Ratoncito (10 Dezember 2020)

Hallo Heinileini,

hier mein Update:

Beim Start des FB wird das Array nicht korrekt gefüllt. Statt der 5 enthält es immer die Zahl 8182.
Vermutlich ist der Code < 2 zufällig festgelegte Elemente des Array tauschen > die Ursache. Wenn ich ihn auskommentiere ist das Array korrekt gefüllt.

Force ich die Zahl 8182 im Array auf 5 funktioniert der FB eine ganze Weile. Dann taucht im Array mal eine Zahl auf, die nicht korrekt ist. Zum Beispiel -14702
Auch hier vermute ich den Fehler im Code < 2 zufällig festgelegte Elemente des Array tauschen >
tiTmp hat dann den Wert -14702

Wenn ich alles richtig deute, wurde für tidx2 die gleiche Zahl wie für tidx1 gebildet und das Tauschen schon während der Erzeugung der Ersatzzahl für tidx2 ausgeführt. Bin mir dabei aber nicht sicher.


----------



## Heinileini (10 Dezember 2020)

Moin Wolfgang,
leider kann ich bisher nicht viel Verdächtiges finden.
- 2 Tippfehler im Kommentar (sklaieren statt skalieren)
- 2 Stellen, an denen 'tiTmp :=' stehen sollte und das Leerzeichen vor dem ':=' fehlt
Wie der Compiler auf die fehlenden Leerzeichen reagiert, weiss ich nicht. Füg sie bitte ein.
Ansonsten bitte auch mal darauf achten, ob ...
- 'X_Zuf' jetzt wirklich immer nur positive Werte im Bereich >0.0 .. <1.0 enthält und
- 'tiIdx1' und 'tiIdx2' immer nur positive Werte im Bereich 0..6 enthalten.


----------



## Ratoncito (10 Dezember 2020)

Hallo Heinileini,

Leerzeichen vor oder nach dem ':='  scheinen egal zu sein, lediglich ein Leerzeichen zwischen dem Doppelpunkt und dem Gleichheitszeichen erzeugt Fehler.
Ich habe die Leerstellen an den angesprochenen Stellen eingefügt.

Um ganz sicher zu sein, dass nichts von mir eingefügtes Fehler verursacht. In PLC_PRG habe ich am Baustein bei iobBlindsDown als Eingang die globale Variable Merker.Start_AB und bei iobBlindsUp die globale Variable Merker.Start_AUF eingetragen.

Den Baustein prüfe ich mit "Applikation simulieren". Dort habe ich die beiden Merker am Anfang des Code eingetragen und und Force sie auf True um die Funktion zu starten.

Die Fehlerbeschreibung habe ich in Beitrag#68 beschrieben.

Ich bin mir nicht sicher, ob meine Annahme richtig ist. Das Programm arbeitet den Code doch einmal durch, setzt am Ende alle Variablen und läuft dann den Code erneut durch usw.
Wenn ich es richtig beobachte, gibt es keine Fehler wenn ich < 2 zufällig festgelegte Elemente des Array tauschen > auskommentiere, dann wird auch zu Beginn das Array korrekt gefüllt.

Wenn ich die Auskommentierung entferne sieht es so aus, als wenn immer nur dann ein Fehler auftritt, wenn die zweite Zufallszahl erneut gebildet wird und beim Tauschen noch nicht erzeugt ist.

Eventuell bin ich hier aber auch total auf dem Holzweg.


----------



## Heinileini (10 Dezember 2020)

Ratoncito schrieb:


> ... sieht es so aus, als wenn immer nur dann ein Fehler auftritt, wenn die zweite Zufallszahl erneut gebildet wird und beim Tauschen noch nicht erzeugt ist.


Es beruhigt mich, dass das "fehlende Leerzeichen" nichts mit dem Spuk zu tun hat - das hätte mich nicht nur sehr gewundert, sondern derbe schockiert. 
Aber, dass der Index noch umgebogen wird, während mit seiner Hilfe bereits der Tauschvorgang ausgeführt wird ... irgendwie überschreitet das mein VorstellungsVermögen.
Wir sind doch hier mit PLC-Programmierung zugange, nicht mit NC-Programmierung!? 
An der Simulation wird es doch wohl nicht liegen.
Und was passiert, wenn - allen Erwartungen zum Trotz - bei der Adressierung des Array mit einem Index <0 oder >6 ins Leere gegriffen wird? Flippt die Steuerung bzw. die Simulation aus oder gibt's ne FehlerMeldung?

Sorry, mir fällt nichts mehr ein. Bitte weiterhin X_Zuf und die beiden Indizes tiIdx1 & tiIdx2 im Auge behalten, wie schon zuvor gesagt.

Grübel, grübel ...


----------



## Ratoncito (11 Dezember 2020)

Hallo Heinileini,

Grübel bitte nicht zuviel 



> Wir sind doch hier mit PLC-Programmierung zugange, nicht mit NC-Programmierung!?


Keine Ahnung, was ist der Unterschied?



> An der Simulation wird es doch wohl nicht liegen.



Ich werde das Programm mal auf die SPS laden und dort testen. Dazu brauche ich aber ein wenig Zeit. Ich melde mich, wenn ich das ausprobiert habe.



> Und was passiert, wenn - allen Erwartungen zum Trotz - bei der  Adressierung des Array mit einem Index <0 oder >6 ins Leere  gegriffen wird? Flippt die Steuerung bzw. die Simulation aus oder gibt's  ne FehlerMeldung?



Wenn ich es richtig beobachtet habe, wird das letzte Array (7) anstelle des falschen Wertes aufgerufen. Die ganze Prozedur läuft aber bis zum Ende durch. Ich achte nochmal genau darauf.



> Ich bin mir nicht sicher, ob meine Annahme richtig ist. Das Programm  arbeitet den Code doch einmal durch, setzt am Ende alle Variablen und  läuft dann den Code erneut durch usw.



Ist das richtig?


----------



## Heinileini (11 Dezember 2020)

Ratoncito schrieb:


> 1. Keine Ahnung, was ist der Unterschied?
> 
> 2. Wenn ich es richtig beobachtet habe, wird das letzte Array (7) anstelle des falschen Wertes aufgerufen.
> 
> ...


Zu 1.: 
Bei PLC-Programmen kann man sich darauf verlassen, dass die Befehle immer schön "der Reihe nach" abgearbeitet werden, so, wie man es erwartet.
Ausnahmen sind, wenn ein Ablauf durch einen anderen unterbrochen wird, z.B. durch einen ZeitAlarm. Dann sollte aber, sobald der Alarm ("Interrupt") abgearbeitet ist, das Programm sauber ab der Stelle weiter ausgeführt werden, an der es unterbrochen wurde.
Bei NC-Programmen ist es so, dass mehrere folgende Befehle schon parallel in Angriff genommen werden, weil die NC-Befehle in ihrer Auswirkung z.T. nahtlos ineinander übergehen müssen, also die Ausführung im Detail davon abhängig ist, wie es anschliessend weitergehen soll. Z.B. die Fahrt in X-Richtung abbremsen und dann die Fahrt in Y-Richtung starten, ggfs mit "Verrunden" der Ecke. 
Das kann zu Überraschungen führen, dadurch dass z.B. bereits mit Werten gerechnet wird, die im ProgrammAblauf aber erst später festgelegt werden. Aber vergessen wir diesen Blick über den Tellerrand ganz schnell wieder, solange wir in der PLC arbeiten und keine "parallel" laufenden Tasks haben.  

Zu 2.:
"das letzte Array (7)"? Eigentlich haben wir ein Array [0..6]!

Zu 3.:
Ja, die Prozedur wird in jedem Zyklus komplett durchlaufen. Komplett heisst von Anfang bis Ende, aber natürlich nur diejenigen Abschnitte, die nicht übersprungen werden (wegen IF oder CASE oder WHILE oder EXIT).


----------



## Ratoncito (11 Dezember 2020)

Hallo Heinileini,

Okay, keine klare Aussage von mir. 
Array [0 ... 6] mit Inhalt (1 ... 7) in gemischter Reihenfolge. Bei einem Fehler steht, wie zum Beispiel beim ersten Starten statt der 5 die 8182 als Inhalt im Array.
In der Case-Anweisung werden diese aufgerufen, dabei wird statt AUF_5 oder AB_5 die 7 ausgeführt. Alles läuft dann weiter, bis iAct 7 ist. Danach ist ein neuer Start nötig.

Ich habe das Programm allerdings noch nicht in der SPS getestet.

Bringt es etwas, wenn man das Bilden der 2 Zahlen in einer If-Schleife ausführt und erst danach das Tauschen zum Ende mit iAct = 7 ausführt?


----------



## Ratoncito (11 Dezember 2020)

Hallo Heinileini,

mal eine Anmerkung


```
tiTmp := giA[Zuf_1] ; 
    giA[Zuf_1] := giA[Zuf_2] ;
    giA[Zuf_2] := tiTmp ;
```

Wird hier nicht auf einen Bereich des Arrays [ 0 ... 6 ] mit einer Zufallszahl ( Zuf_1 oder Zuf_2 ) zugegriffen, die Werte zwischen 1 ... 7 annehmen können?

Ist nur so eine Vermutung, eventuell bin ich aber auch auf dem Holzweg.

Habe es immer noch nicht in der Steuerung probieren können.


----------



## Heinileini (11 Dezember 2020)

Ratoncito schrieb:


> Wird hier nicht auf einen Bereich des Arrays [ 0 ... 6 ] mit einer Zufallszahl ( Zuf_1 oder Zuf_2 ) zugegriffen, die Werte zwischen 1 ... 7 annehmen können?


Du bist genial, Wolfgang, bitte in den beiden Zeilen 7.0 ändern in *6*.0 ! 

```
tiIdx1 := Real_To_Int(X_Zuf * 7.0) ; // skalieren in den Bereich 0..6
```


```
tiIdx2 := Real_To_Int(X_Zuf * 7.0) ; // skalieren in den Bereich 0..6
```
Sorry, der Real_to_Int rundet doch - wie oft habe ich jetzt schon darüber hinweg gelesen 

Aus lauter Verzweiflung wollte ich schon vorschlagen, in der VAR-Deklaration diverses zu initialisieren, aber ...


> Alle Variablendeklarationen und Datentypelemente können Initialisierungen (Zuweisung eines
> initialen Wertes) enthalten. Sie erfolgen mit dem Zuweisungsoperator " := ". Für Variablen von
> elementaren Typen sind diese Initialisierungen Konstanten. *Die Default-Initialisierung ist für alle*
> *Deklarationen 0.*


... sollte eigentlich genügen.


----------



## Ratoncito (11 Dezember 2020)

Hallo Heinileini,

die beiden Zeilen habe ich geändert. Jetzt funktionierts.

Ein blindes Huhn findet auch mal ein Korn 
Aber wenn es hilft, dass der Erstklässler jetzt in die zweite Klasse darf...

Ich hoffe, dass ich morgen alles ins Programm integrieren kann.

Nochmal vielen lieben Dank für Deine Mühe und Geduld.


----------



## Ratoncito (13 Dezember 2020)

Hallo Heinileini,

den FB habe ich ins Programm integriert und auf die Steuerung geladen. Es sieht so aus, als ob alles funktioniert. 

Nun habe ich noch Fragen zu 


```
sbInitAut : BOOL ; // FlankenMerker automatische Initialisierung  
    sbNextAut : BOOL ; // FlankenMerker automatische nächste ArrayBelegung 
    sbInitPre  : BOOL ; // FlankenMerker Initialisierung von extern (optional)
    sbNextPre : BOOL ; // FlankenMerker nächste ArrayBelegung von extern (optional)
```

Wofür sind die Variablen gut?
Wieso Flankenmerker, müssten Sie dann nicht als Datentyp R_TRIG deklariert sein?
Werden sie überhaupt benötigt?


```
VAR_IN_OUT
    Start_AB : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge schliessen"
    Start_AUF : BOOL ; // Startet Sequenz "Rollläden in zufälliger Reihenfolge öffnen"
END_VAR
```

Wieso IN_OUT, werden sie nicht nur zum Starten der Sequenz benötigt, also nur als IN?


----------



## Heinileini (13 Dezember 2020)

Ratoncito schrieb:


> 1. Wofür sind die Variablen gut?
> Wieso Flankenmerker, müssten Sie dann nicht als Datentyp R_TRIG deklariert sein?
> Werden sie überhaupt benötigt?
> 
> 2. Wieso IN_OUT, werden sie nicht nur zum Starten der Sequenz benötigt, also nur als IN?


Moin Wolfgang.

Zu 1.:
Ist denn R_TRIG ein eigener DatenTyp?
Die "FlankenMerker" benutze ich für die selbstgestrickten (bin so altmodisch!) FlankenErkennungen. Darin werden die Zustände für den Vergleich mit den aktuellen Zuständen im nächsten Zyklus gespeichert. 
Der Begriff "FlankenMerker" sagt übrigens nicht aus, dass man hierfür Merker benutzen muss, sondern nur, dass sie sich etwas merken müssen. 

Natürlich darfst Du zur FlankenErkennung wahlweise die von der ProgrammierSprache bereitgestellten HilfsMittel benutzen, *aber erst dann, wenn Du verstanden hast, wie eine FlankenErkennung funktioniert.* 
Jawoll, dies  eine Drohung! 
Du brauchst Dich nur in den Beiträgen hier im Forum umzusehen - die FlankenErkennung bzw. der Umgang mit den angebotenen vermeintlichen Vereinfachungen ist leider ein DauerBrenner.

Zu 2.:
Diese VAR_IN_OUTs dienen als Schnittstelle zum "Drumherum". Sie werden einerseits vom FB abgefragt, ob's Arbeit gibt und andererseits vom FB zurückgesetzt, wenn die Arbeit erledigt ist.
Immerhin dauert es ja eine Weile, bis die 7 RollladenAktionen mit ihren anschliessenden Pausen fertig sind.
Somit kannst Du im "Drumherum" die entsprechenden Bits abfragen, mit denen Du den FB parametriert hast, um zu erfahren ob/wann die gestartete Prozedur beendet ist und Du brauchst Dir auch keinen Kopf zu machen, wann der richtige Moment gekommen ist, diese Bits wieder zu löschen.

Ein schönes RestWochenEnde wünscht Dir  Heinileini

PS:
Nachtrag zu 1.: Natürlich werden sie benötigt. Die Aktionen "Initialisieren" und "nächste ArrayBelegung festlegen" sollen nur einmalig ausgeführt werden, wenn man sie startet.
Stell Dir vor, während eine der Aktionen "Rollläden öffnen" oder "Rollläden schliessen" aktiv ist, würde ständig der Inhalt des Array weiter durcheinandergewirbelt ... wie willst Du dann sicherstellen, dass auch alle 7 Rollläden geöffnet bzw. geschlossen werden? "MomentAufnahme" machen und mit einer Kopie des Array arbeiten, die gar nicht nötig ist?


----------



## Ratoncito (13 Dezember 2020)

Hallo Heinileini,

zu 1.) zumindest werden 2 Bausteine angeboten.

Bevorzugt verwende ich FUP, und dort ganz gerne die an den Eingängen angebotene Flankenerkennung. Diese Version kannte ich aus meiner Programmiererei an der alten SPS noch nicht.

So richtig verstanden habe ich die Flankenerkennung sicherlich nicht, denn ich werde immer mal wieder überrascht, dass etwas nicht so funktioniert, wie ich das erwarte.
Kennst Du gute Tutorials die ich durchackern könnte?

Auch Dir noch einen schönen Sonntag


----------



## Heinileini (13 Dezember 2020)

Ratoncito schrieb:


> So richtig verstanden habe ich die Flankenerkennung sicherlich nicht, denn ich werde immer mal wieder überrascht, dass etwas nicht so funktioniert, wie ich das erwarte.


Zum Glück liegt es nicht immer an der FlankenErkennung, wenn mal etwas nicht so funktioniert, wie man es erwartet.

Und wenn es doch mal so sein sollte, dass es an der FlankenErkennung scheitert, dann ist das ein Grund mehr, sich solange mit dem Thema zu beschäftigen, bis man es kapiert hat! Ich versichere Dir, es ist keine Hexerei, sondern ganz simpel und durchaus logisch. Und das gilt auch für die FlankenMerker: sie müssen bis zur nächsten Abfrage unverändert erhalten bleiben, d.h. die dürfen nicht temporär sein und auch nicht anderweitig benutzt werden - eine Benutzung als FlankenMerker für die FlankenErkennung eines anderen Signals wäre definitiv schon eine anderweitige Benutzung!

Gute Tutorials? Weiss ich nicht. Auch in hochwohlgelobten Lehrgängen etc. stolpert man über Tippfehler, Verwechselungen und sonstige schlampige oder irreführende bis definitiv falsche Formulierungen.
Nicht alles abnicken, was man so liest oder hört. Schön wachsam bleiben und mitdenken. Wenn man die Zeit dafür hat, auch mal Gedanken machen "wie würde ich versuchen, das Problem zu lösen?", bevor man sich mit einem Lösungsweg berieseln lässt. Zumindest kann man die vorgefertigten Lösungen dann besser einschätzen, aber auch Stolpersteine in der eigenen Denkweise besser erkennen ... und daraus lernen.


----------

