Full-stack energy management system for Tianpu Daxing campus. - Frontend: React 19 + TypeScript + Ant Design + ECharts - Backend: FastAPI + SQLAlchemy + PostgreSQL/TimescaleDB - Features: PV monitoring, heat pump management, carbon tracking, alarms, reports Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
3.2 KiB
Python
79 lines
3.2 KiB
Python
from datetime import datetime, timedelta, timezone
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, func, and_
|
|
from app.core.database import get_db
|
|
from app.core.deps import get_current_user
|
|
from app.models.device import Device
|
|
from app.models.energy import EnergyData
|
|
from app.models.user import User
|
|
|
|
router = APIRouter(prefix="/monitoring", tags=["实时监控"])
|
|
|
|
|
|
@router.get("/devices/{device_id}/realtime")
|
|
async def device_realtime(device_id: int, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
|
|
"""获取单台设备的最新实时数据"""
|
|
now = datetime.now(timezone.utc)
|
|
five_min_ago = now - timedelta(minutes=5)
|
|
|
|
result = await db.execute(
|
|
select(EnergyData).where(
|
|
and_(EnergyData.device_id == device_id, EnergyData.timestamp >= five_min_ago)
|
|
).order_by(EnergyData.timestamp.desc()).limit(20)
|
|
)
|
|
data_points = result.scalars().all()
|
|
latest = {}
|
|
for d in data_points:
|
|
if d.data_type not in latest:
|
|
latest[d.data_type] = {"value": d.value, "unit": d.unit, "timestamp": str(d.timestamp)}
|
|
|
|
device_q = await db.execute(select(Device).where(Device.id == device_id))
|
|
device = device_q.scalar_one_or_none()
|
|
|
|
return {
|
|
"device": {
|
|
"id": device.id, "name": device.name, "code": device.code,
|
|
"device_type": device.device_type, "status": device.status,
|
|
"model": device.model, "manufacturer": device.manufacturer,
|
|
} if device else None,
|
|
"data": latest,
|
|
}
|
|
|
|
|
|
@router.get("/energy-flow")
|
|
async def energy_flow(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
|
|
"""能流图数据 - 展示能量流向"""
|
|
now = datetime.now(timezone.utc)
|
|
five_min_ago = now - timedelta(minutes=5)
|
|
|
|
# 获取各类设备最新功率
|
|
result = await db.execute(
|
|
select(Device.device_type, func.sum(EnergyData.value))
|
|
.join(EnergyData, EnergyData.device_id == Device.id)
|
|
.where(and_(EnergyData.timestamp >= five_min_ago, EnergyData.data_type == "power"))
|
|
.group_by(Device.device_type)
|
|
)
|
|
power_by_type = {row[0]: round(row[1], 2) for row in result.all()}
|
|
|
|
pv_power = power_by_type.get("pv_inverter", 0)
|
|
hp_power = power_by_type.get("heat_pump", 0)
|
|
total_load = hp_power + power_by_type.get("meter", 0)
|
|
grid_import = max(0, total_load - pv_power)
|
|
grid_export = max(0, pv_power - total_load)
|
|
|
|
return {
|
|
"nodes": [
|
|
{"id": "pv", "name": "光伏发电", "power": pv_power, "unit": "kW"},
|
|
{"id": "grid", "name": "电网", "power": grid_import - grid_export, "unit": "kW"},
|
|
{"id": "heatpump", "name": "热泵系统", "power": hp_power, "unit": "kW"},
|
|
{"id": "building", "name": "建筑负荷", "power": total_load, "unit": "kW"},
|
|
],
|
|
"links": [
|
|
{"source": "pv", "target": "building", "value": min(pv_power, total_load)},
|
|
{"source": "pv", "target": "grid", "value": grid_export},
|
|
{"source": "grid", "target": "building", "value": grid_import},
|
|
{"source": "grid", "target": "heatpump", "value": hp_power},
|
|
]
|
|
}
|