处理OTA下发无回执超时

This commit is contained in:
stu2not
2026-06-16 11:36:33 +08:00
parent 5ef67d71f6
commit 9ae5bc8f83
2 changed files with 39 additions and 1 deletions

View File

@@ -1,5 +1,5 @@
from collections.abc import Mapping from collections.abc import Mapping
from datetime import datetime, time, timedelta from datetime import datetime, timezone, time, timedelta
from typing import Any, List, Literal from typing import Any, List, Literal
from services.database_service_base import DatabaseServiceBase from services.database_service_base import DatabaseServiceBase
@@ -8,6 +8,7 @@ from fastapi import HTTPException
from banban.dao.device import DeviceDAO from banban.dao.device import DeviceDAO
from banban.service.device_setting import device_setting_service from banban.service.device_setting import device_setting_service
from banban.service.ota_firmware import ota_firmware_service from banban.service.ota_firmware import ota_firmware_service
from config import settings
from services.device_update_manager import device_firmware_update_manager from services.device_update_manager import device_firmware_update_manager
@@ -301,6 +302,29 @@ class DeviceService(DatabaseServiceBase):
latest_parts.extend([0] * (max_len - len(latest_parts))) latest_parts.extend([0] * (max_len - len(latest_parts)))
return latest_parts > current_parts return latest_parts > current_parts
def _firmware_sent_timed_out(self, updated_at: Any) -> bool:
timeout_seconds = max(0, int(settings.ota_sent_timeout_seconds or 0))
if timeout_seconds <= 0 or not updated_at:
return False
if isinstance(updated_at, str):
text = updated_at.strip()
if not text:
return False
try:
updated_at = datetime.fromisoformat(text.replace("Z", "+00:00"))
except ValueError:
try:
updated_at = datetime.strptime(text, "%Y-%m-%d %H:%M:%S")
except ValueError:
return False
if not isinstance(updated_at, datetime):
return False
now = datetime.now(timezone.utc) if updated_at.tzinfo else datetime.now()
return (now - updated_at).total_seconds() >= timeout_seconds
async def get_firmware_status( async def get_firmware_status(
self, self,
*, *,
@@ -320,6 +344,18 @@ class DeviceService(DatabaseServiceBase):
progress = update_record.get("progress") if update_record else 0.0 progress = update_record.get("progress") if update_record else 0.0
target_version = update_record.get("firmware_version") if update_record else None target_version = update_record.get("firmware_version") if update_record else None
updated_at = update_record.get("updated_at") if update_record else None updated_at = update_record.get("updated_at") if update_record else None
if str(update_status or "").strip().lower() == "sent" and self._firmware_sent_timed_out(updated_at):
update_status = "failed"
progress = 0.0
await device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=target_version or latest_version or current_version or "unknown",
update_status="failed",
progress=0.0,
)
update_record = await device_firmware_update_manager.get_firmware_update_dict(device_id)
if update_record:
updated_at = update_record.get("updated_at") or updated_at
return { return {
"device_id": device_id, "device_id": device_id,

View File

@@ -78,6 +78,8 @@ class Settings(BaseSettings):
talkingq_mqtt_keepalive: int = Field(default=60, validation_alias="TALKINGQ_MQTT_KEEPALIVE") talkingq_mqtt_keepalive: int = Field(default=60, validation_alias="TALKINGQ_MQTT_KEEPALIVE")
talkingq_mqtt_nfc_notice_interval: int = Field(default=600, validation_alias="TALKINGQ_MQTT_NFC_NOTICE_INTERVAL") talkingq_mqtt_nfc_notice_interval: int = Field(default=600, validation_alias="TALKINGQ_MQTT_NFC_NOTICE_INTERVAL")
ota_sent_timeout_seconds: int = Field(default=300, validation_alias="OTA_SENT_TIMEOUT_SECONDS")
admin_api_key: str = Field(default="", validation_alias="ADMIN_API_KEY") admin_api_key: str = Field(default="", validation_alias="ADMIN_API_KEY")
client_api_key: str = Field(default="", validation_alias="CLIENT_API_KEY") client_api_key: str = Field(default="", validation_alias="CLIENT_API_KEY")