[Qt-interest] Qml & State Machines

Volodimir.Burlik at nokia.com Volodimir.Burlik at nokia.com
Wed Dec 1 01:35:49 CET 2010


Hi Frank, 

Two exremes:
1) You have only one state - basically it is event handler for all events in the system - the logic ends up in the action functions/handlers 

2) You go crazy and create a state for each condition in your system. Bad, but still better to have logic co-located in one place, even big one.
It is designer decision which logic belongs to application/component logic, which one - implementation detail of one sequence of action. That's why I used "linear-ish" term for action function.

>>	As a somewhat contrived example, consider a state machine that beeps every time you press a button one hundred times in a row.  You could have one >>	hundred states, and the button event causes the transition from state i to state i+1, with state 100 transitioning to the beep state.  But it's a 
>>	fundamentally better design to have a single processing-button state and an extended state variable, number-of-button-presses, with a guard 
>>	condition that takes you to the beep state only when number-of-button-presses equals 100.

Here it is:

// more of a pseudo code though. Emmited properties mean absence of value or null.

 property int counter:0
 states: [
            State
            {
                 "name":"IDLE",
                 onEntry{ reset }
                 { event: "EXT_Click",          next: "COUNT_CLICKS" }
            },
            State
            {
                "name":"COUNT_CLICKS",
                "heartbeatrate": 3000,
                "heartbeats": 1,
                onEntry{ countClicks }
                    { event: "EXT_Click",       action: {countClicks}              },
                    { event: "EXT_Click",       action: {checkNumber}              },           
                    { event: "INT_TimeToBeep",  action: {beep},       next: "IDLE" },
                    { event: "SYS_LASTHEARTBEAT",                     next: "IDLE" }		// Bonus: if you waited to long it resets 
            }
        ]
    }
function reset(){ counter = 0; }
function countClicks(){counter++;}
function checkNumber(){ if(++counter == 100)sm.pushEvent("INT_TimeToBeep");   // or Id }
function beep() { doBeep()}

This example shows that counting is hidden and it is implementation detail. However resulting event (reached 100) is there to understand logic by just reading the state machine.

>> Furthermore, you generally want the additional flexibility provided by letting the events carry event data, and letting the guard conditions depend on >> the event data.
It is given - event should be treated as message, b/c it does carry data.


>> You might want to consider also supporting exit actions. 
OnEntry it is abs nessecary to support N->1 transitions, but exit is optional. Even if you have 1->N transitions - you can write:

{
  { event: Event1, action: do1 }                              // do1() runs 1st
  { event: Event1, action: exitAction, next: state_A }        // existAction() runs 2nd then juming to state_A

  { event: Event2, action: do2 }                              
  { event: Event2, action: exitAction, next: state_B }
}

Although in practise it might save few extra lines in one state. In example above it would save one line.

Thanks
Vlad


-----Original Message-----
From: qt-interest-bounces at trolltech.com [mailto:qt-interest-bounces at trolltech.com] On Behalf Of ext K. Frank
Sent: Tuesday, November 30, 2010 1:33 PM
To: Qt-interest
Subject: Re: [Qt-interest] Qml & State Machines

Hi Vlad -

On Tue, Nov 30, 2010 at 3:40 PM,  <Volodimir.Burlik at nokia.com> wrote:
> Hi,
> I've learned that Qml states are not designed for full blown state machines and intended for UI elements. However here the form that I think would help to make it as such:
>
> states: [
>        State {
>            name: "INIT"
>            onEntry:  { doOnEntry1 }
>
>            event: event id1            action: { doSomething1 }       next: "statename1"
>            event: event id2            action: { doSomething2 }       next: "statename2"
>        },
> ...

I would strongly advise having the framework support using "guards" to make the transitions depend on "extended state variables."

Maybe something like:

   states: [
          State {
              name: "INIT"
              onEntry:  { doOnEntry1 }

              event: event id1 {
                 guardConditionA:  action: { doSomething1A }
next: "statename1A"
                 guardConditionB:  action: { doSomething1B }
next: "statename1B"
                 guardConditionC:  action: { doSomething1C }
next: "statename1C"
              }
              event: event id2            action: { doSomething2 }
  next: "statename2"
          },

The guard conditions are boolean functions of data you deem to be extended state variables.  (And the doSomethings, of course, can change the values of the extended state
variables.)  Furthermore, you generally want the additional flexibility provided by letting the events carry event data, and letting the guard conditions depend on the event data.

In practice it turns out to be too restrictive to use a formalism where the state of the system is encoded solely in a state label that takes on (a hopefully small) number of enumerated values, and where the transitions depend solely on the enumerated state and the event.  If you do things this way, you end up creating lots of very similar states that would be better off being represented by a single state (a single enumerated state label) and some extended state data.

As a somewhat contrived example, consider a state machine that beeps every time you press a button one hundred times in a row.  You could have one hundred states, and the button event causes the transition from state i to state i+1, with state 100 transitioning to the beep state.  But it's a fundamentally better design to have a single processing-button state and an extended state variable, number-of-button-presses, with a guard condition that takes you to the beep state only when number-of-button-presses equals 100.

Without extended state variables and guards, you are forcing (the dreaded) state explosion on your users.

(Of course, extended state variables can be abused, and spoil the benefits of using a state-machine programming formalism.  Deciding how to partition your information between an enumerated state value and extended state variables is a key, judgmental design decision, for which there is no rote procedure or single right answer.)

> ...
> What do you think guys?

One last minor comment:  Your scheme supports entry actions and transition actions.  You might want to consider also supporting exit actions.  Not having explicit exit actions is not that big a restriction -- you can always put them in the transition actions -- but it can get somewhat redundant adding the same exit action for a particular state at the beginning of each transition action bound to a transition that leaves that state.

>
> Thanks
> Vlad

Best.


K. Frank

_______________________________________________
Qt-interest mailing list
Qt-interest at trolltech.com
http://lists.trolltech.com/mailman/listinfo/qt-interest




More information about the Qt-interest-old mailing list