我对 QStateMachine 有一个奇怪的问题,我一个星期都无法解决。
简要说明:
我在我的应用程序中使用 QStateMachine 来控制向通过 COM 端口连接的生物医学设备发送命令。整个状态机很复杂,有数百个状态和频繁的子状态。
机器是根据用户的偏好从设备的通信协议生成的。例如,如果用户想从设备中获得一些“反应”,则该反应由几个子反应组成,这些子反应由基本步骤(设备指令)组成。应用程序从这些基本步骤构建整个反应并在状态机上启动它。
因此,我创建了类CompositeReactionControl(继承自 QState),它允许从特定子状态构建复合。
QSignalTransition 实例用于将状态连接在一起。
问题描述:
有时,当发出前一个状态的 finish() 信号时,机器不会从一种状态转换到另一种状态。
笔记:
- 我 100% 确定该状态已连接。
- 我 100% 确定该信号已被触发。
- 通过机器的事件及其状态在与应用程序的其余部分不同的线程中运行,这个特定的信号被触发并在相同的线程中捕获
- 它在所有反应中随机发生(没有与此问题相关的特定反应或子状态)
- 在一次运行(反应)中引起问题的子状态,在相同反应的其他运行中运行没有任何问题
- StateMachine 长时间运行时会发生这种情况,它不会出现在第 4 反应之前(无论用户选择哪种反应)。
代码
有问题的部分在以下代码中标有注释“这是问题”...
向 CompositeReactionControl 添加新的子状态:
/**
* @brief Add new reaction control as a child of this composite
* @param reaction child reaction (inherited from QState)
* @return added child reaction
*/
AbstractReactionControl* CompositeReactionControl::addChildReaction(
AbstractReactionControl* reaction) {
// Check whether reaction is valid
if (reaction == NULL) {
QString msg = tr("Cannot add NULL subreaction to reaction '%1'.");
Logger::getInstance()->addError(msg.arg(getName()), this);
return NULL;
}
// Adopt reaction
reaction->setParent(this);
// Store previous reaction control and add current to list
AbstractReactionControl* prev = controls.size() > 0 ? controls.last() : NULL;
controls.append(reaction);
// Connects current state
connect(reaction, SIGNAL(reactionEntered(core::AbstractCommunicationState*)),
SLOT(onSubReactionEntered(core::AbstractCommunicationState*)));
connect(reaction, SIGNAL(reactionFinished(core::AbstractCommunicationState*)),
SLOT(onSubReactionFinished(core::AbstractCommunicationState*)));
// If previous state does not exist (this is the first one) then set current
// state 'c' as initial state and add transition from 'c' to final state
// Otherwise add transition from previous state to 'c', remove transition from
// previous state to final state and add transition from 'c' to final state
if (prev == NULL) {
this->setInitialState(reaction);
this->firstState = reaction;
} else {
// Remove end transitions from previous state
prev->removeTransition(endTransition1);
prev->removeTransition(endTransition2);
delete endTransition1;
delete endTransition2;
// Replaced with PassTransition
//prev->addTransition(prev, SIGNAL(finished()), reaction);
// HERE IS PROBLEM: I am 100% sure that finished() signal is emitted,
// but sometimes not caught by transition
PassTransition* t = new PassTransition(this, prev, SIGNAL(finished()));
t->setTargetState(reaction);
prev->addTransition(t);
}
// Assign new end transitions to current state
endTransition1 = new RepeatTransition(this, reaction, SIGNAL(finished()));
endTransition2 = new FinishTransition(this, reaction, SIGNAL(finished()));
endTransition1->setTargetState(firstState);
endTransition2->setTargetState(allFinished);
reaction->addTransition(endTransition1);
reaction->addTransition(endTransition2);
// Finish if halt
reaction->addTransition(this, SIGNAL(halt()), allFinished);
// Exit reaction
return reaction;
}
//---------------------------------------------------------------------------
传递过渡:
/**
* @class PassTransition
* @brief Passes control from one substate to another
* @author Michal Rost
* @date 6.11.2013
*/
class PassTransition : public QSignalTransition {
public:
PassTransition(CompositeReactionControl* owner, QObject* sender,
const char *signal) : QSignalTransition(sender, signal) {
this->owner = owner;
}
protected:
CompositeReactionControl* owner;
bool eventTest(QEvent *event) {
// HERE IS PROBLEM: I know that signal is called, but event is not received
QString msg = tr("Event Test %1, source: %2 target: %3");
AbstractReactionControl* s = dynamic_cast<AbstractReactionControl*>(this->sourceState());
AbstractReactionControl* t = dynamic_cast<AbstractReactionControl*>(this->targetState());
common::Logger::getInstance()->addDebug(msg.arg(event->type()).arg(s->getName()).arg(t->getName()), this);
return QSignalTransition::eventTest(event);
}
void onTransition(QEvent *event) {
QSignalTransition::onTransition(event);
QString msg = tr("Transition called");
common::Logger::getInstance()->addDebug(msg, this);
}
};
来自好案例的日志
在这种情况下,一切正常。重要的是 PassTransition::EventTest 接收到 192 类型的事件,这是从先前状态的 finished() 信号创建的状态机事件。
[08:33:01:976][core::CompositeReactionControl] State 'send_fluorescence_on' finished, disconnecting...
[08:33:01:976][core::CompositeReactionControl] State 'ProfileData' removed control from 'send_fluorescence_on' substate.
[08:33:01:977][core::ReactionEmitter] Sending state entered. Timer Started.
[08:33:01:977][core::PassTransition] Event Test 0, source: send_fluorescence_on target: send_fluorescence_off
[08:33:01:977][core::PassTransition] Event Test 0, source: measure_fluorescence target: measure_fluorescence
[08:33:01:977][core::PassTransition] Event Test 192, source: send_fluorescence_on target: send_fluorescence_off
[08:33:01:977][core::PassTransition] Transition called
[08:33:01:977][core::CompositeReactionControl] State 'ProfileData' gave control to 'send_fluorescence_off' substate.
不良案例的日志
可以看出,如果错误随机发生,则方法 PassTransition::EventTest 不会接收到提到的事件。5 秒超时后(我尝试了更长的时间间隔),我停止状态机并打印错误。
[08:33:01:991][core::CompositeReactionControl] State 'send_fluorescence_off' finished, disconnecting...
[08:33:01:991][core::CompositeReactionControl] State 'ProfileData' removed control from 'send_fluorescence_off' substate.
[08:33:01:991][core::ReactionEmitter] Sending state entered. Timer Started.
[08:33:01:991][core::PassTransition] Event Test 0, source: send_fluorescence_off target: led_off
[08:33:01:991][core::PassTransition] Event Test 0, source: measure_fluorescence target: measure_fluorescence
[08:33:16:966][core::ReactionEmitter] State machine is frozen. Reaction will be stopped!