Updates to docs and samples
This commit is contained in:
@@ -21,12 +21,6 @@ There are few choices a state machine developer can choose.
|
||||
state transition into a next state when state has entry and its
|
||||
actions has been completed.
|
||||
|
||||
.How do I defer an event
|
||||
|
||||
{zwsp} +
|
||||
|
||||
For more complete example and explanation, see cdplayer sample.
|
||||
|
||||
== Extented State
|
||||
|
||||
.How I can initialise variables on state machine start
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 24 KiB |
@@ -123,6 +123,72 @@ include::samples/demo/showcase/Application.java[tags=snippetD]
|
||||
include::samples/demo/showcase/Application.java[tags=snippetE]
|
||||
----
|
||||
|
||||
Lets go throught what this state machine do when it's executed and we
|
||||
send various event to it.
|
||||
|
||||
[source,text]
|
||||
----
|
||||
sm>sm start
|
||||
Entry state S0
|
||||
Entry state S1
|
||||
Entry state S11
|
||||
Init foo to 0
|
||||
State machine started
|
||||
|
||||
sm>sm event A
|
||||
Event A send
|
||||
|
||||
sm>sm event C
|
||||
Exit state S11
|
||||
Exit state S1
|
||||
Entry state S2
|
||||
Entry state S21
|
||||
Entry state S211
|
||||
Event C send
|
||||
|
||||
sm>sm event H
|
||||
Switch foo to 1
|
||||
Event H send
|
||||
|
||||
sm>sm event C
|
||||
Exit state S211
|
||||
Exit state S21
|
||||
Exit state S2
|
||||
Entry state S1
|
||||
Entry state S11
|
||||
Event C send
|
||||
|
||||
sm>sm event A
|
||||
Exit state S11
|
||||
Exit state S1
|
||||
Entry state S1
|
||||
Entry state S11
|
||||
Event A send
|
||||
----
|
||||
|
||||
What happens in above sample:
|
||||
|
||||
* State machine is started which takes it to its initial state _S11_
|
||||
via superstates _S1_ and _S0_. Also extended state variable `foo` is
|
||||
init to `0`.
|
||||
* We try to execute self transition in state _S1_ with event _A_ but
|
||||
nothing happens because transition is guarded by variable `foo` to
|
||||
be `1`.
|
||||
* We send event _C_ which takes us to other state machine where
|
||||
initial state _S211_ and its superstates are entered. In there we
|
||||
can use event _H_ which does a simple internal transition to flip
|
||||
variable `foo`. Then we simply go back using event _C_.
|
||||
* Event _A_ is sent again and now _S1_ does a self transition because
|
||||
guard evaluates true.
|
||||
* It's also worth to pay attention to how event _H_ is handled in
|
||||
different states _S0_, _S1_ and _S2_. This is a good example of how
|
||||
hierarchical states and their event handling works. If state _S2_ is
|
||||
unable to handle event _H_ due to guard condition, its parent is
|
||||
checked next. This guarantees that while on state _S2_, `foo` flag
|
||||
is always flipped around. However in state _S1_ event _H_ always
|
||||
match to its dummy transtion without guard or action, not never
|
||||
happens.
|
||||
|
||||
== CD Player
|
||||
CD Player is a sample which resembles better use case of most of use have
|
||||
used in a real world. CD Player itself is a really simple entity where
|
||||
@@ -271,3 +337,30 @@ event sent to a state machine.
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetL]
|
||||
----
|
||||
|
||||
One other important aspect of a state machines is that they have their
|
||||
own responsibilies mostly around handling states and all application
|
||||
level logic should be kept outside. This means that application needs
|
||||
to have a ways to interact with a state machine and below sample is
|
||||
how cdplayer does it order to update lcd status. Also pay attention
|
||||
that we annotated _CdPlayer_ with _@WithStateMachine_ which instructs
|
||||
state machine to find methods from your pojo which are then called
|
||||
with various transitions.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/CdPlayer.java[tags=snippetA]
|
||||
----
|
||||
|
||||
In above example we use _@OnTransition_ annotation to hook a callback
|
||||
when transition happens with a target state _BUSY_.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/CdPlayer.java[tags=snippetB]
|
||||
----
|
||||
|
||||
_@OnTransition_ we used above can only be used with strings which are
|
||||
matched from enums. _@StatesOnTransition_ is then something what user
|
||||
can create into his own application to get a type safe annotation where
|
||||
a real enums can be used.
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ public class CdPlayer {
|
||||
return cdStatus + " " + trackStatus;
|
||||
}
|
||||
|
||||
//tag::snippetA[]
|
||||
@OnTransition(target = "BUSY")
|
||||
public void busy(ExtendedState extendedState) {
|
||||
Object cd = extendedState.getVariables().get(Variables.CD);
|
||||
@@ -70,6 +71,7 @@ public class CdPlayer {
|
||||
cdStatus = ((Cd)cd).getName();
|
||||
}
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
@StatesOnTransition(target = States.PLAYING)
|
||||
public void playing(ExtendedState extendedState) {
|
||||
@@ -88,6 +90,7 @@ public class CdPlayer {
|
||||
cdStatus = "Open";
|
||||
}
|
||||
|
||||
//tag::snippetB[]
|
||||
@StatesOnTransition(target = States.CLOSED)
|
||||
public void closed(ExtendedState extendedState) {
|
||||
Object cd = extendedState.getVariables().get(Variables.CD);
|
||||
@@ -98,5 +101,6 @@ public class CdPlayer {
|
||||
}
|
||||
trackStatus = "";
|
||||
}
|
||||
//end::snippetB[]
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package demo.showcase;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.Bootstrap;
|
||||
@@ -14,6 +18,8 @@ import org.springframework.statemachine.guard.Guard;
|
||||
@Configuration
|
||||
public class Application {
|
||||
|
||||
private final static Log log = LogFactory.getLog(Application.class);
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class StateMachineConfig
|
||||
@@ -51,7 +57,8 @@ public class Application {
|
||||
.withStates()
|
||||
.parent(States.S21)
|
||||
.initial(States.S211)
|
||||
.state(States.S211);
|
||||
.state(States.S211)
|
||||
.state(States.S212);
|
||||
}
|
||||
//end::snippetAA[]
|
||||
|
||||
@@ -62,7 +69,7 @@ public class Application {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S1).event(Events.A)
|
||||
.action(fooAction())
|
||||
.guard(foo1Guard())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S11).event(Events.B)
|
||||
@@ -111,7 +118,13 @@ public class Application {
|
||||
.source(States.S1).event(Events.H)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S11).target(States.S12).event(Events.I);
|
||||
.source(States.S11).target(States.S12).event(Events.I)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S211).target(States.S212).event(Events.I)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S12).target(States.S212).event(Events.I);
|
||||
|
||||
}
|
||||
//end::snippetAB[]
|
||||
@@ -137,7 +150,7 @@ public class Application {
|
||||
|
||||
//tag::snippetB[]
|
||||
public static enum States {
|
||||
S0, S1, S11, S12, S2, S21, S211
|
||||
S0, S1, S11, S12, S2, S21, S211, S212
|
||||
}
|
||||
//end::snippetB[]
|
||||
|
||||
@@ -152,13 +165,17 @@ public class Application {
|
||||
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
Object foo = context.getExtendedState().getVariables().get("foo");
|
||||
Map<Object, Object> variables = context.getExtendedState().getVariables();
|
||||
Object foo = variables.get("foo");
|
||||
if (foo instanceof Integer && ((Integer)foo) == 0) {
|
||||
context.getExtendedState().getVariables().put("foo", 1);
|
||||
log.info("Switch foo to 1");
|
||||
variables.put("foo", 1);
|
||||
} else if (foo instanceof Integer && ((Integer)foo) == 1) {
|
||||
context.getExtendedState().getVariables().put("foo", 0);
|
||||
log.info("Switch foo to 0");
|
||||
variables.put("foo", 0);
|
||||
} else {
|
||||
context.getExtendedState().getVariables().put("foo", 0);
|
||||
log.info("Init foo to 0");
|
||||
variables.put("foo", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
+---------------------------------------------------------------------------------------------+
|
||||
| S0 |
|
||||
+---------------------------------------------------------------------------------------------+
|
||||
| entry/ |
|
||||
| exit/ |
|
||||
| H/[foo.equals(0)]; |
|
||||
| |
|
||||
| +-------------------------+ +--------------------------------------------+ |
|
||||
| *-->| S1 | | S2 | |
|
||||
| +-------------------------+ +--------------------------------------------+ |
|
||||
| | entry/ | C | entry/ | |
|
||||
| D | exit/ |----->| exit/ | |
|
||||
|<----------| H/ | | H/[foo.equals(1)]; | |
|
||||
| | | | | |
|
||||
| | +---------------+ | C | +------------------------------+ | |
|
||||
| | *-->| S11 | |<-----| *-->| S21 | | |
|
||||
| | +---------------+ | | +------------------------------+ | |
|
||||
| +--| | entry/ | | F | | entry/ | | |
|
||||
| A| | | exit/ |<---------| | exit/ | | |
|
||||
| +->| | | | | | +--------------+ | | |
|
||||
| A[foo 1]; | B | | | | | *-->| s211 | | | |
|
||||
| |---->| | | | F | +--------------+ | | |
|
||||
| | | | |-------------------->| entry/ | | G | |
|
||||
| | +--| | | | G | | exit/ |----------------->|
|
||||
| | | | |------------------------>| | | | |
|
||||
| | | +---------------+ | | | B | | | E | |
|
||||
| | | | | |------>| |<-----------------|
|
||||
| | | +---------------+ | | | | | | | |
|
||||
| | I| | S12 | | | | D | | | | |
|
||||
| | | +---------------+ | | |<------| | | | |
|
||||
| | | | entry/ | | | | +--------------+ | | |
|
||||
| | | | exit/ | | | | | | |
|
||||
| | | | | | | | | | |
|
||||
| | +->| | | | | | | |
|
||||
| | +---------------+ | | +------------------------------+ | |
|
||||
| | | | | |
|
||||
| +-------------------------+ +--------------------------------------------+ |
|
||||
| |
|
||||
+---------------------------------------------------------------------------------------------+
|
||||
+----------------------------------------------------------------------------------------------+
|
||||
| S0 |
|
||||
+----------------------------------------------------------------------------------------------+
|
||||
| entry/ |
|
||||
| exit/ |
|
||||
| H/[foo.equals(0)]; |
|
||||
| |
|
||||
| +-------------------------+ +--------------------------------------------+ |
|
||||
| *-->| S1 | | S2 | |
|
||||
| +-------------------------+ +--------------------------------------------+ |
|
||||
| | entry/ | C | entry/ | |
|
||||
| D | exit/ |----->| exit/ | |
|
||||
|<-----------| H/ | | H/[foo.equals(1)]; | |
|
||||
| | | | | |
|
||||
| | +---------------+ | C | +------------------------------+ | |
|
||||
| | *-->| S11 | |<-----| *-->| S21 | | |
|
||||
| | +---------------+ | | +------------------------------+ | |
|
||||
| | | entry/ | | F | | entry/ | | |
|
||||
| | | exit/ |<---------| | exit/ | | |
|
||||
| | | | | | | +--------------+ | | |
|
||||
| | B | | | | | *-->| s211 | | | |
|
||||
| |---->| | | | F | +--------------+ G | | |
|
||||
| | | | |-------------------->| entry/ |----------------->|
|
||||
| | +--| | | | G | | exit/ | | | |
|
||||
| | | | |------------------------>| | | E | |
|
||||
| | | +---------------+ | | | B | |<-----------------|
|
||||
| | | | | |------>| | | | |
|
||||
| | | +---------------+ | | | | | D | | |
|
||||
| | I| | S12 | | | | +--| |------>| | |
|
||||
| | | +---------------+ | | | | +--------------+ | | |
|
||||
| | | | entry/ | | | | | | | |
|
||||
| | | | exit/ | | | | I| +--------------+ | | |
|
||||
| | | | | | | | | | s212 | | | |
|
||||
| | +->| | | | | | +--------------+ | | |
|
||||
| +--| | | | | | +->| entry/ | | | |
|
||||
| | | | | | I | | | exit/ | | | |
|
||||
| | | | |------------------------>| | | | |
|
||||
| A| | | | | | | +--------------+ | | |
|
||||
| | | | | | | | | | |
|
||||
| | | +---------------+ | | +------------------------------+ | |
|
||||
| +->| | | | |
|
||||
| +-------------------------+ +--------------------------------------------+ |
|
||||
| A[foo.equals(1)]; |
|
||||
+----------------------------------------------------------------------------------------------+
|
||||
|
||||
@@ -49,6 +49,20 @@ public class ShowcaseTests {
|
||||
|
||||
@Test
|
||||
public void testA() throws Exception {
|
||||
listener.reset(1, 0, 0);
|
||||
machine.sendEvent(Events.A);
|
||||
listener.stateChangedLatch.await(1, TimeUnit.SECONDS);
|
||||
assertThat(machine.getState().getIds(), contains(States.S0, States.S1, States.S11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCHCA() throws Exception {
|
||||
listener.reset(3, 0, 0);
|
||||
machine.sendEvent(Events.C);
|
||||
machine.sendEvent(Events.H);
|
||||
machine.sendEvent(Events.C);
|
||||
listener.stateChangedLatch.await(1, TimeUnit.SECONDS);
|
||||
|
||||
listener.reset(1, 2, 2, 1);
|
||||
machine.sendEvent(Events.A);
|
||||
listener.stateChangedLatch.await(1, TimeUnit.SECONDS);
|
||||
@@ -137,6 +151,19 @@ public class ShowcaseTests {
|
||||
assertThat(listener.statesExited.get(0).getId(), is(States.S11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testII() throws Exception {
|
||||
machine.sendEvent(Events.I);
|
||||
|
||||
listener.reset(1, 0, 0);
|
||||
// TODO: should think if need to bypass
|
||||
// S211 as initial state and go directly
|
||||
// to S212.
|
||||
machine.sendEvent(Events.I);
|
||||
listener.stateChangedLatch.await(1, TimeUnit.SECONDS);
|
||||
assertThat(machine.getState().getIds(), contains(States.S0, States.S2, States.S21, States.S212));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testH() throws Exception {
|
||||
listener.reset(0, 0, 0, 1);
|
||||
@@ -164,7 +191,7 @@ public class ShowcaseTests {
|
||||
machine.sendEvent(Events.H);
|
||||
listener.transitionLatch.await(1, TimeUnit.SECONDS);
|
||||
assertThat(listener.transitionCount, is(1));
|
||||
assertThat(listener.transitions.get(0).getSource().getId(), is(States.S2));
|
||||
assertThat(listener.transitions.get(0).getSource().getId(), is(States.S0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user