feat: Safety Manager + Traceability + PlantUML in CI
Validate / build-and-test (push) Successful in 30s

- Implement Safety Manager (SWA-001, ASIL-D): Hill-Hold + Auto-Apply
  state machine, 13 unit tests
- Update SWA-002 + SWA-001 link coverage so all SWE reqs are covered
- New tool: tools/traceability.py — Markdown-frontmatter-basierter
  Traceability-Checker + HTML/JSON-Matrix-Generator (Doorstop-Format ohne
  Doorstop-Dependency)
- New tool: tools/render_plantuml.py — extrahiert PlantUML-Bloecke aus
  arch/**.md und rendert via plantuml.com zu SVG
- validate.yml: neue Steps Traceability-Check, Matrix-Publish, PlantUML-
  Render; uploaded als Gitea-Artefakte
This commit is contained in:
Stefan Lohmaier
2026-05-11 23:51:55 -07:00
parent 7c1848cb26
commit 4351dfa4e1
19 changed files with 1292 additions and 37 deletions
+139
View File
@@ -0,0 +1,139 @@
/**
* @file safety_manager.c
* @brief Safety Manager — Hill-Hold + Auto-Apply Logik.
*
* @arch SWA-001
* @reqs SWE-007 SWE-008 SWE-009 SWE-010
*
* ASIL: D. Diese Komponente entscheidet, wann der Apply Controller eine
* Apply-Anforderung erhaelt (Hill-Hold-Uebergabe, Auto-Apply bei Motor-Aus).
* Aenderungen erfordern Technical Review mit 2 Approvals.
*/
#include <stddef.h>
#include "safety_manager.h"
typedef struct {
SafetyState state;
uint16_t ticks_in_state; /* 50ms-Ticks im aktuellen Zustand */
bool apply_requested;
} SafetyCtx;
static SafetyCtx s_ctx;
static void enter(SafetyState s)
{
s_ctx.state = s;
s_ctx.ticks_in_state = 0U;
}
static bool standstill(const SafetyInputs* in)
{
return in->vehicle_speed_kmh < SAFETY_STANDSTILL_KMH;
}
static bool grade_steep(const SafetyInputs* in)
{
/* @reqs SWE-009: Hill-Hold ab |grade| > 5% */
float g = in->grade_percent;
if (g < 0.0f) {
g = -g;
}
return g > SAFETY_HILLHOLD_GRADE_PCT;
}
EpbStatus safety_mgr_init(void)
{
s_ctx.state = SAFETY_IDLE;
s_ctx.ticks_in_state = 0U;
s_ctx.apply_requested = false;
return EPB_OK;
}
void safety_mgr_step_50ms(const SafetyInputs* in)
{
if (in == NULL) {
return;
}
if (s_ctx.ticks_in_state < UINT16_MAX) {
++s_ctx.ticks_in_state;
}
/* Default: no apply request unless explicitly set below. */
s_ctx.apply_requested = false;
switch (s_ctx.state) {
case SAFETY_IDLE:
/* @reqs SWE-009: Hill-Hold-Aktivierung */
if (grade_steep(in) && standstill(in) && in->brake_pedal_pressed) {
enter(SAFETY_HILL_HOLD_ARMED);
break;
}
/* @reqs SWE-007: Motor-Aus-Bedingung erkennen */
if (!in->engine_running && standstill(in)
&& in->current_state != EPB_STATE_APPLIED
&& in->current_state != EPB_STATE_APPLYING) {
enter(SAFETY_AUTO_APPLY_ARMED);
}
break;
case SAFETY_HILL_HOLD_ARMED:
/* @reqs SWE-010: Beim Loslassen des Bremspedals Apply triggern */
if (!in->brake_pedal_pressed) {
s_ctx.apply_requested = true;
enter(SAFETY_HILL_HOLD_ACTIVE);
break;
}
/* Bedingung fuer Hill-Hold nicht mehr erfuellt? */
if (!grade_steep(in) || !standstill(in)) {
enter(SAFETY_IDLE);
}
break;
case SAFETY_HILL_HOLD_ACTIVE:
/* Beendet, wenn Fahrzeug rollt oder Bremse appliziert */
if (in->vehicle_speed_kmh > SAFETY_RELEASE_KMH
|| in->current_state == EPB_STATE_APPLIED) {
enter(SAFETY_IDLE);
} else {
s_ctx.apply_requested = true;
}
break;
case SAFETY_AUTO_APPLY_ARMED:
/* Bedingung muss durchgaengig erfuellt sein */
if (in->engine_running || !standstill(in)) {
enter(SAFETY_IDLE);
break;
}
/* @reqs SWE-008: Auto-Apply nach 2 s (40 Ticks) Verzoegerung */
if (s_ctx.ticks_in_state >= SAFETY_AUTO_APPLY_DELAY_50MS) {
s_ctx.apply_requested = true;
enter(SAFETY_AUTO_APPLY_TRIGGERED);
}
break;
case SAFETY_AUTO_APPLY_TRIGGERED:
if (in->current_state == EPB_STATE_APPLIED) {
enter(SAFETY_IDLE);
} else {
s_ctx.apply_requested = true;
}
break;
default:
enter(SAFETY_IDLE);
break;
}
}
bool safety_mgr_apply_requested(void)
{
return s_ctx.apply_requested;
}
SafetyState safety_mgr_get_state(void)
{
return s_ctx.state;
}
+51
View File
@@ -0,0 +1,51 @@
/**
* @file safety_manager.h
* @brief Safety Manager — Hill-Hold + Auto-Apply Logik.
*
* @arch SWA-001
* @reqs SWE-007 SWE-008 SWE-009 SWE-010
*
* ASIL: D.
*
* State Machine:
* IDLE --(engine_off & v<0.5)--> 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
*/
#ifndef SAFETY_MANAGER_H
#define SAFETY_MANAGER_H
#include "epb_types.h"
typedef enum {
SAFETY_IDLE = 0,
SAFETY_HILL_HOLD_ARMED = 1,
SAFETY_HILL_HOLD_ACTIVE = 2,
SAFETY_AUTO_APPLY_ARMED = 3,
SAFETY_AUTO_APPLY_TRIGGERED = 4
} SafetyState;
typedef struct {
bool engine_running;
bool brake_pedal_pressed;
float vehicle_speed_kmh;
float grade_percent;
EpbState current_state; /* aus Apply Controller */
} SafetyInputs;
/* Schwellwerte als Konstanten, damit Tests darauf zugreifen koennen. */
#define SAFETY_AUTO_APPLY_DELAY_50MS 40U /* 40 * 50ms = 2.0 s */
#define SAFETY_STANDSTILL_KMH 0.5f
#define SAFETY_RELEASE_KMH 2.0f
#define SAFETY_HILLHOLD_GRADE_PCT 5.0f
EpbStatus safety_mgr_init(void);
void safety_mgr_step_50ms(const SafetyInputs* in);
bool safety_mgr_apply_requested(void);
SafetyState safety_mgr_get_state(void);
#endif /* SAFETY_MANAGER_H */
-27
View File
@@ -1,27 +0,0 @@
/**
* @file safety_manager.h
* @brief Safety Manager — Hill-Hold + Auto-Apply Logik.
*
* @arch SWA-001
* @reqs SWE-007 SWE-008 SWE-009 SWE-010
*
* ASIL: D. STUB — nicht implementiert in dieser Demo.
*/
#ifndef SAFETY_MANAGER_H
#define SAFETY_MANAGER_H
#include "../epb_types.h"
typedef struct {
bool engine_running;
bool brake_pedal_pressed;
float vehicle_speed_kmh;
float grade_percent;
EpbState current_state;
} SafetyInputs;
EpbStatus safety_mgr_init(void);
void safety_mgr_step_50ms(const SafetyInputs* in);
bool safety_mgr_apply_requested(void);
#endif