first commit

This commit is contained in:
mega 2026-03-19 11:32:17 +00:00
commit 4b98219bf7
144 changed files with 31561 additions and 0 deletions

View file

@ -0,0 +1,110 @@
from fastapi import APIRouter, Depends, Query
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
from core.database import get_session
router = APIRouter()
ROOMS = {
"sg-01": [
{"room_id": "hall-a", "racks": [f"SG1A01.{i:02d}" for i in range(1, 21)] + [f"SG1A02.{i:02d}" for i in range(1, 21)], "crac_id": "crac-01"},
{"room_id": "hall-b", "racks": [f"SG1B01.{i:02d}" for i in range(1, 21)] + [f"SG1B02.{i:02d}" for i in range(1, 21)], "crac_id": "crac-02"},
]
}
# Rated capacity config — would be per-asset configurable in production
RACK_POWER_CAPACITY_KW = 10.0 # max kW per rack
ROOM_POWER_CAPACITY_KW = 400.0 # 40 racks × 10 kW
CRAC_COOLING_CAPACITY_KW = 160.0 # rated cooling per CRAC
RACK_U_TOTAL = 42
@router.get("/summary")
async def capacity_summary(
site_id: str = Query(...),
session: AsyncSession = Depends(get_session),
):
"""Per-rack and per-room capacity: power used vs rated, cooling load vs rated, rack space."""
result = await session.execute(text("""
SELECT DISTINCT ON (sensor_id)
rack_id, room_id, sensor_type, value
FROM readings
WHERE site_id = :site_id
AND sensor_type IN ('power_kw', 'temperature')
AND rack_id IS NOT NULL
AND recorded_at > NOW() - INTERVAL '10 minutes'
ORDER BY sensor_id, recorded_at DESC
"""), {"site_id": site_id})
rows = result.mappings().all()
# Index: rack_id → {power_kw, temperature, room_id}
rack_idx: dict[str, dict] = {}
for row in rows:
rid = row["rack_id"]
if rid not in rack_idx:
rack_idx[rid] = {"room_id": row["room_id"]}
if row["sensor_type"] == "power_kw":
rack_idx[rid]["power_kw"] = round(float(row["value"]), 2)
elif row["sensor_type"] == "temperature":
rack_idx[rid]["temperature"] = round(float(row["value"]), 1)
rooms_out = []
racks_out = []
for room in ROOMS.get(site_id, []):
room_id = room["room_id"]
room_power = 0.0
populated = 0
for rack_id in room["racks"]:
d = rack_idx.get(rack_id, {})
power = d.get("power_kw")
temp = d.get("temperature")
if power is not None:
room_power += power
populated += 1
power_pct = round((power / RACK_POWER_CAPACITY_KW) * 100, 1) if power is not None else None
racks_out.append({
"rack_id": rack_id,
"room_id": room_id,
"power_kw": power,
"power_capacity_kw": RACK_POWER_CAPACITY_KW,
"power_pct": power_pct,
"temp": temp,
})
room_power = round(room_power, 2)
rooms_out.append({
"room_id": room_id,
"power": {
"used_kw": room_power,
"capacity_kw": ROOM_POWER_CAPACITY_KW,
"pct": round((room_power / ROOM_POWER_CAPACITY_KW) * 100, 1),
"headroom_kw": round(ROOM_POWER_CAPACITY_KW - room_power, 2),
},
"cooling": {
"load_kw": room_power, # IT power ≈ heat generated
"capacity_kw": CRAC_COOLING_CAPACITY_KW,
"pct": round(min(100.0, (room_power / CRAC_COOLING_CAPACITY_KW) * 100), 1),
"headroom_kw": round(max(0.0, CRAC_COOLING_CAPACITY_KW - room_power), 2),
},
"space": {
"racks_total": len(room["racks"]),
"racks_populated": populated,
"pct": round((populated / len(room["racks"])) * 100, 1),
},
})
return {
"site_id": site_id,
"config": {
"rack_power_kw": RACK_POWER_CAPACITY_KW,
"room_power_kw": ROOM_POWER_CAPACITY_KW,
"crac_cooling_kw": CRAC_COOLING_CAPACITY_KW,
"rack_u_total": RACK_U_TOTAL,
},
"rooms": rooms_out,
"racks": racks_out,
}