A problem with debugging a simple state machine.

524 visualizações
Pular para a primeira mensagem não lida

Alexey Morozov

não lida,
26 de mar. de 2014, 08:22:0626/03/2014
para squirrel-st...@googlegroups.com
Hi!

It looks like I misunderstand something in methodology of a state machine. Please take a look at a simple example:

package test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.StateMachineConfiguration;
import org.squirrelframework.foundation.fsm.TransitionType;
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.annotation.Transit;
import org.squirrelframework.foundation.fsm.annotation.Transitions;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;

import test.TestFSM.CharContext;
import test.TestFSM.TokenState;
import test.TestFSM.CharClass;

@States({
  @State(name="NO_TOKENS", entryCallMethod="cleanupTokens", initialState = true),
  @State(name="OUT_OF_TOKENS"),
  @State(name="IN_WORD"),
  @State(name="IN_NUMBER"),
  @State(name="STOP", isFinal = true)

})
@Transitions({
  @Transit(from="NO_TOKENS", on = "ALPHA",   to="IN_WORD",    callMethod = "startToken"),
  @Transit(from="NO_TOKENS", on = "DIGIT",   to="IN_NUMBER",  callMethod = "startToken"),
  @Transit(from="NO_TOKENS", on = "EOL",     to="STOP"),
  @Transit(from="NO_TOKENS", on = "SPACE",   to="NO_TOKENS",  type = TransitionType.INTERNAL),

  @Transit(from="OUT_OF_TOKENS", on = "ALPHA", to="IN_WORD",    callMethod = "startToken"),
  @Transit(from="OUT_OF_TOKENS", on = "DIGIT", to="IN_NUMBER",  callMethod = "startToken"),
  @Transit(from="OUT_OF_TOKENS", on = "EOL",   to="STOP"),
  @Transit(from="OUT_OF_TOKENS", on = "SPACE",   to="OUT_OF_TOKENS",  type = TransitionType.INTERNAL),

  @Transit(from="IN_WORD",   on = "ALPHA",  to="IN_WORD",       type = TransitionType.INTERNAL),
  @Transit(from="IN_WORD",   on = "DIGIT",  to="IN_WORD",       type = TransitionType.INTERNAL), // oh really?
  @Transit(from="IN_WORD",   on = "EOL",    to="STOP",          callMethod = "completeWordToken"),
  @Transit(from="IN_WORD",   on = "SPACE",  to="OUT_OF_TOKENS", callMethod = "completeWordToken"),

  @Transit(from="IN_NUMBER", on = "ALPHA",  to="IN_WORD",       callMethod = "completeNumberToken"),
  @Transit(from="IN_NUMBER", on = "DIGIT",  to="IN_NUMBER",     type = TransitionType.INTERNAL),
  @Transit(from="IN_NUMBER", on = "EOL",    to="STOP",          callMethod = "completeNumberToken"),
  @Transit(from="IN_NUMBER", on = "SPACE",  to="OUT_OF_TOKENS", callMethod = "completeNumberToken"),
})

@StateMachineParameters(stateType = TokenState.class, eventType = CharClass.class, contextType = CharContext.class)
public class TestFSM extends AbstractUntypedStateMachine {
  public enum CharClass {
    ALPHA,
    DIGIT,
    SPACE,
    EOL,
  }

  public enum TokenState {
    NO_TOKENS,
    OUT_OF_TOKENS,
    IN_WORD,
    IN_NUMBER,
    STOP,
  }

  static class CharContext {
  }

  private final static Logger logger = LoggerFactory.getLogger(TestFSM.class);

  protected void cleanupTokens(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
    logger.info("cleanupTokens");
  }

  protected void startToken(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
    logger.info("startToken");
  }

  protected void completeWordToken(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
    logger.info("completeWordToken");
  }

  protected void completeNumberToken(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
    logger.info("completeNumberToken");
  }

  public static void main(String[] args) {
    final UntypedStateMachineBuilder builder =
        StateMachineBuilderFactory.create(TestFSM.class, TokenState.class, CharClass.class, CharContext.class);
    CharClass[][] addresses = {
        { CharClass.ALPHA, CharClass.ALPHA, CharClass.ALPHA, CharClass.SPACE, CharClass.DIGIT, CharClass.DIGIT, CharClass.ALPHA },
        { CharClass.SPACE, CharClass.ALPHA, CharClass.ALPHA, CharClass.ALPHA, CharClass.DIGIT, CharClass.DIGIT, CharClass.ALPHA },
        { CharClass.SPACE, CharClass.ALPHA, CharClass.ALPHA, CharClass.ALPHA, CharClass.DIGIT, CharClass.DIGIT, CharClass.SPACE },
    };
    for(final CharClass[] address: addresses) {
      final CharContext ctx = new CharContext();
      final boolean isDebug = false; // set to true, and the machine will break just right on the first event
      final TestFSM fsm = builder.newUntypedStateMachine(TokenState.NO_TOKENS, StateMachineConfiguration.create().setDebugEnabled(isDebug));
      fsm.start(ctx);
      for(final CharClass event: address) {
        fsm.fire(event, ctx);
      }
      fsm.fire(CharClass.EOL, ctx);
      logger.info("Processed an address");
    }
  }
}

===
This example works pretty well and produces expected output. But if isDebug is set to true, then the first invocation of fsm.fire() dies with the following output:

==
2014-03-26 18:46:23,382  INFO [main] (TestFSM.java:76) - cleanupTokens
2014-03-26 18:46:23,454  INFO [main] (StateMachineLogger.java:66) - TestFSM(BywcpHEV51): Transition from "NO_TOKENS" on "ALPHA" with context "test.TestFSM$CharContext@74b1896c" begin.
2014-03-26 18:46:23,457  INFO [main] (StateMachineLogger.java:94) - Before execute method call action "startToken:0" (1 of 1).
2014-03-26 18:46:23,458  INFO [main] (TestFSM.java:80) - startToken
2014-03-26 18:46:23,462 ERROR [main] (StateMachineLogger.java:85) - TestFSM(BywcpHEV51): Transition from "NO_TOKENS" to "NO_TOKENS" on "ALPHA" caused exception.
org.squirrelframework.foundation.exception.SquirrelRuntimeException: 00010012 : couldn't invoke 'public abstract void org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionListener.afterExecute(org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionEvent)' with [org.squirrelframework.foundation.fsm.impl.AbstractExecutionService$AfterExecActionEventImpl@635194a1] on org.squirrelframework.foundation.fsm.impl.AbstractStateMachine$5@cd2b045: null.
    at org.squirrelframework.foundation.util.ReflectUtils.invoke(ReflectUtils.java:337)
    at org.squirrelframework.foundation.event.ListenerMethod.invokeMethod(ListenerMethod.java:63)
    at org.squirrelframework.foundation.event.PolymEventDispatcher.fireEvent(PolymEventDispatcher.java:68)
    at org.squirrelframework.foundation.component.impl.AbstractSubject.fireEvent(AbstractSubject.java:84)
    at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.doExecute(AbstractExecutionService.java:93)
    at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.executeActions(AbstractExecutionService.java:131)
    at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.execute(AbstractExecutionService.java:139)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvent(AbstractStateMachine.java:195)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvents(AbstractStateMachine.java:246)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.internalFire(AbstractStateMachine.java:279)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:299)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:310)
    at org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine.fire(AbstractUntypedStateMachine.java:26)
    at test.TestFSM.main(TestFSM.java:105)
org.squirrelframework.foundation.exception.SquirrelRuntimeException: 00010012 : couldn't invoke 'public abstract void org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionListener.afterExecute(org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionEvent)' with [org.squirrelframework.foundation.fsm.impl.AbstractExecutionService$AfterExecActionEventImpl@635194a1] on org.squirrelframework.foundation.fsm.impl.AbstractStateMachine$5@cd2b045: null.
    at org.squirrelframework.foundation.util.ReflectUtils.invoke(ReflectUtils.java:337)
    at org.squirrelframework.foundation.event.ListenerMethod.invokeMethod(ListenerMethod.java:63)
    at org.squirrelframework.foundation.event.PolymEventDispatcher.fireEvent(PolymEventDispatcher.java:68)
    at org.squirrelframework.foundation.component.impl.AbstractSubject.fireEvent(AbstractSubject.java:84)
    at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.doExecute(AbstractExecutionService.java:93)
    at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.executeActions(AbstractExecutionService.java:131)
    at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.execute(AbstractExecutionService.java:139)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvent(AbstractStateMachine.java:195)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvents(AbstractStateMachine.java:246)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.internalFire(AbstractStateMachine.java:279)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:299)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:310)
    at org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine.fire(AbstractUntypedStateMachine.java:26)
    at test.TestFSM.main(TestFSM.java:105)
Exception in thread "main" org.squirrelframework.foundation.exception.TransitionException: 00010017 : Transition from 'NO_TOKENS' to 'IN_WORD' on 'ALPHA' with context 'test.TestFSM$CharContext@74b1896c' when invoking action 'UNKNOWN' caused exception '00010012 : couldn't invoke 'public abstract void org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionListener.afterExecute(org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionEvent)' with [org.squirrelframework.foundation.fsm.impl.AbstractExecutionService$AfterExecActionEventImpl@635194a1] on org.squirrelframework.foundation.fsm.impl.AbstractStateMachine$5@cd2b045: null.'.
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvent(AbstractStateMachine.java:217)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvents(AbstractStateMachine.java:246)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.internalFire(AbstractStateMachine.java:279)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:299)
    at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:310)
    at org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine.fire(AbstractUntypedStateMachine.java:26)
    at test.TestFSM.main(TestFSM.java:105)
==

Please help me to understand what's wrong with the SM.

Thank you in advance,
Alexey Morozov

He Henry

não lida,
26 de mar. de 2014, 08:56:0826/03/2014
para squirrel-st...@googlegroups.com
Hi,

Your sample code has no problem in my environment (state machine version 0.3.2). With debug mode enabled, state machine debug information logged out as expect.
BTW, you only need to pass state machine type when creating untyped state machine builder.

final UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(TestFSM.class);

Thanks,
Henry


在 2014年3月26日星期三UTC+8下午8时22分06秒,Alexey Morozov写道:

Alexey Morozov

não lida,
27 de mar. de 2014, 04:08:1027/03/2014
para squirrel-st...@googlegroups.com
Hi!

It's strange but even with the new squirrel-foundation (0.3.2) I get the same error. I've tried both java-1.6.0.26-sun and the latest java-1.7.0.51-oracle.

Here's the command line string generated by Maven AppAssembler plugin:

 /usr/lib/jvm/java/bin/java -classpath /home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/etc:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/org/slf4j/slf4j-api/1.7.5/slf4j-api-1.7.5.jar:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/org/squirrelframework/squirrel-foundation/0.3.2/squirrel-foundation-0.3.2.jar:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/com/google/guava/guava/16.0/guava-16.0.jar:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/log4j/log4j/1.2.12/log4j-1.2.12.jar:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/org/slf4j/slf4j-log4j12/1.6.1/slf4j-log4j12-1.6.1.jar:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/org/apache/commons/commons-lang3/3.1/commons-lang3-3.1.jar:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/org/mvel/mvel2/2.1.8.Final/mvel2-2.1.8.Final.jar:/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo/org/test/Test/0.0.1-SNAPSHOT/Test-0.0.1-SNAPSHOT.jar -Dapp.name=TestFSM -Dapp.pid=20267 -Dapp.repo=/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler/repo -Dapp.home=/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler -Dbasedir=/home/alex/work/AddressRecognizer/tmp/Test/target/appassembler test.TestFSM.

TestFSM is basically the same as the one I provided in the first message, with SM creation expression as you suggested above.
Could you suggest how to fix the problem?


Thank you in advance,
Alexey Morozov

He Henry

não lida,
28 de mar. de 2014, 02:34:5328/03/2014
para squirrel-st...@googlegroups.com
Hi,

From the error stack it seems that error happened in StateMachineLogger.onAfterActionExecuted method. You can set a break point in this method and see what is the problem.
I guess maybe something wrong around stop watch. However, as I cannot reproduce the issue in my environment, I may need your help to find real cause.

Thanks,
Henry

在 2014年3月27日星期四UTC+8下午4时08分10秒,Alexey Morozov写道:
Responder a todos
Responder ao autor
Encaminhar
0 nova mensagem