feat(station): Abschlussbild beim Herunterfahren und beim Neustart
E-Paper behaelt sein Bild dauerhaft, auch stromlos. Faehrt der Pi herunter, wird der Anzeigedienst mit abgeraeumt und das zuletzt gezeichnete Bild bleibt fuer immer stehen - bei der Abnahme stand dort die Rueckfrage des Tastermenues, und der ausgeschaltete Pi sah aus, als warte er noch auf eine Eingabe. Zwei neue Units, ninlight-poweroff.service (WantedBy=poweroff.target) und ninlight-reboot.service (WantedBy=reboot.target). Sie haengen am System, nicht am Tasterdienst: so greifen sie bei jedem Herunterfahren - Menue, "poweroff" per SSH, "reboot" - und nicht nur auf einem von drei Wegen. Zwei Units statt einer Fallunterscheidung, weil systemd einem ExecStart nicht sagt, wohin die Reise geht. Die Texte unterscheiden sich: "AUSGESCHALTET / Strom kann getrennt werden" gegen "NEUSTART / Strom NICHT trennen!" samt Hinweis auf die rund zwei Minuten, die der Zero W bis zum Anzeigedienst braucht. An diesem Pi hat ein Stromzug in genau dieser Wartezeit schon eine SD-Karte gekostet. Reihenfolge: die Units ordnen sich per After=ninlight-display.service hinter dessen Stop ein, und "ninlight-station display final <kind>" wartet zusaetzlich, bis systemctl is-active nicht mehr anschlaegt - dasselbe Muster wie display_handover(), nur ohne Wiederanfahren. Gibt der Dienst die Leitungen nicht frei, wird nicht gezeichnet (GPIO busy), sondern gewarnt: ein Anzeigefehler haelt das Herunterfahren nie auf. Nebenbei behoben: display_run() reichte nur "$1" an display.py weiter und haette "--final poweroff" halbiert.
This commit is contained in:
@@ -129,6 +129,21 @@ case "\$1" in
|
||||
unit="\${!#}"
|
||||
if [ "\$unit" = "ninlight-display" ]; then
|
||||
code="\${FAKE_DISPLAY_SERVICE_ACTIVE_EXIT:-0}"
|
||||
# Testschalter fuer den Abschlussbildschirm: der Anzeigedienst gilt fuer die
|
||||
# ersten N Abfragen als AKTIV und danach als gestoppt - so, wie es beim
|
||||
# Herunterfahren wirklich ablaeuft (systemd stoppt ihn, das dauert einen Moment).
|
||||
# Gezaehlt wird in einer Datei, weil jeder Stub-Aufruf ein eigener Prozess ist.
|
||||
if [ -n "\${FAKE_DISPLAY_ACTIVE_FOR:-}" ]; then
|
||||
n=\$(cat "$rundir/.display-active-count" 2>/dev/null || echo 0)
|
||||
n=\$((n + 1))
|
||||
echo "\$n" >"$rundir/.display-active-count"
|
||||
if [ "\$n" -le "\${FAKE_DISPLAY_ACTIVE_FOR}" ]; then
|
||||
echo active
|
||||
exit 0
|
||||
fi
|
||||
echo inactive
|
||||
exit 3
|
||||
fi
|
||||
else
|
||||
code="\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}"
|
||||
fi
|
||||
@@ -1308,6 +1323,104 @@ ERR=$(run_cli "$SBX" migrate on --listen 2>&1 >/dev/null)
|
||||
set -e
|
||||
echo "$ERR" | grep -q "keine Migrationsimages" && pass "nach dem Umschalten warnt sie" || fail "keine Warnung, obwohl der gewaehlte Satz leer ist: $ERR"
|
||||
|
||||
echo
|
||||
echo "=== Abschluss A: 'display final' kennt zwei verschiedene Bilder ==="
|
||||
# E-Paper behaelt sein Bild dauerhaft. Beim Ausschalten darf man den Stecker ziehen, beim
|
||||
# Neustart gerade nicht - deshalb zwei Schalter, nicht einer.
|
||||
SBX="$TMPROOT/fin1"
|
||||
setup_sandbox "$SBX"
|
||||
export FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=3 # Anzeigedienst laeuft nicht
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" display final poweroff 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT"
|
||||
grep -q "STUB display.py --final poweroff" "$SBX/cmd.log" && pass "display.py wird mit '--final poweroff' aufgerufen" || fail "falscher Aufruf: $(cat "$SBX/cmd.log")"
|
||||
: >"$SBX/cmd.log"
|
||||
run_cli "$SBX" display final reboot >/dev/null 2>&1
|
||||
grep -q "STUB display.py --final reboot" "$SBX/cmd.log" && pass "und mit '--final reboot' fuer den Neustart" || fail "falscher Aufruf: $(cat "$SBX/cmd.log")"
|
||||
grep -q "poweroff" "$SBX/cmd.log" && fail "der Neustart ruft das Ausschaltbild auf" || pass "die beiden Wege werden nicht verwechselt"
|
||||
unset FAKE_DISPLAY_SERVICE_ACTIVE_EXIT
|
||||
|
||||
echo
|
||||
echo "=== Abschluss B: ohne 'poweroff'/'reboot' passiert nichts ==="
|
||||
SBX="$TMPROOT/fin2"
|
||||
setup_sandbox "$SBX"
|
||||
set +e
|
||||
ERR=$(run_cli "$SBX" display final 2>&1 >/dev/null)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -ne 0 ] && pass "Exit-Code != 0" || fail "Exit-Code 0 ohne Spielart"
|
||||
grep -q "STUB display.py" "$SBX/cmd.log" 2>/dev/null && fail "display.py wurde trotzdem aufgerufen" || pass "das Panel wurde nicht angefasst"
|
||||
set +e
|
||||
ERR=$(run_cli "$SBX" display final quatsch 2>&1 >/dev/null)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -ne 0 ] && pass "Exit-Code != 0 fuer eine unbekannte Spielart" || fail "Exit-Code 0 fuer 'quatsch'"
|
||||
grep -q "STUB display.py" "$SBX/cmd.log" 2>/dev/null && fail "display.py wurde fuer 'quatsch' aufgerufen" || pass "und auch dafuer nicht"
|
||||
|
||||
echo
|
||||
echo "=== Abschluss C: gezeichnet wird erst, NACHDEM der Anzeigedienst die Leitungen freigegeben hat ==="
|
||||
# Zwei Prozesse auf denselben GPIO-/SPI-Leitungen enden in "lgpio.error: 'GPIO busy'" -
|
||||
# in diesem Projekt schon zweimal passiert.
|
||||
SBX="$TMPROOT/fin3"
|
||||
setup_sandbox "$SBX"
|
||||
export FAKE_DISPLAY_ACTIVE_FOR=2 # erst beim dritten Blick ist er gestoppt
|
||||
set +e
|
||||
OUT=$(NINLIGHT_DISPLAY_RELEASE_WAIT_S=5 run_cli "$SBX" display final poweroff 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
unset FAKE_DISPLAY_ACTIVE_FOR
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT"
|
||||
grep -q "STUB display.py --final poweroff" "$SBX/cmd.log" && pass "das Abschlussbild wurde gezeichnet" || fail "gar nicht gezeichnet: $(cat "$SBX/cmd.log")"
|
||||
# Die Reihenfolge in der GEMEINSAMEN Logdatei ist der Beleg: jede is-active-Abfrage steht
|
||||
# vor dem display.py-Aufruf, keine danach.
|
||||
# "|| true" ueberall: ohne Treffer liefert grep 1, und unter "set -e" wuerde die SUITE an
|
||||
# dieser Stelle stillschweigend abbrechen, statt einen Fehler zu melden - eine Mutation,
|
||||
# die das Warten ganz entfernt, waere dann scheinbar gruen (genau so passiert).
|
||||
DRAW_LINE=$(grep -n "STUB display.py" "$SBX/cmd.log" 2>/dev/null | head -n1 | cut -d: -f1 || true)
|
||||
LAST_CHECK=$(grep -n "systemctl is-active --quiet ninlight-display" "$SBX/cmd.log" 2>/dev/null | tail -n1 | cut -d: -f1 || true)
|
||||
CHECKS=$(grep -c "systemctl is-active --quiet ninlight-display" "$SBX/cmd.log" 2>/dev/null || true)
|
||||
CHECKS=${CHECKS:-0}
|
||||
if [ -n "$DRAW_LINE" ] && [ -n "$LAST_CHECK" ] && [ "$LAST_CHECK" -lt "$DRAW_LINE" ]; then
|
||||
pass "alle Abfragen des Anzeigedienstes stehen VOR dem Zeichnen"
|
||||
else
|
||||
fail "gezeichnet wurde nicht nach der Freigabe (Zeichnen Zeile '$DRAW_LINE', letzte Abfrage Zeile '$LAST_CHECK')"
|
||||
fi
|
||||
[ "$CHECKS" -ge 3 ] && pass "es wurde wirklich gewartet ($CHECKS Abfragen)" || fail "nur $CHECKS Abfrage(n) - es wurde gar nicht auf die Freigabe gewartet"
|
||||
|
||||
echo
|
||||
echo "=== Abschluss D: ein Anzeigedienst, der nicht loslaesst, haelt das Herunterfahren nicht auf ==="
|
||||
# Lieber kein Abschlussbild als ein haengendes Herunterfahren - und auf keinen Fall ein
|
||||
# zweiter Prozess auf den belegten Leitungen.
|
||||
SBX="$TMPROOT/fin4"
|
||||
setup_sandbox "$SBX"
|
||||
export FAKE_DISPLAY_ACTIVE_FOR=9999
|
||||
set +e
|
||||
ERR=$(NINLIGHT_DISPLAY_RELEASE_WAIT_S=2 run_cli "$SBX" display final poweroff 2>&1 >/dev/null)
|
||||
RC=$?
|
||||
set -e
|
||||
unset FAKE_DISPLAY_ACTIVE_FOR
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0 (ein Anzeigefehler haelt das Herunterfahren nie auf)" || fail "Exit-Code $RC: $ERR"
|
||||
grep -q "STUB display.py" "$SBX/cmd.log" && fail "es wurde auf belegte Leitungen gezeichnet - 'GPIO busy'" || pass "nicht gezeichnet, solange der Dienst haelt"
|
||||
echo "$ERR" | grep -qi "warnung" && pass "Warnung auf stderr" || fail "keine Warnung: $ERR"
|
||||
# Und es wurde NICHT versucht, den Dienst von hier aus zu stoppen: das liefe mitten in
|
||||
# der Abschalt-Transaktion.
|
||||
grep -q "systemctl stop ninlight-display" "$SBX/cmd.log" && fail "'systemctl stop' mitten im Herunterfahren" || pass "kein 'systemctl stop' aus dem Abschaltschritt heraus"
|
||||
|
||||
echo
|
||||
echo "=== Abschluss E: ein scheiterndes display.py bleibt eine Warnung ==="
|
||||
SBX="$TMPROOT/fin5"
|
||||
setup_sandbox "$SBX"
|
||||
export FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=3
|
||||
set +e
|
||||
ERR=$(FAKE_DISPLAY_EXIT=1 run_cli "$SBX" display final reboot 2>&1 >/dev/null)
|
||||
RC=$?
|
||||
set -e
|
||||
unset FAKE_DISPLAY_SERVICE_ACTIVE_EXIT
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC - ein Anzeigefehler wuerde das Herunterfahren rot machen: $ERR"
|
||||
echo "$ERR" | grep -qi "warnung" && pass "Warnung auf stderr" || fail "keine Warnung: $ERR"
|
||||
|
||||
echo
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "Alle Tests bestanden."
|
||||
|
||||
Reference in New Issue
Block a user