Step 7 AWL UDT Symbolische zugriffe und verweis auf Unstrukturierten DB

vollmi

Level-3
Beiträge
5.425
Reaktionspunkte
1.403
Zuviel Werbung?
-> Hier kostenlos registrieren
guten Morgen

Schwierig den Titel zu beschreiben das was sinnvolles bei rauskommt.

Ich versuchs ausführlicher.
Ich habe einen DB der aus einer Quelle generiert wird darin sind Wort sozusagen fortlaufend deklariert. Das sind dann Blöcke an z.B. 20 Worten die sich immerwieder wiederholen. Und jeder Block ist für einen FB aufruf gedacht.

in einem FB möchte ich nun gerne eine UDT Strukur verwenden und zwar als INOUT bzw. ich will sowohl lesen wie auch schreiben.

Am FB will ich dann entweder einen Pointer zum DB anhängen oder halt den ersten Wort Datenpunkt des gewünschten Blocks des DBs. Und dann im FB diesen Block in den UDT bereich übernehmen dass ich da wieder alles symbolisch habe.

Ich hätt da jetzt einen Pointer nehmen können als IN dann mit Blkmove diesen auf den UDT bereicht kopieren. Aber ich hab in diesem Block eben auch Parameter welche ich zurück in den DB schreiben will und das auch noch nicht nur aus dem FB sondern auch aus anderen Programmteilen.

Hat da jemand eine IDEE wie ich das gescheit löse? Oder einen erhellenden Suchbegriff?

mfG René
 
Einlesen über Blockmove in den statischen Bereich.

Rausschreiben händisch, indirekt adressiert.
Was der Baustein nicht geändert hat, wird auch nicht rausgeschrieben.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Solange du im zyklischen Ablauf bist und nicht durch irgendwelche Alarm-OBs auf die Daten zugreifst, kann es doch keine Probleme mit den Zugriffen aus anderen Programmteilen geben.
Ich lege mir immer die passende UDT im Stat-Teil des FBs an und kopiere die Daten beim Eintritt komplett dort rein und schreib beim Austritt komplett zurück.
Da der Blockmove-Befehl recht schnell ist, hab ich zumindest bei meinen Anlagen keine Probleme damit.

Gruß
Dieter
 
Das problem ist eben.

Da kommt z.B. ein Wort rein (z.B. Wort 2 des Blocks)

DB1.DBW0 // status
DB1.DBW2 // Befehl

Code:
TYPE UDT1
VERSION : 0.1



  STRUCT 	
   Status : INT ;	
   Befehl : INT ;	
  END_STRUCT ;	
END_TYPE

Jetzt kommt auf Wort2 ein wert rein z.B. 4 im Baustein will ich dass dann aber auslesen mit
L #Befehl

Wenn das geschehen ist will ich da mit
T #Befehl
was reinschreiben.

Klar ich könnte jetzt den Block da reinkopieren alles bearbeiten und dann wieder rauskopieren. Ich dachte halt es wäre irgendwie möglich nur zu sagen DU liegst eigentlich in diesem DB. als würde man direkt mit Pointern schaffen ohne das wirklich erst umzukopieren. sozusagen eine AT sicht mit Struktur auf einen Globalen DB Bereich.

Aber wird wohl wirklich auf Blockmove rauslaufen. Dachte halt ich könnt mir den sparen ;)

mfG René

mfG René
 
... Ich dachte halt es wäre irgendwie möglich nur zu sagen DU liegst eigentlich in diesem DB. als würde man direkt mit Pointern schaffen ohne das wirklich erst umzukopieren. sozusagen eine AT sicht mit Struktur auf einen Globalen DB Bereich.

Hallo René,

aber genau so müßte das doch sogar gehen (kann ich leider gerade aber nicht testen).
Du machst dir einen IN-Parametzer (oder IN_OUT-Parameter) vom Typ deines DB's (UDT) und legst darauf komplett deine AT-Sicht, die die DB-Struktur dann so interpretiert, wie von dir gewünscht.
Die Größen müßten hier nur zusammen passen.

Gruß
Larry
 
Zuletzt bearbeitet:
Sorry ... ich hatte nur das Stichwort mit der AT-Sicht gelesen und dann den Rest da hinein interpretiert.

Dann eine andere Frage : woher kommt den der DB in der unstrukturierten Form (wer beschreibt den wie) und ist es nicht ggf. möglich, ihn strukturiert anzulegen und dann so zu benutzen ...?

Gruß
Larry
 
Der DB wird durch eine Excel Datenpunktliste erstellt. Leider ist es nicht so einfach möglich da UDTs draus zu machen da die Datenpunkte in der Struktur auch verschiedene Kommentare dazu brauchen. Ein datenpunkt in der Struktur hat z.B. eine gänzlich andere Anlagenbezeichnung im Text als der rest der Struktur.

Das stört mich im Programm nicht, da ich hier nur mit den Objekten arbeite. Aber im Bediensystem soll man dann schon sehen das z.B. die Türe welche durch eine Profibusklappe eingelesen wird nicht direkt zur Klappe gehört sondern einen anderen Standort hat.

Sonst wäre das schon meine erste Wahl gewesen die UDTs direkt in der Datenpunktliste vorzuhalten. Würde die grösse der Datenpunktliste auch erheblich reduzieren.

mfG René
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich hab das in SCL jetzt so gemacht. Vielleicht interessiert es ja jemanden.

Code:
FUNCTION_BLOCK Obj_Beispiel




TITLE = 'Beispiel als Objekt ausgeführt'
// Beispiel wie man einen UDT frei an einen Pointer hängt.
// Um z.B. eine unspezifizierte Datenmenge mit einem UDT zu belegen und trotzdem Symbolisch den Baustein anhängen zu können
//
// V1.0 Startversion
VERSION : '1.0'
AUTHOR  : VoR
NAME    : KLP
FAMILY  : TUV


VAR_INPUT
    LS_PAR : ANY;  // Hier wird das Erste Wort aus dem LS DB aufgelegt
      pLS_PAR AT LS_PAR : ANY_POINTER;
END_VAR


VAR_OUTPUT
    Istposition : INT; // Anzeigevariable
END_VAR


VAR_TEMP
   KLP : LS_ABL_KLP; // temporäre Variablen mit UDT Belegt
END_VAR


(*Pointer auf UDT schreiben zum arbeiten*)
  pLS_PAR.Bereichstyp := 2;   // Typ des Bereichs soll Byte sein (INT = 2)
  pLS_PAR.Anzahl_Werte := 22; // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
                               // Muss so lange sein wie der UDT der angezeigt werden soll.
  Ret :=  BLKMOV(SRCBLK := LS_PAR ,DSTBLK := KLP); // Kopieren in die Temporäre Variable.  


(*Eigentlicher Programmteil starten*)
  KLP.BF_Soll := TRUE; // Klappe soll nach Sollwert fahren
  Istposition := KLP.Istposition / 10; // Istwert der Klappe umrechnen


(*Hier wird der Pointer zurückgeschrieben*)
Ret :=  BLKMOV(SRCBLK := KLP ,DSTBLK := LS_PAR);
END_FUNCTION_BLOCK

Aufgerufen wird der Baustein dann so:
Code:
call Obj_Beispiel, IDB_Obj_Beispiel
   LS_PAR :=  "IO".ErstesWort
 
Hi Flux

Hier noch so ein beispiel.

Code:
FUNCTION_BLOCK SlaveBSENDRCV


TITLE = 'Kommunikationsbaustein'
// Dieser Baustein bietet den Server an.
// Passt zum Baustein MasterCom mit BSEND/RECV
//
// 1.0 Erste Version




VERSION : '1.0'
AUTHOR  : VoR
NAME    : KLP
FAMILY  : TUV


VAR_INPUT
  DB_1_Recv : BLOCK_DB ; // DB der von AS nach LST geht
  DB_1_Send : BLOCK_DB ; // DB der von LST nach AS geht
  ID : WORD ;    //Primäre Verbindung
  ID_Red : WORD ;    //Sekundäre Verbindung
END_VAR
VAR
  PUT : "BSEND";    
  GET : "BRCV";    
  save_status_Put : WORD ;    
  save_status_GEt : WORD ;    
  Zeit_alt : TIME ;    
END_VAR
VAR_TEMP
  zDB_1_Recv : ANY ;  
        pDB_1_Recv AT zDB_1_Recv : ANY_POINTER;  
  zDB_1_Send : ANY ;
        pDB_1_Send AT zDB_1_Send : ANY_POINTER;     
  wDummy : WORD ;    
  RetVAL : INT ;    
  Write_Prot : BOOL ;    
  Zeit : TIME ;  
  Anzahl_Werte_RECV:  WORD;  
  Anzahl_Werte_SEND:  WORD;  


END_VAR


  DB_1_send.DW[0] := DB_1_recv.DW[0]; // Watchdog wieder zurückkopieren




Zeit := TIME_TCK();


(*Pointer aufbereiten*)
(*Empfangen*)
  pDB_1_Recv.syntaxid         := B#16#10; ;                                 // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_Recv.Bereichstyp      := 2;                                         // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_Recv.DB_Nr            := WORD_TO_INT(BLOCK_DB_TO_WORD(DB_1_Recv));  // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  RetVAL := TEST_DB(DB_NUMBER := BLOCK_DB_TO_WORD(DB_1_Recv),DB_LENGTH := Anzahl_Werte_RECV ,WRITE_PROT := Write_Prot);
  pDB_1_Recv.Anzahl_Werte     := WORD_TO_INT(Anzahl_Werte_RECV);             // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  pDB_1_Recv.Startadresse     := DW#16#84000000;  
  
  (*Senden*)
  pDB_1_send.syntaxid         := B#16#10; ;                                 // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_send.Bereichstyp      := 2;                                         // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_send.DB_Nr            := WORD_TO_INT(BLOCK_DB_TO_WORD(DB_1_send));  // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  RetVAL := TEST_DB(DB_NUMBER := BLOCK_DB_TO_WORD(DB_1_send),DB_LENGTH := Anzahl_Werte_SEND ,WRITE_PROT := Write_Prot);
  pDB_1_send.Anzahl_Werte     := WORD_TO_INT(Anzahl_Werte_SEND);             // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  pDB_1_send.Startadresse     := DW#16#84000000;  
  


   
  PUT(ID := ID // IN: WORD
            ,R_ID := w#16#3 // IN: DWORD
            ,SD_1 := zDB_1_Send // INOUT: ANY
            ,LEN :=  Anzahl_Werte_SEND   // INOUT: WORD
            ); 


 
          
          PUT.REQ := TRUE;
          
          IF PUT.ERROR OR PUT.DONE THEN
            Put.Req := False;
          END_IF;
         
          IF PUT.ERROR THEN
           save_status_Put := PUT.STATUS; 
        END_IF;      
        
         
   




      GET(EN_R := TRUE // IN: BOOL
           ,ID := ID // IN: WORD
           ,R_ID := w#16#1 // IN: DWORD
           ,RD_1 := zDB_1_ReCV // INOUT: ANY
           ,LEN :=  Anzahl_Werte_RECV // INOUT: WORD
           ); 
   




        
          IF GET.ERROR THEN
           save_status_GET := GET.STATUS; 
        END_IF;  


 END_FUNCTION_BLOCK

Mit den Beispielen zuvor solltest du in der lage sein sämtliche Schönheiten der Symbolischen Pointeradressierung auszuschöpfen. Habe ich alles aufgrund dieses Treads zusammengebastelt und erfüllt so ziemlich alles was man sich an Adressierungen wünschen kann.

mfG René
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke für dein Beispiel.

Wo ist denn besagte AT Sicht auf den DB? In deinem Beispiel arbeitest du zwar strukturiert, aber nicht symbolisch:
DB_1_send.DW[0] := DB_1_recv.DW[0]; // Watchdog wieder zurückkopieren

Ich hab bisher für das gleiche entweder die DBNo : INT oder DB : ANY als IN verwendet. Sehe da jetzt keinen großen Unterschied zu BLOCK_DB.
 
Zuletzt bearbeitet:
Direkte AT Sicht scheint so nicht zu gehen wie du dir das vorstellst.
Ich sehe das nur so dass du den Block DB einliest, den Pointer so auflöst wie ich im letzten Beispiel und dann per Blockmove in eine Temporäre Struktur (UDT oder Struct kopierst) Dann Symbolisch damit arbeitest. die Structur am schluss wieder per Pointer und Blockmove zurückschreibst.

mfG René
 
Ok, dann weiß ich Bescheid. Hatte aus LLs Aussage entnommen, man könnte direkt ne AT Sicht auf den BLOCK_DB Eingang legen, ohne den Kopiervorgang. .
 
Du kannst einen UDT an IN_OUT übergeben, dann kannst Du ohne umkopieren symbolisch zugreifen.

OK, ich meine das mal für eine Kommunikationsschnittstelle getestet zu haben.

FB1 und FB2 sollten über nen IN_OUT : UDT_Telegramm kommunizieren. Das Telegramm enthält bspw. ein Update-Bit.

FB1 wurde zuerst aufgerufen, dann FB2. Und das Problem war, dass FB1 bspw. das von FB2 gesetzte Update-Bit nicht sehen konnte..

Dafür hätte ich das UDT in einen Global DB auslagern müssen, dann wäre es wahrscheinlich gegangen.

Ausweg war dann, ein UDT_1nach2 und ein UDT_2nach1 zu definieren, ab dem Moment wurde es mir zu unelegant und ich bin auf das Anypointer + Blockmove Konstrukt umgestiegen.
 
Zurück
Oben