Last active
August 29, 2015 13:57
-
-
Save Arneball/9349865 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.concurrent.LinkedBlockingDeque | |
import scala.annotation.tailrec | |
object `MyOwnFsm;)` extends App{ | |
// val musta = new Musta | |
// musta ! "to5" | |
// musta ! "to6" | |
// musta ! "should crash" | |
// println | |
val pc = new PorcheCounter | |
1 to 3 foreach { _ => pc ! Car("porsche") } | |
pc ! "status" | |
} | |
case class Car(brand: String, year: Int=util.Random.nextInt) | |
sealed trait GuyStatus | |
case object CountingMode extends GuyStatus | |
case object AwakeGuy extends GuyStatus | |
class PorcheCounter extends FSM[GuyStatus, List[Car]]{ | |
when(AwakeGuy){ | |
case Event(car @ Car("porsche", _), state) => stay using car::state | |
case Event("count", _) => goto(CountingMode) | |
} | |
when(CountingMode){ | |
case Event("work", _) => goto(AwakeGuy) | |
} | |
onUnhandled{ | |
case Event("status", sd) => | |
println(sd.mkString(",")) | |
stay | |
} | |
startWith(AwakeGuy -> Nil) | |
} | |
class Musta extends FSM[Int, String]{ | |
onTransition{ | |
case 3 -> _ => println("Going from three") | |
case 5 -> 6 => println("In transition 5->6") | |
} | |
onUnhandled{ | |
case t => | |
println(s"Got unhandled message $t") | |
stay | |
} | |
when(3) { | |
case Event(m @ "to5", "runken") => | |
println(s"in 3 $m, going to 5") | |
goto(5) using "batiken" | |
} | |
when(5) { | |
case Event(m @ "to6", _) => | |
println(s"in 5 $m") | |
goto(6) | |
case Event(m, _) => | |
println(s"in 5 $m") | |
stay | |
} | |
startWith(3 -> "runken") | |
} | |
trait FSM[State, Data] { | |
private type SD = (State, Data) | |
private type MessageParser = PartialFunction[Any, Transition] | |
case class Transition private[FSM](state: ->, data: Data) { | |
def using(data: Data) = copy(data=data) | |
} | |
case class Event private[FSM](message: Any, data: Data) | |
case class -> private[FSM](from: State, to: State) | |
private val queue = new LinkedBlockingDeque[Any] | |
private val functions = collection.mutable.HashMap[State, MessageParser]() | |
private val thread = new Thread{ | |
@tailrec override final def run() = { | |
val mess = Event(queue.take(), currentData) | |
val ordinaryFun = functions.get(currentState) | |
// do transition execution | |
def transit(transission: ->) = transitions.foreach{ | |
case pf if pf.isDefinedAt(transission) => pf(transission) | |
case _ => // noop | |
} | |
// apply the message to the Messageparser | |
def handle(pf: MessageParser) = { | |
val Transition(t @ _ -> newstate, newdata) = pf(mess) | |
transit(t) | |
leState = (newstate, newdata) | |
} | |
(ordinaryFun, unhandler) match { | |
case (Some(fun), _) if fun.isDefinedAt(mess) => handle(fun) | |
case (_, Some(unhand)) if unhand.isDefinedAt(mess) => handle(unhand) | |
case _ => throw new RuntimeException(s"No function matching $mess") | |
} | |
run() // tailcall ;) | |
} | |
} | |
thread.start() | |
implicit private def stateArrow(states: (State, State)): -> = ->(states._1, states._2) | |
private var leState: SD = _ | |
private type Trans = PartialFunction[->, Any] | |
private var transitions = Nil: List[Trans] | |
def onTransition(pf: Trans) = transitions = pf::transitions | |
private var unhandler: Option[MessageParser] = None | |
def onUnhandled(pf: MessageParser) = unhandler = Some(pf) | |
def stay: Transition = Transition(currentState -> currentState, currentData) | |
def goto(state: State): Transition = { | |
Transition(currentState -> state, currentData) | |
} | |
def !(a: Any) = queue.offer(a) | |
/** Setter for state handling shit */ | |
def when(state: State)(function: MessageParser) = { | |
functions(state) = function | |
} | |
def currentState: State = leState._1 | |
def currentData: Data = leState._2 | |
def startWith(state: SD) = leState = state | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment