Closed Roald87 closed 4 years ago
VAR variables are stored on the stack during execution of the method, so they are not suitable for holding FB instances (which must retain state between calls) - you will get unpredictable behaviour! There is a check for this in the Static Analysis checks, I believe (Report temporary Function Block instances).
The VAR_INST mechanism is a bit of magic that actually createds the variables as local instances of the function block (with a prefix to the name), then hides them from all but the method.
Instances like timers etc, should be not be instantiated in the head of a method. It's actually better not to instance any FB's in a method if you ask me because the FB instance will not execute correct, just like @RogerChristopher says. Using VAR INST is imho 'an ugly workaround'. Just use the FB's main declaration part instead of the Method and call the FB from the method. Be sure to also call the FB from the main body cyclicly and you'll be fine (composition)
There is nothing wrong with instantiating a class/fb in a method, unless you need it to hold state for more than one method-call (in the TcUnit case, one cycle). As everyone has said, the FB will re-initialize (I assume FB_Init is called every cycle?) after every call to the method, which is fine if the test only needs one cycle.
I think VAR_INST is quite neat "magic" and is a feature I haven't seen in many other languages. It's giving me the possibility to keep stuff on the stack of the fb/class, and at the same time hiding it from the other methods and keeping the main-FB much tidier. I guess these behave different than static variables, and can have different values for every instance of that FB. Just noticed that there are indeed static variables in TwinCAT (this must be recent addition, can't recall it was there before): https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2528787339.html&id=
I haven't tested whether these variables behave like static method variables in C++ though. Anyone here that have elaborated with these?
@Roald87 it's not necessary to add {attribute 'call_after_init'}
in the header of the TestSuite anymore :-) I think this was just an old thing laying around in the documentation prior to the 1.0 release of TcUnit. The reason you get ??? shown at the instance of the function block is because of the constant re-initialization at every cycle. You need to tell TwinCAT exactly which instance to show if you don't want to see the ???. This can be done by creating a breakpoint in the method, at which it will show the values for that particular PLC-cycle at which the breakpoint stopped.
Thanks for all the responses!
I like to keep my tests organized by putting all the variables inside a method. The alternative is to put many function block instances in the main test function block, which gets messy quite fast if there are ten or more tests. I guess in the end there is no right or wrong, only a style preference.
I get that fb instances in the VAR
of a method are not predictable between cycles, but I assumed that for a single cycle it shouldn't matter.
@sagatowski I haven't used VAR_STAT
so far. Also thanks for the tip about the pragma. I guess that one stuck around ever since I started using it x).
I don't think my problem is specific for the TcUnit library, but the behavior I noticed i quite strange.
I have a function block
FB_FunctionBlock
for which I wrote some tests inFB_FunctionBlock_Tests
.FB_FunctionBlock_Tests
has several methods where the tests are defined as follows:I then instantiate
FB_FunctionBlock
as a local variable in each test method like soI then stepped through the code, by setting a break point in one of the tests. Lets say somewhere in
AnotherTest
. I then stepped into thefbFunctionBlock();
in this method. However, here I noticed that sometimes it would not go into the instance belonging to this method, but it would step intoTestNumberOne.fbFunctionBlock
. I could see this on the top of the window, where it shows the complete path of the instance.Furthermore it would show ??? for all variables like so:
In the end I solved it by converting the
VAR
intoVAR_INST
for each test method.Has anyone else experienced issues with instantiating function blocks as local variables in methods?