From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.
We appreciate your patience as we improve our online experience.
From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.
We appreciate your patience as we improve our online experience.
09-10-2013 03:03 PM
Matt,
I am going to confuse you even more! You might be better off with three loops. One loop would handle all the User events - when the user presses a button or enters a value on the front panel. This is a Producer (Events) loop. Another loop might be the one which communicates with the Arduino. This is a Producer (Data) loop. The third loop is the Consumer and it gets commands from the User Interface loop and it gets data from the Arduino loop. It updates displays and saves to files.
The basic idea of the Producer/Consumer concept is to separate functions which might need to operate at different speeds. The user interface needs to "feel" responsive to users - which typically means that it should respond within about 100 ms to user actions. With an even structure and a queue being the only code inside the loop, the response is very fast with almost no CPU overhead. Suppose the Arduino needs to have a response within 10 ms. Having a loop which only communicates with the Arduino and passes the data to the consumer via a (different) queue means that it can run much faster than the other loops. Meanwhile the save to file loop can run once every few seconds. If the OS decides to defragment or reallocate the files and takes hundreds of milliseconds to do it, the other loops continue to run at their usual rates. The queues act as buffers so data is not lost.
Lynn
09-10-2013 03:22 PM
@johnsold wrote:
Matt,
I am going to confuse you even more! You might be better off with three loops. One loop would handle all the User events - when the user presses a button or enters a value on the front panel. This is a Producer (Events) loop. Another loop might be the one which communicates with the Arduino. This is a Producer (Data) loop. The third loop is the Consumer and it gets commands from the User Interface loop and it gets data from the Arduino loop. It updates displays and saves to files.
The basic idea of the Producer/Consumer concept is to separate functions which might need to operate at different speeds. The user interface needs to "feel" responsive to users - which typically means that it should respond within about 100 ms to user actions. With an even structure and a queue being the only code inside the loop, the response is very fast with almost no CPU overhead. Suppose the Arduino needs to have a response within 10 ms. Having a loop which only communicates with the Arduino and passes the data to the consumer via a (different) queue means that it can run much faster than the other loops. Meanwhile the save to file loop can run once every few seconds. If the OS decides to defragment or reallocate the files and takes hundreds of milliseconds to do it, the other loops continue to run at their usual rates. The queues act as buffers so data is not lost.
Lynn
My wife expects a quicker response than that, and as a result, my queue is always full. 😉
It takes a little while to sort out the three loop thingy, but believe me, it's such a pleasure to drive once you've implemented it. Sometimes I will even implement a fourth loop to handle errors. 😉
09-10-2013 03:56 PM - edited 09-10-2013 04:02 PM
I ran into the problem a while back with my code when laoding stuff. My solution was a 3 panel flat sequence structure. 1st panel disabled and grayed out the front panel, 2nd panel loaded and moved everything to that location, and 3rd panel reactivated everything. It was pretty, but it worked (That is the solgan of my code right now... I guess it is time for upgrades).
Hopefully there is sample P-C code out there that I can look at and play with to understand it better.
On a side note, I started using clusters and my code looks a lot nicers. Woo hindsight 😄
Matt
EDIT:
I did not realize there was a 2nd page in the thread and did not read those posts as of my original post.
The Arduino is being controlled over Labview in a massive while loop (making sure it's connected) with a case structure (choosing normal, save, or load). When a front panel control is moved, LV sends a command to the Arduino, where it updated its value and sends an "all good" back to Labview. This seems instananous, even though I know its not, but its faster than I can detect. That code was written by Sammy_K (LIFA creator), except I changed it from serial to ethernet. I guess I should have stated that ealier... This case structure thing is inside a giant while loop that can only be exited by clicking a big red "STOP" button or aborting execution
09-10-2013 07:59 PM
Bill,
The Dequeue on the wife queue does not reduce the number of items in the queue. The timing is irrelevant.
Lynn
09-11-2013 04:53 AM
@Uke88 wrote:
I got to reading about the Consumer/Producer loops from here. I don't think that's what I'm looking for. From my understanding, Consumer/Producer loops are broken down into 2 separate loops - Consumer and Producer. The Producer creates a queue and the consumer takes the queue and processes it. The queue works on shift registers. To me, that means that each loop something has to be passed into the queue or the consumer won't know what to do.
For my code, every loop LabVIEW sets/gets 5 data points to an Arduino. 2 servo pulse widths and 3 pot voltage values. When a save/load button is pressed, LabVIEW takes that data and stores/loads it from a save file. I think event structures would be better for me here. The way I'm looking at things, once a Boolean changes, save/load. Otherwise, keep chugging away at the data.
Matt
You're not wrong, you can implement it as a event structure (just add some user events), but as already mentioned, if some actions take >100ms it'll start being sluggish, that's the beuty of moving the actions to the consumer loop.
In your case, the situations you describe are excellent commands to send to the consumer, and "otherwise" is a timeout-event of the queue. (or to make it more correct, in case of timeout, generate a "Chug data"-command)
For the UI, gather all of those Save-button as a Save-cluster, and use a value change-event on the cluster (or use a Ring/Enum-selector and 1 save-button)
/Y
09-11-2013 10:15 AM
Question. So consumer consumes data, producer produces data. The C-P loop sends data to each other's loop. So if that's the case, I can send the values from the consumer loop (get/set Arduino) to the producer loop to save them when a save button is pressed or I can load the data from the save file and send the data and a flag to the consumer. All of this being done in the producer loop. Then in the consumer loop, I can have a case structure controlled by a flag. True would be the normal get/set while false would be a load.
I'm now thinking of the C-P design as two separate loops where the producer takes care of the LV stuff and consumer takes care of the arduino stuff. Am I on the right track or waaaay off?
Matt
09-11-2013 03:07 PM - edited 09-11-2013 03:08 PM
I'd say information is one way, only to consumer, although there are different designs. It can however be produced from both.
Wait, what?
Well, it's to avoid interdependancies between loops and other strange solutions i've found. I'll explain:
The consumer loop holds all needed information in a shift registered main cluster, the program ofc starts by queing an init-command.
" I can send the values from the consumer loop (get/set Arduino) to the producer loop to save them when a save button is pressed" - No, when you press the save button you generate a save-event, which in the consumer uses the data running in the consumer loop and saves it.
"I can load the data from the save file and send the data and a flag to the consumer. All of this being done in the producer loop" - Could be done that way, but easier is to generate a load-command which in the consumer loads the file and stores in the main cluster. 🙂
"Then in the consumer loop, I can have a case structure controlled by a flag. True would be the normal get/set while false would be a load. " - Not needed as it's different commands executed.
"I'm now thinking of the C-P design as two separate loops where the producer takes care of the LV stuff and consumer takes care of the arduino stuff. Am I on the right track or waaaay off?" - Yes and no, one loop handles buttons, one loop handles all else. If "all else" takes too long (for example repeated slow serial communication) it can actually go into a 3rd loop, requiring a 2nd queue, or user events.
/Y
09-11-2013 04:30 PM
Darn, thought I had it that time >.<
I don't get how to have the producer send a different command to the consumer. I'm looking at a P-C event design. The Producer waits for a button to be pressed on the front panel, say the save button. That sends a queue command to the consumer. The consumer gets the values and saves them. Then, nothing happens on the front panel for a while and the whole time the consumer loop is still updating the Arduino. A load button is pressed and the producer sends a queue to the consumer to laod the data.
The only way I can think of these things to happen is have 3 flags being sent through the queue. One for nothing, save, and load. And a case structure in the consumer to do the proper function. There would have to be a time out for the "nothing" flag. I don't get how the queue can send different commands to the consumer without some sort of value being passed to the consumer loop which tells the consumer what to do.
Matt
09-11-2013 07:42 PM
Matt,
Now you are getting the idea. What you are calling "flags" is what I meant by "commands." Typically I use a typedefed enum for the commands. Sometimes it is necessary to also send parameters. For example your 10 Preset Save booleans might be replaced by a Save command and a numeric (1..10) representing which button was pressed.
If the Consumer is implemented as a state machine (which I do about 99% of the time), one of the states is usually an Idle state.
Lynn
09-12-2013 09:14 AM
Lynn,
By flags, I mean something like a case structure (haven't looked into state machines, I'll get on that), where I was going to have an event structure in the producer with 3 events, 0 for nothing, 1 for save, and 2 for load. The consumer would have a case structure where it would have 3 case, 0 for normal action, 1 for save action, and 2 for load action. For the save and load function, it would send the 1 or 2 (save or load) along with a 1-10 to determine which save/load was pressed. When a time out happened, I would just send a 0. However, all of that would depend on the 0 being sent fast enough so nothing seemed like it is taking too long (1ms?). I know that would load the queue pretty bad so when a save/load was pressed, I'd restart the queue and then send the load/save command.
Like I said, haven't looked into state machines, so I'll get on that.
Matt