Skip to content

Reference

States and transitions

This page lists all states and transitions of the device state machine as tables. It also gives the keys of the interactive controller, the name functions and the errors of invalid transitions. For an introduction, see Device states.

The enum fair::mq::State is in fairmq/States.h. Its underlying type is int. The name is the string that GetStateName() returns and that the log shows.

State Value Name Kind Hooks
Undefined 0 UNDEFINED Not used —
Ok 1 OK Internal, see below —
Error 2 ERROR Error state, final —
Idle 3 IDLE Idling state, initial —
InitializingDevice 4 INITIALIZING DEVICE Working state Init()
Initialized 5 INITIALIZED Idling state —
Binding 6 BINDING Working state Bind()
Bound 7 BOUND Idling state —
Connecting 8 CONNECTING Working state Connect()
DeviceReady 9 DEVICE READY Idling state —
InitializingTask 10 INITIALIZING TASK Working state InitTask()
Ready 11 READY Idling state —
Running 12 RUNNING Working state PreRun(), ConditionalRun(), Run(), PostRun(), data callbacks
ResettingTask 13 RESETTING TASK Working state ResetTask()
ResettingDevice 14 RESETTING DEVICE Working state Reset()
Exiting 15 EXITING Final state —

The state machine has two regions. The first region holds the states from IDLE to EXITING. The second region holds OK and ERROR, and starts in OK. A device does not report the state OK. StateQueue::WaitForNext() with a timeout returns Ok when no state arrives.

The log line No transition from state … on transition … uses other names. They have an underscore instead of a space, for example DEVICE_READY and INIT_TASK (StateMachine.cxx).

The enum fair::mq::Transition is in fairmq/States.h. The key column gives the key of the interactive controller.

Transition Value Name From To Requested by Key
Auto 0 AUTO BINDING, CONNECTING, INITIALIZING TASK, RESETTING TASK, RESETTING DEVICE The next state, see the matrix The device —
InitDevice 1 INIT DEVICE IDLE INITIALIZING DEVICE Controller i
CompleteInit 2 COMPLETE INIT INITIALIZING DEVICE INITIALIZED Controller i
Bind 3 BIND INITIALIZED BINDING Controller b
Connect 4 CONNECT BOUND CONNECTING Controller x
InitTask 5 INIT TASK DEVICE READY INITIALIZING TASK Controller j
Run 6 RUN READY RUNNING Controller r
Stop 7 STOP RUNNING READY Controller, or the device at the end of its work s
ResetTask 8 RESET TASK READY RESETTING TASK Controller t
ResetDevice 9 RESET DEVICE INITIALIZED, BOUND, DEVICE READY RESETTING DEVICE Controller d
End 10 END IDLE EXITING Controller q (shutdown sequence)
ErrorFound 11 ERROR FOUND All states ERROR The device after an exception, or your code —

The rows are the current states, and the columns are the transitions. A cell gives the next state. A dash means that the state machine refuses the transition. The column ErrorFound is not in the matrix: from all states, ErrorFound leads to ERROR. The source is the transition_table in StateMachine.cxx.

Current state InitDevice CompleteInit Bind Connect InitTask Run Stop ResetTask ResetDevice End Auto
IDLE INITIALIZING DEVICE — — — — — — — — EXITING —
INITIALIZING DEVICE — INITIALIZED — — — — — — — — —
INITIALIZED — — BINDING — — — — — RESETTING DEVICE — —
BINDING — — — — — — — — — — BOUND
BOUND — — — CONNECTING — — — — RESETTING DEVICE — —
CONNECTING — — — — — — — — — — DEVICE READY
DEVICE READY — — — — INITIALIZING TASK — — — RESETTING DEVICE — —
INITIALIZING TASK — — — — — — — — — — READY
READY — — — — — RUNNING — RESETTING TASK — — —
RUNNING — — — — — — READY — — — —
RESETTING TASK — — — — — — — — — — DEVICE READY
RESETTING DEVICE — — — — — — — — — — IDLE
EXITING — — — — — — — — — — —
ERROR — — — — — — — — — — —
  • The state machine accepts one transition at a time. While a transition is pending, it refuses all other transitions except ErrorFound.
  • A transition is pending from the call of ChangeState() until the next state starts. The next state starts only after the hooks of the current state return.
  • In a working state, the device requests Auto after its hooks, but only when no other transition is pending. At the end of RUNNING, the device requests Stop in the same way.
  • INITIALIZING DEVICE waits for CompleteInit before it calls Init().
  • Each new transition interrupts the transports. The transports resume at the start of the next state.
  • In EXITING and ERROR, the state machine has stopped, and the state does not change. In EXITING, ChangeState() returns false for all transitions except ErrorFound. In ERROR, ChangeState() can return true (known defect, see the disabled test StateMachine.RefusesTransitionsInError).

With --control dynamic (default), interactive or external, the control plugin reads keys from the standard input. It reads one character at a time, without Enter. The plugin prints the line --> [key] action before it does the action. Source: Control.cxx.

Key Action
h Prints the help and the state machine.
c Logs the current state. The plugin prints --> [i] check current state.
p Logs the number of connected peers of each sub-channel.
i Requests InitDevice. When INITIALIZING DEVICE starts, it requests CompleteInit.
b Requests Bind.
x Requests Connect.
j Requests InitTask.
r Requests Run.
s Requests Stop.
t Requests ResetTask.
d Requests ResetDevice.
q Ends the key input and runs the shutdown sequence (see below).
k Increases the log severity of the console.
l Decreases the log severity of the console.
n Increases the log verbosity.
m Decreases the log verbosity.
Other Prints Invalid input: [key]. and the help.

The keys b, x, j, r, s, t and d request only one transition. They do not wait for the Auto transitions. If the state does not accept the transition, the state does not change.

The control modes dynamic, static and gui start with the same sequence (RunStartupSequence()). The plugin requests a transition and waits for the next states:

Step Request Wait for
1 InitDevice INITIALIZING DEVICE
2 CompleteInit INITIALIZED
3 Bind BINDING, then BOUND
4 Connect CONNECTING, then DEVICE READY
5 InitTask INITIALIZING TASK, then READY
6 Run RUNNING

A signal stops the sequence after the current step, and the plugin runs the shutdown sequence. The state ERROR also stops the sequence, but without the shutdown sequence. The shutdown sequence (RunShutdownSequence()) requests one transition for each state, until EXITING or ERROR:

Current state Request
IDLE End
INITIALIZING DEVICE CompleteInit
INITIALIZED, BOUND, DEVICE READY ResetDevice
READY ResetTask
RUNNING Stop
Other states None. The plugin waits for the next state.

These functions in the namespace fair::mq convert between the enums and the names (States.h):

fairmq/States.h
std::string GetStateName(State);
std::string GetTransitionName(Transition);
State GetState(const std::string& state);
Transition GetTransition(const std::string& transition);
struct DeviceErrorState : std::runtime_error { using std::runtime_error::runtime_error; };
inline std::ostream& operator<<(std::ostream& os, const State& state) { return os << GetStateName(state); }
inline std::ostream& operator<<(std::ostream& os, const Transition& transition) { return os << GetTransitionName(transition); }
fairmq/States.h:55–63
  • GetStateName() and GetTransitionName() return the name with spaces, for example DEVICE READY. The operator<< for State and Transition writes the same name.
  • GetState() and GetTransition() accept only these names, in upper case and with spaces.
  • All four functions throw std::out_of_range for an unknown name or value.

Other functions use the same names:

Function Description
Device::ChangeState(const std::string&) Requests the transition with this name, for example "INIT DEVICE".
Device::ChangeStateOrThrow(const std::string&) The same, but throws if the state machine refuses the transition.
Device::WaitForState(const std::string&) Waits for the state with this name.
Device::GetCurrentStateName() Returns the name of the current state.
PluginServices::ToDeviceState(), ToDeviceStateTransition() Call GetState() and GetTransition().
PluginServices::ToStr(), ToStr() Call GetStateName() and GetTransitionName().
Device::GetStateName(), Device::GetTransitionName() Deprecated static functions. Use the functions of States.h.

The test States.UnknownNameThrows shows the exceptions:

test/state_machine/_state_machine.cxx
TEST(States, UnknownNameThrows)
{
EXPECT_THROW(GetState("NO SUCH STATE"), std::out_of_range);
EXPECT_THROW(GetTransition("NO SUCH TRANSITION"), std::out_of_range);
EXPECT_THROW(GetStateName(static_cast<State>(100)), std::out_of_range);
EXPECT_THROW(GetTransitionName(static_cast<Transition>(100)), std::out_of_range);
}
test/state_machine/_state_machine.cxx:339–345
Call Problem Result
Device::ChangeState(Transition) The current state does not accept the transition. Returns false. Logs No transition from state … on transition ….
Device::ChangeState(Transition) Another transition is pending. Returns false. Logs Transition … incoming, but another state transition is already ongoing.
Device::ChangeStateOrThrow(Transition) ChangeState() returns false. Throws std::system_error with the code ErrorCode::DeviceChangeStateFailed and the text Invalid transition: <NAME>.
The functions with a std::string The name is unknown. Throw std::out_of_range.
PluginServices::ChangeDeviceState() Another plugin controls the device. Throws PluginServices::DeviceControlError. Otherwise, it returns the result of ChangeState(). Without a controller, the calling plugin becomes the controller.
Device::WaitForState(), Device::WaitForNextState() The next state in the queue is ERROR. Throw DeviceErrorState with the text Controlled device transitioned to error state.

ChangeState() is [[nodiscard]]. The compiler warns when your code ignores the return value. Use ChangeStateOrThrow() when a refused transition is an error in your program. The test Transitions.InvalidChangeStateOrThrow requests Connect in IDLE:

test/device/_transitions.cxx
TEST(Transitions, InvalidChangeStateOrThrow)
{
Device device;
test::StateMachineThread t(device);
ASSERT_THROW(device.ChangeStateOrThrow(Transition::Connect), std::system_error);
ASSERT_NO_THROW(device.ChangeStateOrThrow(Transition::End));
t.Join();
}
test/device/_transitions.cxx:35–44

For the error codes and all exceptions of FairMQ, see Transfer codes and errors.