Sunday, February 8, 2009

Things That Have Bitten Me Lately, Part 1

Despite having been an active LabVIEW developer for the past ten years, sometimes I still fall prey to beginner's mistakes. Well, when I realize what the problem is, they feel like beginner's mistakes... sometimes, though, the root causes can be a little bit obscure. Today, we're going to discuss For Loops. More specifically, what can happen when a For Loop does not execute.

OK, so here's the scenario. I wrote a pretty simple subVI intended to write multiple key values to a config file. Nothing more than the Write Key.vi in a For Loop, fed by arrays of key names and values. Worked like a charm. Then I strung a bunch of these together, and noticed that some of my downstream groups of keys weren't being written. I stuck a bunch of probes along the chain of error clusters and noticed the ubiquitous -- and often confusing -- error 1 being produced. Then I similarly probed the file refnum along the chain, and lo and behold, one of the VIs was outputting an invalid refnum. Take a look at the diagram below and see if you can figure out the bug.
Here's what was going on: the offending VI was being fed a value array of length zero. Thanks to autoindexing, the For Loop wasn't executing. As a result, the input (valid) refnum wasn't being passed to the output. The rule of thumb with tunnels on For Loops is that, if they don't execute, the tunnel will take the default output value for the given datatype. With refnums, that means something invalid. Ooops.

The solution is really, really simple: replace the tunnel with a shift register. The shift register acts as a pointer to the input wire's value, so that even if the For Loop doesn't execute, the output wire will still point to the correct value. Problem solved. For the skeptical among you, try the code shown below.
We tend to think to use shift registers when we know that we will be changing a value of something from within a loop. In the case shown above, you'd naturally think, "well, the value of the refnum is static, so a tunnel in and out will be sufficient." It will be fine, as long as the loop runs at least once. So here's the new rule for For Loops: if there's a possibility that they won't execute, make sure that everything that needs to have a valid value gets passed through a shift register. Alternatively, you could just wire the refnum around the loop, but that ends up looking clumsy and ugly, and the style police will jump all over you.

6 comments:

D Natt said...

The way I usually avoid the problem cited in your first screenshot is to wire the refnum in control directly to the refnum out indicator on VIs that pass through a refnum. But if I do have to pass something through a loop, then I always use a shift register to avoid the zero-iteration behavior.

Patrick said...

Nice one!

I've not run into this yet. But if I do, I'll have a solution. Thanks.

Flight34 said...

Good post. Thank you. I haven't been bitten by this one yet, surprisingly because I often use tunnels where I should be using shift registers.
...also useful for error clusters.

Jim said...

I just found this one, today, in some code that I was reviewing.

Anders said...

Thanks for good post!

Aum - Virtual Instruments for appliance testing said...

Hey this is a common for newbies.

Another worth mentioning refinement is to place the 'for loop' inside the 'sub-vi' and pass the arrays. The time to call a 'sub-vi' adds to the execution time when in a loop and can be prevented.
Besides, sometimes you have to pass an array to sub-vi. Then, the LabVIEW execution has to make copy and can bog down performance of your Virtual Instrument.