0

我有一个工作状态机,可以从多个状态发送类似的消息。目前它们都是硬编码的,所以我的 .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>

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

我想对这些消息进行标准化,这样我只需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

foodatamodel.cpp

#include "foodatamodel.h"

FooDatamodel::FooDatamodel()
{
}

免责声明:

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

再次感谢,为问题的长度道歉。

4

2 回答 2

0

由于关于此的内容很少……这是我的 2p(对不起,英语)……

我的 DataModel.h 看起来像这样

#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
  • 得到交换

所以我的 MainWindow 标题看起来像

#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

于 2020-10-28T03:02:47.930 回答
0

万一有人再这样过来……

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

鉴于这个函数是我所派生的那个函数的基类,我对错过它感到更恼火而不是尴尬。YMMV。

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

于 2019-02-22T13:36:33.680 回答