Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Substitute Actor.vi: Child to parent?

Hi,

At the moment Substitute Actor allows for initialising a child Actor with a parent Actor's data. However, what about the reverse case? How do you initialise a parent Actor with a child Actor's data?

The question arrises from attempting to implement a heirarchical sate machine. Currently, moving down the heirarchy i.e., from RunState to Run.EnabledState (a child of RunState) is no problem. Moving from Run.EnabledState to RunState is not currently possible?

I was thinking about implmenting a Copy To Parent.vi and Copy To Child.vi method on my abstract State.lvclass (which inherits from Actor.lvclass). If this is the way to go I was wondering if I should make use of the Sawp Values primitive form the Memory Control palette?

Essentially, any advice would be welcome. I don't think I can simply typecast the parent to the child since the "actual object" on the wire remains unchanged.

Regards,

Steve.

0 Kudos
Message 1 of 21
(12,572 Views)

It sounds like you need RunState to be a sibling of Run.EnabledState, not the parent. Then you need a common parent class. Then put things shared by all in the parent and write an accessor to get at the data when needed. Remember to "hand off" the data when you change state (put the data in the new state).

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 2 of 21
(5,808 Views)

Ah, type changing an object. A topic that comes up regularly. I'll spare you the philosophical and technical details -- they're somewhere in this community forum and elsewhere on the Internet. Short story: Any type change, whether from parent to child or child to parent or between siblings, is essentially the same and requires basically the same solution. You have to provide a method on the "source object" that the "destination object" can call to extract the information that it needs. With a parent-to-child, you can just put a single method on the parent class that returns all the data the child needs and scope it appropriately. With the other two, if you want a generic solution (where the source object could be any of several classes at runtime) you have to put type-testing code into the destination class -- in other words, the method of the destination class will take in a source object and then use "To More Specific Class" or "Get LV Class Name.vi" to test "is it this class?" repeatedly until it finds a matching class and then invokes that class' API. You'll need a specific case for every concrete class that you want to be convertable. That means you really can't have a dynamic system without also creating a dynamic plug-in system for conversion VIs.

In a state machine type environment, you often have a finite and well-defined set of transitions between the state classes. This means if you have a transition from Alpha State to Beta State, you can put a method on Beta State.lvclass called "Transition from Alpha.vi" and have that method take in a Alpha explicitly and the method knows how to extract the info it needs from Alpha. That eliminates all the type testing and makes for some very straightforward to understand code.

0 Kudos
Message 3 of 21
(5,808 Views)

I had to do something like this on a recent project.

If you are transitioning from a child state to a parent state, and the parent needs a piece of data in the child, then consider putting the data in the parent class to begin with.  Then you can transfer the data as part of Parent:Substitute Actor.

If that doesn't work - say, you need to move data between to sibling state classes, and the data should not exist in any other state class, then you can do something like this:

Untitled.png

This code is from a state called Ready.  Substitute Actor dynamically dispatches based on the current state (in this case, the class on the Current Ready wire).  Copy from Ready dispatches based on the new state (in this case, the class on the Substitute Actor wire).  Copy from Ready is a member of Ready, so it has access to Ready attributes.  If the new state is a child of Ready, then Copy from Ready also has direct access to any attributes the new state inherits from Ready.  Otherwise, you can use accessor methods.

0 Kudos
Message 4 of 21
(5,808 Views)

St3ve wrote:

How do you initialise a parent Actor with a child Actor's data?

Let's start by taking a step back and examine the question a bit.  In the Actor Framework, each actor is a class, so we can restate your question like this:

"How do you initialize a parent class with a child class' data?"

Phrased that way it is apparent the question doesn't make much sense.  Child classes often have more data than the parent (the parent's data plus their own,) meaning there's no place in a parent object to put the child object's data.  If you need the child object's data, instantiate a child object.

You should take a closer look at your inheritance hierarchy.  The class hierarchy implementing the states doesn't necessarily need to follow the state hierarchy.  I suspect Casey is correct that RunState and Run.EnabledState should be siblings, not parent-child.

0 Kudos
Message 5 of 21
(5,808 Views)

Daklu wrote:

Phrased that way it is apparent the question doesn't make much sense.  Child classes often have more data than the parent (the parent's data plus their own,) meaning there's no place in a parent object to put the child object's data. 

It sometimes does make sense in specific cases. It makes as much sense as using any existing object to initialize any other object of any class, related or not, and in some cases, putting the initialization data into the parent class makes reasonable sense. It only does not make sense in the general case, which is why there is no built-in language support for the operation.

Daklu wrote:

If you need the child object's data, instantiate a child object.

If you have a Parent object through part of your program, and then new information comes in, you may wish to transition to a specific child class, taking information from the Parent class object when initailizing your new child class object. Yes, you can have a totally separate class that nests inside the Parent class, but you're still handing off that data. This is true even if the actual concrete class is a sibling.

0 Kudos
Message 6 of 21
(5,808 Views)

AristosQueue wrote:

...and in some cases, putting the initialization data into the parent class makes reasonable sense.

I agree, but at that point it's no longer the child actor's data--it's the parent actor's data.  However, one needs to be careful when moving data fields from children to the parent.  I've seen many developers (including me) put code in the parent class to handle corner case situations that arise with specific child classes.  That kind of code belongs in the child class, not the parent class.

AristosQueue wrote:

If you have a Parent object through part of your program, and then new information comes in, you may wish to transition to a specific child class, taking information from the Parent class object when initailizing your new child class object. Yes, you can have a totally separate class that nests inside the Parent class, but you're still handing off that data. This is true even if the actual concrete class is a sibling.

Again, I agree, and I'm not trying to contradict anything you or Allen have posted. 

Steve said several things that hint he is venturing into (for him) the unknown:

--"How do you initialise a parent Actor with a child Actor's data?"

--"I don't think I can simply typecast the parent to the child since the "actual object" on the wire remains unchanged."

--"Moving from Run.EnabledState to RunState is not currently possible?"

You and Allen are providing details on how Steve might implement a solution.  I'm seeing warning flags and saying "make sure your design is correct before you implement it."  I interpreted "child Actor's data" as the set of data that is unique to that specific class (state data), not data shared among all of the state classes (state machine data.)  I can imagine a few scenarios where state specific data needs to be maintained after the state machine has exited that state, but I think the code is easier to understand when you don't need to do that.  Unfortunately he didn't supply a state diagram, so I can't comment on the design.

Regarding my suggestion that RunState and Run.EnabledState should be siblings instead of parent-child, I'll retract that and say, "you may want to consider breaking the inheritance relationship, depending on the design of your state machine."

0 Kudos
Message 7 of 21
(5,808 Views)

Everyone, thanks for the useful replies,

I'll give a bit more background and the missing state diagram. I often use this basic state machine (with whatever required sub-States) for hardware control modules (inspired some time ago by a presentation I saw by Paul Lotz):

StateDiagram.png

There have been a few replies so I will respond in general:

I think that in a hierarchical state machine (HSM) the States are best represented as an inheritance hierarchy. This enables super-States to handle common behaviour e.g., in the diagram shown, RunState handles the Error and Standby events, meaning the code is not repeated in Run.Enabled and Run.Disabled. This allows a "code by difference" approach, drastically reducing the chance of bugs by eliminating repeated code etc.

If a traditional "flat" state machine is used (making RunState and Run.EnabledState siblings) I have found that there is very quickly an explosion of states/transitions which makes it unwieldy for anything except trivial problems. Or, as is often seen, the number of States is kept to a minimum and then boolean variables in the "state data" are used to create pseudo-States with case structures e.g., there could be an Enabled flag and Run.EnabledState and Run.DisabledState would be replaced by a case structure inside all of the messages received when in Run.State (not nice!).

In the past I've had all of the data contained in the Context object which has a State object as part of its private data (a similar approach would have all of the state machines data in ControllerState as shown in the diagram). However, this approach means that in some States there is data floating about which is irrelevant. Worse, it creates a single hot-spot for changes i.e., change any States implementation and you find yourself changing the one single class holding the data (and breaking other States which use it as well).

To solve this I wondered: why I didn't make the Context (the thing with State) just a wrapper and let State have the data? That way sub-States have access to super-State data (since a sub-State is a super-State through inheritance) but can introduce new variables which are relevant (and scoped) to their level of the hierarchy. In practice, there will be a lot of common data held in a few super-States and only a few sub-State specific variables introduced as needed.

During a state transition e.g., from Run.EnabledState to FaultState the transition goes up to the least common ancestor (LCA) State and then down to the target State, calling the Exit and Entry actions along the way: Run.EnabledState:Exit.vi --> RunState:Exit.vi --> (now in the LCA: ControllerState) --> FaultState:Entry.vi. This means that even if the ultimate transition is over several levels of the hierarchy it occurs by stepping up and down the hierarchy one level at a time -- I think this negates the type checking AristosQueue was describing.

During a transition, data from the LCA level should be preserved. This leads to the copying (parent data) data between objects e.g., Run.State --> Run.EnabledState, Run.EnabledState should be initialised with Run.State data. The copying/initialisation can be accomplished by a method similar to Actor:Substitute Actor.vi (dynamic dispatch on the parent -- the current, not substitute object -- as it knows what data it can pull from the child). Going the other way (child substituted for parent), as niACS pointed out, the copy method must dynamically dispatch on the substitute parent (again the dynamic dispatch must occur on the parent object as only it knows what the parent data is).

At the core of the copy-to-child and copy-to-parent methods I was thinking about implementing the following copy core.vi:

Copy Core.png

As a specific question is this really crying out for the Swap Values Prim (I've never actually needed to use it)?

WOW what a long ramble. Apologies, if there was far to much discussion on HSMs but thanks for the useful input and at least forcing me to put some ideas into words (it always seems so simple in my head).

Regards,

Steve.

0 Kudos
Message 8 of 21
(5,808 Views)

St3ve wrote:

I think this negates the type checking AristosQueue was describing.

It does so long as the particular transition that you're making doesn't need any specific data from the original state. For example, your RunState might include private data for "last instruction executed" that is not included in the ancestor class (because the ancestor class knows nothing about execution). And when you transition from RunState to FaultState, you might really want to include that last instruction executed information (either for display to the user or in case you recover the fault and need to transition back to RunState and need to pick up where you left off). If you need to preserve that kind of information, you have to know "the state I am transitioning from is this specific state, and here's the information I need to harvest from it."

I find that the vast majority of the time, at least a few transitions in these systems want that data preservation ability. That's when "Transition From XYZ State.vi" functions become valuable.

St3ve wrote:

As a specific question is this really crying out for the Swap Values Prim (I've never actually needed to use it)?

Yes. Yes it is.

0 Kudos
Message 9 of 21
(5,808 Views)

Steve,

Thanks for the diagram and explanation.  It helps me understand what you're trying to do and it looks like my concerns about your design were unfounded.  What I don't understand is why you want to instantiate a RunState object?  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.

0 Kudos
Message 10 of 21
(5,808 Views)