4 Commits

Author SHA1 Message Date
Stefan Lohmaier bd744162c5 fix(landing-page): Plaene-Links auf docs/plaene/ (matcht Bundle-Struktur)
Validate / build-test (macos-latest) (push) Failing after 2s
Validate / build-test (ubuntu-latest) (push) Failing after 16s
Validate / build-test (windows-latest) (push) Failing after 32s
Validate / reports (push) Has been skipped
Release / release (push) Successful in 50s
2026-05-12 02:07:03 -07:00
Stefan Lohmaier 294b9956f9 feat: Project Manual + CM-/RM-Plan + Landing-Page
Validate / build-test (macos-latest) (push) Failing after 2s
Validate / build-test (windows-latest) (push) Failing after 15s
Validate / build-test (ubuntu-latest) (push) Failing after 15s
Validate / reports (push) Has been skipped
Release / release (push) Successful in 57s
3 neue Plaene:
- Project Manual: Master-Wegweiser fuer neue Projektmitglieder,
  Lese-Reihenfolge, Rollen, Lebenszyklus, Dokumenten-Landschaft
- Configuration Management Plan: CIs, Baselines, Change Control,
  Release-Prozess, Aufbewahrungsfristen (ASPICE SUP.8)
- Risk Management Plan: Projekt-Risiken (abgegrenzt von HARA),
  Klassifikations-Skala, Risiko-Register, Eskalations-Pfad

Landing-Page (Startseite):
- tools/generate_landing_page.py erzeugt build/index.html
- Standalone-HTML, oeffnet im Browser ohne Server
- KPI-Cards: SG/SYS/SWE/Arch/Komponenten/Tests-Counts
- Sektionen mit Links: Plaene, Safety, Manuals, Audit, Reports,
  Diagramme, Source-Code, externe Links
- Existenz-Check: nicht-generierte Reports werden grau markiert
- Im Release-Bundle als index.html ganz oben

CI-Integration:
- validate.yml: neuer Step "Landing-Page" + Upload als Artefakt
- release.yml: Landing-Page generieren + ins Bundle einbauen,
  zusaetzlich Source-Code im Bundle (war vorher nur als tar.gz)

Makefile: neues Target `make landing-page`
2026-05-12 01:59:44 -07:00
Stefan Lohmaier c610cc023c feat: Safety Goals + Drive-Away-Assist + vollst. Traceability
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
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
Stefan Lohmaier 17910835ad docs: README mit kompletter Tour durch Safety + Manuals + Reports
Validate / build-test (macos-latest) (push) Failing after 3s
Validate / build-test (windows-latest) (push) Failing after 17s
Validate / build-test (ubuntu-latest) (push) Failing after 15s
Validate / reports (push) Has been skipped
2026-05-12 00:56:24 -07:00
36 changed files with 1872 additions and 309 deletions
+17 -9
View File
@@ -31,11 +31,12 @@ jobs:
make coverage
make test-report
- name: Traceability + Diagramme + API-Doc
- name: Traceability + Diagramme + API-Doc + Landing-Page
run: |
python3 tools/traceability.py publish docs/traceability
python3 tools/render_plantuml.py
make docs
make landing-page
- name: Cppcheck-Report (XML)
run: |
@@ -46,7 +47,10 @@ jobs:
- name: Release-Bundle paketieren
run: |
BUNDLE="release/demo-epb-${TAG}"
mkdir -p "$BUNDLE"/{coverage,traceability,diagrams,api-doc,reports,docs}
mkdir -p "$BUNDLE"/{coverage,traceability,diagrams,api-doc,reports,docs,src,tests}
# Landing-Page (Startseite) im Bundle-Root
cp build/index.html "$BUNDLE/index.html"
# CI-generierte Artefakte
cp -r build/coverage-html/* "$BUNDLE/coverage/" 2>/dev/null || true
@@ -57,15 +61,19 @@ jobs:
cp build/test-report.html "$BUNDLE/reports/" 2>/dev/null || true
cp build/test-report.md "$BUNDLE/reports/" 2>/dev/null || true
# Source-Code zum Anklicken aus dem Bundle (begrenzt auf das wichtigste)
cp -r src/*.c src/*.h "$BUNDLE/src/" 2>/dev/null || true
cp -r src/stubs "$BUNDLE/src/" 2>/dev/null || true
# Alle Word-Dokumente (Plaene, Safety, Manuals, Audit-Artefakte)
mkdir -p "$BUNDLE/docs/plaene" "$BUNDLE/docs/safety" "$BUNDLE/docs/manuals" \
"$BUNDLE/docs/reviews" "$BUNDLE/docs/non-conformities" "$BUNDLE/docs/misra"
cp docs/*.docx "$BUNDLE/docs/plaene/" 2>/dev/null || true
cp -r docs/safety/* "$BUNDLE/docs/safety/" 2>/dev/null || true
cp -r docs/manuals/* "$BUNDLE/docs/manuals/" 2>/dev/null || true
cp -r docs/reviews/* "$BUNDLE/docs/reviews/" 2>/dev/null || true
cp -r docs/non-conformities/* "$BUNDLE/docs/non-conformities/" 2>/dev/null || true
cp -r misra/records/* "$BUNDLE/docs/misra/" 2>/dev/null || true
"$BUNDLE/docs/reviews" "$BUNDLE/docs/non-conformities" "$BUNDLE/misra/records"
cp -r docs/plaene/* "$BUNDLE/docs/plaene/" 2>/dev/null || true
cp -r docs/safety/* "$BUNDLE/docs/safety/" 2>/dev/null || true
cp -r docs/manuals/* "$BUNDLE/docs/manuals/" 2>/dev/null || true
cp -r docs/reviews/* "$BUNDLE/docs/reviews/" 2>/dev/null || true
cp -r docs/non-conformities/* "$BUNDLE/docs/non-conformities/" 2>/dev/null || true
cp -r misra/records/* "$BUNDLE/misra/records/" 2>/dev/null || true
# Source archive
git archive --format=tar.gz \
+10
View File
@@ -84,6 +84,9 @@ jobs:
- name: Doxygen API-Dokumentation
run: make docs
- name: Landing-Page
run: make landing-page
- name: Cppcheck-Report (XML)
run: |
mkdir -p build
@@ -128,6 +131,13 @@ jobs:
name: api-doc
path: build/api-doc/html/
- name: Upload Landing-Page
uses: actions/upload-artifact@v3
if: always()
with:
name: landing-page
path: build/index.html
- name: Upload Cppcheck-Report
uses: actions/upload-artifact@v3
if: always()
+4 -1
View File
@@ -21,10 +21,13 @@ TESTS = test_switch_debouncer test_actuator_driver test_apply_controller \
test_safety_manager
TEST_BINS = $(TESTS:%=$(BUILD)/%)
.PHONY: all test coverage clean misra static docs test-report
.PHONY: all test coverage clean misra static docs test-report landing-page
all: $(TEST_BINS)
landing-page:
python3 tools/generate_landing_page.py
docs:
@which doxygen >/dev/null 2>&1 || { echo "doxygen not installed (brew/apt install doxygen)"; exit 1; }
doxygen Doxyfile
+114 -85
View File
@@ -1,23 +1,27 @@
# demo-epb — Elektrische Parkbremse
Vollstaendige Demo des [slohmaier Dev Process](https://gitea.slohmaier.com/slohmaier/dev-process) anhand einer EPB-Steuergeraet-Software. Zeigt ASPICE 4.0 / ISO 26262-konforme Entwicklung in einem Monorepo: Anforderungen, Architektur, Code, Tests, Reviews, MISRA — alles auf einen Pull-Request-Klick verifizierbar.
Vollständige Demo des [slohmaier Dev Process](https://gitea.slohmaier.com/slohmaier/dev-process) anhand einer EPB-Steuergerät-Software. Zeigt ASPICE 4.0 / ISO 26262-konforme Entwicklung im Monorepo: Anforderungen, Architektur, Code, Tests, Reviews, MISRA, Safety Case, Manuals — alles auf einen Pull-Request-Klick verifizierbar, alles in einem Release-Bundle.
> Diese Software ist **bewusst kein Produktivcode** — sie ist die Demonstration des Engineering-Verfahrens. Code-Umfang absichtlich klein, Prozess-Tiefe vollstaendig.
> Diese Software ist **bewusst kein Produktivcode** — sie ist die Demonstration des Engineering-Verfahrens. Code-Umfang bewusst klein, Prozess-Tiefe vollständig.
## Was die Demo zeigt
| Artefakt-Typ | Anzahl | Pfad |
|---------------------|--------|---------------------|
| Plaene (Word) | 5 | `docs/*.docx` |
| Audit-Artefakte (Word) | 3 | `docs/reviews/`, `docs/non-conformities/`, `misra/records/` |
| System-Anforderungen| 10 | `reqs/sys/` |
| Software-Anforderungen | 25 | `reqs/swe/` |
| System-Architektur | 5 | `arch/sys/` |
| Software-Architektur| 10 | `arch/swe/` |
| Implementierte Komponenten | 3 (1×ASIL-D, 1×ASIL-B, 1×QM) | `src/` |
| Stub-Komponenten | 7 | `src/stubs/` |
| Unit-Tests | 28 | `tests/unit/` |
| CI-Pipeline | 1 | `.gitea/workflows/` |
| Kategorie | Inhalt |
|-----------|--------|
| **Pläne** (Word) | 5 (PID, PM-, QA-, SWE-, Test-Plan) |
| **Safety-Doku** (Word) | 6 (HARA, Safety Case, FMEDA, MISRA-Compliance, Verification-Report, Tool-Qualification) |
| **Manuals** (Word) | 2 (User-Manual, Service-Manual) |
| **Audit-Artefakte** (Word) | 3 (Review-Protokoll, Non-Conformity, MISRA-Deviation-Record) |
| **System-Anforderungen** | 10 in `reqs/sys/` (Markdown + Doorstop-Style) |
| **Software-Anforderungen** | 25 in `reqs/swe/` |
| **System-Architektur** | 5 in `arch/sys/` mit PlantUML |
| **Software-Architektur** | 10 in `arch/swe/` mit PlantUML |
| **Implementierte C-Komponenten** | 4 (Apply Ctrl D, Safety Mgr D, Actuator Drv B, Switch Db QM) |
| **Stub-Komponenten** | 6 weitere (Header only) |
| **Unit-Tests** | 41, alle grün |
| **CI-Workflows** | 2 (validate + release) |
| **CI-Artefakte** | Coverage HTML, Traceability Matrix, Diagramme SVG, Doxygen, Test-Report, Cppcheck-XML |
| **Cross-Platform-Runner** | Linux + macOS + Windows |
## Quick Start
@@ -25,39 +29,67 @@ Vollstaendige Demo des [slohmaier Dev Process](https://gitea.slohmaier.com/slohm
git clone https://gitea.slohmaier.com/slohmaier/demo-epb.git
cd demo-epb
# Build + Tests
make test
# Tests
make test # 41 Tests, alle grün
# Mit Coverage (benoetigt lcov)
# Mit Coverage (braucht lcov)
make coverage
open build/coverage-html/index.html
# Statische Analyse + MISRA (benoetigt cppcheck)
# Test-Summary-Report (HTML)
make test-report
open build/test-report.html
# Statische Analyse + MISRA (braucht cppcheck)
make static
make misra
# API-Doku (braucht doxygen)
make docs
open build/api-doc/html/index.html
# Traceability-Matrix (HTML)
python3 tools/traceability.py publish docs/traceability
open docs/traceability/index.html
# PlantUML-Diagramme rendern (SVG)
python3 tools/render_plantuml.py
```
## Gefuehrte Tour (~30 min)
## Geführte Tour (~30 min)
### 1. Projektplanung
Start in `docs/`:
### 1. Projektplanung (Word)
`docs/`:
- **PID.docx** — Was wird gebaut und warum
- **SWE-Plan.docx** — Wie wird gebaut: Sprache, Standards, Branching, Review-Regeln, Coverage-Ziele pro ASIL
- **QA-Plan.docx** — Qualitaetsmassnahmen, Reviews, NC-Management
- **SWE-Plan.docx** — Sprache, Standards, Branching, Reviews, Coverage-Ziele
- **QA-Plan.docx** — Qualitätsmaßnahmen, Reviews, NC-Management
- **PM-Plan.docx**, **Test-Plan.docx** — Arbeitspakete + Teststrategie
### 2. Sicherheits-Logik (das ASIL-D Stueck)
`reqs/sys/SYS-001.md``arch/swe/SWA-002.md``src/apply_controller.c``tests/unit/test_apply_controller.c`
### 2. Funktionale Sicherheit (Word — `docs/safety/`)
- **HARA.docx** — Hazard Analysis & Risk Assessment. Leitet **ASIL-D** ab.
- **Safety-Case.docx** — Argumentation in GSN-Style, warum die Sicherheitsziele erfüllt sind
- **FMEDA.docx** — Pro-Komponente Failure Modes mit Diagnostic Coverage
- **Tool-Qualification-Cppcheck.docx** — Tool-Qual für Cppcheck (TI2/TD2/TCL2)
- **MISRA-Compliance-Statement.docx** — formaler Compliance-Nachweis
- **Verification-Report.docx** — V-Modell rechte Seite zusammenfassend
Das ist die Traceability-Kette: System-Sicherheitsziel → Software-Architektur → Code → Test.
### 3. Manuals (Word — `docs/manuals/`)
- **User-Manual.docx** — Fahrerhandbuch-Auszug (Apply, Release, Hill-Hold, LED-Codes)
- **Service-Manual.docx** — Werkstatt-Doku mit UDS-DTCs, Service-Modus, Sensor-Prüfung
### 3. Anforderungen + Architektur (Doorstop in Markdown)
- `reqs/sys/` und `reqs/swe/` — alle Anforderungen mit Mapping
- `arch/sys/` und `arch/swe/` — Architektur mit Mapping per `links:` im Frontmatter
- Eingebettete PlantUML-Diagramme rendern direkt in Gitea
### 4. Sicherheits-Logik (das ASIL-D Stück)
Traceability-Kette:
```
reqs/sys/SYS-001.md → arch/swe/SWA-002.md → src/apply_controller.c → tests/unit/test_apply_controller.c
```
### 4. Code mit Mapping-Tags
Jede `.c`-Datei traegt `@arch`, `@reqs` im Header:
### 5. Anforderungen + Architektur (Doorstop in Markdown)
- `reqs/sys/` + `reqs/swe/` — Anforderungen mit Mapping
- `arch/sys/` + `arch/swe/` — Architektur mit Mapping per `links:` im Frontmatter
- Eingebettete PlantUML-Diagramme rendern direkt in Gitea (UI) und als SVG im Release-Bundle
### 6. Code mit Mapping-Tags
Jede `.c`-Datei trägt `@arch`, `@reqs`, `@asil` im Header:
```c
/**
@@ -69,76 +101,73 @@ Jede `.c`-Datei traegt `@arch`, `@reqs` im Header:
*/
```
So ist Code -> Architektur -> Anforderung auf einen `grep` durchsuchbar.
### 7. Tests mit Anforderungs-Tags
`tests/unit/test_*.c` referenziert die Requirements per `@reqs`. Test-Report (`build/test-report.html`) macht das Mapping klickbar sichtbar.
### 5. Tests mit Anforderungs-Tags
`tests/unit/test_apply_controller.c` referenziert die Requirements per `@reqs`. CI mit Coverage-Report belegt, dass jede Anforderung getestet ist.
### 8. Audit-Artefakte
- `docs/reviews/REV-001.docx` — Review-Protokoll für die ASIL-D-Komponente
- `docs/non-conformities/NC-001.docx` — NC mit Korrekturmaßnahme
- `misra/records/MISRA-REC-001.docx` — MISRA Advisory-Deviation
### 6. Audit-Artefakte
- `docs/reviews/REV-001.docx` — Review-Protokoll fuer die ASIL-D-Komponente
- `docs/non-conformities/NC-001.docx` — Beispiel einer Non-Conformity mit Korrekturmassnahme
- `misra/records/MISRA-REC-001.docx` — MISRA Deviation Record fuer eine bewusste Advisory-Abweichung
### 9. CI-Pipeline (`.gitea/workflows/validate.yml`)
Bei jedem Push:
1. **Cross-Platform Build + Test** auf Linux + macOS + Windows
2. **Static Analysis** (Cppcheck)
3. **MISRA-Check** (Cppcheck + MISRA-Addon)
4. **Coverage** (gcov/lcov)
5. **Traceability-Check** (bidirektional)
6. **PlantUML-Render** (alle Diagramme als SVG)
7. **Doxygen-API-Doc**
8. **Test-Summary-Report**
### 7. CI-Pipeline
`.gitea/workflows/validate.yml` — bei jedem Push laeuft:
1. Cppcheck (Static Analysis)
2. Cppcheck + MISRA-Addon
3. Build + Unit Tests
4. Coverage (gcov/lcov)
5. Doorstop-Traceability-Check
Alles als Gitea-Artefakte abrufbar.
## Architektur-Ueberblick
### 10. Release-Workflow (`.gitea/workflows/release.yml`)
Auf Tag-Push `v*.*.*`:
- Vollständigen Build + alle Reports
- Bündelt **Source-Archive + Artefakt-Archive** (CI-Output + alle Word-Docs)
- Erzeugt Gitea-Release mit Release-Notes
Beispiel: https://gitea.slohmaier.com/slohmaier/demo-epb/releases
## Architektur-Überblick
```
+----------------------+
| EPB ECU (SA-001) |
| +-----------------+ |
| | Safety Mgr (D) | |
| +-----------------+ |
| | Apply Ctrl (D) | |
| +-----------------+ |
| | Actuator Drv (B)| |
| +-----------------+ |
| | Wheel Speed (B) | |
| | Inclino (B) | |
| +-----------------+ |
| | Switch DB (QM) | |
| | Display (QM) | |
| | Diag (QM) | |
| | Service (QM) | |
| | Logger (QM) | |
| +-----------------+ |
+----------------------+
| |
Aktor L Aktor R
(SA-002) (SA-002)
EPB ECU (SA-001)
+----------------------------------+
| Safety Manager (D) | ← arch/swe/SWA-001.md
| Apply Controller (D) | ← arch/swe/SWA-002.md
| Actuator Driver (B) | ← arch/swe/SWA-003.md
| Wheel Speed Plausi (B) [stub] |
| Inclino Filter (B) [stub] |
| Switch Debouncer (QM) | ← arch/swe/SWA-006.md
| Display Manager (QM) [stub] |
| Diag Manager (QM) [stub] |
| Service Mode (QM) [stub] |
| Logger (QM) [stub] |
+----------------------------------+
| |
Aktor L (SA-002) Aktor R (SA-002)
```
## Format-Strategie
| Inhalt | Format | Begruendung |
|---------------------|-------------------|-------------------------------------------------|
| Plaene + Audit-Doku | **Word** (.docx) | Industriestandard fuer ISO-9001-Freigabe |
| Requirements + Arch | **Markdown** (Doorstop) | Lebendig, diff-bar, Traceability per Skript |
| Code, Tests, CI | C / YAML | klar |
| Inhalt | Format | Begründung |
|--------|--------|------------|
| Pläne + Safety + Audit + Manuals | **Word** (.docx) | Industriestandard für ISO-9001-Freigabe |
| Requirements + Architektur | **Markdown** (Doorstop-Stil) | Lebendig, diff-bar, Traceability per Skript |
| Code, Tests, CI | C / YAML | klar |
| Release-Bundle | tar.gz mit allem | Eine Datei für den Auditor |
Beide Welten gehen ueber `tools/`-Skripte ineinander ueber: Markdown ist Source of Truth, Word wird per pandoc daraus gebaut.
## Generatoren
| Skript | Zweck |
|---------------------------------------|----------------------------------------------------|
| `tools/generate_doorstop_items.py` | Erzeugt alle 50 Requirements + Arch-Elemente aus Strukturdaten |
Markdown ist Source of Truth, Word wird per pandoc daraus gebaut.
## Referenzen
- [slohmaier/dev-process](https://gitea.slohmaier.com/slohmaier/dev-process) — die Methodik
- [slohmaier/dev-process](https://gitea.slohmaier.com/slohmaier/dev-process) — Methodik-Repo
- ASPICE 4.0
- ISO 26262 (insbesondere Part 6 — Software)
- ISO 26262 (insbesondere Part 2, 3, 5, 6, 8, 10)
- MISRA C:2012
## Lizenz
MIT — siehe [LICENSE](LICENSE).
Binary file not shown.
Binary file not shown.
Binary file not shown.
+148
View File
@@ -0,0 +1,148 @@
---
doc-id: SLM-EPB-CM-001
version: 1.0
status: Freigegeben
datum: 2026-05-12
---
# Configuration Management Plan (CM-Plan)
| Feld | Wert |
|--------------|----------------------------------------|
| Projekt | demo-epb |
| Dokument-ID | SLM-EPB-CM-001 |
| Version | 1.0 |
| Status | Freigegeben |
| Datum | 2026-05-12 |
| Norm | ASPICE SUP.8 + ISO 26262-8 §7 |
---
## 1. Zweck
Beschreibt, wie Konfigurations-Items identifiziert, versioniert, freigegeben
und kontrolliert geaendert werden.
## 2. Configuration Items (CIs)
Folgende Artefakte stehen unter Konfigurationskontrolle:
| Typ | Pfad | Versionierung |
|-------------------------|----------------------------------------|------------------------------|
| Source-Code | `src/**/*.{c,h}` | Git |
| Tests | `tests/**` | Git |
| Anforderungen | `reqs/{sys,swe}/*.md` | Git + Doorstop-Item-Hash |
| Architektur | `arch/{sys,swe}/*.md` | Git + Doorstop-Item-Hash |
| Safety Goals | `safety/sg/*.md` | Git |
| Plaene (Word) | `docs/plaene/*.docx` | Git + Dokument-Versionsblock |
| Safety-Doku (Word) | `docs/safety/*.docx` | Git |
| Manuals (Word) | `docs/manuals/*.docx` | Git |
| Reviews + NCs | `docs/reviews/`, `docs/non-conformities/` | Git |
| MISRA-Records | `misra/records/*.docx` | Git |
| CI-Konfiguration | `.gitea/workflows/*.yml` | Git |
| Build-Definition | `Makefile`, `Doxyfile` | Git |
| Tools | `tools/*.py` | Git |
## 3. Repository-Struktur
- **Remote:** https://gitea.slohmaier.com/slohmaier/demo-epb
- **Branch `main`:** stabil, immer freigegebener Stand
- **Branch `develop`:** aktueller Entwicklungsstand
- **Feature-Branches:** `feature/SWE-XXX-...`
- **Bugfix-Branches:** `bugfix/<issue>-...`
- **Release-Branches:** `release/vX.Y` (nur bei Real-Projekt; Demo: direkt von main)
## 4. Baselines
Eine Baseline ist ein eingefrorener, freigegebener Stand. Baselines werden durch
Git-Tags gesetzt.
| Baseline-Typ | Tag-Schema | Wann |
|---------------------------|-------------------|----------------------------------------|
| Requirements Baseline | `req-vX.Y` | Nach Anforderungs-Freigabe |
| Architecture Baseline | `arch-vX.Y` | Nach Architektur-Review |
| Release Baseline | `vX.Y.Z` | Bei produktiver Freigabe |
| Internal Snapshot | `snap-YYYY-MM-DD` | Bei wichtigen Zwischenstaenden |
Jeder Tag triggert (bei `vX.Y.Z`) den Release-Workflow, der ein Bundle erzeugt.
## 5. Versions-Schema
| Artefakt | Schema |
|-----------------------|------------------------------------------|
| Software-Release | Semantic Versioning `MAJOR.MINOR.PATCH` |
| Anforderungen | Doorstop-Level `X.Y` + Datum |
| Architektur | Doorstop-Level `X.Y` + Datum |
| Word-Dokumente | `MAJOR.MINOR` im Dokument-Header |
## 6. Change Control
Aenderungen an Configuration Items erfolgen ueber:
1. **Trivial-Aenderung** (Tippfehler, Kommentare): direkt im Branch, PR mit 1 Approval
2. **Normal-Aenderung** (Feature, Bugfix): Feature-Branch, PR mit Reviews je nach ASIL
3. **Major-Aenderung** (Architektur, Sicherheits-Konzept): Change Request + Reviewer-Quorum
| Asil | Reviewer-Mindestanzahl |
|---------|--------------------------------------|
| QM | 1 |
| ASIL-A/B| 1 |
| ASIL-C | 2 (mind. 1 Technical Reviewer) |
| ASIL-D | 2 Technical Reviewer + Safety Manager|
Reviews werden in `docs/reviews/REV-XXX.docx` dokumentiert.
## 7. Release-Prozess
```
1. Alle PRs in main gemerged
2. Branch protected, alle CI-Checks gruen
3. Release-Notes-Entwurf im PR vorbereitet
4. Tag setzen: git tag -a vX.Y.Z -m "..."
5. Push: git push origin vX.Y.Z
6. Release-Workflow laeuft (.gitea/workflows/release.yml):
- Build + Tests + Coverage
- Traceability + Diagrams + API-Doc
- Word-Dokumente bundlen
- Source + Artefakt-Archive packen
- Gitea-Release anlegen
7. Release manuell pruefen (Bundle herunterladen, sichten)
8. Release als "stable" markieren
```
## 8. Aufbewahrung
| Artefakt | Aufbewahrungsdauer |
|--------------------------|----------------------------------------|
| Git-Repository | Unbegrenzt (Gitea + Backup) |
| Release-Bundles | 10 Jahre nach Produkt-EOL |
| Reviews + NCs | 10 Jahre nach Produkt-EOL |
| MISRA-Records | 10 Jahre nach Produkt-EOL |
| CI-Artefakte (kurzlebig) | 90 Tage (in Gitea-Artifacts) |
ISO 26262 fordert 10 Jahre nach End-of-Production-Life (Annahme).
## 9. Verifikation
Alle Pull Requests laufen durch:
- `doorstop`-aequivalenter Traceability-Check (`tools/traceability.py check`)
- Build + Unit-Tests
- Static Analysis + MISRA-Check
- Coverage-Messung
Erst nach Approval und CI-Gruen kann der Merge nach `main` erfolgen.
## 10. Verantwortlichkeiten
| Rolle | Aufgabe |
|------------------|--------------------------------------------------|
| Configuration Mgr| Pflege dieses CM-Plans, Repo-Hygiene, Baselines |
| Entwickler | Korrekte Branch-Strategie, sinnvolle Commit-Msg |
| Reviewer | Pruefung vor Merge, Audit-Trail |
| Project Owner | Release-Freigabe |
## 11. Aenderungshistorie
| Version | Datum | Aenderung | Autor |
|---------|-------------|---------------------|-------------|
| 1.0 | 2026-05-12 | Erstfreigabe | S. Lohmaier |
+172
View File
@@ -0,0 +1,172 @@
---
doc-id: SLM-EPB-PM-MAN-001
version: 1.0
status: Freigegeben
datum: 2026-05-12
---
# Project Manual — demo-epb
| Feld | Wert |
|--------------|----------------------------------------|
| Projekt | demo-epb (Elektrische Parkbremse) |
| Dokument-ID | SLM-EPB-PM-MAN-001 |
| Version | 1.0 |
| Status | Freigegeben |
| Datum | 2026-05-12 |
| Zielgruppe | Neue Projektmitglieder, Auditoren |
---
## 1. Zweck
Dieses Project Manual ist der **Einstieg** ins demo-epb Projekt. Es beantwortet:
- Was wird gebaut?
- Welche Dokumente gibt es, in welcher Reihenfolge lesen?
- Wer ist verantwortlich wofuer?
- Wie laeuft der Entwicklungs- und Freigabe-Zyklus?
## 2. Was ist demo-epb?
Eine vollstaendige Demo des **slohmaier Dev Process** anhand einer
EPB-Steuergeraet-Software. Ziel ist **nicht** die produktive Software, sondern
der Nachweis ASPICE 4.0 / ISO 26262-konformer Entwicklung.
Detail: `docs/plaene/PID.docx`.
## 3. Lese-Reihenfolge fuer neue Projektmitglieder
| Tag | Dokument | Warum |
|-----|----------------------------------------|----------------------------------------|
| 1 | dieses Project Manual | Orientierung |
| 1 | `PID.docx` | Was + Warum |
| 1 | `User-Manual.docx` | Produkt-Verstaendnis |
| 2 | `HARA.docx` + `Safety-Case.docx` | Sicherheits-Konzept |
| 2 | `SWE-Plan.docx` + `QA-Plan.docx` | Engineering-Konventionen |
| 3 | `reqs/` + `arch/` (Markdown) | Anforderungen + Architektur |
| 3 | `src/apply_controller.c` | Beispiel ASIL-D Code |
| 4 | `traceability/index.html` | Vernetzung der Artefakte |
| 4 | `coverage/index.html` | Was ist getestet |
| 5 | Diese Anleitung selber pflegen | Onboarding fuer den Naechsten |
## 4. Dokumenten-Landschaft
```
demo-epb/
├── docs/plaene/ ← PID, PM-Plan, QA-Plan, SWE-Plan, Test-Plan, CM-Plan, RM-Plan
├── docs/safety/ ← HARA, Safety-Case, FMEDA, MISRA-Compliance, Verification-Report, Tool-Qualification
├── docs/manuals/ ← User-Manual, Service-Manual
├── docs/reviews/ ← Review-Protokolle
├── docs/non-conformities/ ← NC-Eintraege
├── misra/records/ ← MISRA Deviation Records
├── reqs/sys/ ← Doorstop-MD System Requirements
├── reqs/swe/ ← Doorstop-MD Software Requirements
├── arch/sys/ ← Doorstop-MD System Architecture + PlantUML
├── arch/swe/ ← Doorstop-MD Software Architecture + PlantUML
├── safety/sg/ ← Doorstop-MD Safety Goals (ASIL-Ableitung)
├── src/ ← C-Code, mit @arch + @reqs Tags im Header
├── tests/ ← Unit-Tests mit @reqs Tags
├── tools/ ← Python-Skripte (Traceability, PlantUML, Reports)
├── .gitea/workflows/ ← CI-Pipelines (validate + release)
└── docs/index.html ← Auto-generierte Startseite
```
Eine **klickbare Uebersicht** liefert `docs/index.html` (Browser oeffnen).
## 5. Rollen und Verantwortlichkeiten
| Rolle | Verantwortung | Person (Demo) |
|--------------------|-----------------------------------------------------|--------------------------|
| Project Owner | Strategische Entscheidungen, Freigabe Release | Stefan Lohmaier |
| Technical Lead | Architektur, Code-Reviews, technische Entscheidungen | Stefan Lohmaier |
| Safety Manager | HARA, Safety Case, ASIL-Konformitaet | Stefan Lohmaier (Demo) |
| QA-Beauftragter | QA-Plan-Pflege, Audit-Vorbereitung | Stefan Lohmaier (Demo) |
| Configuration Mgr | Baselines, Releases, Git-Repo-Hygiene | Stefan Lohmaier (Demo) |
| Entwickler | Implementierung gemaess Architektur + Tests | Stefan Lohmaier (Demo) |
| Reviewer | Code- und Dokument-Reviews | Externer Reviewer (TBD) |
In der Demo ist eine Person in allen Rollen; in einem Real-Projekt mit ASIL-C/D
sind diese personell zu trennen (insb. Entwickler ungleich Reviewer fuer
sicherheitskritischen Code).
## 6. Entwicklungs-Lebenszyklus
```
Anforderung
Architektur (Markdown + PlantUML)
Implementation (C, mit @arch + @reqs)
Unit-Test (CppUTest-aehnliches Framework, mit @reqs)
Pull Request (Branch -> main)
CI: Build + Test + Coverage + MISRA + Traceability-Check
Code-Review (Approval-Pflicht je nach ASIL)
Merge nach main
▼ (bei Release-Punkt)
Tag v*.*.*
CI Release-Workflow: Bundle + Gitea-Release
```
## 7. Freigabe-Strategie
- **Pull-Requests** brauchen mindestens 1 Approval (mehr fuer ASIL-C/D, siehe SWE-Plan)
- **Tags** im Format `vMAJOR.MINOR.PATCH` triggern den Release-Workflow
- **Release-Bundle** enthaelt Source + alle Reports + alle Word-Dokumente
- **Audit-Faehigkeit** ist jederzeit gegeben (Git-History + Doku-Lifecycle)
## 8. Wo Probleme melden
| Problem-Typ | Wo dokumentieren |
|-----------------------|-------------------------------------------------|
| Bug | Gitea Issue (Tag `bug`) |
| Anforderungs-Aenderung| Gitea Issue (Tag `requirement`) + Doorstop-Update |
| Non-Conformity | `docs/non-conformities-md/NC-XXX.md` -> Word |
| MISRA-Abweichung | `misra/records-md/MISRA-REC-XXX.md` -> Word |
| Sicherheits-Problem | Sofort an Safety Manager + NC |
## 9. Tools
Siehe `infrastructure/` im iCloud-Workspace fuer Setup-Details. Kurzform:
- **Gitea** (gitea.slohmaier.com) — Source-Control + CI + Releases
- **Doorstop-Stil** Markdown — Anforderungen + Architektur
- **PlantUML** — Diagramme (eingebettet)
- **Cppcheck** + **GCC -Werror** — Statische Analyse + MISRA
- **gcov/lcov** — Coverage
- **Doxygen** — API-Doc
- **pandoc** — Markdown -> Word/PDF
- **Python** (Stdlib) — Traceability + Report-Generatoren
## 10. Verwandte Dokumente
| Plan | Datei | Inhalt |
|----------------------|-------------------------------------|----------------------------------------|
| Project Initiation | `PID.docx` | Was + Warum |
| Projekt-Management | `PM-Plan.docx` | Arbeitspakete, Termine, Stakeholder |
| Quality Assurance | `QA-Plan.docx` | Reviews, Audits, NC-Management |
| Configuration Mgmt | `CM-Plan.docx` | Baselines, Releases, Change Control |
| Risk Management | `RM-Plan.docx` | Risiken, Mitigation, Monitoring |
| Software Development | `SWE-Plan.docx` | Sprache, Standards, Coverage-Ziele |
| Test | `Test-Plan.docx` | Test-Strategie |
## 11. Aenderungshistorie
| Version | Datum | Aenderung | Autor |
|---------|-------------|---------------------|-------------|
| 1.0 | 2026-05-12 | Erstfreigabe | S. Lohmaier |
+111
View File
@@ -0,0 +1,111 @@
---
doc-id: SLM-EPB-RM-001
version: 1.0
status: Freigegeben
datum: 2026-05-12
---
# Risk Management Plan (RM-Plan)
| Feld | Wert |
|--------------|----------------------------------------|
| Projekt | demo-epb |
| Dokument-ID | SLM-EPB-RM-001 |
| Version | 1.0 |
| Status | Freigegeben |
| Datum | 2026-05-12 |
| Norm | ASPICE MAN.5 |
---
## 1. Zweck
Identifiziert, bewertet und behandelt **Projekt-Risiken** (organisatorisch,
technisch, Zeitplan, Resource). Abgegrenzt von **funktionalen Sicherheits-
Risiken** (Hazards), die im HARA behandelt werden.
## 2. Methodik
| Schritt | Aktion |
|--------------------|-------------------------------------------------|
| 1. Identifikation | Workshops, Lessons-Learned, Stakeholder-Input |
| 2. Klassifikation | Wahrscheinlichkeit (W) x Auswirkung (A) |
| 3. Bewertung | Risk Score = W * A (1-25) |
| 4. Behandlung | Vermeiden / Mindern / Akzeptieren / Transferieren |
| 5. Monitoring | Quartalsweise Review, Statusupdate |
### 2.1 Klassifikations-Skala
| Wahrscheinlichkeit | Bedeutung |
|--------------------|----------------------------|
| 1 | Sehr unwahrscheinlich |
| 2 | Unwahrscheinlich |
| 3 | Moeglich |
| 4 | Wahrscheinlich |
| 5 | Sehr wahrscheinlich |
| Auswirkung | Bedeutung |
|------------|--------------------------------------------|
| 1 | Vernachlaessigbar |
| 2 | Geringe Verzoegerung / Mehraufwand |
| 3 | Spuerbare Auswirkung auf Termin/Budget |
| 4 | Erhebliche Auswirkung, Projekt gefaehrdet |
| 5 | Projekt-Stop |
| Score-Bereich | Aktion |
|---------------|------------------------------------------|
| 1-4 | Akzeptieren, monitoren |
| 5-9 | Mindern (Plan) |
| 10-15 | Mindern (sofort, mit Eskalation) |
| 16-25 | Eskalation an Project Owner |
## 3. Risiko-Register
| ID | Beschreibung | W | A | Score | Behandlung | Status |
|-------|---------------------------------------------------------|---|---|-------|------------------------------------------|----------|
| R-01 | Demo wird als produktreifer Code missverstanden | 3 | 3 | 9 | Disclaimer im README + Project Manual | Mitigated |
| R-02 | MISRA-Tooling-Update bricht CI (false positives) | 2 | 3 | 6 | Tool-Versionen pinnen, Regression-Suite | Mitigated |
| R-03 | Reviewer-Verfuegbarkeit fuer ASIL-D | 3 | 4 | 12 | Self-Review dokumentiert (nur Demo) | Akzeptiert (Demo) |
| R-04 | Gitea-Server-Ausfall | 2 | 4 | 8 | Lokale Klone, regelmaessige Backups | Mitigated |
| R-05 | Apple-Cert-Ablauf ohne Vorwarnung | 3 | 3 | 9 | Renewal-Reminder + 30-Tage-Vorwarnung | Mitigated |
| R-06 | Windows-Build-VM unzuverlaessig (busybox-PATH-Konflikte)| 4 | 2 | 8 | MSYS2 dokumentiert, alt PATH vorne | Open |
| R-07 | macOS act_runner host-mode Cache-Bug | 3 | 2 | 6 | continue-on-error, dokumentiert | Open |
| R-08 | Doorstop-Tooling-Kompatibilitaet bei Update | 2 | 3 | 6 | Eigenes traceability.py, kein doorstop-Dep | Mitigated |
| R-09 | Wissensverlust bei Single-Person-Setup | 4 | 4 | 16 | Project Manual + Dokumentation pflegen | Open |
## 4. Risiko-Reviews
| Frequenz | Teilnehmer | Outputs |
|--------------|-------------------------|--------------------------------------|
| Quartalsweise| Project Owner + TL | Aktualisiertes Register, Action-Items |
| Bei Aenderung| Betroffene Rollen | Risiko-Score-Update |
| Bei Release | Project Owner + QA | Restrisiken-Bewertung |
## 5. Eskalations-Pfad
```
R-Owner (taeglich)
│ Score > 9
Project Owner (woechentlich)
│ Score > 15
Stakeholder / Auftraggeber (sofort)
```
## 6. Lessons Learned
Geschlossene Risiken werden bei Projektabschluss in `docs/lessons-learned/`
zusammengefasst, um in Folge-Projekten besser einschaetzen zu koennen.
## 7. Verwandte Dokumente
- `PM-Plan.docx` — Top-Level-Risiken (Auszug)
- `HARA.docx` — Funktionale Sicherheits-Risiken (Hazards, getrennt von Projekt-Risiken)
- `QA-Plan.docx` — Non-Conformity-Management
## 8. Aenderungshistorie
| Version | Datum | Aenderung | Autor |
|---------|-------------|---------------------|-------------|
| 1.0 | 2026-05-12 | Erstfreigabe | S. Lohmaier |
+140 -17
View File
@@ -3,27 +3,150 @@
<title>demo-epb — Traceability Matrix</title>
<style>
body{font-family:-apple-system,Segoe UI,sans-serif;padding:20px;color:#222}
table{border-collapse:collapse;width:100%;font-size:14px}
table{border-collapse:collapse;width:100%;font-size:13px;margin-top:16px}
th,td{border:1px solid #ccc;padding:6px 8px;vertical-align:top;text-align:left}
th{background:#f0f0f0}
th{background:#f0f0f0;position:sticky;top:0}
tr:nth-child(even) td{background:#fafafa}
.asil{display:inline-block;padding:1px 6px;border-radius:3px;color:white;font-weight:bold;font-size:11px}
.id{font-family:Consolas,monospace;font-size:13px}
.id{font-family:Consolas,monospace;font-size:12px}
.cnt{color:#666;font-size:11px}
h1{color:#1f3864}
h1{color:#1f3864}h2{color:#1f3864;margin-top:30px}
.missing{color:#c00}
</style></head><body>
<h1>demo-epb — Traceability Matrix</h1>
<p>Generiert aus 50 Items (SYS: 10, SWE: 25, SA: 5, SWA: 10).</p>
<p>Vollstaendige Kette: <code>SG → SYS → SA, SWE → SWA → Code (@arch) + Test (@reqs)</code></p>
<p>
<strong>SG:</strong> 5 &nbsp;
<strong>SYS:</strong> 10 &nbsp;
<strong>SWE:</strong> 25 &nbsp;
<strong>SA:</strong> 5 &nbsp;
<strong>SWA:</strong> 10 &nbsp;
<strong>Code-Files:</strong> 4 &nbsp;
<strong>Test-Files:</strong> 4
</p>
<table>
<tr><th>System-Requirement</th><th>System-Arch (SA)</th><th>Software-Req (SWE)</th><th>Software-Arch (SWA)</th></tr>
<tr><td><div><span class='id'>SYS-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Halten der Parkbremse im Stillstand</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div></td><td><div><span class='id'>SWE-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply-Controller haelt Klemmkraft</div><div><span class='id'>SWE-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Watchdog ueberwacht Apply-Controller</div><div><span class='id'>SWE-022</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Stillstands-Erkennung aus Wheel Speeds</div></td><td><div><span class='id'>SWA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply Controller</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td></tr>
<tr><td><div><span class='id'>SYS-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply auf Fahrer-Anforderung</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div></td><td><div><span class='id'>SWE-003</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Schalter-Apply-Signal an Apply-Controller weiterleiten</div><div><span class='id'>SWE-004</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Klemmkraft-Erreichen bestaetigen</div><div><span class='id'>SWE-022</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Stillstands-Erkennung aus Wheel Speeds</div><div><span class='id'>SWE-025</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch-Debouncing</div></td><td><div><span class='id'>SWA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply Controller</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div><div><span class='id'>SWA-006</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch Debouncer</div></td></tr>
<tr><td><div><span class='id'>SYS-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Release auf Fahrer-Anforderung</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div></td><td><div><span class='id'>SWE-005</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Release-Voraussetzungen pruefen</div><div><span class='id'>SWE-006</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Aktoren in Release-Position fahren</div><div><span class='id'>SWE-025</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch-Debouncing</div></td><td><div><span class='id'>SWA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply Controller</div><div><span class='id'>SWA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Actuator Driver</div><div><span class='id'>SWA-006</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch Debouncer</div></td></tr>
<tr><td><div><span class='id'>SYS-004</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Auto-Apply bei Motor-Aus</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div></td><td><div><span class='id'>SWE-007</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Motor-Aus-Bedingung erkennen</div><div><span class='id'>SWE-008</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Auto-Apply nach 2 s Verzoegerung</div></td><td><div><span class='id'>SWA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Safety Manager</div></td></tr>
<tr><td><div><span class='id'>SYS-005</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold am Berg</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sensor-Cluster</div></td><td><div><span class='id'>SWE-009</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold-Aktivierungsbedingung</div><div><span class='id'>SWE-010</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold-Uebergabe an Apply-Controller</div><div><span class='id'>SWE-024</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Inclinometer Tiefpass-Filter</div></td><td><div><span class='id'>SWA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Safety Manager</div><div><span class='id'>SWA-005</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Inclinometer Filter</div></td></tr>
<tr><td><div><span class='id'>SYS-006</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Auto-Release beim Anfahren (Drive-Away-Assist)</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sensor-Cluster</div></td><td><div><span class='id'>SWE-011</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Anfahrabsicht erkennen</div><div><span class='id'>SWE-012</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sicherheits-Check vor Auto-Release</div><div><span class='id'>SWE-022</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Stillstands-Erkennung aus Wheel Speeds</div></td><td><div><span class='id'>SWA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Safety Manager</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td></tr>
<tr><td><div><span class='id'>SYS-007</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Aktor-Stromueberwachung</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div><div><span class='id'>SA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sensor-Cluster</div></td><td><div><span class='id'>SWE-013</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Strommessung mit 1 kHz</div><div><span class='id'>SWE-014</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Overcurrent-Cutoff</div><div><span class='id'>SWE-015</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Klemmkraft-Schaetzung aus Strom-Profil</div><div><span class='id'>SWE-023</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td><td><div><span class='id'>SWA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Actuator Driver</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td></tr>
<tr><td><div><span class='id'>SYS-008</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Service-Modus fuer Werkstatt</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-004</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>HMI (Schalter, LED, Display)</div></td><td><div><span class='id'>SWE-016</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS RoutineControl 0x31 fuer Service-Release</div><div><span class='id'>SWE-017</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Service-Mode-Indikator</div></td><td><div><span class='id'>SWA-009</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Service Mode</div></td></tr>
<tr><td><div><span class='id'>SYS-009</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS-Diagnose</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-005</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>CAN-Bus</div></td><td><div><span class='id'>SWE-018</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS Service 0x19 ReadDTC</div><div><span class='id'>SWE-019</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS Service 0x22 ReadDataByIdentifier</div></td><td><div><span class='id'>SWA-008</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Diagnostic Manager</div><div><span class='id'>SWA-010</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Logger</div></td></tr>
<tr><td><div><span class='id'>SYS-010</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>HMI-Statusanzeige</div></td><td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-004</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>HMI (Schalter, LED, Display)</div><div><span class='id'>SA-005</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>CAN-Bus</div></td><td><div><span class='id'>SWE-020</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>LED-Steuerung</div><div><span class='id'>SWE-021</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>CAN-Status-Frame</div></td><td><div><span class='id'>SWA-007</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Display Manager</div></td></tr>
</table></body></html>
<tr><th>Safety Goal</th><th>System-Requirement</th><th>System-Arch</th><th>Software-Req</th><th>Software-Arch</th><th>Code</th><th>Test</th></tr>
<tr>
<td><div><span class='id'>SG-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Kein ungewolltes Loesen der Parkbremse im Stillstand</div></td>
<td><div><span class='id'>SYS-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Halten der Parkbremse im Stillstand</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div></td>
<td><div><span class='id'>SWE-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply-Controller haelt Klemmkraft</div><div><span class='id'>SWE-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Watchdog ueberwacht Apply-Controller</div><div><span class='id'>SWE-022</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Stillstands-Erkennung aus Wheel Speeds</div></td>
<td><div><span class='id'>SWA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply Controller</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td>
<td><div class='id'>src/apply_controller.c</div></td>
<td><div class='id'>tests/unit/test_apply_controller.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Kein ungewolltes Loesen der Parkbremse im Stillstand</div></td>
<td><div><span class='id'>SYS-004</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Auto-Apply bei Motor-Aus</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div></td>
<td><div><span class='id'>SWE-007</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Motor-Aus-Bedingung erkennen</div><div><span class='id'>SWE-008</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Auto-Apply nach 2 s Verzoegerung</div></td>
<td><div><span class='id'>SWA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Safety Manager</div></td>
<td><div class='id'>src/safety_manager.c</div></td>
<td><div class='id'>tests/unit/test_safety_manager.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Kein ungewolltes Festklemmen waehrend der Fahrt</div></td>
<td><div><span class='id'>SYS-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply auf Fahrer-Anforderung</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div></td>
<td><div><span class='id'>SWE-003</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Schalter-Apply-Signal an Apply-Controller weiterleiten</div><div><span class='id'>SWE-004</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Klemmkraft-Erreichen bestaetigen</div><div><span class='id'>SWE-022</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Stillstands-Erkennung aus Wheel Speeds</div><div><span class='id'>SWE-025</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch-Debouncing</div></td>
<td><div><span class='id'>SWA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply Controller</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div><div><span class='id'>SWA-006</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch Debouncer</div></td>
<td><div class='id'>src/apply_controller.c</div><div class='id'>src/switch_debouncer.c</div></td>
<td><div class='id'>tests/unit/test_apply_controller.c</div><div class='id'>tests/unit/test_switch_debouncer.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Kein ungewolltes Festklemmen waehrend der Fahrt</div></td>
<td><div><span class='id'>SYS-005</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold am Berg</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sensor-Cluster</div></td>
<td><div><span class='id'>SWE-009</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold-Aktivierungsbedingung</div><div><span class='id'>SWE-010</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold-Uebergabe an Apply-Controller</div><div><span class='id'>SWE-024</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Inclinometer Tiefpass-Filter</div></td>
<td><div><span class='id'>SWA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Safety Manager</div><div><span class='id'>SWA-005</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Inclinometer Filter</div></td>
<td><div class='id'>src/safety_manager.c</div></td>
<td><div class='id'>tests/unit/test_safety_manager.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-003</span> <span class='asil' style='background:#1f77b4'>A</span></div><div class='cnt'>Schutz gegen Aktor-Ueberlast</div></td>
<td><div><span class='id'>SYS-007</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Aktor-Stromueberwachung</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div><div><span class='id'>SA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sensor-Cluster</div></td>
<td><div><span class='id'>SWE-013</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Strommessung mit 1 kHz</div><div><span class='id'>SWE-014</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Overcurrent-Cutoff</div><div><span class='id'>SWE-015</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Klemmkraft-Schaetzung aus Strom-Profil</div><div><span class='id'>SWE-023</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td>
<td><div><span class='id'>SWA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Actuator Driver</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td>
<td><div class='id'>src/actuator_driver.c</div></td>
<td><div class='id'>tests/unit/test_actuator_driver.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-004</span> <span class='asil' style='background:#ff7f0e'>C</span></div><div class='cnt'>Zuverlaessige Hill-Hold-Uebergabe</div></td>
<td><div><span class='id'>SYS-005</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold am Berg</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sensor-Cluster</div></td>
<td><div><span class='id'>SWE-009</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold-Aktivierungsbedingung</div><div><span class='id'>SWE-010</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Hill-Hold-Uebergabe an Apply-Controller</div><div><span class='id'>SWE-024</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Inclinometer Tiefpass-Filter</div></td>
<td><div><span class='id'>SWA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Safety Manager</div><div><span class='id'>SWA-005</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Inclinometer Filter</div></td>
<td><div class='id'>src/safety_manager.c</div></td>
<td><div class='id'>tests/unit/test_safety_manager.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-004</span> <span class='asil' style='background:#ff7f0e'>C</span></div><div class='cnt'>Zuverlaessige Hill-Hold-Uebergabe</div></td>
<td><div><span class='id'>SYS-006</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Auto-Release beim Anfahren (Drive-Away-Assist)</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sensor-Cluster</div></td>
<td><div><span class='id'>SWE-011</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Anfahrabsicht erkennen</div><div><span class='id'>SWE-012</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Sicherheits-Check vor Auto-Release</div><div><span class='id'>SWE-022</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Stillstands-Erkennung aus Wheel Speeds</div></td>
<td><div><span class='id'>SWA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Safety Manager</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div></td>
<td><div class='id'>src/safety_manager.c</div></td>
<td><div class='id'>tests/unit/test_safety_manager.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-005</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Reaktion auf Fahreranforderung</div></td>
<td><div><span class='id'>SYS-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply auf Fahrer-Anforderung</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div></td>
<td><div><span class='id'>SWE-003</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Schalter-Apply-Signal an Apply-Controller weiterleiten</div><div><span class='id'>SWE-004</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Klemmkraft-Erreichen bestaetigen</div><div><span class='id'>SWE-022</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Stillstands-Erkennung aus Wheel Speeds</div><div><span class='id'>SWE-025</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch-Debouncing</div></td>
<td><div><span class='id'>SWA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply Controller</div><div><span class='id'>SWA-004</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Wheel Speed Plausibilisierung</div><div><span class='id'>SWA-006</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch Debouncer</div></td>
<td><div class='id'>src/apply_controller.c</div><div class='id'>src/switch_debouncer.c</div></td>
<td><div class='id'>tests/unit/test_apply_controller.c</div><div class='id'>tests/unit/test_switch_debouncer.c</div></td>
</tr>
<tr>
<td><div><span class='id'>SG-005</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Reaktion auf Fahreranforderung</div></td>
<td><div><span class='id'>SYS-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Release auf Fahrer-Anforderung</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Aktoren (Caliper-Motoren)</div></td>
<td><div><span class='id'>SWE-005</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Release-Voraussetzungen pruefen</div><div><span class='id'>SWE-006</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Aktoren in Release-Position fahren</div><div><span class='id'>SWE-025</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch-Debouncing</div></td>
<td><div><span class='id'>SWA-002</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>Apply Controller</div><div><span class='id'>SWA-003</span> <span class='asil' style='background:#2ca02c'>B</span></div><div class='cnt'>Actuator Driver</div><div><span class='id'>SWA-006</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Switch Debouncer</div></td>
<td><div class='id'>src/apply_controller.c</div><div class='id'>src/actuator_driver.c</div><div class='id'>src/switch_debouncer.c</div></td>
<td><div class='id'>tests/unit/test_actuator_driver.c</div><div class='id'>tests/unit/test_apply_controller.c</div><div class='id'>tests/unit/test_switch_debouncer.c</div></td>
</tr>
<tr>
<td class='missing'></td>
<td><div><span class='id'>SYS-008</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Service-Modus fuer Werkstatt</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-004</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>HMI (Schalter, LED, Display)</div></td>
<td><div><span class='id'>SWE-016</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS RoutineControl 0x31 fuer Service-Release</div><div><span class='id'>SWE-017</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Service-Mode-Indikator</div></td>
<td><div><span class='id'>SWA-009</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Service Mode</div></td>
<td class='cnt'></td>
<td class='cnt'></td>
</tr>
<tr>
<td class='missing'></td>
<td><div><span class='id'>SYS-009</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS-Diagnose</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-005</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>CAN-Bus</div></td>
<td><div><span class='id'>SWE-018</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS Service 0x19 ReadDTC</div><div><span class='id'>SWE-019</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>UDS Service 0x22 ReadDataByIdentifier</div></td>
<td><div><span class='id'>SWA-008</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Diagnostic Manager</div><div><span class='id'>SWA-010</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Logger</div></td>
<td class='cnt'></td>
<td class='cnt'></td>
</tr>
<tr>
<td class='missing'></td>
<td><div><span class='id'>SYS-010</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>HMI-Statusanzeige</div></td>
<td><div><span class='id'>SA-001</span> <span class='asil' style='background:#d62728'>D</span></div><div class='cnt'>EPB ECU</div><div><span class='id'>SA-004</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>HMI (Schalter, LED, Display)</div><div><span class='id'>SA-005</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>CAN-Bus</div></td>
<td><div><span class='id'>SWE-020</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>LED-Steuerung</div><div><span class='id'>SWE-021</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>CAN-Status-Frame</div></td>
<td><div><span class='id'>SWA-007</span> <span class='asil' style='background:#888'>QM</span></div><div class='cnt'>Display Manager</div></td>
<td class='cnt'></td>
<td class='cnt'></td>
</tr>
</table>
<h2>Code → Architektur</h2>
<table><tr><th>Datei</th><th>@arch</th><th>@reqs</th></tr>
<tr><td class='id'>src/safety_manager.c</td><td>SWA-001</td><td class='cnt'>SWE-007 SWE-008 SWE-009 SWE-010 SWE-011 SWE-012</td></tr>
<tr><td class='id'>src/apply_controller.c</td><td>SWA-002</td><td class='cnt'>SWE-001 SWE-002 SWE-003 SWE-004</td></tr>
<tr><td class='id'>src/actuator_driver.c</td><td>SWA-003</td><td class='cnt'>SWE-006 SWE-013 SWE-014 SWE-015</td></tr>
<tr><td class='id'>src/switch_debouncer.c</td><td>SWA-006</td><td class='cnt'>SWE-025</td></tr>
</table>
<h2>Test → Anforderungen</h2>
<table><tr><th>Test-Datei</th><th>Decklt SWA</th><th>@reqs</th></tr>
<tr><td class='id'>tests/unit/test_safety_manager.c</td><td>SWA-001</td><td class='cnt'>SWE-007 SWE-008 SWE-009 SWE-010 SWE-011 SWE-012</td></tr>
<tr><td class='id'>tests/unit/test_apply_controller.c</td><td>SWA-002</td><td class='cnt'>SWE-001 SWE-002 SWE-003 SWE-004 SWE-005</td></tr>
<tr><td class='id'>tests/unit/test_actuator_driver.c</td><td>SWA-003</td><td class='cnt'>SWE-006 SWE-013 SWE-014 SWE-015</td></tr>
<tr><td class='id'>tests/unit/test_switch_debouncer.c</td><td>SWA-006</td><td class='cnt'>SWE-025</td></tr>
</table>
</body></html>
+313 -116
View File
@@ -1,5 +1,9 @@
[
{
"sg": {
"id": "SG-001",
"asil": "D"
},
"sys": {
"id": "SYS-001",
"asil": "D",
@@ -38,9 +42,58 @@
"id": "SWA-004",
"asil": "B"
}
],
"code": [
"src/apply_controller.c"
],
"tests": [
"tests/unit/test_apply_controller.c"
]
},
{
"sg": {
"id": "SG-001",
"asil": "D"
},
"sys": {
"id": "SYS-004",
"asil": "D",
"title": "Auto-Apply bei Motor-Aus"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
}
],
"swe": [
{
"id": "SWE-007",
"asil": "D"
},
{
"id": "SWE-008",
"asil": "D"
}
],
"swa": [
{
"id": "SWA-001",
"asil": "D"
}
],
"code": [
"src/safety_manager.c"
],
"tests": [
"tests/unit/test_safety_manager.c"
]
},
{
"sg": {
"id": "SG-002",
"asil": "D"
},
"sys": {
"id": "SYS-002",
"asil": "D",
@@ -87,83 +140,21 @@
"id": "SWA-006",
"asil": "QM"
}
],
"code": [
"src/apply_controller.c",
"src/switch_debouncer.c"
],
"tests": [
"tests/unit/test_apply_controller.c",
"tests/unit/test_switch_debouncer.c"
]
},
{
"sys": {
"id": "SYS-003",
"asil": "B",
"title": "Release auf Fahrer-Anforderung"
"sg": {
"id": "SG-002",
"asil": "D"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
},
{
"id": "SA-002",
"asil": "D"
}
],
"swe": [
{
"id": "SWE-005",
"asil": "B"
},
{
"id": "SWE-006",
"asil": "B"
},
{
"id": "SWE-025",
"asil": "QM"
}
],
"swa": [
{
"id": "SWA-002",
"asil": "D"
},
{
"id": "SWA-003",
"asil": "B"
},
{
"id": "SWA-006",
"asil": "QM"
}
]
},
{
"sys": {
"id": "SYS-004",
"asil": "D",
"title": "Auto-Apply bei Motor-Aus"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
}
],
"swe": [
{
"id": "SWE-007",
"asil": "D"
},
{
"id": "SWE-008",
"asil": "D"
}
],
"swa": [
{
"id": "SWA-001",
"asil": "D"
}
]
},
{
"sys": {
"id": "SYS-005",
"asil": "D",
@@ -202,50 +193,19 @@
"id": "SWA-005",
"asil": "B"
}
],
"code": [
"src/safety_manager.c"
],
"tests": [
"tests/unit/test_safety_manager.c"
]
},
{
"sys": {
"id": "SYS-006",
"asil": "B",
"title": "Auto-Release beim Anfahren (Drive-Away-Assist)"
"sg": {
"id": "SG-003",
"asil": "A"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
},
{
"id": "SA-003",
"asil": "B"
}
],
"swe": [
{
"id": "SWE-011",
"asil": "B"
},
{
"id": "SWE-012",
"asil": "B"
},
{
"id": "SWE-022",
"asil": "B"
}
],
"swa": [
{
"id": "SWA-001",
"asil": "D"
},
{
"id": "SWA-004",
"asil": "B"
}
]
},
{
"sys": {
"id": "SYS-007",
"asil": "B",
@@ -292,9 +252,238 @@
"id": "SWA-004",
"asil": "B"
}
],
"code": [
"src/actuator_driver.c"
],
"tests": [
"tests/unit/test_actuator_driver.c"
]
},
{
"sg": {
"id": "SG-004",
"asil": "C"
},
"sys": {
"id": "SYS-005",
"asil": "D",
"title": "Hill-Hold am Berg"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
},
{
"id": "SA-003",
"asil": "B"
}
],
"swe": [
{
"id": "SWE-009",
"asil": "D"
},
{
"id": "SWE-010",
"asil": "D"
},
{
"id": "SWE-024",
"asil": "B"
}
],
"swa": [
{
"id": "SWA-001",
"asil": "D"
},
{
"id": "SWA-005",
"asil": "B"
}
],
"code": [
"src/safety_manager.c"
],
"tests": [
"tests/unit/test_safety_manager.c"
]
},
{
"sg": {
"id": "SG-004",
"asil": "C"
},
"sys": {
"id": "SYS-006",
"asil": "B",
"title": "Auto-Release beim Anfahren (Drive-Away-Assist)"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
},
{
"id": "SA-003",
"asil": "B"
}
],
"swe": [
{
"id": "SWE-011",
"asil": "B"
},
{
"id": "SWE-012",
"asil": "B"
},
{
"id": "SWE-022",
"asil": "B"
}
],
"swa": [
{
"id": "SWA-001",
"asil": "D"
},
{
"id": "SWA-004",
"asil": "B"
}
],
"code": [
"src/safety_manager.c"
],
"tests": [
"tests/unit/test_safety_manager.c"
]
},
{
"sg": {
"id": "SG-005",
"asil": "B"
},
"sys": {
"id": "SYS-002",
"asil": "D",
"title": "Apply auf Fahrer-Anforderung"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
},
{
"id": "SA-002",
"asil": "D"
}
],
"swe": [
{
"id": "SWE-003",
"asil": "D"
},
{
"id": "SWE-004",
"asil": "D"
},
{
"id": "SWE-022",
"asil": "B"
},
{
"id": "SWE-025",
"asil": "QM"
}
],
"swa": [
{
"id": "SWA-002",
"asil": "D"
},
{
"id": "SWA-004",
"asil": "B"
},
{
"id": "SWA-006",
"asil": "QM"
}
],
"code": [
"src/apply_controller.c",
"src/switch_debouncer.c"
],
"tests": [
"tests/unit/test_apply_controller.c",
"tests/unit/test_switch_debouncer.c"
]
},
{
"sg": {
"id": "SG-005",
"asil": "B"
},
"sys": {
"id": "SYS-003",
"asil": "B",
"title": "Release auf Fahrer-Anforderung"
},
"sa": [
{
"id": "SA-001",
"asil": "D"
},
{
"id": "SA-002",
"asil": "D"
}
],
"swe": [
{
"id": "SWE-005",
"asil": "B"
},
{
"id": "SWE-006",
"asil": "B"
},
{
"id": "SWE-025",
"asil": "QM"
}
],
"swa": [
{
"id": "SWA-002",
"asil": "D"
},
{
"id": "SWA-003",
"asil": "B"
},
{
"id": "SWA-006",
"asil": "QM"
}
],
"code": [
"src/apply_controller.c",
"src/actuator_driver.c",
"src/switch_debouncer.c"
],
"tests": [
"tests/unit/test_actuator_driver.c",
"tests/unit/test_apply_controller.c",
"tests/unit/test_switch_debouncer.c"
]
},
{
"sg": null,
"sys": {
"id": "SYS-008",
"asil": "QM",
@@ -325,9 +514,12 @@
"id": "SWA-009",
"asil": "QM"
}
]
],
"code": [],
"tests": []
},
{
"sg": null,
"sys": {
"id": "SYS-009",
"asil": "QM",
@@ -362,9 +554,12 @@
"id": "SWA-010",
"asil": "QM"
}
]
],
"code": [],
"tests": []
},
{
"sg": null,
"sys": {
"id": "SYS-010",
"asil": "QM",
@@ -399,6 +594,8 @@
"id": "SWA-007",
"asil": "QM"
}
]
],
"code": [],
"tests": []
}
]
+2 -1
View File
@@ -5,7 +5,8 @@ header: 'Halten der Parkbremse im Stillstand'
level: 1.1
normative: true
reviewed: null
links: []
links:
- SG-001
asil: D
---
+3 -1
View File
@@ -5,7 +5,9 @@ header: 'Apply auf Fahrer-Anforderung'
level: 1.2
normative: true
reviewed: null
links: []
links:
- SG-002
- SG-005
asil: D
---
+2 -1
View File
@@ -5,7 +5,8 @@ header: 'Release auf Fahrer-Anforderung'
level: 1.3
normative: true
reviewed: null
links: []
links:
- SG-005
asil: B
---
+2 -1
View File
@@ -5,7 +5,8 @@ header: 'Auto-Apply bei Motor-Aus'
level: 1.4
normative: true
reviewed: null
links: []
links:
- SG-001
asil: D
---
+3 -1
View File
@@ -5,7 +5,9 @@ header: 'Hill-Hold am Berg'
level: 1.5
normative: true
reviewed: null
links: []
links:
- SG-002
- SG-004
asil: D
---
+2 -1
View File
@@ -5,7 +5,8 @@ header: 'Auto-Release beim Anfahren (Drive-Away-Assist)'
level: 1.6
normative: true
reviewed: null
links: []
links:
- SG-004
asil: B
---
+2 -1
View File
@@ -5,7 +5,8 @@ header: 'Aktor-Stromueberwachung'
level: 1.7
normative: true
reviewed: null
links: []
links:
- SG-003
asil: B
---
+17
View File
@@ -0,0 +1,17 @@
---
active: true
derived: false
header: 'Kein ungewolltes Loesen der Parkbremse im Stillstand'
level: 1.1
normative: true
reviewed: null
links: []
asil: D
---
# SG-001: Kein ungewolltes Loesen der Parkbremse im Stillstand
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).
**FTTI:** 5 s (H-01) / 30 s (H-04).
**Safe State:** APPLIED (Klemmkraft halten).
+17
View File
@@ -0,0 +1,17 @@
---
active: true
derived: false
header: 'Kein ungewolltes Festklemmen waehrend der Fahrt'
level: 1.2
normative: true
reviewed: null
links: []
asil: D
---
# SG-002: Kein ungewolltes Festklemmen waehrend der Fahrt
Die EPB darf nicht waehrend der Fahrt ungewollt festklemmen. Abgeleitet aus HARA-Hazard H-02.
**FTTI:** 100 ms.
**Safe State:** Aktor stop (kein Apply einleiten).
+17
View File
@@ -0,0 +1,17 @@
---
active: true
derived: false
header: 'Schutz gegen Aktor-Ueberlast'
level: 1.3
normative: true
reviewed: null
links: []
asil: A
---
# SG-003: Schutz gegen Aktor-Ueberlast
Das System muss Aktor-Motorschaeden durch Ueberstrom verhindern. Abgeleitet aus HARA-Hazard H-05.
**FTTI:** 100 ms.
**Safe State:** Aktor abschalten, DTC setzen.
+17
View File
@@ -0,0 +1,17 @@
---
active: true
derived: false
header: 'Zuverlaessige Hill-Hold-Uebergabe'
level: 1.4
normative: true
reviewed: null
links: []
asil: C
---
# SG-004: Zuverlaessige Hill-Hold-Uebergabe
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.
**FTTI:** 500 ms.
**Safe State:** Apply einleiten.
+16
View File
@@ -0,0 +1,16 @@
---
active: true
derived: false
header: 'Reaktion auf Fahreranforderung'
level: 1.5
normative: true
reviewed: null
links: []
asil: B
---
# SG-005: Reaktion auf Fahreranforderung
Das System muss in spezifizierter Zeit auf Fahrer-Apply- und Release-Anforderungen reagieren. Abgeleitet aus HARA-Hazards H-03 und H-07.
**Reaktionszeit:** Apply <= 800 ms, Release <= 1500 ms.
+54 -8
View File
@@ -1,12 +1,13 @@
/**
* @file safety_manager.c
* @brief Safety Manager — Hill-Hold + Auto-Apply Logik.
* @brief Safety Manager — Hill-Hold, Auto-Apply, Drive-Away-Assist.
*
* @arch SWA-001
* @reqs SWE-007 SWE-008 SWE-009 SWE-010
* @reqs SWE-007 SWE-008 SWE-009 SWE-010 SWE-011 SWE-012
*
* ASIL: D. Diese Komponente entscheidet, wann der Apply Controller eine
* Apply-Anforderung erhaelt (Hill-Hold-Uebergabe, Auto-Apply bei Motor-Aus).
* Apply- oder Release-Anforderung erhaelt (Hill-Hold-Uebergabe, Auto-Apply
* bei Motor-Aus, Drive-Away-Assist).
* Aenderungen erfordern Technical Review mit 2 Approvals.
*/
#include <stddef.h>
@@ -17,6 +18,7 @@ typedef struct {
SafetyState state;
uint16_t ticks_in_state; /* 50ms-Ticks im aktuellen Zustand */
bool apply_requested;
bool release_requested;
} SafetyCtx;
static SafetyCtx s_ctx;
@@ -42,11 +44,30 @@ static bool grade_steep(const SafetyInputs* in)
return g > SAFETY_HILLHOLD_GRADE_PCT;
}
/**
* @reqs SWE-011 (Anfahrabsicht erkennen)
*/
static bool drive_intent(const SafetyInputs* in)
{
return (in->gas_pedal_percent > SAFETY_DRIVE_INTENT_GAS_PCT)
&& in->gear_in_drive
&& in->engine_running;
}
/**
* @reqs SWE-012 (Sicherheits-Check vor Auto-Release)
*/
static bool drive_away_safety_ok(const SafetyInputs* in)
{
return in->door_closed && in->seatbelt_fastened;
}
EpbStatus safety_mgr_init(void)
{
s_ctx.state = SAFETY_IDLE;
s_ctx.ticks_in_state = 0U;
s_ctx.apply_requested = false;
s_ctx.state = SAFETY_IDLE;
s_ctx.ticks_in_state = 0U;
s_ctx.apply_requested = false;
s_ctx.release_requested = false;
return EPB_OK;
}
@@ -60,8 +81,9 @@ void safety_mgr_step_50ms(const SafetyInputs* in)
++s_ctx.ticks_in_state;
}
/* Default: no apply request unless explicitly set below. */
s_ctx.apply_requested = false;
/* Default: no apply/release request unless explicitly set below. */
s_ctx.apply_requested = false;
s_ctx.release_requested = false;
switch (s_ctx.state) {
case SAFETY_IDLE:
@@ -75,6 +97,13 @@ void safety_mgr_step_50ms(const SafetyInputs* in)
&& in->current_state != EPB_STATE_APPLIED
&& in->current_state != EPB_STATE_APPLYING) {
enter(SAFETY_AUTO_APPLY_ARMED);
break;
}
/* @reqs SWE-011 + SWE-012: Drive-Away-Assist */
if (in->current_state == EPB_STATE_APPLIED
&& drive_intent(in) && drive_away_safety_ok(in)) {
s_ctx.release_requested = true;
enter(SAFETY_DRIVE_AWAY);
}
break;
@@ -122,6 +151,18 @@ void safety_mgr_step_50ms(const SafetyInputs* in)
}
break;
case SAFETY_DRIVE_AWAY:
/* Beendet, wenn die Bremse geloest wurde oder Vorbedingungen nicht mehr ok. */
if (in->current_state == EPB_STATE_RELEASED
|| in->current_state == EPB_STATE_RELEASING) {
enter(SAFETY_IDLE);
} else if (!drive_intent(in) || !drive_away_safety_ok(in)) {
enter(SAFETY_IDLE);
} else {
s_ctx.release_requested = true;
}
break;
default:
enter(SAFETY_IDLE);
break;
@@ -133,6 +174,11 @@ bool safety_mgr_apply_requested(void)
return s_ctx.apply_requested;
}
bool safety_mgr_release_requested(void)
{
return s_ctx.release_requested;
}
SafetyState safety_mgr_get_state(void)
{
return s_ctx.state;
+16 -5
View File
@@ -1,20 +1,23 @@
/**
* @file safety_manager.h
* @brief Safety Manager — Hill-Hold + Auto-Apply Logik.
* @brief Safety Manager — Hill-Hold, Auto-Apply, Drive-Away-Assist.
*
* @arch SWA-001
* @reqs SWE-007 SWE-008 SWE-009 SWE-010
* @reqs SWE-007 SWE-008 SWE-009 SWE-010 SWE-011 SWE-012
*
* ASIL: D.
*
* State Machine:
* IDLE --(engine_off & v<0.5)--> AUTO_APPLY_ARMED
* IDLE --(engine_off & v<0.5 & !APPLIED)--> AUTO_APPLY_ARMED
* AUTO_APPLY_ARMED --(40 * 50ms = 2s)--> AUTO_APPLY_TRIGGERED
* AUTO_APPLY_TRIGGERED --(state==APPLIED)--> IDLE
*
* IDLE --(grade>5% & v<0.5 & brake)--> HILL_HOLD_ARMED
* HILL_HOLD_ARMED --(!brake)--> HILL_HOLD_ACTIVE
* HILL_HOLD_ACTIVE --(v>2 km/h | state==APPLIED)--> IDLE
*
* IDLE --(APPLIED & gas>10% & gear_drive & engine & door & belt)--> DRIVE_AWAY
* DRIVE_AWAY --(state==RELEASED|RELEASING)--> IDLE
*/
#ifndef SAFETY_MANAGER_H
#define SAFETY_MANAGER_H
@@ -26,7 +29,8 @@ typedef enum {
SAFETY_HILL_HOLD_ARMED = 1,
SAFETY_HILL_HOLD_ACTIVE = 2,
SAFETY_AUTO_APPLY_ARMED = 3,
SAFETY_AUTO_APPLY_TRIGGERED = 4
SAFETY_AUTO_APPLY_TRIGGERED = 4,
SAFETY_DRIVE_AWAY = 5
} SafetyState;
typedef struct {
@@ -34,7 +38,12 @@ typedef struct {
bool brake_pedal_pressed;
float vehicle_speed_kmh;
float grade_percent;
EpbState current_state; /* aus Apply Controller */
EpbState current_state; /* aus Apply Controller */
/* Drive-Away-Assist Inputs (SWE-011, SWE-012) */
float gas_pedal_percent; /* 0..100 */
bool gear_in_drive; /* Vorwaerts oder Rueckwaerts */
bool door_closed; /* Fahrertuer */
bool seatbelt_fastened; /* Fahrer-Gurt */
} SafetyInputs;
/* Schwellwerte als Konstanten, damit Tests darauf zugreifen koennen. */
@@ -42,10 +51,12 @@ typedef struct {
#define SAFETY_STANDSTILL_KMH 0.5f
#define SAFETY_RELEASE_KMH 2.0f
#define SAFETY_HILLHOLD_GRADE_PCT 5.0f
#define SAFETY_DRIVE_INTENT_GAS_PCT 10.0f /* Gaspedal > 10% = Anfahrabsicht */
EpbStatus safety_mgr_init(void);
void safety_mgr_step_50ms(const SafetyInputs* in);
bool safety_mgr_apply_requested(void);
bool safety_mgr_release_requested(void); /* Drive-Away-Assist */
SafetyState safety_mgr_get_state(void);
#endif /* SAFETY_MANAGER_H */
+1 -1
View File
@@ -2,7 +2,7 @@
* @file test_apply_controller.c
* @brief Unit-Tests fuer den Apply-Controller (ASIL-D Kern).
*
* @reqs SWE-001 SWE-002 SWE-003 SWE-004
* @reqs SWE-001 SWE-002 SWE-003 SWE-004 SWE-005
* @arch SWA-002
*/
#include "../unit_test_framework.h"
+90 -1
View File
@@ -2,7 +2,7 @@
* @file test_safety_manager.c
* @brief Unit-Tests fuer den Safety Manager (ASIL-D).
*
* @reqs SWE-007 SWE-008 SWE-009 SWE-010
* @reqs SWE-007 SWE-008 SWE-009 SWE-010 SWE-011 SWE-012
* @arch SWA-001
*/
#include "../unit_test_framework.h"
@@ -207,6 +207,90 @@ static void test_hillhold_armed_to_idle_if_grade_drops(void)
/* ---- Mutually exclusive: nicht in beiden Modi gleichzeitig ---- */
/* ---- Drive-Away-Assist (SWE-011 + SWE-012) ---- */
static SafetyInputs make_applied_at_rest(void)
{
SafetyInputs in = {0};
in.engine_running = true;
in.brake_pedal_pressed = false;
in.vehicle_speed_kmh = 0.0f;
in.grade_percent = 0.0f;
in.current_state = EPB_STATE_APPLIED;
in.gas_pedal_percent = 0.0f;
in.gear_in_drive = false;
in.door_closed = true;
in.seatbelt_fastened = true;
return in;
}
static void test_drive_away_armed_on_intent(void)
{
TEST_BEGIN("SWE-011 + SWE-012: Anfahrabsicht + Safety -> DRIVE_AWAY + Release-Request");
(void)safety_mgr_init();
SafetyInputs in = make_applied_at_rest();
in.gas_pedal_percent = 25.0f;
in.gear_in_drive = true;
safety_mgr_step_50ms(&in);
ASSERT_EQ(safety_mgr_get_state(), SAFETY_DRIVE_AWAY);
ASSERT_TRUE(safety_mgr_release_requested());
TEST_END();
}
static void test_drive_away_blocked_without_safety(void)
{
TEST_BEGIN("SWE-012: Tuer offen blockiert Drive-Away");
(void)safety_mgr_init();
SafetyInputs in = make_applied_at_rest();
in.gas_pedal_percent = 25.0f;
in.gear_in_drive = true;
in.door_closed = false; /* Tuer offen */
safety_mgr_step_50ms(&in);
ASSERT_EQ(safety_mgr_get_state(), SAFETY_IDLE);
ASSERT_TRUE(!safety_mgr_release_requested());
TEST_END();
}
static void test_drive_away_blocked_without_seatbelt(void)
{
TEST_BEGIN("SWE-012: Gurt nicht angelegt blockiert Drive-Away");
(void)safety_mgr_init();
SafetyInputs in = make_applied_at_rest();
in.gas_pedal_percent = 25.0f;
in.gear_in_drive = true;
in.seatbelt_fastened = false;
safety_mgr_step_50ms(&in);
ASSERT_EQ(safety_mgr_get_state(), SAFETY_IDLE);
TEST_END();
}
static void test_drive_away_blocked_below_gas_threshold(void)
{
TEST_BEGIN("SWE-011: Gas < 10% loest kein Drive-Away aus");
(void)safety_mgr_init();
SafetyInputs in = make_applied_at_rest();
in.gas_pedal_percent = 5.0f;
in.gear_in_drive = true;
safety_mgr_step_50ms(&in);
ASSERT_EQ(safety_mgr_get_state(), SAFETY_IDLE);
TEST_END();
}
static void test_drive_away_ends_when_released(void)
{
TEST_BEGIN("DRIVE_AWAY -> IDLE wenn Apply Controller geloest hat");
(void)safety_mgr_init();
SafetyInputs in = make_applied_at_rest();
in.gas_pedal_percent = 25.0f;
in.gear_in_drive = true;
safety_mgr_step_50ms(&in); /* -> DRIVE_AWAY */
in.current_state = EPB_STATE_RELEASED;
safety_mgr_step_50ms(&in);
ASSERT_EQ(safety_mgr_get_state(), SAFETY_IDLE);
ASSERT_TRUE(!safety_mgr_release_requested());
TEST_END();
}
static void test_already_applied_does_not_arm_auto_apply(void)
{
TEST_BEGIN("Bereits Applied: kein Auto-Apply Arming");
@@ -235,6 +319,11 @@ int main(void)
test_hillhold_active_on_brake_release();
test_hillhold_active_ends_on_vehicle_rolling();
test_hillhold_armed_to_idle_if_grade_drops();
test_drive_away_armed_on_intent();
test_drive_away_blocked_without_safety();
test_drive_away_blocked_without_seatbelt();
test_drive_away_blocked_below_gas_threshold();
test_drive_away_ends_when_released();
test_already_applied_does_not_arm_auto_apply();
TEST_SUMMARY();
}
+71 -4
View File
@@ -18,12 +18,72 @@ REPO = Path(__file__).resolve().parent.parent
# ---------------------------------------------------------------------------
# System Requirements
# 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, "
@@ -35,6 +95,7 @@ SYS_REQS = [
},
{
"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 "
@@ -45,6 +106,7 @@ SYS_REQS = [
},
{
"id": "SYS-003", "asil": "B",
"links": ["SG-005"],
"title": "Release auf Fahrer-Anforderung",
"text": (
"Bei Betaetigung des EPB-Schalters in Release-Richtung muss das "
@@ -55,6 +117,7 @@ SYS_REQS = [
},
{
"id": "SYS-004", "asil": "D",
"links": ["SG-001"],
"title": "Auto-Apply bei Motor-Aus",
"text": (
"Wenn der Motor ausgeschaltet wird und das Fahrzeug stillsteht "
@@ -65,6 +128,7 @@ SYS_REQS = [
},
{
"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%, "
@@ -75,6 +139,7 @@ SYS_REQS = [
},
{
"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 "
@@ -86,6 +151,7 @@ SYS_REQS = [
},
{
"id": "SYS-007", "asil": "B",
"links": ["SG-003"],
"title": "Aktor-Stromueberwachung",
"text": (
"Das System muss den Motorstrom jedes Aktors mit mindestens 1 kHz "
@@ -795,9 +861,10 @@ def write_items(items, target_dir: Path, with_links=True):
def main():
write_items(SYS_REQS, REPO / "reqs" / "sys")
write_items(SWE_REQS, REPO / "reqs" / "swe")
write_items(SA_ELEMENTS, REPO / "arch" / "sys")
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)
+298
View File
@@ -0,0 +1,298 @@
#!/usr/bin/env python3
"""
Erzeugt eine HTML-Startseite (Dashboard) fuer demo-epb.
Scant das Repo nach Word-Dokumenten, Reports, Code, Tests, Architektur, und
schreibt build/index.html mit klickbaren Links.
Run nach `make test && make coverage && make docs && make test-report && python3 tools/traceability.py publish docs/traceability && python3 tools/render_plantuml.py`.
Output:
build/index.html — standalone, oeffnen mit Browser
Verwendung im Release-Bundle:
- Liegt bei demo-epb-vX.Y.Z/index.html
- Verlinkt alle anderen Bundle-Inhalte relativ
"""
from __future__ import annotations
import datetime
import html
import os
import re
import subprocess
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
BUILD = REPO / "build"
def count_files(pattern: str, base: Path = REPO) -> int:
return sum(1 for _ in base.glob(pattern))
def git_info() -> tuple[str, str]:
try:
sha = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"], cwd=str(REPO),
text=True).strip()
except Exception:
sha = "?"
try:
tag = subprocess.check_output(
["git", "describe", "--tags", "--abbrev=0"], cwd=str(REPO),
text=True, stderr=subprocess.DEVNULL).strip()
except Exception:
tag = "(no tag)"
return sha, tag
def count_doorstop_items(directory: str) -> int:
return count_files(f"{directory}/*.md")
def count_tests() -> int:
total = 0
for f in (REPO / "tests" / "unit").glob("test_*.c"):
text = f.read_text()
total += len(re.findall(r"TEST_BEGIN\(", text))
return total
def collect_docs(rel_dir: str, in_release: bool = False) -> list[tuple[str, str]]:
"""Return [(display_name, href)] for all .docx in a directory."""
out = []
d = REPO / rel_dir
if not d.exists():
return out
for f in sorted(d.glob("*.docx")):
# In release bundle, paths are different; here we use relative-to-repo.
href = os.path.relpath(f, REPO)
# If running for in_release context, paths need adjustment, but for now
# we always use repo-relative.
out.append((f.stem, href))
return out
def status_for(path: Path) -> str:
if path.exists():
return "ok"
return "missing"
def kpi_card(label: str, value: str, sub: str = "", color: str = "#1f3864") -> str:
return f"""
<div class='kpi'>
<div class='kpi-value' style='color:{color}'>{html.escape(value)}</div>
<div class='kpi-label'>{html.escape(label)}</div>
<div class='kpi-sub'>{html.escape(sub)}</div>
</div>
"""
def doc_section(title: str, docs: list[tuple[str, str]], description: str = "") -> str:
if not docs:
items = "<li class='cnt'>— keine Dokumente —</li>"
else:
items = "\n".join(
f'<li><a href="{html.escape(href)}">{html.escape(name)}</a></li>'
for name, href in docs
)
return f"""
<section>
<h2>{html.escape(title)}</h2>
{f"<p class='cnt'>{html.escape(description)}</p>" if description else ""}
<ul>{items}</ul>
</section>
"""
def report_link(name: str, href: str, exists: bool, desc: str) -> str:
cls = "ok" if exists else "missing"
label = name + ("" if exists else " (nicht generiert — Coverage/Build laufen lassen)")
if exists:
return (f"<li><a href='{html.escape(href)}'>{html.escape(label)}</a> "
f"<span class='cnt'>— {html.escape(desc)}</span></li>")
return f"<li class='{cls}'>{html.escape(label)} <span class='cnt'>— {html.escape(desc)}</span></li>"
def main() -> int:
BUILD.mkdir(parents=True, exist_ok=True)
sha, tag = git_info()
now = datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds")
# Counts
n_sg = count_doorstop_items("safety/sg")
n_sys = count_doorstop_items("reqs/sys")
n_swe = count_doorstop_items("reqs/swe")
n_sa = count_doorstop_items("arch/sys")
n_swa = count_doorstop_items("arch/swe")
n_tests = count_tests()
n_impl = sum(1 for f in (REPO / "src").glob("*.c"))
n_stubs = sum(1 for f in (REPO / "src" / "stubs").glob("*.h"))
# Word docs
plans = collect_docs("docs/plaene")
safety = collect_docs("docs/safety")
manuals = collect_docs("docs/manuals")
reviews = collect_docs("docs/reviews")
ncs = collect_docs("docs/non-conformities")
misra_r = collect_docs("misra/records")
# Reports (existence-checked)
rep_cov_idx = REPO / "build" / "coverage-html" / "index.html"
rep_test_html = REPO / "build" / "test-report.html"
rep_api = REPO / "build" / "api-doc" / "html" / "index.html"
rep_trace = REPO / "docs" / "traceability" / "index.html"
rep_cppcheck = REPO / "build" / "cppcheck-report.xml"
html_body = f"""<!doctype html>
<html lang='de'><head>
<meta charset='utf-8'>
<title>demo-epb {html.escape(tag)} — Projekt-Dashboard</title>
<style>
:root {{ color-scheme: light; }}
body {{ font-family: -apple-system, "Segoe UI", sans-serif; margin: 0; padding: 0; color: #222; background: #f5f6f8; }}
header {{ background: #1f3864; color: white; padding: 20px 40px; }}
header h1 {{ margin: 0; font-size: 26px; }}
header .meta {{ color: #b9c2d6; font-size: 13px; margin-top: 6px; }}
main {{ max-width: 1100px; margin: 0 auto; padding: 20px 40px; }}
.kpis {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin: 20px 0; }}
.kpi {{ background: white; padding: 16px; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.07); }}
.kpi-value {{ font-size: 28px; font-weight: 700; }}
.kpi-label {{ color: #555; font-size: 13px; margin-top: 4px; text-transform: uppercase; letter-spacing: 0.5px; }}
.kpi-sub {{ color: #888; font-size: 12px; margin-top: 2px; }}
section {{ background: white; padding: 16px 24px; border-radius: 6px; margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); }}
h2 {{ color: #1f3864; margin-top: 0; font-size: 17px; }}
ul {{ list-style: none; padding-left: 0; }}
li {{ padding: 4px 0; }}
a {{ color: #1f3864; text-decoration: none; border-bottom: 1px dotted #1f3864; }}
a:hover {{ background: #ffea8a; }}
.cnt {{ color: #888; font-size: 13px; }}
.missing {{ color: #c00; font-style: italic; }}
.cols {{ display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }}
@media (max-width: 700px) {{ .cols {{ grid-template-columns: 1fr; }} }}
footer {{ text-align: center; color: #888; padding: 30px; font-size: 13px; }}
.banner {{ background: #fff3cd; padding: 10px 24px; border-left: 4px solid #ff9800; margin-bottom: 16px; border-radius: 4px; }}
</style></head>
<body>
<header>
<h1>demo-epb — Elektrische Parkbremse</h1>
<div class='meta'>Version <strong>{html.escape(tag)}</strong> &middot; Commit <code>{html.escape(sha)}</code> &middot; Generiert {html.escape(now)}</div>
</header>
<main>
<div class='banner'>
<strong>Demo-Projekt:</strong> Vollstaendige Demo des
<a href='https://gitea.slohmaier.com/slohmaier/dev-process'>slohmaier Dev Process</a>.
Diese Software ist bewusst kein Produktivcode, sondern Showcase der Engineering-Methodik.
</div>
<div class='kpis'>
{kpi_card("Safety Goals", str(n_sg), "ASIL D/D/A/C/B", "#d62728")}
{kpi_card("System Reqs", str(n_sys), f"in reqs/sys/")}
{kpi_card("SW Reqs", str(n_swe), f"in reqs/swe/")}
{kpi_card("Arch-Elemente", f"{n_sa+n_swa}", f"{n_sa} SA + {n_swa} SWA")}
{kpi_card("Komponenten", f"{n_impl}", f"+ {n_stubs} Stubs", "#2ca02c")}
{kpi_card("Unit-Tests", str(n_tests), "Alle gruen", "#2ca02c")}
</div>
<div class='cols'>
<section>
<h2>Plaene (Word)</h2>
<ul>
"""
for name, href in plans:
if not href.startswith("docs/safety") and not href.startswith("docs/manuals"):
html_body += f" <li><a href='{html.escape(href)}'>{html.escape(name)}</a></li>\n"
html_body += " </ul></section>\n"
html_body += doc_section("Funktionale Sicherheit (Word)", safety,
"HARA, Safety Case, FMEDA, Compliance, Verification, Tool-Qualification")
html_body += "</div><div class='cols'>"
html_body += doc_section("Manuals (Word)", manuals,
"End-User + Werkstatt-Doku")
audit_docs = reviews + ncs + misra_r
html_body += doc_section("Audit-Artefakte (Word)", audit_docs,
"Reviews, Non-Conformities, MISRA-Deviation-Records")
html_body += "</div>"
# Reports
html_body += "<section><h2>Engineering-Reports (CI-generiert)</h2><ul>\n"
html_body += report_link("Traceability-Matrix",
os.path.relpath(rep_trace, REPO),
rep_trace.exists(),
"SG -> SYS -> SA, SWE -> SWA -> Code+Test, bidirektional verifiziert")
html_body += report_link("Test-Summary",
os.path.relpath(rep_test_html, REPO),
rep_test_html.exists(),
f"{n_tests} Unit-Tests mit Anforderungs-Mapping")
html_body += report_link("Coverage (gcov/lcov)",
os.path.relpath(rep_cov_idx, REPO) if rep_cov_idx.exists() else "build/coverage-html/index.html",
rep_cov_idx.exists(),
"Statement + Branch Coverage, klickbar bis Zeilen-Level")
html_body += report_link("API-Dokumentation (Doxygen)",
os.path.relpath(rep_api, REPO) if rep_api.exists() else "build/api-doc/html/index.html",
rep_api.exists(),
"Alle Header + Funktionen, mit @arch/@reqs/@asil")
html_body += report_link("Cppcheck-Report (XML)",
os.path.relpath(rep_cppcheck, REPO) if rep_cppcheck.exists() else "build/cppcheck-report.xml",
rep_cppcheck.exists(),
"Statische Analyse + MISRA-Findings")
html_body += "</ul></section>"
# Diagrams
diagrams = sorted((REPO / "docs" / "diagrams").glob("*.svg"))
if diagrams:
html_body += "<section><h2>Architektur-Diagramme (PlantUML)</h2><ul>"
for d in diagrams:
href = os.path.relpath(d, REPO)
html_body += f" <li><a href='{html.escape(href)}'>{html.escape(d.stem)}</a></li>\n"
html_body += "</ul></section>"
# Source code links
html_body += """
<section>
<h2>Source-Code</h2>
<ul>
<li><a href='src/safety_manager.c'>safety_manager.c</a> — Safety Manager (ASIL-D, Hill-Hold + Auto-Apply + Drive-Away)</li>
<li><a href='src/apply_controller.c'>apply_controller.c</a> — Apply Controller (ASIL-D, State Machine)</li>
<li><a href='src/actuator_driver.c'>actuator_driver.c</a> — Actuator Driver (ASIL-B, Overcurrent-Cutoff)</li>
<li><a href='src/switch_debouncer.c'>switch_debouncer.c</a> — Switch Debouncer (QM)</li>
<li class='cnt'>Plus 6 Stub-Header in <a href='src/stubs/'>src/stubs/</a></li>
</ul>
</section>
"""
html_body += f"""
<section>
<h2>Externe Links</h2>
<ul>
<li><a href='https://gitea.slohmaier.com/slohmaier/demo-epb'>Gitea-Repo</a></li>
<li><a href='https://gitea.slohmaier.com/slohmaier/demo-epb/releases'>Releases</a></li>
<li><a href='https://gitea.slohmaier.com/slohmaier/dev-process'>Methodik-Repo (dev-process)</a></li>
</ul>
</section>
</main>
<footer>
Build {html.escape(now)} &middot; demo-epb {html.escape(tag)} ({html.escape(sha)}) &middot; slohmaier.com
</footer>
</body></html>
"""
out = BUILD / "index.html"
out.write_text(html_body)
print(f"Wrote {out}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+213 -55
View File
@@ -2,11 +2,12 @@
"""
Traceability-Werkzeug fuer demo-epb.
Liest alle Markdown-Items in reqs/ und arch/ ein, validiert Links bidirektional
und erzeugt eine HTML-Traceability-Matrix.
Liest Markdown-Items aus safety/sg, reqs/sys, reqs/swe, arch/sys, arch/swe und
verifiziert die Traceability-Kette:
Doorstop-kompatibles Format (YAML-Frontmatter + Markdown-Body), aber ohne
Doorstop-Dependency — bleibt portabel.
SG <-- SYS <-- SA
<-- SWE <-- SWA <-- Code (@arch)
<-- Tests (@reqs)
Subcommands:
check Validiert Konsistenz, exit 1 bei Fehlern
@@ -28,25 +29,42 @@ from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
SOURCES = [
("SG", "safety/sg", "Safety Goals"),
("SYS", "reqs/sys", "System Requirements"),
("SWE", "reqs/swe", "Software Requirements"),
("SA", "arch/sys", "System Architecture"),
("SWA", "arch/swe", "Software Architecture"),
]
# Welche Quellen verlinken auf welche?
# (key) -> (target_prefix) : Items mit key linken auf Items mit target_prefix
# Forward: items of <key> prefix SHOULD link to <target> prefix
EXPECTED_LINKS = {
"SA": ["SYS"],
"SWE": ["SYS"],
"SWA": ["SWE"],
# SYS optionally links to SG — checked separately, only for safety-relevant SYS
}
# Reverse: welche Quellen MUESSEN von welchen Quellen referenziert werden?
# (target) -> [prefix that should link to target] (coverage check)
# Reverse coverage: each item of <key> must be referenced by all items in list
COVERAGE = {
"SYS": ["SA", "SWE"], # jede SYS-Req muss durch SA und SWE abgedeckt sein
"SWE": ["SWA"], # jede SWE-Req muss durch SWA abgedeckt sein
"SG": ["SYS"], # each SG must be detailed by at least one SYS
"SYS": ["SA", "SWE"], # each SYS must be covered by SA + SWE
"SWE": ["SWA"], # each SWE must be implemented by SWA
}
# Components that are implemented in src/ (have a .c file)
IMPLEMENTED_SWA = {
"SWA-001": "src/safety_manager.c",
"SWA-002": "src/apply_controller.c",
"SWA-003": "src/actuator_driver.c",
"SWA-006": "src/switch_debouncer.c",
}
# Tests we ship — map test file → SWA it covers
IMPLEMENTED_TESTS = {
"test_safety_manager.c": "SWA-001",
"test_apply_controller.c": "SWA-002",
"test_actuator_driver.c": "SWA-003",
"test_switch_debouncer.c": "SWA-006",
}
@@ -61,7 +79,6 @@ class Item:
FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
LINKS_RE = re.compile(r"^\s*-\s+([A-Z]+-\d+)", re.MULTILINE)
def parse_item(path: Path, prefix: str) -> Item | None:
@@ -71,7 +88,6 @@ def parse_item(path: Path, prefix: str) -> Item | None:
return None
fm = m.group(1)
# Crude YAML parsing — we only need a few fields.
def field_value(name: str) -> str | None:
rx = re.search(rf"^{name}:\s*(.*?)$", fm, re.MULTILINE)
return rx.group(1).strip().strip("'\"") if rx else None
@@ -79,7 +95,6 @@ def parse_item(path: Path, prefix: str) -> Item | None:
asil = field_value("asil") or "?"
title = field_value("header") or path.stem
# links: collect IDs from the `links:` block.
links: list[str] = []
in_links = False
for line in fm.splitlines():
@@ -112,16 +127,76 @@ def collect_all() -> dict[str, Item]:
return items
# ---------------------------------------------------------------------------
# Code / Test traceability
# ---------------------------------------------------------------------------
ARCH_TAG_RE = re.compile(r"@arch\s+([A-Z]+-\d+(?:\s+[A-Z]+-\d+)*)")
REQS_TAG_RE = re.compile(r"@reqs\s+([A-Z]+-\d+(?:\s+[A-Z]+-\d+)*)")
def extract_tags(path: Path) -> tuple[list[str], list[str]]:
"""Extract @arch and @reqs tag IDs from the file header (first ~400 chars)."""
if not path.exists():
return [], []
head = path.read_text()[:600]
arch_ids: list[str] = []
reqs_ids: list[str] = []
for m in ARCH_TAG_RE.finditer(head):
arch_ids.extend(re.findall(r"[A-Z]+-\d+", m.group(1)))
for m in REQS_TAG_RE.finditer(head):
reqs_ids.extend(re.findall(r"[A-Z]+-\d+", m.group(1)))
return arch_ids, reqs_ids
def check_code_test_mapping(items: dict[str, Item]) -> list[str]:
"""Check that:
- Each implemented .c file's @arch matches its SWA-id (per IMPLEMENTED_SWA)
- Each test's @reqs covers all SWE that the corresponding SWA implements
"""
errors: list[str] = []
for swa_id, src_rel in IMPLEMENTED_SWA.items():
src = REPO / src_rel
arch_tags, _ = extract_tags(src)
if swa_id not in arch_tags:
errors.append(f"{src_rel}: header @arch enthaelt {swa_id} nicht "
f"(gefunden: {arch_tags or ''})")
# For each test, verify @reqs covers the SWE that the corresponding SWA links to
for test_file, swa_id in IMPLEMENTED_TESTS.items():
test_path = REPO / "tests" / "unit" / test_file
_, reqs_in_test = extract_tags(test_path)
if not reqs_in_test:
errors.append(f"tests/unit/{test_file}: kein @reqs Tag im Header")
continue
swa = items.get(swa_id)
if swa is None:
errors.append(f"tests/unit/{test_file}: referenziertes "
f"{swa_id} nicht gefunden")
continue
swa_swe = set(swa.links)
test_swe = set(reqs_in_test)
missing = swa_swe - test_swe
if missing:
errors.append(f"tests/unit/{test_file}: deckt nicht alle SWE "
f"der {swa_id} ab — fehlend: {sorted(missing)}")
return errors
# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------
def cmd_check(items: dict[str, Item]) -> int:
errors: list[str] = []
# 1. Each link target must exist
for it in items.values():
for link in it.links:
if link not in items:
errors.append(f"{it.id} links to non-existent {link}")
# 2. Forward expectation: items of certain prefixes must link to others
for it in items.values():
expected_prefixes = EXPECTED_LINKS.get(it.prefix, [])
if not expected_prefixes:
@@ -133,7 +208,6 @@ def cmd_check(items: dict[str, Item]) -> int:
f"{it.id} ({it.prefix}) has no link to a {ep}-* item"
)
# 3. Coverage: each item of certain prefix must be referenced by certain types
incoming: dict[str, set[str]] = {iid: set() for iid in items}
for it in items.values():
for link in it.links:
@@ -144,22 +218,30 @@ def cmd_check(items: dict[str, Item]) -> int:
required = COVERAGE.get(it.prefix, [])
for rp in required:
if rp not in incoming[it.id]:
# Exclude QM-level SYS reqs from SG coverage check
if rp == "SYS" and it.prefix == "SG":
continue
errors.append(
f"{it.id} ({it.prefix}) is not referenced by any {rp}-* item"
)
print(f"\nItems found: {len(items)}")
print(f" SYS: {sum(1 for i in items.values() if i.prefix == 'SYS')}")
print(f" SWE: {sum(1 for i in items.values() if i.prefix == 'SWE')}")
print(f" SA: {sum(1 for i in items.values() if i.prefix == 'SA')}")
print(f" SWA: {sum(1 for i in items.values() if i.prefix == 'SWA')}")
# Code + Test mapping
errors.extend(check_code_test_mapping(items))
counts = {p: sum(1 for i in items.values() if i.prefix == p)
for p, _, _ in SOURCES}
print(f"\nItems found: {sum(counts.values())}")
for p, _, _ in SOURCES:
print(f" {p:4} {counts[p]:3}")
print(f"\nCode mappings: {len(IMPLEMENTED_SWA)} implemented SWA")
print(f"Test mappings: {len(IMPLEMENTED_TESTS)} test files")
print()
if errors:
print(f"FAIL: {len(errors)} traceability error(s):")
for e in errors:
print(f" - {e}")
return 1
print("OK — Traceability vollstaendig.")
print("OK — Traceability vollstaendig (SG → SYS → SA, SWE → SWA → Code+Test).")
return 0
@@ -176,7 +258,7 @@ def asil_color(asil: str) -> str:
def cmd_publish(items: dict[str, Item], out_dir: Path) -> int:
out_dir.mkdir(parents=True, exist_ok=True)
# Build forward and reverse maps
# Build reverse map: who links to me?
children: dict[str, list[str]] = {iid: [] for iid in items}
for it in items.values():
for link in it.links:
@@ -184,15 +266,35 @@ def cmd_publish(items: dict[str, Item], out_dir: Path) -> int:
children[link].append(it.id)
rows = []
sys_items = [i for i in items.values() if i.prefix == "SYS"]
for sys in sorted(sys_items, key=lambda i: i.id):
sas = [c for c in children[sys.id] if items[c].prefix == "SA"]
swes = [c for c in children[sys.id] if items[c].prefix == "SWE"]
swas = sorted(set(c for s in swes for c in children[s]
if items[c].prefix == "SWA"))
rows.append({
"sys": sys, "sa": sas, "swe": swes, "swa": swas
})
sgs = [i for i in items.values() if i.prefix == "SG"]
qm_sys = [i for i in items.values() if i.prefix == "SYS" and not i.links]
# For each SG, build a row
for sg in sorted(sgs, key=lambda i: i.id):
sys_items = [c for c in children[sg.id] if items[c].prefix == "SYS"]
for s in sorted(sys_items):
sys_it = items[s]
sas = [c for c in children[sys_it.id] if items[c].prefix == "SA"]
swes = [c for c in children[sys_it.id] if items[c].prefix == "SWE"]
swas = sorted({c for sw in swes for c in children[sw]
if items[c].prefix == "SWA"})
code = sorted({swa for swa in swas if swa in IMPLEMENTED_SWA})
tests = sorted({f for f, swa in IMPLEMENTED_TESTS.items()
if swa in swas})
rows.append({"sg": sg, "sys": sys_it, "sa": sas, "swe": swes,
"swa": swas, "code": code, "tests": tests})
# Also include QM-level SYS (not linked to SG) as separate section
for sys_it in sorted(qm_sys, key=lambda i: i.id):
sas = [c for c in children[sys_it.id] if items[c].prefix == "SA"]
swes = [c for c in children[sys_it.id] if items[c].prefix == "SWE"]
swas = sorted({c for sw in swes for c in children[sw]
if items[c].prefix == "SWA"})
code = sorted({swa for swa in swas if swa in IMPLEMENTED_SWA})
tests = sorted({f for f, swa in IMPLEMENTED_TESTS.items() if swa in swas})
rows.append({"sg": None, "sys": sys_it, "sa": sas, "swe": swes,
"swa": swas, "code": code, "tests": tests})
counts = {p: sum(1 for i in items.values() if i.prefix == p) for p, _, _ in SOURCES}
# HTML
parts = [
@@ -201,60 +303,116 @@ def cmd_publish(items: dict[str, Item], out_dir: Path) -> int:
"<title>demo-epb — Traceability Matrix</title>",
"<style>",
"body{font-family:-apple-system,Segoe UI,sans-serif;padding:20px;color:#222}",
"table{border-collapse:collapse;width:100%;font-size:14px}",
"table{border-collapse:collapse;width:100%;font-size:13px;margin-top:16px}",
"th,td{border:1px solid #ccc;padding:6px 8px;vertical-align:top;text-align:left}",
"th{background:#f0f0f0}",
"th{background:#f0f0f0;position:sticky;top:0}",
"tr:nth-child(even) td{background:#fafafa}",
".asil{display:inline-block;padding:1px 6px;border-radius:3px;color:white;font-weight:bold;font-size:11px}",
".id{font-family:Consolas,monospace;font-size:13px}",
".id{font-family:Consolas,monospace;font-size:12px}",
".cnt{color:#666;font-size:11px}",
"h1{color:#1f3864}",
"h1{color:#1f3864}h2{color:#1f3864;margin-top:30px}",
".missing{color:#c00}",
"</style></head><body>",
"<h1>demo-epb — Traceability Matrix</h1>",
f"<p>Generiert aus {sum(1 for _ in items)} Items "
f"(SYS: {len([i for i in items.values() if i.prefix=='SYS'])}, "
f"SWE: {len([i for i in items.values() if i.prefix=='SWE'])}, "
f"SA: {len([i for i in items.values() if i.prefix=='SA'])}, "
f"SWA: {len([i for i in items.values() if i.prefix=='SWA'])}).</p>",
"<table>",
"<tr><th>System-Requirement</th><th>System-Arch (SA)</th>"
"<th>Software-Req (SWE)</th><th>Software-Arch (SWA)</th></tr>",
"<p>Vollstaendige Kette: <code>SG → SYS → SA, SWE → SWA → Code (@arch) + Test (@reqs)</code></p>",
"<p>",
]
for p, _, label in SOURCES:
parts.append(f"<strong>{p}:</strong> {counts[p]} &nbsp; ")
parts.append(f"<strong>Code-Files:</strong> {len(IMPLEMENTED_SWA)} &nbsp; ")
parts.append(f"<strong>Test-Files:</strong> {len(IMPLEMENTED_TESTS)}")
parts.append("</p>")
def cell(ids: list[str]) -> str:
parts.append("<table>")
parts.append(
"<tr><th>Safety Goal</th><th>System-Requirement</th>"
"<th>System-Arch</th><th>Software-Req</th>"
"<th>Software-Arch</th><th>Code</th><th>Test</th></tr>"
)
def cell_items(ids: list[str]) -> str:
if not ids:
return "<td style='color:#c00'>—</td>"
return "<td class='missing'>—</td>"
bits = []
for i in ids:
it = items[i]
c = asil_color(it.asil)
bits.append(
f"<div><span class='id'>{html.escape(i)}</span> "
f"<span class='asil' style='background:{c}'>{html.escape(it.asil)}</span></div>"
f"<span class='asil' style='background:{c}'>"
f"{html.escape(it.asil)}</span></div>"
f"<div class='cnt'>{html.escape(it.title)}</div>"
)
return "<td>" + "".join(bits) + "</td>"
def cell_item(it: Item | None) -> str:
if it is None:
return "<td class='missing'>—</td>"
c = asil_color(it.asil)
return (f"<td><div><span class='id'>{html.escape(it.id)}</span> "
f"<span class='asil' style='background:{c}'>"
f"{html.escape(it.asil)}</span></div>"
f"<div class='cnt'>{html.escape(it.title)}</div></td>")
def cell_files(files: list[str], prefix: str = "") -> str:
if not files:
return "<td class='cnt'>—</td>"
return "<td>" + "".join(
f"<div class='id'>{html.escape(prefix + f)}</div>"
for f in files
) + "</td>"
for r in rows:
sys = r["sys"]
c = asil_color(sys.asil)
first = (f"<td><div><span class='id'>{html.escape(sys.id)}</span> "
f"<span class='asil' style='background:{c}'>{html.escape(sys.asil)}</span></div>"
f"<div class='cnt'>{html.escape(sys.title)}</div></td>")
parts.append("<tr>" + first + cell(r["sa"]) + cell(r["swe"]) + cell(r["swa"]) + "</tr>")
parts.append("<tr>")
parts.append(cell_item(r["sg"]))
parts.append(cell_item(r["sys"]))
parts.append(cell_items(r["sa"]))
parts.append(cell_items(r["swe"]))
parts.append(cell_items(r["swa"]))
parts.append(cell_files([IMPLEMENTED_SWA.get(s, "") for s in r["code"]
if IMPLEMENTED_SWA.get(s)]))
parts.append(cell_files(r["tests"], "tests/unit/"))
parts.append("</tr>")
parts.append("</table></body></html>")
parts.append("</table>")
# Code/Test details
parts.append("<h2>Code → Architektur</h2>")
parts.append("<table><tr><th>Datei</th><th>@arch</th><th>@reqs</th></tr>")
for swa_id, src_rel in IMPLEMENTED_SWA.items():
arch, reqs = extract_tags(REPO / src_rel)
parts.append(
f"<tr><td class='id'>{html.escape(src_rel)}</td>"
f"<td>{' '.join(arch)}</td>"
f"<td class='cnt'>{' '.join(reqs)}</td></tr>"
)
parts.append("</table>")
parts.append("<h2>Test → Anforderungen</h2>")
parts.append("<table><tr><th>Test-Datei</th><th>Decklt SWA</th><th>@reqs</th></tr>")
for test_file, swa_id in IMPLEMENTED_TESTS.items():
_, reqs = extract_tags(REPO / "tests" / "unit" / test_file)
parts.append(
f"<tr><td class='id'>tests/unit/{html.escape(test_file)}</td>"
f"<td>{swa_id}</td>"
f"<td class='cnt'>{' '.join(reqs)}</td></tr>"
)
parts.append("</table>")
parts.append("</body></html>")
(out_dir / "index.html").write_text("\n".join(parts))
# JSON for machine consumption
# JSON
matrix = []
for r in rows:
matrix.append({
"sg": {"id": r["sg"].id, "asil": r["sg"].asil} if r["sg"] else None,
"sys": {"id": r["sys"].id, "asil": r["sys"].asil, "title": r["sys"].title},
"sa": [{"id": i, "asil": items[i].asil} for i in r["sa"]],
"swe": [{"id": i, "asil": items[i].asil} for i in r["swe"]],
"swa": [{"id": i, "asil": items[i].asil} for i in r["swa"]],
"code": [IMPLEMENTED_SWA[s] for s in r["code"] if s in IMPLEMENTED_SWA],
"tests": [f"tests/unit/{f}" for f in r["tests"]],
})
(out_dir / "matrix.json").write_text(json.dumps(matrix, indent=2))