Files
demo-epb/tools/generate_doorstop_items.py
T
Stefan Lohmaier c610cc023c
Validate / build-test (macos-latest) (push) Failing after 4s
Validate / build-test (windows-latest) (push) Failing after 17s
Validate / build-test (ubuntu-latest) (push) Successful in 16s
Validate / reports (push) Has been skipped
Release / release (push) Successful in 48s
feat: Safety Goals + Drive-Away-Assist + vollst. Traceability
Neue Layer:
- safety/sg/SG-001..005 als eigene Doorstop-Items (ASIL D/D/A/C/B)
- SYS-Reqs verlinken nach oben auf SG via frontmatter
- Kette ist jetzt: SG -> SYS -> SA, SWE -> SWA -> Code (@arch) + Test (@reqs)

Drive-Away-Assist im Safety Manager:
- SWE-011 (Anfahrabsicht erkennen) implementiert
- SWE-012 (Sicherheits-Check Tuer + Gurt) implementiert
- Neuer State SAFETY_DRIVE_AWAY + safety_mgr_release_requested()
- SafetyInputs erweitert um gas_pedal_percent, gear_in_drive,
  door_closed, seatbelt_fastened
- 5 neue Tests (DRIVE_AWAY armed/blocked/end-conditions)
- Test-Header @reqs erweitert auf SWE-007..012

traceability.py erweitert:
- SG als neuer Top-Level
- Code-Mapping-Check: @arch im Header von src/*.c muss SWA-id matchen
- Test-Mapping-Check: @reqs im Header der Tests muss alle SWE der
  zugehoerigen SWA abdecken
- HTML zeigt 7 Spalten: SG | SYS | SA | SWE | SWA | Code | Test
- 2 zusaetzliche Tabellen: Code->Arch und Test->Reqs

test_apply_controller.c:
- @reqs Header um SWE-005 ergaenzt (war funktional drin, nur Tag fehlte)

Counts:
- 55 doorstop-Items (war 50)
- 46 Unit-Tests (war 41)
- Traceability vollstaendig in beide Richtungen
2026-05-12 01:50:12 -07:00

876 lines
31 KiB
Python

#!/usr/bin/env python3
"""
Generate Doorstop-compatible Markdown items for the EPB demo.
Source of truth: the dict EPB_DATA below.
Outputs to: reqs/sys/, reqs/swe/, arch/sys/, arch/swe/
Each output file uses Doorstop's Markdown mode (YAML frontmatter + body).
Run: python3 tools/generate_doorstop_items.py
"""
from __future__ import annotations
import textwrap
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
# ---------------------------------------------------------------------------
# Safety Goals (ISO 26262, abgeleitet aus HARA)
# ---------------------------------------------------------------------------
SG_GOALS = [
{
"id": "SG-001", "asil": "D",
"title": "Kein ungewolltes Loesen der Parkbremse im Stillstand",
"text": (
"Die EPB darf sich im Stillstand des Fahrzeugs nicht ungewollt loesen. "
"Abgeleitet aus HARA-Hazards H-01 (ungewolltes Loesen, Parkphase) und "
"H-04 (Klemmkraftverlust im Hold).\n\n"
"**FTTI:** 5 s (H-01) / 30 s (H-04).\n"
"**Safe State:** APPLIED (Klemmkraft halten)."
),
},
{
"id": "SG-002", "asil": "D",
"title": "Kein ungewolltes Festklemmen waehrend der Fahrt",
"text": (
"Die EPB darf nicht waehrend der Fahrt ungewollt festklemmen. "
"Abgeleitet aus HARA-Hazard H-02.\n\n"
"**FTTI:** 100 ms.\n"
"**Safe State:** Aktor stop (kein Apply einleiten)."
),
},
{
"id": "SG-003", "asil": "A",
"title": "Schutz gegen Aktor-Ueberlast",
"text": (
"Das System muss Aktor-Motorschaeden durch Ueberstrom verhindern. "
"Abgeleitet aus HARA-Hazard H-05.\n\n"
"**FTTI:** 100 ms.\n"
"**Safe State:** Aktor abschalten, DTC setzen."
),
},
{
"id": "SG-004", "asil": "C",
"title": "Zuverlaessige Hill-Hold-Uebergabe",
"text": (
"Beim Loslassen des Bremspedals an einem Hang muss die EPB die "
"Bremskraft uebernehmen, bevor das Fahrzeug zu rollen beginnt. "
"Abgeleitet aus HARA-Hazard H-06.\n\n"
"**FTTI:** 500 ms.\n"
"**Safe State:** Apply einleiten."
),
},
{
"id": "SG-005", "asil": "B",
"title": "Reaktion auf Fahreranforderung",
"text": (
"Das System muss in spezifizierter Zeit auf Fahrer-Apply- und Release-"
"Anforderungen reagieren. Abgeleitet aus HARA-Hazards H-03 und H-07.\n\n"
"**Reaktionszeit:** Apply <= 800 ms, Release <= 1500 ms."
),
},
]
# ---------------------------------------------------------------------------
# System Requirements (linken nach oben auf SG)
# ---------------------------------------------------------------------------
SYS_REQS = [
{
"id": "SYS-001", "asil": "D",
"links": ["SG-001"],
"title": "Halten der Parkbremse im Stillstand",
"text": (
"Wenn die Parkbremse aktiviert ist und das Fahrzeug stillsteht, "
"muss das EPB-System die mechanische Klemmkraft an beiden hinteren "
"Bremssaetteln aufrecht erhalten, bis ein Loesen ausdruecklich "
"angefordert wird. Sicherheitsziel: SG-01.\n\n"
"**Verifikation:** SiL-Test mit Auf-/Ab-Hangelung, Klemmkraftmessung."
),
},
{
"id": "SYS-002", "asil": "D",
"links": ["SG-002", "SG-005"],
"title": "Apply auf Fahrer-Anforderung",
"text": (
"Bei Betaetigung des EPB-Schalters in Apply-Richtung muss das "
"System innerhalb von 800 ms die Parkbremse anlegen, sofern die "
"Voraussetzungen erfuellt sind (Stillstand oder Geschwindigkeit "
"unter 5 km/h). Sicherheitsziel: SG-01."
),
},
{
"id": "SYS-003", "asil": "B",
"links": ["SG-005"],
"title": "Release auf Fahrer-Anforderung",
"text": (
"Bei Betaetigung des EPB-Schalters in Release-Richtung muss das "
"System die Parkbremse loesen, sofern die folgenden Voraussetzungen "
"erfuellt sind: Motor laeuft, Fahrer betaetigt Bremspedal, Gang "
"ist eingelegt. Maximalzeit fuer Loesen: 1500 ms."
),
},
{
"id": "SYS-004", "asil": "D",
"links": ["SG-001"],
"title": "Auto-Apply bei Motor-Aus",
"text": (
"Wenn der Motor ausgeschaltet wird und das Fahrzeug stillsteht "
"und keine Parkbremse aktiv ist, muss das System die Parkbremse "
"spaetestens 2 s nach Erkennung Motor-Aus automatisch anlegen. "
"Sicherheitsziel: SG-01."
),
},
{
"id": "SYS-005", "asil": "D",
"links": ["SG-002", "SG-004"],
"title": "Hill-Hold am Berg",
"text": (
"Bei aktivem Hill-Hold (Fahrzeug steht am Hang mit Neigung > 5%, "
"Fahrer betaetigt Bremspedal) uebernimmt das EPB-System die "
"Bremskraft beim Loesen des Bremspedals und haelt diese, bis die "
"Anfahrt erkannt wird. Sicherheitsziel: SG-01."
),
},
{
"id": "SYS-006", "asil": "B",
"links": ["SG-004"],
"title": "Auto-Release beim Anfahren (Drive-Away-Assist)",
"text": (
"Wenn die Parkbremse aktiv ist und der Fahrer Anfahrabsicht zeigt "
"(Gaspedal-Betaetigung bei eingelegtem Gang), muss das System die "
"Parkbremse innerhalb von 500 ms loesen. Voraussetzung: alle "
"Sicherheitskriterien (Fahrertuer geschlossen, Sicherheitsgurt) "
"erfuellt."
),
},
{
"id": "SYS-007", "asil": "B",
"links": ["SG-003"],
"title": "Aktor-Stromueberwachung",
"text": (
"Das System muss den Motorstrom jedes Aktors mit mindestens 1 kHz "
"ueberwachen und bei Ueberschreitung von 8 A fuer mehr als 100 ms "
"den Aktor abschalten und einen DTC setzen. Sicherheitsziel: SG-03."
),
},
{
"id": "SYS-008", "asil": "QM",
"title": "Service-Modus fuer Werkstatt",
"text": (
"Das System muss ueber UDS RoutineControl (Service 0x31) einen "
"Service-Modus bereitstellen, in dem die Aktoren manuell in "
"Wartungs-Position gefahren werden koennen (z.B. fuer Bremsbelag-"
"Wechsel)."
),
},
{
"id": "SYS-009", "asil": "QM",
"title": "UDS-Diagnose",
"text": (
"Das System muss UDS-Diagnose nach ISO 14229 bereitstellen: "
"ReadDTC (0x19), ReadDataByIdentifier (0x22), RoutineControl (0x31), "
"ECUReset (0x11). Tester-Adresse 0x712, Antwort-Adresse 0x71A."
),
},
{
"id": "SYS-010", "asil": "QM",
"title": "HMI-Statusanzeige",
"text": (
"Der EPB-Status muss dem Fahrer signalisiert werden: LED am "
"Schalter (an = Apply, aus = Release, blinkend = Fehler) sowie "
"Text im Kombi-Display via CAN-Bus (Frame-ID 0x3A0, 50 Hz)."
),
},
]
# ---------------------------------------------------------------------------
# Software Requirements (each links to one or more SYS reqs)
# ---------------------------------------------------------------------------
SWE_REQS = [
# SYS-001 — Halten
{"id": "SWE-001", "asil": "D", "links": ["SYS-001"],
"title": "Apply-Controller haelt Klemmkraft",
"text": "Der Apply-Controller muss die Klemmkraft im Hold-Zustand alle 50 ms "
"verifizieren und bei Abweichung > 10% nachregeln."},
{"id": "SWE-002", "asil": "D", "links": ["SYS-001"],
"title": "Watchdog ueberwacht Apply-Controller",
"text": "Ein unabhaengiger Watchdog muss die Liveness des Apply-Controllers "
"mit 100 ms Timeout ueberwachen und bei Ausbleiben in den sicheren "
"Zustand (Apply) gehen."},
# SYS-002 — Apply
{"id": "SWE-003", "asil": "D", "links": ["SYS-002"],
"title": "Schalter-Apply-Signal an Apply-Controller weiterleiten",
"text": "Das Software-Modul Switch-Debouncer muss ein entprelltes "
"Apply-Signal innerhalb von 50 ms an den Apply-Controller liefern."},
{"id": "SWE-004", "asil": "D", "links": ["SYS-002"],
"title": "Klemmkraft-Erreichen bestaetigen",
"text": "Der Apply-Controller muss das Erreichen der Ziel-Klemmkraft via "
"Strommessung erkennen und ein Status-Flag setzen."},
# SYS-003 — Release
{"id": "SWE-005", "asil": "B", "links": ["SYS-003"],
"title": "Release-Voraussetzungen pruefen",
"text": "Vor jedem Release muss der Apply-Controller pruefen: Motor laeuft, "
"Bremspedal betaetigt, Gang ist eingelegt. Andernfalls Release abweisen."},
{"id": "SWE-006", "asil": "B", "links": ["SYS-003"],
"title": "Aktoren in Release-Position fahren",
"text": "Der Actuator-Driver muss beide Aktoren parallel in Release-Position "
"fahren. Maximalzeit: 1200 ms. Bei Timeout DTC setzen."},
# SYS-004 — Auto-Apply
{"id": "SWE-007", "asil": "D", "links": ["SYS-004"],
"title": "Motor-Aus-Bedingung erkennen",
"text": "Der Safety-Manager muss erkennen: Motor-Status = aus, "
"Geschwindigkeit < 0.5 km/h. Auswertezyklus 50 ms."},
{"id": "SWE-008", "asil": "D", "links": ["SYS-004"],
"title": "Auto-Apply nach 2 s Verzoegerung",
"text": "Ist die Motor-Aus-Bedingung 2 s stabil erfuellt und Parkbremse "
"noch nicht aktiv, muss der Safety-Manager Apply-Anforderung an "
"den Apply-Controller senden."},
# SYS-005 — Hill-Hold
{"id": "SWE-009", "asil": "D", "links": ["SYS-005"],
"title": "Hill-Hold-Aktivierungsbedingung",
"text": "Der Safety-Manager muss Hill-Hold aktivieren, wenn Neigung "
"(gefiltert) > 5%, Geschwindigkeit < 0.5 km/h und Bremspedal "
"betaetigt sind."},
{"id": "SWE-010", "asil": "D", "links": ["SYS-005"],
"title": "Hill-Hold-Uebergabe an Apply-Controller",
"text": "Wird das Bremspedal bei aktivem Hill-Hold losgelassen, muss der "
"Safety-Manager unmittelbar Apply-Anforderung an den Apply-"
"Controller senden, bevor das Fahrzeug zu rollen beginnen kann."},
# SYS-006 — Auto-Release
{"id": "SWE-011", "asil": "B", "links": ["SYS-006"],
"title": "Anfahrabsicht erkennen",
"text": "Anfahrabsicht ist erkannt, wenn: Gaspedal > 10%, Gang in Vorwaerts "
"oder Rueckwaerts, Motor laeuft."},
{"id": "SWE-012", "asil": "B", "links": ["SYS-006"],
"title": "Sicherheits-Check vor Auto-Release",
"text": "Vor Auto-Release muessen erfuellt sein: Fahrertuer geschlossen, "
"Sicherheitsgurt angelegt. Andernfalls warnen und nicht loesen."},
# SYS-007 — Aktor-Strom
{"id": "SWE-013", "asil": "B", "links": ["SYS-007"],
"title": "Strommessung mit 1 kHz",
"text": "Der Actuator-Driver muss den Motorstrom jedes Aktors mit "
"mindestens 1 kHz abtasten. Genauigkeit +/- 100 mA."},
{"id": "SWE-014", "asil": "B", "links": ["SYS-007"],
"title": "Overcurrent-Cutoff",
"text": "Bei Motorstrom > 8 A laenger als 100 ms muss der Actuator-Driver "
"den Motor abschalten und einen DTC P0xxx setzen."},
{"id": "SWE-015", "asil": "B", "links": ["SYS-007"],
"title": "Klemmkraft-Schaetzung aus Strom-Profil",
"text": "Der Actuator-Driver muss die erreichte Klemmkraft aus dem "
"Stromverlauf bei Apply schaetzen (Modell: F = k * I_peak)."},
# SYS-008 — Service-Modus
{"id": "SWE-016", "asil": "QM", "links": ["SYS-008"],
"title": "UDS RoutineControl 0x31 fuer Service-Release",
"text": "Service-Mode wird ueber UDS RoutineControl Service 0x31, "
"Routine-ID 0x0301 aktiviert. Bedingung: Fahrzeug muss stillstehen."},
{"id": "SWE-017", "asil": "QM", "links": ["SYS-008"],
"title": "Service-Mode-Indikator",
"text": "Im Service-Mode muss die EPB-LED am Schalter mit 2 Hz blinken."},
# SYS-009 — UDS
{"id": "SWE-018", "asil": "QM", "links": ["SYS-009"],
"title": "UDS Service 0x19 ReadDTC",
"text": "Das System muss alle gespeicherten DTCs ueber Service 0x19 "
"(Subfunktion 0x02 reportDTCByStatusMask) ausgeben."},
{"id": "SWE-019", "asil": "QM", "links": ["SYS-009"],
"title": "UDS Service 0x22 ReadDataByIdentifier",
"text": "Folgende DIDs muessen lesbar sein: 0xF187 (SW-Version), "
"0x0301 (Klemmkraft links), 0x0302 (Klemmkraft rechts)."},
# SYS-010 — HMI
{"id": "SWE-020", "asil": "QM", "links": ["SYS-010"],
"title": "LED-Steuerung",
"text": "Apply-aktiv: LED dauerleuchtend. Release: LED aus. Fehler: "
"LED blinkt 4 Hz. Service-Mode: LED blinkt 2 Hz."},
{"id": "SWE-021", "asil": "QM", "links": ["SYS-010"],
"title": "CAN-Status-Frame",
"text": "Status-Frame 0x3A0 mit 50 Hz: Byte 0 = Status (0=Released, 1=Applied, "
"2=Applying, 3=Releasing, 0xFF=Error), Byte 1-2 = Klemmkraft links, "
"Byte 3-4 = Klemmkraft rechts."},
# Sensorik & Plausibilisierung
{"id": "SWE-022", "asil": "B", "links": ["SYS-001", "SYS-002", "SYS-006"],
"title": "Stillstands-Erkennung aus Wheel Speeds",
"text": "Stillstand ist erkannt, wenn alle 4 Wheel-Speed-Signale fuer "
"mindestens 200 ms unter 0.5 km/h liegen."},
{"id": "SWE-023", "asil": "B", "links": ["SYS-007"],
"title": "Wheel Speed Plausibilisierung",
"text": "Spreizung der Wheel-Speed-Signale: bei Geradeaus-Fahrt darf die "
"Differenz nicht > 3 km/h sein. Andernfalls Sensor-Fehler-DTC."},
{"id": "SWE-024", "asil": "B", "links": ["SYS-005"],
"title": "Inclinometer Tiefpass-Filter",
"text": "Das Roh-Neigungssignal muss mit einem Tiefpass 1. Ordnung "
"(Zeitkonstante 200 ms) gefiltert werden, bevor es zur Hill-Hold-"
"Bewertung verwendet wird."},
{"id": "SWE-025", "asil": "QM", "links": ["SYS-002", "SYS-003"],
"title": "Switch-Debouncing",
"text": "Der EPB-Schalter muss mit einer Entprell-Zeit von 50 ms "
"entprellt werden. Stabiler Pegel = Eingangssignal fuer "
"Apply-Controller."},
]
# ---------------------------------------------------------------------------
# System Architecture Elements
# ---------------------------------------------------------------------------
SA_ELEMENTS = [
{
"id": "SA-001", "links": ["SYS-001", "SYS-002", "SYS-003", "SYS-004",
"SYS-005", "SYS-006", "SYS-007", "SYS-008",
"SYS-009", "SYS-010"],
"title": "EPB ECU",
"asil": "D",
"text": textwrap.dedent("""
## Verantwortung
Zentrales Steuergeraet der elektrischen Parkbremse. Beinhaltet alle Software-
Komponenten und die elektronische Ansteuerung der Aktoren.
## System-Kontext
```plantuml
@startuml
node "EPB ECU" as ECU
node "Aktor links" as AL
node "Aktor rechts" as AR
node "Wheel Speed Sensoren (x4)" as WS
node "Inclinometer" as IN
node "EPB-Schalter + LED" as SW
node "CAN-Bus" as CAN
node "Kombi-Display" as DI
node "OBD-Tester" as OBD
ECU --> AL : PWM, I-Mess
ECU --> AR : PWM, I-Mess
WS --> ECU : Pulse
IN --> ECU : SPI
SW --> ECU : GPIO
ECU --> SW : LED
ECU <-> CAN
CAN <-> DI
CAN <-> OBD
@enduml
```
## Schnittstellen
| Schnittstelle | Typ | Richtung |
|---------------|----------------|----------|
| Aktor L/R | PWM + Shunt | I/O |
| Wheel Speed | Hall-Pulse | In |
| Inclinometer | SPI | In |
| Schalter | GPIO debounced | In |
| LED | GPIO | Out |
| CAN | ISO 11898 | I/O |
## Subkomponenten (Aufteilung auf SW)
Realisiert in Software: alle SWA-Elemente SWA-001..SWA-010.
## Nichtfunktionale Eigenschaften
- Worst-Case Reaktionszeit (Schalter → Aktor-Bewegung): 250 ms
- Flash-Bedarf: < 256 KB
- RAM-Bedarf: < 32 KB
- Stromaufnahme: < 200 mA (Standby) / < 30 A (Aktor-Spitze)
""").strip(),
},
{
"id": "SA-002", "links": ["SYS-001", "SYS-002", "SYS-003", "SYS-007"],
"title": "Aktoren (Caliper-Motoren)",
"asil": "D",
"text": textwrap.dedent("""
## Verantwortung
Zwei elektromechanische Aktoren an den hinteren Bremssaetteln klemmen
und loesen die Bremsbelaege. Geliefert (Annahme): kommerzielles Bauteil
eines Tier-1-Lieferanten.
## Schnittstellen
| Schnittstelle | Typ | Bemerkung |
|---------------|--------------|-----------------------------------|
| Power | 12 V, PWM | bidirektional fuer Apply/Release |
| Strom-Shunt | Analog | wird in der ECU abgegriffen |
## Nichtfunktionale Eigenschaften
- Max. Klemmkraft: 20 kN
- Apply-Zeit (0 → max): 600 ms
- Strom (nominal): 4 A
- Strom (Spitze): 30 A (kurzzeitig)
- Temperaturbereich: -40°C bis +85°C
""").strip(),
},
{
"id": "SA-003", "links": ["SYS-005", "SYS-006", "SYS-007"],
"title": "Sensor-Cluster",
"asil": "B",
"text": textwrap.dedent("""
## Verantwortung
Zusammenfassung aller fuer die EPB benoetigten Eingangssignale:
Wheel-Speed-Sensoren (4x), Inclinometer (1x), EPB-Schalter, Bremspedal-
Status, Gear-Position, Door-Open, Seat-Belt — die letzten vier per CAN.
## Schnittstellen
| Sensor | Typ | Quelle |
|-----------------|------------------|--------------|
| Wheel Speed x4 | Hall-Pulse | direkt |
| Inclinometer | SPI 1 kHz | direkt |
| EPB-Schalter | GPIO | direkt |
| Bremspedal | CAN 0x100 | aus BCM |
| Gear | CAN 0x110 | aus TCU |
| Door / Belt | CAN 0x120 | aus BCM |
## Nichtfunktionale Eigenschaften
- Wheel-Speed-Genauigkeit: +/- 0.1 km/h ab 1 km/h
- Inclinometer-Genauigkeit: +/- 0.5°
- Sampling-Frequenz Inclinometer: 100 Hz
""").strip(),
},
{
"id": "SA-004", "links": ["SYS-008", "SYS-010"],
"title": "HMI (Schalter, LED, Display)",
"asil": "QM",
"text": textwrap.dedent("""
## Verantwortung
Fahrer-Interaktion und -Information: Tippschalter mit integrierter LED,
Statusanzeige im Kombi-Display via CAN.
## Schnittstellen
| Element | Typ | Verhalten |
|---------------|----------|--------------------------------------------|
| Tippschalter | GPIO | Apply-Richtung / Release-Richtung |
| LED | GPIO | aus / an / blink 2 Hz / blink 4 Hz |
| Display | CAN 0x3A0 | 50 Hz Status-Frame |
""").strip(),
},
{
"id": "SA-005", "links": ["SYS-009", "SYS-010"],
"title": "CAN-Bus",
"asil": "QM",
"text": textwrap.dedent("""
## Verantwortung
Kommunikations-Backbone fuer Eingangsdaten (Bremspedal, Gang, Tuer, Gurt),
Ausgabe (Status-Frame an Display) und Diagnose (UDS auf Tester-Adresse).
## Schnittstellen
- Baudrate: 500 kbit/s, CAN 2.0B
- Empfangene Frames: 0x100 (Bremspedal), 0x110 (Gang), 0x120 (Door/Belt),
0x712 (UDS-Request)
- Gesendete Frames: 0x3A0 (Status 50 Hz), 0x71A (UDS-Response)
""").strip(),
},
]
# ---------------------------------------------------------------------------
# Software Architecture Elements
# ---------------------------------------------------------------------------
SWA_ELEMENTS = [
{
"id": "SWA-001", "asil": "D",
"links": ["SWE-007", "SWE-008", "SWE-009", "SWE-010", "SWE-011", "SWE-012"],
"title": "Safety Manager",
"text": textwrap.dedent("""
## Verantwortung
Hoechste Sicherheitsschicht. Erkennt Motor-Aus, aktiviert Hill-Hold,
triggert Auto-Apply. Lebenswichtige Logik mit redundanter Pruefung.
## Statische Sicht
```plantuml
@startuml
package "Safety Manager" {
[Engine State Monitor]
[Hill-Hold Logic]
[Auto-Apply Logic]
}
[Safety Manager] ..> [Apply Controller] : Apply-Anforderung
[Wheel Speed Plausi] --> [Safety Manager] : v_vehicle
[Inclinometer Filter] --> [Safety Manager] : grade
@enduml
```
## Schnittstellen (Provided)
```c
Status safety_mgr_init(void);
void safety_mgr_step_50ms(const SafetyInputs* in);
```
## Dynamisches Verhalten
```plantuml
@startuml
[*] --> Idle
Idle --> HillHoldArmed : grade>5% & v=0 & brake
HillHoldArmed --> HillHoldActive : brake released
HillHoldActive --> Idle : v>2 km/h
Idle --> AutoApplyArmed : engine_off & v=0
AutoApplyArmed --> AutoApplyTriggered : t>=2s
AutoApplyTriggered --> Idle : applied
@enduml
```
## Ressourcen
- Stack: <= 256 B
- Worst-Case Timing: 200 us / Aufruf
## Mapping auf Anforderungen
| Anforderung | Wie abgedeckt |
|-------------|---------------|
| SWE-007 | engine_off + v<0.5 in step_50ms |
| SWE-008 | 2s-Filter und Trigger |
| SWE-009 | Hill-Hold-Aktivierung |
| SWE-010 | Brake-Released-Detektion |
""").strip(),
},
{
"id": "SWA-002", "asil": "D",
"links": ["SWE-001", "SWE-002", "SWE-003", "SWE-004", "SWE-005"],
"title": "Apply Controller",
"text": textwrap.dedent("""
## Verantwortung
Zentraler Controller fuer Apply, Hold und Release der Parkbremse.
ASIL-D-Kern der EPB-Software. Implementiert in `src/apply_controller.c`.
## Statische Sicht
```plantuml
@startuml
[Apply Controller] --> [Actuator Driver L] : apply/release
[Apply Controller] --> [Actuator Driver R] : apply/release
[Switch Debouncer] --> [Apply Controller] : sw_apply, sw_release
[Safety Manager] --> [Apply Controller] : auto_apply, hill_hold_request
[Apply Controller] --> [Display Manager] : status
[Apply Controller] <-- [Watchdog] : alive_check
@enduml
```
## Schnittstellen (Provided)
```c
Status apply_ctrl_init(void);
void apply_ctrl_step_50ms(const ApplyInputs* in);
EpbStatus apply_ctrl_get_status(void);
```
## Dynamisches Verhalten
```plantuml
@startuml
[*] --> Released
Released --> Applying : apply_request & v_low
Applying --> Applied : current_target_reached
Applied --> Releasing : release_request & preconditions_ok
Applied --> Applied : 50ms hold check (re-clamp if needed)
Releasing --> Released : release_complete
Applying --> Error : timeout > 1500ms
Releasing --> Error : timeout > 1200ms
Error --> Released : reset & no fault
@enduml
```
## Ressourcen
- Stack: <= 384 B
- Worst-Case Timing: 350 us / Aufruf
## Designentscheidungen
| Entscheidung | Begruendung |
|--------------|-------------|
| Statische Allokation, kein Heap | Determinismus, MISRA C 21.3 |
| State Machine | Einfacher zu verifizieren, deterministisch |
| 50ms Step-Funktion | Synchron zur Inclinometer-Abtastung |
## Mapping auf Anforderungen
| Anforderung | Wie abgedeckt |
|-------------|---------------|
| SWE-001 | Hold-Zustand mit periodischer Klemmkraft-Pruefung |
| SWE-002 | Watchdog-Pet im step_50ms |
| SWE-003 | sw_apply Input wird sofort ausgewertet |
| SWE-004 | Current-Target-Detektion via Actuator-Driver-Feedback |
""").strip(),
},
{
"id": "SWA-003", "asil": "B",
"links": ["SWE-006", "SWE-013", "SWE-014", "SWE-015"],
"title": "Actuator Driver",
"text": textwrap.dedent("""
## Verantwortung
Low-Level-Ansteuerung der beiden Aktor-Motoren. PWM-Generierung,
Strom-Messung, Overcurrent-Cutoff, Klemmkraft-Schaetzung.
Implementiert in `src/actuator_driver.c`.
## Statische Sicht
```plantuml
@startuml
[Apply Controller] --> [Actuator Driver]
[Actuator Driver] --> [Hardware PWM] : pwm_set
[Actuator Driver] <-- [Hardware ADC] : current_sample
[Actuator Driver] --> [Diagnostic Manager] : DTC
@enduml
```
## Schnittstellen (Provided)
```c
Status actuator_init(void);
void actuator_apply(ActuatorId id, uint8_t pwm_percent);
void actuator_release(ActuatorId id, uint8_t pwm_percent);
void actuator_stop(ActuatorId id);
ActuatorStatus actuator_get_status(ActuatorId id);
void actuator_isr_1khz(void); // Strom-Sampling
```
## Ressourcen
- Stack: <= 256 B
- Worst-Case Timing: 50 us / ISR
- Static RAM: 64 B pro Aktor
## Mapping auf Anforderungen
| Anforderung | Wie abgedeckt |
|-------------|---------------|
| SWE-006 | actuator_release fuer beide Aktoren parallel |
| SWE-013 | actuator_isr_1khz |
| SWE-014 | Overcurrent-Detektor in ISR |
| SWE-015 | Peak-Current-Tracking + lineare Klemmkraft-Schaetzung |
""").strip(),
},
{
"id": "SWA-004", "asil": "B",
"links": ["SWE-022", "SWE-023"],
"title": "Wheel Speed Plausibilisierung",
"text": textwrap.dedent("""
## Verantwortung
Aufbereitung und Plausibilisierung der 4 Wheel-Speed-Signale. Erkennt
Stillstand und plausibilisiert untereinander.
## Schnittstellen (Provided)
```c
Status wheel_speed_init(void);
void wheel_speed_step_10ms(const WheelInputs* in);
bool wheel_speed_is_standstill(void);
float wheel_speed_get_vehicle(void);
```
""").strip(),
},
{
"id": "SWA-005", "asil": "B",
"links": ["SWE-024"],
"title": "Inclinometer Filter",
"text": textwrap.dedent("""
## Verantwortung
Tiefpass-Filterung des Inclinometer-Roh-Signals fuer die Hill-Hold-Bewertung.
## Schnittstellen (Provided)
```c
Status inclino_init(void);
void inclino_step_10ms(int16_t raw_mdeg);
float inclino_get_grade_percent(void);
```
""").strip(),
},
{
"id": "SWA-006", "asil": "QM",
"links": ["SWE-025"],
"title": "Switch Debouncer",
"text": textwrap.dedent("""
## Verantwortung
Software-Entprellung des EPB-Schalters. Liefert stabiles Apply / Release
Signal an den Apply-Controller. Implementiert in `src/switch_debouncer.c`.
## Schnittstellen (Provided)
```c
Status switch_init(void);
void switch_step_10ms(SwitchRaw raw);
SwitchState switch_get_state(void);
```
## Mapping auf Anforderungen
| Anforderung | Wie abgedeckt |
|-------------|---------------|
| SWE-025 | 50ms Debounce-Logik |
""").strip(),
},
{
"id": "SWA-007", "asil": "QM",
"links": ["SWE-020", "SWE-021"],
"title": "Display Manager",
"text": textwrap.dedent("""
## Verantwortung
Steuert LED am EPB-Schalter und CAN-Status-Frame an das Kombi-Display.
Empfaengt Status vom Apply-Controller.
## Schnittstellen (Provided)
```c
Status display_init(void);
void display_set_status(EpbStatus s);
void display_step_20ms(void); // 50 Hz CAN-Frame
```
""").strip(),
},
{
"id": "SWA-008", "asil": "QM",
"links": ["SWE-018", "SWE-019"],
"title": "Diagnostic Manager",
"text": textwrap.dedent("""
## Verantwortung
UDS-Diagnose nach ISO 14229: ReadDTC, ReadDataByIdentifier, RoutineControl.
## Schnittstellen (Provided)
```c
Status diag_init(void);
void diag_handle_request(const uint8_t* data, uint16_t len);
void diag_set_dtc(uint16_t dtc_id);
```
""").strip(),
},
{
"id": "SWA-009", "asil": "QM",
"links": ["SWE-016", "SWE-017"],
"title": "Service Mode",
"text": textwrap.dedent("""
## Verantwortung
Service-Modus fuer Werkstatt. Wird ueber UDS RoutineControl 0x31, Routine-ID
0x0301 aktiviert. Steuert Aktoren in Wartungsposition.
""").strip(),
},
{
"id": "SWA-010", "asil": "QM",
"links": ["SWE-018", "SWE-019"],
"title": "Logger",
"text": textwrap.dedent("""
## Verantwortung
Logging fuer Entwicklung und Service. Ringpuffer im RAM (1 KB) sowie
Persistenz im EEPROM bei kritischen Ereignissen.
## Schnittstellen (Provided)
```c
Status log_init(void);
void log_event(LogLevel lvl, uint16_t event_id, uint32_t param);
```
""").strip(),
},
]
# ---------------------------------------------------------------------------
# Generation
# ---------------------------------------------------------------------------
REQ_FRONTMATTER = textwrap.dedent("""
---
active: true
derived: false
header: '{title}'
level: 1.{level}
normative: true
reviewed: null
links:{links_yaml}
asil: {asil}
---
# {id}: {title}
{text}
""").strip() + "\n"
def emit_links(links):
if not links:
return " []"
parts = ["\n - {}".format(l) for l in links]
return "".join(parts)
def write_items(items, target_dir: Path, with_links=True):
target_dir.mkdir(parents=True, exist_ok=True)
for i, item in enumerate(items, start=1):
path = target_dir / f"{item['id']}.md"
links_yaml = emit_links(item.get("links", []))
content = REQ_FRONTMATTER.format(
title=item["title"],
level=i,
links_yaml=links_yaml,
asil=item["asil"],
id=item["id"],
text=item["text"],
)
path.write_text(content)
print(f"Wrote {len(items)} items to {target_dir.relative_to(REPO)}/")
def main():
write_items(SG_GOALS, REPO / "safety" / "sg")
write_items(SYS_REQS, REPO / "reqs" / "sys")
write_items(SWE_REQS, REPO / "reqs" / "swe")
write_items(SA_ELEMENTS, REPO / "arch" / "sys")
write_items(SWA_ELEMENTS, REPO / "arch" / "swe")
print("\nTotal: {} reqs/arch items.".format(
len(SYS_REQS) + len(SWE_REQS) + len(SA_ELEMENTS) + len(SWA_ELEMENTS)
))
if __name__ == "__main__":
main()