Files
OAMC-Auswertung/docs/zeitmessung-endpoint.md
T
linusandClaude Sonnet 5 03f6fdb0d9 Erstumsetzung: OAMC Turnierauswertung (AP 0–7, 9)
* 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>
2026-09-05 18:49:00 +02:00

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)

  1. POST /api/v1/geraete/heartbeat alle ~15 s → Statusanzeige + aktives_turnier_id
  2. GET /api/v1/geraete/konfiguration → Entprellzeit, Messpunkte, Plausibilitätsgrenzen
  3. GET /api/v1/turniere/aktiv/startliste (ETag-fähig) → Startreihenfolge für die Anzeige
  4. Pro Durchfahrt eine Messung lokal puffern, dann als Batch senden: POST /api/v1/zeitmessungen
  5. 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 unzugeordnet gespeichert und im internen Frontend zur Zuordnung angeboten.
  • Plausibilitätsverstöße werden markiert, nicht abgelehnt. Grenzen: OAMC_MESS_MIN_SEKUNDEN / OAMC_MESS_MAX_SEKUNDEN bzw. je Gerät in geraet.konfiguration.
  • gemessen_am weit 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 messpunkt und die Summenbildung.
  • O-14 Woher kennt der Client die Startnummer? → bis geklärt: startnummer_gemeldet als Hinweis, Server löst gegen die Startliste auf.
  • O-16 Geforderte Messgenauigkeit / eine oder zwei Lichtschranken.