将C++数据模型与Qt SCXML状态机一起使用

Using C++ data model with Qt SCXML state machine

本文关键字:状态机 一起 SCXML Qt C++ 数据模型      更新时间:2023-10-16

我有一个工作状态机,可以从几个状态发送类似的消息。目前它们都是硬编码的,所以我的 .scxml 文件中有一些片段,如下所示:

<state id="state1">
<transition event="event_1">
<send event="unexpectedEvent1FromState1"/>
</transition>
</state>

<state id="state2">
<transition event="event_2">
<send event="unexpectedEventEvent2FromState2"/>
</transition>
</state>

我必须在C++代码中的其他地方捕获多个unexpectedEventXxxxFromYyyy消息。

我想对这些消息进行标准化,以便我只需要在我的代码中捕获单个参数化的unexpectedEvent信号,它将检查 QScxmlEvent 对象以查找导致信号发出的转换和源状态。

看了Qt文档后,我认为我需要添加一个数据模型。我不在任何地方使用这些,所以不熟悉。我之前曾相当成功地尝试过 EcmaScript 数据模型,但发现如果我尝试创建大约 150 台以上的机器,应用程序就会在我的机器上崩溃,这显然是因为 150+ V8 JavaScript 引擎需要内存。由于我需要运行状态机的 1000+ 个副本,因此排除了 EcmaScript 数据模型,我需要使用C++数据模型。

我没有运气,当我实例化的第一台机器第一次尝试处理事件时,程序崩溃了。我已经将数据模型中的代码简化为最基本的骨架,如下所示,它仍然崩溃。

请有人告诉我该怎么做才能让我的数据模型工作吗?我看过Qt的例子,它们似乎都太微不足道了,没有帮助,谁能指出我任何更丰富的例子?非常感谢。

基本代码更改

添加到 .scxml 文件中的根元素:

datamodel="cplusplus:FooDatamodel:foodatamodel.h"

foodatamodel.h:

#ifndef FOODATAMODEL_H
#define FOODATAMODEL_H
#include "qscxmlcppdatamodel.h"
class FooDatamodel : public QScxmlCppDataModel
{
Q_OBJECT
Q_SCXML_DATAMODEL
public:
FooDatamodel();
};
#endif // FOODATAMODEL_H

食物模型.cpp

#include "foodatamodel.h"
FooDatamodel::FooDatamodel()
{
}

免责 声明:

  1. 我在Qt Creator中使用状态机编辑器,我很可能在顶部的手写SCXML片段中遗漏了一些重要的东西。我很确定真正的文件在语法和语义上都是有效的 - 尽管上面的datamodel属性非常准确。
  2. 实际文件名以及状态和转换名称是不同的,我可能无法更改上面的C++片段中的某些内容。实际文件不包含任何实质性代码。

再次感谢,对问题的长度表示歉意。

万一有人再来

...事实证明,您需要在QScxmlCppDataModel派生类中显式代码以将其与状态机相关联。这是通过调用QScxmlDataModel::setStateMachine来实现的,将指针传递给状态机实例。

鉴于此函数位于我派生的基类中,我感到更恼火而不是我错过它的尴尬。扬子晚报.

我很抱歉任何未来的读者,在提出这个问题七个月后,我现在无法构建一个简单的例子来说明需要什么。

由于 SO 上关于这一点的信息很少......这是我的 2p(对不起英语(...

我的数据模型看起来像这样

#ifndef DATAMODEL_H
#define DATAMODEL_H
#include <QDebug>
#include <QScxmlCppDataModel>
#include <QScxmlEvent>
class DataModel :public QScxmlCppDataModel
{
Q_OBJECT
Q_SCXML_DATAMODEL
public:
DataModel(QObject *parent);
void UpdateFields(QString call, int rst, QString exchange);
bool CallIsValid(QString s);
bool RstIsValid(int i);
bool ExchangeIsValid(QString s);
QString Call;
int Rst;
QString Exchange;
QString m_Descript;
QVariant m_var;
};

#define DATAMODEL_H
#endif // DATAMODEL_H

它的实现我喜欢这个....

#include "DataModel.h"

DataModel::DataModel(QObject *parent):
QScxmlCppDataModel(parent)
{
qDebug() << "Data Model Initalized";
}
void DataModel::UpdateFields(QString call, int rst, QString exchange)
{
Call=call;
Rst=rst;
Exchange=exchange;
}
bool DataModel::CallIsValid(QString s)
{
if (s.length()>4)
{
Call=s;
return true;
}
else
return false;
}

我的测试状态引擎有 3 种状态,呼叫、RST、交换...和 然后之间的事件被称为

  • 得到呼叫
  • 得到RST
  • 得到交换

所以我的主窗口标题看起来像

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QScxmlStateMachine>
#include <QScxmlCppDataModel>
#include <QDebug>
#include "DataModel.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// These are the States
void onCallState(bool isActive);
void onRstState(bool isActive);
void onExchangeState(bool isActive);
// These are the Events
void ongotCallEvent(const QScxmlEvent &event);
void ongotRstEvent(const QScxmlEvent &event);
void ongotExchangeEvent(const QScxmlEvent &event);

public slots:
void OnReturnPressed();

private:
Ui::MainWindow *ui;
QScxmlStateMachine *m_stateMachine;
DataModel *datamodel;

};
#endif // MAINWINDOW_H

实现是完成所有"布线"的地方......

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);

connect(ui->f1,SIGNAL(returnPressed()),this,SLOT(OnReturnPressed()));
connect(ui->f2,SIGNAL(returnPressed()),this,SLOT(OnReturnPressed()));
connect(ui->f3,SIGNAL(returnPressed()),this,SLOT(OnReturnPressed()));

m_stateMachine = QScxmlStateMachine::fromFile(":/QsoEx.scxml");
for(QScxmlError& error:m_stateMachine->parseErrors())
{
qDebug()<<error.description();
}


// Connect up the States
// We have 3 States... Call Rst Exchange
m_stateMachine->connectToState("Call", this, &MainWindow::onCallState);
m_stateMachine->connectToState("Rst", this, &MainWindow::onRstState);
m_stateMachine->connectToState("Exchange", this, &MainWindow::onExchangeState);
// We have 3 Events which move us between each State
m_stateMachine->connectToEvent("gotCall", this, &MainWindow::ongotCallEvent);
m_stateMachine->connectToEvent("gotRst", this, &MainWindow::ongotRstEvent);
m_stateMachine->connectToEvent("gotExchange", this, &MainWindow::ongotExchangeEvent);

datamodel = new DataModel(this);

m_stateMachine->setDataModel(datamodel);
m_stateMachine->init();
m_stateMachine->start();

}

"逻辑"...即控制国家之间运动的东西 - 这样表达......字段 f1、f2 和 f3 是 UI 中的行输入对象。

void MainWindow::OnReturnPressed()
{
QString curState = m_stateMachine->activeStateNames()[0];
qDebug() << "On Return Pressed Triggered";
qDebug() << "Current State is " + curState;
if (curState == "Call")
{
qDebug() << "We are in Call State. Checking Call";
// We can move from call to rst is we have a valid call
if (datamodel->CallIsValid(ui->f1->text()))
{
qDebug() << "Data Is Valid";
QVariant var = QVariant(ui->f1->text());
m_stateMachine->submitEvent("gotCall", var);
ui->f2->setFocus();
}
else
{
ui->f1->setFocus();
}
}
if (curState == "RST")
{
if (datamodel->RstIsValid(QString(ui->f2->text()).toInt()))
{
QVariant var = QVariant(ui->f1->text());
m_stateMachine->submitEvent("gotRst", QVariant("Rst"));
ui->f3->setFocus();
}
else
{
ui->f2->setFocus();
}
}
if (curState == "Exchange")
{
if (datamodel->ExchangeIsValid(ui->f3->text()))
{
QVariant var = QVariant(ui->f1->text());
m_stateMachine->submitEvent("gotExchange", QVariant("Rst"));
ui->f1->setFocus();
}
else
{
ui->f3->setFocus();
}
}


ui->label->setText("State "+m_stateMachine->activeStateNames()[0]);
}

如果您想要此代码的副本,因为我可能遗漏了一些东西...... 请转到 https://github.com/timseed/Ex_Qt_StateMachine