DQMH “Actors”: Self-Messaging or Helper Loops?

Sometimes (actually more often than not), we want our modules to be more than passive libraries. We want them to do more than merely sit there, waiting for work to be thrown at them. Modules shall actively poll for information, acquire data from hardware, manage network connections, and more. We want DQMH “Actors”!

DQMH Actor: Self-Messaging or Helper Loop?
DQMH Actor: Self-Messaging or Helper Loop?

Caveat: When I say actor, I am talking about the Actor Model. I really mean a DQMH module that executes code by itself, without its’ request events being called. This blog post is not about the Actor Framework.

How can we turn a DQMH module into a DQMH Actor? What could that bringing-to-life of a DQMH module actually look like? Any queued message handler already comes with everything we need to bring it to life, you might say. Still, following the easiest and most straightforward way might not always take us where we wanted to get in the first place. Let’s take a look at our possibilities.

Self-Messaging

The aforementioned, easy way is to have a message case inside the Message Handling Loop (MHL) call itself over and over again, as long as a given condition is met. Take a look at the following screenshot: The Loop Case calls itself, by putting a corresponding message into the message queue, as long as condition == true.

DQMH: Message handling case calling itself
DQMH: Message handling case calling itself

Update 2023-11-24

Here at HSE, we definitely do not allow this design for repetitive, periodic actions. We will always go with a helper loop instead.

The downsides to the self-messaging approach (non-exhaustive) are:

  1. No parallelism with/to the MHL
  2. You may get messages out of order if the MHL has to cater to other requests or messages
  3. There’s a risk of flooding the queue with messages
  4. Other cases of the MHL might starve
  5. It’s more fragile if you need to start and stop the repetitive action (you might be tempted to use priority messages for the stopping, but that just leads to the need for flushing and is a slippery slope)
  6. Timing and timeouts are tricky to handle correctly

Helper Loops

To avoid clogging the message queue and slowing down the MHL, and to gain additional flexibility, we can move the action into a third, separate loop: The Helper Loop. Pete Horn was one of the first people to standardize helper loops and talk about them on the NI Forums.

Here’s an excerpt taken from the DQMH best practices website, explaining this very technique:

When coding a repetitive operation, use a helper loop that is registered to the stop Module event and does the repetitive operation inside the timeout case.

In some cases, a helper loop is designed to be a private resource of the module which should not be accessible to the outside world. If so, you can use Private Requests to limit communication to the helper loop to VIs that are members of the DQMH module library.

Update 2023-06-15

Darren Nattinger was kind enough to sum up what it means to create a Private Request:

  1. Create a new DQMH request just like you normally would. This will be your private request.
  2. Delete the EHL and MHL frames that were automatically created for the new private request.
  3. For your helper loop, unbundle and register for the new private request’s event just like you already are for the Stop Module event.
  4. Call the private request VI from your module code (usually somewhere in the MHL).
  5. Move the new request VI in the project from the ‘Public API’ folder (where it was automatically generated) to the private ‘Requests’ folder.

Helper loops are usually made up of a while loop containing an event structure. The event structure registers for the Stop Module event of the DQMH module (see the screenshot at the top of this post).

Update 2023-11-01: DQMH 7.0

With the advent of DQMH 7.0, you don’t have to do all the tedious manual work of creating helper loops and private/local request events anymore. DQMH now supports the scripting (= programmatic and automatic creation) of these directly from the Tools menu! It is still helpful to have an understanding of the inner workings and implications, so please read on!

We’ve been using helper loops of two different flavours: Either triggering from within – an event structure timeout case helps with that – or triggered through dynamically registered broadcast events from other modules. Read on to find out more about these two concepts.

Helper loops with timeout event case

The timeout event of an event structure can be used to execute code repetitively. Timings can be controlled by setting the timeout value of the event structure, or by implementing your own timing code within the timeout case of the event structure.

See the update at the bottom of this post for things to look out for when using the timeout event.

DQMH: Helper loop with an event structure and a timeout case
DQMH: Helper loop with an event structure and a timeout case

The beauty of this solution lies in the ease with which the helper loop can be enabled or disabled. It’s a question of wiring the timeout value of the event structure to a shift register. Then, you can simply modify the value by means of DMQH requests.

DQMH: Disabling the timeout case of the helper loop
DQMH: Disabling the timeout case of the helper loop

Setting the timeout value to -1 means that the event structure will never timeout. Your DQMH actor is effectively put to sleep and disabled.

DQMH: Enabling the timeout case of the helper loop
DQMH: Enabling the timeout case of the helper loop

Any other value sets your DQMH actor to be wide awake, waiting for that next timeout to occur!

A timeout value of 0 makes the event structure timeout immediately. You can (and have to) control the timing of the loop manually. Values larger than 0 will make the event structure timeout after this time. If you need precise timing, take into account how long your source code inside the timeout case takes to execute. Also, other events might have been executed in between timeouts. For a reference implementation of this technique, take a look at Wovalab‘s Utilities toolkit which is available on vipm.io.

Update 2023-11-26

Fellow #LabVIEWfriend Cyril Gambini pointed out that setting the timeout value to 0 and implementing any kind of wait function inside the timeout case will effectively block the Helper Loop, making it hard (or impossible) to cancel the wait period.

For fast-running loops with periods of 10Hz (100ms) or more that might not be critical, but if your loops run slower, there is an alternative approach. Instead of letting the Helper Loop time out immediately, you could instead set the timeout to a value larger than 0, but lower than your loop rate. This would reduce the time your timeout case is blocking the Helper Loop, making it much more responsive to other events.

For example, a loop rate of 1Hz (once per second) could go together with a timeout value of 900ms, which would make your timeout case block for only 100ms (loop rate – timeout value).

Helper Loop registered for broadcasts

The second kind of helper loop doesn’t trigger itself. Instead, it registers for other modules’ broadcast events and is then triggered by them. This architecture is even more elegant than the first one. The following screenshots show an exemplary implementation, where the ActorModule DQMH module registers for the System Message broadcast event of the User Interface DQMH module.

Initially, we only supply a constant of the UI.System Message user event refnum for the event registration node. This allows us to create the corresponding event case. Beware that our helper loop is only loaded, but not yet armed.

DQMH: Helper loop with event structure registered to broadcast events
DQMH: Helper loop with event structure registered to broadcast events

Update 2018-03-02

It is very important to mention that each event structure that exposes and uses its Dynamic Event Terminals must have its own Event Registration Refnum, created from a separate Register For Events node. Do not fork the Event Registration Refnum wire! For background information, read this blog post.

We can now arm our helper loop (i.e. enable or activate it) by supplying a valid user event refnum for our event registration. In the example, the Obtain Broadcast Events for Registration.vi VI supplies the user event refnum:

DQMH: Enabling the event registration for broadcast events in the helper loop
DQMH: Enabling the event registration for broadcast events in the helper loop

Disabling the helper loop in this scenario is as easy as registering for the constant of the UI.SystemMessage event, just as we did during initialisation:

DQMH: Disabling the event registration for broadcast events in the helper loop
DQMH: Disabling the event registration for broadcast events in the helper loop

Real-World Example

Where and how do we use this? Well, imagine a simple application that acquires measurement data from some kind of hardware. It needs to display this data and write the data to the hard drive.

The DQMH module acquiring data from the hardware (DAQ) would implement the first kind of helper loop. It would use the timeout case of the event structure to read data from the hardware periodically. It would then distribute these blocks of data via a broadcast event.

LOG would be a DQMH module implementing the second flavour of the helper loop. It registers for the DAQ module’s broadcast event. It can be enabled and disabled separately. This allows the user to turn logging on and off independently from acquiring and displaying the measurement data in the GUI.

How great is that!?

Source Code

In case you’re interested in the example source code, take a look at the GitLab repo at:

https://code.hampel-soft.com/courses/hse-dqmh-training

Here’s hoping that this blog post is only half as helpful as these helper loops!


Update 2018-02-12

When talking to some LabVIEW friends about this blog post, I was confronted with reservations regarding using the timeout event of an event structure for periodic actions.

It is important to state that the timeout event is only triggered if no other events happen during the timespan that was wired to the timeout terminal of the event structure. That’s why it’s called the timeout event after all. So if you’re implementing your event structure to handle all sorts of critical events, your event structure might never actually timeout, and the code inside the timeout event case will never execute.

In our case, the timeout event case IS the critical code, and as we only enable/disable the timeout case and register for the Stop Module event, we should be good [read below for cloneable modules].

I also brought this up on the NI forums, please read Fabiola’s and Darren’s comments.

Update 2019-02-01: Cloneable Modules

Fellow NI Forums user AlexElb pointed out – legitimately – that this article does not explicitly cover the use case of cloneable modules implementing helper loops. As there is a side effect that can cause a lot of headache, I’ll happily elaborate on that.

By design, DQMH creates one set of events for any module, be it singleton or cloneable. As a result, all clones of one cloneable module share the same events. Naturally, you will use the Addressed to This Module.vi in the helper loop to determine if an Enable or Disable request was sent to a clone.

Depending on how exactly your helper loops work, that could be a problem: Every time you call the Enable or Disable request, all of the clones will leave the “wait for timeout” state and execute the corresponding case (and discard the event if it’s not addressed to them), resetting their timeout counter and effectively elongating the timeout period!

If you need exact timing in your helper loop, you have two options:

  • Make sure the timeout counter is not being reset by finding a different way of communicating between your MHL and your helper loop (a separate, manually created local event for example).
  • Add some logic to your helper loop to calculate the exact timing value for each next iteration depending on how much time has passed since the last timeout event.

Update 2023-11-24: Timing

For a reference implementation of helper loop timing logic, take a look at Wovalab‘s Utilities toolkit which is available on vipm.io.

Wovalab Utilities - Helper Loop Timing
Wovalab Utilities – Helper Loop Timing

This post was also published on dqmh.org.

10 thoughts on “DQMH “Actors”: Self-Messaging or Helper Loops?

    1. Saran, thanks for the comment. I agree it would be nice to have a demo video, but I don’t have the time at the moment for proper video recordings. I’ll try to get to that soon.

  1. Joerg,
    Just wanted to let you know I appreciate this post and for sharing your source code. I find myself coming back and reading this post every time I create a new DQMH module with a helper loop.

    1. Ryan, thank you for taking the time to comment. I‘m glad that this article and the sourcecode are helpful to you. Please don‘t hesitate to let me know if anything is unclear.

  2. Hi Jörg,
    I’m developing a project using DQMH (thanks Fabiola) and your beautiful Helper Loop (thanks Jörg).
    In fact, my Actor, polls data from a digital input board, at constant rate and broadcast data.
    In order to send relevant configuration data to the helper loop, I found the following solution:
    1. create a Configure request in the MHL Loop which read from .ini file to the Module Data cluster.
    2. create a Configure Actor request to send the Module Data to the Helper Loop.
    3. add the user event case Configure Actor to the Helper Loop and read the configuration to initialize hardware.
    What do you think?
    Is there another way to send configuration data to the Helper Loop?

    Regards,
    Maurizio.

    1. Maurizio, thanks for commenting! I’m happy that the Helper Loop stuff is of use for you.

      Honestly, I sometimes just use local variables for simple configuration stuff. If you do it like you described, you may want to think about making the “Configure Actor” request a private method in order to restrict direct access to the helper loop from outside the module. And finally, if it’s configuration data that only the helper loop needs, you can have the “Configure Actor” event case read the .ini file directly, and not use the MHL’s Module Data cluster on the shift register.

      What do you think? Does this make sense to you?

      Again, thanks for getting in touch,
      J.

      1. Hi Joerg,
        thanks for message. In a framework like this I prefer the Helper Loop to read by itself the .ini file in order to have stuffs configurable at run-time. Anyhow, during this project, I will make some experiments comparing the advantages of the Helper Loop vs an additional logger module communicating with the MHL with a queue. As a third option, this logger (loop time 200 ms, single point measurements) can be in fact implemented in the MHL with self messaging to read tags. At this point, all three solutions deserve to be implemented and tested.
        Thanks again for your support and for the beautiful works of the code shared.
        Maurizio.

Leave a Reply

Your email address will not be published. Required fields are marked *