3

I am trying to build a system, which is orchestrated by a main state machine and has different sub state machines for the tasks it is executing. Being a long-time Qt user I looked at the new SCXML implementation in Qt 5.8. However I cannot figure out how to use the API provided with 5.8 to implement sub state machines properly.

My idea is to use a main state machine and then invoke the specific sub state machines in the states of the main state machine. While invoking the sub state machines works I could not figure out how to access them after the top state machine objects emits invokedServicesChanged(..) I can access the pointer to QScxmlInvokableService but not to the concerning state machine.

Also the top level state machine object only exposes states and events from the top and not from the invoked state machines. For example topLevelStateMachine->activeStateNames() only lists the top states.

Looking at Qt's source code I saw that QScxmlInvokableService is actually a the base class for QScxmlScxmlService which contains a pointer to the concerning state machine. Unfortunately QScxmlScxmlService is defined in qscxmlinvokableservice_p.h which is private as the _p in the name indicates. So how am I supposed to use the public SCXML API? Am I missing something? IIRC SCXML support was a technical preview in 5.7 but is now included in 5.8 as part of the normal distribution.

Nils
  • 13,319
  • 19
  • 86
  • 108
  • 1
    I'd say this is an oversight in the API. Feel free to add `QT+= scxml_private` to the project and use the implementation details to do what you need done. That Qt module is quite new and yes, it lacks quite a few necessary bits to make it useful. – Kuba hasn't forgotten Monica Jan 31 '17 at 15:31
  • When you say "sub state machines", are you referring to separate `.scxml` state machines, or are you referring to [compound states](https://www.w3.org/TR/scxml/#N10192)? If the former, why not the latter? – Phrogz Mar 03 '17 at 15:22
  • Separate state machines. I do not think that having a large state machine in just one XML file is a good idea, but in theory it could also be solved with compound states. How would you organize a large state chart with many sub state using SCXML and Qt? – Nils Mar 03 '17 at 15:31
  • 1
    I'm not sure how to answer that question in general. I have done so in the past for several "large" (depending on your definition) state charts. Each was different, with different approaches to how to organize the states, and there were certainly design decisions that I had to make for how to organize the states in parallels and compounds, with different pros and cons for each. A good visual editor was important for this: see Figure 2 in [this whitepaper of mine](http://phrogz.net/files/Developing%20User%20Interfaces%20using%20SCXML%20Statecharts.pdf) for an excerpted example. – Phrogz Mar 03 '17 at 15:59
  • Thx for the quick answer I need to study this :) – Nils Mar 03 '17 at 22:33

1 Answers1

0

I spent the last week or so studying the examples and then writing my own state machine and code that responds to it. It took a while as the documentation is not as clear as it could be.

I have found that activeStateNames does retrieve all states contained within the state machine, including sub-states.

It took me a couple of readings of the traffic light example to get figure this out. The key is that the sub-state machines are contained in specific states. (The graphical view of the state machine helps here.)

In the example there are only two states in the over-all, top-level machine: working and broken. The transitions are controlled by events smash and repair.

Within each of the two states are smaller state machines. Broken contains a state machine of two states: blinking and unblinking. That state machine starts in the state blinking when broken is entered.

When the in the sub-state blinking of broken, activeStateNames will return blinking if called with false (defaulted) or blinking and broken when called with true.

So how do you use this?

If I have something I want to set/unset according to a specific state, I can connectToState in the machine. The slot I connect to will get called when the state changes between active and inactive, and it will receive a boolean value saying if the state is active. In the traffic light example, the state red is connected to the redLight. Since redLight should be on when in the red state and not otherwise, it is connected to a slot takes a boolean: true turns the light on, false turns it off.

Okay, but what if I want to catch an event when I enter a state?

I simply select the state, then add an onEntry -> send and specify the event name. This will cause an event to be sent when I enter the state. This event can be routed to a slot (in Qt 5.8) using connectToEvent. [The Qt 5.7 version only has a generic eventOccurred signal which you can send to a slot and then query which event using event.name().]

Matthew Kraus
  • 6,660
  • 5
  • 24
  • 31
jwernerny
  • 6,978
  • 2
  • 31
  • 32