Prevent local variable retention

允我心安 提交于 2020-07-30 14:44:31

问题


Suppose I have a function block POU1 which has local variables val1: INT and val2: INT, e.g.

FUNCTION_BLOCK POU1
VAR
    val1: INT := 1;
    val2: INT := 1;
END_VAR

Now suppose the user of the FB declares it as RETAIN, e.g.

VAR RETAIN
    p1: POU1;
END_VAR
p1.val1 := 2;
p1.val2 := 2;

This will result in both val1 and val2 retaining the value of 2 in case of a warm reset, but what if I don't want that to happen to say val2 i.e. I want val1 to retain it's current value, but val2 to be reset in case of a warm reset (if the user declares my FB as RETAIN, otherwise I want both to reset)

How can I achieve this? (Also. same question goes for PERSISTENT)

PS. I tried {attribute 'init_on_onlchange'} and {attribute 'no_copy'} but they did nothing (maybe I used them wrong?). I also tried creating an additional FB with {attribute 'no_instance_in_retain'} and adding it as a local variable of POU1 but that resulted in a build error.


回答1:


One way I just found is to implement FB_Exit explicitly and reset those variable in it:

METHOD FB_Exit: BOOL
VAR_INPUT
    bInCopyCode: BOOL;  // TRUE: the exit method is called in order to leave the instance which will be copied afterwards (online change).  
END_VAR
val2 := 1; // reset all variables you don't want retained to their defaults

This seems to work, but not sure if this might have other consequences. e.g. Is FB_Exit called in case of a power failure?




回答2:


The problem with FB_Exit or FB_Init and VAR PERSISTENT/RETAIN is that I couldn't find a consistent behaviour across platforms (Twincat/Codesys) And yes there are cases where fb_exit is not called, for example in Twincat when you do a cold reset.

My approach on this would be different. I would neither use attributes nor fb_exit or fb_init which under certain circumstances could be difficult to debug. Instead I would use a simple global FB like this one:

FUNCTION_BLOCK FB_System

VAR
    bInit : BOOL;
    nCycleCount : UINT;
END_VAR

VAR CONSTANT
    cINIT_AFTER_CYCLE : UINT := 2;
END_VAR


IF NOT bInit 
THEN
    nCycleCount := nCycleCount + 1;
END_IF

IF nCycleCount >= cINIT_AFTER_CYCLE
THEN
    bInit := TRUE;
END_IF

METHOD isInit : BOOL

isInit := bInit;

Now, add an input to your retain/persistent FB:

VAR_INPUT
    bSystemInit : BOOL;
END_VAR 

And call it like this:

fbRetain(bSystemInit := fbSystem.isInit());

Initialize your values if the system is not initialized. Add this check in your FB implementation:

IF NOT bSystemInit THEN
    anIntVar := 0;
    //or call a reset() method where you put all your variables that need to be initialized
END_IF 

If you have many FBs that need this kind of initialization, you can extend them with an FB that has this example code in it. By doing so you can reuse your code efficiently.

Having said that, I must warn you that I had many problems with persistent data in the past. It happened to me repeatedly that persistent data became corrupt causing production problems or even preventing the runtime to start.

If I had to design a system from scratch I would use the XML-server from Beckhoff or the XML-utility from codesys to store relevant production data in an xml-file and retrieve this data at runtime start.



来源:https://stackoverflow.com/questions/63059799/prevent-local-variable-retention

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!