@@ -192,14 +192,23 @@ def default_state(f=None):
192
192
193
193
class StateMachine (metaclass = OrderedClass ):
194
194
'''
195
- This object is designed to be used to easily implement magicbot
196
- components that are basically a big state machine.
195
+ The StateMachine class is used to implement magicbot components that
196
+ allow one to easily define a `finite state machine (FSM)
197
+ <https://en.wikipedia.org/wiki/Finite-state_machine>`_ that can be
198
+ executed via the magicbot framework.
197
199
198
- You use this by defining a class that inherits from ``StateMachine``.
199
- To define each state, you use the :func:`timed_state` decorator on a
200
- function. When each state is run, the decorated function will be
201
- called. Decorated state functions can receive the following
202
- parameters:
200
+ You create a component class that inherits from ``StateMachine``.
201
+ Each state is represented as a single function, and you indicate that
202
+ a function is a particular state by decorating it with one of the
203
+ following decorators:
204
+
205
+ * :func:`@default_state <.default_state>`
206
+ * :func:`@state <.state>`
207
+ * :func:`@timed_state <.timed_state>`
208
+
209
+ As the state machine executes, the decorated function representing the
210
+ current state will be called. Decorated state functions can receive the
211
+ following parameters (all of which are optional):
203
212
204
213
- ``tm`` - The number of seconds since autonomous has started
205
214
- ``state_tm`` - The number of seconds since this state has been active
@@ -209,20 +218,30 @@ class StateMachine(metaclass=OrderedClass):
209
218
will be set to True at the start of each state.
210
219
211
220
To be consistent with the magicbot philosophy, in order for the
212
- state machine to execute its states you must call the
213
- :func:`engage` function upon each execution of the main
214
- robot control loop. If you do not call this function, then
215
- execution will cease unless the current executing state is
216
- marked as ``must_finish``.
221
+ state machine to execute its states you must call the :func:`engage`
222
+ function upon each execution of the main robot control loop. If you do
223
+ not call this function, then execution of the FSM will cease.
224
+
225
+ .. note:: If you wish for the FSM to continue executing state functions
226
+ regardless whether ``engage()`` is called, you must set the
227
+ ``must_finish`` parameter in your state decorator to be True.
217
228
218
- When execution ceases, the :func:`done` function will be called
219
- unless execution was stopped by calling the ``done`` function.
229
+ When execution ceases (because ``engage()`` was not called), the
230
+ :func:`done` function will be called and the FSM will be reset to the
231
+ starting state. The state functions will not be called again unless
232
+ ``engage`` is called.
220
233
221
- As a magicbot component, this contains an ``execute`` function that
234
+ As a magicbot component, StateMachine contains an ``execute`` function that
222
235
will be called on each control loop. All state execution occurs from
223
- within that function call. If you call other components from this
224
- component, you should ensure that your component occurs *before*
225
- the other components in your Robot class.
236
+ within that function call. If you call other components from a
237
+ StateMachine component, you should ensure that your component is defined
238
+ *before* the other components in your Robot class.
239
+
240
+ .. warning:: As StateMachine already contains an execute function,
241
+ there is no need to define your own ``execute`` function for
242
+ a state machine component -- if you override ``execute``,
243
+ then the state machine may not work correctly. Instead,
244
+ use the :func:`@default_state <.default_state>` decorator.
226
245
227
246
Here's a very simple example of how you might implement a shooter
228
247
automation component that moves a ball into a shooter when the
@@ -240,16 +259,25 @@ def fire(self):
240
259
241
260
@state(first=True)
242
261
def begin_firing(self):
262
+ """This function will only be called IFF fire is called and
263
+ the FSM isn't currently in the 'firing' state. If fire
264
+ was not called, this function will not execute."""
243
265
self.shooter.enable()
244
266
if self.shooter.ready():
245
267
self.next_state('firing')
246
268
247
269
@timed_state(duration=1.0, must_finish=True)
248
270
def firing(self):
249
- """This state will continue executing even if engage isn't called"""
271
+ """Because must_finish=True, once the FSM has reached this
272
+ state, this state will continue executing even if engage
273
+ isn't called"""
250
274
self.shooter.enable()
251
275
self.ball_pusher.push()
252
-
276
+
277
+ #
278
+ # Note that there is no execute function defined as part of
279
+ # this component
280
+ #
253
281
...
254
282
255
283
class MyRobot(magicbot.MagicRobot):
0 commit comments