Add room and analytics services with CRUD API endpoints
- Implement RoomService for room management and metrics - Add AnalyticsService for sensor data analytics and trends - Extend models with Room, RoomCreate, RoomUpdate, RoomInfo - Add room CRUD endpoints to FastAPI app - Add database connection logic for MongoDB and Redis - Refactor sensor service logic into SensorService class
This commit is contained in:
251
microservices/sensor-service/sensor_service.py
Normal file
251
microservices/sensor-service/sensor_service.py
Normal file
@@ -0,0 +1,251 @@
|
||||
"""
|
||||
Sensor service business logic
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Dict, Any, Optional
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SensorService:
|
||||
"""Service for managing sensors and sensor data"""
|
||||
|
||||
def __init__(self, db, redis_client):
|
||||
self.db = db
|
||||
self.redis = redis_client
|
||||
|
||||
async def get_sensors(self, room: Optional[str] = None, sensor_type: Optional[str] = None, status: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||
"""Get sensors with optional filtering"""
|
||||
try:
|
||||
query = {}
|
||||
|
||||
if room:
|
||||
query["room"] = room
|
||||
if sensor_type:
|
||||
query["sensor_type"] = sensor_type
|
||||
if status:
|
||||
query["status"] = status
|
||||
|
||||
cursor = self.db.sensors.find(query)
|
||||
sensors = []
|
||||
|
||||
async for sensor in cursor:
|
||||
sensor["_id"] = str(sensor["_id"])
|
||||
sensors.append(sensor)
|
||||
|
||||
return sensors
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting sensors: {e}")
|
||||
raise
|
||||
|
||||
async def get_sensor_details(self, sensor_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get detailed sensor information"""
|
||||
try:
|
||||
sensor = await self.db.sensors.find_one({"sensor_id": sensor_id})
|
||||
|
||||
if sensor:
|
||||
sensor["_id"] = str(sensor["_id"])
|
||||
|
||||
# Get recent readings
|
||||
recent_readings = await self.get_sensor_data(sensor_id, limit=10)
|
||||
sensor["recent_readings"] = recent_readings.get("readings", [])
|
||||
|
||||
return sensor
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting sensor details: {e}")
|
||||
raise
|
||||
|
||||
async def get_sensor_data(self, sensor_id: str, start_time: Optional[int] = None,
|
||||
end_time: Optional[int] = None, limit: int = 100, offset: int = 0) -> Dict[str, Any]:
|
||||
"""Get historical sensor data"""
|
||||
try:
|
||||
query = {"sensor_id": sensor_id}
|
||||
|
||||
if start_time or end_time:
|
||||
query["timestamp"] = {}
|
||||
if start_time:
|
||||
query["timestamp"]["$gte"] = start_time
|
||||
if end_time:
|
||||
query["timestamp"]["$lte"] = end_time
|
||||
|
||||
# Get total count
|
||||
total_count = await self.db.sensor_readings.count_documents(query)
|
||||
|
||||
# Get readings
|
||||
cursor = self.db.sensor_readings.find(query).sort("timestamp", -1).skip(offset).limit(limit)
|
||||
readings = []
|
||||
|
||||
async for reading in cursor:
|
||||
reading["_id"] = str(reading["_id"])
|
||||
readings.append(reading)
|
||||
|
||||
return {
|
||||
"readings": readings,
|
||||
"total_count": total_count,
|
||||
"execution_time_ms": 0 # Placeholder
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting sensor data: {e}")
|
||||
raise
|
||||
|
||||
async def create_sensor(self, sensor_data) -> Dict[str, Any]:
|
||||
"""Create a new sensor"""
|
||||
try:
|
||||
# Check if sensor already exists
|
||||
existing = await self.db.sensors.find_one({"sensor_id": sensor_data.sensor_id})
|
||||
if existing:
|
||||
raise ValueError(f"Sensor {sensor_data.sensor_id} already exists")
|
||||
|
||||
# Create sensor document
|
||||
sensor_doc = {
|
||||
"sensor_id": sensor_data.sensor_id,
|
||||
"name": sensor_data.name,
|
||||
"sensor_type": sensor_data.sensor_type.value if hasattr(sensor_data.sensor_type, 'value') else str(sensor_data.sensor_type),
|
||||
"room": sensor_data.room,
|
||||
"location": sensor_data.location if hasattr(sensor_data, 'location') else None,
|
||||
"status": "active",
|
||||
"created_at": datetime.utcnow(),
|
||||
"updated_at": datetime.utcnow()
|
||||
}
|
||||
|
||||
result = await self.db.sensors.insert_one(sensor_doc)
|
||||
|
||||
return {"created_at": datetime.utcnow()}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating sensor: {e}")
|
||||
raise
|
||||
|
||||
async def update_sensor(self, sensor_id: str, update_data: Dict[str, Any]) -> bool:
|
||||
"""Update sensor metadata"""
|
||||
try:
|
||||
update_data["updated_at"] = datetime.utcnow()
|
||||
|
||||
result = await self.db.sensors.update_one(
|
||||
{"sensor_id": sensor_id},
|
||||
{"$set": update_data}
|
||||
)
|
||||
|
||||
return result.modified_count > 0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating sensor: {e}")
|
||||
raise
|
||||
|
||||
async def delete_sensor(self, sensor_id: str) -> Dict[str, Any]:
|
||||
"""Delete a sensor and its data"""
|
||||
try:
|
||||
# Delete readings
|
||||
readings_result = await self.db.sensor_readings.delete_many({"sensor_id": sensor_id})
|
||||
|
||||
# Delete sensor
|
||||
await self.db.sensors.delete_one({"sensor_id": sensor_id})
|
||||
|
||||
return {"readings_deleted": readings_result.deleted_count}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting sensor: {e}")
|
||||
raise
|
||||
|
||||
async def ingest_sensor_data(self, sensor_data) -> Dict[str, Any]:
|
||||
"""Ingest real-time sensor data"""
|
||||
try:
|
||||
# Create reading document
|
||||
reading_doc = {
|
||||
"sensor_id": sensor_data.sensor_id,
|
||||
"timestamp": sensor_data.timestamp,
|
||||
"value": sensor_data.value,
|
||||
"unit": sensor_data.unit if hasattr(sensor_data, 'unit') else None,
|
||||
"room": sensor_data.room if hasattr(sensor_data, 'room') else None,
|
||||
"created_at": datetime.utcnow()
|
||||
}
|
||||
|
||||
# Store in database
|
||||
await self.db.sensor_readings.insert_one(reading_doc)
|
||||
|
||||
# Cache recent value in Redis
|
||||
if self.redis:
|
||||
cache_key = f"sensor:{sensor_data.sensor_id}:latest"
|
||||
await self.redis.setex(cache_key, 3600, json.dumps(reading_doc, default=str))
|
||||
|
||||
return {"success": True}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error ingesting sensor data: {e}")
|
||||
raise
|
||||
|
||||
async def export_data(self, start_time: int, end_time: int, sensor_ids: Optional[str] = None,
|
||||
format: str = "json") -> Dict[str, Any]:
|
||||
"""Export sensor data"""
|
||||
try:
|
||||
query = {
|
||||
"timestamp": {"$gte": start_time, "$lte": end_time}
|
||||
}
|
||||
|
||||
if sensor_ids:
|
||||
sensor_list = [s.strip() for s in sensor_ids.split(",")]
|
||||
query["sensor_id"] = {"$in": sensor_list}
|
||||
|
||||
cursor = self.db.sensor_readings.find(query).sort("timestamp", 1)
|
||||
readings = []
|
||||
|
||||
async for reading in cursor:
|
||||
reading["_id"] = str(reading["_id"])
|
||||
readings.append(reading)
|
||||
|
||||
return {
|
||||
"format": format,
|
||||
"data": readings,
|
||||
"total_records": len(readings),
|
||||
"period": {"start": start_time, "end": end_time}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error exporting data: {e}")
|
||||
raise
|
||||
|
||||
async def get_events(self, severity: Optional[str] = None, event_type: Optional[str] = None,
|
||||
hours: int = 24, limit: int = 50) -> List[Dict[str, Any]]:
|
||||
"""Get system events"""
|
||||
try:
|
||||
start_time = datetime.utcnow() - timedelta(hours=hours)
|
||||
|
||||
query = {"timestamp": {"$gte": start_time}}
|
||||
|
||||
if severity:
|
||||
query["severity"] = severity
|
||||
if event_type:
|
||||
query["event_type"] = event_type
|
||||
|
||||
cursor = self.db.system_events.find(query).sort("timestamp", -1).limit(limit)
|
||||
events = []
|
||||
|
||||
async for event in cursor:
|
||||
event["_id"] = str(event["_id"])
|
||||
events.append(event)
|
||||
|
||||
return events
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting events: {e}")
|
||||
return []
|
||||
|
||||
async def cleanup_old_data(self, cutoff_date: datetime):
|
||||
"""Clean up old sensor data"""
|
||||
try:
|
||||
result = await self.db.sensor_readings.delete_many({
|
||||
"created_at": {"$lt": cutoff_date}
|
||||
})
|
||||
|
||||
logger.info(f"Cleaned up {result.deleted_count} old sensor readings")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error cleaning up old data: {e}")
|
||||
raise
|
||||
Reference in New Issue
Block a user