Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Creating a Sequencer in AF?

Solved!
Go to solution

I have an actor framework project that I'm trying to clean up, and its current state feels like a spagettii code mess of messages being sent between all the actors, so it's hard to see the sequence of events that makes the system run. I'm wanting to clean it up, but I'm having a hard time figuring out how to cleanly document/describe/execute/modify a sequence of events/messages in an actor system.  

 

In my research I found this article, but it's pretty old, and wanted to know if there are some more up-to-date references or design patterns I should be looking at?

https://forums.ni.com/t5/Actor-Framework-Discussions/Sequencial-steps-in-an-actor-design/td-p/345484...

 

How are you implementing synchronous sequences among asynchronous actors?

 

Cheers,

Nathan

Systems Engineer
SISU
0 Kudos
Message 1 of 26
(2,223 Views)

Do you need each sequence step to be an actor? Or could they just be regular old objects? Usually my sequencers don't need each individual sequence step to have its own message handling capability, with the exception being a potential "abort" step.

 

For a very simple example, you could have a "Sequence step" object with dynamic dispatch "Setup", "Run", and "Teardown" methods that you could override with specific tests.

0 Kudos
Message 2 of 26
(2,212 Views)

I actually have a sequencer actor in a current project.  It's a little primitive - no branching, looping, or nested sequences yet, and I need to rework how I dispatch steps before I can have those things, but I know what I need to do.

 

The sequencer is the top level actor, though I don't think it needs to be per se.  It is a state pattern actor, using the State Actor class available on the Tools Network.  That gives me access to pre- and post-step operations, as well as step-specific data.  Steps also have an Action.

 

Simple steps breeze past pre-step, execute their Action methods (by sending messages to nested actors), and then transition to the next step.  Those actually work for most of your interactions with your nested actors.  For steps that need a response, send the message to the nested actor from the pre-step, and then wait.  The nested actor can send an announcement when it has done the thing that invokes Step:Action.

 

The latter type of step may have a fair amount of conceptual (if not actual) coupling with its corresponding nested actor, but you should be able to keep the nesteds decoupled.

 

I'm using it on a pretty small system, but it should scale, at least to the point where you're probably better off using TestStand.

0 Kudos
Message 3 of 26
(2,210 Views)

At some point, I do plan to make my sequencer available as a community package, maybe after I get branching and looping done.

0 Kudos
Message 4 of 26
(2,204 Views)

Do you need each sequence step to be an actor?


No, the individual steps don't need to be actors themselves.  My system is setup where the actors are long-lived, where I launch them, and keep them running for the life of the application. If it helps, they are hardware actors, representing the hardware resources I need to sequence.

 

Thus I have top level "coordinator" actor, which contains the logic, and a bunch of nested actors, and I want to run through a sequence, which sends messages and awaits responses from those nested actors before moving to the next step. Or handle branching the sequence when a nested actor throws an error up through "Handle Error.vi" (for example).  

 

At the moment, this "coordinator" actor, just sends a message, and then when it gets the response, the message .do sends the next message, etc. and this results in a very long chain of messages being sent, and I have to hunt through each message sent and received to know where I'm at in the sequence. This is what I'm wanting to clean up.

 


For a very simple example, you could have a "Sequence step" object with dynamic dispatch "Setup", "Run", and "Teardown" methods that you could override with specific tests.


Yes, so this would involve making an OOP-based command pattern, where you execute each step. But how would you integrate this inside of an actor?

 

 

Systems Engineer
SISU
0 Kudos
Message 5 of 26
(2,178 Views)

Hey justACS, thanks for your reply. It's been a while!

 

Using the State Pattern Actor is definitely one approach I was considering, it consolidates the actor itself with a state machine style sequencer. In a plain LabVIEW state machine, the entire sequence is in just one case structure (each case a different state), so it's easier to see and modify the whole sequence. Whereas with AF, that state machine get's spread out across many LV classes and VIs, and that's my main reservation to this approach. They are still just state/children actors yes, but I was hoping to have something where it was easier to see the entire sequence of events in the code somehow.  Hence why I was asking about design patterns, there's gotta be something other than TestStand (which we are using for a different project, but not an AF-based one at least).   

 

I do like your idea of using the pre-step to send the messages and then have the action messages just wait for the responses. Very clever use of the framework that's already there, but I feel the State Pattern Actors are better suited for actually implementing a state machine, where you need to respond to the different messages in different ways depending on the state. I was planning on using State Pattern Actors for this, so the top-level actor can maintain a "system state" for the whole system.  So then by doing that, I would lose the ability to use the same actor for the sequencer, so the nested-sequence problem you mentioned I would immediately run into. 

 

Any other ideas? I'd love to keep this discussion going to brainstorm a novel way to do this, if one doesn't already exist. 🙂

Systems Engineer
SISU
0 Kudos
Message 6 of 26
(2,172 Views)

@Nathan-P wrote:

 

Yes, so this would involve making an OOP-based command pattern, where you execute each step. But how would you integrate this inside of an actor?

Your coordinator actor could hold an array of "Sequence step" objects and index through the array to progress the sequence. When you initialize the array, you wind up with a clean looking list of object constants on your block diagram. This also feels like a pretty flexible approach because you can assemble the correct sequence based on the particular test you need to run - or maybe pass the sequence to run as a parameter in the message that starts running the sequence.

0 Kudos
Message 7 of 26
(2,143 Views)

@zsmorison wrote:

 

Your coordinator actor could hold an array of "Sequence step" objects and index through the array to progress the sequence. When you initialize the array, you wind up with a clean looking list of object constants on your block diagram. This also feels like a pretty flexible approach because you can assemble the correct sequence based on the particular test you need to run - or maybe pass the sequence to run as a parameter in the message that starts running the sequence


That's pretty much what I do, except the sequence steps are child classes of the base sequencer actor.  Also, I've discovered that an array is an inadequate structure for tracking progress through the sequence if you want subsequences or branching and looping.  I'm going to be switching to a more complex aggregator that maintains an internal stack of subsequences.  The top of the stack is your current sequence, the next one down is your parent, and so on.  I haven't worked out the details yet.

 

Also, consider looking at Bowzer the Browser; it may be helpful for viewing your sequence steps.

Message 8 of 26
(2,132 Views)

@justACS wrote:

Also, consider looking at Bowzer the Browser; it may be helpful for viewing your sequence steps.


👍🏼 that's a good reminder to continue making some feature updates as well...

CLA CLED AF Guild
0 Kudos
Message 9 of 26
(2,125 Views)

@justACS wrote:


Also, I've discovered that an array is an inadequate structure for tracking progress through the sequence if you want subsequences or branching and looping.


Agreed, if the level of complexity warrants a more complex sequencer. My current use cases are relatively simple, so indexing through an array and checking the output (pass/fail) of each step works for me.

 

In the past, I've handled more complex sequencing that includes subsequences and branching logic by using the decorator pattern to create a "macro" object that inherits from the "sequence step" object. Internally, the macro contains its own list of "sequence steps" and the logic to iterate through them. At the level of the coordinator actor, the macro looks just like another sequence step that produces an overall step result. This makes grouping and reusing a common set of sequence steps very easy. For example: a generic file transfer macro contained a list of individual sequence steps to 1) initiate the file transfer, 2) monitor the transfer status, and 3) verify the file checksum at completion. From the perspective of the sequence coordinator, those three things looked like one step. This was really useful if that file copy operation failed and needed to be retried. The coordinator just initiated a retry of the file copy macro, and didn't need to know any of the details to reset the state of the macro. The logic internal to the macro took care of resetting it's own state and restarting itself at step 1.

 

That was a different job and different language, so I don't have any code snippets to post here though.

0 Kudos
Message 10 of 26
(2,123 Views)