Files
sases 5e694f6b74 Komunikat offline mówi, co faktycznie nie działa: powiadamianie o alarmach
"Dane mogą być nieaktualne" wprowadzało w błąd — iframe pokazuje infoterminal
ALS bezpośrednio, więc jego dane są aktualne niezależnie od LuPorta. Przy
zerwanym WebSockecie pada wyłącznie nasza warstwa wykrywania alarmów i to
komunikat ma mówić. (Niuans opisany w README: karty na ekranie alarmowym przy
przerwie w trakcie alarmu mogą być nieświeże do 5 min, potem znikają.)

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

177 lines
7.3 KiB
Markdown
Raw Permalink 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ńczowa pulsująca ramka wokół ekranu + plakietka w rogu (na obu widokach) |
Alarm jest wykrywany, gdy pole `status` maszyny zawiera słowo **`alarm`**
(bez rozróżnienia wielkości liter), np. `"Alarm - awaria"`, `"ALARM"`.
Wskaźnik offline to cienka pulsująca RAMKA wokół całego ekranu plus mała
plakietka w rogu — niczego nie zasłania (pełnoekranowa belka zakrywała dane
ALS w iframe). Pomarańcz to celowo INNY kolor niż czerwień alarmów.
Komunikat mówi o tym, co FAKTYCZNIE nie działa: **powiadamianie o alarmach**.
Dane infoterminala w iframe są aktualne niezależnie od LuPorta (iframe to
bezpośrednie źródło) — przy zerwanym WebSockecie pada wyłącznie nasza warstwa
wykrywania alarmów. Jedyny wyjątek: karty na ekranie alarmowym, jeśli przerwa
nastąpi w trakcie alarmu — mogą być nieświeże do 5 min, potem znikają.
---
## 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ć ramkę offline (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.** Pulsująca ramka offline świeci CIĄGLE
przez całą przerwę (znacznik początku przerwy ustawiany tylko przy
przejściu połączone→rozłączone — reset przy każdej nieudanej próbie
powodował cykliczne miganie). Ekran nigdy nie pokazuje po cichu
nieświeżego obrazu.
Uwaga: do czasu wdrożenia NOWEGO LuPorta ramka będzie świecić stale —
stary system nie ma kanału zbiorczego ani uwierzytelniania WS, więc nowa
strona nie ma się z czym połączyć. To oczekiwane; po przełączeniu
serwera na nowy system strona wstaje bez żadnych zmian.
---
## 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 (11 asercji: migawka, aktualizacje, reconnect, ciągłość
wskaźnika offline podczas długiej przerwy, watchdog, wygasanie danych)
jedzie na wirtualnym czasie Chromium — całość trwa kilkanaście sekund mimo
symulowania ~8 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).