Jump to content

Dynamic Dispatching on User Events


Daklu

Recommended Posts

I've been playing around with defining classes as the data type for user events so I can add additional capabilities/data to the user event without breaking client code. I've created an abstract UserEventData parent and all the user events my module exposes are typed as children of the parent class. I thought wiring a user event defined with a child class into a terminal of the parent class user event would be fine, but apparently User Events are really particular about what types they accept.

post-7603-126351546194_thumb.png

The only solution I can think of is to wrap each user event in another class and give each of them unique static Get/Set methods. This is a lot of extra code for only a little benefit. Is there a better solution to this problem?

Link to comment

These are the only ideas I can think of...

a) Try upcasting your objects to LabVIEW Object. That loses your type safety but it might be acceptable in some cases.

b) You can wrap them in a variant and carry around an array of variants

c) The Event Registration node creates a composite event registration wire out of multiple events. Can you use that? (It's more like bundling with a cluster than building an array, so probably not much use to you.)

post-5877-126351917644_thumb.png

For the record, no one has ever asked for this before and I've never thought to try this. Anyone see a reason why the polymorphism couldn't be added?

  • Like 1
Link to comment

I wasn't actually trying to put different events in the same array. That was just a quick test I threw together when I realized what I was trying to do wasn't working. This is probably way more information than you want, but since you suggested it might be possible to add the functionality I figured I better explain exactly what I was doing. (BTW, this is in 8.6.1)

I'm building an application using the Model-View-Controller architecture. I'm creating the app engine (Model) as an active object. (I'll be doing it with the UI too, but I'm not at that point yet.) The engine exposes user events to notify clients of what's going on. I'm not sure exactly what events the engine will need to expose or what data each event should include, so I wanted to make it as easy as possible to add new events or modify the data in existing events, preferably without breaking client code. This led me to try encapsulating all the event data in classes.

My first cut was for the engine have a single event of the parent EventData class type. I passed the appropriate child EventData object into the Generate User Event prim. Client code called EventData:GetName to return a unique string and had to case out the event data based on that.

post-7603-126353647484_thumb.png

This is okay and probably would have worked for my immediate requirements. I'm not real happy with it though because casing out on a string is a runtime bughole I'd prefer to keep closed. As a general purpose technique it also doesn't allow clients to selectively register/unregister for events at runtime. The client has to decide at design time which events will be handled. At runtime the only choice is to either handle all of those events or none of them.

That led me to exposing each user event refnum so the client can pick and choose. However, if the client code destroys the user event it will cause errors in the active object and cause any other clients registered for that same event to quit responding to it. So I figured I'd try creating a new user event every time a GetUserEventRefnum method is called. This gives each client a unique user event. The engine stores the new user event in an array with all the other refnums for that same user event. The engine class has an EventRefnumArray for each unique user event it supports. When it needs to raise an event it iterates through the array sending the same EventData object to each event.

post-7603-126353942192_thumb.png

I figured raising an event is a pretty common activity so I tried putting that functionality in a sub vi to avoid all that duplicate code. This is where I ran into the trouble. The top center vi is my sub vi for raising all the events in the array. At the top right is a simple example of trying to send an array of user event refnums typed with a child class into the sub vi. I'm not sure if my example in the original post is effectively the same under the hood as what I really tried to do. I think they are, but...?

Getting back to your suggestions, upcasting the child EventData object into the parent EventData object lets me wire it up. I suppose I could require clients to downcast back into the correct data object since each event always sends the same subclass. Not as clean as keeping the type info, but probably better than my proposed solution. (Too bad I implemented all those event refnum wrappers before reading your post.) I'm pretty sure I'll be able to maintain EventData object type information using EventRefnum wrappers and overriding EventRefnum:RaiseError in each specific event subclass. Better for the client... PITA for me.

post-7603-126354140745_thumb.png

b) You can wrap them in a variant and carry around an array of variants

Converting the user events to variants doesn't solve the fundamental issue I was trying to avoid, which is identical copies of the raise event vi that differ only in the specific class the event refnum array is typed as.

c) The Event Registration node creates a composite event registration wire out of multiple events. Can you use that? (It's more like bundling with a cluster than building an array, so probably not much use to you.)

Exposing the registration number instead of the user event takes me back where I started. Your comment about it being like a cluster rather than an array may shed some light on why I've had such a hard time figuring out dynamic events. What started me on this little adventure was the idea of giving users to access to each event so they could choose when to register and unregister for each of them individually rather than treating them as a whole. In truth, I haven't figured out how to do that myself, though I had always assumed it was possible.

For example, my engine exposes 12 events. Suppose a client initially registers for the six load and save events. But there are certain situation where the client wants to temporarily receive the GeneralEngineErrorEvent and other situations where the client wants to temporarily stop receiving the CalFileLoadErrorEvent and CalFileSaveErrorEvent.

I've often wondered why I haven't been able to add or remove user events that are registered to an event structure. If the Register for Events prim behaves similarly to the Bundle (not Bundle by Name) prim, then that explains a lot. I still wish there were a built-in way to filter user events at runtime, but at least I have (I think) a better understanding of the current behavior.

Edited by Daklu
Link to comment

My first cut was for the engine have a single event of the parent EventData class type. I passed the appropriate child EventData object into the Generate User Event prim. Client code called EventData:GetName to return a unique string and had to case out the event data based on that.

post-7603-126353647484_thumb.png

This is okay and probably would have worked for my immediate requirements. I'm not real happy with it though because casing out on a string is a runtime bughole I'd prefer to keep closed. As a general purpose technique it also doesn't allow clients to selectively register/unregister for events at runtime. The client has to decide at design time which events will be handled. At runtime the only choice is to either handle all of those events or none of them.

Why can't you dynamically dispatch off the user event and resort to casing out based on class name. DD works for me. See attached. I guess Stephen alluded to this in his post.

I've often wondered why I haven't been able to add or remove user events that are registered to an event structure. If the Register for Events prim behaves similarly to the Bundle (not Bundle by Name) prim, then that explains a lot. I still wish there were a built-in way to filter user events at runtime, but at least I have (I think) a better understanding of the current behavior.

Not sure if I'm really answering your question or just misunderstanding, but you can register/unregister for user events during runtime. Doesn't that qualify as add/remove. Again, see attached. I apologize if I'm not following you completely.

Thinking about this also made it clear to me why we cannot ever support polymorphism of the user event wires. It is the wires themselves that propagate the types to the Event Structure. Those types have to be known at *compile* time. If you upcast all the wires, it would be no different than if you had created all your events with LabVIEW Object in the first place: you'd loose all the type safety in the Event Structure, and all the events would be named the same.

blink.gif Then what's going on here in 2009. Am I missing something:

post-415-126357866506_thumb.png

Class User Events Folder.zip

Link to comment

If you dynamically registered for an event that you had never included in your code, there would be no frame of the event structure that would handle that event.

The event would be in the source code and the event structure would have all the code to handle any events it ever expected to receive, it would just be a runtime decision if and when the event structure actually receives those events. In the past I've handled it by maintaining my own flags in the event code; I just wish there were a better way to do it. It feels a little clumsy to have to carry an array of flags through the event loop and enclose all the event processing in a case statement.

post-7603-126357607868_thumb.png

The dynamic event example included with Labview unregisters for events from specific VIs by removing that vi ref from an array and reregistering the vi ref array. It's not quite the same as what I'd like to be able to do but it might be an avenue worth exploring.

post-7603-126357774572_thumb.png

Suppose the developer wanted to unregister for one of the user events. Is there a preferred way of doing that? I suppose you could effectively unregister for a user event by registering a dummy event in it's place. I don't think you need to worry about memory leaks from the dummy user event since it's a constant rather than a runtime creation. However, I don't see any way of reregistering that single event when the event consumer is only given the registration wire instead of the user events themselves. Thoughts on this technique?

post-7603-126357935287_thumb.png

Thinking about this also made it clear to me why we cannot ever support polymorphism of the user event wires. It is the wires themselves that propagate the types to the Event Structure. Those types have to be known at *compile* time. If you upcast all the wires, it would be no different than if you had created all your events with LabVIEW Object in the first place: you'd loose all the type safety in the Event Structure, and all the events would be named the same.

The naming issue is fairly easy to solve by simply relabelling the parent class at the upcast. The side effect is that it isn't clear to the event consumer that they need to downcast the object in the event structure, but what are you going to do?

post-7603-126357629626_thumb.png

The original purpose of making all the event data object siblings was so I could send any of them through a single event and use a common GetName method. Given that it's more beneficial to give the client each user event independently maybe there's not point in deriving them from a common parent. I'll have to think about this for a while.

As always, I appreciate your thoughtful answers and the time you spend responding to users on the forum.

Link to comment

Why can't you dynamically dispatch off the user event and resort to casing out based on class name. DD works for me. See attached. I guess Stephen alluded to this in his post.

I actually did start out by casing out based on class name as I showed in the first diagram of post #3. The paragraph following that diagram explains why I don't really like using that as a general purpose solution.

The main difference between out code is that I'm using events across code module boundaries. The event producer is in one module, the event consumer is in another. The producer doesn't know anything about the consumers that are observing it. I can't include consumer code in the producer without coupling the producer to it, which drastically reduces my ability to reuse the producer. In your code the event classes, which would be in the event producer, contain the code that actually executes when the event fires at runtime. I'm looking for a way that allows the consumers to execute their own arbitrary code at runtime.

The event data classes are (currently) nothing more than generic containers for data. By putting the event data in a class and providing getters to the client for retrieving each piece of data, I can add arbitrary data elements to the specific event data class and supply a new getter for it without having to worry about breaking any client code. Since each event the producer exposes has a unique set of data elements I can't put all the getter methods in the parent class and override them in children without really making a mess of the EventData api. The getters have to be unique to each child class, which means the object that pops out of the event structure when the event fires needs to be typed as the correct child class at design time. (Either that or the consumers have to downcast the object at runtime, which I mentioned above.)

Not sure if I'm really answering your question or just misunderstanding, but you can register/unregister for user events during runtime. Doesn't that qualify as add/remove. Again, see attached. I apologize if I'm not following you completely.

Yep, it does. Replacing the registration refnum with a constant hadn't occurred to me until I was composing my previous post. Looks like I've been missing the boat on this one. :)

Link to comment

I actually did start out by casing out based on class name as I showed in the first diagram of post #3. The paragraph following that diagram explains why I don't really like using that as a general purpose solution.

The main difference between out code is that I'm using events across code module boundaries. The event producer is in one module, the event consumer is in another. The producer doesn't know anything about the consumers that are observing it. I can't include consumer code in the producer without coupling the producer to it, which drastically reduces my ability to reuse the producer. In your code the event classes, which would be in the event producer, contain the code that actually executes when the event fires at runtime. I'm looking for a way that allows the consumers to execute their own arbitrary code at runtime.

The event data classes are (currently) nothing more than generic containers for data. By putting the event data in a class and providing getters to the client for retrieving each piece of data, I can add arbitrary data elements to the specific event data class and supply a new getter for it without having to worry about breaking any client code. Since each event the producer exposes has a unique set of data elements I can't put all the getter methods in the parent class and override them in children without really making a mess of the EventData api. The getters have to be unique to each child class, which means the object that pops out of the event structure when the event fires needs to be typed as the correct child class at design time. (Either that or the consumers have to downcast the object at runtime, which I mentioned above.)

Yep, it does. Replacing the registration refnum with a constant hadn't occurred to me until I was composing my previous post. Looks like I've been missing the boat on this one. smile.gif

OK, first I have to confess that while I read through all the posts in this thread I didn't read each one in detail.

The solution we use here sounds like it meets the needs mentioned in this last post.

Caveat: We don't key off event types. We use a single event (for any given message thread) and pass all data (of all types) over this single event.

In our case this event is a shared variable event, but other things are possible. All the actual messages inherit from a top-level Command object. When we receive a message (flattened to a string) we unflatten to a Command (parent) object, but then dynamically dispatch on the actual type (object type) on the wire. The actual model code does not need to be in the message object, and it can obtain whatever data is necessary.

This is just an implementation of the Command Pattern.

This does mean that all listeners to a particular shared variable get all messages on that thread but can easily ignore the ones that don't apply. In our case we do have several lines of communication so a subscriber can subscribe to the topics of interest to that subscriber. (So a thread might contain all messages related to a particular axis or subsystem.)

Back to the caveat. We can identify the shared variable on the event, which gives us an option of filtering that way as well.

Can you list the key requirements and constraints for this part of your application? I'm not sure whether this solution meets them all or not.

Link to comment

(Three posts in a row by me... apologies to anyone who considers this spamming...)

Thinking about this also made it clear to me why we cannot ever support polymorphism of the user event wires. It is the wires themselves that propagate the types to the Event Structure.

I understand why the registration wire has to be strongly typed. Thinking out loud, is there a reason the strong typing has to propogate back to the user event wire? Wouldn't it be possible to allow the user event wire to be polymorphic and have the Register for Events prim create the registration wire assuming the event data object will always be that type? Obviously the Reg Events prim would have to throw a runtime error if a child object is on the user event wire at runtime, but there are other things I can do with a user event wire that don't include the Reg Events prim. If this were possible I could have single RaiseEvent and Destroy events methods that operate on any event that carries a child of my EventData class.

It seems odd that other containers, such as clusters and arrays, can polymorph when they contain classes, but user events cannot. I get why they can't, but it creates an inconsistent programming environment. Are there other user event use cases that would get messed up if the user event wires were polymorphic?

[Edit - Hopefully this is a simpler request. Would it be possible for the Reg Events prim to list the user events by name instead of just calling them all "User Event?"]

Edited by Daklu
Link to comment

OK, first I have to confess that while I read through all the posts in this thread I didn't read each one in detail.

The solution we use here sounds like it meets the needs mentioned in this last post.

Caveat: We don't key off event types. We use a single event (for any given message thread) and pass all data (of all types) over this single event.

In our case this event is a shared variable event, but other things are possible. All the actual messages inherit from a top-level Command object. When we receive a message (flattened to a string) we unflatten to a Command (parent) object, but then dynamically dispatch on the actual type (object type) on the wire. The actual model code does not need to be in the message object, and it can obtain whatever data is necessary.

This is just an implementation of the Command Pattern.

This does mean that all listeners to a particular shared variable get all messages on that thread but can easily ignore the ones that don't apply. In our case we do have several lines of communication so a subscriber can subscribe to the topics of interest to that subscriber. (So a thread might contain all messages related to a particular axis or subsystem.)

Back to the caveat. We can identify the shared variable on the event, which gives us an option of filtering that way as well.

Can you list the key requirements and constraints for this part of your application? I'm not sure whether this solution meets them all or not.

Thanks for the input Paul. I've seen your posts on the MVC architecture and my desire to decouple the UI from the business layer led me in that direction. Having a name for it saved me a lot of effort trying to reinvent it, and I've got plenty of half-built wheels hanging around. smile.gif

I believe upcasting the event data object as suggested by AQ and downcasting it in the event consumer is a good enough solution for my immediate application. Not everything I was looking for, but good enough. The rest of it is a theoretical thought exercise to see what I can do using current technology or could do if user events were polymorphic.

In a nutshell, I'm looking for the best way for reusable code modules to expose user events to clients. "Best," in this context means several things. Among them are:

  1. The code module should not be coupled to any clients. Clients should only be loosely coupled to the module.
  2. It should be easy and intuitive for clients to do arbitrary operations on the event data they receive.
  3. It should be relatively easy to add new events or extend existing events in the module source code.
  4. The code module framework to enable this should not be too large. (Cries of "Code bloat!" from non-OO coworkers still ring loudly in my ears.)

I considered the command pattern and know client code could inherit from a Command parent class supplied by the module. The problem with the command pattern in this instance is that it doesn't work well if your command process for some of the sub classes requires additional inputs or outputs. Suppose the client code has registered for the LoadFileError event exposed by my engine, but the client only wants to log those errors if a checkbox on the user interface is true.

The client subclasses the LoadFileErrorCommand class provided by the code module and overrides the Execute method. The client doesn't have a good way to get the boolean value into the overridden execute method. The execute method is an override so you can't add terminals to it. You can't add a Set LogToDisk method to the subclass and put that in the event handler without typechecking and downcasting the parent object at runtime, which defeats the purpose of dynamic dispatch in the first place. (It might be possible to set a front panel boolean by getting a reference to the overriding execute vi and using the SetControlValue invoke node. I don't know if that works on controls that aren't on the connector pane. Regardless, I consider that a pretty difficult hoop to jump through for developers of unknown skills who are working on client code.)

To allow clients to do arbitrary operations on arbitrary data from arbitrary events, I think you have to expose all the data elements to them, which in turn means the user event structure has to give them a strongly typed EventData subclass at design time. This of course requires the module expose unique events rather than a single generic event. I can do that... so far so good. The inconvenience arises while developing the source code to enable this functionality. Since user events are not polymorphic it is very difficult to operate on the them in sub vis or case statements. This forces me to create and maintain a lot of duplicate code; I have to recreate any common user event processes (Create, RaiseEvent, Destroy, etc.) for each user event the module exposes. Hopefully this image helps explain the problem I'm trying to address.

post-7603-126359039945_thumb.png

If it seems like I'm trying to find a "one size fits all" solution, you'd be pretty close to spot on. If I had and understood a general solution to the problem I'd be in a much better position to know where I can take shortcuts when applying it to a specific situation, and I'd have a better understanding of what the long term consequences are of using the shortcuts. I've got the hammer, but there are lots of things laying around that don't look like nails. I'm trying to fill out the rest of my tool belt.

Edited by Daklu
Link to comment

I think we read a lot of the same posts! book.gif

Q: When you talk about adding a Boolean, are you talking about something on the front panel of the client (here I mean message recipient), or something that is part of the original message?

You can add the Boolean to the message easily enough, but I don't think that is what you mean. I generally try to keep my controllers from needing inputs not in the message, but where that would be necessary it might be tricky, although possible. The method the recipient performs can vary, though, because you can include the recipient as an object within the message object.

Anyway, it sounds like you have found a path to success.

Link to comment

Q: When you talk about adding a Boolean, are you talking about something on the front panel of the client...

Yep. BTW, I'm not familiar with shared variables. When you say a "shared variable event" do you mean a regular user event typed as a shared variable or is it a different construct altogether?

I generally try to keep my controllers from needing inputs not in the message, but where that would be necessary it might be tricky, although possible.

Figuring out exactly what functionality belongs in the controller and what belongs in the model (and sometimes even what belongs in the view) isn't very clear. There's a lot of gray in there.

The method the recipient performs can vary, though, because you can include the recipient as an object within the message object.

I don't quite follow... do you mean the parent command class' execute method has an input for "recipient" that the sub classes can use?

Anyway, it sounds like you have found a path to success.

Heh... sometimes I wonder. This path is covered with jagged rocks and prickly thornbushes. I don't think many have travelled this way. ;)

Link to comment

To allow clients to do arbitrary operations on arbitrary data from arbitrary events, I think you have to expose all the data elements to them, which in turn means the user event structure has to give them a strongly typed EventData subclass at design time. This of course requires the module expose unique events rather than a single generic event. I can do that... so far so good. The inconvenience arises while developing the source code to enable this functionality. Since user events are not polymorphic it is very difficult to operate on the them in sub vis or case statements. This forces me to create and maintain a lot of duplicate code; I have to recreate any common user event processes (Create, RaiseEvent, Destroy, etc.) for each user event the module exposes. Hopefully this image helps explain the problem I'm trying to address.

hammer, but there are lots of things laying around that don't look like nails. I'm trying to fill out the rest of my tool belt.

Heh... sometimes I wonder. This path is covered with jagged rocks and prickly thornbushes. I don't think many have travelled this way. wink.gif

Is LV2009 at all an option for you. Based on the image below, it seems to get you farther down the rocky path. It seems that it supports some level of polymorphism on class-based user events where 8.x doesn't. I was hoping AQ would shed some light on this based on his previous comment of never supporting polymorphism of user event wires. I can understand that comment when applied to types that are completely different (say string and int), but when the types are part of a class hierarchy and follow certain constraints, LV2009 appears to contradict his statement.

-Scott

post-415-126359876975_thumb.png

Link to comment

Yep. BTW, I'm not familiar with shared variables. When you say a "shared variable event" do you mean a regular user event typed as a shared variable or is it a different construct altogether?

...

I don't quite follow... do you mean the parent command class' execute method has an input for "recipient" that the sub classes can use?

It is straightforward to register for shared variable value change events, but only with the DSC Module, unfortunately. The DSC Module includes several functions ("Enable Value Change Notifications", "Request Value Change Notifications" [accepts and array of shared variables], "Cancel Value Change Notifications", and "Disable Value Change Notifications") for this purpose. Connect the resulting event wire to the Dynamic Event Terminal on an event structure. Then the event structure has a "shared variable value change notification" user event. (The Value and Shared Variable are some of the data elements associated with the event.) This page illustrates the basic idea: Creating a Value Change Event for Shared Variables.

I was thinking that he command class itself (parent--if the Receivers are all of the same type--or child level) can include any data we want, so this could be a Receiver object. (This means that the command sender knows the intended receiver in this case, which is often undesirable.) I haven't included a Receiver object myself (my applications haven't needed it) but I have included references in the specific commands, where each reference is a unique type, and really this information could be anything. I don't think this is possible without the extension possibilities allowed by OOP!

___

Update: Since I wrote this I found that upcasting the control reference still worked so I can write the references in multiple instances (objects) of the same class, which saves a lot of development time and makes the application very easy to reuse. Reflecting on this thread is what inspired me to take another look at this, so thanks!

Link to comment

It turns out upcasting the child data object to the EventData object didn't work out as well as I had hoped. So I tinkered around with it for a while and came up with a better solution. I ended up with what I think will be a highly reusable EventManagerTemplate code module that meets almost all of my goals. Adding a new event only requires deriving a new MyEvent class and a new MyEventData class with a single Create sub vi for each of them (though you do need to add getter methods to MyEventData if it carries data), and adding one GetMyEventRefnum vi to the EventManager class. No need to create duplicate RaiseEvent or DestroyEvent methods. No need for the event consumer to type check the event data object in the event structure, OR to downcast it to the proper event data subclass.

If there's enough demand I'll try to spend some time cleaning it up and documenting it. shifty.gif No promises on how soon I could post it though... it would likely be at least a week. In the meantime, here's a screenshot of a quick example I rigged up.

post-7603-126369260084_thumb.png

-------------------------------------

Thinking out loud, is there a reason the strong typing has to propogate back to the user event wire? Wouldn't it be possible to allow the user event wire to be polymorphic and have the Register for Events prim create the registration wire assuming the event data object will always be that type? Obviously the Reg Events prim would have to throw a runtime error if a child object is on the user event wire at runtime, but there are other things I can do with a user event wire that don't include the Reg Events prim.

I've reconsidered. Rather than throwing an error, how about forcing an upcast to the parent object at the Reg Events prim? The registration wire would still be strongly typed, which as near as I can tell is really the one that matters, and the user event wire could be polymorphic. (Maybe that's what's happening in 2009?)

Is LV2009 at all an option for you.

Not on this project unfortunately. What happens when you try to wire a parent class user event and child class user event to the same output node in a case block?

I was thinking that he command class itself (parent--if the Receivers are all of the same type--or child level) can include any data we want, so this could be a Receiver object. (This means that the command sender knows the intended receiver in this case, which is often undesirable.) I haven't included a Receiver object myself (my applications haven't needed it) but I have included references in the specific commands, where each reference is a unique type, and really this information could be anything.

Ah, I think I understand. If the command subclass knows what vi has the additional information it needs to execute it's process, it can obtain a reference to that vi at runtime and acquire the data it needs from the fp controls. Am I close?

  • Like 1
Link to comment

The destroy user event seems completly wrong to me.

In LV7.1, you get a crash when calling a Unregister for Event after the User Event is destroyed. I guess that LV is now checking if the refnum is still active and discarding the destroy.

Furthermore I get error 1 when generating an Event that is already destroyed, which I use for my producer template as 'good' stop condition.

Felix

Link to comment

The destroy user event seems completly wrong to me.

In LV7.1, you get a crash when calling a Unregister for Event after the User Event is destroyed. I guess that LV is now checking if the refnum is still active and discarding the destroy.

Furthermore I get error 1 when generating an Event that is already destroyed, which I use for my producer template as 'good' stop condition.

I assume you are referring to the Destroy User Event prim? What is it that seems wrong to you?

I've never used 7.1 so I'm not familiar with what can and can't be done in it. 8.6 doesn't crash when unregistering for an event after it has been destroyed. It doesn't even throw an error. I suspect you're right about discarding the destroy.

Using error 1 to shutdown the producer is a convenient shortcut. I don't use it that way in my template because it limits the flexibility in how I can use the producer. (I developed this mainly with an eye towards using it with active objects, though it could also be used with single threaded applications.) I can't have my consumer stop receiving the events without shutting down the producer, which I may not want to do. Or, I might want to start up an active object without anyone receiving events and have it run in the background while consumers dynamically connect (register for a user event) and disconnect (destroy the user event) from it as needed. IMO it is a better general solution to have independent methods to start and stop the AO rather than linking it's execution to the user event.

I can imagine situations where you would want the AO to shutdown automatically when it is no longer needed. If you had multiple independent clients that had no knowledge of each other then none of them would know whether or not they should shut it down. In other languages this is typically done by having the AO keep an internal count of the number of clients who need it and automatically shut down when that reaches 0. It wouldn't be difficult to implement that in Labview.

Given that typical dev efforts often include aborting VIs and incomplete error handling, I frequenty find the AOs running with no way to shut them down. I've toyed with the idea of implementing a watchdog timer; when the timer triggers the AO would automatically shut itself down. Clients would have to reset the timer periodically to prevent the shutdown. I haven't implemented that mainly because I don't really want to burden the client with a parallel reset loop but the idea is floating around in the back of my head.

Link to comment

I remember when you posted that tip on the dark side, though I didn't know F. Schubert == Black Pearl. I read through the thread at the time but didn't fully understand it and didn't have time to investigate the code. Of course I'd completely forgotten about it when the time came for me to tackle the same problem! I'll have to dl the code and give it a look-see.

The problem with the unregister event prim is that it unregisters all the events for a given event structure. You can't selectively unregister the events. It seems to me that prim should be reserved for cleanup when the event handling code is exiting.

Your comments did get me thinking about a hole in this framework: What if the client exits without destroying the user event? The event manager could potentially maintain a large array of unmonitored events. I'm not sure if there's a good solution to this--I'll have to think on it for a while.

Link to comment

What happens when you try to wire a parent class user event and child class user event to the same output node in a case block?

It depends on how you wire it up. You can get a class conflict error on the parent event wire or you can get it to wire up correctly. It has to do with how you wire through the cases. If you wire the parent event through the FALSE case and the child event through TRUE, it will wire ok. If you do the opposite, you get a class conflict error on the parent event wire.

post-415-12638330524_thumb.png

I don't know what NI calls it, but I call it the master case rule. I don't know if my understanding is completely correct, but it seems that case structures (and probably others as well) have a "master" case that governs the type of a wire coming out of the case structure if other conversion/casting rules fail. The master case is normally the first one listed (FALSE, 0) when you drop the structure, but you can rearrange or relabel it after the fact.

-Scott

Link to comment

[quote name='Daklu' date='16 January 2010 - 07:45 PM' timestamp='1263692722' post='71183'

Not on this project unfortunately. What happens when you try to wire a parent class user event and child class user event to the same output node in a case block?

With the advent of DVR in 2009 it became necessary for terminals with typed references, that have objects as their data type, to permit the full hierarcy of decendants to be allowed to enter that terminal. But luckily, because it was required for DVR, every other type reference came along for the ride.

So whereas pre 09, you could not have an input terminal to a VI be a QRef with a parent class as its data and wire in a QRef w/ a descendant class as its data, you now can in 2009.

Link to comment

To unregister an user event dynamically, use the event registration refnum and wire the not a refnum constant to the appropiate event terminal. I havn't used that, so I can't detail the issues this might lead to. But there is an example either coming with the installation or online on ni.com, which uses several of the Mouse Events to draw lines on a picture control with unregistering/registering inside each of the event frames.

Felix

Link to comment

With the advent of DVR in 2009 it became necessary for terminals with typed references, that have objects as their data type, to permit the full hierarcy of decendants to be allowed to enter that terminal. But luckily, because it was required for DVR, every other type reference came along for the ride.

So whereas pre 09, you could not have an input terminal to a VI be a QRef with a parent class as its data and wire in a QRef w/ a descendant class as its data, you now can in 2009.

OH NO. This is VERY BAD. That's going to have to be fixed in LV2010. That's a severe bug and I had no idea it existed until today. The ONLY reference on which it is safe -- and by safe I mean "won't cause a crash" -- is the DVRs. Take a look:

post-5877-126391068144_thumb.png

Link to comment

For some more ideas, check that nugget of mine on the dark side.

I got a chance to look over your framework. As I said earlier, I'm not familiar with 7.1 so I can't fairly comment on the implementation. I suspect that it is a very good solution given the tools that are available in that version. Personally, I don't like the idea of a message bus at the application level. It encourages direct communication between application components, which doesn't scale well at all. Direct communication creates a lot of communication paths to monitor. If I have an application with 5 components, I already have 10 communication paths to keep track of. Adding a 6th module to satisfy some new requirement leaves me 15 paths to keep in my head. Pretty soon it just gets too complicated and impossible to figure out what's happening when. Direct communication also leads to dependencies between those components, which limits reusability.

I was debugging an app recently that used direct communication and one module was receiving a message that was causing it to go into an unexpected state. It was very difficult not only to figure out where the message was coming from, but what state the application as a whole was in that caused this bug. These days if I'm going to have (or event think I might end up with) more than 3 modules I use a mediator to centralize the communication. It's much easier to control what's going on in the app.

A little known fact is that you can register multiple Event Registration Refnums to an individual Event structure.

I did know about this, but I've struggled trying to figure out how to best use it. I don't want my modules to export a single registration wire for all internal events. The strong typing of the wire prevents me from adding new events to the module without breaking legacy code. I experimented with exposing a registration wire for each event but I didn't like that either, though the reasons escape me at the moment.

I really like the idea of encapsulating an Init event though; I had not thought of that.

-- and by safe I mean "won't cause a crash" --

I have noticed a lot of instability in 8.6 when using classes as user event types. I spend so much time experimenting with different ideas and changing things around I don't have a repro case or even a general idea of what might be causing it. I'll be sure to let someone know if I ever narrow it down.

The bottom half of your diagram also illustrates why my suggestion of keeping the registration wire strongly typed but allowing the user event wire to be polymorphic isn't feasible. Thanks for the info.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.