Files
tianpu-ems/backend/app/api/v1/monitoring.py
Du Wenbo f53a610a19 Initial commit: Tianpu Zero-Carbon EMS Platform
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>
2026-04-01 13:36:06 +08:00

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},
]
}