Skip to content

Commit

Permalink
add sample case about decision state
Browse files Browse the repository at this point in the history
  • Loading branch information
hekailiang committed Jul 12, 2014
1 parent 0b7285c commit 67cbcea
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
5 changes: 5 additions & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ Examples
* **ATM State Machine - Example on context insensitive typed state machine**
The sample code could be found in package *"org.squirrelframework.foundation.fsm.atm"*.

* **Decision State Machine - Example on local transition**
This example demonstrated how to leverage local transition and nested state to create a decision node.
![DecisionStateMachine](http://hekailiang.github.io/squirrel/images/decisionfsm.png)
This example can be found in package *"org.squirrelframework.foundation.fsm.samples.DecisionStateSampleTest"*.

* **Simple CSS Parser - Example usage of fluent API**
This example illustrates how to parse incoming characters by define parser grammar in state machine.
![SimpleCssParser](http://hekailiang.github.io/squirrel/images/SimpleCssParser.png)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package org.squirrelframework.foundation.fsm.samples;

import junit.framework.Assert;
import org.junit.Test;
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.annotation.State;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.annotation.States;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;

/**
* Created by kailianghe on 7/12/14.
*/
public class DecisionStateSampleTest {

enum DecisionState {
A, _A, B, C, D
}

enum DecisionEvent {
A2ANY, A2B, A2C, A2D, ANY2A
}

@States({
@State(name="A", entryCallMethod = "enterA", exitCallMethod = "leftA"),
@State(parent="A", name="_A", entryCallMethod = "enterMakeDecision", exitCallMethod = "leftMakeDecision") // _A is decision state for A
})
@StateMachineParameters(stateType = DecisionState.class, eventType = DecisionEvent.class, contextType = Integer.class)
static class DecisionStateMachine extends AbstractUntypedStateMachine {

StringBuilder logger = new StringBuilder();

public void enterA(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
logger.append("enterA");
}

public void leftA(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
logger.append("leftA");
}

public void enterMakeDecision(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
logger.append("enterMakeDecision");
}

public void leftMakeDecision(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
logger.append("leftMakeDecision");
}

public void makeDecision(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
if(context < 10) {
fire(DecisionEvent.A2B, context);
} else if(context < 20) {
fire(DecisionEvent.A2C, context);
} else if(context < 40) {
fire(DecisionEvent.A2D, context);
} else {
fire(DecisionEvent.ANY2A, context);
}
}

public void a2b(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
logger.append("a2b");
}

public void a2c(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
logger.append("a2c");
}

public void a2d(DecisionState from, DecisionState to, DecisionEvent event, Integer context) {
logger.append("a2d");
}

String consumeLog() {
final String result = logger.toString();
logger = new StringBuilder();
return result;
}

@Override
protected void beforeActionInvoked(Object fromState, Object toState, Object event, Object context) {
if (logger.length() > 0) {
logger.append('.');
}
}

}

DecisionStateMachine buildStateMachine() {
DecisionStateMachine fsm;
final UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(DecisionStateMachine.class);
builder.transition().from(DecisionState._A).to(DecisionState.B).on(DecisionEvent.A2B).callMethod("a2b");
builder.transition().from(DecisionState._A).to(DecisionState.C).on(DecisionEvent.A2C).callMethod("a2c");
builder.transition().from(DecisionState._A).to(DecisionState.D).on(DecisionEvent.A2D).callMethod("a2d");

builder.transition().from(DecisionState.B).to(DecisionState.A).on(DecisionEvent.ANY2A);
builder.transition().from(DecisionState.C).to(DecisionState.A).on(DecisionEvent.ANY2A);
builder.transition().from(DecisionState.D).to(DecisionState.A).on(DecisionEvent.ANY2A);

// use local transition avoid invoking state A exit functions
builder.localTransition().from(DecisionState.A).to(DecisionState._A).on(DecisionEvent.A2ANY).callMethod("makeDecision");
builder.localTransition().from(DecisionState._A).to(DecisionState.A).on(DecisionEvent.ANY2A);

fsm = builder.newUntypedStateMachine(DecisionState.A);
return fsm;
}

@Test
public void testDecisionStateMachine() {
DecisionStateMachine fsm = buildStateMachine();

fsm.start();
Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.A));
Assert.assertTrue("enterA".equals(fsm.consumeLog()));

fsm.fire(DecisionEvent.A2B, 30);
Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.A));
Assert.assertTrue("".equals(fsm.consumeLog()));

fsm.fire(DecisionEvent.A2ANY, 100);
Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.A));
Assert.assertTrue("enterMakeDecision.leftMakeDecision".equals(fsm.consumeLog()));


fsm.fire(DecisionEvent.A2ANY, 5);
Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.B));
Assert.assertTrue("enterMakeDecision.leftMakeDecision.leftA.a2b".equals(fsm.consumeLog()));


fsm.fire(DecisionEvent.ANY2A, 1000);
Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.A));
Assert.assertTrue("enterA".equals(fsm.consumeLog()));


fsm.fire(DecisionEvent.A2ANY, 15);
Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.C));
Assert.assertTrue("enterMakeDecision.leftMakeDecision.leftA.a2c".equals(fsm.consumeLog()));

fsm.terminate();
// System.out.println(fsm.exportXMLDefinition(true));
}

}

0 comments on commit 67cbcea

Please sign in to comment.