package require sequence sequence::newState db state-name sequence::newTransition db from to sequence::add db sequence-name trigger set stepno [sequence::addStep db seqname program predelay postdelay] sequence::rmvstep db name stepno set stepno [sequence::insertStep db seqname program priorstepno predelay postdelay] set stepno [sequence::prependStep db seqname program predelay postdelay] sequence::rmvState db state-name sequence::rmvSequence db name sequence::reachable db state-name sequence::listReachableStates db state-name sequence::listLegalFromStates db state-name sequence::listStates db sequence::listLegalNextStates db sequence::isLegalTransition db next sequence::currentState db sequence::listSequences db sequence::listSteps db sequence-name sequence::addMonitor db sequence stepno monitor-base-command sequence::runSequence db name ?endproc? sequence::transition db transition ?endscript? sequence::getCurrentTransition sequence::relayOutput text
This package provides several facilities:
The capability to define or modify a state machine.
The ability to describe sequences of programs that will be run as a result of state transitions in the state machine. The programs come in two major flavors; transient and persistent. Sequence steps that run a transient program stall until that program exits. Sequence stesps that run persistent programs start the program and continue on. Persistent programs can, furthermore be declared to be critical. If a critical program exists, a transition to the special, SHUTDOWN stage is forced.
The ability to introspect the state machine and sequences.
The ability to perform state transitions. State transitions, as well as many other package features depend on an event loop. Since this package is normally run from within the mananager which, itself is a customized TclHTTPD sever, that server normally provides the event loop.
A server that supports the ability to relay output to programs that are connected to it via a TCP/IP socket stream.
In the entry points below the formal arguments named
db
are always an sqlite3 database connection
command that designates the database that will be operated on.
Defines a new state in the state machine. The new
state definition is stored in the configuration database
connecected to the sqlite3 command
db
. The new state will be named by the
new-state
parameter. The new state
name must be unique.
Note that unless transitions into this state are defined, the state remains an orphan in the state diagram, in the sense that it cannot be reached via state transition operations. See ::sequence::newTransition below.
Defines a new state transition in the database
connected to db
. The transtion
is defined between the named states from
as a precursor and to
as a successor.
Both the from
and
to
states must already be defined.
Defines a new sequence in the database connected to
db
. The sequence will be named
sequence-name
which must not
yet be the name of a sequence. The
steps in the sequence will be triggered by a transition
into the state named by the trigger
.
Initially the sequence is empty and will do nothing if triggered. sequence::addStep and other sequence step editing commands are used to define the steps in a sequence and their order.
Adds a new step to the end of the sequence
seqname
. The step will run
the program named progname
. The optional
predelay
and postdelay
parameters are a delay in seconds before and after executing
the step. These default to 0 if not
specified.
The return value of this command is the step number that was assigned to the step. Step numbers are real numbers and are used to ensure steps are executed in the correct order. Real values are used to allow steps to be inserted between existing steps without needing to renumber the entire sequence.
Removes the step numbered stepno
from the sequence named by name
.
Inserts a new sequence step after the existing step with a step
numbered priorstepno
. The remaining
parameters are the same as the parameters for
::sequence::insertStep.
Naturally if priorstepno
designates
the last step in seqname
, this
command acts identically to ::sequence::addStep.
The command returns the number of the new step.
Adds a new first step to the beginning of seqname
All the remaining parameters are the same as
::sequence::addStep. The command returns
the number of the new step.
Removes the state state-name
from
the state machine definition. This can have far reaching consequences
so use with care:
All sequences triggered by entry to this state are removed along with all steps in those sequences.
Depending on the actual graph of the state machine, this may leave the system with unreachable states.
Depending on the current state of the system, this may leave the system in a state with no valid successor states.
Removes the definition of the sequence name
.
Note that all steps in the sequence are also removed from
the database.
Returns the number of valid precursor states to
the state state-name
. A precursor
state is a state that has a transition to
state-name
defined. If the
returned value is 0,
state-name
is orphaned in the sense
that there it cannot be reached from any other state in the
state machine via a legal transition.
Returns a list of the states that can be reached by transition
from the state state-name
.
Returns a list of state names that can transition to
state-name
.
Lists all the states defined in the system. Using this and sequence::listReachableStates as well as sequence::listLegalFromStates it is possible to programmtically reconstruct the full state diagram.
Returns a list of states that can be transitioned to from the current state. Note that the system's current state is stored in the database.
Returns a boolean that is true valued if next
is a legal next state for the current state. This is a
shortcut for
expr $next in [::sequence::listLegalNextStates].
Returns the name of the current state.
Returns a list of dicts that describe the currently defined set of sequences. The key/values in this dict are:
Integer primary key of the sequence.
Name of the sequence.
Name of the state whose that triggers the execution of the sequence. An attempted legal transition to this named state will trigger sequence execution.
The primary key of the transition named in transition_name.
Returns a list of dicts that describe the steps in the sequence
named by sequence-name
.
Each of these dicts has the following key/value pairs:
Real number that is the step number of that step. Steps are executed from lowest step number to highest step number.
Name of the program to run when the step is executed.
Primary key of the program to run when the step is executed.
Number of seconds to delay prior to running the program (integer).
Number of seconds to delay after running the program (integer).
Attaches an output monitor to a
step in a sequence. The name of the sequence is
sequence
. The monitor is attached to
step stepno
.
monitor-base-command
is the base command
of a command ensemble described in
OUTPUT MONITOR OBJECTS that is the
output monitor for the program in that step.
Runs the named sequence name
.
If endproc
is specified, it is called
when the sequence has completed. Note that running sequences
require the event loop.
The endproc
command, if specified, is called
with the SequenceRunner object, the completion status and
a reason for the completion.
SequenceRunner objects are described in
SEQUENCE RUNNER OBJECTS below.
The status can be either OK or
ABORTED with meanings that should be clear.
The reason is a user readable reason for the completion.
This is an empty string for
Runs a transition to the state specified by
transition
. For the transition to
succeed, all steps in all sequences must complete
successfully. If endscript
is specified,
it is invoked at the end of the attempted transition.
Note that as with sequence::runSequence,
the event loop is needed to execute the transition.
If specified, the end script is invoked with the database command, the Transition Manager (see TRANSITION MANAGER OBJECTS below), and completion status. The completion status is one of OK successful completion, ABORTED the sequence executaion was aborted (the system is not in a well defined state). SHUTDOWN, sequence execution failed and the system forced a transition to the SHUTDOWN initial state.
Returns the base command of the transition manager that is performing the current transition. If no transition is in progress, an empty string is returned. See TRANSITION MANAGER OBJECTS for information about transition managers.
Relays the text
to all clients of the
output relay server. See
OUTPUT RELAY SERVER below for more information.
When the sequence package performs a transition, it instantiates a transition manager object. This object, runs all of the sequences triggered by the transition and invokes any script associated with the end of the transition. The Tcl/Tk event loop is required to both to perform sequences and the transition as a whole.
The transition manager object instances are are instances of a
snit::type named sequence::TransitionManager
.
This section describes the options and methods of that object.
-database
This readonly (must be set at construction time) option must be set with the database connection command. The connection command must be valid for the duration of the transition.
-type
This readonly option must be set with the desired final state of the system.
-endscript
This option can be modified any time before the transition ends. If, when the transition ends, it is non empty, it is assumed to contain a script that is evaluated at the global level with the database command, the transition manager instance command, and the transition completion status. The completion status is one of OK successful completion, ABORTED the sequence executaion was aborted (the system is not in a well defined state). SHUTDOWN, sequence execution failed and the system forced a transition to the SHUTDOWN initial state.
start
Starts the transition managed by the instance. If the transition is already in progress, this method reports an error.
If there are no sequences triggered by this transition, the transition completes immediately. Otherwise, the sequences are executed from the event loop.
abort
Aborts the transition. The state remains unchanged, however it's not, in general, possible to reverse sequence steps already performed. The current sequence is aborted. The transition end is signalled with the status ABORTED.
isActive
Returns a boolean that is true valued if the transition is active and false valued if not.
currentSequence
Returns the name of the currently active sequence in the transition.
Sequence runner objects are used to manage the execution of the
steps in a sequence. They are instances of the snit::type
sequence::SequenceRunner
. Sequence execution
requires the event loop.
-database
The SQLite3 database connection command
-name
Name of the sequence.
-steps
List of step definition dicts. These are the output of sequence::listSteps
-endcommand
If not an empty string, this is a script
that will be invoked when the sequence
finishes running. The script will receive
three actual parameters.
The command that represents the
sequence::SequenceRunner
instance, the completion status which will be
one of NORMAL or
ABORT and a text string that,
if the completion reason was
ABORT describes why the sequence
was aborted.
The following public methods are exported by
sequence::SequenceRunner
objects.
They are intended for use by the
sequence::TransitionManager
as
it executes sequences associated with its transition, however
they can be used for test purposes as well.
start
Starts sequence execution. Sequence execution relies on the event loop to dispatch its execution of each step.
abort
?reason?
Aborts sequence execution. The sequence will
complete immediately. Any
-endcommand
will be executed with
the reason ABORT and the
reason text string given by the optional
reason
parameter which,
if not provided defaults to
Programmatically aborted
WHen the sequence package is loaded it starts a server on an advertised service name. The name of the service is the value of the environment variable SERVICE_NAME with -outputMonitor appended. Note that when the manager is run, the environment variable SERVICE_NAME is set to the name of the service the manager advertises for REST interactions.
For test purposes, if the SERVICE_NAME environment variable is not defined the service name dummy-outputMonitor is used instead.
The server started in this manner accepts connections from clients and sends them the text that's passed to the command sequence::relayOutput. All clients receive this text.
By default, output from programs that are run in sequences are sent to the ::sequence::relayOutput command. In practice, this means that this service provides combined output from all programs both persistent and transitory.