* Datenmodell + Alembic-Initialmigration (Plan §4), Seed Saison 2026 * Wertungs-Engine (Plan §3.3), regelbasiert je Kategorie/Saison - Abnahmetest gegen die Ergebnisliste 2026-05-31 * Meisterschaftsberechnung mit Streichresultaten + Tie-Break (PLATZHALTER-Punktetabelle, O-1 offen) * Ingest-API (idempotent, dry_run) inkl. Excel-Leser und Fahrer-Matching/Merge * Zeitmessungs-Endpoint (Batch, idempotent, unzugeordnet/unplausibel) + SSE-Anzeige - Mess-Pi-Client bewusst NICHT enthalten (docs/zeitmessung-endpoint.md) * Frontend: öffentliche Seiten + internes Backend (Jinja2/HTMX, kein Build) * Auslieferung: Docker (multi-arch) + docker-compose sowie Debian-Paket/APT-Repo * 57 Tests grün gegen echte PostgreSQL-Testdatenbank Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.6 KiB
Endpoint für die Zeitmessung (Lichtschranke)
Der Mess-Pi-Client selbst ist nicht Teil dieses Projekts — dieses Dokument beschreibt nur den Endpoint, den ein beliebiger Client (der zweite Raspberry Pi an der Lichtschranke) bespielt. Fachlicher Hintergrund:
plan.md§3.6 / §6.2.
Authentifizierung
Alle Geräte-Endpunkte erwarten einen Geräte-Token (nicht den Auswerter-Login):
Authorization: Bearer <geraete-token>
Token anlegen (interne Admin-Session):
POST /api/v1/admin/geraete
{ "bezeichnung": "lichtschranke-ziel-01", "typ": "lichtschranke" }
→ 201 { "id": 1, "bezeichnung": "...", "token": "oamc_dev_…" } # Token nur hier einmalig
Token neu ausgeben: POST /api/v1/admin/geraete/{id}/token ·
Gerät sperren: POST /api/v1/admin/geraete/{id}/sperren
Ablauf auf dem Client (empfohlen)
POST /api/v1/geraete/heartbeatalle ~15 s → Statusanzeige +aktives_turnier_idGET /api/v1/geraete/konfiguration→ Entprellzeit, Messpunkte, PlausibilitätsgrenzenGET /api/v1/turniere/aktiv/startliste(ETag-fähig) → Startreihenfolge für die Anzeige- Pro Durchfahrt eine Messung lokal puffern, dann als Batch senden:
POST /api/v1/zeitmessungen GET /api/v1/turniere/aktiv/anzeige(SSE) → Live-Stream für die Anzeige am Parcours
POST /api/v1/zeitmessungen — Batch, idempotent
{
"geraet": "lichtschranke-ziel-01", // informativ
"messungen": [
{
"id": "9f2c1e7a-4d38-4b6e-9a11-5c0b2d7e8f30", // UUID VOM GERÄT → Idempotenz
"turnier_id": 42,
"startnummer_gemeldet": 32, // Hinweis, kein Fremdschlüssel
"messpunkt": "durchgang-1",
"dauer_sekunden": 159.16, // vom Gerät berechnet (monotone Zeit)
"gemessen_am": "2026-05-31T10:14:02.482+02:00",
"monotonic_ns": 884213771004,
"roh": { "trigger_start_ns": 884054611004, "trigger_ziel_ns": 884213771004 }
}
]
}
Antwort — jede Messung wird einzeln quittiert
{
"quittungen": [
{ "id": "9f2c1e7a-…", "status": "angenommen", "start_id": 512, "hinweis": null }
],
"angenommen": 1, "duplikate": 0, "unzugeordnet": 0, "unplausibel": 0
}
status |
Bedeutung | Client-Reaktion |
|---|---|---|
angenommen |
gespeichert und einem Start zugeordnet | aus lokaler Queue löschen |
duplikat |
UUID war schon da (Nachlieferung nach Timeout) | aus lokaler Queue löschen |
unzugeordnet |
gespeichert, aber startnummer_gemeldet nicht auflösbar → manuelle Zuordnung im Frontend |
aus lokaler Queue löschen |
unplausibel |
gespeichert, Dauer außerhalb der Grenzen → im Frontend markiert | aus lokaler Queue löschen |
fehler |
z. B. turnier_id unbekannt |
in der Queue behalten, später erneut |
Grundregeln (Server-seitig umgesetzt):
- Idempotent über
id— ein doppelt gesendeter Batch erzeugt keine Dubletten. Ein abgebrochener Request darf gefahrlos wiederholt werden. - Die Dauer kommt fertig vom Gerät — der Server rechnet keine Differenz aus zwei Wanduhr-Zeitstempeln (NTP-Sprung auf einem Pi ohne RTC).
- Eine Messung ohne zuordenbare Startnummer wird nie verworfen, sondern als
unzugeordnetgespeichert und im internen Frontend zur Zuordnung angeboten. - Plausibilitätsverstöße werden markiert, nicht abgelehnt. Grenzen:
OAMC_MESS_MIN_SEKUNDEN/OAMC_MESS_MAX_SEKUNDENbzw. je Gerät ingeraet.konfiguration. gemessen_amweit in der Vergangenheit → die Messung wird als nachgeliefert markiert und trotzdem angenommen.
Fehlmessung nachträglich verwerfen
POST /api/v1/zeitmessungen/{id}/verwerfen
{ "grund": "Zuschauer hat die Schranke ausgelöst" }
Die Messung bleibt mit Grund gespeichert (Status verworfen), zählt aber nicht in
die summe_zeit.
Was der Server aus den Messungen macht
ergebnis.summe_zeit wird bei der Auswertung als Summe aller gültigen,
zugeordneten Einzelmessungen eines Starts gebildet
(oamc.wertung.service.summe_zeit_fuer_start). Aus Einzelmessungen lässt sich die
Summe jederzeit bilden — der umgekehrte Weg existiert nicht, deshalb speichert der
Server jede Einzelmessung.
Offene Punkte (aus plan.md §10), die den Endpoint noch betreffen
- O-13 Was misst die Lichtschranke genau (eine Gesamtdurchfahrt, mehrere
Durchgänge, einzelne Abschnitte)? → bestimmt
messpunktund die Summenbildung. - O-14 Woher kennt der Client die Startnummer? → bis geklärt:
startnummer_gemeldetals Hinweis, Server löst gegen die Startliste auf. - O-16 Geforderte Messgenauigkeit / eine oder zwei Lichtschranken.