The state manager is a program that manages and enforces the experiment state diagram. It also provides a mechanism for external programs to:
Request state transitions
Become aware of the current state
Be notified of state transitions.
The state diagram that is enforced by the state manager is shown below:
Here's a description of the states and the possible transitions out of each state.
This state means that the experiment has been defined but that one or more pieces have not yet been started. The BOOT transition implies an intent to start all of the pieces that make up the experiment. The BOOT transition places the experiment in the Booting state.
This state implies the pieces that make up the experiment are being started. The Boot manager described in The Boot utility responds to entering this state by creating rings and starting programs defined by a list of experiment created by the experiment configuration editor described in Experiment Configuration utility.
The READY transition indicates a successful start of all of the components. the FAIL transition indicates that at least one component could not be created. If the boot manager is used it will initiate a FAIL transition if it detects an error while creating the experiment components. It will also initiate a READY transition if all of the components started correctly.
Indicates that the experiment is ready to take data but is not yet taking data. If the boot manager is being used, it monitors the programs it started and if any of them exit (normally or abnormally), it intiates a FAIL transition and shuts down all of the programs it is managing.
The BEGIN transition indicates a desire to start taking data.
Means that the experiment is acquiring data. If any component fails or if any component was not able to start taking data due to an unrecoverable error, it can initiate a FAIL transition taking the program back to the NotReady state.
The END transition indicates a desire to stop taking data and makes the transition to the Ready state.
The state diagram enforces this diagram. For example, it will refuse to respond to a BOOT request while in the Ready state, returning an error condition if that request is made.
To run the state manager, you must first define some environment variables. This is done sourcing the dasetup.bash script from the root of the installation directory for your DAQ distribution.
If, for example, NSCLDAQ is installed in /usr/opt/daq/11.0:
makes the necessary environment variable definitions.
Once the daqsetup.bash script has been run, you can start up the experiment configuration tool via the command:
The state manager obtains and registers two ports named StatePublish and StateRequest from the NSCLDAQ port manager.
By itself, the state manager does nothing. Its value is in its interactions with other programs.
The state manager provides a Transition request port. Clients can connect to that port and request one or more transitions.
The state manager also provides a Transition subscription port. Clients can use that port to monitor the current state as well as state transitions.
The state manager periodically broadcasts the current state to clients on the transition subscription port. This allows a new client to become aware of the state within a second or so of connecting.
Whenever the state manager successfully makes a state transition a state message is sent to the subscription port.
Below is an annotated example of a Tcl state manager client.
Example 36-1. Tcl client of the state manager
## # @file tcltest1.tcl # @brief Test the state monitor tcl callbacks. # @author Ron Fox <fox@nscl.msu.edu> lappend auto_path [file join $env(DAROOT) TclLibs] package require statemanager set reqURI [lindex $argv 0] set subURI [lindex $argv 1] statemanager::statemonitor start $reqURI $subURI statemanager::statemonitor register NotReady test statemanager::statemonitor register Ready test statemanager::statemonitor register Booting test proc test {from to} { puts "Transition $from -> $to" if {$to eq "Ready"} { statemanager::transition BEGIN } } vwait forever
The URI's for the state manager are of the form>
tcp://hostname:portnum
where hostname is the name
of the host running the state manager and
portnum
is the number of the
TCP/IP port on which the state manager is listening for
connection for that specific service.
The parameters are the request and subscription URIs that tell the state manager API how to connect with the state manager server.
This can happen either because the program receives its first state broadcast before it ever sees a transition, or it sees a transition broadcast.
from
and to
parameters of the proc. The first time
the state becomes known (either via a state broadcast or if
a transition is received prior to a state broadcast), the
prior state is unknown and the from parameter is set to the
empty string.
Note this is just a toy application and this code is nonesensical as it means that whenever the run stops it will start again.
forever
variable is modified
(it never is). This allows the program to respond to
state transitions that are posted as events. A Tcl/Tk
application automatically gets an event loop and does not
need this statement.
The C++ state manager api lives in the $DAQLIB/libStateMonitor.so shared library. The header <CStateMonitor.h>. The DAQLIB environment variable is defined by the daqsetup.bash script in the top level directory of NSCLDAQ 11.0 or later.
Let's look at an annotated C++ program that is a client to the state manager:
Example 36-2. C++ client for the state manager.
#include "CStateMonitor.h" #include <iostream> void NotReady(CStateMonitor* pMonitor, std::string from, std::string to, void* arg) { const char* p = reinterpret_cast<const char*>(arg); std::cout << "NotReady transition " << from << "->" << to << " (" << p << ")\n"; } void Ready(CStateMonitor* pMonitor, std::string from, std::string to, void* arg) { const char* p = reinterpret_cast<const char*>(arg); std::cout << "Ready transition " << from << "->" << to << " (" << p << ")\n"; } int main(int argc, char**argv) { std::string reqURI = argv[1]; std::string stateURI = argv[2]; CStateMonitor mon(reqURI, stateURI); mon.Register("NotReady", NotReady, const_cast<char*>("Some text")); mon.Register("Ready", Ready, const_cast<char*>("Different text")); mon.run(); }
NotReady
will be a state handler.
State handlers are registered to be called when one or
more of a set of states has been entered. State handlers
are passed a pointer to the state monitor, the state names
involved in the transition (from
is the
empty string if the prior state is not known), and an
application specific parameters.
arg
), usable as a
char* again.
CStateMonitor
is a class that
encapsulates the API to the state manager. This line
creates an instanc eof that class, providing it the
URI's it needs to connect to the state manager ports.
Note that while this program does not demonstrate that
fact, the class has a method requestTransition
tht can request state transition from the
state manager.
CStateMonitor
object.
These lines register intereste in the NotReady and Ready states.
run
method on a
CStateMonitor
object enters its
event loop. The monitor processes data from the
state manager and invokes callbacks for registered
states as they are entered. This method won't exit,
however there are classes that do allow you to
interleave a state manager event loop with other
processing. See the reference material for more information.
This section shows two sample Python programs that interface with the state manager. The first program monitors state transitions. The second program simply takes lines from stdin and pushes them as state transition requests to the statemanager, outputing the status of the request to stdout.
Both of the examples assume that the environment variables defined by $DAQROOT/daqsetup.bash have been incorporated into you shell environment. This is necessary as the PYTHONPATH variable is modified to allow NSCLDAQ specific packages to be imported.
Example 36-3. Monitoring the state manager in Python
from nscldaq.statemanager import StateMonitor import sys ## usage: # python StateMonitorTest1 statemanager-req-uri state-manager-pub-uri # requestUri = sys.argv[1] pubUri = sys.argv[2] # We'll just use unbound methods for our callbacks: def NotReadyHandler(monitor, fromState, toState, cbarg): print('Not Ready state entered: %r -> %r (%s)' %(fromState, toState, cbarg)) def ReadyHandler(monitor, fromState, toState, cbarg): print('Ready state entered: %r -> %r (%s)' %(fromState, toState, cbarg)) mon = StateMonitor.StateMonitor(requestUri, pubUri) mon.register('NotReady', NotReadyHandler, 'poof') mon.register('Ready', ReadyHandler, 'poof poof') mon.run()
All nscldaq related packages for Python are sub packages in the nscldaq package tree to ensure their names don't collide with other packages you might use within your scripts.
This program accepts those URI's on the command line The first URI is the URI that corresponds to the state transition request port, the second corresponds to the state/transition publication port.
As we will see in the second example, if you know which host the state manager is running under, it is possible to ask the nscl port manager for the ports it use and, from them, construct the approprate URIs
monitor
The state monitor API object.
fromState
The prior state. This is None if the previous state is not known. It is the string state name of the previous state otherwise.
toState
String containing the state that has just been entered.
cbarg
A parameter that is provided by the application when the state handler is registered and is not interpreted/modified in any way by the API.
StateMonitor
class represents
the API that accesses the state manager server. This
line creates an instance of that class providing
it with the URI's that describe how to connect to both
the transition request and state/transition publication
ports.
This line of code and the next line register interest, and callback handlers, for the NotReady and Ready states.
StateMonitor
.
This loop processes state and transition publications from
the state manager server dispatching interesting ones
to registered callback handlers.
Example 36-4. Command line state changer
from nscldaq.statemanager import StateMonitor from nscldaq.statemanager import Utilities import sys import getpass username = getpass.getuser() requestPort = Utilities.getPort('localhost', 'StateRequest', username) transitionPort = Utilities.getPort('localhost', 'StatePublish', username) requestUri = 'tcp://localhost:%d' % (requestPort) transitionUri = 'tcp://localhost:%d' %(transitionPort) mon = StateMonitor.StateMonitor(requestUri, transitionUri) while 1: line = sys.stdin.readline() if not line: break reply = mon.requestTransition(line[0:-1]) print("Reply: %s" % (reply))
The StateMonitor package provides a high level API to the state manager. The Utiltities package provides several utilities useful both to the state manager and its clients.
The sys and getpass packages are standard Python packages that are also used by this application.
The first three linse of this section of code use the
utility functions to get the ports the state manager
has requested from the port manager. The
StateRequest service handles requests
for transitions while the StatePublish
service publishes states and transitions. The statemanager
is also assumed to be run by the same user as the one
running this program. The getPort
function will throw a RuntimeException
if it is not able to locate the requested services.
The next two lines encode the port into URIs so that
they can be passed to the constructor for the
StateMonitor
class.
StateMonitor
class. This object provides a method
requestTransition
that will be
used to request state transitions.
sys.stdin.readline()
returns an
empty string when the end of file is reached on stdin.
When that happens we exit. Note that realine
includes the newline line terminator in the data it returns.