- Added a new filtering system to the dashboard for device types and statuses. - Implemented asynchronous SLM status synchronization to update the Emitter table. - Updated the status snapshot endpoint to sync SLM status before generating the snapshot. - Refactored the dashboard HTML to include filter controls and JavaScript for managing filter state. - Improved the unit detail page to handle modem associations and cascade updates to paired devices. - Removed redundant code related to syncing start time for measuring devices.
126 lines
4.3 KiB
Python
126 lines
4.3 KiB
Python
"""
|
|
SLM Status Synchronization Service
|
|
|
|
Syncs SLM device status from SLMM backend to Terra-View's Emitter table.
|
|
This bridges SLMM's polling data with Terra-View's status snapshot system.
|
|
|
|
SLMM tracks device reachability via background polling. This service
|
|
fetches that data and creates/updates Emitter records so SLMs appear
|
|
correctly in the dashboard status snapshot.
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime, timezone
|
|
from typing import Dict, Any
|
|
|
|
from backend.database import get_db_session
|
|
from backend.models import Emitter
|
|
from backend.services.slmm_client import get_slmm_client, SLMMClientError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def sync_slm_status_to_emitters() -> Dict[str, Any]:
|
|
"""
|
|
Fetch SLM status from SLMM and sync to Terra-View's Emitter table.
|
|
|
|
For each device in SLMM's polling status:
|
|
- If last_success exists, create/update Emitter with that timestamp
|
|
- If not reachable, update Emitter with last known timestamp (or None)
|
|
|
|
Returns:
|
|
Dict with synced_count, error_count, errors list
|
|
"""
|
|
client = get_slmm_client()
|
|
synced = 0
|
|
errors = []
|
|
|
|
try:
|
|
# Get polling status from SLMM
|
|
status_response = await client.get_polling_status()
|
|
|
|
# Handle nested response structure
|
|
data = status_response.get("data", status_response)
|
|
devices = data.get("devices", [])
|
|
|
|
if not devices:
|
|
logger.debug("No SLM devices in SLMM polling status")
|
|
return {"synced_count": 0, "error_count": 0, "errors": []}
|
|
|
|
db = get_db_session()
|
|
try:
|
|
for device in devices:
|
|
unit_id = device.get("unit_id")
|
|
if not unit_id:
|
|
continue
|
|
|
|
try:
|
|
# Get or create Emitter record
|
|
emitter = db.query(Emitter).filter(Emitter.id == unit_id).first()
|
|
|
|
# Determine last_seen from SLMM data
|
|
last_success_str = device.get("last_success")
|
|
is_reachable = device.get("is_reachable", False)
|
|
|
|
if last_success_str:
|
|
# Parse ISO format timestamp
|
|
last_seen = datetime.fromisoformat(
|
|
last_success_str.replace("Z", "+00:00")
|
|
)
|
|
# Convert to naive UTC for consistency with existing code
|
|
if last_seen.tzinfo:
|
|
last_seen = last_seen.astimezone(timezone.utc).replace(tzinfo=None)
|
|
else:
|
|
last_seen = None
|
|
|
|
# Status will be recalculated by snapshot.py based on time thresholds
|
|
# Just store a provisional status here
|
|
status = "OK" if is_reachable else "Missing"
|
|
|
|
# Store last error message if available
|
|
last_error = device.get("last_error") or ""
|
|
|
|
if emitter:
|
|
# Update existing record
|
|
emitter.last_seen = last_seen
|
|
emitter.status = status
|
|
emitter.unit_type = "slm"
|
|
emitter.last_file = last_error
|
|
else:
|
|
# Create new record
|
|
emitter = Emitter(
|
|
id=unit_id,
|
|
unit_type="slm",
|
|
last_seen=last_seen,
|
|
last_file=last_error,
|
|
status=status
|
|
)
|
|
db.add(emitter)
|
|
|
|
synced += 1
|
|
|
|
except Exception as e:
|
|
errors.append(f"{unit_id}: {str(e)}")
|
|
logger.error(f"Error syncing SLM {unit_id}: {e}")
|
|
|
|
db.commit()
|
|
|
|
finally:
|
|
db.close()
|
|
|
|
if synced > 0:
|
|
logger.info(f"Synced {synced} SLM device(s) to Emitter table")
|
|
|
|
except SLMMClientError as e:
|
|
logger.warning(f"Could not reach SLMM for status sync: {e}")
|
|
errors.append(f"SLMM unreachable: {str(e)}")
|
|
except Exception as e:
|
|
logger.error(f"Error in SLM status sync: {e}", exc_info=True)
|
|
errors.append(str(e))
|
|
|
|
return {
|
|
"synced_count": synced,
|
|
"error_count": len(errors),
|
|
"errors": errors
|
|
}
|