Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Substitute Actor.vi: Child to parent?

Daklu wrote:

RunState is kind of a pseudostate in your design--you're never in the RunState without also being in the RunEnabled or RunDisabled state.  In theory you shouldn't need to instantiate it.

You are right, it is a sort-of-pseudo-State, mostly it's there to hold functionality common to its sub-States. However, it does need instantiating during State transitions when its Exit/Entry actions need to be called -- you need an instance of the class to be able to dynamic dispatch Entry.vi and Exit.vi.

0 Kudos
Message 11 of 21
(2,437 Views)

AristosQueue,

Data preservation of State data is something I have been thinking about. The use case I had in mind was persisting State data internally: I am considering including a Memento (GoF pattern) type class. There would need to be a Memento store -- some sort of hash-table (variant attributes maybe) -- which is accessible to all States. States could then store any internal data in a Memento object and write it to the store in its exit action and read it back in the entry action.

For the case of sharing data from one State to another my first thought would be that it should be packaged up in the event. The transition to FaultState is an interesting one since the error "event" is not an "event" at all but an error on the error-wire which is picked up by the Actor error handler (where it is delegated to State:Handle Error.vi). The transition occurs inside the error handler such that errors are handled atomically. I did think about the error handler packaging up an error "event" and using the send-to-self enqueuer with the priority set to high. Then I got concerned that there would be nothing preventing another message/event coming in with a higher priority -- relying on message priorities and order of messages arriving feels like a slippery slope!

This is all still very much at the concept stage -- any comments would be welcome.

P.S. Reading this thread back it is not entirely clear that the whole state machine shebang is inside an Actor. When I have talked about events these are in fact messages sent to the Actor (event is a poor choice of terminology here but consistent with more general state machine discussion). The Actor in question contains a State object and all actions are delegated to State.

0 Kudos
Message 12 of 21
(2,437 Views)

I wondered about the Entry/Exit thing, but I didn't want to start making assumptions.

You actually don't need to instantiate a RunState object to call those methods--just use the 'Call Parent Method' node in the RunEnabled.Entry, RunEnabled.Exit, RunDisabled.Entry, and RunDisabled.Exit methods.  As long as RunState.Entry and RunState.Exit don't also call their parent (and I don't think there's any reason they should,) the call chain will stop right there.

See the attached project for a quick and dirty example.

0 Kudos
Message 13 of 21
(2,437 Views)

Daklu wrote:

I wondered about the Entry/Exit thing, but I didn't want to start making assumptions.

Ask away! Just talking about it helps clarify it in my mind and hopefully will lead to the worst assumptions becoming obvious.

I can't open your example as I've only got LV2012 at the moment but I think I understand what you're saying. It will work fine transitioning say from StandbyState --> Run.DisabledState. The sequence of entry/exit actions would be StandbyState:Exit, ((RunState:Entry), Run.DisabledState:Entry) -- the brackets are intended to show that RunState:Entry is actually called using the call-parent method inside Run.DisabledState:Entry.

It breaks on the transition from Run.DisabledState --> Run.EnabledState (and the other way). Now the sequence of entry/exit actions would be ((Run:Exit), Run.Disabled:Exit), ((Run:Entry), Run.Enabled:Entry). The RunState entry/exit actions are now being called incorrectly since they are hard-coded on the block diagram of the sub-States entry/exit actions.

Currently, working out which entry/exit actions need calling is done by finding the least common ancestor of the source and target states (a VI does this). Then stepping up and down the hierarchy, instantiating each state (using a factory method) and calling the entry/exit methods.

Users of the framework have very nothing to do with this: they create their states (obviously after carefully drawing the state diagram ), instantiate the state factory with them and off they go. From experience, with the last incarnation of this framework, state heirarchies are often <4 layers deep e.g., there may likely be sub-states of Run.EnabledState. Typically the state machine spends most of its time sat in one of the run.Enabled sub-States.

So far, I haven't worried too much about performance of state changes. I suspect it's not blazing but then again transitions don't happen that often. The main purpose of the framework is to be able to implement a hierarchical state machine (with all the associated benefits)... I started off with the JKI state machine (and other "QSMs") and used them for a while until I burnt myself. Personally, I find them unreadable with all the macro-states and also very easy to create simultaneously executing, possibly interacting, loops with self enqueued messages, aghh! Since LVOOP came along, and I had a bit too much GoF kool-aid, I've been using various implementations of state machines where the state is represented by an object which is part of an inheritance heirarchy. This is my latest push toward a UML-ish HSM. Things I'd like to get in this attempt: private state data, orthogonal regions and history pseudo-states. Private state data: heading there; history, do-able; orthoganal regions: inklings based on launching them as sub-actors and using the Actor:Recieve Message.vi I just heard about in this thread (http://https://decibel.ni.com/content/message/67347#67347) to dispatch messages to them!

0 Kudos
Message 14 of 21
(2,437 Views)
St3ve wrote:

It breaks on the transition from Run.DisabledState --> Run.EnabledState (and the other way).

Yep, I overlooked that.  I've created a quasi-HSM a few times, but I don't remember if I used class inheritance to implement it.  I think I did the first time, then ditched it the second time because of my experience the first time.  Personally I've never been very satisfied with any of the HSM implementations I've done.  It seemed like they were more complex than necessary.

St3ve wrote:

...then stepping up and down the hierarchy, instantiating each state

Interesting idea... I hadn't thought of that.  The way I implement state machines the Exit Actions (and Transition Actions, if you have them) can easily know what the next state will be (since that decision is made as part of the Do Actions, which occur before the Exit Actions) so it's no big deal to drop 'Call Parent Method' in a case structure and run it only when you're actually leaving the RunState.  You can do the same thing for Entry Actions if you are willing to add a previous state field, but I'd understand if that feels wrong to you.

But it sounds like your current design is close to where you want it, and all you need is to figure out how move the data back up the state tree--just like your original post said--and Stephen and Allen have already explained that part.

St3ve wrote:

I started off with the JKI state machine (and other "QSMs") and used them for a while until I burnt myself. Personally, I find them unreadable with all the macro-states and also very easy to create simultaneously executing, possibly interacting, loops with self enqueued messages, aghh!

Actually my objections to the JKI SM are almost entirely stylistic.  It solves some of the major structural problems of traditional QSMs, but I find the business logic hard to follow and prefer to simply string sub vis together.  I'm not so much a fan of traditional QSMs though.

It's interesting you mentioned macros and self-messages.  I'm presenting at the CLA summit next week and those are two of the common QSM techniques I talk about that don't work particularly well with actors.  See you there?

St3ve wrote:

and also very easy to create simultaneously executing, possibly interacting, loops

You mean where multiple independent sequences are mixed together in the queue and the loop kind of flips back and forth between executing them?  I call it meta-instability.  (Clunky name, I know... haven't heard a better one yet.)  That can be very difficult bug to discover, and even harder to fix.

0 Kudos
Message 15 of 21
(2,437 Views)

Daklu wrote:


Personally I've never been very satisfied with any of the HSM implementations I've done.  It seemed like they were more complex than necessary.

There is certainly a good deal of effort involved in developing the framework but well worth it -- I've been happy enough with previous iterations. Entry/Exit actions that are guaranteed to execute are invaluable e.g., Run.EnabledState:Entry sets big-dangerous-motor-drive's enabled line TRUE and Run.EnabledState:Exit sets it FALSE and is guaranteed to run however we exit Run.EnabledState (nice warm fuzzy feeling). The hierarchical nature curbs the state/transition explosion inherent with  "flat" state machines without resorting to flags in the state data (a sure sign another State is needed!).

In my early iterations I had case structures around call parent methods for entry/exit actions but I found it was: (1) complex to maintain and (2) error prone. That led to the approach I outlined before.

Daklu wrote:


You mean where multiple independent sequences are mixed together in the queue and the loop kind of flips back and forth between executing them?  I call it meta-instability.  (Clunky name, I know... haven't heard a better one yet.)  That can be very difficult bug to discover, and even harder to fix.

Exactly! I thought at one stage my meta-mess had achieved sentience it was so hard to debug! Not an argument against QSMs but certainly a gotcha to watch.

Daklu wrote:


Actually my objections to the JKI SM are almost entirely stylistic.  It solves some of the major structural problems of traditional QSMs, but I find the business logic hard to follow and prefer to simply string sub vis together.  I'm not so much a fan of traditional QSMs though.

As a long time lurker I have seen several of your posts about QMHs and QSMs and couldn't agree more on the important distinction. I would also agree that any QMH that has "shifter-data" can be a state machine by storing some sort of state-variable whose value is defined by the history of inputs. Then the response to future inputs can be modified based on the value of that state variable. However, I think the problem is essentially most LabVIEW state machines are not very rigorous: a mish-mash of boolean flags, enums etc. are used; state is often not explicit. Also non-object-orientated designs tend to end up with monolithic/a lot of case structures to maintain.

In general, I would say more confusion arises from self-enqueued messages leading to hidden loops and what you describe as meta-instability (regardless of being a QMH/QSM distinctions).

The problem I found when using the JKI SM (I was learning LV at the time) was that it seemed to encourage combining "states/actions" through macros rather than creating sub-VIs and chaining them together as required. Subsequently, I moved away to more explicit state machines...

EDIT: Yes the JKI QSM does solve many problems by implementing a public queue (via the events structure) and a private "task" queue. I think the importance of this is often overlooked.

Daklu wrote:

I'm presenting at the CLA summit next week and those are two of the common QSM techniques I talk about that don't work particularly well with actors.  See you there?

Unfortunately, the trip from the UK is expensive (although I wouldn't mind the holiday) also my current level of certification is lapsed CLAD, I'm probably not qualified to even place a LV object constant on the BD!

Message was edited by: St3ve

0 Kudos
Message 16 of 21
(2,437 Views)

St3ve wrote:

There is certainly a good deal of effort involved in developing the framework but well worth it -- I've been happy enough with previous iterations.

I assume your HSM framework is written for the AF and each state inherits from a base State class, which in turn inherits from Actor.  The AF doesn't--as far as I know--support implementing a state machine directly in the message handling loop very well.  Do you implement the state machine as a helper loop inside another actor?

0 Kudos
Message 17 of 21
(2,437 Views)

Yes, it is written for Actor. In many cases I think the "default" Actor should be a state machine (SM). For example, I have an abstract Actor which expects to have "registered" sub-Actors. On receiving a Stop message it sends a Stop message to all its registered sub actors and waits for their Last Acks. Really, to prevent inadvertently allowing messages (other than Last Acks) to execute during the shutdown phase, even this basic Actor would be better as an SM.

My "State Actor" implementation actually works like this: Context inherits from Actor. Context has a State object as part of its private data and defers all actions to State. Context also defines a method for transitioning between Sates.

In my current experimentations, I tried the approach you described, swapping between a family of Actors (States) at run-time using an extension of Actor:Substitute Actor.vi for the state transitions. Essentially the same as https://decibel.ni.com/content/thread/15069 but with some more logic in the transition states VI to account for hierarchical states. There is some discussion in the linked thread, but the main problem I encountered was that some of the Actor methods such as Handle Error.vi and Stop Core.vi have DD output terminals which prevents changing State (by changing the Actor) inside them. This was a show stopper as it effectively prevents handling the error event (or executing guaranteed state transitions on exit).

When implementing state machines inside an Actor one of my gripes has been the way a "stop" event is asserted (by using an error). From a state machine perspective a "stop" event and an "error" event probably want to be handled differently so you need to put code in place to separate the two.

0 Kudos
Message 18 of 21
(2,437 Views)

Daklu wrote:

The AF doesn't--as far as I know--support implementing a state machine directly in the message handling loop very well. 

I'm missing something. The AF is a state machine as far as I can tell. As messages arrives, the actor transitions to different states and responds differently based on the current values of the fields. That can be implemented either as fields within the actor or by trading out the actor using the State Pattern. What are you thinking is missing?

0 Kudos
Message 19 of 21
(2,437 Views)

AristosQueue wrote:

What are you thinking is missing?

I don't think anything is missing (except perhaps simplicity.)  When I said the AF doesn't support implementing a state machine directly in the MHL, I was thinking of the way I often implement state machines in the message handler:

Capture.PNG

There's a message handling loop, and the entire state machine implementation is right there.  That's what I meant by "directly."

In the AF the MHL is implemented in Actor.ActorCore.  Child classes can override it, but since they are required to invoke the base class method they cannot replace the MHL implementation with their own to do something similar to what I do.  It is certainly possible to implement state machines using the AF--I did not mean to imply otherwise--but it requires more indirection and it is (imo) harder to create a mental model of the state machine by reading the code.

0 Kudos
Message 20 of 21
(2,437 Views)