Commit d1f058e6 authored by Antoine Lima's avatar Antoine Lima

Merge branch 'dev' of gitlab.utc.fr:PR-Baby-A18/source-rasp into dev

parents a09cf6ff f82fda56
[codestyle]
indentation = True
[main]
version = 0.1.0
[encoding]
text_encoding = utf-8
[main]
version = 0.1.0
[vcs]
use_version_control = False
version_control_system =
[main]
version = 0.1.0
[workspace]
restore_data_on_startup = True
save_data_on_exit = True
save_history = True
save_non_project_files = False
[main]
version = 0.1.0
recent_files = ['/home/antoine/prog/PR/source-rasp/main.py', '/home/antoine/prog/PR/source-rasp/modules/game.py', '/home/antoine/prog/PR/content/settings.json', '/home/antoine/prog/PR/source-rasp/replay.py', '/home/antoine/prog/PR/source-rasp/com.py']
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne
"""
import logging
import autopy
import serial
from os.path import dirname, abspath, join, isfile as exists
from autopy.key import tap as PressKey, Code as KeyCode
from threading import Thread
from player import Side
class InputThread(Thread):
keyButtonBindings = [KeyCode.ESCAPE, KeyCode.UP_ARROW, KeyCode.LEFT_ARROW, KeyCode.RIGHT_ARROW, KeyCode.DOWN_ARROW, KeyCode.RETURN]
def __init__(self, dispatcher, side):
Thread.__init__(self)
self.side = side
self.dispatcher = dispatcher
self.continueRunning = True
self.path = '/dev/ttyUSB0' if self.side==Side.Left else '/dev/ttyUSB1'
if exists(self.path):
self.arduino = serial.Serial(self.path, 9600, timeout=1)
else:
raise RuntimeError('No arduino connected on the {} side'.format(self.side.name.lower()))
def run(self):
while self.arduino.isOpen():
msg = self.arduino.readline()[:-1]
if msg:
parsedMessage = self.parseMsg(msg)
if 'butn' in parsedMessage:
self.sendKeyStroke(parsedMessage)
else:
self.dispatcher.dispatchMessage(parsedMessage)
else:
logging.warn('No message read on Arduino {}'.format(self.side.name))
def stop(self):
self.continueRunning = False
self.arduino.close()
def sendKeyStroke(self, msg):
if 'butn' in msg:
button = int(msg['butn'])
print({button: ['ESCAPE', 'UP_ARROW', 'LEFT_ARROW', 'RIGHT_ARROW', 'DOWN_ARROW', 'RETURN'][button]})
key = InputThread.keyButtonBindings[button]
PressKey(key, [])
def parseMsg(self, msg):
parts = msg.split(':')
return {parts[0]: parts[1], 'source': self.side}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne
"""
import logging
import pyautogui # PyPi library
from threading import Thread
import RPi.GPIO as GPIO
from player import Side
class GPIOThread(Thread):
_keyButtonBindings = {
26: 'up',
22: 'left',
27: 'right',
23: 'down',
17: 'return',
18: 'escape'
}
def __init__(self, dispatcher):
Thread.__init__(self)
self.dispatcher = dispatcher
self.continueRunning = True
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
for pin in GPIOThread._keyButtonBindings.keys():
print(pin)
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(pin, GPIO.RISING, callback=self.handleButtonPress)
def run(self):
try:
while self.continueRunning:
pass
finally:
GPIOThread.clean()
def handleButtonPress(self, button_pin):
if button_pin not in GPIOThread._keyButtonBindings.keys():
logging.warn('Unknown button pin: {}'.format(button_pin))
else:
key = GPIOThread._keyButtonBindings[button_pin]
logging.debug('Sending {} as {}'.format(button_pin, key))
pyautogui.press(key)
def stop(self):
self.continueRunning = False
@staticmethod
def clean():
GPIO.cleanup()
......@@ -18,7 +18,7 @@ from PyQt5.QtCore import QTime, Qt
from ui.main_ui import Ui_MainWindow
from modules import *
from player import Side
from com import InputThread
from input import GPIOThread
class MainWin(QtWidgets.QMainWindow):
def __init__(self, parent=None):
......@@ -66,8 +66,12 @@ class MainWin(QtWidgets.QMainWindow):
mod_idx = [i for i, x in enumerate(self.modules) if isinstance(x, type)]
return -1 if len(mod_idx)==0 else mod_idx[0]
def dispatchMessage(self, msg, toAll=False):
modulesIdx = self.modules if toAll else [self.findMod(type(self.ui.panels.currentWidget()))]
def dispatchMessage(self, msg, toType=None, toAll=False):
if toType!=None:
modulesIdx = [self.findMod(toType)]
else:
modulesIdx = self.modules if toAll else [self.findMod(type(self.ui.panels.currentWidget()))]
for modIdx in modulesIdx:
self.modules[modIdx].other(**msg)
......@@ -88,24 +92,33 @@ class MainWin(QtWidgets.QMainWindow):
if __name__=='__main__':
from settings import Settings
from replay import Replay as ReplayThread
#logging.basicConfig(filename='babyfoot.log', level=logging.DEBUG)
logging.basicConfig(level=logging.DEBUG)
app = QtWidgets.QApplication(sys.argv)
myapp = MainWin()
if Settings['app.mode']!='dev':
threadArduinoLeft = InputThread(myapp, Side.Left)
#threadArduinoRight = InputThread(myapp, Side.Right)
threadArduinoLeft.start()
#threadArduinoRight.start()
myapp.show()
app.exec_()
if Settings['app.mode']!='dev':
threadArduinoLeft.stop()
#threadArduinoRight.stop()
threadArduinoLeft.join()
#threadArduinoRight.join()
try:
#logging.basicConfig(filename='babyfoot.log', level=logging.DEBUG)
logging.basicConfig(level=logging.DEBUG)
app = QtWidgets.QApplication(sys.argv)
myapp = MainWin()
if ReplayThread.isCamAvailable():
threadReplay = ReplayThread(Side.Left)
threadReplay.start()
myapp.dispatchMessage({'replayThread': threadReplay}, toType=GameModule)
threadGPIO = GPIOThread(myapp)
threadGPIO.start()
myapp.show()
app.exec_()
threadGPIO.stop()
if ReplayThread.isCamAvailable():
threadReplay.stop()
threadReplay.join()
threadGPIO.join()
finally:
GPIOThread.clean()
......@@ -18,6 +18,7 @@ from PyQt5.QtMultimediaWidgets import QVideoWidget
from player import Side, PlayerGuest
from replay import Replay
from module import Module
from settings import Settings
import modules
from ui.game_ui import Ui_Form as GameWidget
......@@ -25,25 +26,25 @@ class GameOverChecker():
def __init__(self, conditionType, limit):
self.conditionType = conditionType
self.limit = limit
def check(self, time, scores):
'''
Checks if a game is over and return the winner if that's the case
Returns the winning side or Side.Undef otherwise
Takes the game time is seconds and a list containing the two scores
'''
# Gets the index of the highest scoring player
# Gets the index of the highest scoring player
bestPlayer = max(scores, key=scores.get)
if self.conditionType=='score' and scores[bestPlayer]>=self.limit:
return bestPlayer
elif self.conditionType=='time' and time>self.limit:
return bestPlayer
else:
return Side.Undef
class GameModule(Module):
def __init__(self, parent=None):
super().__init__(parent, GameWidget())
......@@ -56,18 +57,20 @@ class GameModule(Module):
self.ui.btnScore1.clicked.connect(lambda: self.goal(Side.Left))
self.ui.btnScore2.clicked.connect(lambda: self.goal(Side.Right))
self.replayer = Replay()
self.replayer = None
def load(self):
logging.debug('Loading GameModule')
self.gameStartTime = QTime.currentTime()
self.timerUpdateChrono.start(1000)
self.ui.lcdChrono.display(QTime(0,0).toString("hh:mm:ss"))
self.showingReplay = False
if self.replayer:
self.replayer.start_recording()
self.gameoverChecker = GameOverChecker('score', 10)
if all([len(val)==0 for val in self.players.values()]):
self.players[Side.Left ].append(PlayerGuest)
self.players[Side.Right].append(PlayerGuest)
......@@ -79,17 +82,23 @@ class GameModule(Module):
logging.debug('Unloading GameModule')
del self.gameStartTime
self.timerUpdateChrono.stop()
if self.replayer:
self.replayer.stop_recording()
def other(self, **kwargs):
logging.debug('Other GameModule')
for key, val in kwargs.items():
if key=='goal' and 'source' in kwargs:
self.goal(kwargs['source'])
elif key=='players':
self.players = val
elif key=='replayThread':
self.replayer = val
def resizeEvent(self, event):
# 40% of the window width to have (5% margin)-(40% circle)-(10% middle)-(40% circle)-(5% margin)
btnDiameter = self.mainwin.width()*0.4
......@@ -98,7 +107,7 @@ class GameModule(Module):
self.ui.btnScore2.setMinimumSize(btnDiameter, btnDiameter)
self.ui.btnScore1.setMask(region)
self.ui.btnScore2.setMask(region)
QtWidgets.QWidget.resizeEvent(self, event)
def keyPressEvent(self, e):
......@@ -112,54 +121,58 @@ class GameModule(Module):
def updateChrono(self):
# Updated each second
self.ui.lcdChrono.display(QTime(0,0).addSecs(self.getGameTime()).toString("hh:mm:ss"))
# Don't check scores while showing a replay to avoid closing the engame screen too soon
# Don't check scores while showing a replay to avoid closing the engame screen too soon
if not self.showingReplay:
self.checkEndGame()
def getGameTime(self):
return self.gameStartTime.secsTo(QTime.currentTime())
def updateScores(self):
self.ui.btnScore1.setText(str(self.scores[Side.Left]))
self.ui.btnScore2.setText(str(self.scores[Side.Right]))
self.checkEndGame()
def goal(self, side):
if side not in Side:
logging.error('Wrong goal side: {}'.format(side))
else:
self.scores[side] += 1
# Show replay
# May require `sudo apt-get install qtmultimedia5-examples` in order to install the right libraries
replayFile = self.mainwin.getContent('Replay {}.mp4'.format(side.name))
if os.path.exists(replayFile):
if True: # Debug Mode
self.updateScores()
else:
self.showingReplay = True
self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.player.stateChanged.connect(self.endOfReplay)
self.player.setMuted(True)
self.player.setVideoOutput(self.ui.videoWidget)
self.player.setMedia(QMediaContent(QUrl.fromLocalFile(replayFile)))
self.player.play()
self.ui.videoWidget.setFullScreen(True)
if self.replayer:
replayFile = self.replayer.stop_recording()
if not (self.replayer and Settings['replay.show'] and os.path.exists(replayFile)):
self.updateScores()
else:
self.showingReplay = True
self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.player.stateChanged.connect(self.endOfReplay)
self.player.setMuted(True)
self.player.setVideoOutput(self.ui.videoWidget)
self.player.setMedia(QMediaContent(QUrl.fromLocalFile(replayFile)))
self.player.play()
self.ui.videoWidget.setFullScreen(True)
def endOfReplay(self, status):
if status!=QMediaPlayer.PlayingState:
self.ui.videoWidget.setFullScreen(False);
self.showingReplay = False
self.updateScores()
if self.replayer:
self.replayer.start_recording()
def handleCancel(self):
self.switchModule(modules.MenuModule)
def checkEndGame(self):
winSide = self.gameoverChecker.check(self.getGameTime(), self.scores)
if winSide!=Side.Undef:
self.send(modules.EndGameModule, players=self.players, winSide=winSide, scores=self.scores, time=self.getGameTime())
self.switchModule(modules.EndGameModule)
......@@ -12,8 +12,9 @@ from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from module import Module
import modules
from module import Module
from settings import Settings
from ui.menu_ui import Ui_Form as MenuWidget
from player import Side
......@@ -42,7 +43,7 @@ class MenuModule(Module):
self.ui.btnStartQuick.animateClick()
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
if e.key() == Qt.Key_Escape and Settings['app.mode']=='dev':
self.handleExit()
elif e.key() == Qt.Key_Up:
......
......@@ -7,6 +7,9 @@ Created on Wed Apr 18 18:34:40 2018
"""
import os
from threading import Thread, Event
from main import MainWin
from settings import Settings
onRasp = os.uname()[1] == 'raspberrypi'
......@@ -14,33 +17,63 @@ onRasp = os.uname()[1] == 'raspberrypi'
if onRasp:
import picamera
class Replay(Thread):
def __init__(self, side):
Thread.__init__(self)
self.replayPath = MainWin.getContent('Replay {}.mp4'.format(side.name))
self.shutdown = False
self.start_flag = Event()
self.stop_flag = Event()
self.stopped_flag = Event()
class Replay():
def __init__(self):
if onRasp:
self.cam = picamera.PiCamera()
self.cam.resolution = Settings['picam.resolution']
self.cam.framerate = Settings['picam.fps']
self.cam.hflip = Settings['picam.hflip']
self.cam.vflip = Settings['picam.vflip']
self.format = Settings['picam.format']
self.continue_recording = False
self.stream = picamera.PiCameraCircularIO(self.cam, seconds=Settings['replay.duration'])
def capture(self, fileToSave):
def start_recording(self):
if onRasp:
self.start_flag.set()
def stop_recording(self):
if onRasp:
self.cam.start_recording(self.stream, self.format)
self.continue_recording = True
self.stop_flag.set()
self.stopped_flag.wait()
try:
while self.continue_recording:
self.cam.wait_recording(1)
finally:
self.cam.stop_recording()
self.stop_flag.clear()
self.start_flag.clear()
self.stopped_flag.clear()
return self.replayPath
def stop(self):
self.start_flag.set()
self.shutdown = True
def run(self):
while not self.shutdown:
self.start_flag.wait()
if not self.shutdown:
self.cam.start_recording(self.stream, Settings['picam.format'])
try:
while not self.stop_flag.is_set():
self.cam.wait_recording(1)
self.stream.copy_to(fileToSave)
self.cam.close()
self.stream.close()
finally :
self.cam.stop_recording()
self.stream.copy_to(self.replayPath)
self.stream.clear()
self.stopped_flag.set()
def stop():
self.continue_recording = False
self.cam.close()
self.stream.close()
@staticmethod
def isCamAvailable():
return onRasp # and other checks (ToDo)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment