lidar-maps : navigation en cache seule et pyramide entretenue en tâche de fond
Sur une petite machine (Raspberry Pi), LIDAR_TILE_CACHE_ONLY sert les tuiles depuis le cache uniquement (tuile absente = transparente non mémorisable, X-Tile-Pending) et LIDAR_TILE_BACKGROUND fait surveiller les dalles par un sondeur : chaque dalle nouvelle ou régénérée par le worker remet sa pyramide en file, rendue à basse priorité et au ralenti. Scan et état pilotables via /api/map/background, pré-calcul exhaustif toujours via /api/map/warm.
This commit is contained in:
@ -530,3 +530,68 @@ def test_worker_limits_are_configurable(monkeypatch):
|
||||
monkeypatch.delenv("LIDAR_TILE_FETCH_WORKERS", raising=False)
|
||||
importlib.reload(tiles)
|
||||
importlib.reload(mapserve)
|
||||
|
||||
|
||||
def _reset_bg(mapserve):
|
||||
mapserve._bg["queue"].clear()
|
||||
mapserve._bg["queued"].clear()
|
||||
mapserve._bg["snapshot"] = None
|
||||
mapserve._bg["stats"].update(rendues=0, a_jour=0, vides=0, scans=0,
|
||||
dernier_scan=None, ajouts_dernier_scan=0)
|
||||
|
||||
|
||||
def test_tile_cache_only_never_renders(tmp_path, monkeypatch):
|
||||
"""Mode cache seule : tuile manquante = transparente non mémorisable."""
|
||||
import lidar_pipeline.mapserve as mapserve
|
||||
from lidar_pipeline import tiles
|
||||
_make_dalle(tmp_path, 1054, 6882, ["slope"])
|
||||
monkeypatch.setattr(mapserve, "OUTPUT_DIR", tmp_path)
|
||||
tiles.source_index(tmp_path, force=True)
|
||||
monkeypatch.setenv("LIDAR_TILE_CACHE_ONLY", "1")
|
||||
z = 15
|
||||
x, y = _tile_of_cell(1054, 6882, z)
|
||||
resp = asyncio.run(mapserve.tile("slope", z, x, f"{y}.png"))
|
||||
assert resp.headers["X-Tile-Pending"] == "1"
|
||||
assert resp.headers["X-Tile-Empty"] == "1"
|
||||
assert resp.headers["Cache-Control"] == "no-store"
|
||||
assert not tiles.tile_cache_path(tmp_path, "slope", z, x, y).exists()
|
||||
# Une fois rendue par ailleurs : servie depuis le cache, mémorisable
|
||||
tiles.get_tile(tmp_path, "slope", z, x, y)
|
||||
resp = asyncio.run(mapserve.tile("slope", z, x, f"{y}.png", v="s"))
|
||||
assert resp.headers["X-Tile-Empty"] == "0"
|
||||
assert "X-Tile-Pending" not in resp.headers
|
||||
assert "immutable" in resp.headers["Cache-Control"]
|
||||
|
||||
|
||||
def test_background_scan_detects_and_fills(tmp_path, monkeypatch):
|
||||
"""La maintenance détecte les dalles, remplit la pyramide, puis se tait."""
|
||||
import lidar_pipeline.mapserve as mapserve
|
||||
from lidar_pipeline import tiles
|
||||
_make_dalle(tmp_path, 1054, 6882, ["slope"])
|
||||
monkeypatch.setattr(mapserve, "OUTPUT_DIR", tmp_path)
|
||||
monkeypatch.setenv("LIDAR_TILE_BACKGROUND_MAX_Z", "14")
|
||||
monkeypatch.setenv("LIDAR_TILE_BACKGROUND_SCALE", "1")
|
||||
monkeypatch.setenv("LIDAR_TILE_BACKGROUND_FMT", "png")
|
||||
monkeypatch.setenv("LIDAR_TILE_BACKGROUND_PAUSE", "0")
|
||||
_reset_bg(mapserve)
|
||||
try:
|
||||
# Premier scan : tout est nouveau, bas niveaux en tête de file
|
||||
assert mapserve.background_scan(tmp_path) > 0
|
||||
zs = [t[1] for t in mapserve._bg["queue"]]
|
||||
assert zs == sorted(zs) and max(zs) <= 14
|
||||
processed = 0
|
||||
while mapserve._bg["queue"] and processed < 1000:
|
||||
mapserve._bg_process_one()
|
||||
processed += 1
|
||||
assert not mapserve._bg["queue"]
|
||||
assert mapserve._bg["stats"]["rendues"] > 0
|
||||
x, y = _tile_of_cell(1054, 6882, 14)
|
||||
assert tiles.tile_cache_path(
|
||||
tmp_path, "slope", 14, x, y, 1, "png").exists()
|
||||
# Rien n'a changé : plus aucun ajout
|
||||
assert mapserve.background_scan(tmp_path) == 0
|
||||
# Une dalle nouvelle (worker) : sa pyramide repart seule en file
|
||||
_make_dalle(tmp_path, 1055, 6882, ["slope"])
|
||||
assert mapserve.background_scan(tmp_path) > 0
|
||||
finally:
|
||||
_reset_bg(mapserve)
|
||||
|
||||
@ -487,3 +487,20 @@ def test_remote_index_failure_keeps_local(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(tiles, "_remote_payload", lambda force=False: None)
|
||||
layers = tiles.source_index(tmp_path, force=True)
|
||||
assert set(layers) == {"slope"}
|
||||
|
||||
|
||||
def test_cached_tile_states(tmp_path):
|
||||
"""`cached_tile` : lecture cache seule — fresh / pending / empty, sans rendu."""
|
||||
from lidar_pipeline import tiles
|
||||
_make_dalle(tmp_path, 1054, 6882, ["slope"])
|
||||
x, y = _tile_of_cell(1054, 6882, 14)
|
||||
data, state = tiles.cached_tile(tmp_path, "slope", 14, x, y)
|
||||
assert state == "pending" and data is None
|
||||
assert not tiles.tile_cache_path(tmp_path, "slope", 14, x, y).exists()
|
||||
tiles.get_tile(tmp_path, "slope", 14, x, y) # rendu (maintenance, warm…)
|
||||
data, state = tiles.cached_tile(tmp_path, "slope", 14, x, y)
|
||||
assert state == "fresh" and data
|
||||
# Hors données : état empty + marqueur posé, toujours sans rendu
|
||||
data, state = tiles.cached_tile(tmp_path, "slope", 14, 0, 0)
|
||||
assert state == "empty" and data is None
|
||||
assert tiles.empty_marker_exists(tmp_path, "slope", 14, 0, 0)
|
||||
|
||||
Reference in New Issue
Block a user