Package org.squirrelframework.foundation.fsm.samples

Source Code of org.squirrelframework.foundation.fsm.samples.DecisionStateSampleTest$DecisionMaker

package org.squirrelframework.foundation.fsm.samples;

import com.google.common.collect.Lists;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.UntypedAnonymousAction;
import org.squirrelframework.foundation.fsm.UntypedStateMachine;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
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
    }

    static StringBuilder logger;

    @Before
    public void setUp() {
        logger = new StringBuilder();
    }

    @StateMachineParameters(stateType = DecisionState.class, eventType = DecisionEvent.class, contextType = Integer.class)
    static class DecisionStateMachine extends AbstractUntypedStateMachine {

        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 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('.');
            }
        }

    }

    static class DecisionMaker extends UntypedAnonymousAction {
        // wrap any local state in action
        int transitionCount = 0;
        final String name;

        DecisionMaker(String name) {
            this.name = name;
        }

        @Override
        public void execute(Object from, Object to, Object event, Object context, UntypedStateMachine stateMachine) {
            DecisionState typedFrom = (DecisionState)from;
            DecisionState typedTo = (DecisionState)to;
            DecisionEvent typedEvent = (DecisionEvent)event;
            if(typedFrom!=null && typedTo==null) {
                logger.append("leftMakeDecision");
                // local state clean up at some situation, e.g. at some criteria restore transitionCount
            } else if(typedFrom==null && typedTo!=null) {
                logger.append("enterMakeDecision");
                // local state initialize at some situation
            } else if(typedFrom!=null && typedFrom!=null && typedEvent==DecisionEvent.A2ANY) {
                // perform dynamic transitions
                Integer typedContext = (Integer)context;
                if(typedContext < 10) {
                    stateMachine.fire(DecisionEvent.A2B, context);
                    transitionCount++;
                } else if(typedContext < 20) {
                    stateMachine.fire(DecisionEvent.A2C, context);
                    transitionCount++;
                } else if(typedContext < 40) {
                    stateMachine.fire(DecisionEvent.A2D, context);
                    transitionCount++;
                } else {
                    stateMachine.fire(DecisionEvent.ANY2A, context);
                }
                // e.g. if transitionCount>10 then change transition router rules
            }
        }

        @Override
        public String name() {
            return name;
        }
    }

    DecisionStateMachine buildStateMachine() {
        DecisionStateMachine fsm;
        final UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(DecisionStateMachine.class);
        final DecisionMaker decisionMaker = new DecisionMaker("DecisionMaker");

        // _A is decision state for A and it is invisible to user
        builder.defineNoInitSequentialStatesOn(DecisionState.A, DecisionState._A);
        builder.onEntry(DecisionState.A).callMethod("enterA");
        builder.onExit(DecisionState.A).callMethod("leftA");
        builder.onEntry(DecisionState._A).perform(decisionMaker);
        builder.onExit(DecisionState._A).perform(decisionMaker);

        // transition to left state A are all started with _A which means all transition cause exit state A must be router by _A
        builder.transitions().from(DecisionState._A).toAmong(DecisionState.B, DecisionState.C, DecisionState.D).
                onEach(DecisionEvent.A2B, DecisionEvent.A2C, DecisionEvent.A2D).callMethod("a2b|a2c|_");

        builder.transitions().fromAmong(DecisionState.B, DecisionState.C, DecisionState.D).
                to(DecisionState.A).on(DecisionEvent.ANY2A);

        // use local transition avoid invoking state A exit functions when entering its decision state
        builder.localTransitions().between(DecisionState.A).and(DecisionState._A).
                onMutual(DecisionEvent.A2ANY, DecisionEvent.ANY2A).
                perform(Lists.newArrayList(decisionMaker, null));

        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.fire(DecisionEvent.ANY2A, 1000);
        Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.A));
        Assert.assertTrue("enterA".equals(fsm.consumeLog()));

        fsm.fire(DecisionEvent.A2ANY, 30);
        Assert.assertTrue(fsm.getCurrentState().equals(DecisionState.D));
        Assert.assertTrue("enterMakeDecision.leftMakeDecision.leftA".equals(fsm.consumeLog()));

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

}
TOP

Related Classes of org.squirrelframework.foundation.fsm.samples.DecisionStateSampleTest$DecisionMaker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.