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. Pass parameters to a QState
Forum Updated to NodeBB v4.3 + New Features

Pass parameters to a QState

Scheduled Pinned Locked Moved Unsolved General and Desktop
14 Posts 4 Posters 2.9k Views 1 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.
  • K Offline
    K Offline
    kain
    wrote on 12 Nov 2018, 11:25 last edited by
    #1

    Hello,

    I have a QStateMachine which controls some MotorControl-logic. Basically it is a StateMachine which models the behavior of an cover/door which can be opened or closed.
    Therefore I want to have 3 QStates: 'Open', 'Moving' and 'Closed'. The state "Moving" is a QStateMachine itself and models the process of controlling my motor. However, I need to pass parameters to that QStateMachine somehow because it needs to know, wether it should open or close the cover.
    What I did up until know is, that I just created 2 "Moving" QStateMachines, one as "Opening" and one as "Closing" and passed the parameters via constructor.
    This might be sufficent in simple cases, but I need to be able to pass parameters to that QStateMachine using the signal which triggers the transition into that QStateMachine.
    Is there any way to achieve that?

    1 Reply Last reply
    0
    • D Offline
      D Offline
      dheerendra
      Qt Champions 2022
      wrote on 12 Nov 2018, 12:06 last edited by
      #2

      You can subclass QState and define the member variables,signals,slots required. You can set /use these values appropriately.

      Dheerendra
      @Community Service
      Certified Qt Specialist
      http://www.pthinks.com

      1 Reply Last reply
      1
      • K Offline
        K Offline
        kain
        wrote on 12 Nov 2018, 13:42 last edited by
        #3

        Okay, I am not sure how to do that.
        My current approach is this:

        #ifndef SMAXISPOSITIONING_H
        #define SMAXISPOSITIONING_H
        
        #include <QStateMachine>
        #include "axiscontrol.h"
        
        class SmAxisPositioning : public QStateMachine
        {
            Q_OBJECT
        public:
            explicit SmAxisPositioning(AxisControl *control, QStateMachine *parent = nullptr);
            long TargetPosition;
            long MovementSpeed;
            long Acceleration;
        private:
            AxisControl *axisControl;
        signals:
            void step();
            void retry();
            void error();
            void done();
        public slots:
            void onEnable();
            void onWaitEnable();
            void onPosition();
            void onWaitPosition();
        };
        
        #endif // SMAXISPOSITIONING_H
        

        I want this QStateMachine to set parameters on the "OnEntry"-Slot. How would I pass data into that slot then? OnEntry is called by the parent StateMachine...

        1 Reply Last reply
        0
        • D Offline
          D Offline
          dheerendra
          Qt Champions 2022
          wrote on 12 Nov 2018, 13:58 last edited by
          #4

          QStateMachine should hold the states and transition the states. It is better you define the three states called Open, Move and Close. You should add these states to State Machine and control the transition. In turn each state should carry out its task.

          You need define statement like Motor is object. This object is the state called Open, Close, Moving. When motor is appropriate state called - moving, state should automatically carry out the task assigned for that state. Now State machine role is just manage the different state and do the transition.

          In your case, you are making the StateMachine itself as "Moving" state. So you are overloading the meaning of stjatemachine. According to me it is not a good idea.

          Hope it is clear.

          Dheerendra
          @Community Service
          Certified Qt Specialist
          http://www.pthinks.com

          1 Reply Last reply
          1
          • K Offline
            K Offline
            kain
            wrote on 12 Nov 2018, 15:41 last edited by
            #5

            I am not sure about what you are suggesting. The reason the "Moving" state itself is a QStateMachine is to reduce the complexity.
            My parent statemachine looke like this:

            public:
                explicit CoverStateMachine(AxisControl *axisControl, QObject *parent = nullptr);
            private:
                AxisControl *axisControl;
                QState* stateOpen;
                SmAxisPositioning* stateClosing;
                SmAxisPositioning* stateOpening;
                QState* stateClosed;
                QState* stateError;
            signals:
                void closeCover();
                void openCover();
            public slots:
                //Commands
                void cmdCloseCover();
                void cmdOpenCover();
            };
            
            #endif // COVERSTATEMACHINE_H
            

            The advantage by modelling my process as statemachine is that it is 'interruptable' by different signals.

            1 Reply Last reply
            0
            • D Offline
              D Offline
              dheerendra
              Qt Champions 2022
              wrote on 12 Nov 2018, 17:11 last edited by dheerendra
              #6

              Moving, Open, Close are different states. It is better to represent them as different QState. It is more readable, maintainable. Now your system has three states. It moves between these states. Each of these state itself should define what action to be carried out for each of those states. Now you need QstateMachine to transition between these states. This is how statemachine need to work. We can put QState inside QStateMachine & it works. However it puts the StateMachine with two responsibilities(StateMachine & State). This code confuses the overall state machine working.

              Dheerendra
              @Community Service
              Certified Qt Specialist
              http://www.pthinks.com

              1 Reply Last reply
              1
              • S Offline
                S Offline
                SGaist
                Lifetime Qt Champion
                wrote on 12 Nov 2018, 20:39 last edited by
                #7

                Hi,

                Aren't you complicating things a bit ?
                You could have four states:

                1. Closed
                2. Opening
                3. Open
                4. Closing

                Wouldn't that be simpler to manage your door ?

                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
                1
                • K Offline
                  K Offline
                  kain
                  wrote on 13 Nov 2018, 09:17 last edited by
                  #8

                  Well, right now I got 4 states which is fine for me.
                  But I got different cases where I have to be able to position that cover (or any other motor) to a specific position.
                  For example that CoverStateMachine above would have a Slot

                  void cmdPositionCover(int position, int speed, int acceleration);
                  

                  In this case I can not just have 2 states 'Opening' and 'Closing' but need a more general approach where I could pass the slots parameter into the QState/QStateMachine which handles the movement of that cover. I can't have an infinite number of QStates for every possible position/speed/acceleration-combination.

                  1 Reply Last reply
                  0
                  • D Offline
                    D Offline
                    dheerendra
                    Qt Champions 2022
                    wrote on 13 Nov 2018, 09:58 last edited by
                    #9

                    You can define the base class like the following.

                    class BaseMotorState : public QState
                    {
                    public:
                    BaseMotorState();

                    public slots :
                    void setMotoValues(int p, int s, int a);
                    virtual void doTask();

                    private :
                    int pos;
                    int speed;
                    int acc;
                    };

                    All the four states inherit from BaseMotoState. Based on the entered() and exit() signals of QState you can set the appropriate values. Implement doWork() method in each of your derived state. Call the doWork().. when you enter the state.

                    Dheerendra
                    @Community Service
                    Certified Qt Specialist
                    http://www.pthinks.com

                    1 Reply Last reply
                    0
                    • K Offline
                      K Offline
                      kain
                      wrote on 13 Nov 2018, 11:08 last edited by
                      #10

                      Well, if I do it like this, I have to call 2 slots. The first one to set the parameters and the second one to transition into that QState.
                      Let's assume I got a Signal

                      void StartMovement(int pos, int speed, int acc)
                      

                      and a transition

                      idleState->addTransition(this, &Wrapper::StartMovement, MovingState);
                      

                      Is there any way to have these parameters available in the 'entered()'-Slot of QState? Of course I could call

                      setMotoValues(int p, int s, int a);
                      

                      before emitting the signal 'StartMovement' but this does not look to me like a good pattern. Or is there no other solution?

                      1 Reply Last reply
                      0
                      • S Offline
                        S Offline
                        SGaist
                        Lifetime Qt Champion
                        wrote on 13 Nov 2018, 11:21 last edited by
                        #11

                        Where does this signal come from ?
                        What is Wrapper ?

                        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
                        • K Offline
                          K Offline
                          kain
                          wrote on 13 Nov 2018, 14:18 last edited by
                          #12

                          Wrapper in this Case is the 'CoverStateMachine' above.
                          For example this would be

                          I need a signal with parameters like this:

                          void StartMovement(int pos, int speed, int acc)
                          

                          which triggers a transition like that

                          stateOpen->addTransition(this, &CoverStateMachine::StartMovement, stateMoving);
                          

                          but in that stateMoving QState I need those parameters from that signal above.

                          1 Reply Last reply
                          0
                          • D Offline
                            D Offline
                            dheerendra
                            Qt Champions 2022
                            wrote on 13 Nov 2018, 14:33 last edited by
                            #13

                            addTransition will help only in transitioning the state and make the state enter. Before making the transition to new state, set the appropriate properties/methods of next state. From there is should continue. Idea is that State should have all the properties set work when it starts.

                            Dheerendra
                            @Community Service
                            Certified Qt Specialist
                            http://www.pthinks.com

                            1 Reply Last reply
                            0
                            • M Offline
                              M Offline
                              MrShawn
                              wrote on 13 Nov 2018, 15:08 last edited by
                              #14

                              What @dheerendra is saying makes sense.... usually what i do is have a handler that oversees the execution of my state machine, which also holds the variables that will be used.

                              For example rather than inhereting from QStateMachine or QState i create a handler and have a function that builds my state machine. All the variables being worked with live in the scope of my handler class, and i have signals and slots in the handler class to do operations when states are entered and emit when it is time to change states. Here is some code from an example I did recently.

                              I posted a lot of code but hopefully it helps. I gave you the code in every slot that is connected to onEntry signal for each state, and then a little extra.

                              I read you said you have a state machine within a state machine. I suggest simplifying that down to using child states, it is effectively the same thing but allows you to have one state machine.

                              void MainHandler::buildStateMachine()
                              {
                                  stateRunning = new QState;
                                  stateError = new QFinalState;
                                  stateFinished = new QFinalState;
                                  stateSendCommand = new QState(stateRunning);
                                  stateWaitForArrival = new QState(stateRunning);
                                  stateDelay = new QState(stateRunning);
                                  stateRunning->setInitialState(stateSendCommand);
                              
                                  stateRunning->addTransition(this,&MainHandler::t_Error,stateError);
                                  stateRunning->addTransition(this,&MainHandler::t_LastCommandExecuted,stateFinished);
                                  stateSendCommand->addTransition(this,&MainHandler::t_CommandSent,stateWaitForArrival);
                                  stateWaitForArrival->addTransition(this,&MainHandler::t_LocationReached,stateDelay);
                                  stateWaitForArrival->addTransition(this,&MainHandler::t_KeepWaiting,stateWaitForArrival);
                                  stateDelay->addTransition(this,&MainHandler::t_NextCommand,stateSendCommand);
                              
                                  myStateMachine.addState(stateRunning);
                                  myStateMachine.addState(stateError);
                                  myStateMachine.addState(stateFinished);
                                 
                                  connect(stateRunning,&QState::entered,this,&MainHandler::slot_running);
                                  connect(stateSendCommand,&QState::entered,this,&MainHandler::slot_sendCommand);
                                  connect(stateWaitForArrival,&QState::entered,this,&MainHandler::slot_waitForArrival);
                                  connect(stateDelay,&QState::entered,this,&MainHandler::slot_delayStart);
                                  connect(stateError,&QState::entered,this,&MainHandler::slot_error);
                                  connect(stateFinished,&QState::entered,this,&MainHandler::slot_finished);
                                  connect(&myStateMachine,&QStateMachine::started,this,&MainHandler::slot_stateMachineStarted);
                                  connect(&myStateMachine,&QStateMachine::finished,this,&MainHandler::slot_stateMachineFinished);
                              
                              
                                  myStateMachine.setInitialState(stateRunning);
                              }
                              
                              void MainHandler::slot_executeStateMachine()
                              {
                                  currentCommand.getById(Command::getAllCommandIDs().first());
                                  myStateMachine.start();
                              }
                              
                              void MainHandler::slot_stateMachineStarted()
                              {
                                  qDebug() << "Started!";
                                  //currentCommand.getById(Command::getAllCommandIDs().first());
                              }
                              
                              void MainHandler::slot_stateMachineFinished()
                              {
                                  qDebug() << "State Machine Finished!!";
                              }
                              
                              void MainHandler::slot_running()
                              {
                                  qDebug() << "Running!";
                              }
                              
                              void MainHandler::slot_sendCommand()
                              {
                                  if (currentCommand.getId() == -1)
                                      currentCommand.getById(Command::getAllCommandIDs().first());
                              
                                  Position tmpPosition = myPositions.getPosition(currentCommand.getSavedPositionID());
                                  if (tmpPosition.id != -1)
                                  {
                                      currentPosition = tmpPosition;
                                      qDebug() << "Sending command for id" << currentCommand.getId();
                                      emit sig_executingCommand(currentCommand.getOrderNumber());
                                      qDebug() << currentPosition.getPosition(1) <<  currentPosition.getPosition(2) <<  currentPosition.getPosition(3) <<  currentPosition.getPosition(4) <<  currentPosition.getPosition(5);
                                      qDebug() << currentPosition.getPositions();
                                      qDebug() << currentCommand.toJSON().toJson(QJsonDocument::Compact);
                                      myConnection.write(currentCommand.toJSON().toJson(QJsonDocument::Compact)+"\r\n");
                                      myTimer.start();
                                      emit t_CommandSent();
                                  }
                                  else
                                  {
                                      emit t_Error();
                                  }
                              
                              }
                              
                              void MainHandler::slot_waitForArrival()
                              {
                                  //qDebug() << "Waiting...";
                              
                                  bool done = true;
                                  QListIterator<Axis> iter(myAxisList);
                                  Axis A1(-1,0),A2(-1,0),A3(-1,0),A4(-1,0),A5(-1,0);
                                  while (iter.hasNext())
                                  {
                                      Axis tmpAxis = iter.next();
                                      switch (tmpAxis.axis) {
                                      case 1:
                                          //qDebug() << "got 1";
                                          A1 = tmpAxis;
                                          break;
                                      case 2:
                                          //qDebug() << "got 2";
                                          A2 = tmpAxis;
                                          break;
                                      case 3:
                                          //qDebug() << "got 3";
                                          A3 = tmpAxis;
                                          break;
                                      case 4:
                                          //qDebug() << "got 4";
                                          A4 = tmpAxis;
                                          break;
                                      case 5:
                                          //qDebug() << "got 5";
                                          A5 = tmpAxis;
                                          break;
                                      }
                                  }
                              
                                  done = (determineIfAxisIsInPosition(A1) && determineIfAxisIsInPosition(A2) && determineIfAxisIsInPosition(A3) && determineIfAxisIsInPosition(A4) && determineIfAxisIsInPosition(A5));
                              
                                  if (done)
                                  {
                                      emit t_LocationReached();
                                  }
                                  else if(myTimer.elapsed() < 5000)
                                  {
                                      QTimer::singleShot(100,this,&MainHandler::t_KeepWaiting);
                                  }
                                  else
                                      emit t_Error();
                              }
                              
                              void MainHandler::slot_delayFinished()
                              {
                                  qDebug() << "Delay finished!";
                                  if(currentCommand.isLastCommand())
                                      emit t_LastCommandExecuted();
                                  else
                                  {
                                      currentCommand.getById(currentCommand.getNextCommandId());
                                      QTimer::singleShot(30,this,&MainHandler::t_NextCommand);
                                  }
                              }
                              
                              void MainHandler::slot_delayStart()
                              {
                                  qDebug() << "Delay starting!";
                                  QTimer::singleShot(currentCommand.getMsDelay(),this,&MainHandler::slot_delayFinished);
                              }
                              
                              void MainHandler::slot_finished()
                              {
                                  qDebug() << "Finished";
                              }
                              
                              void MainHandler::slot_error()
                              {
                                  emit sig_Error("Error running command number: " + QString::number(currentCommand.getOrderNumber()));
                                  qDebug() << "Error!";
                              }
                              
                              
                              1 Reply Last reply
                              4

                              1/14

                              12 Nov 2018, 11:25

                              • Login

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