Skip to content

Commit

Permalink
Microwave controller example
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreDecan committed May 19, 2016
1 parent 4404490 commit 07a525a
Show file tree
Hide file tree
Showing 2 changed files with 320 additions and 0 deletions.
132 changes: 132 additions & 0 deletions docs/examples/microwave_controller.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
statechart:
name: Microwave controller
description: |
in:
input_power_inc
input_power_dec
input_power_reset
input_timer_inc
input_timer_dec
input_timer_reset
input_cooking_start
input_cooking_stop
timer_tick
item_placed
item_removed
door_opened
door_closed
out:
lamp_switch_on
lamp_switch_off
display_set(text:str)
display_clear
heating_set_power(power:int)
heating_on
heating_off
beep(number:int)
turntable_start
turntable_stop
preamble: |
POWER_VALUES = [600, 700, 800, 900, 1000, 1100, 1200]
POWER_DEFAULT = 3
root state:
name: controller
initial: door closed
on entry: |
power = 0
timer = 0
transitions:
- event: input_cooking_stop
action: |
power = POWER_DEFAULT
timer = 0
states:
- name: door closed
initial: closed without item
states:
- name: closed without item
transitions:
- event: door_opened
target: opened without item
- name: closed with item
on exit: send('display_clear')
transitions:
- event: door_opened
target: opened with item
- event: input_timer_inc
action: |
timer = timer + 1
send('display_set', text='TIMER: %d' % timer)
- event: input_timer_dec
action: |
timer = max(0, timer - 1)
send('display_set', text='TIMER: %d' % timer)
- event: input_timer_reset
action: |
timer = 0
send('display_set', text='TIMER: %d' % timer)
states:
- name: program mode
initial: not ready
transitions:
- event: input_power_reset
action: |
power = POWER_DEFAULT
send('display_set', text='POWER: %d' % POWER_VALUES[power])
- event: input_power_inc
action: |
power = min(len(POWER_VALUES) - 1, power + 1)
send('display_set', text='POWER: %d' % POWER_VALUES[power])
- event: input_power_dec
action: |
power = max(0, power - 1)
send('display_set', text='POWER: %d' % POWER_VALUES[power])
states:
- name: not ready
transitions:
- guard: timer > 0
target: ready
- name: ready
transitions:
- guard: timer == 0
target: not ready
- event: input_cooking_start
target: cooking mode
- name: cooking mode
on entry: |
send('heating_set_power', power=POWER_VALUES[power])
send('heating_on')
send('lamp_switch_on')
send('turntable_start')
on exit: |
send('heating_off')
send('lamp_switch_off')
send('turntable_stop')
transitions:
- guard: timer == 0
target: program mode
action: |
send('beep', number=3)
send('display_set', text='DONE')
- event: timer_tick
action: |
timer -= 1
send('display_set', text='REMAINING: %d' % timer)
- name: door opened
on entry: send('lamp_switch_on')
on exit: send('lamp_switch_off')
initial: opened without item
states:
- name: opened without item
transitions:
- event: door_closed
target: closed without item
- event: item_placed
target: opened with item
- name: opened with item
transitions:
- event: item_removed
target: opened without item
- event: door_closed
target: program mode
188 changes: 188 additions & 0 deletions docs/examples/microwave_controller_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import tkinter as tk

# The two following lines are NOT needed in a typical environment.
# These lines make sismic available in our testing environment
import sys
sys.path.append('../..')

from functools import partial

from sismic.io import import_from_yaml
from sismic.interpreter import Interpreter
from sismic.model import Event


####################################################


# Create a tiny GUI
class MicrowaveApplication(tk.Frame):
def __init__(self, master=None):
super().__init__(master)

# Initialize widgets
self.create_widgets()

# Create a Stopwatch interpreter
with open('microwave_controller.yaml') as f:
statechart = import_from_yaml(f)
self.interpreter = Interpreter(statechart)

# Bind interpreter events to the GUI
self.interpreter.bind(self.event_handler)

self.execute()

def execute(self):
self.interpreter.execute()

# Update the widget that contains the list of active states.
self.w_states['text'] = 'active states: \n\n' + '\n'.join(self.interpreter.configuration)
self.w_timer['text'] = 'timer: %d' % self.interpreter._evaluator.context_for('controller')['timer']
self.w_power['text'] = 'power: %d' % self.interpreter.context['POWER_VALUES'][self.interpreter._evaluator.context_for('controller')['power']]

def create_widgets(self):
self.pack(fill=tk.BOTH)

left_frame = tk.Frame(self)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH)

# Input frame
input_frame = tk.LabelFrame(left_frame, text='INPUT BUTTONS')
input_frame.pack(side=tk.LEFT, fill=tk.BOTH)

# Sensor frame
sensors_frame = tk.LabelFrame(left_frame, text='SENSORS')
sensors_frame.pack(side=tk.LEFT, fill=tk.BOTH)

# Add buttons
self.w_input_power_inc = tk.Button(input_frame, text='power +', command=partial(self.send_event, event_name='input_power_inc'))
self.w_input_power_dec = tk.Button(input_frame, text='power -', command=partial(self.send_event, event_name='input_power_dec'))
self.w_input_power_reset = tk.Button(input_frame, text='power 0', command=partial(self.send_event, event_name='input_power_reset'))

self.w_input_timer_inc = tk.Button(input_frame, text='timer +', command=partial(self.send_event, event_name='input_timer_inc'))
self.w_input_timer_dec = tk.Button(input_frame, text='timer -', command=partial(self.send_event, event_name='input_timer_dec'))
self.w_input_timer_reset = tk.Button(input_frame, text='timer 0', command=partial(self.send_event, event_name='input_timer_reset'))

self.w_input_cooking_start = tk.Button(input_frame, text='start', command=partial(self.send_event, event_name='input_cooking_start'))
self.w_input_cooking_stop = tk.Button(input_frame, text='stop', command=partial(self.send_event, event_name='input_cooking_stop'))

self.w_tick = tk.Button(sensors_frame, text='tick', command=partial(self.send_event, event_name='timer_tick'))

self.w_item_placed = tk.Button(sensors_frame, text='place item', command=partial(self.send_event, event_name='item_placed'))
self.w_item_removed = tk.Button(sensors_frame, text='remove item', command=partial(self.send_event, event_name='item_removed'))

self.w_door_opened = tk.Button(sensors_frame, text='open door', command=partial(self.send_event, event_name='door_opened'))
self.w_door_closed = tk.Button(sensors_frame, text='close door', command=partial(self.send_event, event_name='door_closed'))

# Pack
self.w_input_power_inc.pack(side=tk.TOP, fill=tk.X)
self.w_input_power_dec.pack(side=tk.TOP, fill=tk.X)
self.w_input_power_reset.pack(side=tk.TOP, fill=tk.X)

self.w_input_timer_inc.pack(side=tk.TOP, fill=tk.X)
self.w_input_timer_dec.pack(side=tk.TOP, fill=tk.X)
self.w_input_timer_reset.pack(side=tk.TOP, fill=tk.X)

self.w_input_cooking_start.pack(side=tk.TOP, fill=tk.X)
self.w_input_cooking_stop.pack(side=tk.TOP, fill=tk.X)

self.w_tick.pack(side=tk.TOP, fill=tk.X)

self.w_item_placed.pack(side=tk.TOP, fill=tk.X)
self.w_item_removed.pack(side=tk.TOP, fill=tk.X)

self.w_door_opened.pack(side=tk.TOP, fill=tk.X)
self.w_door_closed.pack(side=tk.TOP, fill=tk.X)


right_frame = tk.Frame(self)
right_frame.pack(side=tk.LEFT, fill=tk.BOTH)


# Display component
component_frame = tk.LabelFrame(right_frame, text='Display')
component_frame.pack(side=tk.TOP, fill=tk.BOTH)
self.w_display = tk.Label(component_frame)
self.w_display.pack(side=tk.TOP)

# Lamp component
lamp_frame = tk.LabelFrame(right_frame, text='Lamp')
lamp_frame.pack(side=tk.TOP, fill=tk.BOTH)
self.w_lamp = tk.Label(lamp_frame)
self.w_lamp.pack(side=tk.TOP)

# Heating component
heating_frame = tk.LabelFrame(right_frame, text='Heating')
heating_frame.pack(side=tk.TOP, fill=tk.BOTH)
self.w_heating_power = tk.Label(heating_frame)
self.w_heating_status = tk.Label(heating_frame)
self.w_heating_power.pack(side=tk.TOP)
self.w_heating_status.pack(side=tk.TOP)

# Beeper component
beep_frame = tk.LabelFrame(right_frame, text='Beeper')
beep_frame.pack(side=tk.TOP, fill=tk.BOTH)
self.w_beep = tk.Label(beep_frame)
self.w_beep.pack(side=tk.TOP)

# Turntable component
turntable_frame = tk.LabelFrame(right_frame, text='Turntable')
turntable_frame.pack(side=tk.TOP, fill=tk.BOTH)
self.w_turntable = tk.Label(turntable_frame)
self.w_turntable.pack(side=tk.TOP)

# Statechart status
statechart_frame = tk.LabelFrame(right_frame, text='Statechart')
statechart_frame.pack(side=tk.TOP, fill=tk.BOTH)

self.w_states = tk.Label(statechart_frame)
self.w_states.pack(side=tk.BOTTOM, fill=tk.X)

self.w_timer = tk.Label(statechart_frame)
self.w_timer.pack(side=tk.BOTTOM, fill=tk.X)

self.w_power = tk.Label(statechart_frame)
self.w_power.pack(side=tk.BOTTOM, fill=tk.X)

def event_handler(self, event):
name = event.name

if name == 'lamp_switch_on':
self.w_lamp['text'] = 'on'
elif name == 'lamp_switch_off':
self.w_lamp['text'] = 'off'
elif name == 'display_set':
self.w_display['text'] = event.text
elif name == 'display_clear':
self.w_display['text'] = '-'
elif name == 'heating_set_power':
self.w_heating_power['text'] = event.power
elif name == 'heating_on':
self.w_heating_status['text'] = 'on'
elif name == 'heating_off':
self.w_heating_status['text'] = 'off'
elif name == 'beep':
self.w_beep['text'] = event.number
elif name == 'turntable_start':
self.w_turntable['text'] = 'on'
elif name == 'turntable_stop':
self.w_turntable['text'] = 'off'
else:
raise ValueError('Unknown event %s' % event)

def send_event(self, event_name):
self.interpreter.queue(Event(event_name))
self.execute()

def _quit(self):
self.master.destroy()


if __name__ == '__main__':
# Create GUI
root = tk.Tk()
root.wm_title('Microwave')
app = MicrowaveApplication(master=root)

app.mainloop()

0 comments on commit 07a525a

Please sign in to comment.