TIA Ladespeicher Begrenzung für WRIT_DBL umgehen?

BaumimGarten

Level-1
Beiträge
63
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin,

Ich arbeite gerade daran einen Datensatz aus einem UDT in den Ladespeicher zwischen zu puffern, da im Arbeitsspeicher recht wenig Platz ist und die Ausfallzeiten in den gepuffert werden muss, recht klein sind.
Das schreiben in den Ladespeicher des UDTs übernimmt der WRIT_DBL Baustein von Siemens. Dieser schreibt in ein DB im Ladespeicher mit einer Arraystruktur aus dem UDT. Das passt auch alles so weit, er schreibt rein und ließt dann später wieder mit READ_DBL die Daten passend aus, allerdings speichert er nur bis zu 50 Datensätze im Ladespeicher DB und gibt anschließend den Fehlercode 80C3 aus. In der TIA Hilfe steht folgender Satz zum Fehlercode:
1698066310457.png
Ich dachte als erstes, dass der Baustein mehrere Male aufgerufen wird, das kann ich aber ausschließen. Nach einer kleinen Recherche könnte ich noch das zum Fehlercode finden:

1698066294603.png

Das heißt ja im Endeffekt, dass ich nicht mehr Requests als 50 stellen kann. Die Frage ist, wie der Baustein richtig funktioniert, ob er erstmal intern bis zu 50 Datensätze speichert und dann im Notfall in den Ladespeicher ablegt wird, oder wann er aus den Requests einen Auftrag erledigt.
Ich könnte das Problem umgehen, indem ich mehrere Datensätze zusammenfasse und dann über den Baustein in den Ladespeicher schreibe, jedoch find ich das recht umständlich und hässlich.

Hat jemand noch eine weitere Idee

----------------------------------------------------------------------------------------------------------------------------------------------------
Ich arbeite mit TIA V18 einer S7 1212c und der Firmware 4.6

Vielen Dank im voraus
 
Moin BauminGarten,

Wie hast Du die WRIT_DBL denn programmiert? Es geht ja um die gleichzeitig aktiven WRIT_DBL-Aufrufe. Der Baustein arbeitet ja asynchron. Wie oft rufst Du ihn auf bzw. hast Du ihn programmiert?

VG

MFreiberger
 
Moin BauminGarten,

Wie hast Du die WRIT_DBL denn programmiert? Es geht ja um die gleichzeitig aktiven WRIT_DBL-Aufrufe. Der Baustein arbeitet ja asynchron. Wie oft rufst Du ihn auf bzw. hast Du ihn programmiert?
Deswegen bin ich ja auch irritiert. Laut Doku habe ich ja "nur" 50 Request angelegt, aber es gibt ja auch keine Ausgang der die angibt, dass der Datensatz erfolgreich abgelegt wurde, sondern nur den Status 16#7002, der die aktive Datenübergabe anzeigt und die 16#7001 der eine Datenübergabe anstößt.
1698068513506.png

Ich rufe ihn im gesamten Programm nur einmal auf und da trigger ich den REQ Eingang über eine positive Flanke eines Zyklischen bzw. auch einem Alarmbasierten Trigger. An sich bin ich wie in der Dokumentation zum Baustein vorgegangen.
Der zyklische Trigger gibt eine positive Flanke jede Minute aus und der Alarmtrigger wurde nicht benutzt da noch keine Digitalen Eingänge an der SPS geschaltet wurden.

Irgendwie fühlt es sich so an als würde er das nicht in den Ladespeicher schreiben, sondern nur reservieren und intern abspeichert.
 
Zuletzt bearbeitet:
und grundsätzlich drüber nachdenken, wieviel Schreibvorgänge der Ladespeicher verkraftet, bis er für immer defekt ist. Wo speichert die 1200er das hin, ohne SMC?

Ja ich weiß, die Zwischenspeicherung tritt auch nur ein, falls alle Kommunikationen ausfallen, also nur im Notfall, um ein Datenverlust zu verhindern.
Der Ladespeicher ist über eine SMC 4MB vergrößert wurden.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Deswegen bin ich ja auch irritiert. Laut Doku habe ich ja "nur" 50 Request angelegt, aber es gibt ja auch keine Ausgang der die angibt, dass der Datensatz erfolgreich abgelegt wurde, sondern nur den Status 16#7002, der die aktive Datenübergabe anzeigt und die 16#7001 der eine Datenübergabe anstößt.
Anhang anzeigen 72367

Ich rufe ihn im gesamten Programm nur einmal auf und da trigger ich den REQ Eingang über eine positive Flanke eines Zyklischen bzw. auch einem Alarmbasierten Trigger. An sich bin ich wie in der Dokumentation zum Baustein vorgegangen.
Der zyklische Trigger gibt eine positive Flanke jede Minute aus und der Alarmtrigger wurde nicht benutzt da noch keine Digitalen Eingänge an der SPS geschaltet wurden.

Irgendwie fühlt es sich so an als würde er das nicht in den Ladespeicher schreiben, sondern nur reservieren und intern abspeichert.
Vielleicht wird die Bausteinbearbeitung nicht beendet, wenn BUSY noch true ist und dann eine neue Flanke am REQ kommt?

Sperrst Du das triggern über den REQ, solange BUSY noch true ist?
 
Vielleicht wird die Bausteinbearbeitung nicht beendet, wenn BUSY noch true ist und dann eine neue Flanke am REQ kommt?

Sperrst Du das triggern über den REQ, solange BUSY noch true ist?
Also wir geben ihm nur eine positive Flanke.
Ich hab auch gerade mal ein positives Signal am Req angelegt und da ist er direkt auf 50 hochgeschossen und hat den Fehler ausgelöst.
 
Ich glaube, da ist irgendwas völlig falsch programmiert bei der Auftragssteuerung. Ich kenne kein Problem, was es erfordert, dass mehr als ein Schreibauftrag gleichzeitig aktiv ist. Wie sieht die Logik rund um WRIT_DBL aus?

Btw: was macht eigentlich TIA, wenn es im Projekt und online im Ladespeicher unterschiedliche DB feststellt?
 
Btw: was macht eigentlich TIA, wenn es im Projekt und online im Ladespeicher unterschiedliche DB feststellt?
Nach meiner Erfahrung mit WRIT_DBL werden die DBs dann spätestens nach dem nächsten Online gehen nicht mehr grün sondern als Unterschied angezeigt. Das verwirrt zumindest den Instandhalter.
 
Okay ich hab den Fehler gefunden. Ich hab nach einem Status bzw. Output des Bausteins geguckt, um meinen Index auf den er meinen aktuellen Datensatz drauf schreiben soll. Dabei hab ich auf die 7002 geguckt, weil es meines Verständnisses (nach lesen der Hilfe und Doku des Bausteines) keinen Output bzw. Status für das beenden eines Auftrages gab. Allerdings gibt die Doku zu den Asynchronen Bausteinen einen 0000 Status als ein beenden eines Auftrages an. In der Doku zu dem Baustein gibt die 0000 ein "No error" aus.

D.h ich hab den Index geändert währenddessen der Baustein noch Busy war und dadurch den nächsten Auftrag gestartet ohne den vorherigen komplett abzuarbeiten.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich triggere den Baustein nur an wenn das Busy weg ist. Leider kann man nicht herausfinden wieviele WRIT_DBL gerade aktiv sind. Das wäre ne Coole funktion wenn man vor Ausführung prüfen könnte wieviele begrenzte Recourcen schon in Zugriff sind.
Ich habe da übrigens mal n Beispiel gemacht.
Das könntest du für deine Zwecke anpassen wenn du willst.

Code:
TYPE "ANY_POINTER"
VERSION : 0.1
   STRUCT
      SyntaxID : Byte;
      Bereichstyp : Byte;
      Anzahl_Werte : UInt;
      DB_Nr : UInt;
      Startadresse : DWord;
   END_STRUCT;

END_TYPE

FUNCTION_BLOCK "SaveDatablock"
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : VoR
VERSION : 0.1
//Baustein soll einen kompletten DB im Ladespeicher sichern. Um TIA Reinitialisationen zu begrenzen.
//Der zu sichernde DB darf nicht optimiert sein.
//Die Sicherung muss durch SaveDB getriggert. SaveDB wird vom Baustein zurückgesetzt.
   VAR_INPUT
      DB_TO_SAVE : Any;   // Welcher DB soll gesichert werden
      DB_NR_Loadmem { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt := 60000;   // DBNR_Ab 60000 im Ladespeicher
      Cycl_Save : Bool;   // DB Zyklisch in remanenz Lade und Arbeitspeicher sichern
      Cycl_Time : Time := T#1H;   // cycle time to save datablock on SD Card
   END_VAR

   VAR_IN_OUT
      SaveDB : Bool;
      RecoverDB : Bool;
   END_VAR

   VAR
      init : Bool := TRUE;   // true. Instance is reinitialized
      DeleteDB : Bool;
      CreateDB : Bool;
      DelBusy : Bool;
      Attrib : Byte;
      Attr_DB_Lenght : UDInt;
      Create_DB_Busy : Bool;
      Create_DB_Num : UInt;
      Busy_Write : Bool;
      Busy_Read : Bool;
      Status_BLKMOV_Save_1 : Int;
      Status_BLKMOV_Recover_1 : Int;
      speicherbereich : Word;
      testpointer : "ANY_POINTER";
      trueval { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool := TRUE;
      FN_Busy {InstructionName := 'F_TRIG'; LibVersion := '1.0'} : F_TRIG;
      Status { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
      Status_attr { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
      Status_crea { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
      Status_Attr_DB { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
      Status_BLKMOV_Save { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
      Status_BLKMOV_Recover { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
      acttime {InstructionName := 'DTL'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DTL;
      oldtime {InstructionName := 'DTL'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DTL;
      time_status : Int;
      timeflanke : Bool;
      GetSMCinfo_Instance {InstructionName := 'GetSMCinfo'; LibVersion := '1.1'} : GetSMCinfo;
      SD_Info : UDInt;
      SD_Info_Req : Bool;
      Anz_Restores : UDInt;
      Anz_Sicherungen : UDInt;
      Anz_creates : UDInt;
      Trigger {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : Array[0..5] of R_TRIG;
      tpINIT {InstructionName := 'TP_TIME'; LibVersion := '1.0'} : TP_TIME;
      tonCycl {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
   END_VAR

   VAR_TEMP
      pSourceDB : Any;
      ptSourceDB AT pSourceDB : "ANY_POINTER";
      pSaveDB : Any;
      ptSaveDB AT pSaveDB : "ANY_POINTER";
      DB_Lenght : UDInt;
      Attrib_RCV : UDInt;
      Anzahl_Werte : Word;
      InitBool : Bool;
      DB_save_Length : UDInt;
      DB_save_Attrib : Byte;
      Attr_status : Int;
      Attr_status_st : Struct
         Ladespeicher : Bool;
         writeprotect : Bool;
         remanent : Bool;
         ladeundarbeitspeicher : Bool;
      END_STRUCT;
   END_VAR


BEGIN
    #time_status := RD_SYS_T(#acttime); // Reads the Actual Systemtime
    #tpINIT(IN := #init, // When init is set then there is a 10s pulse witch prevents that zero data will be written because of wrong readings
            PT := t#10s);
    
    // read actual Lifestatus of the SD Card
    #GetSMCinfo_Instance(REQ := #SD_Info_Req,
                         Mode := 2,
                         Info := #SD_Info);
    
    IF #SD_Info_Req AND (#GetSMCinfo_Instance.Done OR #GetSMCinfo_Instance.Error) THEN
        #SD_Info_Req := false;
    END_IF;
    
    // Jede Zeiteinstellung eine Sicherung durchführen
    #tonCycl(IN := NOT (#tpINIT.Q OR #timeflanke OR #RecoverDB OR #SaveDB),
             PT := #Cycl_Time);
    #timeflanke := #tonCycl.Q;
    
    IF #Cycl_Save THEN // If Cycl_Time active save at Cycle_time
        IF #timeflanke THEN
            #SaveDB := true;
        END_IF;
    END_IF;
    
    #pSourceDB := #DB_TO_SAVE;
    
    #speicherbereich := DWORD_TO_WORD(ROR(IN := #ptSourceDB.Startadresse, N := 24));
    
    (*Pointer for DB to save from*)
    #ptSourceDB.SyntaxID := B#16#10;
    #ptSourceDB.Bereichstyp := 2;
    #Status_Attr_DB := ATTR_DB(REQ := True,
                               DB_NUMBER := #ptSourceDB.DB_Nr,
                               DB_LENGTH => #DB_Lenght,
                               ATTRIB => #Attrib_RCV);
    #Anzahl_Werte := UDINT_TO_WORD(#DB_Lenght);
    #ptSourceDB.Anzahl_Werte := UDINT_TO_UINT(#Anzahl_Werte);
    #ptSourceDB.Startadresse := DW#16#84000000;
    
    (*Pointer for DB to save to*)   
    #ptSaveDB := #ptSourceDB; //Source/Save Pointer abgleichen
    #ptSaveDB.DB_Nr := #DB_NR_Loadmem;
    #Attr_status := ATTR_DB(REQ := True, DB_NUMBER := #ptSaveDB.DB_Nr, DB_LENGTH => #DB_save_Length, ATTRIB => #DB_save_Attrib);
    #Attr_status_st.Ladespeicher := #DB_save_Attrib.%X0; // Nachsehen ob DB zur Sicherung existiert.
    #Attr_status_st.writeprotect := #DB_save_Attrib.%X1;
    #Attr_status_st.remanent := #DB_save_Attrib.%X2;
    #Attr_status_st.ladeundarbeitspeicher := #DB_save_Attrib.%X3;
    
    // To Delete DB if needed.
    #Status := DELETE_DB(REQ := #DeleteDB, DB_NUMBER := LINT_TO_UINT(60000), BUSY => #DelBusy);
    #DeleteDB := false;
    
    (* Wenn DB nicht im Ladespeicher und auch keine DB Generierung im Gang
    Dann Einen DB auf der Karte erstellen um ihn als Sicherung zu nutzen *)
    IF #tpINIT.Q THEN
        IF NOT #Attr_status_st.Ladespeicher AND NOT #Create_DB_Busy THEN
            #CreateDB := true;
        ELSIF #Create_DB_Busy THEN // Generierung im Gang Init Bool setzen da dieser DB nicht zurückgesichert werden soll
            #CreateDB := false;
            POKE_BOOL(area := 16#84,
                      dbNumber := #ptSourceDB.DB_Nr,
                      byteOffset := 0,
                      bitOffset := 0,
                      value := TRUE);
        END_IF;
    END_IF;
    
    // Erstes Bit im zu sichernden DB überprüfen, wenn 0 dann soll DB restored werden.
    #InitBool := PEEK_BOOL(area := 16#84, dbNumber := #ptSourceDB.DB_Nr, byteOffset := 0, bitOffset := 0);
    
    #Status_crea := CREATE_DB(REQ := #CreateDB,
                              LOW_LIMIT := #ptSaveDB.DB_Nr,
                              UP_LIMIT := #ptSaveDB.DB_Nr,
                              COUNT := #ptSourceDB.Anzahl_Werte, // DB in Sicherungsgrösse da Remanenz und Arbeitspeicher gespart werden soll
                              ATTRIB := B#2#1000, // in Arbeits und Ladespeicher remanent
                              SRCBLK := #pSourceDB, BUSY => #Create_DB_Busy,
                              DB_NUM => #Create_DB_Num);
    
    
    #Status_BLKMOV_Save := WRIT_DBL(REQ := #SaveDB AND NOT #Create_DB_Busy, // DB inhalt sichern
                                    SRCBLK := #pSourceDB,
                                    BUSY => #Busy_Write,
                                    DSTBLK => #pSaveDB);
    
    
    IF #Status_BLKMOV_Save <> w#16#7000 THEN
        #Status_BLKMOV_Save_1 := #Status_BLKMOV_Save;
    END_IF;
    
    IF NOT (#Busy_Write OR #Create_DB_Busy) THEN
        #SaveDB := FALSE;
    END_IF;
    
    // DB wurde initialisiert Daten aus Sicherungsdb wiederherstellen.
    IF NOT #InitBool THEN
        #RecoverDB := true;
    END_IF;
    
    #Status_BLKMOV_Recover := READ_DBL(REQ := #RecoverDB AND NOT #Create_DB_Busy,
                                       SRCBLK := #pSaveDB,
                                       BUSY => #Busy_Read,
                                       DSTBLK => #pSourceDB);
    
    IF #Status_BLKMOV_Recover <> w#16#7000 THEN
        #Status_BLKMOV_Recover_1 := #Status_BLKMOV_Recover;
    END_IF;
    
    #FN_Busy(CLK := #Busy_Read);
    
    IF #FN_Busy.Q THEN
        #RecoverDB := FALSE;
        POKE_BOOL(area := 16#84,
                  dbNumber := #ptSourceDB.DB_Nr,
                  byteOffset := 0,
                  bitOffset := 0,
                  value := TRUE);
    END_IF;
    
    // Zähler der Lese und Schreibvorgänge
    #Trigger[0].CLK := (#Status_BLKMOV_Recover = W#16#7001);
    IF #Trigger[0].Q THEN
        #Anz_Restores := #Anz_Restores + 1;
    END_IF;
    
    #Trigger[1].CLK := (#Status_BLKMOV_Save = W#16#7001);
    IF #Trigger[1].Q THEN
        #Anz_Sicherungen := #Anz_Sicherungen + 1;
    END_IF;
    
    #Trigger[2].CLK := (#Status_crea = W#16#7001);
    IF #Trigger[2].Q THEN
        #Anz_creates := #Anz_creates + 1;
    END_IF;
    
    // Init reset
    #init := false;
END_FUNCTION_BLOCK
 
Zurück
Oben