Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

Recipe automatically save

alex87
2015-12-30
2020-02-24
  • alex87 - 2015-12-30

    Why does function "Save recipe changes to files automatically" NOT work? What is the sense of this function and how is ment to be used.

    I have solved my way by setting timer every 60sec to save variables to current file.
    via:

    RecipeManCommands.ReadRecipe(sRecipeDef, cRecipe);
    

    It works but is anoying and when unsuspected shutdown happens right in middle saving writes 000 to file.

    how to handle auto save variables?

    BR
    Alex

    IMG: recipe1_save.png

     
  • Anonymous - 2016-01-02

    Originally created by: crthomas1234

    I am also interested in the answer to Alex's question.

    The PLC I use does not have a lot of persistent retain memory, so I use the recipe system to backup and restore system parameters on a machine power cycle. This is also really helpful during the development stage when you are making a lot of changes to the PLC and a full download erases your retain memory.

    What we have done for now is to create a function block and reads the name of each variable in the recipe file and then use the IEC Var Access library to request that variable's value by its name. I can then walk through the entire recipe file and if any value has changed, I can initiate the recipe save. I did this because if you were to just save the recipe file every minute or so, you would eventually render the flash memory in the controller unusable due to the limited number of writes you can have.

    The downside of this method is that you cannot actually get the actual variable name from the recipe. Instead, there is a column for the user to type in their own descriptive name which can be accessed through the IEC program. So for every variable that I add to the recipe system, I must copy and paste the variable name including the full path in the "name" column of the recipe file for this to work. And since you can't copy/paste in the recipe editor or export to some sort of text file, this is very labor intensive in the beginning.

    I would love to ask an extension to the RecipeManCommand function block that would automate looking at the values or the recipe file versus the current values to know if they are different so you can initiate the save.

    The save changes to recipes option Alex mentioned sounds like it would do this, but I think it is for something different such as actual changes to the definition, not the actual recipe values.

    I have read the help files over and over again trying to understand how the recipe system works. It sort of makes sense to me and through a lot of trial and error, I have gotten the system to work as I need it too.

     
  • alex87 - 2016-01-07

    Anyone?

     
  • marks - 2020-02-24

    I didn't use recipes..

    But, I did find where someone had posted enough information about saving retentive memory that I was able to get store / restore working around startup.. it was necessary as we've written apps on the raspberry pi..

    It could use a little 'clean up', but its getting the job done. Notice also that I use it to initiate a controlled shutdown rather than just powering off the device.

    I created a separate task, which runs this:

    PROGRAM persistentStorage
    VAR
       command1       : STRING := 'sudo halt';
       stdout          : STRING(1000) := '';   
       result          : RTS_IEC_RESULT;
       doRetain              : FB_AppRetain;
       T_restoreAlarm           : TON;
       doSync         : BOOL;
    END_VAR
    // handle save of retained memory to disk: /var/opt/codesys/PlcLogic/Application/estacada.ret
    doRetain(   store := GVL.doSavePersist,
             restore := GVL.doRestorePersist,
             shutdown := GVL.doShutDown,
             busy => GVL.pmBusy,
             done => GVL.pmDone,
             error => GVL.pmError,
             result => GVL.pmResult );
             
    // Save retained memory complete, sync the file system with the SD Card
    IF ( GVL.doSavePersist AND ( GVL.pmDone OR GVL.pmError ) ) THEN
       GVL.doSavePersist   :=   FALSE;
       doSync   :=   GVL.pmDone;
    END_IF
    // restore persistent memory upon startup
    IF ( ( GVL.iPersistState = 0 ) AND NOT GVL.doRestorePersist ) THEN
       GVL.doRestorePersist   :=   TRUE;
    END_IF
    // Restore retained memory complete, let the system know it can proceed
    IF ( GVL.doRestorePersist AND GVL.pmDone ) THEN
       GVL.doRestorePersist := FALSE;
       GVL.iPersistState   :=   1;
    END_IF
    //Attention: allow shell commands in CODESYSControl.cfg - presently AllowAll
    IF ( doSync ) THEN
       doSync   :=   FALSE;
       command1   :=   'sudo sync';   
       SysProcess._(pszCommand:=command1, pszStdOut:=stdout, udiStdOutLen:= SIZEOF(stdout),pResult := ADR(result));
    END_IF
    //Attention: allow shell commands in CODESYSControl.cfg
    IF ( GVL.doShutDown AND NOT ( GVL.doSavePersist OR GVL.doRestorePersist ) ) THEN
       GVL.doShutDown := FALSE;
       command1   :=   'sudo halt';   
       SysProcess._(pszCommand:=command1, pszStdOut:=stdout, udiStdOutLen:= SIZEOF(stdout),pResult := ADR(result));
    END_IF
    // Signal to alarm processing that restore has failed
    T_restoreAlarm( IN := GVL.doRestorePersist, PT := T#3S);
    IF ( T_restoreAlarm.Q ) THEN
       GVL.doRestorePersist := FALSE;
       GVL.iPersistState := 2;   // lie after warning the operator
    END_IF
    // end
    

    The function block that it calls is this:

    FUNCTION_BLOCK  FB_AppRetain
    VAR_INPUT
       store            : BOOL;
       restore         : BOOL;
       shutdown         : BOOL;
    END_VAR
    VAR_OUTPUT
       done            : BOOL;
       busy            : BOOL;
       error            : BOOL;
       result         : RTS_IEC_RESULT;
    END_VAR
    VAR
       fbDelete            : FILE.Delete; (* Function block to delete the file *)
        (* The retain file <application-name>.ret is placed in the directory of the bootproject *)
       sFileName           : STRING := '/var/opt/codesys/PlcLogic/Application/estacada.ret';
       pApp            : POINTER TO APPLICATION;
       step            : INT := 0;
       R_store            : R_TRIG;
       R_restore         : R_TRIG;
       F_store            : F_TRIG;
       F_restore         : F_TRIG;
    END_VAR
    // RETAIN memory is saved to /var/opt/codesys/PlcLogic/Application/estacada.ret
    R_store( CLK := store );
    R_restore( CLK := restore );
    F_store( CLK := store );
    F_restore( CLK := restore );
    // finished, or aborted?
    IF ( F_store.Q OR F_restore.Q ) THEN
       step   :=   0;
       busy   :=   FALSE;
       error   :=   FALSE;
       done   :=   FALSE;
    END_IF
    CASE step OF 
       
       0:   // begin retained memory store to file
          IF ( R_store.Q AND NOT ( restore OR shutdown ) ) THEN
             step   :=   10;
             busy   :=   TRUE;
             error   :=   FALSE;
             done   :=   FALSE;
          END_IF
          // restore retained memory from file
          IF ( R_restore.Q AND NOT ( store OR shutdown ) ) THEN
             step   :=   20;
             busy   :=   TRUE;
             error   :=   FALSE;
             done   :=   FALSE;
          END_IF
       10: (*   First delete the retain file.
                This is necessary, because the function AppStoreRetainsInFile appends the data at the end of the file. *)
          fbDelete(xExecute:=TRUE, sFileName:=sFileName);
          IF (    fbDelete.xDone
             OR (fbDelete.xError AND ( fbDelete.eError = File.Error.NOT_EXIST ) ) ) THEN
       
             (* Attention: It takes at least one cycle until xDone is TRUE *)
             fbDelete(xExecute:=FALSE);
             step   :=   12;   // file gone, replace it with a fresh copy
          ELSIF ( fbDelete.xError ) THEN
             fbDelete(xExecute:=FALSE);
             step   :=   14;   // file delete failed ugly
          END_IF
          
       12:   (* Now, it's the time to save the retains. *)
          pApp := AppGetCurrent(pResult:=ADR(result));
          IF pApp <> 0 THEN
             (* Store the variables in a file*)
             result   := AppStoreRetainsInFile(pApp,sFileName);
             error   :=   FALSE;
             busy   :=   FALSE;
             done   :=   TRUE;
             step   :=   0;
          ELSE
             step   :=   14;
          END_IF
       
       // some step along the way failed
       14:      error   :=   TRUE;
             busy   :=   FALSE;
             done   :=   FALSE;
             step   :=   0;
             
       20:   (*   Restore the Retain Variables from the file.
             For storing and restoring, the same pointer to application (pApp) must be used.*)
          pApp := AppGetCurrent(pResult:=ADR(result));
          IF pApp <> 0 THEN
             result := AppRestoreRetainsFromFile(pApp, sFileName);
             error   :=   FALSE;
             busy   :=   FALSE;
             done   :=   TRUE;
             step   :=   0;
          ELSE
             step   :=   14;
          END_IF      
       
    END_CASE
    // END
    
     

Log in to post a comment.