init
This commit is contained in:
commit
ce1095bb24
27 changed files with 621 additions and 0 deletions
10
app/init.py
Normal file
10
app/init.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from flask import Flask
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
app.secret_key = "rennstopuhr2025"
|
||||
|
||||
from app.routes import main as main_blueprint
|
||||
app.register_blueprint(main_blueprint)
|
||||
|
||||
return app
|
||||
99
app/routes.py
Normal file
99
app/routes.py
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
||||
import json
|
||||
import os
|
||||
from app.utils import gpio_handler, countdown, zeitmessung, system_tools
|
||||
|
||||
main = Blueprint("main", __name__)
|
||||
|
||||
@main.route("/")
|
||||
def index():
|
||||
return render_template("index.html")
|
||||
|
||||
@main.route("/rennen_starten")
|
||||
def rennen_starten():
|
||||
with open("config/fahrer.json") as f:
|
||||
fahrer = json.load(f)
|
||||
return render_template("rennen_starten.html", fahrer=fahrer)
|
||||
|
||||
@main.route("/countdown_starten", methods=["POST"])
|
||||
def countdown_starten():
|
||||
countdown.start()
|
||||
return redirect(url_for("main.rennansicht"))
|
||||
|
||||
@main.route("/rennansicht")
|
||||
def rennansicht():
|
||||
with open("config/fahrer.json") as f:
|
||||
fahrer = json.load(f)
|
||||
return render_template("rennbildschirm.html", fahrer=fahrer)
|
||||
|
||||
@main.route("/fahrerverwaltung")
|
||||
def fahrerverwaltung():
|
||||
with open("config/fahrer.json") as f:
|
||||
fahrer = json.load(f)
|
||||
return render_template("fahrerverwaltung.html", fahrer=fahrer)
|
||||
|
||||
@main.route("/fahrerverwaltung/speichern", methods=["POST"])
|
||||
def fahrer_speichern():
|
||||
data = request.get_json()
|
||||
with open("config/fahrer.json", "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
return jsonify({"status": "ok"})
|
||||
|
||||
@main.route("/admin")
|
||||
def admin():
|
||||
return render_template("admin.html")
|
||||
|
||||
@main.route("/zeit_zuordnen")
|
||||
def zeit_zuordnen():
|
||||
with open("data/zeiten.json") as f:
|
||||
zeiten = json.load(f)
|
||||
with open("config/fahrer.json") as f:
|
||||
fahrer = json.load(f)
|
||||
return render_template("zeit_zuordnen.html", zeiten=zeiten, fahrer=fahrer)
|
||||
|
||||
@main.route("/zuordnung/speichern", methods=["POST"])
|
||||
def zuordnung_speichern():
|
||||
data = request.get_json()
|
||||
with open("data/zuordnung.json", "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
return jsonify({"status": "ok"})
|
||||
|
||||
@main.route("/ergebnis")
|
||||
def ergebnis():
|
||||
zuordnung_path = "data/zuordnung.json"
|
||||
fahrer_path = "config/fahrer.json"
|
||||
|
||||
with open(fahrer_path) as f:
|
||||
fahrer_list = json.load(f)
|
||||
|
||||
if os.path.exists(zuordnung_path):
|
||||
with open(zuordnung_path) as f:
|
||||
zuordnungen = json.load(f)
|
||||
else:
|
||||
zuordnungen = []
|
||||
|
||||
fahrer_dict = {str(f["nummer"]): {"name": f["name"], "zeiten": [], "farbe": f["farbe"]} for f in fahrer_list}
|
||||
|
||||
for eintrag in zuordnungen:
|
||||
fahrer_id = str(eintrag["fahrer"])
|
||||
zeit = float(eintrag["zeit"])
|
||||
if fahrer_id in fahrer_dict:
|
||||
fahrer_dict[fahrer_id]["zeiten"].append(zeit)
|
||||
|
||||
ergebnisse = []
|
||||
for nummer, daten in fahrer_dict.items():
|
||||
zeiten = daten["zeiten"]
|
||||
if zeiten:
|
||||
beste = min(zeiten)
|
||||
durchschnitt = sum(zeiten) / len(zeiten)
|
||||
gesamt = sum(zeiten)
|
||||
ergebnisse.append({
|
||||
"nummer": nummer,
|
||||
"name": daten["name"],
|
||||
"farbe": daten["farbe"],
|
||||
"beste": f"{beste:.3f}",
|
||||
"durchschnitt": f"{durchschnitt:.3f}",
|
||||
"gesamt": f"{gesamt:.3f}"
|
||||
})
|
||||
|
||||
return render_template("ergebnis.html", ergebnisse=ergebnisse)
|
||||
102
app/static/css/styles.css
Normal file
102
app/static/css/styles.css
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f7f7f7;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
input, select, button {
|
||||
padding: 8px;
|
||||
margin: 5px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.green {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button.red {
|
||||
background-color: #e53935;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button.orange {
|
||||
background-color: orange;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.fahrer-eintrag {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.licht {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: grey;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.licht.rot.aktiv {
|
||||
background-color: red;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.licht.gruen {
|
||||
background-color: green !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(10, 10, 10, 0.9);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
color: white;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.ampel-klein {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 10px auto;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.ergebnis {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.fahrerbox {
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
flex: 1 1 200px;
|
||||
}
|
||||
30
app/static/js/admin.js
Normal file
30
app/static/js/admin.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
function systemCommand(cmd) {
|
||||
fetch(`/admin/${cmd}`, { method: "POST" })
|
||||
.then(res => res.text())
|
||||
.then(alert);
|
||||
}
|
||||
|
||||
function fetchLogs() {
|
||||
fetch("/admin/logs")
|
||||
.then(res => res.text())
|
||||
.then(data => {
|
||||
document.getElementById("logs").innerText = data;
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("adminForm").addEventListener("submit", function(e) {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const json = {};
|
||||
for (let [k, v] of data.entries()) {
|
||||
json[k] = v;
|
||||
}
|
||||
json["ziel_aktiviert"] = data.get("ziel_aktiviert") === "on";
|
||||
|
||||
fetch("/admin/save", {
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(json)
|
||||
}).then(res => res.text())
|
||||
.then(alert);
|
||||
});
|
||||
17
app/static/js/fahrer.js
Normal file
17
app/static/js/fahrer.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
function addFahrer() {
|
||||
const fahrerListe = document.getElementById("fahrerListe");
|
||||
const index = fahrerListe.children.length + 1;
|
||||
const farben = ["#ff0000", "#00ff00", "#0000ff", "#ffa500"];
|
||||
const neueFarbe = farben[index % farben.length];
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "fahrer-eintrag";
|
||||
div.innerHTML = `
|
||||
<label>Fahrer ${index}:
|
||||
<input type="text" name="name" value="Fahrer ${index}">
|
||||
<input type="number" name="nummer" value="${10 + index}">
|
||||
<input type="color" name="farbe" value="${neueFarbe}">
|
||||
</label>
|
||||
`;
|
||||
fahrerListe.appendChild(div);
|
||||
}
|
||||
31
app/static/js/main.js
Normal file
31
app/static/js/main.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
function startCountdown() {
|
||||
const beep = new Audio("/static/sounds/beep.wav");
|
||||
const go = new Audio("/static/sounds/go.wav");
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementById("licht1").classList.add("aktiv");
|
||||
beep.play();
|
||||
}, 1000);
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementById("licht2").classList.add("aktiv");
|
||||
beep.play();
|
||||
}, 2000);
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementById("licht3").classList.add("aktiv");
|
||||
beep.play();
|
||||
}, 3000);
|
||||
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll(".licht").forEach(el => {
|
||||
el.classList.remove("rot");
|
||||
el.classList.add("gruen");
|
||||
});
|
||||
go.play();
|
||||
}, 4000);
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "/rennen";
|
||||
}, 7000);
|
||||
}
|
||||
20
app/static/js/zuordnen.js
Normal file
20
app/static/js/zuordnen.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
document.getElementById("zuordnungForm").addEventListener("submit", function(e) {
|
||||
e.preventDefault();
|
||||
const zuordnungen = [];
|
||||
|
||||
document.querySelectorAll(".zuordnung").forEach(row => {
|
||||
const zeit = row.querySelector("span").innerText;
|
||||
const fahrer = row.querySelector("select").value;
|
||||
if (fahrer) {
|
||||
zuordnungen.push({ zeit: zeit, fahrer: fahrer });
|
||||
}
|
||||
});
|
||||
|
||||
fetch("/zeit_zuordnen", {
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ zuordnungen: zuordnungen })
|
||||
}).then(() => {
|
||||
window.location.href = "/ergebnis";
|
||||
});
|
||||
});
|
||||
BIN
app/static/sounds/beep.wav
Normal file
BIN
app/static/sounds/beep.wav
Normal file
Binary file not shown.
BIN
app/static/sounds/go.wav
Normal file
BIN
app/static/sounds/go.wav
Normal file
Binary file not shown.
37
app/templates/admin.html
Normal file
37
app/templates/admin.html
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Admin</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/admin.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Admin-Einstellungen</h2>
|
||||
<form id="adminForm">
|
||||
<label>
|
||||
Startlichtschranke GPIO:
|
||||
<input type="number" name="start_pin" value="17">
|
||||
</label><br>
|
||||
<label>
|
||||
Zielllichtschranke GPIO:
|
||||
<input type="number" name="ziel_pin" value="27">
|
||||
</label><br>
|
||||
<label>
|
||||
Schutzzeit (Sekunden):
|
||||
<input type="number" name="schutzzeit" value="3">
|
||||
</label><br>
|
||||
<label>
|
||||
Ziellichtschranke aktiv:
|
||||
<input type="checkbox" name="ziel_aktiviert" checked>
|
||||
</label><br><br>
|
||||
<button type="submit" class="green">Speichern</button>
|
||||
</form>
|
||||
<hr>
|
||||
<h3>Systemsteuerung</h3>
|
||||
<button onclick="systemCommand('restart')">Dienst neustarten</button>
|
||||
<button onclick="systemCommand('reboot')">Neu starten</button>
|
||||
<button onclick="systemCommand('shutdown')">Herunterfahren</button>
|
||||
<button onclick="fetchLogs()">Live-Logs anzeigen</button>
|
||||
<pre id="logs"></pre>
|
||||
</body>
|
||||
</html>
|
||||
18
app/templates/countdown.html
Normal file
18
app/templates/countdown.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Countdown</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
</head>
|
||||
<body class="countdown">
|
||||
<div class="ampel-overlay">
|
||||
<div class="licht rot" id="licht1"></div>
|
||||
<div class="licht rot" id="licht2"></div>
|
||||
<div class="licht rot" id="licht3"></div>
|
||||
</div>
|
||||
<script>
|
||||
startCountdown();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
20
app/templates/ergebnis.html
Normal file
20
app/templates/ergebnis.html
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Ergebnis</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<h1>🏁 Ergebnis</h1>
|
||||
<div class="ergebnis">
|
||||
{% for e in ergebnisse %}
|
||||
<div class="fahrerbox" style="background-color: {{ e.farbe }}">
|
||||
<h3>#{{ e.nummer }} - {{ e.name }}</h3>
|
||||
<p>Beste Zeit: {{ e.beste }} s</p>
|
||||
<p>Durchschnitt: {{ e.durchschnitt }} s</p>
|
||||
<p>Gesamtzeit: {{ e.gesamt }} s</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
27
app/templates/fahrerverwaltung.html
Normal file
27
app/templates/fahrerverwaltung.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Fahrerverwaltung</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/fahrer.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Fahrer verwalten</h2>
|
||||
<form id="fahrerForm">
|
||||
<div id="fahrerListe">
|
||||
{% for f in fahrer %}
|
||||
<div class="fahrer-eintrag">
|
||||
<label>Fahrer {{ loop.index }}:
|
||||
<input type="text" name="name" value="{{ f.name }}">
|
||||
<input type="number" name="nummer" value="{{ f.nummer }}">
|
||||
<input type="color" name="farbe" value="{{ f.farbe }}">
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" onclick="addFahrer()">+ Fahrer hinzufügen</button>
|
||||
<button type="submit" class="green">Speichern</button>
|
||||
</form>
|
||||
<a href="{{ url_for('main.index') }}">Zurück</a>
|
||||
</body>
|
||||
</html>
|
||||
15
app/templates/index.html
Normal file
15
app/templates/index.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Rennstoppuhr</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Rennstoppuhr</h1>
|
||||
<div class="menu">
|
||||
<a href="{{ url_for('main.rennen_starten') }}">🏁 Rennen starten</a>
|
||||
<a href="{{ url_for('main.fahrerverwaltung') }}">👤 Fahrerverwaltung</a>
|
||||
<a href="{{ url_for('main.admin') }}">⚙️ Einstellungen (Admin)</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
29
app/templates/rennbildschirm.html
Normal file
29
app/templates/rennbildschirm.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Rennbildschirm</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="kleine-ampel"></div>
|
||||
<div class="zeitinfo">
|
||||
<h3>Aktuelle Runde: <span id="aktuell">--.--</span></h3>
|
||||
<p>Beste Runde: <span id="beste">--.--</span></p>
|
||||
<p>Durchschnitt: <span id="schnitt">--.--</span></p>
|
||||
</div>
|
||||
<div class="rundenstand">
|
||||
Runde <span id="runde">1</span> / <span id="gesamt">3</span>
|
||||
</div>
|
||||
<div class="fahrer">
|
||||
{% for f in fahrer %}
|
||||
<div class="fahrerbox" style="background-color: {{ f.farbe }}">
|
||||
#{{ f.nummer }} - {{ f.name }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="aktionen">
|
||||
<button class="gelb">Safety</button>
|
||||
<button class="rot">Unterbrechen</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
17
app/templates/rennen_starten.html
Normal file
17
app/templates/rennen_starten.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Rennen starten</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<h2>Rennen starten</h2>
|
||||
<form method="POST" action="{{ url_for('main.countdown_starten') }}">
|
||||
<label>Anzahl Runden:
|
||||
<input type="number" name="runden" min="1" value="1">
|
||||
</label>
|
||||
<button type="submit" class="green">Countdown starten</button>
|
||||
</form>
|
||||
<p><a href="{{ url_for('main.fahrerverwaltung') }}">Zur Fahrerverwaltung</a></p>
|
||||
</body>
|
||||
</html>
|
||||
25
app/templates/zeit_zuordnen.html
Normal file
25
app/templates/zeit_zuordnen.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Zeit zuordnen</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/zuordnen.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Zeit zuordnen</h2>
|
||||
<form id="zuordnungForm">
|
||||
{% for z in zeiten %}
|
||||
<div class="zuordnung">
|
||||
<span>{{ z.zeit }}</span>
|
||||
<select name="fahrer">
|
||||
<option value="">-- Fahrer wählen --</option>
|
||||
{% for f in fahrer %}
|
||||
<option value="{{ f.nummer }}">#{{ f.nummer }} - {{ f.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<button type="submit" class="green">Speichern</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
17
app/utils/countdown.py
Normal file
17
app/utils/countdown.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import time
|
||||
import threading
|
||||
import pygame
|
||||
|
||||
def play_sound(path):
|
||||
pygame.mixer.init()
|
||||
sound = pygame.mixer.Sound(path)
|
||||
sound.play()
|
||||
time.sleep(sound.get_length())
|
||||
|
||||
def start():
|
||||
def countdown_thread():
|
||||
for i in range(3):
|
||||
play_sound("app/static/sounds/beep.wav")
|
||||
time.sleep(1)
|
||||
play_sound("app/static/sounds/go.wav")
|
||||
threading.Thread(target=countdown_thread).start()
|
||||
21
app/utils/gpio_handler.py
Normal file
21
app/utils/gpio_handler.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from gpiozero import Button
|
||||
from signal import pause
|
||||
import time
|
||||
|
||||
class Lichtschranke:
|
||||
def __init__(self, gpio_pin, callback, schutzzeit=3):
|
||||
self.pin = gpio_pin
|
||||
self.callback = callback
|
||||
self.last_trigger = 0
|
||||
self.button = Button(gpio_pin, pull_up=False, bounce_time=0.05)
|
||||
self.button.when_pressed = self._ausgeloest
|
||||
self.schutzzeit = schutzzeit
|
||||
|
||||
def _ausgeloest(self):
|
||||
jetzt = time.time()
|
||||
if jetzt - self.last_trigger > self.schutzzeit:
|
||||
self.last_trigger = jetzt
|
||||
self.callback()
|
||||
|
||||
def stop(self):
|
||||
self.button.close()
|
||||
1
app/utils/init.py
Normal file
1
app/utils/init.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# leer
|
||||
14
app/utils/system_tools.py
Normal file
14
app/utils/system_tools.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import subprocess
|
||||
|
||||
def reboot():
|
||||
subprocess.run(["sudo", "reboot"])
|
||||
|
||||
def shutdown():
|
||||
subprocess.run(["sudo", "shutdown", "now"])
|
||||
|
||||
def restart_service():
|
||||
subprocess.run(["sudo", "systemctl", "restart", "rennstopuhr.service"])
|
||||
|
||||
def get_logs():
|
||||
result = subprocess.run(["journalctl", "-u", "rennstopuhr.service", "--no-pager"], capture_output=True, text=True)
|
||||
return result.stdout
|
||||
22
app/utils/zeitmessung.py
Normal file
22
app/utils/zeitmessung.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import time
|
||||
|
||||
class Zeitmesser:
|
||||
def __init__(self):
|
||||
self.startzeit = None
|
||||
self.runden = []
|
||||
|
||||
def start(self):
|
||||
self.startzeit = time.perf_counter()
|
||||
|
||||
def runde(self):
|
||||
if self.startzeit is None:
|
||||
return None
|
||||
jetzt = time.perf_counter()
|
||||
rundenzeit = jetzt - self.startzeit
|
||||
self.runden.append(rundenzeit)
|
||||
self.startzeit = jetzt
|
||||
return rundenzeit
|
||||
|
||||
def reset(self):
|
||||
self.startzeit = None
|
||||
self.runden = []
|
||||
Reference in a new issue