Verständnisproblem mit AR in Schleife

Pico1184

Level-2
Beiträge
332
Reaktionspunkte
9
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

ich möchte einen Standartbaustein für Statistiken schreiben. Dazu hab ich einen Statistik_DB der folgendermaßen aufgebaut sein kann:

Code:
Statistik_Kamera : STRUCT
Anzahl_Aufnahme_1 : INT
Anzahl_Aufnahme_2 : INT
Anzahl_Aufnahme_3 : INT
Anzahl_Aufnahme_4 : INT
Anzahl_Aufnahme_5 : INT
Anzahl_Aufnahme_6 : INT
Anzahl_Aufnahme_7 : INT
Anzahl_Aufnahme_8 : INT
END_STRUCT

Die Anzahl der Aufnahmen kann variieren.

Nun habe ich einen Statistik FB welcher die Daten verwalten soll. Ich möchte dem Baustein als IN Parameter die Anzahl der Stationen als INT vorgeben und die oben genannte Struktur als ANY IN_OUT.

Code:
IN Parameter:
IN_INT_Anzahl Stationen : INT

IN OUT Parameter:
IN_OUT_ANY_Register : ANY

STAT Parameter:
Statistik_Register_lokal : Array [0..127] Of Int

nun möchte ich die Daten des DB auf die lokale Variable "Statistik_Register_lokal" umkopieren, die Größe kann je nach übergebener Anzahl unterschiedlich sein, daher dachte ich das ist mit einer Schleife am besten zu lösen. Hier mein Ansatz:

Code:
  TAR2  
      T     #T_DW_AR2_Sicherung         // Sichern Adressregister
      L     DW#16#FFFFF                 // nicht relevante Bits ausblenden
      UD    
      T     #T_DW_Instanzanfang         // Anfang der Instanzdaten merken

      L     P##IN_OUT_ANY_Register
      +D    
      LAR2                              // Zeiger AR2 auf DB setzen
      L     W [AR2,P#4.0]               // und auslesen
      T     #T_INT_DB_Komponente        // DB für Komponente aufschlagen
      AUF   DB [#T_INT_DB_Komponente]

      L     D [AR2,P#6.0]               // Zeiger AR2 auf DW setzen
      T     #T_DW_Dat_Komponente
      LAR2  


      LAR1  P##Statistik_Register       // Zeiger AR1 auf T_Struktur setzen

      L     0
      T     #ST_INT_CopySchleife

      L     #IN_INT_Anzahl_Stationen
N1:   T     #ST_INT_CopySchleife

      L     W [AR2,P#0.0]               //Kopiere Daten 
      T     W [AR1,P#0.0]

      L     P#2.0
      +AR1  
      +AR2  
  
      L     #ST_INT_CopySchleife
      LOOP  N1

      LAR2  #T_DW_AR2_Sicherung         // Rücksichern Adressregister AR2

Nun geht aber meine CPU in Stop wegen "Bereichslängenfehler beim Lesen".
Kann das allerdings nicht verstehen, die Schleife läuft falls 8 Stationen übergeben werden ja auch nur 8 mal durch.

Warum wird dann hier in einem nicht vorhanden Speicherbereich gelesen?

Wenn ich +AR2 auskommentiere gibt es keine Probleme. Dann wird der erste Datensatz des DBs auf die lokale Daten 8 mal kopiert.

Aber solbald ich +AR2 wieder aktiv schalte geht die CPU in Stop!

:confused::confused:

Grüße Pico
 
ist das ein FB?
da gelten andere regeln für das ar2 in einem fc
Das DI-Register und das Adressregister AR2 werden systemseitig für den FB- und Multiinstanz-CALL verwendet und dürfen deshalb innerhalb von FB's nicht verändert werden.
Das Adressregister AR1 wird von einem Teil der ladbaren Standardbausteine verwendet.
Der Befehl "L P#Parametername" lädt innerhalb eines FB den Adressoffset des angegebenen Parameters, relativ zum Adressregister AR2. Um in multiinstanzfähigen FBs den absoluten Offset im Instanzdatenbaustein zu ermitteln, muss zu diesem Wert noch der bereichsinterne Zeiger (nur Adresse) des AR2-Registers addiert werden.
Weitere Information zu den Registern der CPU finden Sie in der Hilfe zur Programmiersprache (KOP/FUP/AWL).
schau dir auch mal sfc20 an
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
da gelten andere regeln für das ar2 in einem fc

Ja das ist ein FB aber das ist mir schon klar. Ich rette ja das AR2 am Anfang des FB, arbeite dann mit dem AR2,
addiere den ausgerechneten Instanzanfang und sichere das AR2 danch zurück, daher sollte das ja eigentlich funktionieren?!?!?

Sichern des AR2 und berechnen des Instanzanfangs:
Code:
TAR2
T     #T_DW_AR2_Sicherung         // Sichern Adressregister      
L     DW#16#FFFFF                 // nicht relevante Bits ausblenden       
UD           
T     #T_DW_Instanzanfang         // Anfang der Instanzdaten merken        
L     P##IN_OUT_ANY_Register       
+D           
LAR2

Rückschreiben des AR2:

Code:
 LAR2  #T_DW_AR2_Sicherung         // Rücksichern Adressregister AR2

Grüße Pico
 
Zuletzt bearbeitet:
Bei jedem Zugriff auf Instanz-Variablen des FB (außer auf TEMP-Variablen) muß AR2 seinen originalen Inhalt vom Bausteinaufruf haben.

Wenn der FB als "multiinstanzfähig" erstellt wurde, dann macht der FB "unsichtbar" bei jedem Instanzvariablenzugriff die erwähnte Offset-Berechnung mit dem Inhalt des AR2 - und geht davon aus, daß AR2 den Inhalt vom Bausteinaufruf hat.

Also: vor jedem Instanzvariablen-Zugriff AR2 wiederherstellen.

Harald
 
Also: vor jedem Instanzvariablen-Zugriff AR2 wiederherstellen.

Also ist mein Problem genau hier??? Bzw. die Schleifenvariable.....

Code:
N1:   [COLOR=#ff0000]T     #ST_INT_CopySchleife[/COLOR] 
        L     W [AR2,P#0.0]               //Kopiere Daten        
        T     W [AR1,P#0.0]        
        L     P#2.0       
        +AR1
        +AR2 
        [COLOR=#ff0000]L     #ST_INT_CopySchleife[/COLOR] 
        LOOP  N1

Grüße Pico
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Nicht nur der Zugriff auf die STAT-Variablen. Auch der Zugriff auf die IN-, OUT-, IN_OUT-Variablen - auf alle Instanz-Variablen eben.
(öffne mal den Instanz-DB - alle Variablen die Du da findest).

Also das Programm möglichst so umformen, daß in der Schleife nur mit TEMP-Variablen gearbeitet wird. Dann muß AR2 nicht so oft wieder hergestellt werden.

Theoretisch kann man auch einen FB erstellen, der nicht multiinstanzfähig ist - dann kann man AR2 nach belieben nutzen. Würde ich aber nicht machen, das vergisst man irgendwann und kopiert Code und wundert sich, warum der nicht funzt.

Harald
 
Nochwas:
Du sicherst Dir am Bausteinanfang den Offsetanteil des AR2 in #T_DW_Instanzanfang, benutzt in aber in dem uns gezeigten Code nie.
Bei "LAR1 P##Statistik_Register" muß dieser Instanzanfang noch addiert werden wenn #Statistik_Register keine TEMP- sondern eine STAT- bzw. Instanz-Variable ist. Wenn Du Deinen FB nicht als Multiinstanz sondern mit einem eigenen IDB aufrufst, dann ist der Instanzanfang aus AR2 allerdings 0 und es fällt nicht auf, daß diese Addition von 0 nicht erfolgt ist.

Harald
 
so sollte es jetzt mal funktionieren bzw. mein Test hat nun funktioniert:

Code:
//Instanzanfang berechnen
      TAR2  
      T     #T_DW_AR2_Sicherung         // Sichern Adressregister
      L     DW#16#FFFFF                 // nicht relevante Bits ausblenden
      UD    
      T     #T_DW_Instanzanfang         // Anfang der Instanzdaten merken

      L     P##Statistik_Register       // Zeiger AR1 auf T_Struktur setzen
      +D    
      LAR1  

//Initialisierungen
      L     0
      T     #ST_INT_CopySchleife
      T     #T_INT_Schleifenzaehler


//Schleife
      L     #IN_INT_Anzahl_Stationen
N1:   T     #ST_INT_CopySchleife

      L     #T_DW_Instanzanfang
      L     P##IN_OUT_ANY_Register
      +D    
      LAR2                              // Zeiger AR2 auf DB setzen

      L     W [AR2,P#4.0]               // und auslesen
      T     #T_INT_DB_Komponente        // DB für Komponente aufschlagen
      AUF   DB [#T_INT_DB_Komponente]

      L     D [AR2,P#6.0]               // Zeiger AR2 auf DW setzen
      T     #T_DW_Dat_Komponente
      LAR2  

//Zeiger berechnen und Offset für AR2 einstellen
      L     #T_INT_Schleifenzaehler
      L     16
      *I    
      T     #T_DWORD_Zeiger
      +AR2  

//Daten kopieren
      L     W [AR2,P#0.0]
      T     W [AR1,P#0.0]

//Offset AR1 einstellen
      +AR1  P#2.0

// Rücksichern Adressregister AR2
      LAR2  #T_DW_AR2_Sicherung

//Schleifenzähler inkrementieren
      L     #T_INT_Schleifenzaehler
      L     1
      +I    
      T     #T_INT_Schleifenzaehler

//Schleifenende prüfen
      L     #ST_INT_CopySchleife
      LOOP  N1

Die einzigste Sorge bereitet mir jetzt noch die Temp Variable "T_INT_Schleifenzaehler".
Ich inkrementiere diese ja am Ende der Schleife und bilde mir daraus den bereichsinternen Zeiger.

Im nächsten Zyklus ist ja aber eigentlich der Wert der Temp Variablen wieder verschwunden....aber komischerweise funktioniert es trotzdem?!?!?

Grüße Pico
 
Zuviel Werbung?
-> Hier kostenlos registrieren
so sollte es jetzt mal funktionieren bzw. mein Test hat nun funktioniert:

Code:
//Instanzanfang berechnen
      TAR2  
      T     #T_DW_AR2_Sicherung         // Sichern Adressregister
      L     DW#16#FFFFF                 // nicht relevante Bits ausblenden
      UD    
      T     #T_DW_Instanzanfang         // Anfang der Instanzdaten merken

      L     P##Statistik_Register       // Zeiger AR1 auf T_Struktur setzen
      +D    
      LAR1  

//Initialisierungen
      L     0
      T     #ST_INT_CopySchleife
      T     #T_INT_Schleifenzaehler


//Schleife
      L     #IN_INT_Anzahl_Stationen
N1:   T     #ST_INT_CopySchleife

      L     #T_DW_Instanzanfang
      L     P##IN_OUT_ANY_Register
      +D    
      LAR2                              // Zeiger AR2 auf DB setzen

      L     W [AR2,P#4.0]               // und auslesen
      T     #T_INT_DB_Komponente        // DB für Komponente aufschlagen
      AUF   DB [#T_INT_DB_Komponente]

      L     D [AR2,P#6.0]               // Zeiger AR2 auf DW setzen
      T     #T_DW_Dat_Komponente
      LAR2  

//Zeiger berechnen und Offset für AR2 einstellen
      L     #T_INT_Schleifenzaehler
      L     16
      *I    
      T     #T_DWORD_Zeiger
      +AR2  

//Daten kopieren
      L     W [AR2,P#0.0]
      T     W [AR1,P#0.0]

//Offset AR1 einstellen
      +AR1  P#2.0

// Rücksichern Adressregister AR2
      LAR2  #T_DW_AR2_Sicherung

//Schleifenzähler inkrementieren
      L     #T_INT_Schleifenzaehler
      L     1
      +I    
      T     #T_INT_Schleifenzaehler

//Schleifenende prüfen
      L     #ST_INT_CopySchleife
      LOOP  N1

Die einzigste Sorge bereitet mir jetzt noch die Temp Variable "T_INT_Schleifenzaehler".
Ich inkrementiere diese ja am Ende der Schleife und bilde mir daraus den bereichsinternen Zeiger.

Im nächsten Zyklus ist ja aber eigentlich der Wert der Temp Variablen wieder verschwunden....aber komischerweise funktioniert es trotzdem?!?!?

Grüße Pico

Da brauchst du dir keine Sorgen zu machen, du arbeitest die Schleife komplett in einem Zyklus ab (Loop). Vor der Schleife initialisierst du "T_INT_Schleifenzaehler" mit 0, also machst du das richtig.

Aber du solltest den Hinweis in einer Antwort weiter oben beachten, dein FB ist nicht multiinstanzfähig. Das ist nicht weiter schlimm, wenn du ihn nicht in einer Multiinstanz einzusetzen gedenkst. Wenn doch muß bei der Adressberechnung noch der Offset der Daten im Multiinstanz-DB beachtet werden, den du im AR2 am Anfang praktischerweise ohnehin absicherst.

PS. Siehe den Beitrag von PN/DP zu diesem Thema.
 
Code:
//Zeiger berechnen und Offset für AR2 einstellen     
L     #T_INT_Schleifenzaehler      
L     16      
*I          
T     #T_DWORD_Zeiger      
+AR2

Zur Sicherheit würde ich aus

*I noch ein *D machen, man weiß ja nie, ob man irgendwann mal doch sehr viele Daten kopieren will und der berechnete Pointer ist ja eigentlich ein DWORD.
Auch wenn man später mal Code kopiert, kann das *I Ärger bereiten.
 
ber du solltest den Hinweis in einer Antwort weiter oben beachten, dein FB ist nicht multiinstanzfähig. Das ist nicht weiter schlimm, wenn du ihn nicht in einer Multiinstanz einzusetzen gedenkst. Wenn doch muß bei der Adressberechnung noch der Offset der Daten im Multiinstanz-DB beachtet werden, den du im AR2 am Anfang praktischerweise ohnehin absicherst.

Ich muss diesen Baustein multiinstanzfähig machen, das ist mir jetzt wiederrum nicht klar, alles was ich in die AR lade habe ich ja mit dem Instanzanfang addiert.

Code:
L     #T_DW_Instanzanfang         // Anfang der Instanzdaten merken
L     P##Statistik_Register       // Zeiger AR1 auf T_Struktur setzen
+D    
LAR1  

L     #T_DW_Instanzanfang
L     P##IN_OUT_ANY_Register
+D    
LAR2 

L     D [AR2,P#6.0]               // Zeiger AR2 auf DW setzen
T     #T_DW_Dat_Komponente
LAR2  

//Zeiger berechnen und Offset für AR2 einstellen
L     #T_INT_Schleifenzaehler
L     16
*I    
T     #T_DWORD_Zeiger
 +AR2

oder wo meinst du jetzt genau??

Grüße Pico
 
Aber das hab ich doch.....

Code:
[COLOR=#ff0000]L     #T_DW_Instanzanfang         [/COLOR] 
L     P##Statistik_Register       // Zeiger AR1 auf T_Struktur setzen [COLOR=#ff0000]
+D [/COLOR]    
LAR1    [COLOR=#ff0000]

L     #T_DW_Instanzanfang[/COLOR] 
L     P##IN_OUT_ANY_Register [COLOR=#ff0000]
+D[/COLOR]     
LAR2

Grüße Pico
 
Du hast vollkommen Recht, das hab ich übersehen, du hast das ja schon in deinen Code aufgenommen, in Bezug auf AR2 war ich gedanklich noch in deinem Anfangscode aus Post 1.
 
Ich nochmal ... eine Anmerkung habe ich noch:
Code:
L     #T_DW_Instanzanfang         // Anfang der Instanzdaten merken
L     P##Statistik_Register       // Zeiger AR1 auf T_Struktur setzen
+D    
LAR1  

L     #T_DW_Instanzanfang
L     P##IN_OUT_ANY_Register
+D    
LAR2 

L     D [AR2,P#6.0]               // Zeiger AR2 auf DW setzen
T     #T_DW_Dat_Komponente
[COLOR="#FF0000"]LAR2     <-------- ist das so richtig ???[/COLOR]

//Zeiger berechnen und Offset für AR2 einstellen
L     #T_INT_Schleifenzaehler
L     16
*I    
T     #T_DWORD_Zeiger
 +AR2

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Achso, Du baust ja erstmal einen Pointer auf den IN_OUT-Parameter, welcher selber auch wieder ein Pointer ist.
Es ist also richtig so.


Allerdings sehen Deine Codeschnipsel mittlerweise etwas unübersichtlich aus.
Ich würde einige Sachen anders formulieren, z.B. so oft wie möglich mit AR1 arbeiten und AR2 nur anfassen, wenn es unbedingt nötig ist. Weiters benutze ich fast immer die Kombination DB-Register + AR1 für die Quelle und DI-Register + AR2 für das Ziel, wenn ich beide AR benutzen muß. Und bei Zugriffen auf den mit dem DI-Register adressierten DB schreibe ich für mich zur Erinnerung und "zur Sicherheit" gerne DIB/DIW/DID statt nur B/W/D - nicht, daß mal aus irgendeinem Grund die Bereichskennung in AR2 futsch geht und auf den falschen DB zugegriffen wird (paranoid - aber übersichtlicher ;))
Doch eigentlich ist meine Variante gar nicht soweit weg von Deinem Entwurf.
Code:
//AR2 sichern und Instanzanfang merken
      TAR2  
      T     #T_DW_AR2_Sicherung         // Sichern Adressregister
      UD    DW#16#FFFFFF                // Bereichskennung ausblenden
      T     #T_DW_Instanzanfang         // Anfang der Instanz merken

//IN-Parameter in TEMP umkopieren bevor AR2 verändert wird
      L     #IN_INT_Anzahl_Stationen
      T     #T_INT_Anzahl_Stationen

//Adresse des externen Datenbereichs holen und Pointer AR1 darauf setzen
      L     #T_DW_Instanzanfang         // Anfang der Instanz
      L     P##IN_OUT_ANY_Register      // Adresse des IO_Any-Parameters in dieser Instanz
      +D                                // Adresse meines IO_Any-Parameters
      LAR1                              // Pointer AR1 auf mein IN_OUT_ANY_Register setzen
      L     DIW [AR1,P#4.0]             // DB-Nr des übergebenen Any
      T     #T_INT_DB_Komponente        // in lokalen TEMP umspeichern
      L     DID [AR1,P#6.0]             // Speicherbereich und Offset des übergebenen Any
      T     #T_DW_Dat_Komponente        // in lokalen TEMP umspeichern (falls nochmal benötigt)
      LAR1                              // <DB>.AR1: Pointer auf die externe Datenstruktur
      AUF   DB [#T_INT_DB_Komponente]

//Pointer AR2 auf lokale T_Struktur setzen
      L     #T_DW_Instanzanfang         // Anfang der Instanz
      L     P##Statistik_Register       // Anfang der T_Struktur in dieser Instanz
      +D    
      LAR2                              // <DI>.AR2: Pointer auf meine T_Struktur

//Schleife zum Umkopieren vom externen Datenbereich in die lokale Datenstruktur
      L     #T_INT_Anzahl_Stationen     // Schleifenzähler initialisieren
N1:   T     #T_INT_CopySchleife

      L     W [AR1,P#0.0]               // Kopiere Daten von externem Bereich
      T     W [AR2,P#0.0]               // in lokale Struktur [COLOR="#FF0000"](*)[/COLOR]

      L     P#2.0                       // Pointer-Abstand für INT-Variablen
      +AR1                              // SourcePointer++
      +AR2                              // DestPointer++

      L     #T_INT_CopySchleife
      LOOP  N1

//*** lokale Datenkopien auswerten **********
//AR2 wiederherstellen
      LAR2  #T_DW_AR2_Sicherung
      L     #Statistik_Register[0]
      L     #Statistik_Register[1]
//...

//AR2 wiederherstellen
      LAR2  #T_DW_AR2_Sicherung
(*) Normalerweise würde ich "zur Sicherheit" T DIW [AR2,P#0.0] schreiben, doch wegen einer Macke des AWL-Editors geht das nicht, wenn der erste IN-Parameter vom Datentyp INT oder WORD ist (oder es müsste AR1 mit AR2 vertauscht werden).


PS: Ich sehe zwar nicht, warum dieser FB multiinstanzfähig sein muß, doch Du wirst schon wissen, was der FB noch alles machen soll. Und ich nehme an, Du weißt was "multiinstanz" heißt.

Harald
 
Zuletzt bearbeitet:
PS: Ich sehe zwar nicht, warum dieser FB multiinstanzfähig sein muß, doch Du wirst schon wissen, was der FB noch alles machen soll. Und ich nehme an, Du weißt was "multiinstanz" heißt.

Der FB muss deshalb multiinstanz fähig sein, weil es ein Standartbaustein werden soll der für verschiedenste Statistikfunktionen verwendet wird.

Code:
STAT:
Kamera_Statistik : FBS_Statistik
Sensor_Statistik : FBS_Statistik
Ausschuss_Statistik : FBS_Statistik

Grüße Pico
 
Ich möchte den Baustein nun noch gerne so erweitern, dass alle Felder des lokalen Registers welche nicht benutzt werden, auf Null gesetzt werden.

Dazu hab ich mir gedacht ein zusätzliches Array anzulegen welches die gleiche Größe hat wie das Statistik Array:

Code:
STAT:
Statistik_Register : Array [0..127] of Int
Statistik_Register_Null : Array [0..127] of Int

Um nun die nicht benötigten Daten (IN_INT_Anzahl + 1) auf Null zu setzen möchte ich einfach die Größe aus dem Null Register nehmen (da stehen ja eh nullen drin, eben die Anfangswerte)
und in das Statistik_Register Array kopieren.

Code:
      L     126
      L     #IN_INT_Anzahl_Stationen
      -I    
      T     #T_INT_Anzahl_Not_Used

      L     #IN_INT_Anzahl_Stationen
      L     1
      +I    
      T     #T_INT_Index

//Lokaldatenbereich Statistik_Register
      CALL  "FC MAKE ANY POINTER"
       IN_IN_Typ             :=4
       IN_IN_Anzahl          :=#T_INT_Anzahl_Not_Used
       IN_IN_Index           :=#T_INT_Index
       IN_IN_DB_Nr           :=20
       IN_IN_Operandenbereich:=5
       IN_DW_Zeiger          :=DW#16#160    //44.0
       OUT_ANY_Anyzeiger     :=#T_ANY_CPY_Zeiger


//Lokaldatenbereich Statistik_Register_Null
      CALL  "FC MAKE ANY POINTER"
       IN_IN_Typ             :=4
       IN_IN_Anzahl          :=#T_INT_Anzahl_Not_Used
       IN_IN_Index           :=#T_INT_Index
       IN_IN_DB_Nr           :=20
       IN_IN_Operandenbereich:=5
       IN_DW_Zeiger          :=DW#16#970    //302.0
       OUT_ANY_Anyzeiger     :=#T_ANY_NU_Zeiger

      CALL  "BLKMOV"
       SRCBLK :=#T_ANY_NU_Zeiger
       RET_VAL:=#T_DummyInt
       DSTBLK :=#T_ANY_CPY_Zeiger

Würdet ihr das auch so machen, oder gibts da eine elegantere Lösung?

Grüße Pico
 
Zurück
Oben