In IEC 61131-3 implementations (e.g. CoDeSys, TwinCAT, Unity), what actually happens when you use the assignment operator on variables of Function Block type? I've looked at the TwinCAT and CoDeSys docs without finding any kind of clear description; I assume it's defined by the 61131-3 spec, but I don't have a copy of that.
E.g:
VAR
fbA, fbB : TON;
END_VAR
// Function Block assignment
fbB := fbA;
In this case, memory is statically allocated for both the fbA
and fbB
instances before the assignment is executed. Does the assignment operator just copy the VAR_INPUT
, VAR
, and VAR_OUTPUT
internal state variables from fbA
's memory area to fbB
's? Does anything else happen?
What if fbA
or fbB
are instances of a Function Block type that implements FB_init()
, FB_reinit()
or FB_exit()
? Does assigning fbB := fbA
result in calls to any of those methods?
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.)