ocd.states – OCD state machines

Module containing the states necessary to run OCD

The classes here use the transitions package for the states definitions and transitions.

TCS events

Transition event

When any of the states change a TCS-like event with topic STATE_CHANGE_TOPIC and the following payload, based on tcs_lib.tcs_event.TCSDict.

Topic Event
ocd.states.change machine_name (string): name of the state machine that just had the transition
transition (string): name of the transition
old_state (string): name of the old state
new_state (string): name of the new state
msg (string): extra message attached to the transition
traceback (string): full error traceback if some problem happened
__data_time (string): time at which the event payload is created
__wire_time (string): time at which the event is sent
__data (bool): whether an other event with data is coming (always false)
__system, __source, __key (string): components of the topic

State event

When a TCS_HEARTBEAT_TOPIC is received, emit a TCS-like event with topic STATE_NAME_TOPIC and the following payload, based on tcs_lib.tcs_event.TCSDict.

Topic Event
ocd.states.state machine_name (string): name of the state machine that just had the transition
state (string): name of the current state
__data_time (string): time at which the event payload is created
__wire_time (string): time at which the event is sent
__data (bool): whether an other event with data is coming (always false)
__system, __source, __key (string): components of the topic

Variables

ocd.states.TCS_HEARTBEAT_TOPIC = 'tcs.receiver.heartbeat'

Topic of the TCS heartbeat events. The machines in this module emit their state when such an event arrive

ocd.states.STATE_CHANGE_TOPIC = 'ocd.states.change'

Topic used when emitting TCS-like events on state change

ocd.states.STATE_NAME_TOPIC = 'ocd.states.state'

Topic used when emitting TCS-like events when a TCS_HEARTBEAT_TOPIC event comes in

BaseState – The state machines API

class ocd.states.BaseState[source]

Bases: object

This class defines the mandatory API for state machines used by OCD. The properties and methods defined here must be reimplemented in derived classes

Attributes:
topics

list of topics handled by the state machine. Defaults to

trigger_topics

list of topics that can trigger a machine state change

machine

transitions.Machine instance representing the states

handle_event(self, tcs_topic, tcs_event)[source]

Handle a TCS event and decide whether the status need to be changed or not.

Parameters:
tcs_topic : string

topic of the event

tcs_event : dict

event to handle

Returns:
bool

True if the event has been handled, False otherwise

machine

transitions.Machine instance representing the states

topics

list of topics handled by the state machine. Defaults to trigger_topics plus TCS_HEARTBEAT_TOPIC

trigger_topics

list of topics that can trigger a machine state change

RunShotState – Keep track of the shots run by OCD

class ocd.states.RunShotState[source]

Bases: ocd.states.BaseState, ocd.states.CallbackMixin, ocd.states.StateMixin

State machine that keeps track of shots run by OCD.

The method handle_event() accepts events from the TCS stream and, if relevant, use the event payloads to modify the internal state.

See the documentation for a graph of the states and explicit transitions.

Warning

all the transitions called to_{state}, with {state} one of the state names, are created by the transitions.Machine and allow to force the transition to the corresponding state.

Attributes:
exp_f : string

format string to build the exposure name

machine : transitions.Machine

underlying class representing the states

topic_handler_map : dict

mapping between the topics and the functions that handle the corresponding events

topics

list of topics handled by the state machine. Defaults to

trigger_topics

Topics that trigger a machine state transition.

_do_transition(self, trigger_name, force_name, *args, **kwargs)[source]

Try to execute the triggered transition: if it fail, run the forced one.

Parameters:
triggered, forced : string

name of the transitions to force

args, kwargs : arguments

positional and keyword arguments to pass to the triggered transitions

_handle_exp_event(self, tcs_event)[source]

Handle events associated with EXP_TOPIC with the following logic:

  • if the event is marked as starting, try to execute the do_exp{0:02d}() transition; if it fails force to_exp{0:02d}();
  • if the event is marked as finished, try to execute the finish_exp{0:02d}() transition; if it fails force to_exp{0:02d}_done().

The format field {0:02d} is a zero padded two digit integer, whose value is taken from the exposure key of the input tcs_event

Parameters:
tcs_event : dict

event to handle

_handle_run_event(self, tcs_event)[source]

Handle events associated with RUN_TOPIC with the following logic:

  • if the event is marked as starting, try to execute the start() transition; if it fails force to_started();
  • if the event is marked as finished, try to execute the finish() transition; if it fails force to_idle();
  • if the event is marked as finished and an error happened, execute the abort() with the traceback contained in the event
Parameters:
tcs_event : dict

event to handle

_handle_setup_event(self, tcs_event)[source]

Handle events associated with SETUP_TOPIC with the following logic:

  • if the event is marked as starting, try to execute the do_setup() transition; if it fails force to_setup();
  • if the event is marked as finished, try to execute the finish_setup() transition; if it fails force to_setup_done();
Parameters:
tcs_event : dict

event to handle

_make_machine(self)[source]

create the machine with all the relevant states and transitions

Returns:
machine : transitions.Machine

state machine

handle_event(self, tcs_topic, tcs_event)[source]

Handle a TCS event and:

Parameters:
tcs_topic : string

topic of the event

tcs_event : dict

event to handle

Returns:
bool

True if tcs_topic in topics, False otherwise

on_enter_idle(self, state_event)[source]

Callback that is triggered when entering the idle state.

The function looks for the following entry in event.kwargs:

  • tcs_event: if present and dictionary like, try to extract the shotid key; if successful it updates the database calling ocd.shots_db.update_shot(). Failures are logged as warning. If tcs_event is not present nothing happens.
Parameters:
state_event : transition.EventData

happened event

trigger_topics

Topics that trigger a machine state transition. See ocd.run_shot for a description of topics.

_images/run_shot_state.png

MetrologyState – Monitor the metrology

class ocd.states.MetrologyState(conf, vault)[source]

Bases: ocd.states.BaseState, ocd.states.CallbackMixin, ocd.states.StateMixin

State machine that keep track of the metrology.

Any time one of the events that can update containers in the ocd.storage.MetrologyVault, a state change is evaluated.

Parameters:
conf : pyhetdex.tools.configuration.ConfigParser

configuration

vault : ocd.storage.MetrologyVault

vault containing the values of interest

Attributes:
machine : transitions.Machine

underlying class representing the states

topics

list of topics handled by the state machine. Defaults to

trigger_topics

Same as the topics used by the input vault

quantities : list

names of the quantities to check

ref_values : dict

for each quantities, save the minimum and maximum value allowed

_make_machine(self)[source]

create the machine with all the relevant states and transitions

Returns:
machine : transitions.Machine

state machine

_set_ref_values(self)[source]

set the reference value in ref_values

compare_probes(self)[source]

For each probe, check that the conditions are good enough to run HETDEX.

Compare the median_masked of the values stored in the MetrologyVault the corresponding expected values from the [containers] section of the configuration file.

Returns:
is_good : list of bool

True is the conditions are in spec for each guide probe

probe_msg : string

string with the guide probe values and their reference values

good_conditions(self, probes_conditions)[source]

From the list of boolean representing the probes conditions, extract a single value representing good/bad conditions.

If the option both_gp_good of the [containers] section is yes/true, then all the probes must be in spec, otherwise can be just one of them.

Parameters:
probes_conditions : list of bool

True is the conditions are in spec for each guide probe

Returns:
bool

good conditions

handle_event(self, tcs_topic, tcs_event)[source]

Handle a TCS event and:

Parameters:
tcs_topic : string

topic of the event

tcs_event : dict

event to handle

Returns:
bool

whether the topic triggers a state change

trigger_topics

Same as the topics used by the input vault

update_config(self, conf)[source]

Update the local copy of the configuration file and ref_values.

Parameters:
conf : pyhetdex.tools.configuration.ConfigParser

new configuration object

Returns:
string

message to report back

_images/metrology_state.png

HetdexAllowedState – It is time to run HETDEX?

ocd.states.HETDEX_ALLOWED_TOPIC = 'ocd.states.hetdex_allowed'

Topic associated with the events necessary to update the HetdexAllowedState state machine

ocd.states.HETDEX_CHANGED_TOPIC = 'ocd.states.hetdex_allowed_changed'

Topic of the events emitted after a state is changed

ocd.states.HETDEX_TOGGLE = ENUM(ENABLE=0, DISABLE=1)

Enumerator to use to signal that HETDEX is to be enabled or disabled

ocd.states.HETDEX_CHANGED = ENUM(ENABLED=10, DISABLED=11)

Enumerator to use to signal that HETDEX has been enabled or disabled

class ocd.states.HetdexAllowedState[source]

Bases: ocd.states.BaseState, ocd.states.CallbackMixin, ocd.states.StateMixin

This state tracks whether OCD is allowed to execute HETDEX shots even if all the other states would allow it.

A state change is triggered by tcs events with topic HETDEX_ALLOWED_TOPIC. The associated event needs to contain one entry:

Topic Event
ocd.states.hetdex_allowed action (int): if it is HETDEX_TOGGLE.ENABLE triggers allow(), if it is HETDEX_TOGGLE.DISABLE triggers forbid()

This state is triggered externally via the ocd allow_hetdex {start,stop} command.

When a state change happens, a TCS-like event with topic HETDEX_CHANGED_TOPIC is emitted. The associated event is:

Topic State Event
ocd.states.hetdex_allowed allowed new_state (int): HETDEX_CHANGED.ENABLED
ocd.states.hetdex_allowed not_allowed new_state (int): HETDEX_CHANGED.DISABLED

Note

Since the state is update in the main OCD loop, it make sense to use the ocd_main_loop ZeroMQ server here. In the case we need more flexibility it is possible to add a new server.

Attributes:
machine : transitions.Machine

underlying class representing the states

topics

list of topics handled by the state machine. Defaults to

trigger_topics

Topics that triggers a machine state change.

_make_machine(self)[source]

create the machine with all the relevant states and transitions

Returns:
machine : transitions.Machine

state machine

emit_tcs_event(self, state_event)[source]

Emit a TCS-like event using a ZeroMQ server. See HetdexAllowedState for more info.

Parameters:
state_event : transition.EventData

happened event

handle_event(self, tcs_topic, tcs_event)[source]

Handle a TCS event and:

Parameters:
tcs_topic : string

topic of the event

tcs_event : dict

event to handle

Returns:
bool

whether the event has been handled

trigger_topics

Topics that triggers a machine state change. I.e. HETDEX_ALLOWED_TOPIC

_images/hetdex_allowed_state.png

MetaState – Meta state machine

This machine aggregates the state of other machines and according to their states switch between the satisfied and not_satisfied states. Consider the following case:

>>> run_shot = RunShotState()
>>> hetdex_allowed = HetdexAllowedState()
>>> meta = MetaState([(run_shot, 'idle'), (hetdex_allowed. 'allowed')])

If run_shot.state == idle and hetdex_allowed == allowed, then meta.state == satisfied. Any other combination of states of run_shot and hetdex_allowed results in meta.state == not_satisfied.

class ocd.states.MetaState(machines_states)[source]

Bases: ocd.states.BaseState, ocd.states.CallbackMixin, ocd.states.StateMixin

Collect other states and decide whether they all have the expected state.

Parameters:
machines_states : list of 2-tuples (machine, string)

each element is a state machine and the name of the reference state; if all the machines are in the corresponding state, MetaState switches to the allowed state, otherwise it switches to not_allowed

Raises:
ValueError

if machines_states doesn’t contain at least one element and if any element is not a 2-tuple

Attributes:
machine : transitions.Machine

underlying class representing the states

topics

list of topics handled by the state machine. Defaults to

trigger_topics

topics that trigger a machine state change; they are the union of

_make_machine(self)[source]

create the machine with all the relevant states and transitions

Returns:
machine : transitions.Machine

state machine

handle_event(self, tcs_topic, tcs_event)[source]

If tcs_topic is in the list of trigger_topics that were handled by the input state machines, re-evaluate their states and if they all are in their reference state, allow() the execution of a new shot, otherwise forbid() it.

If tcs_topic is not in trigger_topics but in topics, emit the current the state with StateMixin.emit_state_name().

Parameters:
tcs_topic : string

topic of the event

tcs_event : dict

event to handle, ignored

Returns:
bool

whether the event has been handled

trigger_topics

topics that trigger a machine state change; they are the union of the trigger_topics from the input machines.

_images/meta_state.png

Mixin classes and helpers

class ocd.states.CallbackMixin[source]

Bases: object

Mixin class that provides implementation for common callbacks that can be used by state machine

_msg_fmt(self, state_event)[source]

Create a message from the state_event containing the transition name, the source and destination state name and whether the transition is forced.

The function looks for the following entries in event.kwargs:

  • forced: if the transition is forced; typically the to_{state} transitions will use it.
Parameters:
state_event : transition.EventData

happened event

Returns:
msg : string

string to containing the message to send

fmt_args : dict

formatting keys for msg (only positional arguments)

emit_state_change(self, state_event)[source]

Emit a TCS event with topic STATE_CHANGE_TOPIC to document state transitions. This should be used after a transition takes place, for example as a finalize_event callback.

The function looks for the following entries in event.kwargs:

  • extra_msg: an extra message to appen to the state change message; if present comes after the traceback;
  • traceback: traceback to add to the log message; the abort transition will typically use this;
  • forced: if the transition is forced; typically the to_{state} transitions will use it.
Parameters:
state_event : transition.EventData

happened event

log_state_change(self, state_event)[source]

Log state transitions. This should be used after a transition takes place, for example as a finalize_event callback.

The function looks for the following entries in event.kwargs:

  • extra_msg: an extra message to appen to the state change message; if present comes after the traceback;
  • traceback: traceback to add to the log message; the abort transition will typically use this;
  • forced: if the transition is forced; typically the to_{state} transitions will use it.
Parameters:
state_event : transition.EventData

happened event

class ocd.states.StateMixin[source]

Bases: object

Mixin class that provides implementation for common functionalities that can be used by state machines

emit_state_name(self)[source]

Emit a TCS event with topic STATE_NAME_TOPIC to document state the current state.

This method assumes that the class has a state attribute

Inheritance scheme

Inheritance diagram of ocd.states