Files
vd_960/DBNMQTTool/dbn_mqtt_tool/device_manager.py
T
wangfq 9d9e4fa4bc DBNMQTTool: initialize 消息解析后同步显示设备型号到列表
- device_manager.mark_online 增加 model/hard_ver/soft_ver 参数
- 修复旧格式 extra_info.version 取 soft_ver 的问题
- main.py 的 initialize 处理传入完整设备信息
2026-07-10 09:04:39 +08:00

131 lines
4.3 KiB
Python

"""
设备管理器 — 管理已发现的设备及其状态
"""
import threading
from typing import Callable, Optional
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class DeviceInfo:
dev_serial: str
model: str = ""
hard_ver: str = ""
soft_ver: str = ""
product_code: str = ""
net_enabled: bool = False
iot_enabled: bool = False
bus: dict = field(default_factory=dict)
last_seen: Optional[datetime] = None
online: bool = False
loop_data: Optional[dict] = None
events: list = field(default_factory=list)
extra_info: dict = field(default_factory=dict) # from initialize 消息
class DeviceManager:
"""管理设备列表和状态"""
def __init__(self):
self._devices: dict[str, DeviceInfo] = {}
self._lock = threading.RLock() # 可重入锁: update_xxx 内部调用 get_or_create
self._callbacks: list[Callable] = []
def on_change(self, callback: Callable):
self._callbacks.append(callback)
def _notify(self):
for cb in self._callbacks:
try:
cb()
except Exception:
pass
def get_or_create(self, dev_serial: str) -> DeviceInfo:
with self._lock:
if dev_serial not in self._devices:
self._devices[dev_serial] = DeviceInfo(dev_serial=dev_serial)
return self._devices[dev_serial]
def update_from_dev_info(self, dev_serial: str, data: dict):
with self._lock:
dev = self.get_or_create(dev_serial)
dev.dev_serial = data.get("dev_serial", dev_serial)
dev.model = data.get("model", dev.model)
dev.hard_ver = data.get("hard_ver", dev.hard_ver)
dev.soft_ver = data.get("soft_ver", dev.soft_ver)
dev.product_code = data.get("product_code", dev.product_code)
sc = data.get("sub_code", {})
dev.net_enabled = sc.get("net", dev.net_enabled)
dev.iot_enabled = sc.get("iot", dev.iot_enabled)
dev.bus = data.get("bus", dev.bus)
dev.last_seen = datetime.now()
dev.online = True
self._notify()
def update_loop_data(self, dev_serial: str, data: dict):
with self._lock:
dev = self.get_or_create(dev_serial)
dev.loop_data = data
dev.last_seen = datetime.now()
dev.online = True
self._notify()
def update_event(self, dev_serial: str, data: dict):
with self._lock:
dev = self.get_or_create(dev_serial)
events = data.get("events", [])
for evt in events:
evt["_ts"] = datetime.now()
dev.events.insert(0, evt)
dev.events = dev.events[:100] # 保留最近 100 条
dev.last_seen = datetime.now()
dev.online = True
self._notify()
def update_heartbeat(self, dev_serial: str, data: dict):
with self._lock:
dev = self.get_or_create(dev_serial)
dev.last_seen = datetime.now()
dev.online = True
self._notify()
def mark_online(self, dev_serial: str, model: str = "",
hard_ver: str = "", soft_ver: str = "",
extra_info: Optional[dict] = None):
"""V1.03: 设备上线初始化标记"""
with self._lock:
dev = self.get_or_create(dev_serial)
dev.online = True
dev.last_seen = datetime.now()
if model:
dev.model = model
if hard_ver:
dev.hard_ver = hard_ver
if soft_ver:
dev.soft_ver = soft_ver
if extra_info:
dev.extra_info = extra_info
self._notify()
def set_offline(self):
"""标记所有设备离线(断开连接时调用)"""
with self._lock:
now = datetime.now()
for dev in self._devices.values():
if dev.last_seen and (now - dev.last_seen).seconds > 120:
dev.online = False
self._notify()
@property
def devices(self) -> dict[str, DeviceInfo]:
with self._lock:
return dict(self._devices)
@property
def online_devices(self) -> list[DeviceInfo]:
with self._lock:
return [d for d in self._devices.values() if d.online]