Scala with FSM
FSM stands for Finite State Machine.
Lets break this down,
State : State is a description of the status of a system that is waiting to execute a transition. A transition is a set of actions to be executed when a condition is fulfilled or when an event is received.
Its the best way to describe it.
lets have a look at a sample i wrote
package test
import akka.actor.{Props, ActorSystem, FSM} import akka.persistence.fsm.PersistentFSM.FSMState import akka.testkit.{ImplicitSender, TestKit} import org.scalatest.{Matchers, FlatSpecLike, BeforeAndAfterAll} //States sealed trait CarState extends FSMState case class Driving() extends CarState {override def identifier: String = "Driving"} case class Parked() extends CarState {override def identifier: String = "Parked"} case class Crashed() extends CarState {override def identifier: String = "Crashed"} case class BeingRepaired() extends CarState {override def identifier: String = "BeingRepaired"} //Data sealed trait CarEntity { var carType: String var carModel: String var yearOfManufactor: String } case object UnInitialized extends CarEntity { var carType = "" var carModel = "" var yearOfManufactor = "" } case object VWGolf extends CarEntity { override var carType: String = "VW" override var carModel: String = "GOLF" override var yearOfManufactor: String = "2016" } class Car(carDetails : CarEntity) extends FSM[CarState, CarEntity] { startWith(Parked(), carDetails) //When we are Parked, we can only go Driving or BeingRepaired
when(Parked()){ case Event(event: Driving,_) => //"_" is the Data part of the event goto(event) case Event(event: BeingRepaired,_) => goto(event) } when(Driving()){ case Event(event: Parked,_) => goto(event) case Event(event: Crashed,_) => println("We just crashed!!!") goto(event) }
when(Crashed()){ case Event(event: BeingRepaired,_) => goto(event) }
//for capturing all state transactions onTransition { case (fromState, toState) => { if (fromState != toState) { val oldData = stateData val newData = nextStateData println(s"$fromState -> $toState") } } }
whenUnhandled { case Event(event, state) => println(s"We cannot go from $stateName, to state $event (data = $stateData)") stay }
initialize()
} class CarTestSpec extends TestKit(ActorSystem("CarTestSpec")) with ImplicitSender with FlatSpecLike with Matchers with BeforeAndAfterAll { behavior of "CarTestSpec" it should "should validate correct FSM transaction" in { val vwGolf = system.actorOf(Props(new Car(VWGolf))) vwGolf ! Driving() vwGolf ! Parked() vwGolf ! Crashed() vwGolf ! Driving() vwGolf ! Crashed() expectNoMsg() } override def afterAll { TestKit.shutdownActorSystem(system) } }
The following test prints out
Parked() -> Driving()
Driving() -> Parked()
We cannot go from Parked(), to state Crashed() (data = VWGolf)
Parked() -> Driving()
We just crashed!!!
Driving() -> Crashed()
Of course we could very easily attached data to each state transition like “total mile travel” or something like that, and update the car object in this class at each transition.