In this lesson, we present how to implement automata in ReactiveML.
Lets start with a simple example: a light controller.
The controller has two states:
The user controls the light through the emission of two
This two states automaton can be implemented with two mutually
recursive processes. Transitions are triggered by the emission of
initial state is
let process light switch_on switch_off = let rec process state_on = do print_endline "Light on!"; halt until switch_off -> run state_off done and process state_off = do print_endline "Light off!"; halt until switch_on -> run state_on done in run state_off
When a state becomes active, the message
Light off! is displayed.
Let us declare the control signals.
signal switch_on, switch_off;;
Now we can run the controller,
#run light switch_on switch_off;;
and control the light via the emission of signals
Messages displayed on the standard output show the current state of the automaton.
Lets try a little more complex example: an alarm-clock.
The controller of an alarm-clock has three states:
idle: the alarm is deactivated
armed: the countdown is triggered
ring: the clock is ringing
The user controls the alarm-clock through the emission of three signals
ck_snooze. An additional signal
is emitted when the alarm starts ringing.
First, we need a process that waits either for one step or for a given
d expressed in a number of logical steps (for
the sake of simplicity).
let process sleep d = pause || for i = 1 to d do pause done
It is possible to link logical time and physical time, the interested reader can find details in the Reactive Asco example.
sleep process, we can define the ring tone, a
process that periodically prints
Bip Bip Bip Bip on the
let process bipbipbipbip = loop for i = 1 to 4 do print_string "Bip "; flush stdout; run sleep 10 done; run sleep 100; print_newline () end
Let us build the three state automata using three mutually recursive processes.
let process alarm ck_off ck_bip ck_snooze ck_arm = let rec process idle = do halt until | ck_arm (d) -> print_endline "Armed!"; run (armed d) done and process armed d = do run (sleep d); emit ck_bip; pause until | ck_off -> print_endline "Off!"; run idle | ck_bip -> run ring done and process ring = do run bipbipbipbip until | ck_snooze -> print_endline "Snooze!"; run (armed 200) | ck_off -> print_endline "Off!"; run idle done in run idle
Now, we define the clock control signals. Note that
ck_arm carries the value used to set up the
countdown of the alarm.
signal ck_off, ck_bip, ck_snooze;;
signal ck_arm default 0 gather (fun x y -> x);;
Then, we can run our alarm-clock.
#run alarm ck_off ck_bip ck_snooze ck_arm;;
We arm the alarm by emitting the value 500 on
wait until the alarm rings (about 5 seconds depending on the browser).
emit ck_arm 500;;
Then, one can snooze the clock. It should ring again in one or two seconds.
Finally, one can turn off the clock.
It is very easy to make our two automata communicate through a third one. For instance we can switch on the light when the alarm is ringing, and switch off the light when the alarm is snoozed or turned off.
The idea is to write a one state automaton that listens to the control signals of the alarm clock and emits control signals of the light.
let rec process light_clock = do halt until | ck_off -> emit switch_off; run light_clock | ck_bip -> emit switch_on; run light_clock | ck_snooze -> emit switch_off; run light_clock done
Now we can run the light clock,
and play with it!
emit ck_arm 500;;
You can vary the duration of the coundown by sending another value on
emit ck_arm 250;;
Again, messages displayed on the standard output show the state of the light controller and the alarm-clock.