Skip to content

Latest commit

 

History

History
128 lines (86 loc) · 4.84 KB

basics.rst

File metadata and controls

128 lines (86 loc) · 4.84 KB

Basic Usage

State machines are constructed using :pymachinist.constructFiniteStateMachine.

Inputs, Outputs, States

Before a machine can be constructed its inputs, outputs, and states must be defined. These are all defined using :pytwisted.python.constants.

turnstile.py

Transitions

Also required is a transition table. The transition table is defined using :pymachinist.TransitionTable. :pyTransitionTable instances are immutable and have several methods for creating new tables including more transitions.

turnstile.py

First, define how the FARE_PAID input is handled in the LOCKED state: output DISENGAGE_LOCK and change the state to the ACTIVE.

turnstile.py

Next, define how the ARM_TURNED input is handled in the UNLOCKED state: output ENGAGE_LOCK and change the state to ACTIVE.

turnstile.py

Last, define two transitions at once for getting out of the ACTIVE state (in this model DISENGAGE_LOCK and ENGAGE_LOCK activate a physical device to change the lock state; the state machine then waits for an input indicating the physical device has completed the desired operation).

addTransitions is a convenient way to define more than one transition at once. It is equivalent to several addTransition calls.

turnstile.py

One thing to note here is that the outputs are lists of symbols from the output set. The output of any transition in Machinist is always a list. This simplifies the definition of output symbols in many cases and grants more flexibility in how a machine can react to an input. You can see one way in which this is useful already: the transitions out of the ACTIVE state have no useful outputs and so use an empty list. The handling of these lists of outputs is discussed in more detail in the next section, Output Executors.

Output Executors

The last thing that must be defined in order to create any state machine using Machinist is an output executor. In the previous sections we saw how the outputs of a state machine must be defined and how transitions must specify the outputs of each transition. The outputs that have been defined so far are only symbols: they can't have any impact on the world. This makes them somewhat useless until they are combined with code that knows how to turn an output symbol into an actual output. This is the output executor's job. Machinist provides a helper for writing classes that turn output symbols into side-effects:

turnstile.py

When used as the output executor for a state machine, the methods of this instance will be called according to the names of the outputs that are produced. That is, when a transition is executed which has :pyOutput.ENGAGE_LOCK as an output, :pyoutput_ENGAGE_LOCK will be called. This lets the application define arbitrary side-effects to associate with outputs. In this well-defined way the otherwise rigid, structured, explicit state machine can interact with the messy world.

Construction

Having defined these things, we can now use :pyconstructFiniteStateMachine to construct the finite state machine.

turnstile.py

Apart from the inputs, outputs, states, transition table, and output executor, the only other argument to pay attention to in this call right now is initial. This defines the state that the state machine is in immediately after :pyconstructFiniteStateMachine returns.

Receiving Inputs

Having created a state machine, we can now deliver inputs to it. The simplest way to do this is to pass input symbols to the :pyreceive method:

turnstile.py

If we combine all of these snippets <turnstile-example> and call :pycycle the result is a program that produces this result:

turnstile

import turnstile

turnstile

turnstile.cycle()

turnstile

Disengaging the lock. Engaging the lock.

turnstile-main

execfile("turnstile.py", {"__name__": "__main__"})

turnstile-main

Disengaging the lock. Engaging the lock.