Akka FSM:状态向同一状态的过渡并不像预期的那样发生

时间:2016-11-06 09:21:20

标签: scala akka fsm

在FSM的实现中(旧故事:对Elevator / Lift进行建模),我试图看看我是否可以测试FSM是否遵循从同一状态转换到同一状态。看起来,它没有发生,但令我感到惊讶的是因为根据this对StackOverFlow的讨论,该解决方案在Akka 2.4.x中可用。

这是代码的相关部分:

  class LiftCarriageWithMovingState extends Actor
  with FSM[LiftState,LiftData]
  with ActorLogging
{

  import context._

  val mxTimeToWaitStopping = 500 milliseconds
  private var pendingPassengerRequests: Vector[NextStop] = Vector.empty
  private var currentFloorID = 0 // Always start at Ground Floor



  val timeToReachNextFloor = 1000 millis  // up  or down, same time taken

  private val dontCare: StateFunction = { /* ... */ }

  private val powerYourselfOff: StateFunction = { /* ... */ }

  private val powerYourselfOn: StateFunction = { /* ... */ }

  private val beReady: StateFunction = { /* ... */ }

  private val moveToWaitingPassenger: StateFunction = { /* ... */ }

  private val transportPassengerToDest: StateFunction = {/* ... */ }

  private val actWhenStoppedForLongEnough: StateFunction = {
    case Event(StateTimeout,_)   =>
      if (this.pendingPassengerRequests isEmpty) {
        println("timeout in Stopped, no passenger requests pending")
        goto (Stopped)
      }
      else {
        println("timeout in Stopped, moving to floor(" + this.pendingPassengerRequests.head + ")")
        simulateMovementTo(this.pendingPassengerRequests.head)
        goto(Moving)
      }
  }

  startWith(PoweredOff,InitialData)

  when (PoweredOff) (powerYourselfOn orElse
                     dontCare)

  when (PoweredOn)  (powerYourselfOff orElse
                     beReady orElse
                     dontCare)

  when (Ready)      (moveToWaitingPassenger   orElse
                     transportPassengerToDest )


  when (Stopped)   (actWhenStoppedForLongEnough orElse
                    moveToWaitingPassenger      orElse
                    transportPassengerToDest    )

  when (Moving)              {
    case Event(ReachedFloor(floorID, PurposeOfMovement.ToWelcomeInAnWaitingPassenger),_) =>
      currentFloorID = pendingPassengerRequests.head.floorID
      pendingPassengerRequests = pendingPassengerRequests.tail
      goto (Stopped) forMax(this.mxTimeToWaitStopping)

    case Event(ReachedFloor(floorID,PurposeOfMovement.ToAllowATransportedPassengerAlight),_) =>
      currentFloorID = pendingPassengerRequests.head.floorID
      pendingPassengerRequests = pendingPassengerRequests.tail
      goto (Stopped) forMax(this.mxTimeToWaitStopping)

   // .... Other events
  }

  whenUnhandled {
    // Event handlers...
  }

  onTransition {
    case Stopped -> Stopped => {
      println("Remaining in Stopped ...")
    }
  }

  // Rest of the Actor

我已经修剪了不必要的部分,因为重点在于: 当演员收到

Event(ReachedFloor(floorID, PurposeOfMovement.ToWelcomeInAnWaitingPassenger),_)

当它处于Moving状态时,它会切换到Stopping状态,并指定超时。当FSM停止时该计时器触发时,应该执行的处理程序被命名(如上所示):

actWhenStoppedForLongEnough

在这里,我正在检查某个退出条件,如果我不满意,我会再次切换回相同的停止状态。我现在正在使用 goto(),但原始代码 stay()

我也设置了一个 onTransition 块(再次显示在上面),以证明过渡确实发生了。

onTransition {
    case Stopped -> Stopped => {
      println("Remaining in Stopped ...")
    }
  }

当我运行代码时, println 语句根本不会被执行。

我也有一个测试代码;相关的测试用例是这样的:

var carriage: ActorRef = _

  override def beforeAll(): Unit = {
    super.beforeAll()

    carriage = TestActorRef(Props(new LiftCarriageWithMovingState))
    carriage ! SubscribeTransitionCallBack(testActor)
  }

  "A LiftCarriage" must {
    "be ready, when it settles down after being PoweredOn" in {

      expectMsg(Duration(100, TimeUnit.MILLISECONDS), new CurrentState(carriage,PoweredOff))
      carriage ! InstructedToPowerOn
      expectMsg (new Transition(carriage,PoweredOff,PoweredOn))
      carriage ! BeReady
      expectMsg((new Transition(carriage,PoweredOn,Ready)))
    }

    "move to where a passenger is waiting, if ready" in {
      carriage ! ReportCurrentFloor
      expectMsg(StoppedAt(0))
      carriage ! PassengerIsWaitingAt(3)
      expectMsg(new Transition(carriage,Ready,Moving))
      expectMsg(Duration(5000, TimeUnit.MILLISECONDS), new Transition(carriage,Moving, Stopped))
      carriage ! ReportCurrentFloor
      expectMsg(StoppedAt(3))

    }
    "let a passenger in and transport her to her destination" in {
      carriage ! ReportCurrentFloor
      expectMsg(StoppedAt(3))
      carriage ! PassengerRequestsATransportTo(Vector(7))
      expectMsg(new Transition(carriage,Stopped,Moving))
      expectMsg(Duration(5000, TimeUnit.MILLISECONDS), new Transition(carriage,Moving,Stopped))
      carriage ! ReportCurrentFloor
      expectMsg(StoppedAt(7))

      // This always times out and fails.
      expectMsg(Duration(5000, TimeUnit.MILLISECONDS), new Transition(carriage,Stopped,Stopped)) 

    }

我正在努力思考我理解中的差距是否存在,但是失败了。任何提示?

这是 build.sbt

name := """akka-sample-fsm-scala"""

version := "2.4"

scalaVersion := "2.11.8"

libraryDependencies ++= Seq(
  // https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor_2.11
  "com.typesafe.akka" % "akka-actor_2.11" % "2.4.12",
// https://mvnrepository.com/artifact/org.scalatest/scalatest_2.11
  "org.scalatest" % "scalatest_2.11" % "2.1.3",
  // https://mvnrepository.com/artifact/com.typesafe.akka/akka-testkit_2.11,
  "com.typesafe.akka" % "akka-testkit_2.11" % "2.3.15"
)

另一个question - 与我的很相似 - 早些时候被问过,但我没有看到任何回复。可能是,答案已经找到但尚未分享。

0 个答案:

没有答案