feat(3-1): stale state detection and dimmed display
When fetch times out or returns empty after prior data, retain last aircraft with is_stale=True and render them as black outlines so the display stays informative rather than blank or crashing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+27
-3
@@ -1,8 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import logging
|
||||
import time
|
||||
|
||||
import requests
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from planemapper.constants import (
|
||||
@@ -13,6 +15,7 @@ from planemapper.constants import (
|
||||
)
|
||||
from planemapper.display import DisplayInterface, WaveshareDisplay
|
||||
from planemapper.fetcher import HttpFetcher
|
||||
from planemapper.models import Aircraft
|
||||
from planemapper.provisioning.config import read as read_config
|
||||
from planemapper.renderer.basemap import load as load_basemap
|
||||
from planemapper.renderer.projection import MapBounds
|
||||
@@ -34,9 +37,28 @@ def _make_startup_screen() -> Image.Image:
|
||||
return image
|
||||
|
||||
|
||||
def _run_one_cycle(renderer: Renderer, fetcher: HttpFetcher, display: DisplayInterface) -> None:
|
||||
def _run_one_cycle(
|
||||
renderer: Renderer,
|
||||
fetcher: HttpFetcher,
|
||||
display: DisplayInterface,
|
||||
last_aircraft: list[Aircraft],
|
||||
) -> list[Aircraft]:
|
||||
t0 = time.monotonic()
|
||||
aircraft_list = fetcher.fetch()
|
||||
stale_needed = False
|
||||
try:
|
||||
fresh = fetcher.fetch()
|
||||
except requests.Timeout:
|
||||
log.warning("fetch timeout — using stale data")
|
||||
fresh = []
|
||||
stale_needed = True
|
||||
else:
|
||||
stale_needed = len(fresh) == 0 and len(last_aircraft) > 0
|
||||
|
||||
if stale_needed:
|
||||
aircraft_list = [dataclasses.replace(a, is_stale=True) for a in last_aircraft]
|
||||
else:
|
||||
aircraft_list = fresh
|
||||
|
||||
t1 = time.monotonic()
|
||||
image = renderer.render(aircraft_list)
|
||||
t2 = time.monotonic()
|
||||
@@ -52,6 +74,7 @@ def _run_one_cycle(renderer: Renderer, fetcher: HttpFetcher, display: DisplayInt
|
||||
)
|
||||
if total > RENDER_WARN_S:
|
||||
log.warning("render slow: %.1fs > %ds threshold", total, RENDER_WARN_S)
|
||||
return aircraft_list
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -68,6 +91,7 @@ def main() -> None:
|
||||
display = WaveshareDisplay()
|
||||
startup = _make_startup_screen()
|
||||
display.show(startup)
|
||||
last: list[Aircraft] = []
|
||||
while True:
|
||||
_run_one_cycle(renderer, fetcher, display)
|
||||
last = _run_one_cycle(renderer, fetcher, display, last)
|
||||
time.sleep(REFRESH_INTERVAL_S)
|
||||
|
||||
@@ -5,7 +5,12 @@ import math
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from planemapper.constants import COLOUR_TRAIL, TRAIL_DOT_SIZE_MAX, TRAIL_DOT_SIZE_MIN
|
||||
from planemapper.constants import (
|
||||
COLOUR_STALE_OUTLINE,
|
||||
COLOUR_TRAIL,
|
||||
TRAIL_DOT_SIZE_MAX,
|
||||
TRAIL_DOT_SIZE_MIN,
|
||||
)
|
||||
from planemapper.models import Aircraft
|
||||
from planemapper.renderer.colours import altitude_to_colour
|
||||
|
||||
@@ -78,5 +83,8 @@ def draw_aircraft(
|
||||
colour = altitude_to_colour(aircraft.altitude_ft)
|
||||
draw = ImageDraw.Draw(image)
|
||||
_draw_trail(draw, trail)
|
||||
_draw_arrow(draw, cx, cy, aircraft.heading or 0.0, colour, aircraft.is_mlat)
|
||||
if aircraft.is_stale:
|
||||
_draw_arrow(draw, cx, cy, aircraft.heading or 0.0, COLOUR_STALE_OUTLINE, is_mlat=True)
|
||||
else:
|
||||
_draw_arrow(draw, cx, cy, aircraft.heading or 0.0, colour, aircraft.is_mlat)
|
||||
_draw_label(draw, cx, cy, aircraft, colour)
|
||||
|
||||
Reference in New Issue
Block a user