PDA

View Full Version : Questions related to QStateMachine



ottawaDan
12th February 2013, 16:47
Good afternoon,

I am doing some refactoring toward using a QStateMachine for an existing application. So far that process is going well but I do have a few questions.

a. How can I define a QHistoryState state that applies to the entire state machine. For example, the operator can elects to close the application at any time, but our SW requirements stipulate that a confirmation is required. I was thinking of using a history state (to be used as an interrupt), request the confirmation from the operator, and then restore to the current state when the operator declines to close the application. Or should I simply leave the handling of the 'Close Application' signal outside the state machine and post the appropriate event when the application is to be closed.

b. I get a compilation error when trying to declare a transition based on a signal emitted from an object ScriptThread (which is derived from QThread).


error: no matching function for call to ‘QState::addTransition(ScriptThread*&, const char [12], QState*&)’
acquisitionState->addTransition( m_scriptThread, SIGNAL( finished()), acquisitionCompletedState);

However, the original code had a 'connect' using the same signal as the one listed above. This code compiles and executes properly.

connect( m_scriptThread, SIGNAL( finished()), this, SLOT( scriptFinished()), Qt::QueuedConnection);
As I said, ScriptThread is derived from QThread. I can rework around this using postEvent() in a SLOT handling the finished() signal. But I am curious about this compilation error.

Thanks in advance
Daniel

Santosh Reddy
13th February 2013, 09:53
a. How can I define a QHistoryState state that applies to the entire state machine. For example, the operator can elects to close the application at any time, but our SW requirements stipulate that a confirmation is required. I was thinking of using a history state (to be used as an interrupt), request the confirmation from the operator, and then restore to the current state when the operator declines to close the application. Or should I simply leave the handling of the 'Close Application' signal outside the state machine and post the appropriate event when the application is to be closed.
QHistoryState is not a solution to save the state when application closes.



I get a compilation error when trying to declare a transition based on a signal emitted from an object ScriptThread (which is derived from QThread).

error: no matching function for call to ‘QState::addTransition(ScriptThread*&, const char [12], QState*&)’
acquisitionState->addTransition( m_scriptThread, SIGNAL( finished()), acquisitionCompletedState);

However, the original code had a 'connect' using the same signal as the one listed above. This code compiles and executes properly.
Try to clean all and rebuild, that should slove the problem.

ottawaDan
13th February 2013, 16:19
Thanks for the reply,

Regarding the compilation error, clean all and rebuild did not resolve the issues. I get the same error whether the code is compiled using Qt 4.8.1 or Qt 4.6. So the investigation continues.

The 'Close application' was used as an example. As I said, our SW requirements specify that a confirmation is required from the operator before actually closing the application. Say the operator confirms closing the application, the state machine would enter a QFinalState. However, a negative response would return the application to its current state. Hence the idea to use a QHistoryState approach.

But in general, I wanted to know how to implement a QHistoryState that could be used across the entire state machine. The examples I saw are defining QHistoryState "as a child of the state for which we wish to record the current child state" (from QStateMachine framework). The state machine I am working on has 4 parent states (identification, setup, acquisition, review). Do I need to define a QHistoryState for each parent state although the code associated to the history state would be the same? As my original question, how to implement a QHistoryState that applies to the entire state machine?

I guess this confirmation from the operator can be viewed differently. A QMessageBox (with Yes/No buttons) is used to handle the confirmation. QSignalTransition could be used to set the transition to the correct state, but then the current child state must be recorded somehow before the QMessageBox is used.

Daniel

anda_skoa
14th February 2013, 03:40
I think what you would need to do is create one top level state that contains your current top level states, lets call that runningState and a sibling top level state called "confirmQuit" state.

Then you create a QHIstoryState as a child of runningState (as a sibling of your previous top level states) and a transition from confirmQuit to that history state which is triggered by "do not quit".

Cheers,
_

ottawaDan
28th February 2013, 23:10
The last response to my post read:

I think what you would need to do is create one top level state that contains your current top level states, lets call that runningState and a sibling top level state called "confirmQuit" state.

Then you create a QHIstoryState as a child of runningState (as a sibling of your previous top level states) and a transition from confirmQuit to that history state which is triggered by "do not quit"

I modified the state machine, creating a top level state (applicationParentState), and included the declaration of the portion of the state machine defined as parallel states (identified as setup state in the state machine I am working on; 4 parent states (identification, setup, acquisition, review))



applicationParentState = new QState();

imagingSetupState = new QState( QState::ParallelStates, applicationParentState);
QState* haloReflectionSetup = new QState( imagingSetupState);
QState* patientAlignment = new QState( imagingSetupState);

QState* phakicStateGroup = new QState( haloReflectionSetup);
QState* initPhakicState = new QState( phakicStateGroup);
QState* phakicState = new QState( phakicStateGroup);
QState* haloReflectionAlignment = new QState( phakicStateGroup);

QState* pseudoPhakicStateGroup = new QState( haloReflectionSetup);
QState* initPseudoPhakicState = new QState( pseudoPhakicStateGroup);
QState* pseudoPhakicState = new QState( pseudoPhakicStateGroup);
QState* iolAlignLeftState = new QState( pseudoPhakicStateGroup);
QState* iolAlignRightState = new QState( pseudoPhakicStateGroup);

QState* alignmentIdle = new QState( patientAlignment);
QState* pupilSetupState = new QState( patientAlignment);
QState* focusOnRetinaState = new QState( patientAlignment);

patientAlignment->setInitialState( alignmentIdle);
phakicStateGroup->setInitialState( initPhakicState);
pseudoPhakicStateGroup->setInitialState( initPseudoPhakicState);
haloReflectionSetup->setInitialState( phakicStateGroup);

stateMachine.addState( applicationParentState);

QHistoryState* applicationParentStateHistory = new QHistoryState( QHistoryState::DeepHistory, applicationParentState);

QState* confirmAppClosingState = new QState();
confirmAppClosingState->addTransition( applicationParentStateHistory);
applicationParentState->addTransition( actionClose, SIGNAL( triggered()), confirmAppClosingState);
QObject::connect( confirmAppClosingState, SIGNAL( entered()), this, SLOT( confirmClosingApplication()));
stateMachine.addState( confirmAppClosingState);

closingApplicationState = new QFinalState( );
...

Testing the confirmation code works well when in the identification state. However, if confirmation is executed while in any child state declared under imagingSetupState (say pupilSetupState), the execution will go thru all the state transitions when returning to pupilSetupState, and do the same in the haloReflectionSetup; ie walk thru every state transitions and execute every method assigned with entered() and exited() signal for each state.

I am looking at changing the definition of the state machine to eliminate the QState::ParallelStates but I am afraid that the number of transitions will become unmanageable as I know that some expansion is coming soon.

Would it be preferable to define a QHistoryState locally for each the haloReflectionSetup and patientAlignment parent states which would handle the confirmation on closing the application.

I guess I could leave the confirmation completely out of the state machine definition and only define a transition to the QFinalState.
But I still would like to know if it is possible/recommended to use a QHistoryState as a sibling of a parent state with the QState::ParallelStates attributes?

Thanks
Daniel

anda_skoa
1st March 2013, 17:23
Maybe this behavior is triggered by the deep history.

You could try having a history state for each state group and using the history state as the initial state. I would expect this to restore each entered substate, kind of like a restore cascade.

Cheers,
_