State machine - transition a child state before exiting parent
-
I'm trying to implement a state machine in a machine control application. The machine has 3 basic runtime states, Idle, Standby, and Running, which I put into a single parent state called Normal.
The basic transitions are as follows:
Idle --> Running on 'Start'
Running-->Standby on 'Stop'
Running --> Idle on 'Finished'
Standby --> Running on 'Start'
Standby --> Idle on 'Reset'In addition to these normal transitions however there is also an 'Emergency Stop' function which I want to handle separately, because it's possible for this button to be engaged at any time, so I want that state to be outside of 'Normal' and instead use a History state to return to Normal when the button is disengaged. This way an E-Stop from Idle or Standby will return to those states, however, if the button is hit while Running, I want the following to happen:
Running --> Standby (so that the History state of Normal will return to Standby instead of back to Running)
Normal --> Emergency StopTo that end I made the following state definitions and transitions:
// Basic states QState* NormalState = new QState(stateMachine); QState* EStopState = new QState(stateMachine); // Normal states QState* IdleState = new QState(NormalState); QState* StandbyState = new QState(NormalState); QState* RunningState = new QState(NormalState); // Normal transitions IdleState->addTransition(machineManager, &Manager::OnStart, RunningState); RunningState->addTransition(machineManager, &Manager::OnStop, StandbyState); RunningState->addTransition(machineManager, &Manager::OnFinish, IdleState); StandbyState->addTransition(machineManager, &Manager::OnStart, RunningState); StandbyState->addTransition(machineManager, &Manager::OnReset, IdleState); // EStop transitions QHistoryState *normalReturnState = new QHistoryState(NormalState); RunningState->addTransition(machineManager, &Manager::OnEStop, StandbyState); NormalState->addTransition(machineManager, &Manager::OnEStop, EStopState); EStopState->addTransition(machineManager, &Manager::OnEStopReleased, normalReturnState);
This works fine when E-Stop is engaged in Idle or Standby, but if I engage E-Stop while Running, only the Running --> Standby transition occurs and the second one is ignored.
I know I could potentially solve this issue by making parallel states, i.e. Idle/Standby/Running in one parallel group and Normal/EStop in the other, but I'm concerned that would add much more complication to my display logic
For example, if I were to do:
StandbyState->assignProperty(widget, "enabled", true); EStopState->assignProperty(widget, "enabled", false);
This would work if EStop and Standby are not parallel. If they were parallel then it would depend on which one the state machine enters first. The same problem would happen if I put any processing on the
entered()
orexited()
events, but if the states were not parallel, and EStop were able to first transition Running --> Standby, then I expect I would receive a predictable sequence of events like this:On EStopDown:
RunningState::exited()
StandbyState::entered()
StandbyState::exited()
NormalState::exited()
EStopState::entered()
On EStopUp:
EStopState::exited()
NormalState::entered()
StandbyState::entered()
-
Hi,
I might be wrong but I don't think it can be easily achieved with QHistoryState but that said, you could have an additional emergency stopped state for the Running case and simply go back from that state to Idle.
-
@SGaist That occurred to me as a possibility but there is another complicating factor which is that there are other runtime error conditions that should essentially do the same thing as the E-Stop (drop from Running to Standby then go to an Error state) so it would require duplicating those as well.
I did have a thought about a way to handle it though, which is to essentially have an 'Interrupt' signal that is fired by the Manager object before the E-Stop/Error signals. The interrupt signal would trigger the Running --> Standby transition while the regular OnEStop signal would come second, and trigger the Normal --> EStop transition