Files
ros_control/app/services/poller.py
T

45 lines
1.7 KiB
Python
Raw Normal View History

"""Фоновый опрос устройств: быстрое обнаружение недоступности (period = POLL_INTERVAL)."""
import asyncio
import logging
import time
from app.config import get_settings
from app.services import devices, ops
log = logging.getLogger("ros_control.poller")
async def cycle(last_full: dict[int, float]) -> None:
"""Один проход по всем устройствам (параллельно, не более MAX_CONCURRENCY одновременно)."""
s = get_settings()
ids = [d.id for d in devices.list_devices()]
for gone in set(last_full) - set(ids):
del last_full[gone]
sem = asyncio.Semaphore(s.max_concurrency)
started = time.monotonic()
async def one(device_id: int) -> None:
async with sem:
full = started - last_full.get(device_id, float("-inf")) >= s.update_check_interval
try:
await ops.poll_device(device_id, full)
except LookupError: # удалили во время цикла
return
except Exception: # noqa: BLE001 — сбой одного устройства не должен ронять опрос остальных
log.exception("poll failed: device %s", device_id)
return
if full:
last_full[device_id] = time.monotonic()
await asyncio.gather(*(one(i) for i in ids))
async def run() -> None:
last_full: dict[int, float] = {}
interval = get_settings().poll_interval
while True:
try:
await cycle(last_full)
except Exception: # noqa: BLE001
log.exception("poll cycle failed")
await asyncio.sleep(interval)