LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Question about QSM complexity with several parallel tasks

I'm building an application that uses a few motor stages and a camera and will be doing analysis when the moving object is in certain positions. I've never had much luck with real architectures but I'm trying to build this based off of a queued state machine, and I've followed some templates. My original thought was I'd have one event handling loop to handle front panel interactions, the state machine loop which reads states from the queue and "does everything" that gets done, and a third parallel loop that is constantly reading from the camera to update an image display on the front panel as well as the indicators for positions of the motors. 

 

I'm confused about the complexity necessary to use this framework, how many loops and how many queues I should have, etc. I opened the example program called "Continuous Acquisition and Logging Example" and was immediately overwhelmed with the need for 5 different queues and message handlers for what seems like a relatively simple use case. I'd like to keep all of my non-constant actions handled by the state loop (processing and movement happen in a sequence of events after a user input or input from a script, whereas the camera and one sensor's readout should be updating constantly). The example for those not familiar looks like this:

image.png

 

I'll attach a snippet of what I have so far below. So far, I haven't integrated the VIs for image acquisition but I think my application  QMH Example.png

 

I'm not sure how much you can actually access through a VI snippet, so I'll elaborate some parts. "Context" is a front panel control containing all of the information about camera and stage sessions and metadata necessary to use them. It also contains two flags at the moment "Hardware Initialized" and "System Stopped", because I needed a way of letting the acquisition loop know that the stages are connected/disconnected. I was originally going to have everything that happens in "Initialize" happen in a sequence structure that gets called before the queue is even created, but that seemed "dirty"? I don't know, it felt like I'm breaking the format too much. But it would solve the issue of needing flags in this context data, and I hate the idea of using a bunch of queues like in the official example. 

 

If I have to boil this all down to one question (and I'm really looking more for guidance than a specific answer, but I know some people like specific questions to answer), how can I synchronize images from the camera, info about whether or not the hardware is initialized and connected, hardware states, and STOP conditions (this is pretty important to have in sync) etc. between multiple loops? Reading from front panel property nodes? Should I have every frame from this camera put into its own queue data? 

0 Kudos
Message 1 of 19
(2,275 Views)

Hi Nokaroa,

 

This sounds like an interesting application.  Let's see if I understand your setup correctly:

 

Hardware:

- Motor stage

- Camera

- Sensor

 

How does your camera/sensor interact with your motor stage?  Are they inherently parallel processes?  Or do they have some sort of handshaking?  I think of the UI messaging loop as a central dispatcher.  This the Uber server, telling Uber drivers what to do and where to go.  Its purpose is to get commands from the front panel, interpret the commands, and enqueue commands to the other slave loops.  However, slave loops can issue commands to one another as well.  I think you have a good start.   To answer your question:

 

how can I synchronize images from the camera, info about whether or not the hardware is initialized and connected, hardware states, and STOP conditions (this is pretty important to have in sync) etc. between multiple loops?

 

If you look at the example, you'll see that there's actually a queue for each loop.  This allows you to send commands from one slave loop to another, based on certain conditions.  For example, if you wanted the cameras to stop once the motor stages stop moving, you can add code after the motion sequence, to enqueue a "stop" command to the camera loop.

 

If you can write out your program flow using psuedocode, I (or we, meaning folks on the forum) can give you ideas on how to translate the flow to LabVIEW.  Cheers.

 

 

 

 

0 Kudos
Message 2 of 19
(2,250 Views)
Hello,

 

The sensors are encoders for the stages, so constant parallel readout. 

The whole thing will move the camera/sample to a specific position and then the camera will capture after movement is complete. In the "state loop" (I'll rename it when I need to), the VI that sends the move command will always wait until that movement is complete to return, so no real concerns there. I currently intend to send a "Capture and process" message in the state loop in the case that handles the movement, so the capture and process always happens after movement.

 

If I make a "camera loop", I imagine I'd need to be putting a new message in the queue containing the image for every frame captured in order to display. If I was just using a sensor, that wouldn't be an issue, but this feels like a memory hog if I enqueue each image. 

The way I currently have it set up (meaning in the two hours since I wrote the question), I have an IMAQ image reference being passed into both the state machine loop and the data loop, and if I need to read the position of the encoders in the state machine loop, I can either use the same VI to read them that I use in the data loop, or I can read the front panel indicator values. I haven't implemented any code that explicitly needs to do this yet, but I imagine it will work.

 

I think I've basically got my problem solved in a dirty way, where I'm passing the context to each loop, ignoring errors if I try to read data from a closed visa session, and using the global(ish) "System stopped" flag in the context object to tell the acquisition loop when to shut down, but I'd still love to understand the cleaner way. It seems like it's just "more queues, more loops" though. I've got a few more hardware things that I'm going to throw on that don't need any sort of feedback, so I was just going to put them in the event structure because they don't really justify messages, but if every hardware type gets its own queue, it will get busy on this block diagram, which is where my aversion comes from.

 

So here's not the pseduocode, but the flow instead:

in one thread:

User/script inputs position -> camera/UUT moves to appropriate position ->  wait for movement to stop -> Capture/process/report

 

in the other thread:

encoder values and camera image on the front panel is constantly updated so the operator can see what's going on 

 

 

Thank you for your help

 

 

 

0 Kudos
Message 3 of 19
(2,239 Views)

The QSM and QMH design patterns are IMHO useless... They always seemed far too complicated, too tightly coupled, and not documented well. I tried to make them work for my larger projects and abandoned them several times over the years.

 

Recently I have become a fan of the Channel Message Handler (CMH) and have reduced the design template into something useful by basically starting from scratch.

 

It sounds like a CMH would work nicely for you too Here's a thread I replied to: Practical use of channel writers? check out my post and CMH description.

 

You may find the CMH easier to understand and program.

 

 

========================
=== Engineer Ambiguously ===
========================
0 Kudos
Message 4 of 19
(2,225 Views)

@RTSLVU wrote:

The QSM and QMH design patterns are IMHO useless... They always seemed far too complicated, too tightly coupled, and not documented well. I tried to make them work for my larger projects and abandoned them several times over the years.

 

 


This is my whole experience with LabView architecture in a nutshell. The simple stuff seems messy so I check out the design patterns that NI really seems to love pushing and I feel like the only one confused by how complicated and how tightly coupled the framework is. I went down an Actor Framework rabbit hole last year and that ended up feeling like a waste of time. I used to be pretty good at Java back in the day so I thought the OOPiness and boilerplate wouldn't bother me as much as it did.

 

I think I'm figuring out the QSM thing for this project, so I probably will continue with just more queues for now, but I'll definitely check out your suggestion. 

0 Kudos
Message 5 of 19
(2,219 Views)

Because you are using a property node to read the value of a control, your loops will never truly run in parallel. That property node requires a switch to the UI thread which means each loop will switch to that thread when reading the value. In this case a local variable would be better than a property node. Even better, would be to rid yourself of either a property node or local variable; for example, send the current value of the control in a message.

 

mcduff

0 Kudos
Message 6 of 19
(2,206 Views)

McDuff,

 

I've been stewing on this. I've already switched the property nodes to local variables. I wasn't aware that there was really a difference but I guess it makes sense.

 

Now sending the current value of the control in a message is where I'm confused. I've got two loops that need access to the context: The main consumer loop and the loop that acquires and displays to the user every frame/tick/value. Since the context contains states such as "The hardware has been initialized" and "the application has stopped", every single iteration of the bottom loop needs that info. Additionally, the main loop needs that for certain commands to do processing and analysis. So if I send it in a message, I'm either having a new queue where every message is identical, or I'm making a queue which has duplicate data from a separate one in order to keep the front element from getting popped by multiple loops. 

 

 

0 Kudos
Message 7 of 19
(2,171 Views)

NI templates are the last thing I would recommend anybody use.  Partly this us because they have too many loops; they split things that naturally go together among more than one loop, immediately causing one the headache of how to share info between the two-loops-that-should-be-one-loop.  

Message 8 of 19
(2,155 Views)

You have two loops that need the context, yet both depend on the same hardware I believe. In one loop, you set the hardware, Initialize, Stop, Move, etc. In the other loop you read the position of the hardware.

 

In this case I agree with @drjdpowell. All of the queries, settings for the hardware should be in a single loop. In your middle loop, if you go into the Idle state, you can read the position of the hardware, or do something similar to that. Right now, you have a dependency between loops that depends on when the property nodes/local variables are read/written.

 

If you can, I would refactor into a single loop. In my use cases, I put all of the hardware in a single loop. I read the data in that same loop. Now if I need to do anything with that data, like process it, then I send that data to a different loop. So the hardware acquisition and data processing take place in separate loops.

 

Putting all of the hardware in a single loop has other advantages also. If you have an error, you can reset the hardware in the same loop and have no worries about a separate loop trying to access that hardware while it is in a non-functioning state.

 

mcduff

0 Kudos
Message 9 of 19
(2,148 Views)

Hi Nokaroa,

 

How about you holding queue references in a FGV (Functional global variable)? At minimum this will eliminate excessive wires.

0 Kudos
Message 10 of 19
(2,145 Views)