PDA

View Full Version : SCXML state machines and scripting



zephod
17th April 2012, 18:06
I'm trying to learn about SCXML (http://www.w3.org/TR/scxml/) and the Qt implementation of it (http://http://doc.trolltech.com/solutions/4/qtstatemachine/scxml.html).

I'm experimenting with the simplest state machine I can think of which has 2 states, s1, the initial state and s2. A transition from s1 to s2 occurs when some value exceeds a number.

I've written a small Qt app that displays a button that says "hello". When the button is pushed, nClicked is incremented. A transition to state s2 should occur when nClicked > 2 and then the button label changes to "goodbye". I have forced a transition if nClicked > 4.

I have been unable to get the transition to occur automatically. It seems that the value of nClicked is not getting to the script engine to be evaluated. When the transition is forced, the label does change and in fact if I use anything other than myClass.nClicked in the cond parameter in the scxml file, I get an error message about not being able to find the variable when the event is triggered so it appears that I have set the property correctly but the nClicked that the script knows about is not the same as the nClicked that it getting incremented. I'm obviously missing something.

Thanks,
Steve

I have attached my test program and data (and the qscxml.cpp and .h file)
I am running this on a CentOS 6.2 machine and I used
$ qmake-qt4 -project
$ qmake
Edit the generated Makefile to add /usr/include/QtScript to the INCPATH and -lQtScript to LIBS.
$ gmake

junk.scxml:


<?xml version='1.0' encoding='UTF-8'?>

<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<transition event="t" cond="myClass.nClicked > 2" target="s2"/>
</state>

<state id="s2">
</state>
</scxml>


class.h:


#ifndef MYCLASS_H
#define MYCLASS_H

#include <iostream>

#include <QWidget>
#include <QPushButton>
#include <QScriptEngine>
#include <QString>

#include "qscxml.h"

using namespace std;

class MyClass : public QWidget
{
Q_OBJECT
public:
MyClass(QWidget* parent = 0);
int nClicked;

QScxml* sm;
QPushButton* pb;
QScriptValue sv;
public slots:
void addClicks();
void changeLabel() { pb->setText("Goodbye"); }
private:
const QString getCurrentState() const;
void printStateName() const;
};

#endif


main.cpp:


#include <iostream>

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QObject>
#include <QScriptEngine>
#include <QString>

#include "qscxml.h"
#include "class.h"

using namespace std;

MyClass::MyClass(QWidget* parent)
: QWidget(parent), nClicked(0)
{
pb = new QPushButton("Hello", this);
sm = QScxml::load("./junk.scxml");
sm->registerObject(this, "myClass", true);

sv = sm->scriptEngine()->newQObject(this);
sm->scriptEngine()->globalObject().setProperty("myClass", sv);

connect(pb, SIGNAL(clicked()), this, SLOT(addClicks()));
connect(sm, SIGNAL(eventTriggered(QString)), this, SLOT(changeLabel()));
sm->start();
}

void MyClass::addClicks()
{
nClicked++;
cout << "nclicked = " << nClicked << endl;

if (nClicked > 4)
sm->postNamedEvent("t");
}

int main(int argc, char* argv[])
{
QApplication app(argc, argv);

MyClass* w = new MyClass;
w->show();

return app.exec();
}