LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Using Producer Consumer design pattern incorrectly?

My setup is the following:

 

3 Instruments (DMM, LCR, and Electrometer), and a switch Matrix.

 

I am developing code that I think goes against the purpose of the model, but I don't know all of the DOs and DONTs associated with the Producer/Consumer design.

 

I need the code to be able to run in the following modes (single measurement, multiple measurements, continuous) so that the user can select those. Basically the user will provide a sequence of measurements to make that will look like the following:

 

MeasurementSequence.png

 

Then the system will step through this and handle the measurements accordingly. If it is set to Multiple or Continouos it will loop through the sequence until the desired amount or the stop button has been pressed. My main question is whether I should add ANOTHER consumer to handle the timing for when Multiple/Continuos modes are selected or what is the best practice for that?

 

Currently the timing is in the Producer loop, so whenever the tests are being ran, the UI is unavailable because of that, so it sounds stupid but I just can think of a better way. I'll attach the main.vi and others that are necessary, I may miss a few dependencies.

 

Pleading for some direction, thank you in advance. Here is the UI for now.

 

UI.png

 

 

0 Kudos
Message 1 of 20
(4,751 Views)

Well, it certainly makes sense to use P/C to handle the design/specification/parameterization of the Tests.  The Producer would be the Bells and Whistles of your UI to select test, repetitions, parameters, etc, to build the table of tests.  The Consumer receives these button-presses and creates an Array of Tests (and populates the Front Panel display of Tests-in-Planning and -in-Progress).

 

Let's stop and consider, now, that you have such an Array and want to do a sequence of tests based on this Array.  This sounds like a State Machine, to me, perhaps a Queued State Machine.  Suppose I have three tests, Test A, Test B, and Test C.  I've built an Array of Tests in my Consumer, and now I push the "Start" button (in my Producer).  The Consumer sees "Start" and goes to its "Start" case.  What should it do?  Well, it should start to process the Tests held in the Test Array.  The first Test is Test A.  Suppose the consumer put "Do Test A" on the same Queue that the Producer was using to send requests to the Consumer -- the Consumer would execute "Do Test A".  But here's the catch -- you don't want the Consumer to "sit" in this state while waiting for Test A to finish.  So what the Consumer could do is "play Producer", put "Test A" on another Queue that goes to another Consumer (running in parallel -- that's now 3 parallel loops) which sees "Test A", does Test A (and thus stays in this State until A is done, but that doesn't matter, since you can only do one test at a time), and when finished, puts "Test Done" back as the next Command for the middle (Consumer) loop.

 

Three Loops -- Producer (responding to Front Panel switches), Consumer (handling Front Panel and other things), and Tester (a second Consumer just doing a specified Test).  

  • Start -- Producer sends command to Consumer
  • Consumer Start -- gets (first) Test from Test Array, sends self "Do Test".
  • Consumer Do Test -- decides which Test to do, sends command to Tester
  • Tester -- does specified Test, when done sends "Test Done" command back to Consumer
  • Consumer Test Done -- either does next test or, if all done, simply does nothing
  • Stop -- User pushes button and Producer catches it, sends Stop command to Consumer
  • Consumer Stop -- sets Consumer flag to alter Test Done action and not do any more tests

Do you see how simple this is?  If you need to stop a test in progress (instead of waiting until it finishes) when you press Stop, you'll need to get information "into" each test.  One way to do this is through a Functional Global "Stop" Variable set by the Consumer and tested periodically (and frequently) by all of the Tests.

 

Give these ideas a try.  Others will probably suggest even better ways, but this should, at least, "sound familiar" to you.

 

Bob Schor

Message 2 of 20
(4,719 Views)

On an unrelated note, you should think about  changing your color scheme. I re-did software for a turbine-testing facility a while ago. The old software was colored mostly blue like yours and the techs hated it. They said they would go home at night and the blue was still burned in to their retinas.

 

You could fix this effect by maybe just using a dimmer color and softening your darks to a more grey color.

Cheers


--------,       Unofficial Forum Rules and Guidelines                                           ,--------

          '---   >The shortest distance between two nodes is a straight wire>   ---'


Message 3 of 20
(4,708 Views)

Bob,

 

I have come back to your response several times for help with projects I'm working on. I have an additional question that I was hoping to get answered. I am using a Producer Consumer design pattern on a Automation Unit controlling some valves and reading their pressures. I want to read and display the data from the pressure transducers the entire time the program is running (using Compac-DAQ modules), but am not sure if I would create an additional loop just to constantly sample the voltages coming from the transducers, and then when actually performing automation just grabbing data from that loop when needed since i'll only actually need the measurements when the tests are running?

 

Loop #1 - Producer (Handle User Interface Switches - i.e. Run button)

Loop #2 - Consumer ( Handle the stage of the automation as determined by the user, shut valves based on Pressure Transducer readings) State Machine..

Loop #3 - Data Collector (Always pulling data consistently from the Pressure Transducers)

Loop #4 - Update the GUI, Log the Data to TDMS.

 

Any help you can provide would be much appreciated.

 

Thanks,

 

Kellen

0 Kudos
Message 4 of 20
(4,463 Views)

@rkmadse wrote:

 

 

Loop #1 - Producer (Handle User Interface Switches - i.e. Run button)

Loop #2 - Consumer ( Handle the stage of the automation as determined by the user, shut valves based on Pressure Transducer readings) State Machine..

Loop #3 - Data Collector (Always pulling data consistently from the Pressure Transducers)

Loop #4 - Update the GUI, Log the Data to TDMS.

 

 


Hi, Kellen.

 

     It looks like Loop 3 is, as you say, "always pulling data consistently from the Pressure Transducers".  Similarly, Loop 4 "updates the GUI and logs data to TDMS".  Am I correct in thinking that Loop 3 is acting as a Producer, putting data on a Queue and sending it to Loop 4, acting as a Consumer?

 

     If you already have these loops, then I'm not sure I understand the Question -- you don't need any additional loops since you are showing and saving all the data.  It's not clear to me how Loops 3 and 4 interact with (or are controlled by) your State Machine (Loop 2).

 

     Can you attach the VI that includes these four loops?  It should be possible to tell, almost at a glance, what is going on (unless this is one of those VIs that take 10 screens to view, have hundreds of wires going across multiple screens (rarely straight), and have no user-written sub-VIs ... in that case, it will take a bit more work).

 

Bob Schor

0 Kudos
Message 5 of 20
(4,453 Views)

@rkmadse wrote:

Loop #1 - Producer (Handle User Interface Switches - i.e. Run button)


Loop #4 - Update the GUI, Log the Data to TDMS.


In my opinion, the Updating the GUI should be part of Loop #1 (the GUI loop).  Use User Events to send the data to that loop and have it update the screen.  Then Loop #4 just handles logging of the data.  This greatly simplifies the logging loop and opens it up to being reused.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
0 Kudos
Message 6 of 20
(4,438 Views)

If you are using Loop #1 to update the GUI then don't you lose the ability for the user to interact with the gui and move to different tabs etc?

 

-- Kellen

 

PS. I am asking what is the best structure for my code (that isn't written yet). 

 

I want to have 8 pressure transducers I am reading continually (whether in test or not in test). The data will only be logged while testing.

 

I then have 8 air-op solenoid valves that control high pressure valves. 

 

So a simple example would be.. open valve #1 (turns on pump), when Pressure Transducer #1 reaches 15kpsi turn off valve and hold for 2 hours. Then open valve #2 to release pressure. 

 

This is a simple example but I want to basically allow the user to interact with the gui while this testing is going on, and being logged.

 

Thanks. 

0 Kudos
Message 7 of 20
(4,383 Views)

@rkmadse wrote:

If you are using Loop #1 to update the GUI then don't you lose the ability for the user to interact with the gui and move to different tabs etc?


How often are you sending updates to the GUI?  The user is slow.  GUI updates in general should be slow.  Even if you are making constant updates, those event cases should be fast.  So, no, it will not affect the user interaction.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
0 Kudos
Message 8 of 20
(4,380 Views)

I'm avoiding doing the work I should be... so to aid me in this, I did a peer review of your code.

 

To start off with, you have a lot of good practices going on.  Lots of type defined clusters, wire labels, and meaningful subvi icons.

 

For documentation:

  • You are lacking comments in your actual code. I am a big fan of adding the subdiagram label to all of my case structures and a quick note at the top for each case explaining what it does. When you pick your code back up 2 years later you will thank yourself for it 🙂
  • While you have meaningful icons on your sub-vis, going through the effort of adding a description in the vi properties so it shows up in context help will also help a lot. Especially for someone else going through your code. It prevents the need to actually open the VI and see what it is doing.

Functionality:

  • General:
    • You commonly see stopping loops with an error wire, and I have found this to be more problematic than useful. Say for example an error occurs in your state machine test controller loop. That loop just stops executing, but the other 2 continue to execute without you or your operator knowing and just waits for the error.  A better method to do this is have an error user event. If any of your consumer loops throw an error it triggers the user event and you can handle it in your producer loop.  You could then, for example, look at the severity of the error and command the "Exit" state to your consumers. If the error is not critical, you can either prompt the user, or discard the error without either of the other 2 loops stopping or pausing execution.
  • Main producer loop
    • I used to use a state machine combined with event structure like you are doing here.  I no longer do as I found the design pattern completely unnecessary. In addition if my producer state machine is running through multiple different states that take some time to execute you have effectively frozen your GUI. In fact, pressing "E-STOP" in this case won't trigger the event case, which added the need to use a local variable to kick you back to having GUI control
  • State machine test controller
    • This checks for and handles the E-STOP event, but you have sections of the code (one button dialogue) that can freeze the loop and prevent it from executing.
    • Your "E-Stop Check.vi" could potentially miss an ESTOP queue command. If an ESTOP command is enqueued and another one instantly enqueued after it before your consumer loop can check element 0 of the array you would have a problem. It would eventually get caught in your "Wait" state dequeue, but obviously the reason for this VI is for it to interrupt execution.
    • Your "Stop" and "Exit" states seem to be functionally the same.
  • Update GUI loop
    • Good use of variant to send multiple typedef data to this loop. You did it right.
    • "Convert output to array.vi" - try to use build array when possible instead of insert into array. It is much faster.
    • Your "Stop" and "Exit" states seem to be functionally the same.

 

And yes, I know this didn't answer your question at all... 🙂

 

Edit:  One last comment.  I don't see anywhere in your "Update GUI loop" that removes or resets your 2d array... it will grow until you run out of memory.

0 Kudos
Message 9 of 20
(4,366 Views)

Ok, I like the direction you are heading, but I'm still missing a piece I think to fully understand the logic/how-to. Do you know of any examples of using "user-events" to update the gui with data? Or using "user-events" when errors are encountered so that the producer can determine how to handle the error?

 

I'm using a c-DAQ-9178 with a 9205 module to sample the pressure transducers. I believe the sampling rate on those is 250 kS/s.

 

 

0 Kudos
Message 10 of 20
(4,355 Views)