Files
ninlight-w600/tools/station/ninlight-display.service
T
Michael Sauer 38c70ac5ce fix(station): EnvironmentFile-Syntax, Bias-Zyklus, echte Panel-Tests
Fixrunde 3 zu Task 6a. EnvironmentFile=-/etc/ninlight-station.conf statt
-EnvironmentFile= (Fixrunde 2 hatte das Praefix vor den Direktivennamen statt
vor den Dateipfad gesetzt - systemd verwarf die Zeile komplett, die
Konfigurationsdatei wurde ueberhaupt nicht mehr gelesen). clear() stellte die
in Panel dokumentierte Awake-Invariante nicht her (kein 0x26-Referenzpuffer)
und verwendet jetzt wie draw_full() displayPartBaseImage(). Die 30-Minuten-
Vollaufbau-Uhr feuerte bisher auch ohne vorherige Teilaufbauten und erzeugte
im eingeschwungenen Ruhezustand einen endlosen Schlaf-/Wach-Zyklus mit 33
Prozent Bias-Anteil - sie greift jetzt nur noch, wenn seit dem letzten
Vollaufbau tatsaechlich Teilaufbauten stattfanden, und der Zaehler wird beim
Einschlafen zurueckgesetzt. draw_partial() meldet zurueck, ob es intern zu
einem Vollaufbau aufgewertet hat, damit die 30-Minuten-Uhr korrekt bleibt.
compose() ersetzt eingebettete Zeilenumbrueche vor der Breitenmessung.

Dazu ein Kommando-Rekorder fuer die Tests, der die tatsaechlich gesendete
SPI-Byte-Folge protokolliert: die vorherigen Tests fuer den Referenzpuffer
(N2) und die Registerfolge nach dem Schlafen (N3) pruefften nur "keine
Ausnahme" und blieben laut Reviewer-Mutation gruen, wenn die eigentliche
Fehlerursache wieder eingefuehrt wurde. Die neuen Tests pruefen die
gesendeten Kommandos direkt und wurden gegen genau diese Mutationen
verifiziert.
2026-09-13 21:46:59 +02:00

74 lines
4.3 KiB
Desktop File

[Unit]
Description=ninlight E-Paper-Anzeige (Status der Migrations-/Rettungsstation)
# Nur eine Anzeige fuer den Hauptdienst - Wants=/After=, NICHT Requires=/BindsTo=: ein
# fehlendes oder abgestuerztes Panel darf server.py niemals mitreissen, und server.py
# darf umgekehrt starten, auch wenn dieser Dienst fehlt oder deaktiviert ist. Siehe
# display.py's Modul-Docstring: die Anzeige ist nirgends Voraussetzung fuer die
# Migration.
After=ninlight-station.service
Wants=ninlight-station.service
# Gehoeren seit systemd 229 ins [Unit], nicht ins [Service] (dort standen sie zuerst -
# Fixrunde 1, I1: systemd ignoriert sie dort mit "Unknown key name", die Vorgabe (5
# Starts/10s) greift dann bei RestartSec=5 nie, und genau das sollte die Deckelung unten
# eigentlich verhindern). ninlight-station.service (dieselbe Sektion) macht es richtig.
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Type=simple
# Reihenfolge wichtig: systemd wendet Environment=/EnvironmentFile= in der Reihenfolge
# an, in der sie hier stehen, und eine spaetere Zuweisung ueberschreibt eine gleichnamige
# fruehere. EPD_REVISION=V3 hier ist nur der Vorgabewert, falls die Konfigurationsdatei
# (unten, per EnvironmentFile=) den Schluessel (noch) nicht enthaelt - z.B. auf einem Pi,
# der vor Task 6 aufgesetzt wurde. Steht er dort, gewinnt er.
Environment=EPD_REVISION=V3
Environment=NINLIGHT_RUN_DIR=/run/ninlight-station
Environment=NINLIGHT_STATION_CONF=/etc/ninlight-station.conf
# "-"-Praefix VOR dem Dateipfad, nicht vor dem Direktivennamen (Fixrunde 3, F1 - das war
# in Fixrunde 2 falsch herum: "-EnvironmentFile=..." ist kein gueltiger Schluesselname,
# systemd verwirft die Zeile komplett mit "Unknown key name", und die Konfigurationsdatei
# wurde dadurch ueberhaupt nicht mehr gelesen - Environment=EPD_REVISION=V3 oben haette
# IMMER gewonnen, unabhaengig davon, was in /etc/ninlight-station.conf steht). Mit dem
# Praefix am richtigen Platz gilt: ohne die Konfigurationsdatei startet die Anzeige
# trotzdem (mit den Environment=-Vorgaben oben) statt mit einem harten Fehler - "die
# Anzeige darf nirgends Voraussetzung sein" gilt auch fuer ihre eigene Konfiguration.
EnvironmentFile=-/etc/ninlight-station.conf
ExecStart=/usr/bin/python3 /opt/ninlight-station/display/display.py
# display.py faengt ein fehlendes/abgestuerztes Panel als DisplayUnavailable ab,
# protokolliert eine Zeile und beendet sich mit sys.exit(0) - das ist also nie ein
# "Fehlschlag" im systemd-Sinne und loest kein Restart= aus. Restart=on-failure (nicht
# always) greift nur fuer echte Abstuerze (z.B. ein Python-Traceback aus einem
# Programmierfehler); ohne den Deckel wuerde ein dauerhaft fehlendes Panel sonst NICHT
# staendig neu versucht (gut so), waehrend ein echter Bug endlos neu starten wuerde und
# so das Journal fluten koennte.
Restart=on-failure
RestartSec=5
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
# KEIN ReadOnlyPaths=/run/ninlight-station hier (stand hier zuerst, Fixrunde 1, C3):
# server.py's Unit traegt RuntimeDirectory=ninlight-station, und "ninlight-station off"
# stoppt den Hauptdienst regulaer - dabei raeumt systemd dessen RuntimeDirectory auf,
# sofern nicht gerade ein Neustart lief (RuntimeDirectoryPreserve=restart deckt nur den
# Neustart-Fall ab, nicht den regulaeren Stop). Ein nicht mit "-" prefigierter Pfad in
# ReadOnlyPaths=/ReadWritePaths=/BindPaths=, der beim Start fehlt, laesst systemd die
# Namensraum-Einrichtung mit 226/NAMESPACE abbrechen - ausgerechnet dann, wenn die
# Anzeige "Station aus" zeigen soll, waere sie selbst nicht mehr startbar gewesen. Die
# Zeile war obendrein wirkungslos: ProtectSystem=strict macht "/" (und damit "/run",
# soweit nicht per ReadWritePaths= explizit freigegeben) bereits schreibgeschuetzt.
RestrictAddressFamilies=AF_UNIX AF_NETLINK
MemoryMax=64M
# Laeuft als root (wie der Hauptdienst) fuer /dev/spidev0.0 und die GPIO-Zeichenleitung -
# kein SupplementaryGroups=spi gpio hier (stand hier zuerst, Fixrunde 1, I2): auf reinem
# Debian (im Unterschied zu Raspberry Pi OS) gibt es diese Gruppen oft gar nicht, und
# eine fehlende Gruppe ist fuer systemd ein harter Startfehler (216/GROUP) - ausgerechnet
# in der Unit, die nie stoeren soll, wenn etwas an der Anzeige fehlt. Als root sind die
# Gruppen fuer den Geraetezugriff ohnehin nicht noetig.
[Install]
WantedBy=multi-user.target