LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Community Nugget 2009/03/13: An Event based messageing framework for larger applications

Community Nugget 2009/03/13: An Event based messageing framework for larger applications

 

Motivation:

 

It happened to me that my top level VI evolved into a spaghetti bowl of queues, notifiers, events and so on that connected the multiple modules running in parallel. To overcome this issue, I decided to code a many-to-many communication framework. If it would be hardware-wiring, this is the analog of a bus connecting several devices by a single cable.

As a framework, this will evolve over several projects adding more and more tools, instrument drivers and so forth to the libraries, ready to get clicked together to meet the next challenging deadline.

 

Basic Architecture:

 

Each message consists of a message ID (string) and some data (variant). These messages are passed as User Events through the application.

 

Talkers generate the Events and Listeners register for the ‘Message Event’-User Event. Listeners have a case structure for the Message ID string to react on some of these Events, discarding all unknown Events in the default case.

 

The most important Event is the Message ID ‘Exit’, which causes all Listeners to stop.

 

 

Message Edited by F. Schubert on 03-13-2009 12:06 PM
Download All
Message 1 of 17
(19,092 Views)

Talkers inside a spinning loop will throw error 1 when the Event is destroyed.

 

Intelligent Controls/ Indicators Tip:

You can put a VI in the User controls. When you drop the Control on your Front Panel, the code of the VI is placed in the Block Diagram.

 

Error Handling Concept:

Errors should all be caught in the main loop using the Message ID ‘Error’ and the error cluster as data. So the error handler for all Sub-Modules Talks if an error occurred.

 

Message Edited by F. Schubert on 03-13-2009 12:10 PM
Download All
Message 2 of 17
(19,082 Views)

The main loop reports the error to the user and exits.

 

Large Scale Architecture:

An application might have several modules, which have there own ‘internal’ bus. Data on the internal bus is not visible to other parallel processes. But they might be interested on data from the top level bus, thus we need a VI to forward these messages.

 

So when the Top-Level sends an exit event, the module will shut down, as it does with the local Exit Event. But the Top-Level VI is not affected by the local Exit Event.

 

Message Edited by F. Schubert on 03-13-2009 12:13 PM
Download All
Message 3 of 17
(19,080 Views)

At first the Event queue is properly flushed. Then all Events are passed from one Event queue to the other. A Top-Level Exit Event is passed to the local bus and read from the local bus to end this VI.

 

Problems with the chatter:

 

When all Sub-Modules are continuously broadcasting there measurement readings, status and other information over the bus, consumers that need to perform tasks in a timeout event are not working. So a filter needs to block all unwanted events.

 

 

 

Attached is the complete Set of VIs and Templates in 7.1.

 

Any comments, critics, kudos, suggestions are appreciated.

 

Nice weekend.

 

Felix
Message Edited by F. Schubert on 03-13-2009 12:16 PM
Download All
Message 4 of 17
(19,076 Views)
This looks very interesting.  I have one question for you, why do you use a string for the Message ID instead of a type defined enum?
0 Kudos
Message 5 of 17
(19,069 Views)

Nice Felix, thanks for sharing!

I have used a very similar concept with local and global bus systems in an application, but based on queues and notifiers. The event based approach combines the advantages of the two, it's queued (like the queue Smiley Wink ) and it's many-to-many (like the notifier).

 

These "bus systems" are really powerful, you can just add a module to your application and it will connect to the bus, no extra include work required.

 

While it's quite general, I personally don't really like variants for messages. I prefer strings, you can still use any data type (flatten/unflatten/typecast), but sometimes the message is actually a string. But I guess this is just a question of taste.

 

Depending on what the bus is used for, I also prefer to separate a command bus and a data bus. This can be an alternative or an addition to the filter you suggested.

 

Thanks again and have a nice weekend.

Daniel

 

Message Edited by dan_u on 03-13-2009 09:26 PM
0 Kudos
Message 6 of 17
(19,027 Views)

Thanks jmcbee for putting a finger on a lacking documentation issue. Actually using Strings instead of enums was the issue most reasoned about in the design process. I don't have my documentation from then right here, but it mainly covers concerns about going with the string. I'm always going with a type def'ed enum, so why not this time? Looking back with the new insights I got during the developement of this framework, it is a perfect example of:

'it is a good practice to break with good practices'

All the advantages of type def'ed enums (compile time errors) turn against it if you need a framework that is independent from implementation (single project or application). I want a very loose coupling between this framework and the applications I build using the framework and the tools that exist within the framework.

 

Maybe it ismore clear if you consider the following scenario:

Serveral developers each having a set of software applications. Each software application might be transfered from one developer to the other (workload is a main factor). Each software application might be 'reactivated' at any time to do some of these: bug fixing, implementation of new hardware/functionality, reused as starting point for a complete new application, a branch for some special customizations. Sleeping-time of the software can go up to some years.

Concurrently, the framework has it's own life cycle, with tools, hardware drivers, .... added, upgrades, bug fixes...

Yes, this is programming on the edge of chaos.

 

If you look at the Architectur vi, you see that the tools within the framework are always placed on one line according to the message IDs they Talk/Listen to. That makes testing easy.

 

So to conclude: Strings where choose to provide a very loose coupling, and a lot of headach was involved in making that decision.

 

On the Daniels points:

* Using Variant or String for the data is a matter of taste.

* One of the big pros for using events was that they combine some features of queues and notifiers, the draw-back is the additional coding involved for the event registration, additional error handling case structure and a bit more coding for the timeout behaviour.

* You mention a separation of command and data bus. Possible to provide an example? The enlightnenment of that I'm using a bus came pretty late during developement, so I'm now very courious of how to implement different bus concepts within LV. The concept of a bus is so cool in the hard-wire-world, and propably much more the thing to talk about with other soft-wire-workers than CS (computer science) things like 'OOP`, 'recursion', 'XControls'. In some future, bus concepts might be a fundamental concept of LV programming like state machines, with the most basic bus be the various implementations of producer/consumer design patterns.

 

Felix

Message 7 of 17
(19,002 Views)

The reason for separating command and data bus is as follows:

if submodules publish data on the bus this might cause a lot of events which most modules might not be interested in. If you use a separate bus for the data only those modules interested in the data "subscribe" (connect) to it.

It basically just means that instead of one you have 2 parallel buses. One of them you use for commands (mostly "downstream") and one for data (DAQ processes can publish measurements or similar on this bus).

 

So it's a different approach to solve the potential problem you mentioned before


F. Schubert wrote:

Problems with the chatter:

 

When all Sub-Modules are continuously broadcasting there measurement readings, status and other information over the bus, consumers that need to perform tasks in a timeout event are not working. So a filter needs to block all unwanted events.



and that you solved by filtering unwanted events.

 

 

Daniel

 

Message Edited by dan_u on 03-13-2009 11:42 PM
Message 8 of 17
(18,987 Views)

Felix,

 

This is a great nugget.  I redesigned my user interface template to include this message bus.  That is a significant event, since the last time I updated my template was to include an event structure several years ago.

 

I did modify a few things that didn't work well with my programming style, but that is to be expected.

 

I will put my name on the string side of the enum/string debate.  I prefer strings in case structures, queues, and the message bus.  The biggest benefit is that you don't have to make a new copy of all your VIs every time you create a new interface from your template.  I also dislike the tedious editing process when editing the list of enums, when I can just type a new name for a new case when using strings.  Don't get me wrong - I love type def enums for most other situations.

 

Thanks for sharing this concept.  I learned a lot from it. 

 

Bruce

Bruce Ammons
Ammons Engineering
0 Kudos
Message 9 of 17
(18,493 Views)

Hi,

 

Thanks for this nugget it look very interesting as I am looking to solve some of the problems your are talking about.

 

I intend spending some time looking at this further, however I have one VERY silly problem Smiley Sad I cannot find a events pallete in my Functions Palette, I am running labview 8.2.1  Full proffessionla version can anybody help me, where are they. I there a known problem with this ?

 

I even tried the Find on Function Palette button in the help for XCreate User event

 

 

Danny Thomson AshVire Ltd
0 Kudos
Message 10 of 17
(17,504 Views)