LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Generators in Labview (lazy programming)

Solved!
Go to solution

Hello,

 

I am new to LabVIEW and have some programming experience in Python.

In Python, I have learned about generators, which are functions that effectively return a list of elements, however the elements are computed and returned one-at-a-time. The use of generators is known as lazy programming (because values are never computed before you need them), which I find extremely useful.

 

I would like to implement this behavior in Labview. For example, I already have a VI that continuously acquires camera images. I want to obtain these images one-at-a-time and analyze them in real-time. This means that first building an array of all camera images is not a solution for me. I also don't want to merge my VI's together because I want my code to remain modular and readable.

 

Is there any way to achieve this in LabVIEW?

 

Thanks,

Kasper

0 Kudos
Message 1 of 13
(373 Views)

Hi Kasper,

 

what about a producer-consumer scheme based on a queue?

Best regards,
GerdW


using LV2016/2019/2020 on Win8.1/10+cRIO
0 Kudos
Message 2 of 13
(366 Views)

"I want to obtain these images one-at-a-time and analyze them in real-time"

 

I would probably use channel wires. No need for extra wires in a producer/consumer using a queue. It's all built-in. Start here.

Certified LabVIEW Architect
Message 3 of 13
(354 Views)

Thank you so much, a stream channel seems what i am looking for. Could you explain how I can make this for two separate VIs? The link you showed has everything in one vi

0 Kudos
Message 4 of 13
(348 Views)

Isn't that called Lazy Evaluation?

 

Anyway, you can separate the producer and consumer(s) with either (user) events or queues.

 

This will start to resemble a producer\consumer pattern.

0 Kudos
Message 5 of 13
(344 Views)

@KasperWUR wrote:

Thank you so much, a stream channel seems what i am looking for. Could you explain how I can make this for two separate VIs? The link you showed has everything in one vi


Select the code you want in a sub VI, and then select Edit>Create SubVI.

 

This will show the way, but usually we do this manually. The automatically generated VIs usually need tweaking.

0 Kudos
Message 6 of 13
(341 Views)

Edit: duplicate, already answered

Certified LabVIEW Architect
0 Kudos
Message 7 of 13
(338 Views)
Solution
Accepted by KasperWUR

According to Kasper's Post, a Python Generator is not like a Producer/Consumer Design Pattern, but is even simpler to construct with LabVIEW.  Kasper's description of a Generator agrees with what I just learned by doing a Web search for Python Generator.  [I must confess I don't see why this is so "exciting" -- it is trivial, in LabVIEW].

 

[I just realized, when starting to write this paragraph, I was about to use the term "produce", so just for fun, I'm going to use the terms "Producer" and "Consumer", but those who know the Producer/Consumer Design pattern will see that what I am describing works backwards, the Consumer causing the Producer to do work ...]

 

You have a VI that want to process (or "Consume") some data.  When it is ready to do so, it calls another routine to "Produce" the next set of data for it to Consume.  Suppose I want to, say, graph a Factorial sequence (0! = 1, 1! = 1, 2! = 2, 3! = 6, ... n! = (n-1)! * n ...).  You write Produce as a subVI consisting of a While Loop with True wired to the Stop condition (so it runs exactly once).  Inside you put a Case Statement with "First Call?" wired to the Conditional terminal.  Create two outputs for this sub-VI -- call the first "N" (and make it an Integer type) and call the other "Factorial N" (and make it a U64 to get the most "digits before it overflows").  Also add two Shift Registers to the While Loop.

 

In the True Case (First Call), return 0 to N using one Shift Register, and 1 to Factorial N using the other.  In the False case (all subsequent calls), bring wires from the two Shift Registers into the False Case, increment the wire going to the N output and connect it to the N output Shift Register.  Take the wire coming from the Factorial N Shift Register (which is, therefore, (n-1)!) and multiply it by the now-incremented N (making (n-1)! * N) and wire it to the Factorial N output Shift Register.

 

All done.  This VI will be a Factorial Generator.  The first time you call it, it will give you 0 (for n) and 1 (for Factorial N).  Subsequent calls will generate subsequent Factorials.

 

Note -- Kasper is new to LabVIEW, so I'm choosing to not show a Block Diagram, but (I hope) to show "how to think through a Problem in the LabVIEW way and find a Good Solution".  The only "non-beginner" thing here is the "First Call?" function.  This can be found by opening the Synchronization Palette on the Block Diagram.  It is True the first time it is called, and False thereafter.

 

Bob Schor

0 Kudos
Message 8 of 13
(319 Views)
Solution
Accepted by KasperWUR

Thanks to everyone who replied, especially to Bob for his elaborate answer. Bobs answer best resembles what a Python generator does, and the producer/consumer pattern seems best practice for my application of acquiring and processing camera images.

 

I tried Bob's example and added a snippet below:

KasperWUR_0-1638177342648.png

(Factorial generator, true case contains just constants 0 and 1 (not shown).)

 

KasperWUR_1-1638177407899.png

(Main loop retrieves the next factorial upon a button press, ready to be used for further analysis)

 

Thanks a lot for teaching me this design pattern, I will be sure to use it in the future.

 

Kasper

 

0 Kudos
Message 9 of 13
(249 Views)

Thank you for translating my "words" into LabVIEW code that created a "Factorial Generator" for you.

 

Here's a "free" improvement for you to consider, one that can also teach you something about the LabVIEW Event Structure.

 

From the standpoint of the User Interface, there are two Buttons (OK and Stop) that "do things", and two Indicators (N and N!) that show the results of "what is done".

 

Most of the time, nothing of interest is being done.  Think about the following situations:

  1. Start the Program and don't push any button.  What happens?  [Think about what Loops run, and what just "sit there and wait"].
  2. Start the Program and push only the Stop button.  What happens?  What would you like to happen?
  3. Start the Program, push OK once, and then push Stop.  What happens?
  4. What series of Button Pushes will let you exit the program showing that 1! = 1?

There's a very interesting Structure in LabVIEW called the Event Structure (on the Block Diagram "Structure" Palette).  This is almost always placed inside a While Loop and the combination called an "Event Loop".  A very important (and very common) use for an Event Loop is to "Do something (once) when a Control on the Front Panel is changed by the User".

 

Do you know about Event Loops?  If not, there are tutorial (see first page of this Forum, or do a Web search for "LabVIEW Event Structure".

 

Your code can be re-written using a single Event Loop with two Event Cases -- a Value-Changed Event for OK, and a Value-Changed Event for Stop.

 

Some things to notice with this "re-formulated" routine:

  • The "anomalous" behavior in Step 1 (above) disappears.
  • Suppose you had a third Loop that is very busy doing some fancy computation and needs as much of the CPU as possible.  How much CPU time do you think your original While Loop solution requires?  Suppose you replaced your While Loop with an Event Loop -- what fraction (to the nearest 0.01% of the CPU time do you think will be available for your fancy computation?  [I hope you are as impressed by this as I was when I first learned this ...].
  • Run through the four Steps I suggested above.  Note the similarities and differences in the behavior.
  • Look at your While Loop method and compare it with the Event Loop.  Which seems "easier to explain to the naive viewer"?  Which do you think is more likely to "work the first time"?

Bob Schor

Message 10 of 13
(192 Views)