07-30-2025 09:04 AM - edited 07-30-2025 09:28 AM
Hello,
I used Labview once more than 10 years ago during a university internship, I wrote only small apps using State Machine approach or with the Labview QMH Demo. I am back with Labview and this time I want to build a more complex and reusable testbench SW. I think DQMH has all the features I need without adding too much complexity. I still have some doubts about the framework, particularly the flow of data and helper loops.
I divided the application into DQMH Modules: Testbench-RIO, CAN Adapter (Kvaser), DUT (a motor), GUI, Manager and Tests. Manager and Tests are not yet implemented; It has for now a small VI to work as a launcher and I am in the moment not concerned about abstraction turning the modules into classes.
The GUI consists of the main.vi having a left side panel and a main subpanel that shows info of the other modules. The subpanel VIs are basically like the DQHM Test VIs, just a while loop with an event structure. They are private to the GUI and interact only via the other modules API and listen to their broadcasts. The other modules should be just backend and have no UI in their main.vi.
The Testbench-RIO read/writes power supplies, analog and digital I/Os, etc. This was the first module I wrote and it is working well. I created a helper loop and a new event to start acquiring data. The flow goes: EHL receives the event with sample rate value, it passes through the MHL that sets the Timeout to the Helper Loop. Every timeout the Helper Loop fires a Read Event that goes to the MHL. The MHL read values from testbench devices (USB and/or TCP/IP) and broadcast it. Read event is also a request/reply event, in case I want a single synch read.
The CAN Adapter module follows the same logic as the RIO Module, but I realized I could create a Start/Stop Acquisition Event directly to the Helper Loop without passing to the EHL. I also noticed that it does not pass through the MHL, unless I explicitly create a message in the Helper Loop and pass through the queue to the MHL. Once activated the Helper Loop uses the API to call a Read Event. Here I change the HL timeout logic, since the Kvaser Driver has its own blocking timeout during reads, I pass the sample rate value directly to the CAN Driver VI and return the Helper Loop as fast as the read is complete with wait for reply = true and event timeout = 0. This module is also working, and it reads, broadcasts without any delays even when the DUT sends CAN messages every 5ms.
As I lack experience the DUT module is making me question this whole implementation. DUT Module should do the following: receive message from CAN module, decryption of CAN message according to ID, parse the payload in human-readable value according to a DBC file and broadcast it. Additionally, it should go the other way around, parse signal values in CAN format, encrypt, write frame to the CAN Module. My logic was to follow the other modules and instead of a Helper Loop with a timeout, it would listen to the CAN broadcast and then act. (Sorry for the excessive amount of IFs statements, but I was testing different codes without deleting older ones and since I am not using git…)
My issue here is that the MHL “Read CAN Motor Message” runs very slowly in comparison to the Kvaser CAN module, and the Helper Loop event structure gets flooded with broadcast events. The DUT module continues to update values several minutes after I’ve already turned off the CAN module and unplugged everything…
Initially I thought it was my decryption code, or the CAN XNET code was not fast enough or just wrong implementation. After debugging and creating a simple linear VI, I noticed that the decryption, parsing etc were fast enough for the sample rate. At this moment I started debugging with IF statements, cutting the code until I found out it was the Broadcast function that slows down considerably. I didn’t suspect it, since the other modules ran fine.
The quick solution that I found was to run everything in the Helper Loop. During initialization the CAN XNET handler and other info are passed from the MHL to the Helper Loop. The Helper Loop now has the Read-Parse VI when CAN message is broadcasted. The Helper Loop also has a timeout of 10ms to broadcast the parse message, and instead of single cluster message, I am putting in an array and broadcasting.
Now, even if I clean this code, this solution feels wrong. I don’t have my core data going through the MHL and the MHL is not doing the main process. I understood that Helper Loops should just be there for continuous, time tracking, actions or external inputs, while the MHL should do the processing and contain the module variables/states. What if when I write test VI and want to write some value for DUT, but it is dependent on some read value which is only located in the Helper Loop? I must create a message to pass the data from HL to MHL again, in the end I have duplicated the data or make a global variable which I want to avoid. Also, my broadcast is fixed at 10ms, but the DUT message rate is dependent on external factors, it can go from ~10ms up to 1000ms. Again, waste of processing time. I could create a logic that tracks the DUT state and set the timeout time, but this only complicates the code. It is easier just to wait and act when there is in fact a message event.
When writing this I noticed that I could listen to the CAN Broadcast directly in the EHL, but because of many CAN message events, it only makes the module slower and irresponsive for other events. Second, I noticed I use the public API to send messages from Helper Loop to the MHL, so it goes through the EHL. I guess it is okay to bypass the EHL and write directly in the message queue from the Helper Loop since both are in the same module. I tried this, and it’s still slower, cause of the Broadcast function.
Most of my questions are general ones. Can the DQMH Framework be implemented like this? Does it make sense? What is the best/correct way to activate a Helper Loop, like the RIO Module or CAN module or a third way? Why Broadcast function just by the DUT module take several milliseconds to complete? What is the best way to deal with an Event Structure (Helper Loop, EHL) that listens to high frequency broadcasts and cannot process it with the same frequency?
In addition, I have an issue that sometimes the GUI subpanel VIs do not close correctly and keep running in the background until I abort it manually. I guess this is linked with the wrong handling of Event Structures and too many events in the queue.
Sorry for the big post, I’ve been reading the DQMH documentation, HSE blog and this forum, but my background is embedded programming bare-metal MCUs. Labview and DQMH have very different way of thinking. Hopefully a debate here can help me and other beginners. (:
07-30-2025 10:31 AM
Hey avellar,
thanks for giving DQMH a try and for taking the time to give this detailed feedback.
I will start off by answering some of the more generic questions - hopefully, that gives you enough food for thought to continue your project. I'm afraid that there is conflicting information on the internet (maybe even in our own documentation) about how to do implement certain functionalities. If you can point me towards those conflicting places, I'll be happy to work towards fixing them.
First of all, the answer will always be "it depends". There are basic rules and guidelines, which work well for the majority of cases, but might be better bent for some corner cases.
I hope these ideas help you along the process of figuring out the best architecture for your application.
J.
DSH Pragmatic Software Development Workshops (Fab, Steve, Brian and me)
Release Automation Tools for LabVIEW (CI/CD integration with LabVIEW)
HSE Discord Server (Discuss our free and commercial tools and services)
DQMH® (Developer Experience that makes you smile )
08-05-2025 10:58 AM
Hi Joerg,
thanks for the fast reply. I thinks most of my confusion comes mostly from the experience I have in bare-metal embedded programming and jumping to Labview/DQMH.
@joerg.hampel wrote:
You don't have to do everything in the MHL. Sometimes it makes more sense to keep some or all the data in the HL and also do all the processing there.
This one I can point to some blog posts that confuse me a little bit. At DQMH blog "Simple DQMH Dos and Don’ts" there are the points Do maintain state only in MHL and Do route everything through the MHL, at the same time I saw in some Demos and "DQMH “Actors”: Self-Messaging or Helper Loops?" post, just what you said. I guess as always it depends of the case, but if the developer is not careful enough, the module can end up sharing variables between loops with race conditions.
@joerg.hampel wrote:
At HSE, we usually add helper loops for processing broadcasts from other modules. Not so much for technical reasons, but rather for readability.
I guess creating a loop just for readability does not affect the performance of the compiled code in Labview.
@joerg.hampel wrote:
I cannot reproduce this. There isn't much code inside a broadcast VI other than getting the event reference and generating the user event. I've never seen this take a long time, so can't really help much right now
Any EHL or HL is supposed to offload the processing of data to the MHL. For short bursts, it's ok to use the message queue as a buffer, but if the MHL cannot keep up with the data rate in the along run, you have to redesign your application. There is no other solution I'm aware of (other than discarding data in the EHL/HL, which is usually not feasible).
I don't have enough Labview experience to debug this issue, but I will keep searching why.
I have some ideas in how to restructure this part of the code. I guess this is the advantage of DQMH being modular.