# Hilfe bei Bool Array vergleichen



## Passion4Automation (12 August 2022)

Hallo,

ich hätte eine Frage zu meinem Code.
Ich möchte gerne Sensoren (Bool) in ein Array einlesen und dieses Array auf Änderungen vergleichen.
Hat sich was geändert soll ein Bit gesetzt werden.Ich denke mit einer For Schleife würde das gehen, es klappt aber nicht.




```
PROGRAM PLC_PRG
VAR
xaZustand_Akt:        ARRAY[0..40] OF BOOL;   
xaZustand_Alt:        ARRAY[0..40] OF BOOL;
iIndex:                INT:=0;
xAenderung:            BOOL;


xFenster1:            BOOL;
xFenster2:             BOOL;
xFenster3:            BOOL;
xFenster4:            BOOL;
xFenster5:            BOOL;
END_VAR
```



```
xaZustand_Akt[0]:=xFenster1;
xaZustand_Akt[1]:=xFenster2;
xaZustand_Akt[2]:=xFenster3;
xaZustand_Akt[3]:=xFenster4;
xaZustand_Akt[4]:=xFenster5;



FOR iIndex:=0 TO 40 BY 1 DO
   IF (xaZustand_Akt [iIndex] <>xaZustand_Alt [iIndex]) THEN       (* ist Unterschied zum letzten Zyklus?*)
      xAenderung:=TRUE;
   EXIT;                                                          (* Bei Unterschied "Aenderung" auf TRUE setzen und aus der Schleife springen*)
   ELSE xAenderung:= FALSE;
   END_IF
           IF iIndex = 40 THEN
            iIndex:= 0;
        END_IF
END_FOR;



xaZustand_Alt:=xaZustand_Akt;                                   (*Alarmarray nach alt schieben*)
```


Was mache ich da falsch?

Danke.


----------



## DeltaMikeAir (12 August 2022)

goifalracer schrieb:


> IF iIndex = 40 THEN iIndex:= 0; END_IF


Das brauchst du nicht. Das macht die FOR Schleife alles selbst.


Warum nicht einfach:

```
xAenderung:= xaZustand_Alt <> xaZustand_Akt;

xaZustand_Alt:=xaZustand_Akt;
```


----------



## DeltaMikeAir (12 August 2022)

goifalracer schrieb:


> es klappt aber nicht


PS:
Was bedeutet "klappt nicht"? Es ist halt nur eine Flanke, beobachten kannst du die nicht.


----------



## Passion4Automation (12 August 2022)

Hi,

also wenn ich das nicht mache

```
IF iIndex = 30 THEN
            iIndex:= 0;
```
dan bleibt die Schleife bei Index 41 stehen.


```
xAenderung:= xaZustand_Alt <> xaZustand_Akt;

xaZustand_Alt:=xaZustand_Akt;
```

Du meinst das ganze nur mit 2 Arrays ohne Schleife umzusetzen?
Sollte eigentlich auch klappen


```
PS:
Was bedeutet "klappt nicht"? Es ist halt nur eine Flanke, beobachten kannst du die nicht.
```

Wenn ich x Aenderung auf einen FBStromstoß verknüpfe, wird der Fb nicht gesetzt.

Danke.


----------



## DeltaMikeAir (12 August 2022)

goifalracer schrieb:


> dan bleibt die Schleife bei Index 41 stehen.


Der Index verlässt die Schleife mit der Wertigkeit 41. Innerhalb der FOR Schleife ist die höchste Wertigkeit 40.


----------



## Thomas_v2.1 (12 August 2022)

Ich würde die Schleife so verkürzen, das ist auch die üblichere Vorgehensweise:


```
xAenderung := FALSE;
FOR iIndex := 0 TO 40 DO
  IF (xaZustand_Akt [iIndex] <> xaZustand_Alt [iIndex]) THEN       (* ist Unterschied zum letzten Zyklus?*)
    xAenderung:=TRUE;
    EXIT;
  END_IF;
END_FOR;
```


----------



## Passion4Automation (12 August 2022)

Scheint jetzt zu klappen.
Hab jetzt

```
IF iIndex = 30 THEN
            iIndex:= 0;
```
entfernt.

Hab anscheinend vorher die Flanke im Zyklus immer verpennt.

Na ja, war meine erste Schleife.


----------



## Thomas_v2.1 (13 August 2022)

DeltaMikeAir schrieb:


> Der Index verlässt die Schleife mit der Wertigkeit 41. Innerhalb der FOR Schleife ist die höchste Wertigkeit 40.


Wobei der Endwert der Indexvariable nach dem Schleifendurchlauf nicht durch irgendeine Norm festgelegt ist. Man müsste das also auf dem konkreten Zielsystem ausprobieren, oder besser, eine andere Lösung dafür finden, und sich nicht auf einen Wert nach dem Schleifendurchlauf verlassen.


----------



## Passion4Automation (13 August 2022)

Ich teste das die Tage mal auf Hardware, das war jetzt rein in der eCockpit Simulation.
Deswegen war ich ja so verwirrt, weil mir der Index bei 41 stehen geblieben ist und ich dachte die Schleife muss ich selbst anstoßen.
Aber ich hab ja jetzt selbst festgestellt das die Schleife von alleine mit dem Zyklus dauerhaft  mit rennt.


----------



## Thomas_v2.1 (13 August 2022)

Wenn du dich darauf verlässt, dann musst du deine Programmierumgebung für immer auf diesem Stand einfrieren. In der Norm nennt sich so ein Verhalten "implementation dependent", und darauf sollte man sich niemals verlassen. Du weißt oben doch auch die 40, also wenn du wolltest dann wüsstest du die 40 unten auch als Konstante.
Wenn du die Position an der die Änderung gefunden wurde benötigst, dann kannst du dir diese auch separat abspeichern.


----------



## Heinileini (13 August 2022)

goifalracer schrieb:


> Was mache ich da falsch?


Keine Ahnung, da ich nicht weiss, was Du eigentlich tun willst.

```
IF (xaZustand_Akt [iIndex] <>xaZustand_Alt [iIndex]) THEN       (* ist Unterschied zum letzten Zyklus?*)
```
"Unterschied zum letzten Zyklus?" Woher kommt die Information, welches der Zustand im letzten Zyklus war?
Kopierst Du etwa nach der gezeigten Schleife noch irgendwo das Array xaZustand_Akt [ ] in das Array xaZustand_Alt [ ] ?
Wenn ja, ist das dann auch das, was Du machen willst?
Der erstbeste gefundene Unterschied führt zum vorzeitigen Abbruch Deiner Schleife und die noch ausstehenden Vergleiche werden nicht mehr durchgeführt.
Ist es OK, wenn die noch nicht verglichenen ArrayElemente nicht mehr auf Unterschiede geprüft werden und trotzdem auch diese Elemente der Alt-Arrays auf den Stand der entsprechenden Akt-Elemente aktualisiert werden? Weitere bestehende Unterschiede würden dann blindlings "glattgebügelt", ehe Du sie überhaupt feststellen kannst?


----------



## Passion4Automation (13 August 2022)

Also ich mache die Aufgabenstellung nochmal deutlich.

Istzustand:
Die Sensoren dienen der Auslösung einer Alarmanlage, aktuell muss diese per Hand scharf gestellt werden und als Bedingung müssen alle Fenster, Tore usw. geschlossen sein damit die scharf wird.
Da man nicht jedesmal alles schliessen will, sondern man auch mal Fenster gekippt lässt, sollen wenigstens die aktuell geschlossenen Fenster überwacht werden.
Sollzustand:
Per Eneocean Tracker wird die SPS über meine Abwesenheit informiert und dann sollen die beiden Arrays auf Unterschiede und Änderungen überwacht werden.
Wird xAenderung True, dann soll Alarm ausgelöst werden.

@Thomas_v2.1

```
Wenn du dich darauf verlässt, dann musst du deine Programmierumgebung für immer auf diesem Stand einfrieren. In der Norm nennt sich so ein Verhalten "implementation dependent", und darauf sollte man sich niemals verlassen. Du weißt oben doch auch die 40, also wenn du wolltest dann wüsstest du die 40 unten auch als Konstante.
Wenn du die Position an der die Änderung gefunden wurde benötigst, dann kannst du dir diese auch separat abspeichern.
```

Ich kann dir da leider nicht ganz folgen.
Ist mein Code so nicht korrekt?
Du meinst das ich das Bit im Array ermitteln soll welches die Änderung verusacht hat?
Das wäre auch interressant, weil man könnte dann den Auslöser genau ermitteln.

@Heinileini 


```
"Unterschied zum letzten Zyklus?" Woher kommt die Information, welches der Zustand im letzten Zyklus war?
Kopierst Du etwa nach der gezeigten Schleife noch irgendwo das Array xaZustand_Akt [ ] in das Array xaZustand_Alt [ ] ?
```

Also die Schleife wird ja in jedem Zyklus aufgerufen. Und nach jedem Schleifendurchgang wird das aktuelle Array in das Alte Array kopiert, sollte im nächsten Zyklus dann wieder eine Änderung sein, dann wird xAenderung wieder kurz (True)
Es funktioniert auch sowiet wie ich mir das vorstelle, allerdings nur in der Simulation getestet.


```
Ist es OK, wenn die noch nicht verglichenen ArrayElemente nicht mehr auf Unterschiede geprüft werden und trotzdem auch diese Elemente der Alt-Arrays auf den Stand der entsprechenden Akt-Elemente aktualisiert werden? Weitere bestehende Unterschiede würden dann blindlings "glattgebügelt", ehe Du sie überhaupt feststellen kannst?
```

Ich denke, ja ist ok. Weil es ist egal ob nach der ersten Änderung im Array 10 Sek.  später eine zweite Änderung kommt und diese aber schon wieder überschrieben wurde.Wenn 1 Fenster offen ist, dann muss Alarm ausgelöst werden.

Wie würde das aussehen, also die Schleife nach der ersten Änderung nicht abbbrechen, sondern noch abarbeiten lassen  und dann die Positionen die sich geändert haben, abzuspeichern.

Danke.


----------



## PN/DP (13 August 2022)

Üblicherweise packt/kopiert man die Eingänge in DWORDs und kann dann ohne Schleifen sehr einfach viele Bits auf einmal verknüpfen und auswerten mit Bitstring-Operationen. Da bleibt auch die Information erhalten, genau welche Bits betroffen sind (z.B. geändert sind). Bei DWORDs ist z.B. sehr einfach auszuwerten ob kein, oder irgendein, oder alle Bits 1 sind. Es können auch sehr einfach Bits von der Auswertung ausgeschlossen werden (dwXxx AND dwEnableBitfield). Braucht man mehr als 32 Bits, dann einfach mit mehreren DWORD arbeiten (z.B. Array of DWORD).

```
dwZustand_Akt[1].0:=xFenster1;
dwZustand_Akt[1].1:=xFenster2;
...
dwZustand_Akt[1].31:=xFenster32;
dwZustand_Akt[2].0:=xFenster33;
dwZustand_Akt[2].1:=xFenster34;
...

dwPosFlanken[1] := dwZustand_Akt[1] AND NOT(dwZustand_Alt[1]);
dwNegFlanken[1] := NOT(dwZustand_Akt[1]) AND dwZustand_Alt[1];
dwGeaendert[1]  := dwZustand_Akt[1] XOR dwZustand_Alt[1];  //oder: dwPosFlanken[1] OR dwNegFlanken[1];
...

xAenderung := (dwGeaendert[1] <> 0) OR (dwGeaendert[2] <> 0);
//oder: xAenderung := (dwGeaendert[1] OR dwGeaendert[2]) <> 0;
...

dwZustand_Alt[1] := dwZustand_Akt[1]; //Eingangszustände für nächsten Zyklus merken
dwZustand_Alt[2] := dwZustand_Akt[2];
```

Harald


----------



## PN/DP (13 August 2022)

PS: bei DWORD ist auch einfach auszuwerten, ob genau ein Bit 1 ist, ohne die 1-Bits suchen zu müssen.

Harald


----------



## Passion4Automation (13 August 2022)

Hi PN/DP,

einfach aber macht alles was ich brauche und alles ohne Schleife...
Machmal denkt man zu kompliziert.

Ob jetzt deine Lösung oder mit Schleife ist wsl. Geschmackssache.

Ich denke, ich werde deine Lösung umsetzen.

Herzlich Dank für die Mühe.


----------



## Heinileini (13 August 2022)

goifalracer schrieb:


> Die Sensoren dienen der Auslösung einer Alarmanlage, aktuell muss diese per Hand scharf gestellt werden und als Bedingung müssen alle *Fenster, Tore* usw. geschlossen sein damit die scharf wird.


Aaach sooo, um die Art von Fenster geht es. Tschuldigung, aber im Zeitalter der ZeitFenster und jener Fenster, die sich auf BildSchirmen tummeln ...


goifalracer schrieb:


> Also die Schleife wird ja in jedem Zyklus aufgerufen. Und nach jedem Schleifendurchgang wird das aktuelle Array in das Alte Array kopiert, ...


Nach jedem SchleifenDurchgang? Davon sehe ich nichts in der Schleife.
Das wäre auch ein "mit Spatzen auf Kanonen schiessen". Ich denke das meinst Du nicht so.


goifalracer schrieb:


> ... 10 Sek.  später eine zweite Änderung ...


Ich denke auch, dass es bei Deiner Anwendung OK ist, wenn die Erkennung eines FlankenWechsels oder sogar mehrerer geschlabbert werden kann, wenn in *demselben* Zyklus auch bei einem der anderen Bit ein FlankenWechsel auftritt.
Kommt doch immer darauf an, was man erreichen will!


goifalracer schrieb:


> Wenn 1 Fenster offen ist, dann muss Alarm ausgelöst werden.


Eigentlich waren wir bei dem Thema "wenn ein Fenster geöffnet (oder geschlossen) wird". Daher die Erkennung, ob eine Änderung stattfindet.
Welche Art von Änderung darf's denn sein? Das Öffnen eines Fenster genügt? Oder darf/muss auch das Schliessen eines Fensters einen Alarm auslösen?
Entschuldige bitte diese scheinbar überflüssigen Fragen, aber man muss einfach irgendwann entscheiden, was das Programm genau tun soll und natürlich auch, was es nicht tun soll ... und auch, was es keinesfalls tun soll/darf.


goifalracer schrieb:


> Wie würde das aussehen, also die Schleife nach der ersten Änderung nicht abbbrechen, sondern noch abarbeiten lassen  und dann die Positionen die sich geändert haben, abzuspeichern.


Ganz einfach: die Schleife nicht vorzeitig mit Exit abbrechen. Aber, das kann/wird auch Auswirkungen auf andere Befehle des Programms / der Schleife haben und auch auf die "Struktur" des Programms.

"... noch abarbeiten lassen *und dann* die Positionen die sich geändert haben, abzuspeichern."
Wenn beide (oder noch weitere) Anforderungen in einer einzigen Schleife erledigt werden können, dann muss man sich natürlich auch Gedanken darüber machen, ob 1 oder 2 oder n Schleifen am zweckmässigsten sind. Bei Schleifen sollte/muss man immer die ZyklusZeit im Auge behalten.
Aber es kann durchaus die Abarbeitung von zwei oder mehr Schleifen nacheinander auch mal insgesamt schneller sein, als die Abarbeitung einer einzigen, die in jedem einzelnen SchleifenDurchlauf aufwändige Sachen erledigen muss. Also auch hier die StandardAntwort bzw. der StandardTipp "Jain, kommt ganz d'rauf an!".

"... die *Positionen*, die sich geändert haben, abzuspeichern ..."
Das ist ein neuer Aspekt. Auch lösbar. Auch verschiedene Lösungswege mit verschiedenen Möglichkeiten, Vorteilen, Nachteilen.
Auch hier nicht nur an das Abspeichern denken, sondern auch schon daran, für wie lange bzw. bis wann gespeichert bleiben soll und wieder löschen wann bzw. wodurch.



PN/DP schrieb:


> Üblicherweise packt/kopiert man die Eingänge in DWORDs und kann dann ohne Schleifen sehr einfach viele Bits auf einmal verknüpfen und auswerten mit Bitstring-Operationen.


Oh ja! Den Kern der Aufgabe kann man dadurch stark vereinfachen. Manchmal ist aber der Aufwand, die einzelnen Bits in einem oder wenigen DWORD zusammenzufassen (und ggfs später den Inhalt des/der DWORDs wieder auf die einzelnen Bits zu verstreuen) weniger erfreulich.



goifalracer schrieb:


> Ob jetzt deine Lösung oder mit Schleife ist wsl. Geschmackssache.


Oh ja, ganz wie im "richtigen Leben"! 

Häwenaissuiikend, allemitenand!


----------



## Passion4Automation (13 August 2022)

```
Nach jedem SchleifenDurchgang? Davon sehe ich nichts in der Schleife.
Das wäre auch ein "mit Spatzen auf Kanonen schiessen". Ich denke das meinst Du nicht so.
```

Ja die läuft nur einmalig, würde Sie dann mit einem Taktmerker bzw. Puls-Pause Timer oder so ansteuern.
Noch eine IF Anweisung um die Schleife packen.


```
(*Schleife zum Alarm abfragen*)

xAenderung:= FALSE;                                                                    (*Array Aenderung zuruecksetzen*)

IF Puls.Output THEN

    FOR iIndex:=0 TO 50 BY 1 DO
          IF (xaZustand_Akt [iIndex] <>xaZustand_Alt [iIndex]) THEN               (* ist Unterschied zum letzten Zyklus?*)
                  xAenderung:=TRUE;
            EXIT;                                                                      (* Bei Unterschied "Aenderung" auf TRUE setzen und aus der Schleife springen*)
          END_IF
                    
    END_FOR;
            
xaZustand_Alt:=xaZustand_Akt;                                                       (*Alarmarray nach alt schieben*)
            
END_IF
```


```
Eigentlich waren wir bei dem Thema "wenn ein Fenster geöffnet (oder geschlossen) wird". Daher die Erkennung, ob eine Änderung stattfindet.
Welche Art von Änderung darf's denn sein? Das Öffnen eines Fenster genügt? Oder darf/muss auch das Schliessen eines Fensters einen Alarm auslösen?
Entschuldige bitte diese scheinbar überflüssigen Fragen, aber man muss einfach irgendwann entscheiden, was das Programm genau tun soll und natürlich auch, was es nicht tun soll ... und auch, was es keinesfalls tun soll/darf.
```

Alles gut, natürlich am besten auf sich öffnende Fenster, aber der Alarm kann auch ganz gerne kommen wenn der Einbrecher meint er muss ein Fenster schließen

Das sollte jetzt mit deimen Beispiel oder mit der Schleife lösbar sein.


----------



## struland (30 August 2022)

Ein weiterer Lösungsansatz wäre der DUT *UNION.
Datenstruktur: UNION
UNION in benutzerdefinierten Datentypen
UNION erweitern per Vererbung*

Union definieren

```
TYPE U_Zustand :
UNION
    A: ARRAY[0..40] OF BOOL;
    U: ULINT; 
END_UNION
END_TYPE
```

Hauptprogramm

```
PROGRAM PRG_Alarm
VAR
    uZustand_Akt: U_Zustand;   
    uZustand_Alt: U_Zustand;
    xAenderung:    BOOL;
    xFenster1:  BOOL;
    xFenster2:  BOOL;
    xFenster3:  BOOL;
    xFenster4:  BOOL;
    xFenster5:  BOOL;
END_VAR

uZustand_Akt.A[0] := xFenster1;
uZustand_Akt.A[1] := xFenster2;
uZustand_Akt.A[2] := xFenster3;
uZustand_Akt.A[3] := xFenster4;
uZustand_Akt.A[4] := xFenster5;

xAenderung := uZustand_Akt.U  <> uZustand_Alt.U;
```

Mit `xAenderung := uZustand_Akt.U  <> uZustand_Alt.U;` kann man sich so die Schleife sparen.


----------



## StructuredTrash (30 August 2022)

Das wird nicht funktionieren, weil ein ULINT nur 8 Bytes abdeckt, das Array aber 41 Byte gross ist.
Aber mit einem String könnte es gehen.

```
ZustandAkt:STRING[41];
ZustandAlt:STRING[41];

ZustandAkt[0]:=BOOL_TO_BYTE(xFenster1)+1;
ZustandAkt[1]:=BOOL_TO_BYTE(xFenster2)+1;
// usw.
xAenderung:=ZustandAkt<>ZustandAlt;
ZustandAlt:=ZustandAkt;
```
Natürlich werden auch die Strings intern in einer Schleife verglichen, aber ich hoffe, daß der Compiler dabei etwas Performanteres zustande bringt als eine FOR..TO-Schleife im Anwenderprogramm.


----------



## Passion4Automation (2 September 2022)

Also die Unions habe ich bei der Aufgabenstellung gar nicht ins Auge gefasst.
Ich verwende die Unions beim wandeln von Modbus-Words auf Real und umgekehrt. Eine super Erleichterung gegenüber CS2.3.

Trotzdem Danke für den Lösungsansatz.


```
Das wird nicht funktionieren, weil ein ULINT nur 8 Bytes abdeckt, das Array aber 41 Byte gross ist.
Aber mit einem String könnte es gehen.
```

Codesys nimmt für den BOOL = 1 Byte, deswegen kommst du auf die 41 Byte oder?


----------



## Rob1337 (3 September 2022)

Servus,

um Speicherbereiche zu vergleichen/setzen/kopieren ist die Bibliothek 
MemoryUtils sehr hilfreich. 
Damit brauchst du keine FOR Schleife, und Größe des Objektes wird automatisch ermittelt. 
Gruß Rob


----------

