In IEC61131-3, what are the semantics of the assignment operator for variables of Function Block type?

142 Views Asked by At

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?

1

There are 1 best solutions below

2
Hydrargyrum On BEST ANSWER

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.)