Updates to docs and samples

This commit is contained in:
Janne Valkealahti
2015-04-06 10:55:54 +01:00
parent e5c67b1c22
commit 45ebce9c64
7 changed files with 194 additions and 54 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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[]
}

View File

@@ -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);
}
}
}

View File

@@ -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)]; |
+----------------------------------------------------------------------------------------------+

View File

@@ -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