Probleme beim adressieren im Datenbaustein..

A

Anonymous

Guest
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

bei meiner S7 habe ich folgendes Problem:
Im Datenbaustein befinden sich diverse Parameter für verschiedene Funktionen. Es gibt 10 Devices, wesshalb für jedes Device ein Parametersatz im DB abgelegt ist, für mein Verständnis also ein Array von 10 Devices mit jeweils gleichen Elementen aber unterschiedlichen Werten. Obwohl in der Definition des DB niergendwo ARRAY steht sondern 10 mal hintereinander der gleiche UDT. (Nee was kompliziert ausgedrückt)

Beispiel

DB:
+-----1-----------
Zeit1 S5TIME
Zeit2 S5TIME
Anz INT
Flag BOOL
+-----2-----------
Zeit1 S5TIME
Zeit2 S5TIME
Anz INT
Flag BOOL
+-----3-----------
Zeit1 S5TIME
Zeit2 S5TIME
Anz INT
Flag BOOL
+-----4-----------
:

Jede Funktion ermittelt nun über zwei Parameter
Parameter 1: Daten Baustein Nummer
Parameter 2: Offset des Datenbereichs im Datenbaustein
wie folgt den zu verwendenden Datenbereich (sprich ARRAY Eintrag)

L #DB_Device
T #DB_Device_Temp
LAR1 P#0.0 // Zeiger mit Null in Adressregister laden

L #Offset_Device // Offset laden (Device Bereich)
SLD 3 // um 3 nach links schieben um Zeigerstruktur zu erzeugen
+AR1 // Zum Adressregister addieren

AUF DB [#DB_Device_Temp]

Dann werden die Werte wie folgt aus dem Datenbaustein entnommen:

L DBB [AR1,P#22.0]

Falls also jemand unter Euch ist der versteht was hier beschrieben ist, insbesondere SPS mäßig, wäre es nett wenn ihr mir sagen könntet wie ich statt der festgelegten Position P#22.0 direkt den Eintrag angeben könnte also z.B. Zeit2. Dann kann ich den UDT ändern und muß nicht durch alle Funktionen und die Positionen anpassen, da die Namen ja bleiben.

Ich hoffe nur, dass das einer verstanden hat... :->

Grüße
Neuling

PS: Vielleicht gibts ja auch ne bessere Methode ?
 
L #Offset_Device // Offset laden (Device Bereich)
SLD 3 // um 3 nach links schieben um Zeigerstruktur zu erzeugen

In welchem Format ist Dein Offset gespeichert? Könnte es sein, dass es schon im Zeigerformat und nicht als INT gespeichert ist, so dass Dein Pointer effektiv um 8 multipliziert?

Oops, sorry, erst Frage zu Ende lesen, dann antworten!

Mmmm, im Edit-Fenster kann man das originale Post nicht mehr sehen!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi RMA,

also die Adressierung funktioniert ja mit
L DBB [AR1,P#22.0]
nur würde ich gerne statt der , ich sag mal Position P#22.0, gerne
den vergebenen Namen z.B. Zeit2 einsetzten, also
L DBB [AR1,Zeit2]
falls das syntaktisch überhaupt so geht ?!

Grüße
 
Hallo
Liegen deine Arrays am Anfang des DBs oder gibt es noch einen DB-Kopf?
Denn wenn dein Offset-Device auf den Anfang des entsprechenden Arrays zeigt und deine Arrays nur aus den 4 Einträgen besteht, verstehe ich die Offseterhöhung P#22.0 nicht.

Man könnte aber eine kleine Funktion schreiben, wo als Eingänge die DB-Nummer und der "Datensatz" und als Ausgänge die Daten erscheinen.

Gruß

Andreas

P.S. Ich habe gerade deine Antwort auf RMA gelesen. Du kannst folgendes machen:

L #Offset_Device
sld 3
L P#22.0
+D
T Zeit2 (Doppelwort)

....

L DBB[Zeit2]

Gruß

Andreas
 
hallöchen wieso nicht ganz einfach

"datenbaustein"."UDT"."zeit1"


Code:
 L     "db".test2.zeit2

oder meintest du was anderes
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi Neuling,

am einfachsten geht das wenn Du dass in einem FB machst, dann kannst Du Dein Offset als Parameter übergeben. Dann würde es ungefähr so gehen:

VAR_INPUT

Zeit_Offset : INT //Byte-Nr der Zeit

END_VAR

NETWORK

AUF DBnn

L #Zeit_Offset //Offset holen
SLD 3 //ändern in Byte.Bit Format
LAR1
L DBB[AR1,P#0.0]

Wenn man mehrere hintereinander laden möchte dann ist #Zeit_Offset nur die Addresse des ersten Parameters und dann wenn die Parameter alle gleich aufgebaut sind (wovon ich ausgehe), dann brauchst Du nur Konstant dazu zu addieren bevor Dein SLD3.

Ich hoffe das hilft Dir auf dem Weg.

Beispiele findest Du auf der Siemens Support-Seit wenn Du eine Suche startest mit dem Begriff "symbolische Programmierung, indirekte Adressierung"
 
Also,

@Znarf
da ich keine Lust hatte sämtliche Einträge meiner Striktur >30 einzutragen hatte ich beispielhaft die kleinen Arrayeinträge geschrieben bei denen natürlich keine Position 22 vorkommt ;-)
Soweit ich das sehe ist Deine Lösung aber auch nicht flexibel wenn sich die Struktur des UDT verändert z:b: beim hinzufügen eines neuen Wertes vor Position 22. Dann stimmt der Bezug Zeit2 nicht mehr und der Kopf müsste auch hier wieder angepaßt werden.

@kpeter
das geht aber nur bei der direkten Adressierung im DB und nicht bei der Adressierung über DB Nummer und Offset. Eine Übergabe an unterschiedliche Funktion scheint mir dann problematisch.

Grüße und danke schon mal für Eure schnellen Antworten
 
Du mußt so zugreifen wie es im Programm steht, ein Symbol kannst du an dieser Stelle nicht nutzen, da das Symbol ja wieder direkt mit der Adresse im DB verbunden ist. In SCL kann man einfacher auf Array's zugreifen, da könnte das schon eher gehen.

Hier kann man sowas machen:

Ein UDT mit den Variablen definieren.
Im DB eine Variable "Prog Array[1..10] of Udt1" anlegen.

im SCL-Baustein z.Bsp. folgendes programmieren:

Code:
FOR I := 1 TO Anzahl DO
       Wert := "TestDB".Prog[I].bond_contr_Win_1_Value_1;    
END_FOR;

IN AWL (von SCL übersetzt) sieht das so aus:

Code:
      SET   
      SAVE  
      =     L      6.1
      L     #Anzahl
      L     1
      T     #I
      TAK   
      T     LW     8
      TAK   
A7d0: L     #I
      L     LW     8
      <=I   
      SPBN  A7d1
      L     #I
      ITD   
      L     L#-1
      +D    
      L     L#480
      *D    
      L     L#32
      +D    
      LAR1  
      AUF   "TestDB"
      L     DBW [AR1,P#0.0]
      ITD   
      T     #Wert
      L     #I
      L     1
      +I    
      T     #I
      SPA   A7d0
A7d1: L     100
      T     #RET_VAL
      CLR   
      U     L      6.1
      SAVE  
      BE
 
Hallo,

den UDT, den du in deinem DB verwendest, könntest du auch als eine Lokalvariable in deinem Baustein verwenden. Dann legst du einen Pointer als Bausteinparameter an, der auf den ersten Eintrag deines Arrays zeigt. Aus den Pointerdaten kannst du die DB-Nummer, die Startadresse im DB und die Länge des UDT gewinnen. Mit der als Eingangsparameter übergebenen "Parametersatz-Nummer", der Startadresse und der Länge des UDT ermittelst du den Offset deiner jeweiligen Daten und kopierst sie in einer Schleife in deine UDT-Lokalvariable. Jetzt hast du symbolischen Zugriff auf die Kopie des jeweiligen Parametersatz. Falls du Daten veränderst, müssen diese natürlich am Ende des Bausteins auf die gleiche Weise in den DB zurück kopiert werden. Das hört sich jetzt vielleicht etwas aufwendig an, ist es auch ein wenig ;-). Bei grösseren Datenmengen ist es aber ganz praktisch. Änderungen an der Datenstruktur macht man dann nur noch im UDT und man hat im Baustein symbolischen Zugriff auf den adressierten Datensatz.


Gruss, Onkel
 
Hi Onkel,

das hört sich absolut super und richtig an ! Aber da mein Benutzername sozusagen "Programm" ist :D habe ich fast nix verstanden. (hüstel)

Aber ich werde mir die Lösung mal durch den Kopf gehen lassen !

Besten Dank !
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo "Neuling",

stimmt, ein Anfängerprojekt ist es nicht unbedingt. Die Members hier helfen dir dabei. Sobald ich etwas Zeit finde, mache ich mal den Anfang. Das soll jetzt aber andere nicht davon abhalten, zu posten.


Gruss, Onkel
 
Hallo,

Dank des bescheidenen Wetters kam meine Musestunde schon früher als erwartet.

Ich hoffe, es schreckt dich nicht zu sehr ab. Den Umgang mit Pointern und Adressregistern kann man sich aneignen, vielleicht hilft dir dieses Beispiel dabei. Wenn du eine Möglichkeit zum Simulieren hast, dann verändere mal den UDT. Zunächst erst einmal nur ein die Schreibweise eines Symbols. Dann füge mal ein paar Daten an. Wenn sich die Struktur des UDT ändert, musst du den DB und die FC über "Datei - Zugriffe prüfen und aktualisieren" aktualisieren. Die Länge des UDT muss am Parameter des FC-Aufrufs ebenfalls aktualisiert werden.

Mein Test-UDT
Code:
TYPE "UDT_DATENSATZ"
VERSION : 0.1
  STRUCT    
   MOTORSTROM : INT ;   //Beispiel Motorstrom
   MOTORSPANNUNG : INT ;    //Beispiel Motorspannung
   DREHZAHL : INT ; //Beispiel Drehzahl
   RESERVE : INT ;  //UDT bis zu einer Doppelwortgrenze auffüllen (macht das Kopieren einfacher)
  END_STRUCT ;  
END_TYPE

Der DB
Code:
DATA_BLOCK "DB_PARAMETER"
TITLE =
VERSION : 0.1
  STRUCT    
   PARAMETERSATZ : ARRAY  [1 .. 35 ] OF "UDT_DATENSATZ";    
  END_STRUCT ;  
BEGIN
   PARAMETERSATZ[1].MOTORSTROM := 0; 
   PARAMETERSATZ[1].MOTORSPANNUNG := 0; 
   PARAMETERSATZ[1].DREHZAHL := 0; 
   PARAMETERSATZ[1].RESERVE := 0; 
   PARAMETERSATZ[2].MOTORSTROM := 0; 
   PARAMETERSATZ[2].MOTORSPANNUNG := 0; 
....usw
END_DATA_BLOCK

Der FC mit dem Umweg über Lokaldaten
Code:
FUNCTION FC 2005 : INT
TITLE =
//ACHTUNG:
//So wie hier geschrieben funktioniert es nur in einer FC, NICHT in einem FB
AUTHOR : Onkel
VERSION : 0.1


VAR_INPUT
  DATENBANK : ANY ; //Datenbereich der in dem die Parametersätze abgelegt sind
  DATENSATZ : INT ; //Nummer des zu bearbeitenden Parametersatzes
END_VAR
VAR_TEMP
  P : "UDT_DATENSATZ";  
  AR1_TEMP : DWORD ;    
  AR2_AKT_DATENSATZ : DWORD ;   
  TEMP_INT : WORD ; 
  TEMP_DINT : DWORD ;   
  LOOP : INT ;  
  LAENGE_UDT : INT ;    //Länge des Parametersatzes [DWORD]
  DB_UDT : INT ;    //Nummer des DB [INT]
  STARTADRESSE : DWORD ;    //Startadresse im DB == 1. Datensatz
END_VAR
BEGIN
NETWORK
TITLE =


//*** AR1 sichern
      TAR1  #AR1_TEMP; 
      SET   ; 
      SAVE  ; 

//*** Ergebnisse initialisieren
      L     B#16#0; 
      T     #RET_VAL; 

//*** AR2 auf Datenbereich im DB
      L     P##DATENBANK; 
      LAR2  ; 

//*** ANY-Ponter "zerlegen"
//*** wir brauchen nur die DB-Nr und Länge des UDT (Anzahl DWORD)
//*** sowie die Startadresse im DB (Bereichszeiger)
      L     B [AR2,P#0.0]; // Syntax-ID, bei S7 immer 10hex
      L     B [AR2,P#1.0]; // Datentyp (muss in diesem Bsp. DWORD sein)
      L     W [AR2,P#2.0]; // Anzahl von "Datentyp" == Länge des UDT (Anzahl DWORD) [INT]
      T     #LAENGE_UDT; // temporär ablegen
      L     W [AR2,P#4.0]; // Nummer des DB [INT]
      T     #DB_UDT; // temporär ablegen
      L     D [AR2,P#6.0]; // Bereichszeiger == Startadresse
      T     #STARTADRESSE; // Startadresse im DB == 1. Datensatz

//*** Prüfe, ob DATENBEREICH vom Typ=Doppelwort
//    Zur Vereinfachung haben wir vorausgesetzt, das der Datentyp DWORD ist.
//    Anderenfalls müssten beim nachfolgendem Kopieren Fallunterscheidungen
//    getroffen werden - das ersparen wir uns.
      L     B [AR2,P#1.0]; // Datentyp (muss in diesem Bsp. DWORD sein)
      L     B#16#6; // 06hex == DWORD
      ==I   ; 
      L     1; // Fehler 1: DATENBEREICH nicht vom Typ DWord
      SPBN  ERR; // !!! Baustein bei Parameterfehler beenden !!!

//*** Datenbank-DB öffnen und Adressregister AR2 auf Bereichszeiger
      AUF   DB [#DB_UDT]; // DB öffnen
      L     #STARTADRESSE; // Bereichszeiger == Startadresse 1. Datensatz
      LAR2  ; // DATENBEREICH

//*** Offset des aktuellen Datensatzes zu AR2 addieren
      L     #DATENSATZ; // Nummer des aktuellen Datensatzes (1..n) [INT]
      L     1; // beim 1. Datensatz ist Offset=0
      -I    ; // Nummer des aktuellen Datensatzes (0..n-1) [INT]
//    Länge des UDT in BIT umrechnen, ohne Akku 2 zu verändern
      L     #LAENGE_UDT; // Länge des UDT (Anzahl DWORD) [INT]
      SLW   5; // Länge des UDT (Anzahl BIT), (SLW 5 == *32)
//    Länge des UDT mit aktueller (Datensatznummer-1) multiplizieren
      *I    ; 
//    Offset [BIT] zu AR2 addieren
      +AR2  ; // AR2 zeigt jetzt auf aktuellen Datensatz im DB

//*** AR2 für späteres Zurückkopieren zwischenspeichern
      TAR2  #AR2_AKT_DATENSATZ; 

//*** AR1 auf Datenbereich in Lokaldaten
      L     P##P; 
      LAR1  ; // AR1 zeigt jetzt auf Datensatz in Lokaldaten

//*** Datensatz aus DB in temporäre Lokaldaten kopieren
      L     #LAENGE_UDT; // Länge des UDT (Anzahl DWORD)
LOO1: T     #TEMP_INT; 
      L     DBD [AR2,P#0.0]; 
      T     D [AR1,P#0.0]; 
      +AR1  P#4.0; 
      +AR2  P#4.0; 
      L     #TEMP_INT; 
      LOOP  LOO1; 

//***************************************************************************************************
// Ab hier steht die Kopie des Datensatzes in den temporären Lokaldaten und kann bearbeitet werden
//***************************************************************************************************
      L     #DATENSATZ; // Nr. des akt. Datensatzes





// Hier könnten u.a. auch Bausteine aufgerufen werden, denen ein Pointer auf die Lokaldaten dieses
// Bausteins übergeben wird ;-)
//*** Sinnloses Beispiel
      CALL "BLKMOV" (
           SRCBLK                   := #P,// Pointer auf akt. Datensatz in den Lokaldaten
           RET_VAL                  := MW  2000,
           DSTBLK                   := P#DB2005.DBX 0.0 DWORD 2);

//*** Normaler Zugriff
      L     #P.MOTORSTROM; // zugehörige Parameter
      L     #P.MOTORSPANNUNG; 
      L     #P.DREHZAHL; 

      L     100; 
      T     #P.MOTORSTROM; // zugehörige Parameter
      L     200; 
      T     #P.MOTORSPANNUNG; 





//***************************************************************************************************
// Nach der Bearbeitung müssen die Daten in den DB zurück kopiert werden (falls sie verändert wurden)
//***************************************************************************************************
//*** AR1 auf Datenbereich in Lokaldaten
      L     P##P; 
      LAR1  ; // AR1 zeigt jetzt auf Datensatz in Lokaldaten

//*** AR2 auf aktuellen Datensatz im DB
      LAR2  #AR2_AKT_DATENSATZ; // AR2 zeigt jetzt auf akt. Datensatz im DB

//*** falls DB-Register verändert wurde, DB erneut öffnen
      AUF   DB [#DB_UDT]; // DB öffnen

//*** Lokalen Datensatz in den DB zurück kopieren
      L     #LAENGE_UDT; // Länge des UDT (Anzahl DWORD)
LOO2: T     #TEMP_INT; 
      L     D [AR1,P#0.0]; 
      T     DBD [AR2,P#0.0]; 
      +AR1  P#4.0; 
      +AR2  P#4.0; 
      L     #TEMP_INT; 
      LOOP  LOO2; 


//***************************************************************************************************
// Ende
//***************************************************************************************************
//*** AR1-Register wiederherstellen
      LAR1  #AR1_TEMP; 
      BEA   ; 

//*** Fehler
ERR:  T     #RET_VAL; 

//*** AR1-Register wiederherstellen
      LAR1  #AR1_TEMP; 
      BE    ; 

END_FUNCTION

Und der Bausteinaufruf
Code:
      CALL FC  2005 (
           DATENBANK                := P#DB2005.DBX 0.0 DWORD 2,
           DATENSATZ                := 10,
           RET_VAL                  := MW  2000);


Gruss, Onkel


Nachtrag:

Etwas einfacher geht es noch, wenn du beim Any-Pointer den Datentyp BYTE wählst. Dann kannst du beim Bausteinaufruf den ANY-Parameter symbolisch angeben. Es wird automatisch ein Pointer mit dem Datentyp BYTE generiert, und mit der Länge der angeknüpften Struktur. Der FC2005 müsste dann entsprechend auf den Datentyp BYTE geändert werden (bei der Typprüfung und beim Kopieren).

Code:
      CALL  FC  2005
       DATENBANK:="DB_PARAMETER".PARAMETERSATZ[1]
       DATENSATZ:=10
       RET_VAL  :=MW2000


Gruss, Onkel
 
Hallo Onkel Dagobert und Co,

was soll ich sagen, sehr beeindruckend !! Nicht nur die Beispiele, sondern auch diese Hilfsbereitschaft.
Ich werde mir das alles mal in aller, aller Ruhe ansehen... Zur Zeit habe ich allerdings noch das Gefühl, dass es ist für mich beim aktuellen Projekt einfacher ist, nach Änderungen im UDT die Funktionen zu bearbeiten in denen er verwendet wird.
Ich hatte gedacht das wäre ein wenig einfacher gewesen.

Besten Dank Euch allen !!
 
Zurück
Oben