[BOOST][MSM] Can Guard be a post condition of the Action, instead of precondition - boost

I am new to MSM, and also UML state machine standards as well. I had some state machine design before, using State Design Pattern, but this time I want to learn to use BOOST MSM, instead of cooking things up again.
One thing that really confused me a lot is the Guard. I want to do this, in State S1, I receive a event E1, then perform some Action A1, based on the result of action A1, I should either transit to new State S2, or stay in same state S1.
Using MSM, I cannot specify Guard G1 to be the result of Action A1, as in MSM's concept, G1 is the precondition whether A1 should be executed or not, rather than a result of executing A1.
Two solutions I can think of are:
Introduce a pseudo choice state, post_S1, where in its on_entry I perform the Action A1, and have a guard G1 testing the result of this action, then either go back to S1, or proceed to S2.
// Start Event Action Next Guard
S1 E1 none post_S1 none
post_S1 none none S2 G1
post_S1 none none S1 G1'(which is reverse of G1)
2.
Move Action A1 code to Guard G1 (Afterall, A1 is a function call, which I can make it return boolean). so basically my transition row would be
// Start Event Action Next Guard
S1 E1 none S2 G1=A1
Am I using MSM right? Is there any better practice for solving this problem? In my application, I would have A LOT of these pseudo choice states, which I really tries to avoid.
Thanks!
Zongjun

This is what the UML Standard defines, guards are preconditions.
You have several ways to your goal, my personal taste in this case would be:
Within State S1, add an internal transition on event E1.
This transition would have A1 as an action. Within A1, execute the action, then check the result.
If result means "stay where you are", stop
Else call (still within A1) fsm.template process_event(E2); where E2 is a new event moving you to S2.
I suggest this way because it will save you some compile-time, states are expensive ;-)
This is the easiest way. Again, there are others, like using eUML to make A1 return a result, then adding a if_ in the transition table, but this is much more advanced.
HTH,
Christophe

// Start Event Action Next Guard
S1 E1 none S2 Result_of_A1
Inside the Guard function itself, we are performing the action A1, then returning True or False based on A1's result. This way, if guard is false, then stay in S1; otherwise, move to S2.
This saves the pseudo choice state, which is useful but if there are a lot of states where its transition to next state is a "post condition" rather than "pre condition", then there will be lots of these pseudo choice states inside the transition table. Compared to the above transition table, it is more messy.

Related

#OnStateChanged for sequential state transitions in spring state machine

I want to define a sequential transition for my spring state machine. My state transitions are A -> B -> C. My business case is as below
I have single event E for transition to B from A and there is no event for B to C. I just want to define that if at all I am in B state I should perform the necessary actions and then move to state C without any event.
Below is the code snippet that I am using
transitions.withExternal().source(A).target(B).event(E)
transitions.withExternal().source(B).target(C)
The above transitions are taking state machine to C from A but the below onStateChanged method is first detecting change from B to C and then A to B.
Are the above transitions are correct or do we need other way to define those transitions
#OnStateChanged
public void anyStateChange() {}

How to retry a failed action?

I went through Spring Statemachine documentation but did not find clear answers for some scenarios. I will greatly appreciate if some one can clarify my questions.
Scenario1: How to retry errors related to action failures? Lets say I have the following states S1, S2 and S3 and when we transition from S1 to S2 I want to perform action A2. If action A2 fails I want to retry it with some time intervals. Is that possible using Spring StateMachine?
Consider AWS state machine Step Functions for example. All work in the step functions States are done using Task. And Task can be configured for retry.
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action());
Scenario 2: Lets say Statemachine has states S1, S2 and S3. The current state is S2. If the server goes down on startup will the Statemachine execution pick up from where it left off or we will have to do it all over again?
Scenario 3: When a Guard returns false (possibly because of error condition) and prevents a transition what happens next?
How to retry a failed action?
There are two types of actions in Spring State Machine - transition actions and state actions. In scenario 1 you're talking about transition action.
When you specify a transition action, you can also specify an error handler if the action fails. This is clearly documented in the spring state machine documentation.
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action(), errorAction());
In your errorAction() method you can implement your logic.
Possible options are:
transition to an earlier state and go the same path
transition to a specific state (e.g. retry state) where you can have your retry logic (e.g. Task/Executor that retries the action N times, and transition to other states (e.g. action success => go normal flow; action failed after N retries => transition to a failure terminal state)
There's also the official Tasks example, that demonstrates recovery/retry logic (source code).

How to change the target state in a state machine

First, I'm not quite sure whether my case is suitable to use spring state machine.
Here're my case:
I have a big mutable object and a set of logic unit which will manipulate this mutable object in a certain sequence.
For a normal flow: A.exec -> B.exec -> C.exec -> Done
For a bad flow that exception throws from A: A -> Error - > Done (B and C is bypassed)
For a bad flow that exception throws from B: A -> B - > Another Flow
I guess A, B, C could be modeled as a state while an action could be used to perform real biz logic when transit from A to B.
How can I capture possible thrown exception from A and change the target to another state instead of B?
Thanks
Leon
I'd use junction or choice and define guards for outgoing transitions. Something what is discussed in gh240. Then you can catch your exceptions and i.e. store something in an extended state and then from your guards you are free to evaluate different conditions.
Choice is pretty much if/elseif/else structure to define which branch machine will follow for transitions.
There's also deploy sample which is modelled with same concepts.

Supercollider: automatic sustain in events with envelope

(
SynthDef(\testEvt,{
arg out, gate = 1;
var sint = Blip.ar(440) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
Out.ar(out, Pan2.ar(sint, 0));
}).add();
Synth(\testEvt)
(instrument: \testEvt, freq:220, sustain: inf).play;
(instrument: \testEvt,freq:220).play;
)
Executing the first and the second line after the SynthDef would create a synth which playes forever, whereas the third line's synth plays for 0.8 seconds as per default value the generated event.
The problem is that I don't use 'sustain' anywhere in my SynthDef and it uses automatically just because there is a Linen.
The same this doesn't happen for freq: both the events play at 440 and not at 220, and that's just because the SynthDef doesn't use 'freq' as an argument. So why sustain doesn't follow the same rule ?
Also, is there a way to reference synths created by an event ? So that, when they have sustain: inf as argument, I can free them on a later time.
(instrument: \testEvt, freq:220, sustain: inf).play;
and
(instrument: \testEvt,freq:220).play;
are events. Events handle a lot of things for you. One thing they do is calculate when to set a gate to 0. (Remember that gate is one of the arguments in your SynthDef.) In the first example, because you sustain for infinite duration, the gate never goes to zero. In the second example, it uses the default duration and sets the gate to zero after that duration has passed. You can find out what key words are used in event environment variables by looking at the Event.sc source file. If you search for sustain, you'll find out the other keywords it uses for timing. One of these is dur. Try this:
(instrument: \testEvt, dur:3).play
Freq is also a keyword for events, but since you have no freq argument, it can't effect your synthDef. If you want to set the freq, you'll need to make a change:
SynthDef(\testEvt,{
arg out, gate = 1, freq = 440;
var sint = Blip.ar(freq) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
Out.ar(out, Pan2.ar(sint, 0));
}).add();
For contrast between events and controlling a synth directly, try:
a = Synth.new(\testEvt, [\out, 0, \gate, 1])
You can add in any other arguments you want, like freq or sustain, but they have no effect because those aren't arguments to your synthdef. And, unlike Event, synth doesn't do any calculations on your behalf. When you want the note to end, set the gate to 0 yourself:
a.set(\gate, 0)
It's good to be aware of event environment variables because they're also used by Pbinds and by using them, you can effect other things. If you had a synthdef that used sustain as an argument for something else, you could be surprised by it changing your durations.
Regarding your last sub-question,
Also, is there a way to reference synths created by an event ? So that, when they have sustain: inf as argument, I can free them on a later time.
Yes, by "indexing" the Event by \id key. This actually returns an array of node ids because an Event with \strum can fire up more than one node/synth. Also, the \id value is nil while the event is not playing. But this indexing method is fairly unnecessary for what you want, because...
You can end the (associated) synth by ending the Event early with release, just like for the Synth itself. What this does is basically gate-out its internal synth. (In your example, this release call transitions to the release point of the ASR envelope generated by Linen, by lowering gate to 0.). And, of course, use a variable to save the "reference" to the synth and/or event, if don't plan to release it right away in a program (which would produce no sound with a gated envelope).
Basically
fork { var x = Synth(\testEvt); 2.wait; x.release }
does the same as
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.release }
except there's one layer of indirection in the latter case for the release. The first example is also equivalent to
fork { var x = Synth(\testEvt); 2.wait; x.set(\gate, 0); }
which does the work of release explicitly. Event also supports set and it passes the value to the corresponding Synth control (if the latter was properly added on the server.)
Now the complicated method you asked about (retrieving node ids for the event and sending them messages) is possible too... although hardly necessary:
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait;
e[\id].do({ arg n; s.sendMsg("/n_set", n, "gate", 0); }) }
By the way, you can't use wait outside of a Routine, that's why fork was needed in the above examples. Interactively, in the editor, you can "wait manually", of course, before calling release on either the Synth or the Event.
As a somewhat subtle point of how envelope gating works, it doesn't actually start playing (technically begin transitioning to the endpoint of the first [attack] envelope segment) until you set gate to 1. I.e. you can delay the (envelope) start as in:
fork { x = Synth(\testEvt, [\gate, 0]); 3.wait; x.set(\gate, 1); 2.wait; x.release }
Beware that the default Event.play doesn't generate this 0 to 1 gate transition though, i.e. you can't rely on it to fire your synth's envelope if you set the initial gate value to zero in your SynthDef.
Also, I'm assuming that by "free" you mean "stop playing" rather than "free their memory on the server". There's no need to manually free those (event) synths in the latter sense since they have doneAction:2 in the envelope, which does that for you once they are released and the final segment of the envelope finishes playing. If you somehow want to kill the synth right away (like Ctrl+. does) instead of triggering its fade-out you can replace the message sent in the inner function of the "complicated" example (above) with s.sendMsg("/n_free", n). Or much more simply
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.free }
Also, if you wonder about \strum, an example is:
e = (instrument: \testEvt, sustain: inf, strum: 1, out: #[0, 0]).play
Now e[\id] is an array of two nodes. Event is a bit cheeky in that it will only create multiple nodes for arrays passed to actual Synth controls rather than random fields, so "strumming" \freq (or its precursors like \degree etc.) only creates multiple nodes if your SynthDesc has a freq control.
Alas the "complicated" method is almost useless when it comes to playing Pbinds (patterns). This is because Pbind.play returns and EventStreamPlayer... that alas makes a private copy of the prototype event being played and plays that private copy, which is inaccessible to the caller context (unless you hack EventStreamPlayer.prNext). Confusingly EventStreamPlayer has an accessible event variable, but that's only the "prototype", not the private copy event being played... So if p is an instance of an EventStreamPlayer then p.event[\id] is always nil (or whatever you set it to beforehand) even while playing. Since one seldom plays Events individually and much more often patterns...
Simply as a hacking exercise tough, it turns out there is an even more convoluted way to access the ids of nodes that EventStreamPlayer fires... This relies on overriding the default Event play which thankfully can be extended outside class inheritance because the default is conveniently saved in a class dictionary...
(p = Pbind(\instrument, \testEvt, \sustain, Pseq([1, 2]), \play, {
arg tempo, srv;
var rv;
"playhack".postln;
rv = Event.parentEvents[\default][\play].value(tempo, srv);
~id.postln;
rv;
}).play)
In general however, patterns are clearly not designed to be used this way, i.e. by hacking "a layer below" to get to the node ids. As "proof", while the above works well enough with Pbind (which uses the default Event type \note) it doesn't work reliably with Pmono which doesn't set the Event \id on its first note (Event type \monoNote) but only on subsequent notes (which generate a different Event type, \monoSet). Pmono keeps an internal copy of the node id, but this is completely inaccessible on the first mono note; it only copies it to the Events on the subsequent notes for some reason (bug perhaps, but could be "by design"). Also, if you use Pdef which extends Event with type \phrase... the above hack doesn't work all, i.e. \id is never set by type \phrase; perhaps you can get to the underlying sub-events generated somehow... I haven't bothered to investigate further.
The SC documentation (in the pattern guide) even says at one point
Remember that streams made from patterns don't expose their internals. That means you can't adjust the parameters of an effect synth directly, because you have no way to find out what its node ID is.
That's not entirely correct given the above hack, but it is true in some contexts.

Handling Events in a State Machine

I have a state machine for my npcs that is structured like the following,
execute state
[[[pred1 pred2....] state1]
[[pred3 pred4....] state2]
[[pred5 pred6....] staten]]
What happens is after current state completes, it starts iterating through the states/predicates list and as soon as a list of predicates that returns all true it will jump to the state that it is associated with.
Certain events can happen during all states, say player commands npc to go somewhere. Just like any other state transition I can check for a predicate and change state but adding the same piece of code to every state seems a bit lame. So I was wondering how people deal with events in state machines?
Just make a data structure like:
state1 -> event1, event2
state2 -> event1
state3 -> event2, event3
By the way, what you have outlined doesn't look like a state machine. In state machine, next state depends on the previous one, so it would look like:
[state1, condition1] -> state2
[state1, condition2] -> state3
...
(condition is your set of predicates). You must also somehow assure that the transition is unique, i.e. that condition1 and condition2 cannot be fulfilled at the same time. Or take the first one which is true.
Try to use pattern called "State" - http://en.wikipedia.org/wiki/State_pattern
Define abstract superclass (e.g. class AbsatractState, put there code of methods that common for all states), and all classes that represents real states must been subclassed from it.
When you have a particular action that can be undertaken from all possible states, do as you would do in mathematics : factor it!
i.e : 4*10 + 5*10 + 6*10 + 4*20 + 5*20 + 6*20 = (4+5+6)*(10+20)
i.e : your (oblivion) npc can be sleeping, or at work, or eating. In all three cases, it must react to some events (be talked to, be attacked, ...). Build two FSM : Daily activity = [sleep, work, eat]. Reaction state : {Undisturbed, Talked to, Attacked, ...}. Then forward the events only to the second FSM
And you can keep on factoring FSM, as long as you're factoring independant things. For example, the mood of the npc (happy, neutral, angry, ...) is independant from it's daily activity and its reaction state. I mean "independant" in the sense "the npc can be at work, talked to, and angry, and there's no contradiction". Of course FSM influence each other, npc that are attacked tend to be angry.
So you could add a third FSM for the Mood state, instead of incorporating it in every single node
Use a hierarchical state machine (HSM). HSMs are exactly designed to factor out the common behavior into superstates and make it reusable for all substates. To learn more about HSMs, you can start with the Wikipedia article "UML State Machine" at http://en.wikipedia.org/wiki/UML_state_machine.

Resources