arraysplctwincatst

Beckhoff-PLC: Declaring an array as remanent inside a Function Block


this is my first question on StackOverlfow, so feel free to give me feedback on the problem :)

I'm new to working with controllers from Beckhoff and I'm trying to program a program block for communicating machine data to the PC. To store the data of different types I use an array of T_ARG in the function block "Communication", which is instantiated in "MAIN". At each restart its data are reset, which leads to the fact that I would have to load the information at the start always again into the array, whereby I would have the data then twice on the system.

Code inside the FB "Communication":

VAR
    Values : ARRAY[DataArrayLow..DataArrayHigh] OF T_ARG;
    ValueChanged : ARRAY[DataArrayLow..DataArrayHigh] OF BOOL;
END_VAR

I am using to the array "ValueChanged" to track change of the data by using a setvalue-method, which marks the index in "Values" to be transmitted.

For now i am calling the dunction block in "MAIN" as usual:

VAR
    Communication : FB_Comm;
END_VAR
__________________________________________________________________________
Communication();

I already tried to mark the variables as persistent, which did not work.

I dont really wont to create an external global variable and give it to the function block as input, since it defeats the purpose of capsulation and it would be cluttered.


Solution

  • Here is a simple project that can show you that PERSISTENT data inside a function block can indeed be saved.

    Declare a test structure:

    TYPE ST_DataToBePersistent :
    STRUCT
        bVar    : BOOL;
        nVar    : INT;
        fVar    : REAL;
    END_STRUCT
    END_TYPE
    

    Now declare a function block, FB_Test using that struct as internal parameter set. Adding the FB_WritePersistentData as VAR_IN_OUT. We need to call it to save the persistent data inside this function block.

    FUNCTION_BLOCK FB_Test
    
    VAR PERSISTENT
        arrPersistentParameterStruct    : ARRAY [0..10] OF ST_DataToBePersistent;
    END_VAR
    
    VAR_IN_OUT
        fbWritePersistentData   : FB_WritePersistentData;
    END_VAR
    
    VAR
        bSavePersistentData : BOOL;
    END_VAR
    

    Body of the FB:

    IF bSavePersistentData THEN
        bSavePersistentData := FALSE;
        fbWritePersistentData.START := TRUE;
    END_IF
    

    A simple method to set the value of the internal structure:

    METHOD PUBLIC WriteToStruct
    VAR_INPUT
        arrPersistentParameterStruct : ARRAY[0..10] OF ST_DataToBePersistent;
    END_VAR
    

    And it's body. When called internal structure array will be changed and we also set a flag to call the passed FB_WritePersistentData which will saved ALL persistent data on the dedicated port (local):

    THIS^.arrPersistentParameterStruct := arrPersistentParameterStruct;
    bSavePersistentData := TRUE;
    

    Instantiate 2 instances, just to set the value of the array and adding FB_WritePersistentData that will be called when the method FB_Test.WriteToStruct is called. Also adding some variables for diagnostic purpose and triggering the methods:

    PROGRAM MAIN
    VAR
        fbTestInstance1 : FB_Test;
        fbTestInstance2 : FB_Test;
        fbWritePersistentData   : FB_WritePersistentData;
        bSetData1   : BOOL;
        bSetData2   : BOOL;
        arrDataToSet    : ARRAY[0..10] OF ST_DataToBePersistent;
        TofPersistentDataSaved  : TOF;
        FtrigDataSaveComplete   : F_TRIG;
        bDataSaveComplete       : BOOL;
    END_VAR
    

    In the main body, cyclically call both fbs as well as the persistent data FB. Set the values of the arrDataToSet in online mode to anything you want and trigger which function block you want to change. Observe the bDataSaveComlete is set for 2 seconds after saving is complete.

    fbTestInstance1(fbWritePersistentData := fbWritePersistentData);
    fbTestInstance2(fbWritePersistentData := fbWritePersistentData);
    
    IF bSetData1 THEN
        bSetData1 := FALSE;
        fbTestInstance1.WriteToStruct(arrPersistentParameterStruct := arrDataToSet);
    END_IF
    
    IF bSetData2 THEN
        bSetData2 := FALSE;
        fbTestInstance2.WriteToStruct(arrPersistentParameterStruct := arrDataToSet);
    END_IF
    
    fbWritePersistentData(NETID := '', PORT := 851);
    
    FtrigDataSaveComplete(CLK := fbWritePersistentData.BUSY);
    TofPersistentDataSaved(IN := FtrigDataSaveComplete.Q, PT := T#2S);
    bDataSaveComplete := TofPersistentDataSaved.Q;
    
    IF fbWritePersistentData.BUSY THEN
        fbWritePersistentData.START := FALSE;
    END_IF
    

    Example:

    Initial state of the fbTestInstance1: enter image description here

    Now I set the structure: enter image description here

    I will write to fbTestInstance by setting the bSetData1: enter image description here

    I will set different values to second fb: enter image description here

    Now I will initiate a restart of the target machine's TwinCAT: enter image description here

    Logging back in, the data is still the same: enter image description here

    Rebooting the target machine: enter image description here

    Persistent data is always loaded properly upon TwinCAT, be it restarting the runtime or operating system.

    Hope this answers your question. As Jakob already mentioned, what dwpessoa is saying ONLY applies to functions since all of the memory they occupy is discarded when it has been released.