Files
als-alert-screen/README.md
T
sases 199e71327f Przejście na kanał zbiorczy LuPort + naprawa reconnectu, watchdog, widoczny offline
PRZYCZYNA ZNIKAJĄCYCH ALARMÓW ZNALEZIONA — i nie była nią liczba połączeń.
Commit 7cf0634 ("hidden status-bar & machine.status") zakomentował element
#status-bar w HTML, ale refreshStatus() nadal po niego sięgał. TypeError na
null przerywał handler ws.onclose PRZED zaplanowaniem reconnectu — każda
maszyna po pierwszym zerwaniu połączenia milkła NA ZAWSZE. Restart z crona
co 2 h maskował problem, zaczynając od zera.

Czerwcowe poprawki na kiosku (timery świeżości per-maszyna, ubijanie "zombie"
połączeń, jitter) szły w dobrą stronę, ale nieświadomie POGORSZYŁY objaw:
watchdog celowo zamykał ciche połączenie, a każde zamknięcie przechodziło
przez ten sam wadliwy onclose — własna obrona uśmiercała maszynę na stałe.
Ich sedno jest zachowane w tej wersji (wykrywanie ciszy, jitter, nakładkowy
iframe z "iframe optimalization").

NOWA ARCHITEKTURA (pod nowy system LuPort):
- JEDNO połączenie na kanał zbiorczy /ws/als-data/ zamiast 35 per-maszyna;
  serwer wysyła migawkę stanu wszystkich maszyn zaraz po połączeniu (dodane
  w LuPort równolegle), więc trwający alarm wraca na ekran natychmiast po
  reconnexie
- token ApiUser w adresie (nowy system wymaga uwierzytelnienia WS); konto
  bot-als-alert-screen bez żadnych uprawnień
- reconnect z wykładniczym odstępem (1->30 s) i jitterem; odstęp wraca do
  minimum dopiero po 60 s stabilnego połączenia
- watchdog ciszy > 90 s (przeglądarka nie wystawia ping/pong do JS — martwe
  półotwarte połączenie inaczej wisi w nieskończoność)
- dane maszyny starsze niż 5 min znikają z ekranu; przy rozłączeniu dane NIE
  są czyszczone od razu (alarm nie znika przy 3-sekundowym czknięciu sieci —
  o niepewności mówi pomarańczowy pasek, migawka po reconnexie przywraca
  prawdę)
- utrata łączności WIDOCZNA: pomarańczowy pasek po 10 s + przywrócony pasek
  statusu; element MUSI istnieć w DOM, refreshStatus() ma mimo to osłonę
- MACHINES stało się filtrem wyświetlania; ?ws= i ?token= nadpisują
  konfigurację z adresu strony
- karty przez textContent; celowo BRAK ws.onerror (po błędzie przeglądarka
  i tak wywołuje onclose)

TESTY (test/testy_ekranu.py): 10 deterministycznych scenariuszy bez sieci —
skrypt wycinany z żywego HTML, WebSocket podmieniony fałszywką, wirtualny
czas Chromium. Scenariusz 6 to bezpośredni test regresji błędu reconnectu;
mutacja (usunięcie planowania reconnectu) wywraca scenariusze 6, 8 i 9.

CRON RESTARTUJĄCY CO 2 H MOŻNA USUNĄĆ — README opisuje czemu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 08:04:30 +02:00

161 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ALS Alert Screen
Jednostronicowa aplikacja HTML/JS wyświetlająca alarmy maszyn na podstawie
danych WebSocket z LuPort. Przeznaczona do montażu na monitorze halowym
(Chromium na Debianie).
---
## Działanie
Strona utrzymuje **jedno** połączenie WebSocket ze zbiorczym kanałem ALS
nowego systemu (`/ws/als-data/` bez maszyny w adresie) — kanał niesie
aktualizacje wszystkich maszyn, a zaraz po połączeniu serwer wysyła migawkę
ich bieżącego stanu, więc trwający alarm pojawia się na ekranie natychmiast,
także po każdym wznowieniu połączenia.
| Sytuacja | Widok |
|----------|-------|
| Brak maszyny w alarmie | Iframe z infoterminalem ALS |
| Co najmniej jedna maszyna w alarmie | Ekran alarmowy z kartami maszyn |
| Brak łączności z LuPort > 10 s | Pomarańczowy pasek u góry (na obu widokach) |
Alarm jest wykrywany, gdy pole `status` maszyny zawiera słowo **`alarm`**
(bez rozróżnienia wielkości liter), np. `"Alarm - awaria"`, `"ALARM"`.
Pomarańczowy pasek to celowo INNY kolor niż czerwień alarmów: mówi „ekran może
pokazywać nieświeży obraz", a nie „maszyna stoi".
---
## Konfiguracja
Wszystkie parametry na początku sekcji `<script>` w `als_alert_screen.html`:
| Stała | Znaczenie |
|---|---|
| `WS_URL` | adres kanału zbiorczego, np. `ws://luport.local/ws/als-data/` |
| `WS_TOKEN` | token konta `ApiUser` (patrz niżej) |
| `MACHINES` | filtr maszyn pokazywanych na ekranie; `[]` = wszystkie |
| `WATCHDOG_SILENCE_MS` | po jakiej ciszy uznać połączenie za martwe (90 s) |
| `STALE_MS` | po jakim czasie dane maszyny znikają z ekranu (5 min) |
| `OFFLINE_BANNER_AFTER_MS` | po jakim czasie bez łączności pokazać pasek (10 s) |
Oba kluczowe parametry można też nadpisać w adresie strony — wygodne przy
konfiguracji kiosku bez edycji pliku:
```
als_alert_screen.html?ws=ws%3A%2F%2Fluport.local%2Fws%2Fals-data%2F&token=...
```
### Token (wymagany przez nowy system)
Nowy LuPort wymaga uwierzytelnienia WebSocket. Utwórz dedykowane konto:
Django Admin → **Users → Api users** → dodaj `bot-als-alert-screen`
**bez żadnych uprawnień** (sam odbiór strumienia ALS ich nie wymaga — konto
ma minimalny możliwy dostęp). Wygenerowany token wpisz w `WS_TOKEN`.
### Format wiadomości
Strona obsługuje dwa typy ramek — obydwa tak samo (liczy się najnowszy stan):
- `machine_data` — migawka wysyłana przez serwer zaraz po połączeniu,
- `machine_update` — aktualizacje na żywo.
```json
{
"type": "machine_update",
"machine": "2.60",
"data": { "status": "Automatyczny", "orders": ["202605350"], "progress": 25, "...": "..." },
"timestamp": "2026-07-31T12:00:32"
}
```
---
## Odporność na awarie — dlaczego cron-restart nie jest już potrzebny
1. **Reconnect z wykładniczym odstępem i jitterem** (1 s → 30 s). Odstęp wraca
do minimum dopiero po 60 s stabilnego połączenia — serwer, który przyjmuje
i zaraz zrywa (np. zły token), nie jest młócony co sekundę.
2. **Watchdog martwego połączenia.** Przeglądarka nie wystawia ping/pong do
JS, więc połączenie zabite bez zamknięcia TCP potrafi wisieć „otwarte"
w nieskończoność. Na kanale zbiorczym dane płyną praktycznie ciągle
(35 maszyn × cykl scrapera 30 s) — cisza dłuższa niż 90 s oznacza martwe
połączenie i wymusza reconnect.
3. **Wygasanie danych.** Dane maszyny starsze niż 5 min znikają z ekranu —
alarm maszyny usuniętej z widgetu ALS nie wisi w nieskończoność.
4. **Widoczna utrata łączności.** Pasek offline + pasek statusu w rogu.
Ekran nigdy nie pokazuje po cichu nieświeżego obrazu.
---
## Historia: dlaczego alarmy „przestawały wyskakiwać" (do 2026-07-31)
Poprzednia wersja utrzymywała **35 osobnych połączeń** (po jednym na maszynę)
i miała błąd, który ujawnił się po ukryciu paska statusu: element `#status-bar`
został **zakomentowany w HTML**, ale `refreshStatus()` nadal po niego sięgał.
`TypeError` na `null` przerywał handler `ws.onclose` **przed** zaplanowaniem
reconnectu — każda maszyna po pierwszym zerwaniu połączenia milkła na zawsze.
Maszyny odpadały jedna po drugiej przy każdym czknięciu sieci, a restart
z crona co 2 h maskował problem, zaczynając od zera.
Wnioski utrwalone w obecnym kodzie:
- element statusu **musi istnieć w DOM** (ukrywanie wyłącznie przez CSS),
a `refreshStatus()` i tak ma osłonę na jego brak,
- brak `ws.onerror` jest celowy — po błędzie przeglądarka i tak wywołuje
`onclose`, a osobny handler był tylko drugim miejscem, z którego dało się
rzucić wyjątkiem,
- scenariusz 6 w testach (`test/testy_ekranu.py`) jest bezpośrednim testem
regresji tego błędu.
Poprawki robione na kiosku w czerwcu (timery świeżości per-maszyna,
ubijanie „zombie" połączeń, jitter reconnectu) szły w dobrą stronę, ale
nieświadomie POGORSZYŁY objaw: watchdog celowo zamykał ciche połączenie,
a każde zamknięcie przechodziło przez ten sam wadliwy `onclose` — czyli
własna obrona uśmiercała maszynę na stałe. Ich sedno (wykrywanie ciszy,
jitter) jest zachowane w obecnej wersji; nakładkowy iframe („iframe
optimalization") również. Zrezygnowano z czyszczenia danych przy rozłączeniu:
alarm nie znika przy 3-sekundowym czknięciu sieci — o niepewności informuje
pomarańczowy pasek, a migawka po reconnexie przywraca prawdę.
**Cron restartujący przeglądarkę co 2 h można usunąć.**
---
## Testy
```bash
python3 test/testy_ekranu.py
# lub na kiosku:
CHROME_BIN=/usr/bin/chromium python3 test/testy_ekranu.py
```
Deterministyczne, bez sieci: skrypt aplikacji jest wycinany z żywego
`als_alert_screen.html`, `window.WebSocket` podmieniany na fałszywkę,
a scenariusz (10 asercji: migawka, aktualizacje, reconnect, baner offline,
watchdog, wygasanie danych) jedzie na wirtualnym czasie Chromium — całość
trwa kilka sekund mimo symulowania ~6 minut.
---
## Tryb debug
Podgląd ekranu alarmowego bez danych i bez połączeń WS:
```
als_alert_screen.html?debug
```
albo w konsoli przeglądarki: `DEBUG_ALARM = true; updateDisplay();`
(powrót: `DEBUG_ALARM = false; updateDisplay();`).
---
## Wymagania
- Chromium/Chrome 105+ (CSS Container Queries, `min()`) — kiosk działa na
Chromium pod Debianem.
- Dostęp sieciowy do LuPort (WebSocket) oraz infoterminala ALS (iframe).