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