TaskTree Traffic Light | Qt TaskTree | Qt 6.11.1
Description: Demonstrates how to implement state machine behavior using TaskTree through a traffic light simulation.

- Qt 6.11
- Qt TaskTree
- Qt TaskTree Examples
- TaskTree Traffic Light
- Qt 6.11.1 Reference Documentation

# TaskTree Traffic Light

Demonstrates how to implement state machine behavior using TaskTree through a traffic light simulation.

The TaskTree Traffic Light is an alternative implementation for the Traffic Light examples in Qt SCXML and Qt State Machine.

The application consists of three main components:

- A `TrafficLight` widget that displays the current state.
- A `TaskTree` recipe that controls state transitions.
- A `GlueInterface` that synchronizes the UI with the state machine.

The following sections describe more details of the example's code.

#### Helper Glue Class

The `GlueInterface` class provides a clean separation between the UI and operational logic.

     enum class Light
     {
         Off    = 0,
         Red    = 1 << 0,
         Yellow = 1 << 1,
         Green  = 1 << 2,
     };

     Q_DECLARE_FLAGS(Lights, Light)
     Q_DECLARE_OPERATORS_FOR_FLAGS(Lights)

     class GlueInterface final : public QObject
     {
         Q_OBJECT

     public:
         // operational logic -> GUI
         void setLights(Lights lights) { emit lightsChanged(lights); }

         // GUI -> operational logic
         void smash() { emit smashed(); }
         void repair() { emit repaired(); }

     signals:
         void lightsChanged(Lights lights);

         void smashed();
         void repaired();
     };

The `GlueInterface` class provides the following interface methods:

Method | Purpose
--- | ---
`setLights()` | Updates traffic light state from TaskTree recipe
`lightsChanged()` | Notifies UI when light states change
`smash()/` `repair()` | Handles fault simulation and recovery actions

When users interact with the UI:

- Clicking pause triggers `smash()`, simulating a fault.
- Clicking play triggers `repair()`, restoring normal operation.

#### Implementing the state machine

The state machine logic is implemented through a TaskTree recipe. The `recipe()` method creates this logic using a reference to `GlueInterface`:

     ExecutableItem recipe(GlueInterface &iface)
     {
         return Forever {
             Group { // "working" state
                 stopOnSuccess,
                 parallel,
                 signalAwaiterTask(&iface, &GlueInterface::smashed), // transitions to the "broken" state
                 Forever {
                     switchLightsTask(&iface, Light::Red, 3s), // "red" state
                     switchLightsTask(&iface, Light::Red | Light::Yellow, 1s), // "redGoingGreen" state
                     switchLightsTask(&iface, Light::Green, 3s), // "green" state
                     switchLightsTask(&iface, Light::Yellow, 1s), // "greenGoingRed" state
                 }
             },
             Group { // "broken" state
                 stopOnSuccess,
                 parallel,
                 signalAwaiterTask(&iface, &GlueInterface::repaired), // transitions to the "working" state
                 Forever {
                     switchLightsTask(&iface, Light::Yellow, 1s), // "blinking" state
                     switchLightsTask(&iface, Light::Off, 1s), // "unblinking" state
                 }
             }
         };
     }

##### Root state management

The top level Forever item executes its children in an infinite loop. It contains two subgroups representing working and broken states.

State transition rules:

- States execute in sequence, because the default execution mode is sequential.
- When one state completes, the other begins.
- Transitions are triggered when a user clicks the pause or play button in the UI, emitting the corresponding signal.

##### The "working" operation state

The working Group consists of two tasks running in parallel:

- The signalAwaiterTask connects to the `GlueInterface::smashed` signal. If the signal is triggered, the task immediately finishes with DoneResult::Success. This causes the working Group to stop executing, and the recipe transitions to the broken state.
- The nested Forever task consist of four `switchLightsTask` tasks ("red", "redGoingGreen", "green", and "greenGoingRed") executed sequentially in an infinite loop.

The stopOnSuccess item of each group instructs it to stop executing when any of its child tasks finishes with DoneResult::Success.

The `switchLightsTask` implementation:

- Updates light states through `GlueInterface::setLights()`.
- Creates a timed delay using timeoutTask.
- Maintains state for the specified duration `timeout`.

     static Group switchLightsTask(GlueInterface *iface, Lights lights, const milliseconds &timeout)
     {
         return {
             onGroupSetup([iface, lights] { iface->setLights(lights); }),
             timeoutTask(timeout, DoneResult::Success)
         };
     }

##### The "broken" operation state

The broken Group is similar to the working Group. The only difference is that it connects to the `GlueInterface::repaired` signal, and the lights cycle only between 2 states toggling the yellow light on and off.

#### Application setup

The main application setup creates and connects all components:

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

         GlueInterface iface;
         QTaskTree taskTree({recipe(iface)});
         TrafficLight widget(iface);

         widget.show();
         taskTree.start();

         return app.exec();
     }

The `main()` function instantiates QApplication, `GlueInterface`, QTaskTree and `TrafficLight` objects.

To construct the `taskTree` object, we pass a `recipe()` constructed with the `iface` object. The `widget` also uses the passed `iface` object.

Before executing the application, the `main()` function shows the `TrafficLight` widget and starts the task tree.

#### Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, see Qt Creator: Tutorial: Build and run.

Example project @ code.qt.io