Skip to content

Instantly share code, notes, and snippets.

@zag
Created February 21, 2018 13:32
Show Gist options
  • Save zag/69997ac130c55e0c01f09acdf65eefd2 to your computer and use it in GitHub Desktop.
Save zag/69997ac130c55e0c01f09acdf65eefd2 to your computer and use it in GitHub Desktop.
simple fsm
//
// +-----+
// | off |
// +-----+
// |
// v
// +--------+
// | red | <- +
// +--------+ /
// | /
// v /
// +--------+ /
// | yellow | /
// +--------+ /
// | /
// V /
// +-------+ /
// | green | /
// +-------+ /
// | /
// +----------+
//
//
// 1) базовое устройство
// 2) данные стейта
// 3) работа с сайд эффектами
// 4) работа ui
Cancelable {
fun cancel()
}
interface Event {}
object Tick : Event {}
object Start : Event {}
object Stop : Event {}
data class LightboxSnapshot(
val stateId: String, // off, red, green, yellow
val meta: Meta
) : Event
interface SnapshotStore {
fun write(snapshot: LightboxSnapshot): Unit
fun read(): LightboxSnapshot
}
interface LogStore {
fun append(event: Event)
fun snapshot(snapshot: LightboxSnapshot)
fun readAll(): List[Event]
}
interface Listener {
fun onOff()
fun onRed()
fun onYellow()
fun onGreen()
}
interface EventBox {
fun listen(listener: Listener)
fun triggerOff()
fun triggerRed()
fun triggerYellow()
fun triggerGreen()
}
class Lightbox {
private var state : State = OffState()
private val store : SnapshotStore = {...}
public val eventBox : EventBox = {...}
fun handle(event: Event) {
state = state.handle(event)
}
fun snapshot() {
var stateId = state.className
var meta = state.meta
var snapshot = LightboxSnapshot(stateId, meta)
store.write(snapshot)
}
fun restore() {
for(event in store.readAll()) {
handle(event)
}
}
fun ticksCount: Long = state.meta.counter
fun timeout(() -> Unit, time: Long): Cancelable
data class Meta(val counter: Long) {
fun inc(): Meta {
return Meta(counter + 1)
}
}
abstarct class State(val meta: Meta) {
fun handle(event: Event) : State {
store.append(event)
return when(event) {
is Tick -> handleTick(event)
is Start -> handleStart(event)
is Stop -> handleStop(event)
is LightboxSnapshot -> restore(event)
else ->
log.error("unexpected event: $event")
state
}
}
open fun handleTick(event: Tick) : State
open fun handleStart(event: Start) : State
open fun handleStop(event: Stop) : State
fun restore(snapshot: Snapshot): State {
val snapshot = store.read()
return when(snapshot.stateId) {
"off" -> OffState()
"red" -> RedState(snapshot.meta)
"yellow" -> YellowState(snapshot.meta)
"green" -> GreenOffState(snapshot.meta)
}
}
}
class OffState : State(Meta(0)) {
init { eventBox.triggerOff() }
override fun handleTick(event: Tick) : State {
log.debug("ignore tick in off state")
return this
}
override fun handleStart(event: Start) : State {
return gotoRed()
}
override fun handleStop(event: Stop) : State {
log.debug("ignore stop in off state")
return this
}
fun gotoRed(): State = RedState(meta)
}
class RedState(meta: Meta) : State(meta) {
init { eventBox.triggerRed() }
var tick = timeout(() -> Lightbox.this.handle(Tick), 10_000)
override fun handleTick(event: Tick) : State {
return gotoYellow()
}
override fun handleStart(event: Start) : State {
log.debug("ignore stop in red state")
return this
}
override fun handleStop(event: Stop) : State {
tick.cancel()
return gotoOff()
}
fun gotoYellow(): State = YellowState(meta.inc())
fun gotoOff(): State = OffState()
}
class YellowState(meta: Meta) : State(meta) {
init { eventBox.triggerYellow() }
var tick = timeout(() -> Lightbox.this.handle(Tick), 10_000)
override fun handleTick(event: Tick) : State {
return gotoGreen()
}
override fun handleStart(event: Start) : State {
log.debug("ignore stop in yellow state")
return this
}
override fun handleStop(event: Stop) : State {
tick.cancel()
return gotoOff()
}
fun gotoGreen(): State = GreenState(meta.inc())
fun gotoOff(): State = OffState()
}
class GreenState(meta: Meta) : State(meta) {
init { eventBox.triggerGreen() }
var tick = timeout(() -> Lightbox.this.handle(Tick), 10_000)
override fun handleTick(event: Tick) : State {
return gotoRed()
}
override fun handleStart(event: Start) : State {
log.debug("ignore stop in green state")
return this
}
override fun handleStop(event: Stop) : State {
tick.cancel()
return gotoOff()
}
fun gotoRed(): State = RedState(meta.inc())
fun gotoOff(): State = OffState()
}
}
val state = YellowState(Meta(20)).gotoGreen()
assert state.meta.counter = 21
val fsm = Lightbox()
fsm.eventBox.listen(new Listener {
fun onOff() { logger.info("off!") }
fun onRed() { logger.info("ref!") }
fun onYellow() { logger.info("yellow!") }
fun onGreen() { logger.info("green!") }
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment