Updates to docs

This commit is contained in:
Janne Valkealahti
2015-05-29 10:59:47 +01:00
parent 2df44abc53
commit 61ec8723df
12 changed files with 644 additions and 39 deletions

View File

@@ -181,6 +181,7 @@ configure(rootProject) {
task copyDocsSamples(type: Copy) {
from 'spring-statemachine-core/src/test/java/org/springframework/statemachine/docs'
from 'spring-statemachine-samples/src/main/java/'
from 'spring-statemachine-samples/washer/src/main/java/'
from 'spring-statemachine-samples/tasks/src/main/java/'
from 'spring-statemachine-samples/turnstile/src/main/java/'
from 'spring-statemachine-samples/showcase/src/main/java/'

View File

@@ -15,6 +15,16 @@ material in this reference documentation.
include::samples/States.java[tags=snippetA]
----
[source,java,indent=0]
----
include::samples/States2.java[tags=snippetA]
----
[source,java,indent=0]
----
include::samples/States3.java[tags=snippetA]
----
[source,java,indent=0]
----
include::samples/Events.java[tags=snippetA]
@@ -86,6 +96,22 @@ directly contained in a state machine and all other regions in the
state machine also are completed, then it means that the entire state
machine is completed.
*History State*::
A pseudo state which allows a state machine to remember its last
active state. Two types of history state exists, _shallow_ which only
remember top level state and _deep_ which remembers active states in a
sub-machines.
*Choice State*::
A pseudo state which allows to make a transition choice based of i.e.
event headers or extended state variables.
*Fork State*::
A pseudo state which gives a controlled entry into a regions.
*Join State*::
A pseudo state which gives a controlled exit from a regions.
*Region*::
A region is an orthogonal part of either a composite state or a state
machine. It contains states and transitions.
@@ -124,6 +150,45 @@ flags, nested if/else/break clauses or other impractical logic you
simply rely on state, state variables or other interaction with a
state machine.
==== Pseudo States
PseudoState is a special type of state which usually introduces more
higher level logic into a state machine by either giving a state a
special meaning like initial state. State machine can then internally
react to these states by doing various actions available in UML state
machine concepts.
===== Initial
Initial state is always needed for every single state machine whether
you have a simple one level state machine or more complex state
machine composed with submachines or regions. Initial state simple
defines where state machine should go when it starts and without it
state machine is ill-formed.
===== End
Terminate state which is also called as end state will indicate that a
particular state machine has reached its final state. Effectively this
mean that a state machine will no longer process any events and will
not transit to any other state. However in a case of submachines are
regions, state machine is able to restart from its terminal state.
===== Choice
Choice can be used to choose a transition conditionally.
===== History
History state can be used to remember a last active state
configuration. After state machine has been exited, history state can
be used to restore previous knows configuration. There are two types
of history states available, _SHALLOW_ only remember active state of a
state machine itself while _DEEP_ also remembers nested states.
===== Fork
Fork can be used to do an explicit entry into one or more regions.
===== Join
Join is used to merge several transitions together originating from
different regions. It it generally used to wait and block for
participating regions to get into its join target states.
==== Guard Conditions
Guard conditions are expressions which evaluates either to *TRUE* or
*FALSE* based on extended state variables and event parameters. Guards

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -442,3 +442,183 @@ What happened in above run:
* We print lcd status and request next track.
* We stop playing.
== Tasks
Tasks is a sample demonstrating a parallel task handling within a
regions and additionally adds an error handling to either
automatically or manually fixing task problems before continuing back
to a state where tasks can be run again.
image::images/statechart5.png[width=500]
On a high level what happens in this state machine is:
* We're always trying to get into READY state so that we can use event
RUN to execute tasks.
* TASKS state which is composed with 3 independent regions has been
put in a middle of FORK and JOIN states which will cause regions to
go into its initial states and to be joined by end states.
* From JOIN state we go automatically into a CHOICE state which checks
existence of error flags in extended state variables. Tasks can set
these flags and it gives CHOICE state a possibility to go into ERROR
state where errors can be handled either automatically or manually.
* AUTOMATIC state in ERROR can try to automatically fix error and goes
back to READY if it succeed to do so. If error is something what
can't be handled automatically, user intervention is needed and
machine is put into MANUAL state via FALLBACK event.
.States
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetB]
----
.Events
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetC]
----
.Configuration - states
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetAA]
----
.Configuration - transitions
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetAB]
----
Guard below is guarding choice entry into a ERROR state and needs to
return TRUE if error has happened. For this guard simply checks that
all extended state variables(T1, T2 and T3) are TRUE.
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetAC]
----
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetAD]
----
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetAE]
----
[source,java,indent=0]
----
include::samples/demo/tasks/Application.java[tags=snippetAF]
----
Lets see an example how this state machine actually works.
[source,text]
----
sm>sm start
State machine started
Entry state READY
sm>tasks run
Entry state TASKS
run task on T1
run task on T2
run task on T3
run task on T1 done
run task on T2 done
run task on T3 done
Entry state T1
Entry state T3
Entry state T2
Entry state T1E
Entry state T2E
Entry state T3E
Exit state TASKS
Entry state JOIN
Exit state JOIN
Entry state READY
sm>
----
== Washer
Washer is a sample demonstrating a use of a history state to recover a
running state configuration with a simulated power off situation.
Anyone ever used a washing machine knows that if you can somehow pause
the program it will continue from a same state when lid is closed.
This kind of behaviour can be implemented in a state machine by using
a history pseudo state.
image::images/statechart6.png[width=500]
.States
[source,java,indent=0]
----
include::samples/demo/washer/Application.java[tags=snippetB]
----
.Events
[source,java,indent=0]
----
include::samples/demo/washer/Application.java[tags=snippetC]
----
.Configuration - states
[source,java,indent=0]
----
include::samples/demo/washer/Application.java[tags=snippetAA]
----
.Configuration - transitions
[source,java,indent=0]
----
include::samples/demo/washer/Application.java[tags=snippetAB]
----
Lets see an example how this state machine actually works.
[source,text]
----
sm>sm start
Entry state RUNNING
Entry state WASHING
State machine started
sm>sm event RINSE
Exit state WASHING
Entry state RINSING
Event RINSE send
sm>sm event DRY
Exit state RINSING
Entry state DRYING
Event DRY send
sm>sm event CUTPOWER
Exit state DRYING
Exit state RUNNING
Entry state POWEROFF
Event CUTPOWER send
sm>sm event RESTOREPOWER
Exit state POWEROFF
Entry state RUNNING
Entry state WASHING
Entry state DRYING
Event RESTOREPOWER send
----
What happened in above run:
* State machine is started which causes machine to get initialized.
* We go to RINSING state.
* We go to DRYING state.
* We cut power and go to POWEROFF state.
* State is restored via HISTORY state which takes state machine back
to its previous known state.

View File

@@ -22,6 +22,15 @@ Statemachine is configured and how it leverages Spring's lightweight
IoC containers to simplify the application internals to make it more
manageable.
[NOTE]
====
Configuration examples in this section are not feature complete, i.e.
you always need to have definitions of both states and transitions,
otherwise state machine configuration would be ill-formed. We have
simply made code snippets less verbose by leaving other needed parts
away.
====
=== Configuring States
We'll get into more complex configuration examples a bit later but
lets first start with a something simple. For most simple state
@@ -43,6 +52,20 @@ particular states are sub-states of some other state.
include::samples/DocsConfigurationSampleTests.java[tags=snippetB]
----
=== Configuring Regions
There are no special configuration methods to mark a collection of
states to be part of a orthogonal state. To put it simple, orthogonal
state is created when same hierarchical state machine has multiple set
of states each having a initial state. Because a individual state
machine can only have one initial state, multiple initial states mush
mean that a specific state mush have multiple independent regions.
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests.java[tags=snippetP]
----
=== Configuring Transitions
We support three different types of transitions, `external`,
`internal` and `local`. Transitions are either triggered by a signal
@@ -80,6 +103,93 @@ Actions can be defined with various steps within a state transitions.
include::samples/DocsConfigurationSampleTests.java[tags=snippetE]
----
=== Configuring Pseudo States
_Pseudo state_ configuration is usually done by configuring states and
transitions. Pseudo states are automatically added to state machine as
states.
==== Initial State
Simply mark a particular state as initial state by using `initial()`
method. There are two methods where one takes extra argument to define
an initial action. This initial action is good for example initialize
extended state variables.
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests.java[tags=snippetQ]
----
==== Terminate State
Simply mark a particular state as end state by using `end()` method.
This can be done max one time per individual sub-machine or region.
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests.java[tags=snippetA]
----
==== History State
History state can be defined once for each individual state machine.
You need to choose its state identifier and `History.SHALLOW` or
`History.DEEP` respectively.
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests.java[tags=snippetR]
----
==== Choice State
Choice needs to be defined in both states and transitions to work
properly. Mark particular state as choice state by using `choice()`
method. This state needs to match source state when transition is
configured for this choice.
Transition is configured using `withChoice()` where you define source
state and `first/then/last` structure which is equivalent to normal
`if/elseif/else`. With `first` and `then` you can specify a guard just
like you'd use a condition with `if/elseif` clauses.
Transition needs to be able to exist so make sure `last` is used.
Otherwise configuration is ill-formed.
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests.java[tags=snippetS]
----
==== Fork State
Fork needs to be defined in both states and transitions to work
properly. Mark particular state as choice state by using `fork()`
method. This state needs to match source state when transition is
configured for this fork.
Target state needs to be a super state or immediate states in
regions. Using a super state as target will take all regions into
initial states. Targeting individual state give more controlled entry
into regions.
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests.java[tags=snippetT]
----
==== Join State
Join needs to be defined in both states and transitions to work
properly. Mark particular state as choice state by using `join()`
method. This state doesn't need to match either source states or
target state in a transition configuretion.
Select one target state where transition goes when all source states
has been joined. If you use state hosting regions as source, end
states of a regions are used as joins. Otherwise you can pick any
states from a regions.
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests.java[tags=snippetU]
----
[[sm-factories]]
== State Machine Factories
There are use cases when state machine needs to be created dynamically

View File

@@ -39,6 +39,7 @@ import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.configurers.StateConfigurer.History;
import org.springframework.statemachine.event.StateMachineEvent;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
@@ -354,4 +355,223 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
}
// tag::snippetP[]
@Configuration
@EnableStateMachine
public static class Config10
extends EnumStateMachineConfigurerAdapter<States2, Events> {
@Override
public void configure(StateMachineStateConfigurer<States2, Events> states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.state(States2.S2)
.and()
.withStates()
.parent(States2.S2)
.initial(States2.S2I)
.state(States2.S21)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S2)
.initial(States2.S3I)
.state(States2.S31)
.end(States2.S3F);
}
}
// end::snippetP[]
// tag::snippetQ[]
@Configuration
@EnableStateMachine
public static class Config11 extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.S1, initialAction())
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
@Bean
public Action<States, Events> initialAction() {
return new Action<States, Events>() {
@Override
public void execute(StateContext<States, Events> context) {
// do something initially
}
};
}
}
// end::snippetQ[]
// tag::snippetR[]
@Configuration
@EnableStateMachine
public static class Config12 extends EnumStateMachineConfigurerAdapter<States3, Events> {
@Override
public void configure(StateMachineStateConfigurer<States3, Events> states)
throws Exception {
states
.withStates()
.initial(States3.S1)
.state(States3.S2)
.and()
.withStates()
.parent(States3.S2)
.initial(States3.S2I)
.state(States3.S21)
.state(States3.S22)
.history(States3.SH, History.SHALLOW);
}
}
// end::snippetR[]
// tag::snippetS[]
@Configuration
@EnableStateMachine
public static class Config13 extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.SI)
.choice(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withChoice()
.source(States.S1)
.first(States.S2, s2Guard())
.then(States.S3, s3Guard())
.last(States.S4);
}
@Bean
public Guard<States, Events> s2Guard() {
return new Guard<States, Events>() {
@Override
public boolean evaluate(StateContext<States, Events> context) {
return false;
}
};
}
@Bean
public Guard<States, Events> s3Guard() {
return new Guard<States, Events>() {
@Override
public boolean evaluate(StateContext<States, Events> context) {
return true;
}
};
}
}
// end::snippetS[]
// tag::snippetT[]
@Configuration
@EnableStateMachine
public static class Config14 extends EnumStateMachineConfigurerAdapter<States2, Events> {
@Override
public void configure(StateMachineStateConfigurer<States2, Events> states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.fork(States2.S2)
.state(States2.S3)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S2I)
.state(States2.S21)
.state(States2.S22)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S3I)
.state(States2.S31)
.state(States2.S32)
.end(States2.S3F);
}
@Override
public void configure(StateMachineTransitionConfigurer<States2, Events> transitions)
throws Exception {
transitions
.withFork()
.source(States2.S2)
.target(States2.S22)
.target(States2.S32);
}
}
// end::snippetT[]
// tag::snippetU[]
@Configuration
@EnableStateMachine
public static class Config15 extends EnumStateMachineConfigurerAdapter<States2, Events> {
@Override
public void configure(StateMachineStateConfigurer<States2, Events> states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.state(States2.S3)
.join(States2.S4)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S2I)
.state(States2.S21)
.state(States2.S22)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S3I)
.state(States2.S31)
.state(States2.S32)
.end(States2.S3F);
}
@Override
public void configure(StateMachineTransitionConfigurer<States2, Events> transitions)
throws Exception {
transitions
.withJoin()
.source(States2.S2F)
.source(States2.S3F)
.target(States2.S5);
}
}
// end::snippetU[]
}

View File

@@ -0,0 +1,9 @@
package org.springframework.statemachine.docs;
//tag::snippetA[]
public enum States2 {
S1,S2,S3,S4,S5,
S2I,S21,S22,S2F,
S3I,S31,S32,S3F
}
//end::snippetA[]

View File

@@ -0,0 +1,8 @@
package org.springframework.statemachine.docs;
//tag::snippetA[]
public enum States3 {
S1,S2,SH,
S2I,S21,S22,S2F
}
//end::snippetA[]

View File

@@ -23,12 +23,12 @@ import org.springframework.statemachine.guard.Guard;
@Configuration
public class Application {
//tag::snippetA[]
@Configuration
@EnableStateMachine
static class StateMachineConfig
extends EnumStateMachineConfigurerAdapter<States, Events> {
//tag::snippetAA[]
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
@@ -62,7 +62,9 @@ public class Application {
.state(States.AUTOMATIC, automaticAction(), null)
.state(States.MANUAL);
}
//end::snippetAA[]
//tag::snippetAB[]
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
@@ -108,7 +110,9 @@ public class Application {
.state(States.ERROR)
.event(Events.FIX);
}
//end::snippetAB[]
//tag::snippetAC[]
@Bean
public Guard<States, Events> tasksChoiceGuard() {
return new Guard<States, Events>() {
@@ -121,7 +125,9 @@ public class Application {
}
};
}
//end::snippetAC[]
//tag::snippetAD[]
@Bean
public Action<States, Events> automaticAction() {
return new Action<States, Events>() {
@@ -152,21 +158,25 @@ public class Application {
}
};
}
//end::snippetAD[]
//tag::snippetAE[]
@Bean
public Tasks tasks() {
return new Tasks();
}
//end::snippetAE[]
//tag::snippetAF[]
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
return taskExecutor;
}
//end::snippetAF[]
}
//end::snippetA[]
//tag::snippetB[]
public static enum States {

View File

@@ -41,24 +41,24 @@ public class Application {
throws Exception {
transitions
.withExternal()
.source(States.WASHING)
.target(States.RINSING)
.source(States.WASHING).target(States.RINSING)
.event(Events.RINSE)
.and()
.withExternal()
.source(States.RINSING)
.target(States.DRYING)
.source(States.RINSING).target(States.DRYING)
.event(Events.DRY)
.and()
.withExternal()
.source(States.RUNNING)
.target(States.POWEROFF)
.source(States.RUNNING) .target(States.POWEROFF)
.event(Events.CUTPOWER)
.and()
.withExternal()
.source(States.POWEROFF)
.target(States.HISTORY)
.event(Events.RESTOREPOWER);
.source(States.POWEROFF).target(States.HISTORY)
.event(Events.RESTOREPOWER)
.and()
.withExternal()
.source(States.RUNNING).target(States.END)
.event(Events.STOP);
}
//end::snippetAB[]
@@ -74,7 +74,7 @@ public class Application {
//tag::snippetC[]
public static enum Events {
RINSE, DRY,
RINSE, DRY, STOP,
RESTOREPOWER, CUTPOWER
}
//end::snippetC[]

View File

@@ -1,27 +1,29 @@
+----------------------------------------------------------------------------------+
| |
+----------------------------------------------------------------------------------+
| |
| +--------------------------------------------------------------------+ |
| *-->| RUNNING |-->X |
| +--------------------------------------------------------------------+ |
| | | |
| | +-------------+ +-------------+ +-------------+ | |
| | *-->| WASHING | RINSE | RINSING | DRY | DRYING | | |
| | | |------>| |------>| | | |
| | | | | | | | | |
| | +-------------+ +-------------+ +-------------+ | |
| | | |
| | H | |
| | ^ | |
| +------------|-------------------------------------------------------+ |
| | | |
| |RESTOREPOWER |CUTPOWER |
| | | |
| +-------------+ | |
| | POWEROFF | | |
| | |<----------+ |
| | | |
| +-------------+ |
| |
+----------------------------------------------------------------------------------+
+--------------------------------------------------------------------------------------+
| |
+--------------------------------------------------------------------------------------+
| |
| +--------------------------------------------------------------------+ STOP |
| *-->| RUNNING |------>X |
| +--------------------------------------------------------------------+ |
| | | |
| | +-------------+ +-------------+ +-------------+ | |
| | *-->| WASHING | RINSE | RINSING | DRY | DRYING | | |
| | | |------>| |------>| | | |
| | | | | | | | | |
| | +-------------+ +-------------+ +-------------+ | |
| | | |
| | +-------+ | |
| | |HISTORY| | |
| | +-------+ | |
| | ^ | |
| +------------|-------------------------------------------------------+ |
| | | |
| |RESTOREPOWER |CUTPOWER |
| | | |
| +-------------+ | |
| | POWEROFF | | |
| | |<----------+ |
| | | |
| +-------------+ |
| |
+--------------------------------------------------------------------------------------+