Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Reducing the number of messages for a UI Actor

I was wondering if there was a better way to solve this problem.

When building a UI with AF, the examples I have seen appear to have an event loop send a message to self.  That message is handled by the UI actor running in parallel with the event loop.  The message ‘Do’ then sends other messages to other actors as needed.

The UI actor is the only thing that has access to state data for the UI so it makes sense to pass a message to the UI actor so it can update state data before passing the message up to the parent that performs most of the control logic.  But this means that for every user action, there must be a minimum of two message classes created.  One for the UI Actor and another to send from the UI actor to the parent controller.

I am wondering if there is a better way to do this so that we can skip the double messages.  Has anyone come up with a cleaner solution?

-John
------------------------
Certified LabVIEW Architect
0 Kudos
Message 1 of 13
(7,275 Views)

Without a definition of "cleaner solution," here are some things you could do:

  • You could create a single message class and use string/variant messaging from your event loop to your UI Actor.  Case out on the string and implement all your forwarding code in that one message.  You'll lose type safety and increase the opportunity for run time errors.
  • You could hack the framework and implement the transport with user events instead of queues.  You'll lose the framework support from NI and probably a lot of time when you run into something unexpected with events.
  • You could shift all your state data to the event loop instead of the Actor loop.  That would allow you to send messages out from the event loop directly.  You'll need to still have a helper loop dequeuing incoming messages and packaging them in user events, but if you have lots of outgoing messages and relatively few incoming messages it will reduce the number of duplicate messages.
  • You could abandon hierarchical messaging and use mesh messaging instead.  You'll lose some of the ease of reasoning about your application and may introduce race conditions.
  • You could build more functionality into each actor, making them bigger and more complex.  In most cases that will reduce the number of messages you have to pass around.  More "stuff" going on in the actor makes it harder to understand.
  • You could send a copy of the event loop Actor to the UI Actor and read stale data.  (I'm kidding... don't really do this.)

The nature of command pattern messaging means every message requires a unique class.  As soon as you diverge from that you're giving up some of the safety built into the AF.  What are you willing to give up in exchange for fewer message classes?

Message 2 of 13
(4,734 Views)

If Daklu hadn't beaten me, I was going to suggest his third option.  Move the message handling into the event loop.   -- James

BTW added later: this is one of the reasons I suggested an alternate class structure for queues; so that you could have an alternate actor design based on a User Event as message receiving mechanism, with actors of different types still able to communicate with each other.

Message was edited by: drjdpowell

0 Kudos
Message 3 of 13
(4,734 Views)

How about using a Time-Delayed Repeating message that contains the event structure with a 0 timeout?

Or have the Event message send itself with a short timeout?

You just have to register all control events into dynamic events.

Ryan Podsim, CLA
0 Kudos
Message 4 of 13
(4,734 Views)

In my experience, I think that maintaining state data in the UI event loop makes a lot of sense.  There could be numerous events that are handled within that loop which require knowledge of the state data, but do not involve any incoming our outgoing messaging.  Outgoing class-based messages can be sent directly from the event loop to the parent Controller Actor.

So, at this point you have the UI Actor receiving incoming class-based messages in the actor loop, and forwarding them along to the event loop using user events.  The event loop is maintaining its own state data and sending class-based messages directly to the Controller Actor as needed.  If you are being thorough about the communication between the UI Actor's actor loop and event loop, maybe you've created an interface class which encapsulates the sending of user events.

At this point, why bother creating a UI Actor at all?  You have already created an interface class to send event-based messages to the event loop, and the event loop can easily send class-based messages back to the Controller Actor as long as it has a copy of the correct enqueuer.  Why not take out the middle man of the UI Actor actor loop, and simply let the Controller Actor send outgoing messages using the UI interface class?  One additional method could be added to the UI interface class to dynamically launch the event loop initially.

Message 5 of 13
(4,734 Views)

Make the UI Actor a child of the business actor and only override methods that will update the UI. Of course, swapping UIs is restricted to launch time - but maybe if you stop the actor, pass the final (stale) state to another launch ...

0 Kudos
Message 6 of 13
(4,734 Views)

I gave some thought to moving the UI control logic from the incoming message Do.vi to the event loop.  My main concerns with this is loading the event loop up with extra code that can make it less responsive.  But, since it currently messages the UI Actor when an event occurs, it is still technically blocked until the UI actor is free.  I also am concerned about having to implement error handling outside the Do.vi in the event loop and all the issues that will bring up.

One benefit of having the event loop message the UI actor is once you create the message for the action, you can map many different controls to that action easily.  In my came many user actions can be done via toolbar, menu selection and right-click menu.  My event loop can capture each of those and simply send the same message to the UI Actor, who can then process them appropriately and send a message to the Parent as needed.

But the primary concern is several of my UIs will have 10 or more user actions.  This results in 20 or more message classes.  And I expect to have 5-6 UIs in the application which means I am likely to have at least 100 message classes just for user action input alone.  And that does not include the message classes for sending data to the UIs!

Another issue I am dealing with is trying to build a test framework for each UI.  Currently, where the UI Actor would send a message to the Parent I am instead sending that message to a test harness to display the data from the UI Actor. This means that all the Do.vi code for these messages is currently customized to the test harness and not what the parent in the real application will do with the data.  This is fine for dev work as I can edit those message classes later when I implement the parent, but that will permanently break my test harness.  I would like to keep both functionalities in place for future development work.  The only idea I came up with was to subclass the parent and test harness to a common ancestor with abstract methods that the message classes would use and then have the test harness and the normal parent implement the concrete methods.  But the problem I run into there is the parent will need to deal with multiple different UI actors and I cannot have it inherit from multiple different ancestors to give it access to the all the possible methods for those UI messages.

As Dave said, maybe this is all just the price of command pattern messaging systems.

-John
------------------------
Certified LabVIEW Architect
0 Kudos
Message 7 of 13
(4,734 Views)

jlokanis wrote:

I gave some thought to moving the UI control logic from the incoming message Do.vi to the event loop.

Don't know about the others, but I at least was suggesting you actually call "Do" on your incoming messages inside the event loop.  Bypass the inner "Actor Core" parent completely.  Don't know if you do that without modifying the framework, though.  You'd need a helper loop to dequeue messages and forward them via user event.

One benefit of having the event loop message the UI actor is once you create the message for the action, you can map many different controls to that action easily.  In my came many user actions can be done via toolbar, menu selection and right-click menu.  My event loop can capture each of those and simply send the same message to the UI Actor, who can then process them appropriately and send a message to the Parent as needed.

With the "Actor" object in the event loop, you can just call a method on it directly.

0 Kudos
Message 8 of 13
(4,734 Views)

drjdpowell wrote:

jlokanis wrote:

I gave some thought to moving the UI control logic from the incoming message Do.vi to the event loop.

Don't know about the others, but I at least was suggesting you actually call "Do" on your incoming messages inside the event loop.  Bypass the inner "Actor Core" parent completely.  Don't know if you do that without modifying the framework, though.  You'd need a helper loop to dequeue messages and forward them via user event.

That's pretty much a no-go in the AF since the actor data is all up in the parent's Actor Core and the framework rules it out from being anywhere else -- no one else but that loop can dequeue messages from the queue.

0 Kudos
Message 9 of 13
(4,734 Views)

jlokanis wrote:

But this means that for every user action, there must be a minimum of two message classes created.  One for the UI Actor and another to send from the UI actor to the parent controller.

I am wondering if there is a better way to do this so that we can skip the double messages.  Has anyone come up with a cleaner solution?

You just described the cleaner solution. 😉 The dirty ones involve variants and flattened strings. I think Daklu's list is pretty comprehensive, except for Todd_Lesher's solution, which actually does cut your number of message classes in half. It's my preferred solution, but it only really applies in applications where the UI and the model have the same lifetime.

0 Kudos
Message 10 of 13
(4,734 Views)