Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

message object linking between applications

Solved!
Go to solution

I am trying to find a way to limit the number of dependency links between two applications that send message objects to each other.

Let’s call them Client and Server.

In the Client, when I receive a message externally, it is processed to decide what internal actions need to be taken.  In many cases, it might send a message internally to one or more places to implement the required behavior.  This additional messaging is does when the message is executed in the receiver.  So, the message class has static links to all the other messages it needs to send.  And if those messages in turn send more messages through the hierarchy, then they to have static links to more messages.  Also, if I call methods in other classes within those messages, those classes are also statically linked.

In the Server, when I want to send the message to the Client, I must use the message class to construct the message and set its attributes (data).  This requires the server to have a copy of the message class.  But due to all the static links mentioned above, almost every message class in the Client is now a dependency of the Server.

My question is: How to break the link?  I want to send the message as an object.  I have already created the code to send an object of type message (ancestor).  This allows my transport code to work with any message.  So, I don't want to create a special case for the data of each message I want to send.  But I do want to un-link the entire message class hierarchy in the receiver from the sender.

thanks for any thoughts.

-John

-John
------------------------
Certified LabVIEW Architect
0 Kudos
Message 1 of 4
(4,690 Views)
Solution
Accepted by topic author jlokanis

Your timing is excellent.  I just presented on this very issue yesterday at the European CLA Summit....

Go check out https://decibel.ni.com/content/docs/DOC-24187, Actor Framework on CompactRIO.  It's a cRIO example, but the techniques apply to all instances where you have to cross the application instance boundary.

The short answer is that you you embed your message transfer mechanism in a remote proxy actor.  That actor must be able to receive all messages that cross the network boundary.  All that actor knows how to do is forward messages across the network.  Make children of that proxy specific to the local application, and override the methods that need to act locally with whatever you need to do.

You load the children onto their respective targets and use them as if they were the local version of whatever remote actor you want to message.  Because only the children call local code, you break the cross-application dependencies.

The parent proxy class remains locked, as does whatever you are using for remote transmission.  But that is a much smaller and much more manageable subset of your total code base.

The linked example shows how to do this between a UI on the PC and the corresponding actor on the cRIO.

Pro Tip:  Messages the proxy receives over the network do not automatically load into memory.  (Any messages you *send* from a local actor are loaded, but the proxy is used on both sides of the boundary).  To get around this, be sure to put a class constant for each message on the block diagram of the proxy's Actor Core.vi (nothing special about Actor Core, but you have to have it anyway, so it's a handy spot.)  That will ensure that all messages for the proxy are loaded.

Feel free to hit me up with any questions you may have about the example.

Message 2 of 4
(3,101 Views)

Thanks for the reply.  I managed to solve the issue with something basically like your suggestion.

In my case, I made an abstract class to store the specific implementation classes in my message system.  I did this so my architecture would remain a generic solution for future projects.

I then made a NOP child class that contained a 'Do' method for each message that needed to be sent between applications.  These were dynamic dispatch methods that did not have any actual implementation.

All messages that needed to be sent between applications would access the abstract class from the system data, cast it to the NOP class and then call the NOP method for that class in their 'Do' method.

I then made a child of the NOP class for each application. (In this case, a Client and a Server) In these classes I placed the actual implementation of the message 'Do' for the messages that were handled by that application.

When each application initialized, I provided the system with the child that that implemented the messages that application could receive to the property that stored the parent abstract class that my NOP inherited from.

The result was the message would only 'Do' what the particular child class of the NOP class had implemented.  My common message now had a different implementation (and therefore different linking) depending on which application it executed in.

Here is diagram.  Pardon my pseudo-UML...

Remote Message Linking Solution.png

Thanks also to the guys over at LAVA for help sorting this out.

-John
------------------------
Certified LabVIEW Architect
Message 3 of 4
(3,101 Views)

I think that I do something similar.

For actors that I need to use a lot, I create an associated "View" actor. I guess I borrowed the term View from the Model View Controller paradigm, but I am not sure if it is the right term to use.

Anyways, I add an Actor Enqueuer object to the private data of the main actor I want to make reusable and I call it "View Enqueuer". Once I have coded up my main actor (the parent or any descendant), I access the View Enqueuer (with an accessor so descendant classes can also get to it) and I send messages to it in spots where my main actor needs to broadcast information. In parallel, I develop associated messages in my View actor that the main actor can use. For instance, if my main actor has two main updates to post (update_my_socks and i_drank_7up), I will create two messages that the View actor responds to (broadcast_update_my_socks and broadcast_i_drank_7up). The main actor can then send those two messages to the View Enqueuer from any part of my actor code (naturally, it is best to send messages right after getting new data that pertains to them). By decoupling the main actor from the view, I decouple the methods that produce the updates and the methods that will send those updates to other processes or a GUI. In my combination of main actor and view, I can switch out any of the two with any of their descendants. This separates the implementation of the update production from the implementation of the broadcasting of those updates.

This system comes in handy when I have a new device I want to work with. This device produces data. I first write a main parent actor that has the basic methods and private data. I then write a parent view actor that the main actor can connect to broadcast information. Then I write an descendant of the main parent actor that emulates the device. I then write a descendant of the parent view actor that writes information sent to it by a main actor to a GUI. I perform my initial connectivity test: I launch my GUI view, connect it to my emulator main actor, and launch the emulator main actor. I can see the front panel of the GUI View updating with the emulated data: it works. I then write my hardware implentation of the main actor. The only change I need to make to my initial connectivity test is to swap the emulator main actor with my hardware main actor. I initialize my hardware main actor, and I launch the test: I see the front panel of the GUI View updating if I wrote my hardware implementation correctly. This is the beauty of this system: I did not write an emulator main actor or a hardware main actor that was associated with writing data to a GUI. I only wrote the main parent actor descendants to represent a certain implementation of how the device works. This is key: to integrate everything into larger application that sends the information over a network, to three GUIs, and to a data processing block, I do not need to change my emulator or hardware main actors. I just need to write a new view that is aware of of the larger test app and what it needs to broadcast where. The implementation of the view is application specific. The main parent actor descendants are totally decoupled from anything they will be indirectly broadcasting information to.

Who thinks what of this system?

0 Kudos
Message 4 of 4
(3,101 Views)