I wrote a bit of test code (TwinCAT 3.1.4024.44)
MAIN.TcPOU
:
PROGRAM MAIN
VAR_INPUT
nInits: LINT;
nReinits: LINT;
nExits: LINT;
END_VAR
VAR
fbA, fbB, fbC : FB_Test;
bRun : BOOL;
bCallStackFB : BOOL;
nStackResult : INT;
END_VAR
-------------
IF bRun THEN
fbA();
fbB := fbA;
fbB();
fbC := fbB;
fbA.nInput := fbB.nOutput;
bRun := FALSE;
END_IF
IF bCallStackFB THEN
nStackResult := UseStackFB(10);
bCallStackFB := FALSE;
END_IF
==============
METHOD UseStackFB : INT
VAR_INPUT
nInput : INT;
END_VAR
VAR
fbStackFB : FB_Test;
END_VAR
---------------
fbStackFB(nInput := nInput);
UseStackFB := fbStackFB.nOutput;
FB_Test.TcPOU
:
FUNCTION_BLOCK FB_Test
VAR_INPUT
nInput : INT;
END_VAR
VAR_OUTPUT
nOutput : INT;
nRunCount : LINT := 0;
END_VAR
VAR
nVar : INT;
END_VAR
---------------
nVar := nInput + 1;
nOutput := nVar + 1;
nRunCount := nRunCount + 1;
================
METHOD FB_exit : BOOL
VAR_INPUT
bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
----------------
MAIN.nExits := MAIN.nExits + 1;
================
METHOD FB_init : BOOL
VAR_INPUT
bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
bInCopyCode : BOOL; // if TRUE, the instance afterwards gets moved into the copy code (online change)
END_VAR
----------------
MAIN.nInits := MAIN.nInits + 1;
================
METHOD FB_reinit : BOOL
VAR_INPUT
END_VAR
----------------
MAIN.nReinits := MAIN.nReinits + 1;
Running the main program in the TC debugger shows that MAIN.nInits
starts at 3 (one each for the statically allocated fbA
, fbB
and fbC
instances). Toggling bRun
does not increment the init, reinit, or exit counters; so clearly the FB_init()
, FB_reinit()
and FB_exit()
methods are not invoked when one FB instance's data is overwritten by that of another.
Inspecting the behaviour of fbA
, fbB
and fbC
in the debugger shows that the assignment operation definitely copies all of the FB's VAR_INPUT
, VAR
, and VAR_OUTPUT
state when the FB is assigned.
Toggling bCallStackFB
does increment both MAIN.nInits
and MAIN.nExits
, so creating a temporary instance of an FB as a stack variable, such as a VAR
in a FUNCTION
, METHOD
or PROPERTY
implementation, definitely calls both FB_init()
and FB_exit()
. (Note: TwinCAT versions before 3.1.4022 and CoDeSys versions before V3.5 SP9 Patch 8 do not call FB_exit()
for stack-allocated FB instances.)