问题
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