Fixrunde 2 zu Task 6a: der sleep()-Einbau aus Fixrunde 1 hatte drei neue Critical erzeugt. epd.sleep() schliesst ueber epdconfig.module_exit() die GPIO-Pins; ein Teilaufbau direkt danach fasste bei V3 eine bereits geschlossene Pin-Referenz an (AttributeError, ungefangen, Neustartschleife - binnen Sekunden nach dem Start, V3 ist die Vorgaberevision) und war bei V2 nach Deep Sleep ohnehin keine belastbare Sequenz (init(PART_UPDATE) allein setzt nicht die Register, die nur der FULL_UPDATE-Zweig setzt). Ausserdem setzte der V3-Vollaufbau nie den Referenzpuffer, den ein Teilaufbau zum Diffen braucht (displayPartBaseImage() statt display()). Panel bekommt ein internes _awake-Flag: draw_partial() wertet sich selbst zu einem Vollaufbau auf, wenn das Panel nicht wach ist (frisch erzeugt oder seit dem letzten Aufbau geschlafen), unabhaengig von der Aufrufreihenfolge. sleep() wird nur noch nach laengerer Ruhe (10 Minuten ohne Aenderung) gerufen, nicht mehr nach jedem Bild, und ist selbst idempotent. Dazu: epdconfig faengt jetzt gpiozero.GPIOZeroError statt nur GPIODeviceError (die PinError-Familie haengt nicht darunter), EnvironmentFile= in der Unit bekommt ein "-"-Praefix, und die Zeilenkuerzung wandert von einer Zeichenzahl-Grenze (ungueltig bei einer Proportionalschrift) zu einer echten Pixelbreitenmessung in compose() - lines() bleibt dafuer ungekuerzt und weiterhin ohne Pillow testbar.
69 lines
3.8 KiB
Desktop File
69 lines
3.8 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: ohne die Konfigurationsdatei soll die Anzeige trotzdem starten (mit den
|
|
# Environment=-Vorgaben oben) statt mit einem harten Fehler - "die Anzeige darf nirgends
|
|
# Voraussetzung sein" gilt auch fuer ihre eigene Konfiguration (Fixrunde 1/2, Hinweis).
|
|
-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
|