89155c3f2a
Dwa zgłoszenia z kiosku (2026-07-31): 1. BELKA ZASŁANIAŁA DANE ALS. Pełnoekranowy pasek u góry zajmował pas ekranu i obcinał dane informacyjne w iframe. Zastąpiony cienką pulsującą RAMKĄ wokół całego ekranu (pointer-events: none, niczego nie zakrywa) plus małą plakietką w rogu. Z daleka widoczna równie dobrze. 2. WSKAŹNIK MIGAŁ zamiast świecić ciągle. disconnectedSince było resetowane przy KAŻDEJ nieudanej próbie reconnectu, nie tylko przy przejściu połączone->rozłączone — przy odstępie ponowień > 10 s (backoff dochodzi do 30 s) licznik ciszy zerował się co próbę i wskaźnik cyklicznie znikał i wracał. Teraz znacznik początku przerwy ustawiany wyłącznie przy przejściu ze stanu połączonego. Kontekst zgłoszenia: strona działa jeszcze przeciw STAREMU LuPortowi, który nie ma kanału zbiorczego ani uwierzytelniania WS — połączenie pada ciągle (oczekiwane do czasu wdrożenia; potem wstaje bez zmian), a błąd zamieniał tę ciągłą przerwę w miganie. Testy: scenariusz 7 próbkuje wskaźnik co 500 ms przez 90 s ciągłej awarii i wymaga 100% aktywnych próbek — z błędem aktywnych było 51/182 (72% czasu zgaszony; dokładnie zgłoszone "pojawia się i znika"). Fałszywy WebSocket odrzuca teraz połączenia jak przeglądarka (onclose po nieudanej próbie), więc długa przerwa z ponowieniami jest testowalna. 11/11 scenariuszy PASS, mutacja poprawki wywraca scenariusz 7. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
172 lines
6.9 KiB
Markdown
172 lines
6.9 KiB
Markdown
# 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: 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ć 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).
|