Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Visitor Pattern and AF

I am in the process of developing a new part of my code base. I have a large AF project for controlling a particle accelerator. I want to add a feature for the system (and subsystems) to run recipes. Here is the catch. I don't want to have to refactor the code for a new type of recipe, for the logic when a step in a recipe might be done, etc.

So, I was thinking I would use the "Visitor Pattern" or something similar. Essentially I want the system and subsystems to accept a recipe "visitor". The system/subsystems then pass their relavent pieces (enqueuers and reply type messages) to the recipe. The recipe is then responsible for running the recipe. I would just need to have the pieces to allow the code to accept a visitor. In this manner the system and subsystems would not need to know anything about recipes. The recipes can change, etc.

Has anyone done something like this? In other languages it seems like a pointer to the real object is given to the visitor. This would be different in LabVIEW.

How would I do this safely in AF.  Since it is a defined visitor it seems like it could be safe.

Thoughts?

Thanks,

CAsey

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

Message 1 of 11
(8,026 Views)

I recall that AQ was playing with variations of this pattern a couple of years ago and ran into some technical hurdles in the LV compiler...hopefully he can talk in more detail about his experience.

Message 2 of 11
(4,303 Views)

I think I should expand the description of my use case a bit in case my understanding is flawed, or someone has experience but calls it something else...

I want to run a "recipe".  The equipment settings for the step and logic for when a step in the recipe is done depends on the recipe step.  So I want to have the system run this recipe, communicate to pieces of equipment, tell them to do things, get updates from the equipment with regards to what they are doing, etc. The recipe will be responsible for knowing when to do the next step. A new recipe can be written with all differrent logic and the orignal system doesn't need to change.

So, first I was thinking that the system would accept a recipe visitor. What could happen here seems to be 2 things.

1. the recipe visitor asks to visit the system. As a result, the system gives the recipe the queues to the equipment and the recipe starts getting the messages and runs the system.

2. (separate alternative) the system has dynamic dispatch methods and the recipe visitor injects the concrete implementation into the system, for example "check for end of step logic" and the system receives the implementation from the visitor just like a strategy pattern.  In this example the default implementation may be to do nothing, so the system just runs.

#1 would have all sorts of implications with regards to AF and communication paths, reply messages, etc.

#2 seems like the strategy pattern.  I am not sure if this is wrong.

Thanks,

Casey

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

0 Kudos
Message 3 of 11
(4,303 Views)

> As a result, the system gives the recipe the queues to the

> equipment and the recipe starts getting the messages and runs the system.

Something doesn't make sense to me here... let me restate your sentence with the way I read it so it'll highlight why I'm confused:

> As a result, the system gives the recipe the enqueuers to the

> equipment and the recipe starts dequeuing the messages and runs the system.

There's no way that the recipie can be dequeuing from the enqueuers. No one outside the private levels of the framework has access to an actor's dequeuer. I read your second option thinking it might clarify what your first option was intended to say. But your second option seems to be addressing a different problem than your first option. Your first option seems to be an option for telling the equipment what steps to perform ("make art"). Your second option seems to be an option for telling the equipment how to perform each step ("if painter, deploy paint; if dancer, move; etc...").

If you can expand on your thinking here, I might be able to help.

0 Kudos
Message 4 of 11
(4,303 Views)

Here is what I am thinking:

I want to encapsulate the actions to be taken for each step of the recipe and the logic to determine when the recipe (or step in the recipe) is done.

All the elements that need it use composition and contain the Recipe class in their private data.

My system has a "Accept Recipe" method that has a Recipe class as the data.  The system has its own way of traversing its own structure to pass the recipe to all recipients (ie subsystems and components).

The system/subsystem/components then do something based on a Dynamic Dispatch (DD) method that is a member of the Recipe class. The system/subs/comps also analyze if they are done etc. using a DD method that is part of the recipe. In this manner the recipe steps and logic for when the recipe is done can be contained entirely in a concrete Recipe class that the system can take as an arguemnt.  The DD methods could take the self enqueuer reference as the input so that it could send messages to that actor (Change setpoint to X).

If I want to use a new recipe I make a new concrete instance and implement the methods that I need to and away I go.  I think I would put the system in a "Running Recipe" state when it is asked to run a recipe and the system is in a state where that is allowed.

Thanks,

Casey

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

0 Kudos
Message 5 of 11
(4,303 Views)

Hm... I started to sketch something up, but there's a ton of variations to a system like this, and I kept having to stop and add caveats like "I suggest ABC unless XYZ is true, in which case I'd go with DEF, unless...".  So let me give you a fairly general answer and see how it feels.

You use the phrase "All the elements that need it" -- I don't know what an element is. Is each "element" an actor? If so, why is Recipie in their private data? I ask because it sounds like each step of a Recipie should be a child of Message. Think about it -- you have a Recipie Execution Actor (maybe you have several such actors if there are several recipies to execute). That actor is responsible for sending Recipie Step messages (of various types) to the actors to execute. They execute and respond back to the Recipie Execution Actor with a standard "I'm Done" message. When REA recieves the "I'm Done" message, the REA sends the next step as a message (perhaps with a time delay).

0 Kudos
Message 6 of 11
(4,303 Views)

The elements are actors.  They are launched as nested actors.  The system launches the subsystems.  The subsystems launch components.

Here are a couple concrete examples for an ion source..

1. I want to condition an ion source.  So I write a recipe.

The ion source has 2 magnet power supplies, a gas flow controller, a magnetron microwave source, some thermocouples, and a gas analyzer.

I just want to step up the power on the magnetron from 1-10 in increments of 1 and not move to the next step until the gas analyzer says things are stable.

2. I want to run a full parameter sweep of the magnets and magnetron to optimize power density.

The magnets need to go each from 100-200A.

The Magnetron needs to go from 1-10

The flow needs to go from 0.5 to 5.0 sccm

A setting is tried and the output is measured once it is stable.  If no output then go to the next step.

What quickly happns is that users come up with new things they want to run with different settings, ranges, logic for when to move on, etc. So I want to have the recipe be responsible and I don't want to rewrite the system state logic internal to the system each time.

I was thinking composition so that I could have recipe methods like "Is Step Done?" called using DD in a template fashion. For instance, the ion source needs to have information from some or all of the components to execute logic to decide if a step is done. A status update method from a components could trigger the logic to get run.

Thanks,

Casey

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

0 Kudos
Message 7 of 11
(4,303 Views)

So each recipie is a set of instructions not to the top-most actor but is actually instructions for all the nested actors within the top-most actor. It isn't just "do this" but it is actually "do this in this way".

Interesting. Most of the AF has been designed around the idea that outside parties would have zero knowledge of the nested actors of any given actor, so the outside world talking to an ion source wouldn't even know that there even was a magnetron or a gas analyzer except as messages to the ion source for "Set your magnetron power to N".

But that does suggest a solution.

Your lowest level actors have messages that they can execute that are a single step, things like "Set your power to N" to the magnetron or "Tell me when you are stable" to the gas analyzer.

Now, let's talk about that "tell me when you are stable" message. The general way to write these types of messages is to Send the "Tell Me When" message to the actor and include in the message's payload *another* message that is the one that the actor should send back. In other words, you send a message to the gas analyzer "Tell Me When Stable", and inside the payload of the gas analyzer is a message intended for the original sender (and sometimes the enqueuer of the sender if the Tell Me When is sent by anyone other than the caller). When the gas analyzer stabilizes, it sends that message back to the caller.

Ok, that response message could be *anything* that the sender wants. So, what if you did this:

Create "Perform Recipie Step" method on Ion Source. Then create a "Perform Recipie Msg". The recipie method contains an array of recipie steps. The Do.vi for Perform Recipie Msg will remove one element from the array -- actually modifying the message class itself -- and execute that step by calling Perform Recipie Step. Perform Recipie Step is responsible for doing the work of that step and then *either* re-enqueuing the Perform Recipie Msg or giving the Perform Recipie Msg to a nested actor to send back to it for any asynch work.

Yes? No?

0 Kudos
Message 8 of 11
(4,303 Views)

I like the idea of the array of steps in the recipe.

The recipe is tighlty coupled to the subsystem, so I feel it is OK to know what the nested components are.  Outside of running a recipe the subsystems generally receive state change messages from the system so that the subsystems can be replaced with another concrete instance that adheres to the interface.

I use the "zero coupling" style for sending messages to caller. The components do not know who they belong to, just that they send a message with some data to their caller. I was considering that the recipe visitor could send the data to the recipe, but I think that is wrong. I want to run a "Recipe Step Logic" method within the subsystem when a component sends a message that it has changed state. The parent class for recipe would have an abstract method with no action taken. The step of the recipe would have an override of the state change check logic. The signature of this method would accept the component states and any actuals needed (I have those in an "Actuals" class) and an output indicating that the step is complete. Then a method to run the next step would get called.

To make this more complicated you can have a recipe that covers multiple subsystems and that it would need a higher level of coordination to involve the "Ion Source", "Accel Column", "Focusing Lens", "Target", etc. This has the chance of being "the" architecture and all modes of operating could just be recipes.

In my implementation the visitor is not an actor, so it isn't sending messagees or receiving them.  It is much like a strategy. I found this non-LabVIEW post helpful in thinging about it:

http://aviadezra.blogspot.com/2008/12/design-patterns-visitor-vs-strategy.html

Casey

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

0 Kudos
Message 9 of 11
(4,303 Views)

I'm going to bow out of this thread at this point... I've got work on my end that needs more focus. 🙂 I hope I've given you enough brainstorm fodder that you can move forward.

0 Kudos
Message 10 of 11
(4,303 Views)