Traffic Lights example

In the Traffic Lights example we use svc_expr to control the traffic lights in a junction. The junction itself, the traffic lights for cars and pedestrians, and the pedestrian crossing button are all represented in the mix as stateful services. The state of the junction and the pedestrian crossing button decides the state of the traffic lights, that is, whether they are green or red.

As visible on Figure 1, the services and operations are divided in between four logical folders and the parent folder that contains the Junction service.

Each service has a state that can be changed by the operations while some operations are invoked on state changes.

The state of the Junction service might be seen as a super state, in that it decides the state of the Lights services. This state, defined as the Mode state variable, can be changed by the GiveMode notify, and the resulting transaction that sets Mode.

The change of Mode fires the NotifyMode operation resulting in a transaction that changes the states of both Lights services based on Mode.

The RequestButton service is a client on the GiveMode notify and fires this operation periodically. Depending on the state of RequestButton, the notify has a different output that decides whether the state of Junction will be changed or kept.

Figure: Traffic Lights - Mix overview


Tip: Get the code from the SPARKL public repository.
Table 1. Traffic Lights - Markup
Example Description
<folder name="Traffic">
  <field name="ON"/>
  <field name="SET"/>
  <field name="mode" 
    type="string"/>
  <field name="PEOPLE"/>
  <field name="TRAFFIC"/>
  <service name="Sequencer" 
    provision="sequencer">
  <service name="Junction" 
    provision="expr">
    <prop name="expr.state" 
      Mode="NewMode"/>
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
NewMode = "traffic".
    ]]></prop>
  </service>
  <mix name="Mix">
    <folder name="Auto">
    ...
    </folder>
    <folder name="TrafficLights">
    ...
    </folder>
    <folder name="PedestrianLights">
    ...
    </folder>
    <folder name="Button">
    ...
    </folder>
  </mix>
</folder>
Note: Do not manually type the <![CDATA...]]> delimiters. The Editor automatically renders text content as XML CDATA sections.
In this mix we use:
  • 6 fields, most of them FLAGs, except for the mode field, that is of type string
  • 4 services in addition to the Sequencer, all stateful and using svc_expr
  • 10 operations divided in between 4 folders
  • 3 transactions started by notify operations either:
    1. Automatically every 20 seconds
    2. On a particular state change
    3. On transaction testing

Each service has a state that can be updated or changed by the operations.

For the Junction service, we defined the state variable Mode. It can have either of the following values:
  • people
  • traffic
The state of the service is initialised to traffic and the NewMode state variable updates it.
Note: The variable can be initialised with the expr.init property too. It does not count as a state change.

The following rows explain the components of each folder and their role in the mix.

See also:
  • Table 2 on the state variables used
  • Table 3 on the variables used
  • Table 4 on the transactions possible in this mix
<folder name="Auto">
  <notify name="NotifyMode" 
    service="Sequencer" 
    clients="Junction" 
    fields="ON mode">
    <prop name="expr.bind.out" 
      Mode="mode"/>
    <prop name="expr.state" 
      Mode="OldMode"/>
  </notify>
  <request name="GetMode" 
    service="Junction" 
    fields="ON mode">
    <prop name="expr.bind.in" 
      ModeField="mode"/>
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
case ModeField of   
  "people" -> "People";
  _Otherwise -> "Traffic" 
end.
    ]]></prop>
    <reply name="People" 
      fields="PEOPLE"/>
    <reply name="Traffic" 
      fields="TRAFFIC"/>
  </request>
  <consume name="SetMode" 
    service="Junction" 
    fields="SET mode">
    <prop name="expr.bind.in" 
      NewMode="mode"/>
  </consume>
</folder>
Whenever the state of the Junction service changes, the NotifyMode operation fires and outputs the field mode.
Note: When the service starts, it is initialised to a state. This also counts as state change.

This field is mapped to the state variable Mode. The value of the field is decided by OldMode, that is, the value of the Mode variable at the time the notify is fired.

The GetMode request operation sends one of either:
  • People reply outputting PEOPLE
  • Traffic reply outputting TRAFFIC
The expression inside the GetMode operation selects the reply set based on the value of the ModeField variable that is bound to the input field mode.

The value of this variable is generated by an operation inside the Button folder.

The SetMode consume updates the state of Junction based on the value of the mode field.

The reply FLAGs are used by the operations inside the TrafficLights and PedestrianLights folders.

<folder name="TrafficLights">
  <service name="Lights" 
    provision="expr">
    <prop name="expr.state" 
      Colour="NewColour"/>
  </service>
  <consume name="SetGreen" 
    service="Lights" 
    fields="ON TRAFFIC">
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
NewColour = green.
    ]]></prop>
  </consume>
  <consume name="SetRed" 
    service="Lights" 
    fields="ON PEOPLE">
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
NewColour = red.
    ]]></prop>
  </consume>
</folder>
The Lights service has a state variable Colour that can be updated by NewColour. The state variable can have the following values:
  • green
  • red
The expressions inside the two consume operations generate the value of NewColour:
  • SetGreen sets the value to green
  • SetRed sets the value to red
Only one of the consumes is invoked as the GetMode operation generates either the PEOPLE or TRAFFIC FLAG field.
<folder name="PedestrianLights">
  <service name="Lights" 
    provision="expr">
    <prop name="expr.state" 
Colour="NewColour"/>
  </service>
  <consume name="SetGreen" 
    service="Lights" 
    fields="ON PEOPLE">
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
NewColour = green.
    ]]></prop>
  </consume>
  <consume name="SetRed" 
    service="Lights" 
    fields="ON TRAFFIC">
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
NewColour = red.
    ]]></prop>
  </consume>
</folder>
The Lights service has a state variable Colour that can be updated by NewColour. The state variable can have the following values:
  • green
  • red
The expressions inside the two consume operations generate the value of NewColour:
  • SetGreen sets the value to green
  • SetRed sets the value to red
Only one of the consumes is invoked as the GetMode operation generates either the PEOPLE or TRAFFIC FLAG field.
<folder name="Button">
  <field name="PRESS"/>
  <service name="RequestButton" 
    provision="expr">
    <prop name="expr.state" 
PersonWaiting="NewPersonWaiting"/>
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
NewPersonWaiting = false.
    ]]></prop>
  </service>
  <notify name="GiveMode" 
    service="Sequencer" 
    clients="RequestButton" 
    fields="SET mode">
    <prop name="expr.auto" 
      interval="20s"/>
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
case PersonWaiting of   
  true ->
    NewPersonWaiting = false,    
    ModeField = "people";        
  false ->    
    ModeField = "traffic" 
end, 
true.
    ]]></prop>
    <prop name="expr.bind.out" 
      ModeField="mode"/>
  </notify>
  <consume name="Reset" 
    service="RequestButton" 
    fields="SET">
    <prop name="expr.src" 
      content-type="text/x-erlang"><![CDATA[
NewPersonWaiting = false.
    ]]></prop>
  </consume>
  <notify name="Press" 
    service="Sequencer" 
    fields="PRESS"/>
  <consume name="Pressed" 
    service="RequestButton" 
    fields="PRESS">
    <prop name="expr.src"
      content-type="text/x-erlang"><![CDATA[
NewPersonWaiting = true.
    ]]></prop>
  </consume>
</folder>
The RequestButton service has a state variable PersonWaiting. It can be updated by NewPersonWaiting. The possible values of the state variable are:
  • true
  • false

The state is initialised to false.

The GiveMode notify is triggered every 20 seconds.

The mode field output by the operation is bound to the variable ModeField. The value of this variable is decided by the expression executing in the GiveMode operation.

If at the time GiveMode is triggered:
  • PersonWaiting is true, the value of ModeField is people
  • PersonWaiting is false, the value of ModeField is traffic
The last line of the expression, true, makes sure the notify is fired every time it is triggered, every 20 seconds.

The Reset operation resets the state of PersonWaiting to false every time GiveMode is fired, regardless of the actual service state.

The Press notify is just a representation of any actual person who happens to press the button. For this reason, we defined no client on this operation.

The Pressed consume sets PersonWaiting to true every time this operation is invoked which will have an effect on GiveMode in the next 20 second window.

Table 2. Traffic Lights example - State variables
Name Used by Updated by Possible values New value generated by
Mode Junction service NewMode variable
  • people
  • traffic
SetMode consume
Note: See markup of Auto folder above for further details.
Colour Lights service NewColour variable
  • green
  • red
  • SetGreen consume
  • SetRed consume
Colour Lights service NewColour variable
  • green
  • red
  • SetGreen consume
  • SetRed consume
PersonWaiting RequestButton service NewPersonWaiting variable
  • true
  • false
  • Pressed consume
  • Reset consume
Note: See markup of Button folder above for further details.

Table 3. Traffic Lights example - Variables
Name Used by Bound to Possible values Value generated by
ModeField
  • GetMode request
  • GiveMode notify
mode field
  • people
  • traffic
GiveMode notify
Note: See markup of Button folder above for further details.

Table 4. Traffic Lights example - Transactions
Transaction Started by Result Operations
Transaction1 State change in Junction service
Note: When the service starts, it is initialised to a state. This also counts as state change.
Updates the states of both Lights services NotifyMode > GetMode > SetGreen/SetRed
Transaction2 Automatic, starts every 20 seconds if the RequestButton service is up Updates the state of the Junction service
Note: If Transaction2 changes the state of Junction, the state change triggers NotifyMode and thus Transaction1 starts.
GiveMode > SetMode/Reset
Transaction3 Can be started manually via transaction testing Changes the state of the RequestButton service.

In the next 20 second window in which Transaction2 starts, the GiveMode notify changes the state of the Junction service.

This state change triggers NotifyMode, and thus Transaction1 starts.

Press > Pressed