Reactive Programming is gaining momentum these days and here is my minimal contribution to get more people interested, which hopefully will rise average software products quality in “The Industry”.

This is part 1 of “Reactive traffic lights” series where I’m going to build automated traffic lights using RxJs.

It’s just a “case-study” so you’re always welcome to comment on things may be done better or more idiomatic way.

Let’s list some basic requirements:

  • There must be 2 automatic lights: for cars and for pedestrians;
  • Lights for cars have 3 colors: red, yellow, green;
  • Pedestrian lights have 2 colors: red and green;
  • Green light should blink before switch.

Visualization

We will “render” our traffic lights using simple blocks with circles inside:

For Vehicles

<div class="t-lights t-lights-h t-lights-3">
    <div class="t-light red"></div>
    <div class="t-light yellow"></div>
    <div class="t-light green"></div>
</div>

For Pedestrians

<div class="t-lights t-lights-h t-lights-3">
    <div class="t-light red"></div>
    <div class="t-light green"></div>
</div>
30

Css source.

Bootstrap

The only dependency we have - is Rx library:

<script type="text/javascript" src="https://unpkg.com/@reactivex/rxjs/dist/global/Rx.js"><!-- --></script>

First - lets build default static version where cars light is always green. As pedestrians light is simpler: have only 2 colors excluding each other

  • we can define it depending on cars light.
/**
 * Returns a css class toggle function, on a given element
 * @param {String} cssClass
 * @returns {function(node:Object):void}
 */
function toggleCssClass(cssClass) {
  return function (element) {
    return function (add) {
      if (add) {
        element.classList.add(cssClass);
      } else {
        element.classList.remove(cssClass);
      }
    }
  }
}

/**
 * Returns an innerHTML setter function
 * @param element
 * @returns {function(html:String):void}
 */
function setInnerHtml(element) {
  return function (html) {
    element.innerHTML = html;
  }
}

(function() {
  var carsRed = Rx.Observable.of(false);
  var carsYellow = Rx.Observable.of(false);
  var carsGreen = Rx.Observable.of(true);

  var pedestrianGreen = carsRed;
  var pedestrianRed = pedestrianGreen.map(function(green) {
    return !green;
  });

  // Visualize
  var carsRedLight = document.querySelector('#cars_lights div.t-light.red');
  var carsYellowLight = document.querySelector('#cars_lights div.t-light.yellow');
  var carsGreenLight = document.querySelector('#cars_lights div.t-light.green');
  var pedestrianRedLight = document.querySelector('#pd_lights div.t-light.red');
  var pedestrianGreenLight = document.querySelector('#pd_lights div.t-light.green');

  // helper to toggle .on css class on light bulbs
  var onClassToggle = toggleCssClass("on");

  carsRed.forEach(onClassToggle(carsRedLight));
  carsYellow.forEach(onClassToggle(carsYellowLight));
  carsGreen.forEach(onClassToggle(carsGreenLight));
  pedestrianGreen.forEach(onClassToggle(pedestrianGreenLight));
  pedestrianRed.forEach(onClassToggle(pedestrianRedLight));

})();

Now green light for cars must be lighter and same for pedestrians red.

React to time

Now let’s make it work, based on a timer

  • we will reserve 40 ticks for cars, and 20 for pedestrians:

    • Pedestrians green will blink it’s last 5 ticks
    • Cars green will blink 5 ticks before switch to yellow
    • Yellow light will be on for 3 more ticks before switch to red
    • Yellow will be on within last 3 ticks while red is on
    • Blinking is implemented by piking only even timer values
  var periodLength = 60; // full iteration time, ticks
  var carsPeriod = 40; // green + yellow period for cars
  var greenBlinkPeriod = 5;
  var yellowPeriod = 3;

Define the timer

Timer is the single signal sources needed for this case, other values will be derived from it.

  // Define ticks source, with 500ms delay, normalized to our period length: [0..59]
  var periodTimer = Rx.Observable.timer(0, 500)
    .map(function (t) {
      return t % periodLength;
    });

Vehicles lights

Green for vehicles will be on first 32 ticks, next 5 - will blink and next 3 - yellow will be on:

  // Green signal will take first 37 ticks
  var carsGreen = periodTimer
    .map(function (t) {
      // last 5 ticks blinking green
      return t < (carsPeriod - yellowPeriod)
        && (
          t < (carsPeriod - yellowPeriod - greenBlinkPeriod)
          || t % 2 == 0 // blinking
        );
    });

Yellow will be on last 3 ticks for both - vehicles and pedestrians periods:

  // yellow will take last 3 ticks before switch to red
  var carsYellow = periodTimer
    .map(function (t) {
      // last 3 ticks for yellow
      return (t < carsPeriod && t > (carsPeriod - yellowPeriod))
        || (t < periodLength && t > (periodLength - yellowPeriod));
    });

Red - on for full pedestrian period:

  // red
  var carsRed = periodTimer
    .map(function (t) {
      return t >= carsPeriod;
    });

Pedestrian lights

Pedestrian green is on while red for vehicles and is blinking last 5 ticks:

  var pedestrianGreen = carsRed;

  var pedGreenBlink = periodTimer
    .map(function (t) {
      // return true to turn green light off
      return t >= (periodLength - yellowPeriod) && t % 2 != 0
    });

  var pedestrianGreenBlinking = Rx.Observable
    .combineLatest(pedestrianGreen, pedGreenBlink)
    .map(function (arr) {
      // is green an not blinking
      return arr[0] && !arr[1];
    });

Pedestrian red is opposite to green:

  var pedestrianRed = pedestrianGreen.map(function (green) {
    return !green;
  });

Additionally let’s add some countdowns. As our timer is running twice a second we will round our countdown to seconds dividing by 2:

  var pedestrianGreenCountdown = periodTimer
    .map(function (t) {
      if (t >= carsPeriod) {
        return Math.round((periodLength - t) / 2);
      }
      return 0;
    });

  var pedestrianRedCountdown = periodTimer
    .map(function (t) {
      if (t < carsPeriod) {
        return Math.round((carsPeriod - t) / 2)
      }
      return 0;
    });

And finally - connect all dots:

  // Visualize
  var carsRedLight = document.querySelector('#cars_lights div.t-light.red');
  var carsYellowLight = document.querySelector('#cars_lights div.t-light.yellow');
  var carsGreenLight = document.querySelector('#cars_lights div.t-light.green');
  var pedestrianRedLight = document.querySelector('#pd_lights div.t-light.red');
  var pedestrianGreenLight = document.querySelector('#pd_lights div.t-light.green');

  // helper to toggle .on css class on light bulbs
  var onClassToggle = toggleCssClass("on");

  // Connecting the dots: vehicle lights
  carsRed.forEach(onClassToggle(carsRedLight));
  carsYellow.forEach(onClassToggle(carsYellowLight));
  carsGreen.forEach(onClassToggle(carsGreenLight));
  // pedestrian lights
  pedestrianGreenBlinking.forEach(onClassToggle(pedestrianGreenLight));
  pedestrianRed.forEach(onClassToggle(pedestrianRedLight));
  // Countdowns
  pedestrianGreenCountdown.forEach(setInnerHtml(pedestrianGreenLight));
  pedestrianRedCountdown.forEach(setInnerHtml(pedestrianRedLight));

Live Demo

Vehicles


Pedestrians

30


Css source. Javascript source.

Conclusion

By defining our problems as signal streams and transformations it becomes really easy to compose the solution using great libraries like RxJs.

Of course being reactive means much more than we seen in this simple example, and I highly recommend you go deeper.

In the next “episode” I’m planning to transform this example to a “Pelican Crossing”.