04-04-2022 07:16 PM
I would like to use a queue to manage multiple tasks that must be executed at periodic intervals. The number of events and the time between them will not be defined until runtime. These are not "front panel" events or "user events" but background execution events like "read temperature" or "measure voltage" The most fundamental implementation might use a cluster with a long integer to store the execution time and a string to indicate the name of the event to be raised at that time. References to each event could be added to the queue in any number and in any order. Instead of handling the queue in the standard FIFO order, the queue will be sorted by execution time. The bottom loop with monitor the queue and raise the events as the test runs. The loop previews the front-most element in the queue until the current time is equal to or greater than the execution time of the event. Then the cluster would be dequeue and raise the event before being discarded.
I'm confident I can assemble and sort the queue but I'm having trouble with the handling loop below. I can't seem to convert the cluster references back to cluster objects in order to access the numeric and string contents. I tried using the functions below but with no success.
Also, I'm unclear exactly how each event gets raised or triggerd when its execution time has arrived. I realize the code I've attached is not functional but I wanted to keep it simple and I'm only asking questions about the bottom loop.
The queue I'm describing should be distinct from the internal event queue LabVIEW uses to respond to Front Panel input and updating indicators. I've also added an image of my block diagram and the vi file. I'd appreciate ideas and suggestions to move this forward. Thanks.
Here is my block diagram
Solved! Go to Solution.
04-05-2022 12:36 AM
You should generate the User Event at the time of Current time exceeding the Cluster time (i.e. inside the bottom case diagram), and instead of putting the Event Refnum into the queue, just put the cluster itself. I think then you will find it much more straightforward.
04-05-2022 03:20 AM - edited 04-05-2022 03:27 AM
You've created a queue of user event references.
You want a queue of data. Or a user event.
If you want a queue of data why is the user event there at all?
Choose between a user event or a queue.
Don't put a user event reference in the queue! You're enqueueing the same reference. The user event that you generate is never read anywhere. You need an event structure to register for that event, and only the event in the top loop does that.
You still have other problems...
A Preview Queue Element will only preview the first element. What if the 2nd element needs to be executed earlier? Then again, what if the 1st element is executed and a 2nd comes in with an earlier time?
You should probably flush the queue, and collect all elements and tread them as a collection.
04-05-2022 09:44 AM
Thanks for your reply. I can see that I should have enqueued the cluster itself, not the reference. In terms of your other comment, perhaps you missed that I am planning to sort the queue by time stamp rather than using it as a standard FIFO. That way I can be confident that the first item in the queue is the next to be executed. Maybe a sorted collection as you suggested would work better. I'll look into that.
04-05-2022 10:09 AM - edited 04-05-2022 10:09 AM
Thanks for your reply. Yeah, initially I tried using a queue of clusters but I believe the "Generate User Event" function returns a reference to a cluster which a queue of type "cluster" will not accept (see the image below). After a fresh look at it this morning, it seems like the "Generate User Event" function is intended for capturing "user events" as the name implies. But I'm trying to work with custom events (background data collection tasks) so I think it's the wrong function for me. I guess I'm unclear on how to create and raise a custom (non-user) event. Can an event be raised somehow using the name (text or string) of the event?
04-05-2022 11:34 AM - edited 04-05-2022 11:41 AM
@skinnedknuckles wrote:
Thanks for your reply. Yeah, initially I tried using a queue of clusters but I believe the "Generate User Event" function returns a reference to a cluster which a queue of type "cluster" will not accept (see the image below).
The Generate User Event returns it's input... The input is a user event reference. The type of the user event reference is user event reference to a user event that has a cluster as user data. You can certainty enqueue that, but you need a queue with a "user event reference to a user event that has a cluster as user data" as it's queue type. Not sure why do would want that (I can think of a few reasons, but that's not your scenario).
Queues and user events can both serve your goal. Pick one. User events or queues. You don't need to mix them.
Queues are used many to one. Many writers, one reader. You can dequeue in more then one place, but the other places won't get the elements. Each element is dequeued once.
User events are many to many. Many writers, many readers. Each registered event structure gets it's own stack of events, and each generate user event gets added to each registered event structure. (Formally, events get stacked even without an event structure, it's the registration that makes them stack. Don't register and forget to use an event structure).
There's also an example: "User Event Generation.vi"...
@skinnedknuckles wrote:
After a fresh look at it this morning, it seems like the "Generate User Event" function is intended for capturing "user events" as the name implies. But I'm trying to work with custom events (background data collection tasks) so I think it's the wrong function for me.
There's no such think as a "custom (non-user) event".
There are user events and events. The user events are custom, the non-user events are build in.
@skinnedknuckles wrote:
I guess I'm unclear on how to create and raise a custom (non-user) event. Can an event be raised somehow using the name (text or string) of the event?
No, you need the user event's reference to generate a user event. You are already doing that! Passing the user event reference to a queue makes no sense at all. You generate the user event, and event structures registered for it will catch that event.
You can't generate user events by a name string, but the data you send can be a string.
Also (at the risk of making things more confusing), you could make a map with strings and user events, and get a specific user event by it's name and use it to generate an event.
04-05-2022 12:08 PM
Hey Wiebe,
Thanks for elaborating. This is very helpful. I hear you saying that queues and events are redundant and I should pick one or the other. Maybe I should back up and ask more fundamental questions.
What if I have 4 different background data collection tasks or actions that I need to execute every 1, 5, 15 and 60 seconds repectively for 4 days. What data structure would you use to represent these tasks or actions and how would you organize them? What kind of container would you use to store them that would allow you to sort them and execute each one at it's designated time? I thought a queue of clusters would serve this purpose and that's why I picked it. Are there alternative data structures and containers you'd prefer to manage a large collection of tasks or actions? I could just make some while loops with delay timers in them but I have been assuming there is a more comprehensive way to do this that could handle more complicated scenarios such as non-periodic execution of the tasks or actions.
Secondly, I found the description below on Events in LabVIEW on the NI website:
LabVIEW generates events from the following sources:
|
Note LabVIEW also supports ActiveX and .NET events, which are external I/O events that you can generate programmatically. |
I guess when I said "custom (non-user) event" I meant the third source in this list, that is, events generated in the block diagram. I'll try to find more information on this topic.
Thanks again,
skinnedknuckles
04-05-2022 02:27 PM
I would not have a master tasks queueing events to the lower level tasks. I would have each task control it's own timing. Each would be it's own state machine and at startup you can configure the desired frequency it should collect it's data at. By trying to have a master task handle all of the timing will make things very complicated and a pain to maintain. Treat each data collection task as it's own self contained entity. The master task simply starts the data collection tasks up. Perhaps it can start/stop them during execution or modify the collection frequency but these would be discrete commands given to the task.
04-05-2022 05:21 PM
I think you have a small misunderstanding of how user events work. User events are somewhat poorly named. They sort of imply "events caused by the user", which is incorrect. They are actually "events defined by the programmer".
When you generate a user event with Generate User Event, event structures that are registered for that user event fire right away. You do not pre-generate user events like you seem to be trying to do. If you want to use a queue to store the things you need to do, store them as clusters, then generate them when you need to use them, not all ahead of time.
Alternatively, unless you need parallel processing you don't really need to use user events at all. Just use your queue, then when you need to process that element just process that element and "do the thing". User events have an internal queue and are handled in the order in which they're generated with no delay between them (assuming your Event Handler loop is in fact in a loop).
In your event queue, you register for the event using the Dynamic Event Registration terminals, but you need to add a case to actually do something with that event. Right click on it and select "New Event" then pick your dynamic event to handle it. Again though, you probably don't need user events at all. Just iterate through your queue and, when the timing is correct, dequeue the element and use the EventName on a case structure with the correct subVI(s) inside it.
04-06-2022 04:05 AM - edited 04-06-2022 04:08 AM
@skinnedknuckles wrote:
Hey Wiebe,
Thanks for elaborating. This is very helpful. I hear you saying that queues and events are redundant and I should pick one or the other. Maybe I should back up and ask more fundamental questions.
What if I have 4 different background data collection tasks or actions that I need to execute every 1, 5, 15 and 60 seconds repectively for 4 days. What data structure would you use to represent these tasks or actions and how would you organize them? What kind of container would you use to store them that would allow you to sort them and execute each one at it's designated time? I thought a queue of clusters would serve this purpose and that's why I picked it. Are there alternative data structures and containers you'd prefer to manage a large collection of tasks or actions?
A cluster in an array does that.
You don't really need a queue at all. If all actions are predefined, you can simply make a VI that returns an array of actions (class or cluster, enum, string, whatever). You can then loop through that array.
If you have processes that create the actions "on the fly", then you could indeed use a queue of clusters.
Or a user event with the same cluster. That is what the image I send earlier does.
In your original post you did create a queue of clusters. That could work. You then tried to add a user event registration to that queue. That didn't work. You need a queue of user event registrations to be able to enqueue a user event registration. You can enqueue a cluster in a queue of clusters.