GPIO matches StateMachine now

This commit is contained in:
Balthasar Reuter
2018-07-17 00:52:58 +02:00
parent 3ec094ff3d
commit 8ae53d701e
3 changed files with 234 additions and 90 deletions

View File

@@ -1,50 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Photobooth - a flexible photo booth software
# Copyright (C) 2018 Balthasar Reuter <photobooth at re - web dot eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gpiozero import LED, Button
class Gpio:
def __init__(self):
self._buttons = []
self._lamps = []
def setButton(self, bcm_pin, handler):
self._buttons.append(Button(bcm_pin))
self._buttons[-1].when_pressed = handler
def setLamp(self, bcm_pin):
self._lamps.append(LED(bcm_pin))
return len(self._lamps) - 1
def lampOn(self, index):
self._lamps[index].on()
def lampOff(self, index):
self._lamps[index].off()
def lampToggle(self, index):
self._lamps[index].toggle()

174
photobooth/gpio/__init__.py Normal file
View File

@@ -0,0 +1,174 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Photobooth - a flexible photo booth software
# Copyright (C) 2018 Balthasar Reuter <photobooth at re - web dot eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from .. import StateMachine
from ..Threading import Workers
class Gpio:
def __init__(self, config, comm):
super().__init__()
self._comm = comm
self._gpio = None
self._is_trigger = False
self._is_enabled = config.getBool('Gpio', 'enable')
self.initGpio(config)
def initGpio(self, config):
if self._is_enabled:
self._gpio = Entities()
lamp_pin = config.getInt('Gpio', 'lamp_pin')
trigger_pin = config.getInt('Gpio', 'trigger_pin')
exit_pin = config.getInt('Gpio', 'exit_pin')
logging.info(('GPIO enabled (lamp_pin=%d, trigger_pin=%d, '
'exit_pin=%d)'), lamp_pin, trigger_pin, exit_pin)
self._gpio.setButton(trigger_pin, self.trigger)
self._gpio.setButton(exit_pin, self.exit)
self._lamp = self._gpio.setLamp(lamp_pin)
else:
logging.info('GPIO disabled')
def run(self):
for state in self._comm.iter(Workers.GPIO):
self.handleState(state)
return True
def handleState(self, state):
if isinstance(state, StateMachine.IdleState):
self.showIdle()
elif isinstance(state, StateMachine.GreeterState):
self.showGreeter()
elif isinstance(state, StateMachine.CountdownState):
self.showCountdown()
elif isinstance(state, StateMachine.CaptureState):
self.showCapture()
elif isinstance(state, StateMachine.AssembleState):
self.showAssemble()
elif isinstance(state, StateMachine.ReviewState):
self.showReview()
elif isinstance(state, StateMachine.PostprocessState):
self.showPostprocess()
elif isinstance(state, StateMachine.TeardownState):
self.teardown(state)
def teardown(self, state):
pass
def enableTrigger(self):
if self._is_enabled:
self._is_trigger = True
self._gpio.lampOn(self._lamp)
def disableTrigger(self):
if self._is_enabled:
self._is_trigger = False
self._gpio.lampOff(self._lamp)
def trigger(self):
if self._is_trigger:
self.disableTrigger()
self._comm.send(Workers.MASTER, StateMachine.GpioEvent('trigger'))
def exit(self):
self._comm.send(
Workers.Master,
StateMachine.TeardownEvent(StateMachine.TeardownEvent.WELCOME))
def showIdle(self):
self.enableTrigger()
def showGreeter(self):
self.disableTrigger()
def showCountdown(self):
pass
def showCapture(self):
pass
def showAssemble(self):
pass
def showReview(self):
pass
def showPostprocess(self):
pass
class Entities:
def __init__(self):
super().__init__()
import gpiozero
self.LED = gpiozero.LED
self.Button = gpiozero.Button
self._buttons = []
self._lamps = []
def setButton(self, bcm_pin, handler):
self._buttons.append(self.Button(bcm_pin))
self._buttons[-1].when_pressed = handler
def setLamp(self, bcm_pin):
self._lamps.append(self.LED(bcm_pin))
return len(self._lamps) - 1
def lampOn(self, index):
self._lamps[index].on()
def lampOff(self, index):
self._lamps[index].off()
def lampToggle(self, index):
self._lamps[index].toggle()

View File

@@ -30,6 +30,7 @@ import sys
from . import camera, gui
from .Config import Config
from .gpio import Gpio
from .util import lookup_and_import
from .StateMachine import Context, ErrorEvent
from .Threading import Communicator, Workers
@@ -38,7 +39,7 @@ from .worker import Worker
class CameraProcess(mp.Process):
def __init__(self, config, comm):
def __init__(self, argv, config, comm):
super().__init__()
self.daemon = True
@@ -60,41 +61,61 @@ class CameraProcess(mp.Process):
self._comm.send(Workers.MASTER, ErrorEvent('Camera', str(e)))
class WorkerProcess(mp.Process):
def __init__(self, config, comm):
super().__init__()
self.daemon = True
self.cfg = config
self.comm = comm
def run(self):
while True:
try:
if Worker(self.cfg, self.comm).run():
break
except Exception as e:
self._comm.send(Workers.MASTER, ErrorEvent('Worker', str(e)))
class GuiProcess(mp.Process):
def __init__(self, argv, config, communicator):
super().__init__()
self.argv = argv
self.cfg = config
self.comm = communicator
self._argv = argv
self._cfg = config
self._comm = communicator
def run(self):
Gui = lookup_and_import(gui.modules, self.cfg.get('Gui', 'module'),
Gui = lookup_and_import(gui.modules, self._cfg.get('Gui', 'module'),
'gui')
sys.exit(Gui(self.argv, self.cfg, self.comm).run())
return Gui(self._argv, self._cfg, self._comm).run()
class WorkerProcess(mp.Process):
def __init__(self, argv, config, comm):
super().__init__()
self.daemon = True
self._cfg = config
self._comm = comm
def run(self):
while True:
try:
if Worker(self._cfg, self._comm).run():
break
except Exception as e:
self._comm.send(Workers.MASTER, ErrorEvent('Worker', str(e)))
class GpioProcess(mp.Process):
def __init__(self, argv, config, comm):
super().__init__()
self.daemon = True
self._cfg = config
self._comm = comm
def run(self):
while True:
try:
if Gpio(self._cfg, self._comm).run():
break
except Exception as e:
self.comm.send(Workers.MASTER, ErrorEvent('Gpio', str(e)))
def run(argv):
@@ -107,29 +128,28 @@ def run(argv):
comm = Communicator()
context = Context(comm)
# Initialize processes: We use four processes here:
# 1. Camera processing
# 2. Postprocessing
# Initialize processes: We use five processes here:
# 1. Master that collects events and distributes state changes
# 2. Camera handling
# 3. GUI
# 4. Master
camera_proc = CameraProcess(config, comm) # camera_conn, worker_queue)
camera_proc.start()
# 4. Postprocessing worker
# 5. GPIO handler
proc_classes = (CameraProcess, WorkerProcess, GuiProcess, GpioProcess)
procs = [P(argv, config, comm) for P in proc_classes]
worker_proc = WorkerProcess(config, comm)
worker_proc.start()
gui_proc = GuiProcess(argv, config, comm)
gui_proc.start()
for proc in procs:
proc.start()
# Enter main loop
for event in comm.iter(Workers.MASTER):
exit_code = context.handleEvent(event)
if exit_code in (0, 123):
break
# Wait for processes to finish
gui_proc.join()
worker_proc.join()
camera_proc.join()
for proc in procs:
proc.join()
logging.debug('All processes joined, returning code {}'. format(exit_code))
return exit_code