Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Scxml module : invoke scxml dynamically
Forum Updated to NodeBB v4.3 + New Features

Scxml module : invoke scxml dynamically

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 2 Posters 292 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • CharbyC Offline
    CharbyC Offline
    Charby
    wrote on last edited by
    #1

    Could anyone indicate a working example of a scxml file loaded dynamically supporting "sub-machine" (defined with scxml files) invoked from the main machine using C++ ?
    This example is the closest from what I am looking for but the submachine is defined within the main scxml whereas I would like it to be part of a separate file and it is a qml example.

    So far, I am able to :

    • dynamicaly load a "main" scxml using QScxmlStateMachine::fromFile
    • connect the scxml machine to be notified when a state change and react to onEnter onExit events
    • be notified when a service is invoked and get the name of the subservice.

    My problem is that I don't know how to retrieve the list of state from the invoked scxml and be notified of its event, calling stateNames on the main scxml is not returning states from the invoked service.

    From the main scxml, I am defining the invoke service with this element inside a transition : <invoke type="http://www.w3.org/TR/scxml/" src="groundOperations.scxml"/>
    It guess the path is correct as the scxml will complain when I set an src attribute value that does not corresponds to an existing file, .

    So I wondering whether I am missing something obvious here or if I miss manual process (for instance, I could create manually sub scxml machines reacting to invokedServicesChanged signal into a stack but I don't want to fight against the framework.

    As an additional but related topic, I feel QScxmlStateMachine is fine to quickly get things up and running (the QtCreator editor is nice) but it is missing a few feature :

    • I don't think there is a way to know the transition of the current state (except parsing the scxml file)
    • there is no way to manually activate a given state
      Actually I am considering creating a class having the same API as QScxmlStateMachine but internally relying on QStateMachine and parsing scxml to keep the same behavior with the missing feature.

    Thanks in advance for any feedback.

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Can you provide a minimal compilable example that shows this behaviour ?

      Silly idea, did you try to load the state machine from the same folder as were the executable is ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • CharbyC Offline
        CharbyC Offline
        Charby
        wrote on last edited by
        #3

        Thanks for your reply, I will come back with a minimalistic example..
        I tried to put the scxml file in the executable folder and got the same behavior : the machine shows no error (if the scxml name is incorrect or could not be found I have an error) but the states of the nested machine is not updated neither the events of the child machine are received.

        1 Reply Last reply
        0
        • CharbyC Offline
          CharbyC Offline
          Charby
          wrote on last edited by
          #4

          I was hoping someone could provide a simple example of a working dynamic nested scxml.

          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #5

            Did you check the module's tests ? There might be data there that would fit your scenario.

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply
            0
            • CharbyC Offline
              CharbyC Offline
              Charby
              wrote on last edited by
              #6

              Good idea, I will review the module's test !

              Here is a small compilable example :

              main.cpp

              #include "mainwindow.h"
              #include <QApplication>
              
              
              int main(int argc, char **argv)
              
              {
                  QApplication app(argc, argv);
                  MainWindow mainWindow;
                  mainWindow.show();
                  return app.exec();
              
              }
              

              mainwindow.h

              #ifndef MAINWINDOW_H
              #define MAINWINDOW_H
              
              #include <QMainWindow>
              #include <QScxmlStateMachine>
              #include <QScxmlInvokableService>
              #include <QDebug>
              
              class MainWindow : public QMainWindow
              {
                  Q_OBJECT
              
              public:
                  MainWindow(QWidget *parent = 0);
                  ~MainWindow();
              
              public slots:
                  void start( bool checked);
                  void onInvokedServicesChanged(const QVector<QScxmlInvokableService *> & lstSrv);
                  void onMessageReceived(const QScxmlEvent &event);
              private:
                  QScxmlStateMachine * m_pSM = nullptr;
              };
              
              #endif // MAINWINDOW_H
              

              mainwindow.cpp

              #include "mainwindow.h"
              #include <QPushButton>
              #include <QFileInfo>
              #include <QLayout>
              
              MainWindow::MainWindow(QWidget *parent)
                  : QMainWindow(parent)
              {
                  QFileInfo fileScxml("main.scxml");
                  if (!fileScxml.exists() || !fileScxml.isReadable())
                      qWarning() << "scxml can't be read :" << fileScxml.absoluteFilePath();
              
                  // show scxml errors if any
                  m_pSM = QScxmlStateMachine::fromFile( fileScxml.absoluteFilePath());
                  for (auto error : m_pSM->parseErrors())
                      qDebug() << error.toString();
              
                  // output xml log
                  connect(m_pSM, &QScxmlStateMachine::log, [](const QString &label, const QString &msg){
                      qDebug() << "error ("<< label <<") : " << msg;
                  });
              
                  connect(m_pSM, &QScxmlStateMachine::invokedServicesChanged, this, &MainWindow::onInvokedServicesChanged);
                  m_pSM->connectToEvent("SendMessage", this, &MainWindow::onMessageReceived );
                  m_pSM->setParent(this);
              
                  // add buttons for starting and sending events
                  QPushButton *startButton = new QPushButton("Start");
                  connect(startButton, &QPushButton::clicked, this, &MainWindow::start);
                  QPushButton *sendNext = new QPushButton("Send 'Next'(main) event");
                  connect(sendNext, &QPushButton::clicked, [&](){m_pSM->submitEvent("nextMain");});
                  QPushButton *sendNext2= new QPushButton("Send 'Next'(child) event");
                  connect(sendNext2, &QPushButton::clicked, [&](){m_pSM->submitEvent("nextChild");});
                  QHBoxLayout *layout = new QHBoxLayout;
                  layout->addWidget(startButton);
                  layout->addWidget(sendNext);
                  layout->addWidget(sendNext2);
                  QWidget* pwidget = new QWidget(this);
                  setCentralWidget(pwidget);
                  pwidget->setLayout(layout);
              
              }
              
              MainWindow::~MainWindow()
              {
              
              }
              
              void MainWindow::start(bool checked)
              {
                  Q_UNUSED(checked);
              
                  if (m_pSM->isRunning())
                      m_pSM->stop();
                  else m_pSM->start();
              
                  qDebug() << "isRunning :" << m_pSM->isRunning();
                  qDebug()  << m_pSM->stateNames(false).join(";");
              }
              
              
              void MainWindow::onInvokedServicesChanged(const QVector<QScxmlInvokableService *> &lstSrv)
              {
                      for (QScxmlInvokableService* srv : lstSrv)
                      {
                          qDebug() << "invoked srv : "<< srv->name();
                          srv->start();
                      }
                      qDebug()  << m_pSM->stateNames(false).join(";"); // nested machines are not listed (as stated in the doc)
              }
              
              void MainWindow::onMessageReceived(const QScxmlEvent &event)
              {
                  QVariantMap eventData = event.data().toMap();
                  qDebug() << "Message :" << eventData.value("Message").toString();
              
              }
              
              

              The nested machine (main.scxml inside the application dir) :

              <?xml version="1.0" encoding="UTF-8"?>
              <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early" xmlns:qt="http://www.qt.io/2015/02/scxml-ext" name="main.scml" qt:editorversion="4.3.1" datamodel="ecmascript" initial="Main_1">
                  <qt:editorinfo initialGeometry="175;59.18;-20;-20;40;40"/>
                  <state id="Main_1">
                      <qt:editorinfo geometry="236.52;203.85;-129.59;-50;120;100" scenegeometry="236.52;203.85;106.93;153.85;120;100"/>
                      <invoke type="http://www.w3.org/TR/scxml/" src="child.scxml"/>
                      <onentry>
                          <send event="SendMessage">
                              <param name="Message" expr="'Entering Main_1'"/>
                          </send>
                      </onentry>
                      <transition type="external" event="nextMain" target="Main_2">
                          <qt:editorinfo movePoint="66.60;-1.01"/>
                      </transition>
                  </state>
                  <final id="Final_1">
                      <qt:editorinfo geometry="166.93;1000.90;-20;-20;40;40" scenegeometry="166.93;1000.90;146.93;980.90;40;40"/>
                  </final>
                  <state id="Main_2">
                      <qt:editorinfo geometry="166.93;472.22;-60;-50;120;100" scenegeometry="166.93;472.22;106.93;422.22;120;100"/>
                      <transition type="external" event="nextMain" target="Final_1">
                          <qt:editorinfo startTargetFactors="43.32;71.16" movePoint="68.61;-110.99"/>
                      </transition>
                      <onentry>
                          <qt:editorinfo geometry="-60;-50;0;0;0;0"/>
                          <send event="SendMessage">
                              <param name="Message" expr="'Entering Main_2'"/>
                          </send>
                      </onentry>
                  </state>
              </scxml>
              

              the machine invoked from main (child.scxml inside the application dir)

              <?xml version="1.0" encoding="UTF-8"?>
              <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early" xmlns:qt="http://www.qt.io/2015/02/scxml-ext" name="child.scxml" qt:editorversion="4.3.1" datamodel="ecmascript" initial="Child_1">
                  <qt:editorinfo initialGeometry="186.73;45.92;-20;-20;40;40"/>
                  <state id="Child_1">
                      <qt:editorinfo geometry="186.73;155.10;-60;-50;120;100" scenegeometry="186.73;155.10;126.73;105.10;120;100"/>
                      <transition type="external" event="nextChild" target="child_2">
                          <qt:editorinfo movePoint="72.65;5.05"/>
                      </transition>
                      <onentry>
                          <send event="SendMessage">
                              <param name="Message" expr="'Entering Child_1'"/>
                          </send>
                      </onentry>
                  </state>
                  <state id="child_2">
                      <qt:editorinfo geometry="186.73;279.59;-60;-50;120;100" scenegeometry="186.73;279.59;126.73;229.59;120;100"/>
                      <transition type="external" event="nextChild" target="child_3">
                          <qt:editorinfo movePoint="66.60;4.04"/>
                      </transition>
                      <onentry>
                          <qt:editorinfo geometry="-60;-50;0;0;0;0"/>
                          <send event="SendMessage">
                              <param name="Message" expr="'Entering Child_2'"/>
                          </send>
                      </onentry>
                  </state>
                  <state id="child_3">
                      <qt:editorinfo geometry="186.73;405.09;-60;-50;120;100" scenegeometry="186.73;405.09;126.73;355.09;120;100"/>
                      <transition type="external" event="nextChild" target="child_4">
                          <qt:editorinfo movePoint="91.83;9.18"/>
                      </transition>
                      <onentry>
                          <qt:editorinfo geometry="-60;-50;0;0;0;0"/>
                          <send event="SendMessage">
                              <param name="Message" expr="'Entering Child_3'"/>
                          </send>
                      </onentry>
                  </state>
                  <state id="child_4">
                      <qt:editorinfo geometry="186.73;518.36;-60;-50;120;100" scenegeometry="186.73;518.36;126.73;468.36;120;100"/>
                      <transition type="external" event="nextChild" target="Final_1">
                          <qt:editorinfo movePoint="89.80;4.04"/>
                      </transition>
                      <onentry>
                          <qt:editorinfo geometry="-60;-50;0;0;0;0"/>
                          <send event="SendMessage">
                              <param name="Message" expr="'Entering Child_4'"/>
                          </send>
                      </onentry>
                  </state>
                  <final id="Final_1">
                      <qt:editorinfo geometry="186.73;623.46;-20;-20;40;40" scenegeometry="186.73;623.46;166.73;603.46;40;40"/>
                  </final>
              </scxml>
              
              

              What the example is doing :

              • load 'main.scxml' a simple state machine having 2 states, the initial one is invoking 'child.scxml'
              • create 3 buttons :
                • Start button => start the state machine
                • Send next (main) => send the event (nextMain) which corresponds to the transitions between the main.scxml states
                • Send next (child) => send the event (nextChild) which corresponds to the transitions between the child.scxml states
              • Every states (defined in main or child scxml) send a SendMessage event with the state name as parameter. This event is connected to output.

              One can see that when start is pressed, the main.scxml actually starts, enter main first state (SendMessage event received), invoke child.scxml (slots reacting to invokedServicesChanged is entered). But later on I don't see how to interact with the invoked service : I can't retrieve its states (as written in the documentation, stateName is not listing nested machines states), can't receive or send event to the child machine....I am surely missing something obvious !

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved