@@ -96,7 +96,10 @@ def timed_state(f=None, *, duration=None, next_state=None, first=False, must_fin
96
96
'''
97
97
If this decorator is applied to a function in an object that inherits
98
98
from :class:`.StateMachine`, it indicates that the function
99
- is a state that will run for a set amount of time unless interrupted
99
+ is a state that will run for a set amount of time unless interrupted.
100
+
101
+ It is guaranteed that a timed_state will execute at least once, even if
102
+ it expires prior to being executed.
100
103
101
104
The decorated function can have the following arguments in any order:
102
105
@@ -170,8 +173,10 @@ def default_state(f=None):
170
173
If this decorator is applied to a method in an object that inherits
171
174
from :class:`.StateMachine`, it indicates that the method
172
175
is a default state; that is, if no other states are executing, this
173
- state will execute. There can only be a single default state in a
174
- StateMachine object.
176
+ state will execute. If the state machine is always executing, the
177
+ default state will never execute.
178
+
179
+ There can only be a single default state in a StateMachine object.
175
180
176
181
The decorated function can have the following arguments in any order:
177
182
@@ -346,7 +351,7 @@ def _build_states(self):
346
351
raise InvalidWrapperError (errmsg )
347
352
348
353
# Can't define states that are named the same as things in the
349
- # base class, will cause issues. Catch it early.
354
+ # base class, will cause issues. Catch it early.
350
355
if hasattr (StateMachine , state .name ):
351
356
raise InvalidStateName ("cannot have a state function named '%s'" % state .name )
352
357
@@ -514,21 +519,21 @@ def execute(self):
514
519
new_state_start = tm
515
520
516
521
# determine if the time has passed to execute the next state
517
- # -> intentionally comes first,
518
- if state is not None and state .expires < tm :
519
-
520
- previous_state = state
522
+ # -> intentionally comes first
523
+ if state is not None and state .ran and state .expires < tm :
524
+ new_state_start = state .expires
521
525
522
526
if state .next_state is None :
523
- state = None
527
+ # If the state expires and it's the last state, if the machine
528
+ # is still engaged then it should cycle back to the beginning
529
+ if self .__should_engage :
530
+ self .next_state (self .__first )
531
+ state = self .__state
532
+ else :
533
+ state = None
524
534
else :
525
535
self .next_state (state .next_state )
526
- new_state_start = state .expires
527
536
state = self .__state
528
-
529
- # Reset the expired time to prevent the state from expiring
530
- # immediately if it's ran a second time
531
- previous_state .expires = 0xffffffff
532
537
533
538
# deactivate the current state unless engage was called or
534
539
# must_finish was set
@@ -583,7 +588,7 @@ def on_enable(self):
583
588
self .__engaged = True
584
589
585
590
def on_iteration (self , tm ):
586
- # TODO, remove the on_iteration function in 2017?
591
+ # TODO, remove the on_iteration function in 2017?
587
592
588
593
# Only engage the state machine until its execution finishes, otherwise
589
594
# it will just keep repeating
0 commit comments