Switch

Introduction

This example demonstrates how to specify a stateless function and how to implement it.

Natural Language Description

A railroad switch is a mechanical installation enabling railway trains to be guided from one track to another at a railway junction. The position of the switch is safety-related. We are considering a control/command system in charge of safely determining the position of the switch. Redudancy is mandatory to take into account sensor failures, leading to a switch equipped with 3 sensors to determine its position. These sensors provide 3 measures: m1, m2, and m3.

The possible values are :

  • normal (right position)
  • reverse (left position)
  • void (middle position while the switch is moving from a position to another)

We have to specify a function

pos <-- estimate (m1, m2, m3) = ...

calculating an estimate of the switch position, considering that:

  • if at least, one normal and one reverse are measured, then the result is void.
  • if all measures are void, then the result is void.
  • in all other cases, it should return normal or reverse

Formal specification

As usual, the modelling solution is not unique.

We could for example make explicit the value of the return value pos for each value of m1, m2, m3. We could also have a more elegant solution, where we are not obliged to list every single case:

MACHINE switch
SETS
    POSITION = {normal,reverse,void}
OPERATIONS
pos <-- estimate(m1,m2,m3) =
    PRE
        m1: POSITION &
        m2: POSITION &
        m3: POSITION
    THEN
        SELECT
            normal: {m1,m2,m3} &
            reverse /: {m1,m2,m3}
        THEN
            pos:=normal
        WHEN
            reverse: {m1,m2,m3} &
            normal /: {m1,m2,m3}
        THEN
            pos:=reverse
        ELSE
            pos:=void
        END
    END
END

This model doesn’t generate any non-obvious proof obligation, as there is no variable, no invariant and no properties to verify.

The next step of this example is to implement this specification. We are now obliged to make explicit the algorithm:

IMPLEMENTATION switch_1
REFINES switch
OPERATIONS
  pos <-- estimate(m1,m2,m3) =
    CASE m1 OF
            EITHER normal THEN
                  IF m2 = reverse or m3 = reverse THEN
                        pos:=void
                  ELSE
                        pos:=normal
                  END
            OR reverse THEN
                  IF m2 = normal or m3 = normal THEN
                        pos:=void
                  ELSE
                        pos:=reverse
                  END
            ELSE
        CASE m2 OF
                    EITHER normal THEN
                          IF m3 = reverse THEN
                                pos:=void
                          ELSE
                                pos:=normal
                          END
                    OR reverse THEN
                          IF m3 = normal THEN
                                pos:=void
                          ELSE
                                pos:=reverse
                          END
                    ELSE
                          pos:=m3
                    END
              END
        END
  END
END

The implementation is automatically demonstrated to comply with its specification.

Source code generated

The resulting C code, generated by ComenC from the implementation is given below:

switch.h

#ifndef _switch_h
#define _switch_h

#include <stdbool.h>

typedef enum {
   switch__normal,
   switch__reverse,
   switch__void
} switch__POSITION; 

extern void switch__INITIALISATION(void); 

extern void switch__estimate(
   switch__POSITION switch__m1,
   switch__POSITION switch__m2,
   switch__POSITION switch__m3,
   switch__POSITION *switch__pos);
#endif

switch.c

#include <stdbool.h>
#include "switch.h"

void switch__INITIALISATION(void) {
}

void switch__estimate(
    switch__POSITION switch__m1,
    switch__POSITION switch__m2,
    switch__POSITION switch__m3,
    switch__POSITION *switch__pos) {

    switch (switch__m1) {
        case switch__normal:
            if ((switch__m2 == switch__reverse) || (switch__m3 == switch__reverse)) {
               *switch__pos = switch__void;
            } else {
               *switch__pos = switch__normal;
            }
            break;
        case switch__reverse:
            if ((switch__m2 == switch__normal) || (switch__m3 == switch__normal)) {
               *switch__pos = switch__void;
            } else {
               *switch__pos = switch__reverse;
            }
            break;
        default:
            switch (switch__m2) {
                case switch__normal:
                    if (switch__m3 == switch__reverse) {
                       *switch__pos = switch__void;
                    } else {
                       *switch__pos = switch__normal;
                    }
                    break;
              case switch__reverse:
                   if (switch__m3 == switch__normal) {
                      *switch__pos = switch__void;
                   } else {
                      *switch__pos = switch__reverse;
                   }
                   break;
              default:
                  *switch__pos = switch__m3;
                  break;
      }
      break;
   }
}