LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
0 Kudos
Andromeda

Make user events more polymorphic

Status: Declined

Any idea that has not received any kudos within a year after posting will be automatically declined. 

One of the frustrations with user events, is that they are so very tightly typed, that a trivial change to a user event breaks all of its event handlers.

This is an ongoing issue for events that are triggered by one VI, and handled by VIs which are operating asynchronously, since you can't just wire the event creation node to its associated handler(s).

 

I'd like to see user events be identified by something unique, such as a name or an enumeration, and to be polymorphic as long as the data types of any associated user data are compatible.

J. Heerema, PhD - LabVIEW specialist
10 Comments
tst
Knight of NI Knight of NI
Knight of NI

I'm not sure what your problem is specifically. If your problem is with the event refnum breaking, then just make your event data a typedef and the reference will be update automatically.

 

If you're talking about the event structure itself, that doesn't break when you add elements to the event data. It does break under other occasions, but you should offer more concrete examples of how it shouldn't break. For instance, if you change the event name, I agree that it woudl be nice if the structure would automatically adapt to the new name, but breaking is also a legitimate option.


___________________
Try to take over the world!
Andromeda
Member

Thanks for your comments.

 

As you quite rightly pointed out, the way to be typesafe is to define a typedef for each of your user events. So the process is to create a user event, capture it as a control, and define a typedef from that control, which you can instantiate wherever you need the event. So far everything works nicely, although a direct way to create a typedef from the create user event node would streamline the process a bit.

 

Now let's say that you are refactoring a VI, and you make what seems to be a trivial change to the user event. Even if the user event data type hasn''t changee, the event references break, so you have to go back and redefine the typedef.

 

Let's say that your system is reasonably big, and that you have dozens of event types that all use the same user data type (let's assume it's either an int or an enumeration). You end up defining a typedef for each of them, because event refnums are more strictly typed than just the base user data type (they have to be, because they contain an invisible event identifier), so you define a subdirectory insde your typedefs directory to hold  all all of your event typedefs. 

 

Now, since this is a reasonably big system, you have at least a dozen different top-level VIs running, and you need a way to pass user event references to them. A queue won't do, unless you define a separate queue for each of your dozens of event typedefs, which is messy. The best way I've found is to create a global which holds all of the event references.

J. Heerema, PhD - LabVIEW specialist
AristosQueue (NI)
NI Employee (retired)

I think tst meant to create a typedef of the data in the event, not the event itself. So if you have a cluster of two numerics wired to "Create User Event", make that cluster into a typedef so that if you add a third numeric, everything everywhere gets updated. That avoids any mucking about with typecasting the refnum itself.

 

In LV 2011, you can just popup on any constant on the diagram and choose "Make Typedef" -- fast and easy.

tst
Knight of NI Knight of NI
Knight of NI

Like AQ said, I was talking about making the original data a typedef. Changes to the typedef should propagate automatically to all references which use the typedef, and that should include event references. If this doesn't help you, I would need to see a specific example of how it breaks to understand what you're talking about.


___________________
Try to take over the world!
Andromeda
Member

Oh yes, I neglected to mention also having to define typedefs for every kind of user data - thanks for mentioning that additional source of clutter!

 

Here's an illustration of excess strictness.

We start by creating a couple of user events, indicating the user data type. I've used the very simplist possible user data type: a boolean constant in the lower of the two create event nodes:

Before (working)

 

Now I refactor by creating a new boolean constant, and wiring it in place of the old one:

After (broken)

 

and hey presto - the VI is now broken.For folks who haven't seen this for themselves, the error message is "The Event Structure's configuration is incompatible with the event registration refnum you have wired, or the event registration refnum is unwired but there are still dynamic events configured. Right-click the structure and use the Edit Events dialog to modify or delete the invalid dynamic event specifiers."

 

As you say, you can avoid this by creating lots and lots of typedefs, but the problem with this is that they add unnecessary clutter. I don't want to define typedefs that add no functionality to a base data type - why would I want to define my very own flavour of a boolean constant as a typedef? This is taking strict typing way, way too far! Why would you want two boolean constants to be treated as being of different and incompatible types? My thoght is that the trivial change illustrated above shouldn't break the VI. (and yes, I do know that I could right-click on the terminal to generate a compatible boolean constant)

 

For complex data types, I agree that defining your own typedefs is the correct approach. But if you have a common typedef that you use for multiple user events, you still have to define each event reference as its own typedef, because each reference contains a unique refnum, and the only way to propagate it to all of the instances that use it is to define it as a typedef.

 

I'd rather see the refnum obtained at runtime from a basic data type (an int or a string), which is how queue references are handled (queues use strings as the lookup key, although an int would be more efficient). That way I can define an enumeration that contains all of my user events, and just wire that enumeration into event generation nodes or event handlers (of course I'd still have to create the event before using it). The RT would lookup the value of the enum to obtain the event instance, and the code would be a lot cleaner.

 

To me it's an example of why invisible references are generally not a good language design idea. For simple code they're fine, but they add unnecessary complexity to larger designs. I don't mind having LabVIEW generate a reference from an internal reference generator by default, but I'd like to be able to override it by wiring to an optional input.

J. Heerema, PhD - LabVIEW specialist
Andromeda
Member

Oops - one correction - if the suggested option were to be implemented,  the compiler could resolve event references from a static key type (say, an enum), no run-time overhead would be incurred.

J. Heerema, PhD - LabVIEW specialist
AristosQueue (NI)
NI Employee (retired)

The problem in the example you posted, where you swap one boolean for another, has nothing to do with type and everything to do with *name*. Events are resolved by name at the event structure. Changing the name is always going to break the VI, regardless of any relaxing of the type restrictions. There's no invisible references involved here -- it's that there's no way for LV to know which refnum the event frame is supposed to fire on if it cannot find one of a matching name.

tst
Knight of NI Knight of NI
Knight of NI

 


Andromeda wrote:.
I'd rather see the refnum obtained at runtime from a basic data type (an int or a string)... That way I can define an enumeration that contains all of my user events, and just wire that enumeration into event generation nodes or event handlers (of course I'd still have to create the event before using it). The RT would lookup the value of the enum to obtain the event instance, and the code would be a lot cleaner.

Well, you can do this today. If you make the event data an Enum + Value cluster, then all user events can share a single reference and you pay by having to select the correct value for the enum where you generate the event and having a case structure inside the event structure to handle the specific case.

 

I understand where you're coming from, but the case where you have several separate events which have the same data type is a specific use case. I don't think it justifies changing the architecture.


___________________
Try to take over the world!
Andromeda
Member

Thanks for your comments!

 

First, my apologies for showing an example for something other than what I was talking about. That was an error on my part - the posted example just shows an event structure not adapting itself to a user event with a different name, which is not a problem - sorry about that!  That what you get for replying in a hurry - thanks for pointing that out, Aristos Queue.

 

Thanks for your suggest tst. Yes, I agree that the mechanism you proposed reduces the number of typedefs required, but it requries all receivers of the event to receive every message of that type, and determine whether they should act or not. It still depends on the use of a special kind of reference, and the user still has to transport it to every VI which uses the event.

 

Let me try to illustrate what I'm proposing. Here's the current mechanism:

Events 1.png

The mechanism is simple and easy to use if the event is created in the same diagram as its consumers. It's not so great if the consumers are in a different top-level VI, because you need to capture the user event and send it to every user of the event. Unfortunately, you can't see inside the event reference - it contains name and type information, plus probably a unique identifier, but we can't inspect it, as it's a closed data structure.

 

Events 2.png

 

Events 3.png

What I'm proposing, is that name and type information be used to resolve the event in consumers of the event, like this:

Evens 4.png

This has several advantages, to my mind:

- the resolution identifier can now be inspected, instead of being a special object which cannot be examined

- event transportation is handled by LabVIEW, instead of user code, reducing user code clutter

- refactoring of the event data structure propagates to consumers through the typedef resolution mechanism

 

In the example above, I have adopted tst's suggestion of a generic event data structure, but that's a design decision which wouldn't really be necessary.

 

The primary change that I'm proposing is pushing the work of event resoution to the compiler or run-time.

This is how queues work - a queue can be resolved by specifying its name and type - users don't have to specially send a special queue identifier to every VI which makes use of the queue.

 

I'm also proposing making the event resolution mechanism a function of inspectable objects (queues use a combination of name + type already, both of which are inspectable.

 

This would be a very similar mechanism to the queue resolution mechanism, except that the identifier would be the label value instead of a general string object, letting the compiler resolve the event, instead of the run-time.

 

A simple way to add this to the existing archetecture would be to keep references as they are, but to add a "Get Event Reference" function, would would take as its input a named type, and would emit an event reference. Users could obtain a reference by using this new function, and avoid any potential overhead of reference lookup by retaining the reference (but if the compiler can resolve the reference, this wouldn't be an issue).

 

Comments are welcome.

 

 

 

 

J. Heerema, PhD - LabVIEW specialist
Darren
Proven Zealot
Status changed to: Declined

Any idea that has not received any kudos within a year after posting will be automatically declined.