# Plan: OAMC Turnierauswertung — Webanwendung **Stand:** 2026-09-05 · **Status:** Entwurf, noch nicht freigegeben **Änderung 05.09.:** Zeitmessung per Lichtschranke aufgenommen (§3.6, §5.4, §6.2, AP 5) --- ## 1. Ausgangslage ### 1.1 Der Verein und die Sportart Der **OAMC Reinheim e.V. im ADAC** (Odenwälder Automobil- und Motorsportclub) richtet **ADAC Motorrad-Turniere** aus. Diese laufen nach der **ADAC-Turnierordnung** im Bereich des **ADAC Hessen-Thüringen (HTH)**. Motorrad-Turniersport ist kein Rennsport, sondern ein Geschicklichkeitswettbewerb: Die Fahrer durchfahren einen Parcours mit festen Aufgaben, dabei zählen **Fehlerpunkte** und **Fahrzeit**. Aufgaben im Parcours (laut Sportbeschreibung auf oamc.de): Fahrzeugabnahme · Schätzen (im Stand und in Fahrt) · Slalom · Acht-Figur · Spurgasse · Kreisel · Tor · Schrägbrett · Wippe (mit und ohne Spurbrett) · Gummiringe · Dose umsetzen · Fahrgasse · Stop Haltelinie · Langsamfahrstrecke ### 1.2 Aktueller Ist-Zustand der Website Die bestehende Seite **oamc.de** (Zweitdomain: oamc-reinheim.de) ist eine statische **HTML-4.01-Frameset-Website**: - Aufbau: `index` → `oben.htm` (Navigation) + `hauptfenster.htm`, jede Rubrik wieder ein eigenes Frameset (`m/frame-m.htm` → `m1.htm` Menü + `m2.htm` Inhalt) - Rubriken: Verein · VÜP ADAC-Platz · Kartgruppe · **Motorradgruppe** · Fahrrad JVS · Anfahrt · Download · Weblinks · Flohmarkt · BikerSafetyDay - **Alle Ergebnisse liegen ausschließlich als PDF** unter `/datei/` und werden auf `d/d-mot-turnier.htm` verlinkt — Tagesergebnisse, Meisterschaftsstände (HTH 2002–2026), Bundesturnier-Ergebnisse, Protokolle - Die Seite **`m/m-fahrer.htm` ist eine „BAUSTELLE"** — es gibt aktuell **keine Fahrerdatenbank**. Genau das ist die Lücke, die dieses Projekt schließt. - `m-ergebnisse.htm` und `m-ausschreibung.htm` verweisen nur auf die PDF-Downloadseite ### 1.3 Wie heute ausgewertet wird Die Rahmenausschreibung 2026 nennt als Anforderung an den Veranstalter: > „Auswertungscomputer mit MSOffice (**Excel**) und Drucker sollte zeitgemäß vorhanden sein." Es existieren Vorlagen als `.xlsx` (z. B. `MT-NENNLISTE-5-2025.xlsx`). Die Auswertung läuft also heute **in Excel**, das Ergebnis wird als PDF exportiert und manuell auf die Website gelegt. Meisterschaftsstände werden separat fortgeschrieben. **Schmerzpunkte:** | Problem | Auswirkung | |---|---| | Ergebnisse nur als PDF | nicht durchsuchbar, nicht verlinkbar, nicht mobil lesbar | | Meisterschaftsstand manuell | fehleranfällig, spät verfügbar | | Keine Fahrerhistorie | „Wie oft war X schon dabei?" nicht beantwortbar | | Fahrerstammdaten pro Turnier neu erfasst | Tippfehler, Dubletten, Namensvarianten | | Frameset-Website | kein Deep-Link, schlechtes SEO, unbrauchbar auf dem Handy | --- ## 2. Ziel Eine Webanwendung, die 1. **Turnierergebnisse auswertet** — Fehlerpunkte + Zeit → Platzierung je Wertungsklasse 2. **Alle Daten in einer Datenbank hält** statt in Excel und PDF 3. **Zeiten per API von der Lichtschranken-Zeitmessung entgegennimmt** (zweiter Raspberry Pi am Parcours, siehe §3.6), in die Datenbank schreibt und **live anzeigt** 4. **Fahrer samt Stammdaten und Historie** bereitstellt (die heutige „Baustelle") 5. Die **Meisterschafts-/Pokalwertung** automatisch fortschreibt ### Nicht-Ziele (bewusst außerhalb dieses Projekts) - Ablösung der kompletten oamc.de (Kart, Fahrrad, Flohmarkt, VÜP bleiben, wo sie sind) - Online-Nennung/Anmeldung durch Teilnehmer → **Phase 2**, siehe §9 - Elektronische Erfassung der **Fehlerpunkte** — die bleiben beim Wertungspersonal im Parcours und werden manuell eingegeben (siehe §3.6) - Zahlungsabwicklung des Nenngelds (10 € je Start, wird vor Ort kassiert) --- ## 3. Fachliche Grundlagen > ⚠️ Die folgenden Regeln wurden aus der Ergebnisliste > `2026-05-31-MT-REIN-HAIN.PDF` und der `MT-Rahmenausschreibung-2026.pdf` > **abgeleitet**. Sie sind **vor der Implementierung gegen die ADAC-Turnierordnung 2026 > (`MT-D2025.pdf`) und mit dem Bereichsleiter zu verifizieren** — siehe §10. ### 3.1 Veranstaltungsstruktur - Ein Renntag kann ein **Doppelturnier** sein: zwei Turniere zweier Ortsclubs laufen parallel auf einem Platz, z. B. „ADAC-Motorrad-Doppelturnier des MSC Hainstadt und des OAMC Reinheim". - **Jeder Fahrer wird in beiden Turnieren getrennt gewertet** — die Ergebnisliste vom 31.05.2026 enthält für dieselben Fahrer je einen Block „Reinheim" und einen Block „Hainstadt" mit **eigenen Zeiten, Fehlern und Platzierungen**. - ⇒ Datenmodell-Konsequenz: `veranstaltung` (Renntag) : `turnier` (Wertungslauf) = 1:n ### 3.2 Klassen **Jugendklassen** (Einteilung strikt nach Jahrgang, Ausweispflicht bei der Nennung): | Klasse | Jahrgang | ~Alter | Fahrzeug | |---|---|---|---| | J 1 | 2019 / 2018 / 2017 | 7–9 | Kindermotorrad max. 110 ccm / 5,5 kW | | J 2 | 2016 / 2015 | 10–11 | Kindermotorrad max. 110 ccm / 5,5 kW | | J 3 | 2014 / 2013 | 12–13 | Mofa, Motorrad + Roller max. 125 ccm / 11 kW | | J 4 | 2012 / 2011 | 14–15 | Mofa, Motorrad + Roller max. 125 ccm / 11 kW | | J 5 | 2010 / 2009 / 2008 (bis 18. Geburtstag) | 16–18 | Mofa, Motorrad + Roller max. 125 ccm / 11 kW | Für Klasse 1 muss der Teilnehmer **am Veranstaltungstag** bereits 7 Jahre alt sein. ⇒ Die Jahrgangstabelle ist **jahresabhängig** und muss versioniert in der DB liegen, nicht im Code (`klassen_jahrgang` mit `saison`). **Erwachsenenklassen:** `S 1`–`S 9` (Stammfahrer) und `A 1`–`A 4` (Anfänger), unterteilt nach Fahrzeugtyp/Hubraum. Aus der Sportbeschreibung: Mokicks und Roller bis 80 ccm · bis 250 ccm · bis 650 ccm · über 650 ccm · Enduro (mehrere Hubraumklassen) · Seitenwagen · **Pedelec / E-Bike** · **E-Scooter** (neu). > ❗ Die genaue Zuordnung `S 1`…`S 9` / `A 1`…`A 4` → Fahrzeugtyp ist aus den > öffentlichen Seiten **nicht** ersichtlich. Muss erfragt werden (§10). **Wertungsklasse ≠ Klasse:** In der Ergebnisliste werden Klassen zu Wertungsklassen zusammengefasst, wenn zu wenige Starter da sind — z. B. eine gemeinsame Wertung „S 5, S 9, S 3, S 6, S 7" und „A 1, A 4". Das Zusammenlegen ist eine **Entscheidung pro Turnier** und gehört als Datum in die DB, nicht als Regel in den Code. ### 3.3 Wertungslogik ← Kernstück der Anwendung Spalten der Ergebnisliste: `Start-Nr.` · `Voller Name` · `Regionalclub` · `Klasse` · `Geschlecht` · `Kategorie` · `Summe Zeit` · `Summe Fehler` · `Gesamt` · `Platzierung` **Es gibt zwei verschiedene Berechnungen für `Gesamt`:** **a) Erwachsene (Kategorie S, und A-Fahrer):** ``` Gesamt = Summe Zeit + Summe Fehler Platzierung = aufsteigend nach Gesamt ``` Belege aus der Liste vom 31.05.2026, Wertungsklasse „S 5, S 9, S 3, S 6, S 7": | Fahrer | Klasse | Zeit | Fehler | Gesamt | Platz | |---|---|---|---|---|---| | Bernius Meik | S 6 | 105,43 | 0 | **105,43** | 1 | | Schmunk Christian | S 9 | 103,26 | 10 | **113,26** | 2 | | Krack Robin | S 5 | 130,33 | 0 | **130,33** | 4 | | Steinbrech Nicolai | S 5 | 65,71 | 1210 | **1275,71** | 11 | **b) Jugend (Kategorie J):** ``` Gesamt = Summe Fehler ← die Zeit geht NICHT in die Wertung ein Platzierung = aufsteigend nach Fehler, bei Gleichstand aufsteigend nach Zeit ``` Das deckt sich mit der Sportbeschreibung: > „Bei den Turnieren kommt es in der Jugendklasse vorrangig auf die fehlerfreie > Durchfahrt an, da die Zeit erst bei Punktgleichheit herangezogen wird." Belege, Wertungsklasse J 3: | Fahrer | Zeit | Fehler | Gesamt | Platz | Anmerkung | |---|---|---|---|---|---| | Freund Philipp | 136,88 | 0 | **0** | 1 | drei Fahrer mit 0 Fehlern … | | D'Angelo Leandro | 140,40 | 0 | **0** | 2 | … Reihenfolge über die Zeit | | Heil Luca | 144,46 | 0 | **0** | 3 | | | Mattes Flynn | 160,05 | 10 | **10** | 4 | | | Ratter Mitja | 148,42 | 60 | **60** | 7 | Gleichstand 60 Fehler … | | Kemesies Finlay | 165,39 | 60 | **60** | 8 | … 148,42 < 165,39 | ⇒ Die Wertungsformel ist **pro Kategorie konfigurierbar** zu implementieren, nicht hart zu verdrahten (`wertungsregel` als Strategie/Enum je Kategorie und Saison). **Fehlerpunkte** treten in Vielfachen von 10 auf (10/20/30/…/1210). Die Zuordnung „welcher Fehler an welcher Aufgabe kostet wie viele Punkte" steht in der ADAC-Turnierordnung und ist zu beschaffen (§10). **Mehrfachstarts:** Ein Fahrer kann am selben Tag **mehrfach starten** (anderes Fahrzeug, andere Klasse). Beispiel 31.05.2026: *Schmunk Christian* startet in S 9 **und** S 3, *Bernius Meik* in S 6 **und** S 7 — jeweils mit eigener Startnummer und eigenem Ergebnis. ⇒ Der Schlüssel eines Ergebnisses ist **nicht** (Turnier, Fahrer), sondern (Turnier, Start), und Nenngeld fällt „für jeden Start" an. ### 3.4 Meisterschafts-/Pokalwertung (HTH) - **Erwachsene:** ein Gesamtklassement der A- und S-Fahrer/innen (klassenübergreifend) - **Jugend:** Pokalwertung **klassenweise** J 1 – J 5 **Streichresultate** — nicht alle Läufe zählen: | Turniere in der Saison | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | |---|---|---|---|---|---|---|---|---|---| | **davon gewertet** | 8 | 8 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | Nicht jede Veranstaltung zählt zur Meisterschaft — der Terminkalender markiert Läufe „mit Meisterschaftswertung" bzw. „o. M. = ohne Meisterschaft". **Endlaufqualifikation:** die besten **10 Erwachsenen** und **10 Jugendlichen** der HTH-Meisterschaft (Jugend ohne Trennung Damen/Herren, Verteilung prozentual nach Teilnehmerzahl der 5 Jugendklassen). **Bei Punktgleichheit zählt die Majorität der Siege.** Endgültige Festlegung durch den Bereichsleiter ⇒ die Anwendung *schlägt vor*, sie *entscheidet nicht*. > ❗ Die **Punktetabelle** (Platz → Meisterschaftspunkte) ist noch nicht bekannt (§10). ### 3.5 Weitere Regeln aus der Rahmenausschreibung 2026 - Nenngeld einheitlich **10 €** je Start (S, A, Jugend, Mofa/Moped, E-Bike, Pedelec, E-Scooter) - **Nennschluss:** Klasse S + Jugendklassen 14:00 Uhr, A-Klasse + E-Bike/Pedelec/E-Scooter 15:00 Uhr - Pokalläufe bei Doppelveranstaltungen beginnen generell 09:00 Uhr - Gültiger Jugendausweis + unterschriebener Haftungsausschluss sind bei der Nennung vorzulegen - Eine **nummerierte Nennungsliste** ist zu führen, die Reihenfolge nach Möglichkeit einzuhalten - Für A-Klassen-Fahrer und Gelegenheitsfahrer wird z. B. jede 10. Startnummer freigehalten - Schlussbericht + Ergebnisliste müssen nach FFM gemeldet werden — **der Veranstalter-Zuschuss wird erst überwiesen, wenn beide Dokumente dort sind** ⇒ Export in genau diesen Formaten ist kein „nice to have", sondern zahlungsrelevant ### 3.6 Zeitmessung per Lichtschranke *(Vorgabe des Auftraggebers)* Die API wird von einem **zweiten Raspberry Pi am Parcours** bespielt. Dieser ist an eine **Lichtschranke** angeschlossen, misst die Fahrzeiten, sendet sie an die Anwendung und **zeigt die Daten des aktuellen Fahrzeugs/Starters an**. Damit gibt es **zwei Datenquellen pro Ergebnis**: | Wert | Quelle | Weg | |---|---|---| | `Summe Zeit` | **Lichtschranke** (Messgerät) | automatisch per API | | `Summe Fehler` | **Wertungspersonal im Parcours** | manuell im internen Frontend | Ein Ergebnis ist also erst vollständig, wenn **beide** Seiten geliefert haben. Das Datenmodell muss einen Start mit Zeit, aber ohne Fehlerpunkte (und umgekehrt) sauber abbilden können — ein „halbes" Ergebnis ist der Normalzustand während der Veranstaltung, kein Fehler. #### Konsequenzen für den Entwurf **a) Rohmessungen statt Summen speichern.** Die Spalte heißt in der Ergebnisliste `Summe Zeit` — die Zeit ist also vermutlich eine **Summe mehrerer Messungen** (mehrere Durchgänge oder mehrere gemessene Abschnitte, z. B. Langsamfahrstrecke und Gesamtdurchfahrt). Die Anwendung speichert deshalb **jede Einzelmessung** (`zeitmessung`) und bildet die Summe selbst. Rückweg gibt es nicht: aus einer gespeicherten Summe lassen sich Einzelmessungen nie wieder herstellen, aus Einzelmessungen die Summe jederzeit. → offene Frage **O-13** **b) Das Messgerät ist ein eigenständiger API-Client, kein Teil der Anwendung.** Es bekommt einen **Geräte-Token** (nicht den Auswerter-Login), darf nur Messungen schreiben und Starter-Daten lesen, und wird in der DB als `geraet` geführt. Ein Gerät am offenen Platz muss man notfalls einzeln sperren können. **c) Netzausfall ist der Normalfall, nicht die Ausnahme.** Am ADAC-Platz kann das Netz wegbrechen. Der Mess-Pi **muss lokal puffern** (SQLite oder Datei-Queue) und nach Wiederverbindung nachliefern. Deshalb gilt zwingend: - jede Messung bekommt eine **UUID vom Messgerät** → Server ist idempotent, doppelt gesendete Messungen erzeugen keine Dubletten - die Messung trägt **zwei Zeitstempel**: Wanduhr (`gemessen_am`) und **monotone Gerätezeit** (`monotonic_ns`). Die Wanduhr eines Pi ohne Netz und ohne RTC springt beim NTP-Sync — die Dauer zwischen zwei Lichtschranken-Ereignissen darf davon nicht abhängen. **Der Pi berechnet die Dauer selbst und sendet sie**, der Server rechnet keine Differenz aus zwei Wanduhr-Zeitstempeln. - der Server nimmt Messungen **rückwirkend** an (`gemessen_am` in der Vergangenheit) und markiert sie als nachgeliefert **d) Zuordnung Messung → Starter ist der kritische Punkt.** Eine Lichtschranke misst eine Zeit, sie weiß nicht, **wer** durchgefahren ist. Die Zuordnung muss irgendwo herkommen — Eingabe der Startnummer am Mess-Pi, eine Warteschlange „nächster Starter" aus der Nennliste, oder ein Transponder/RFID. → offene Frage **O-14**. Bis zur Klärung wird konservativ geplant: - der Mess-Pi holt sich per API die **Startreihenfolge** des laufenden Turniers - er sendet die Messung **mit** der Startnummer, die er gerade anzeigt - eine Messung **ohne** zuordenbare Startnummer wird nicht verworfen, sondern als `zeitmessung` ohne `start_id` gespeichert und im Frontend zur manuellen Zuordnung angeboten. Eine gemessene Zeit ist nicht reproduzierbar — sie darf nie verloren gehen, nur weil die Zuordnung fehlte. **e) Fehlmessungen sind Betriebsrealität.** Zuschauer, Helfer oder ein zurücksetzendes Motorrad lösen die Lichtschranke aus. Nötig sind: **Entprellung/Mindestabstand** zwischen zwei Triggern auf dem Pi, ein **Verwerfen-Knopf** in der Erfassung, sowie Plausibilitätsprüfung serverseitig (eine Fahrzeit von 3 s oder 900 s ist zu markieren, nicht stillschweigend zu übernehmen). Jede verworfene Messung bleibt mit Grund gespeichert. **f) Die Anzeige am Parcours ist ein eigener Anwendungsfall.** Der Mess-Pi zeigt Starter- und Fahrzeugdaten an, zieht sie also **lesend** aus der API. Das braucht einen schlanken, schnellen Endpunkt und eine Live-Aktualisierung (SSE), und er muss **auch bei Netzausfall die zuletzt geladene Startliste weiter anzeigen**. > ❗ Was genau angezeigt werden soll (Startnummer, Name, Klasse, Fahrzeug, laufende Zeit, > letzte Zeit?) und auf welcher Hardware (HDMI-Monitor, kleines Display, LED-Tafel) ist > noch offen → **O-15**. --- ## 4. Datenmodell ``` verein ──┐ ├──< fahrer ──< start >── turnier >── veranstaltung saison ──┤ │ │ │ ├──< klasse ───┘ │ └──< wertungsklasse │ ├──< ergebnis (Zeit + Fehler, berechnet) │ └──< zeitmessung >── geraet (Lichtschranke) └──< meisterschaft ──< meisterschaft_stand ``` ### Tabellen **`verein`** — Regionalclub / Ortsclub `id` · `name` („OAMC Reinheim", „MSC Schotten", „PMS Kassel", „Hainstadt") · `kurzname` · `adac_gau` · `aktiv` **`fahrer`** — Stammdaten, **einmalig**, nicht pro Turnier `id` · `nachname` · `vorname` · `geburtsjahr` · `geburtsdatum` (nullable, für J1-Prüfung „am Veranstaltungstag 7 Jahre") · `geschlecht` (M/W/D) · `verein_id` · `adac_mitgliedsnr` (nullable) · `jugendausweis_gueltig_bis` · `aktiv` · `notiz` · `erstellt_am` / `geaendert_am` → **Dublettenerkennung** beim Import nötig, siehe §6.3 **`saison`** — Jahr, macht Regeln versionierbar `jahr` (PK) · `turnierordnung_version` · `streichresultate_tabelle` (JSONB) · `nenngeld_cent` · `aktiv` **`klasse`** — Klassendefinition **pro Saison** `id` · `saison_jahr` · `code` („J 1", „S 6", „A 4") · `kategorie` (J/S/A) · `bezeichnung` · `jahrgaenge` (int[], nur Jugend) · `ccm_max` · `kw_max` · `fahrzeugtyp` · `sortierung` **`veranstaltung`** — der Renntag `id` · `datum` · `titel` · `ort` · `ausrichter_verein_id` · `ist_doppelturnier` · `veranstaltungsleiter` · `turnierleiter` · `status` (geplant/laufend/vorlaeufig/final) **`turnier`** — der einzelne Wertungslauf (bei Doppelturnier zwei pro Veranstaltung) `id` · `veranstaltung_id` · `name` („Reinheim" / „Hainstadt") · `veranstalter_verein_id` · `zaehlt_meisterschaft` (bool) · `status` **`wertungsklasse`** — pro Turnier zusammengefasste Klassen `id` · `turnier_id` · `bezeichnung` („S 5, S 9, S 3, S 6, S 7") · `klasse_ids` (int[]) · `sortierung` **`start`** — eine Teilnahme; ein Fahrer kann mehrere pro Turnier haben `id` · `turnier_id` · `fahrer_id` · `klasse_id` · `wertungsklasse_id` · `startnummer` · `fahrzeug` (Text) · `nenngeld_bezahlt` · `status` (genannt/gestartet/DNF/DNS/DSQ) → UNIQUE (`turnier_id`, `startnummer`) **`ergebnis`** — 1:1 zum Start, getrennt für saubere Nachträge/Korrekturen `start_id` (PK) · `summe_zeit` (numeric(7,2), **aus `zeitmessung` summiert**) · `summe_fehler` (int, **manuell erfasst**) · `gesamt` (numeric(9,2), **berechnet**) · `platzierung` (int, **berechnet**) · `zeit_vollstaendig` (bool) · `fehler_vollstaendig` (bool) · `erfasst_von` · `erfasst_am` · `korrigiert_am` · `korrektur_grund` → Die beiden `*_vollstaendig`-Flags machen sichtbar, welche der zwei Quellen (§3.6) noch fehlt. Gewertet wird erst, wenn beide gesetzt sind. **`geraet`** — Messgerät / Anzeige am Parcours `id` · `bezeichnung` („Lichtschranke Ziel", „Anzeige Start") · `typ` (lichtschranke/anzeige) · `token_hash` · `aktiv` · `letzte_meldung_am` · `firmware_version` · `notiz` → `letzte_meldung_am` ist der Heartbeat: das Frontend zeigt, ob die Messung noch lebt **`zeitmessung`** — Einzelmessung, **nicht** die Summe `id` (UUID, **vom Gerät vergeben** → Idempotenz) · `geraet_id` · `turnier_id` · `start_id` (**nullable** — unzugeordnete Messungen gehen nicht verloren) · `startnummer_gemeldet` (int, was das Gerät angezeigt hat) · `messpunkt` (z. B. „Durchgang 1", „Langsamfahrstrecke") · `dauer_sekunden` (numeric(7,2), **vom Gerät berechnet**) · `gemessen_am` (timestamptz, Wanduhr) · `monotonic_ns` (bigint) · `empfangen_am` · `nachgeliefert` (bool) · `status` (gueltig/verworfen/unzugeordnet) · `verworfen_grund` · `roh` (JSONB, Rohereignisse der Lichtschranke) → UNIQUE (`id`); Index auf (`turnier_id`, `status`), (`start_id`) **`aufgabe`** / **`ergebnis_aufgabe`** — *optional, Phase 2*: Einzelwertung je Parcours-Aufgabe (Slalom, Wippe, Kreisel …). Die heutigen PDFs enthalten nur Summen; die Struktur vorzusehen kostet jetzt nichts und erlaubt später Detailauswertungen. Sobald die Lichtschranke mehrere Messpunkte bedient, wächst `zeitmessung.messpunkt` natürlich in diese Struktur hinein. **`meisterschaft`** — z. B. „HTH Erwachsene 2026", „HTH Jugend J 3 2026" `id` · `saison_jahr` · `bezeichnung` · `kategorie` · `klasse_id` (nullable) · `regelwerk` (JSONB) **`meisterschaft_stand`** — materialisiert, nach jedem Turnier neu berechnet `meisterschaft_id` · `fahrer_id` · `punkte_gesamt` · `punkte_je_lauf` (JSONB) · `gestrichene_laeufe` (JSONB) · `anzahl_siege` (Tie-Break!) · `platz` · `berechnet_am` **`import_job`** — Audit-Trail jedes API-/Datei-Imports `id` · `quelle` · `dateiname` · `roh_payload` (JSONB) · `status` · `fehler` · `angelegt_von` · `angelegt_am` **`benutzer`** / **`rolle`** — Admin, Auswerter, Lesend ### Kernprinzipien - `gesamt` und `platzierung` werden **von der Anwendung berechnet**, nicht importiert — importierte Werte werden gegen die Berechnung **geprüft** und Abweichungen gemeldet (fängt Excel-Fehler in Altdaten und Regelmissverständnisse auf beiden Seiten) - Alles **saisonversioniert**: Klassen, Jahrgänge, Streichresultate, Punktetabelle. Ergebnisse aus 2019 müssen 2030 noch korrekt reproduzierbar sein. - Ergebnisse sind **append-only mit Korrektur-Historie**, kein stilles UPDATE --- ## 5. Architektur & Technologie ### 5.1 Vorhandene Umgebung (geprüft auf diesem Gerät) | | | |---|---| | Hardware | Raspberry Pi 4 Model B Rev 1.5, 8 GB RAM, 117 GB Speicher (94 GB frei) | | OS | Debian 13 (trixie), Linux 6.18 aarch64 | | **PostgreSQL 17.10** | installiert **und aktiv** ✅ | | **nginx** | installiert **und aktiv** ✅ | | **Python 3.13.5** + pip 25.1.1 | ✅ | | Node.js / npm | ❌ nicht installiert | | Docker | ❌ nicht installiert | | git | ❌ **nicht installiert** — vor Projektstart nachinstallieren | ### 5.2 Empfohlener Stack Der Stack folgt dem, was **schon läuft** — auf einem Pi 4 zählt jedes vermiedene Laufzeit-Ökosystem. | Schicht | Wahl | Begründung | |---|---|---| | Datenbank | **PostgreSQL 17** | läuft bereits; JSONB für Regelwerke, saubere Constraints, echte Transaktionen | | Backend | **Python 3.13 + FastAPI** | Python ist da; FastAPI liefert die API-Doku (OpenAPI/Swagger) automatisch mit — wichtig, weil Dritte die API bespielen sollen | | ORM / Migration | **SQLAlchemy 2 + Alembic** | Schemaänderungen über Jahre nachvollziehbar | | Validierung | **Pydantic v2** | ein Schema für API-Validierung *und* Doku | | Templates | **Jinja2**, serverseitig gerendert | Ergebnislisten sind Dokumente, keine App. Kein Build-Schritt, kein Node, sofort suchmaschinenlesbar | | Interaktivität | **HTMX** (~14 kB, per CDN oder lokal) | Filter/Sortierung/Live-Nachladen ohne SPA-Apparat | | Live-Updates | **SSE** (Server-Sent Events), von FastAPI direkt unterstützt | Anzeige am Parcours und Live-Ergebnisse; einseitiger Datenfluss, kein WebSocket-Zustand nötig, reconnectet von selbst | | CSS | handgeschrieben oder **Pico.css** | keine Build-Pipeline | | Excel/CSV-Import | **openpyxl** + `csv` | die Nennlisten sind `.xlsx` | | PDF-Export | **WeasyPrint** | Schlussbericht + Ergebnisliste müssen nach FFM | | Server | **uvicorn** hinter dem vorhandenen **nginx** | nginx macht TLS und liefert Statisches | | Prozess | **systemd-Unit** | kein Docker nötig | | TLS | **Let's Encrypt / certbot** | | **Bewusst nicht gewählt:** React/Vue-SPA (Build-Kette auf dem Pi, kein SEO, viel Aufwand für Tabellenanzeige) · Docker (Overhead ohne Nutzen bei einer App) · SQLite (Mehrbenutzerbetrieb am Turniertag, Postgres ist schon da) ### 5.3 Betrieb ``` ╔═══ Am Parcours ═══════════════╗ ║ Pi #2 „Mess-Pi" ║ ║ Lichtschranke → GPIO ║ ║ lokale Queue (SQLite) ║──┐ POST Messungen (idempotent, gepuffert) ║ Anzeige Starter/Fahrzeug ║<─┼─ GET Startliste + SSE Live ╚═══════════════════════════════╝ │ │ Internet / LAN ──> nginx (TLS, statisch) ┘ └→ uvicorn/FastAPI (systemd) → PostgreSQL 17 (lokal) → /var/oamc/uploads (PDFs, Bilder) ``` - **Backup:** täglicher `pg_dump` + wöchentliche Vollsicherung auf externes Medium. Auf einer SD-Karte/SSD im Pi ist das nicht optional. - **Offline-Fähigkeit am Turniertag** ist durch die Zeitmessung **Pflicht geworden**, nicht mehr optional (§3.6c). Zwei Ebenen: 1. der **Mess-Pi** puffert lokal und liefert nach — deckt kurze Aussetzer ab 2. bei Veranstaltungen **ohne Internet** muss der Anwendungs-Pi **mit vor Ort** und beide Geräte hängen an einem lokalen Netz (Router/Hotspot ohne Uplink). Die Synchronisation zum öffentlichen Server erfolgt danach. ⇒ Entscheidung dazu offen, siehe **O-7**. - **Zeitserver:** ohne Internet gibt es kein NTP. Der Anwendungs-Pi sollte im lokalen Netz selbst NTP anbieten, damit die Zeitstempel zusammenpassen. Für die *Messung* ist das unkritisch (der Mess-Pi rechnet monoton, §3.6c), für die Protokollierung nicht. ### 5.4 Software auf dem Mess-Pi Bewusst klein und eigenständig — das Gerät muss auch dann sinnvoll weiterlaufen, wenn der Server nicht erreichbar ist. | Teil | Wahl | |---|---| | GPIO / Lichtschranke | `gpiozero` mit `pigpio`- oder `lgpio`-Backend; Trigger als **Interrupt**, nicht Polling | | Zeitmessung | `time.monotonic_ns()`, Entprellung/Mindestabstand konfigurierbar | | Lokale Queue | SQLite, Einträge bleiben bis vom Server quittiert | | Versand | HTTP + Geräte-Token, Retry mit Backoff, Batch-Nachlieferung | | Anzeige | Browser im Kiosk-Modus auf die Anzeige-Seite der Anwendung (§7), Daten per SSE, letzte Startliste im `localStorage` als Offline-Rückfall | | Betrieb | systemd-Unit, `WatchdogSec`, Autostart, read-only Root-FS erwägen (Stromausfall am Platz) | > Für saubere Zeiten unter Last ist ein Interrupt-basierter Trigger Pflicht. Ob die > Genauigkeit eines Linux-Userspace-Prozesses reicht, hängt von der geforderten > Auflösung ab → **O-16**. Die Ergebnislisten führen **1/100 s** (z. B. `159,16`); > das ist mit `pigpio` gut erreichbar, mit Polling in Python nicht zuverlässig. --- ## 6. API Zwei Richtungen: **Ingest** (Daten kommen rein) und **Read** (Daten gehen raus). ### 6.1 Ingest — `POST /api/v1/…` (authentifiziert, API-Key) | Endpunkt | Zweck | |---|---| | `POST /api/v1/veranstaltungen` | Renntag anlegen | | `POST /api/v1/turniere` | Wertungslauf zum Renntag | | `POST /api/v1/fahrer` · `PUT /api/v1/fahrer/{id}` | Stammdaten | | `POST /api/v1/turniere/{id}/starts` | Nennliste (Bulk) | | `POST /api/v1/turniere/{id}/ergebnisse` | **Ergebnis-Bulk-Upload** ← Kernfall | | `POST /api/v1/import/xlsx` | Excel-Nennliste/Ergebnisliste als Datei | | `POST /api/v1/turniere/{id}/finalisieren` | Wertung rechnen, Meisterschaft fortschreiben | Beispiel-Payload Ergebnis-Upload: ```json { "quelle": "excel-auswertung-v3", "idempotenz_schluessel": "2026-05-31-reinheim-final", "ergebnisse": [ { "startnummer": 32, "fahrer": { "nachname": "Gál-Szász", "vorname": "Péter", "geburtsjahr": 2015, "geschlecht": "M", "verein": "OAMC Reinheim" }, "klasse": "J 2", "summe_zeit": 159.16, "summe_fehler": 10, "gesamt_erwartet": 10, "platzierung_erwartet": 1 } ] } ``` **Regeln für den Ingest:** - **Idempotent** über `idempotenz_schluessel` — ein doppelt gesendeter Upload legt keine Dubletten an. Am Turniertag mit wackligem Netz ist das Pflicht, nicht Komfort. - **Zweistufig:** `?dry_run=true` liefert eine Validierungsvorschau (welche Fahrer sind neu, welche werden gematcht, welche Werte weichen ab) — erst danach wird geschrieben. - **`gesamt_erwartet` / `platzierung_erwartet`** sind optional; wenn geliefert, rechnet der Server nach und meldet Abweichungen als Warnung, statt sie zu übernehmen. - Jeder Aufruf landet in `import_job` mit Roh-Payload — bei Streit über eine Platzierung ist rekonstruierbar, was wann ankam. ### 6.2 Geräte-API — Zeitmessung & Anzeige *(Mess-Pi, Token-Auth)* Eigener Namensraum mit eigenem Rechteprofil: schreiben **nur** Messungen, lesen **nur** das, was die Anzeige braucht. Kompromittierung des Geräts am offenen Platz darf keine Ergebnisse manipulieren können. | Endpunkt | Zweck | |---|---| | `POST /api/v1/geraete/heartbeat` | „ich lebe" + Firmware/Queue-Länge → Statusanzeige im Frontend | | `GET /api/v1/geraete/konfiguration` | Entprellzeit, Messpunkte, aktives Turnier — zentral gepflegt statt auf dem Pi | | `GET /api/v1/turniere/aktiv/startliste` | Startreihenfolge für die Anzeige, ETag-fähig, offlinefest zwischenspeicherbar | | `GET /api/v1/turniere/aktiv/anzeige` (SSE) | Live-Stream: aktueller Starter, letzte Zeit | | `POST /api/v1/zeitmessungen` | **Messungen (Batch, idempotent)** ← Kernfall | | `POST /api/v1/zeitmessungen/{id}/verwerfen` | Fehlmessung markieren | Beispiel-Payload der Zeitmessung — bewusst als **Batch**, weil der Pi nach einem Netzausfall mehrere Messungen auf einmal nachliefert: ```json { "geraet": "lichtschranke-ziel-01", "messungen": [ { "id": "9f2c1e7a-4d38-4b6e-9a11-5c0b2d7e8f30", "turnier_id": 42, "startnummer_gemeldet": 32, "messpunkt": "durchgang-1", "dauer_sekunden": 159.16, "gemessen_am": "2026-05-31T10:14:02.482+02:00", "monotonic_ns": 884213771004, "roh": { "trigger_start_ns": 884054611004, "trigger_ziel_ns": 884213771004 } } ] } ``` Antwort quittiert **jede Messung einzeln** (`angenommen` / `duplikat` / `unzugeordnet` / `unplausibel`) — erst eine Quittung löscht den Eintrag aus der lokalen Queue des Pi. **Festlegungen:** - Die **`id` vergibt das Gerät** (UUIDv4/v7). Nur so ist Nachliefern nach Timeout ohne Dubletten möglich — der Pi weiß nach einem abgebrochenen Request nicht, ob der Server ihn verarbeitet hat, und darf ihn deshalb gefahrlos wiederholen. - Die **Dauer kommt fertig vom Gerät**, der Server rechnet keine Differenz aus Wanduhr- Zeitstempeln (Begründung §3.6c). - `startnummer_gemeldet` ist ein **Hinweis, kein Fremdschlüssel**. Der Server löst sie gegen die Startliste auf; scheitert das, wird die Messung als `unzugeordnet` gespeichert und im Frontend zur Zuordnung angeboten — **nie verworfen**. - Plausibilitätsgrenzen (min/max Fahrzeit je Klasse) sind konfigurierbar; Verstöße werden **markiert**, nicht abgelehnt. - Der Geräte-Token ist **nicht** der Auswerter-Login und einzeln sperrbar. ### 6.3 Read — `GET /api/v1/…` (öffentlich, ohne Key) ``` GET /api/v1/veranstaltungen?saison=2026 GET /api/v1/turniere/{id}/ergebnisse?wertungsklasse=J3 GET /api/v1/fahrer?q=bernius&verein=OAMC+Reinheim GET /api/v1/fahrer/{id}/historie GET /api/v1/meisterschaften/{id}/stand GET /api/v1/meisterschaften/{id}/endlauf-qualifikation ``` Zusätzlich **iCal** (`/termine.ics`) für den Terminkalender und **CSV-Export** je Liste. ### 6.4 Fahrer-Matching beim Import Der heikelste Teil des Imports. Aus den Altdaten sichtbar: Namen mit Sonderzeichen (*Gál-Szász*), Vereinsangaben mal als „OAMC Reinheim", mal als „Hainstadt" (Ortsname statt Clubname), Namensgleichheiten in Familien (*Gál-Szász Péter* / *Máté* / *János*; *Bernius Meik* / *Jörg*; *Krack Robin* / *Luca*; *Tinz Xenia* / *Enrique*). Vorgehen: 1. Exakter Match auf (Nachname, Vorname, Geburtsjahr) — normalisiert (Unicode NFKD, Kleinschreibung, Bindestriche) 2. Unscharfer Match (Levenshtein / `pg_trgm`) → **Vorschlag**, nie automatisch 3. Kein Match → neuer Fahrer wird angelegt, aber im Import-Report als „NEU" markiert 4. **Merge-Funktion** im Admin, um nachträglich erkannte Dubletten zusammenzuführen --- ## 7. Seiten (Frontend) **Öffentlich:** | Seite | Inhalt | |---|---| | Startseite Motorrad-Turnier | nächster Termin, letzte Ergebnisse, aktueller Meisterschaftsstand | | **Termine** | Saisonkalender, mit/ohne Meisterschaftswertung, Ausrichter, iCal-Abo | | **Ergebnisse** | je Veranstaltung → Turnier → Wertungsklasse, sortierbar, mobil lesbar | | **Live** (Turniertag) | vorläufiger Stand, sobald Zeiten eintreffen — deutlich als *vorläufig* gekennzeichnet, solange Fehlerpunkte fehlen (§3.6) | | **Fahrer** | durchsuchbare Liste (löst die „BAUSTELLE" ab) | | **Fahrerprofil** | Stammdaten, alle Starts, Platzierungen, Bestzeiten, Saisonverlauf | | **Meisterschaft** | HTH-Stand Erwachsene + Jugend J 1–J 5, inkl. Streichresultat-Kennzeichnung | | **Endlauf-Qualifikation** | aktueller Stand der Top-10-Plätze | | Sportbeschreibung / Ausschreibung / Downloads | bestehende Inhalte, sauber übernommen | **Intern (Login):** | Seite | Inhalt | |---|---| | Nennungsannahme | Fahrer suchen/anlegen, Klasse automatisch aus Jahrgang vorschlagen, Startnummer vergeben, Nenngeld quittieren | | Ergebniserfassung | schnelle Eingabe Zeit/Fehler je Startnummer, tastaturoptimiert | | Auswertung | Wertungsklassen bilden, rechnen, vorläufig/final setzen | | Korrekturen | mit Begründung, mit Historie | | Exporte | Ergebnisliste (PDF, ADAC-Layout), Schlussbericht, Nennliste, CSV | | **Messungen zuordnen** | unzugeordnete/unplausible Lichtschranken-Messungen einem Start zuweisen oder mit Grund verwerfen | | **Geräte** | Mess-Pi-Status (Heartbeat, Queue-Länge), Token vergeben/sperren, Entprellzeit und Messpunkte konfigurieren | | Import-Monitor | Import-Jobs, Warnungen, Fahrer-Dubletten mergen | | Stammdaten | Saison, Klassen, Vereine, Punktetabelle, Benutzer | **Anzeige am Parcours** (eigener, minimaler Modus für den Mess-Pi): | Seite | Inhalt | |---|---| | `/anzeige` | aktueller Starter groß (Nr., Name, Klasse, Fahrzeug), laufende/letzte Zeit, nächste Starter; SSE-Update; Kiosk-tauglich; **behält bei Verbindungsverlust die letzte Startliste** und zeigt den Offline-Zustand deutlich an | **Anforderungen quer:** responsiv (am Platz wird das Handy benutzt) · Deep-Links auf jede Ergebnisliste · druckbare Ansichten · Aushang-/Beamer-Modus (große Schrift, Autorefresh) · barrierearm (Kontrast, Tabellen-Semantik) --- ## 8. Arbeitspakete ### AP 0 — Klärung & Setup *(vor allem anderen)* - Offene Fragen §10 mit dem Verein / Bereichsleiter klären - **ADAC-Turnierordnung 2026 (`MT-D2025.pdf`) beschaffen und Wertungsregeln verifizieren** - Punktetabelle Meisterschaft beschaffen - `git` installieren, Repository anlegen, Python-venv, Projektgerüst - Zugriff auf die Excel-Vorlagen und ein paar echte Nennlisten ### AP 1 — Datenmodell & Migrationen - SQLAlchemy-Modelle + Alembic-Initialmigration - Stammdaten-Seed: Saison 2026, Klassen J 1–J 5, S/A-Klassen, Vereine - Unit-Tests auf Constraints (Mehrfachstart erlaubt, Startnummer eindeutig je Turnier) ### AP 2 — Wertungs-Engine *(der Kern — zuerst und mit Tests)* - Regelstrategien je Kategorie (Jugend: Fehler → Zeit; Erwachsene: Zeit + Fehler) - Platzierung inkl. Gleichstandsauflösung, DNF/DNS/DSQ-Behandlung - **Regressionstest gegen die reale Ergebnisliste vom 31.05.2026** — alle 13 Wertungs- blöcke müssen exakt die publizierten Platzierungen reproduzieren. Das ist der Abnahmetest für die Engine. ### AP 3 — Meisterschaftsberechnung - Punktevergabe, Streichresultate nach Tabelle §3.4 - Tie-Break „Majorität der Siege" - Endlauf-Qualifikationsvorschlag Top 10 / Top 10 Jugend - Nachrechnen gegen einen veröffentlichten HTH-Jahresstand als Test ### AP 4 — API - Read-Endpunkte + OpenAPI-Doku - Ingest-Endpunkte, Idempotenz, `dry_run`, API-Key-Auth, Rate-Limit - Import-Job-Protokollierung - Fahrer-Matching inkl. Merge ### AP 5 — Zeitmessung: Geräte-API & Mess-Pi - Geräte-Registrierung, Token, Heartbeat, zentrale Konfiguration - `POST /zeitmessungen` mit Batch, Idempotenz, Einzelquittung, Plausibilitätsprüfung - Auflösung `startnummer_gemeldet` → Start; unzugeordnete Messungen im Frontend behandeln - **Client auf dem Mess-Pi:** GPIO-Trigger, Entprellung, monotone Zeitmessung, lokale SQLite-Queue, Retry mit Backoff, Kiosk-Anzeige mit Offline-Rückfall - SSE-Endpunkt und `/anzeige`-Seite - **Abnahmetest:** Netzstecker während der Messung ziehen — nach Wiederverbindung müssen alle Messungen genau einmal in der DB stehen. Ebenso: Batch doppelt senden → keine Dubletten. - **Kalibrierung:** gemessene Zeiten gegen eine Handstoppung/Referenz vergleichen, bevor produktiv damit gewertet wird ### AP 6 — Öffentliches Frontend - Layout, Navigation, responsive Tabellen - Termine, Ergebnisse, Fahrerliste, Fahrerprofil, Meisterschaft - iCal, CSV, Druckansichten ### AP 7 — Internes Frontend - Login/Rollen, Nennungsannahme, Ergebniserfassung, Auswertung, Korrekturen - Exporte (PDF-Ergebnisliste im ADAC-Layout, Schlussbericht) ### AP 8 — Datenübernahme Altbestand - Import der PDF-Ergebnisse ab einem zu definierenden Jahr (⚠️ die PDFs nutzen Subset-Fonts mit eigener Kodierung — reine Textextraktion schlägt bei den Meisterschafts-PDFs fehl; realistisch ist ein **halbautomatischer** Import mit manueller Nacharbeit, ggf. direkt aus den Excel-Originalen statt aus den PDFs) - Vereinheitlichung Vereinsnamen, Dublettenbereinigung Fahrer ### AP 9 — Deployment & Betrieb - systemd-Unit, nginx-vHost, TLS - Backup-Automatik + **getesteter Restore** - Monitoring, Logrotation - Einweisung der Auswerter, Kurzanleitung ### AP 10 — Pilotbetrieb - Eine Veranstaltung **parallel** zu Excel auswerten und Ergebnisse vergleichen - **Zeitmessung zunächst nur mitlaufen lassen**, nicht werten: Lichtschranken-Zeit gegen die manuell gestoppte Zeit stellen. Erst wenn beide über eine ganze Veranstaltung übereinstimmen, wird die Messung wertungsrelevant. - Erst nach fehlerfreiem Parallellauf umstellen --- ## 9. Ausbaustufen nach dem Kern - **Online-Voranmeldung** — die Rahmenausschreibung wünscht ausdrücklich Voranmeldung größerer Teilnehmergruppen und eine Anmeldeliste; das ist die naheliegendste Erweiterung - **Aufgaben-Einzelwertung** (Tabellen aus §4 bereits vorgesehen) → „woran verliere ich Zeit?" - Bildarchiv mit Verknüpfung Foto ↔ Fahrer ↔ Veranstaltung - Automatische Meldung von Schlussbericht/Ergebnisliste an den ADAC - Mehrmandantenfähigkeit für andere Ortsclubs (MSC Schotten, MSC Hainstadt, PMS Kassel …) - Übernahme der übrigen oamc.de-Rubriken aus dem Frameset --- ## 10. Offene Fragen | # | Frage | Warum blockierend | |---|---|---| | **O-1** | **Punktetabelle der Meisterschaft** (Platz → Punkte) | AP 3 nicht baubar | | **O-2** | Zuordnung `S 1`–`S 9` und `A 1`–`A 4` → Fahrzeugtyp/Hubraum/kW | Stammdaten-Seed | | **O-3** | Zählen **A-Fahrer** zur Meisterschaft? Die Rahmenausschreibung sagt „Gesamtklassement der A- und S-Fahrer/innen", die Spalte `Kategorie` ist bei A-Fahrern in der Ergebnisliste jedoch **leer** (bei S steht „S", bei Jugend „J") | AP 3 | | **O-4** | Gilt die Jugend-Wertung (nur Fehler) auch beim Bundesendlauf, oder dort Zeit+Fehler? | AP 2 | | **O-5** | Behandlung von DNF / DNS / Disqualifikation in Tages- und Jahreswertung | AP 2/3 | | ~~O-6~~ | ~~Wer soll die API bespielen?~~ → **beantwortet:** ein zweiter Raspberry Pi an der Lichtschranke, der Zeiten sendet und Starter-/Fahrzeugdaten anzeigt (§3.6) | erledigt | | **O-7** | **Ist am ADAC-Platz ein verlässliches Netz?** Wenn nein: muss der Anwendungs-Pi mit vor Ort? WLAN, LTE oder Kabel zwischen Mess-Pi und Server? | **hoch** — bestimmt die gesamte Betriebsarchitektur (§5.3) | | **O-8** | Ab welchem Jahr sollen Altdaten übernommen werden (PDFs reichen bis 2002 zurück)? | Aufwand AP 8 | | **O-9** | Wo wird gehostet — dieser Raspberry Pi, ein Server beim Verein, Webhosting? | AP 9, Erreichbarkeit | | **O-10** | Verhältnis zur bestehenden oamc.de: Unterseite, Subdomain (z. B. `mt.oamc.de`) oder Ablösung? | Navigation, DNS | | **O-11** | Wer bekommt Schreibrechte, wie viele Auswerter, gemeinsamer Account oder je Person? | AP 7 | | **O-12** | Liegen die Excel-Originale der Altveranstaltungen noch vor? | macht AP 8 um ein Vielfaches einfacher | ### Neue Fragen zur Zeitmessung | # | Frage | Warum blockierend | |---|---|---| | **O-13** | Was misst die Lichtschranke genau? **Eine** Gesamtdurchfahrt, **mehrere Durchgänge** oder einzelne Abschnitte (Langsamfahrstrecke …)? Woraus setzt sich `Summe Zeit` zusammen? | bestimmt `zeitmessung.messpunkt` und die Summenbildung; AP 5 | | **O-14** | **Woher weiß der Mess-Pi, wer gerade fährt?** Startnummer manuell eintippen, Warteschlange „nächster Starter" aus der Nennliste, Transponder/RFID? | kritischster Punkt der Zuordnung; AP 5 | | **O-15** | Was genau soll die **Anzeige** zeigen und auf welcher Hardware (HDMI-Monitor, kleines Display, LED-Tafel)? Für wen — Fahrer, Publikum, Wertungspersonal? | Gestaltung `/anzeige`; AP 5 | | **O-16** | Geforderte **Messgenauigkeit**? Die Listen führen 1/100 s. Reicht Userspace-GPIO oder braucht es dedizierte Hardware? Ein oder zwei Lichtschranken (Start **und** Ziel)? | Hardware-Auswahl, AP 5 | | **O-17** | **Wie kommen die Fehlerpunkte in die Anwendung** — Zettel vom Wertungspersonal und zentrale Eingabe, oder Tablets im Parcours? | AP 7; Phase 2 könnte hier ansetzen | | **O-18** | Löst die Lichtschranke **automatisch** aus (Fahrzeug unterbricht Strahl) oder gibt es zusätzlich einen manuellen Start-/Stopp-Taster als Rückfall? | Ausfallkonzept, AP 5 | | **O-19** | Wer baut/betreut die **Hardware** (Lichtschranke, Verkabelung, Stromversorgung am Platz)? Existiert sie schon oder wird sie beschafft? | Terminplanung | --- ## 11. Datenschutz **Das ist hier kein Formalpunkt.** Es werden **Namen, Geburtsjahre, Geschlecht und Vereinszugehörigkeit von Kindern ab 7 Jahren** verarbeitet und **öffentlich im Internet angezeigt**. Die heutige Praxis (Namen in öffentlichen PDFs) wird durch eine durchsuchbare Datenbank mit Personenprofilen **qualitativ verändert** — aus einer schwer auffindbaren PDF-Liste wird ein über Jahre verknüpftes Profil eines Minderjährigen. Das ist datenschutzrechtlich eine andere Kategorie und muss vor dem Livegang geklärt sein. Zu klären und umzusetzen: - **Rechtsgrundlage** je Verarbeitung; Einwilligung der Erziehungsberechtigten für die Veröffentlichung von Jugenddaten (idealerweise in Nennformular/Haftungsausschluss aufnehmen — die Formulare werden ohnehin unterschrieben) - **Datenminimierung öffentlich:** Geburts*jahr* statt Geburtsdatum, kein Wohnort, keine Kontaktdaten, keine ADAC-Mitgliedsnummer - **`robots.txt` / `noindex`** für Fahrerprofile — Ergebnislisten dürfen auffindbar sein, Personenprofile von Minderjährigen sollten es nicht sein - **Opt-out**: Anzeige als „Teilnehmer 12" oder Namenskürzel auf Wunsch - **Löschkonzept / Aufbewahrungsfristen**, Auskunfts- und Berichtigungsrecht - **Verzeichnis von Verarbeitungstätigkeiten**, TOMs (Backup-Verschlüsselung, TLS, Zugriffsrollen, Protokollierung) - Ggf. **Auftragsverarbeitung** mit dem Hoster > Empfehlung: Vor AP 6 (öffentliches Frontend) eine Entscheidung des Vorstands > herbeiführen, wie Jugenddaten öffentlich dargestellt werden. Die technische Umsetzung > ist einfach — die Entscheidung ist es nicht, und sie nachträglich zu ändern bedeutet, > bereits indexierte Seiten wieder einzufangen. --- ## 12. Risiken | Risiko | Auswirkung | Gegenmaßnahme | |---|---|---| | Wertungsregeln in §3.3 sind aus PDFs **abgeleitet**, nicht bestätigt | falsche Platzierungen, Vertrauensverlust | AP 0: Turnierordnung beschaffen; AP 2: Regressionstest gegen echte Listen; AP 10: Parallellauf | | Ausfall am Turniertag (Pi, Netz, Strom) | Veranstaltung blockiert | Excel-Fallback bereithalten, bis der Parallellauf sauber ist; Export vor Ort möglich halten | | **Lichtschranke fällt aus oder misst falsch** | Zeiten des laufenden Turniers fehlen — **nicht nachholbar**, die Fahrt ist vorbei | manuelle Zeitnahme als Rückfall **immer** parallel bereithalten; manuelle Nacherfassung im Frontend; Plausibilitätsprüfung serverseitig; AP 10 misst zunächst nur mit | | **Fehlauslösung** durch Zuschauer/Helfer/rangierendes Motorrad | falsche Zeit im Ergebnis | Entprellung auf dem Pi, Verwerfen-Funktion mit Grund, Plausibilitätsgrenzen, Vier-Augen-Prinzip vor „final" | | **Messung dem falschen Fahrer zugeordnet** | zwei Ergebnisse gleichzeitig falsch, schwer zu bemerken | Zuordnung (O-14) früh und sauber klären; Anzeige am Parcours macht die Zuordnung für alle sichtbar → Fehler fällt sofort auf | | Netzausfall zwischen Mess-Pi und Server | Messungen gehen verloren | lokale Queue + Idempotenz (§3.6c); Abnahmetest in AP 5 zieht bewusst den Netzstecker | | Altdaten-Import aus PDFs scheitert an Font-Kodierung | AP 8 wird teuer | Excel-Originale beschaffen (O-12); Import zeitlich begrenzen; notfalls Start ab 2026 | | Fahrer-Dubletten durch Namensvarianten | falsche Meisterschaftsstände | Matching + Merge-Werkzeug in AP 4, Import-Report mit „NEU"-Markierung | | Datenschutzthema Jugenddaten wird spät geklärt | Livegang verzögert oder Rückbau | §11 vor AP 6 entscheiden | | Projekt hängt an einer Person | keine Wartung | Doku, sauberes Repo, zweite eingewiesene Person | | Raspberry Pi: SD-Karte/Speicherausfall | Datenverlust | Backup extern + Restore testen, SSD statt SD-Karte erwägen; auf dem Mess-Pi read-only Root-FS erwägen | | Scope-Ausweitung (Online-Nennung, Bildarchiv …) | Kern wird nie fertig | §9 ist bewusst *nach* dem Kern; AP 10 ist der Zielpunkt | --- ## 13. Nächster Schritt 1. Diesen Plan im Verein durchgehen, insbesondere **§10 (offene Fragen)** und **§11 (Datenschutz)** 2. **ADAC-Turnierordnung 2026 und Punktetabelle beschaffen** — ohne die stehen AP 2 und AP 3 3. **Zeitmessung klären: O-13 (was wird gemessen), O-14 (Zuordnung zum Fahrer), O-16 (Genauigkeit/Hardware), O-19 (wer baut sie)** — davon hängt AP 5 komplett ab 4. Entscheidung zu **O-7** (Netz am Platz) und **O-9/O-10** (Hosting, Domain) 5. Danach AP 0 → AP 1 → AP 2 > **Reihenfolge-Hinweis:** Die Zeitmessung ist das sichtbarste, aber nicht das erste > Arbeitspaket. Sie liefert nur die Hälfte eines Ergebnisses (§3.6) und braucht Turnier, > Startliste und Wertungslogik als Unterbau. AP 1–3 zuerst, dann AP 5 — sonst hat der > Mess-Pi nichts, wogegen er Startnummern auflösen könnte. --- ### Quellen - `www.oamc.de` — Startseite, Navigation, Rubrik Motorradgruppe (`m/m1.htm`, `m/m2.htm`, `m/m-sportbeschreibung.htm`), Downloadseite `d/d-mot-turnier.htm` - `www.oamc.de/datei/2026-05-31-MT-REIN-HAIN.PDF` — Tagesergebnis Doppelturnier MSC Hainstadt / OAMC Reinheim, 13 Wertungsblöcke; Grundlage für §3.3 - `www.oamc.de/datei/MT-Rahmenausschreibung-2026.pdf` (Stand 1.2.2026) — Nenngeld, Nennschluss, Streichresultate, Jugend-Klasseneinteilung, Endlaufqualifikation - Systemprüfung auf dem Zielgerät (Raspberry Pi 4B) für §5.1