[statemachine] properly prevent transition when clearing error

This commit is contained in:
Eric Mastro 2023-02-20 12:52:13 +11:00
parent 59576e76d8
commit 1c0dc582bd
No known key found for this signature in database
GPG Key ID: AD065ECE27A873B9
2 changed files with 30 additions and 4 deletions

View File

@ -51,7 +51,9 @@ proc schedule*(machine: Machine, event: Event) =
proc checkTransitions(machine: Machine) = proc checkTransitions(machine: Machine) =
for transition in machine.transitions: for transition in machine.transitions:
if transition.trigger(machine, machine.state) and if transition.trigger(machine, machine.state) and
(machine.state == nil or machine.state == transition.prevState or transition.prevState of AnyState): (machine.state == nil or
machine.state == transition.prevState or
transition.prevState of AnyState):
machine.schedule(Event.transition(machine.state, transition.nextState)) machine.schedule(Event.transition(machine.state, transition.nextState))
proc setValue*[T](prop: TransitionProperty[T], value: T) = proc setValue*[T](prop: TransitionProperty[T], value: T) =
@ -74,7 +76,7 @@ proc scheduler(machine: Machine) {.async.} =
if fut.failed(): if fut.failed():
try: try:
machine.errored.setValue(true) # triggers transitions machine.errored.setValue(true) # triggers transitions
machine.errored.setValue(false) # clears error without triggering transitions machine.errored.value = false # clears error without triggering transitions
machine.lastError = fut.error # stores error in state machine.lastError = fut.error # stores error in state
except AsyncQueueFullError as e: except AsyncQueueFullError as e:
error "Cannot set transition value because queue is full", error = e error "Cannot set transition value because queue is full", error = e

View File

@ -167,8 +167,7 @@ suite "async state machines":
test "checks declarative transitions after current state finishes running": test "checks declarative transitions after current state finishes running":
machine.start(state4) machine.start(state4)
machine.slotsFilled.setValue(2) # on wrong starting state, so stays on state4 machine.slotsFilled.setValue(2) # no trigger conditions met yet
await sleepAsync(1.millis)
# manually move to State3, where the trigger and the previous state will # manually move to State3, where the trigger and the previous state will
# be checked # be checked
machine.schedule(Event.transition(state4, state3)) machine.schedule(Event.transition(state4, state3))
@ -182,3 +181,28 @@ suite "async state machines":
check eventually machine.state of State5 check eventually machine.state of State5
check not machine.errored.value # errored state has been cleared check not machine.errored.value # errored state has been cleared
check machine.lastError.msg == "some error" check machine.lastError.msg == "some error"
test "schedules transitions in order of declaration":
var checked = false
machine = MyMachine.new(@[
Transition.new(
state3,
state4,
proc(m: Machine, s: State): bool =
MyMachine(m).slotsFilled.value == 2
),
Transition.new(
state3,
state5,
proc(m: Machine, s: State): bool =
checked = true
MyMachine(m).slotsFilled.value == 2
)]
)
machine.slotsFilled = machine.newTransitionProperty(0)
machine.start(state3)
machine.slotsFilled.setValue(2)
check eventually runs == [0, 0, 1, 1, 0]
check machine.state of State4
check checked
#3->5 transition was checked but not run because state had already moved to 4