Skip to content

Commit c3b8de0

Browse files
committed
Fix bug where autonomous mode would repeat if ended with a timed state
- Also guarantee that done() will always be called when autonomous mode ends, even if engage() is continually called - Fixes #108
1 parent 53a02e8 commit c3b8de0

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

magicbot/state_machine.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ def next_state_now(self, name):
479479
def done(self):
480480
'''Call this function to end execution of the state machine.
481481
482+
This function will always be called when a state machine ends. Even if
483+
the engage function is called repeatedly, done() will be called.
484+
482485
.. note:: If you wish to do something each time execution ceases,
483486
override this function (but be sure to call
484487
``super().done()``!)
@@ -512,6 +515,7 @@ def execute(self):
512515
# tm is the number of seconds that the state machine has been executing
513516
tm = now - self.__start
514517
state = self.__state
518+
done_called = False
515519

516520
# we adjust this so that if we have states chained together,
517521
# then the total time it runs is the amount of time of the
@@ -526,6 +530,10 @@ def execute(self):
526530
if state.next_state is None:
527531
# If the state expires and it's the last state, if the machine
528532
# is still engaged then it should cycle back to the beginning
533+
# ... but we should call done() first
534+
done_called = True
535+
self.done()
536+
529537
if self.__should_engage:
530538
self.next_state(self.__first)
531539
state = self.__state
@@ -561,7 +569,7 @@ def execute(self):
561569

562570
# execute the state function, passing it the arguments
563571
state.run(self, tm, tm - state.start_time, initial_call)
564-
else:
572+
elif not done_called:
565573
# or clear the state
566574
self.done()
567575

@@ -601,3 +609,8 @@ def on_iteration(self, tm):
601609
self.engage()
602610
self.execute()
603611
self.__engaged = self.is_executing
612+
613+
def done(self):
614+
super().done()
615+
self._StateMachine__should_engage = False
616+
self.__engaged = False

tests/test_magicbot_sm.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,43 @@ def something(self):
259259
sm.on_iteration(None)
260260
assert not sm.is_executing
261261

262+
assert sm.i == 6
263+
264+
def test_autonomous_sm_end_timed_state(wpitime):
265+
class _TM(AutonomousStateMachine):
266+
267+
i = 0
268+
j = 0
269+
VERBOSE_LOGGING = False
270+
271+
@state(first=True)
272+
def something(self):
273+
self.i += 1
274+
if self.i == 3:
275+
self.next_state('timed')
276+
277+
@timed_state(duration=1)
278+
def timed(self):
279+
self.j += 1
280+
281+
sm = _TM()
282+
setup_tunables(sm, 'cname')
283+
284+
sm.on_enable()
285+
286+
for _ in range(5):
287+
wpitime.now += 0.7
288+
sm.on_iteration(None)
289+
assert sm.is_executing
290+
291+
for _ in range(5):
292+
wpitime.now += 0.7
293+
sm.on_iteration(None)
294+
assert not sm.is_executing
295+
296+
assert sm.i == 3
297+
assert sm.j == 2
298+
262299
def test_next_fn():
263300
class _TM(StateMachine):
264301
@state(first=True)
@@ -528,6 +565,10 @@ def a(self):
528565
@timed_state(duration=.01)
529566
def b(self):
530567
self.executed.append('b')
568+
569+
def done(self):
570+
super().done()
571+
self.executed.append('d')
531572

532573
sm = _SM()
533574
setup_tunables(sm, 'cname')
@@ -547,4 +588,4 @@ def b(self):
547588

548589
wpitime.now += 0.02
549590

550-
assert sm.executed == ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'b']
591+
assert sm.executed == ['a', 'b', 'd', 'a', 'b', 'd', 'a', 'b', 'd', 'a', 'b']

0 commit comments

Comments
 (0)