first commit
This commit is contained in:
commit
4b98219bf7
144 changed files with 31561 additions and 0 deletions
104
simulators/seed.py
Normal file
104
simulators/seed.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
"""
|
||||
Historical data seeder — runs once at startup.
|
||||
Generates SEED_MINUTES of backdated readings so the dashboard
|
||||
has chart history from the moment the app first loads.
|
||||
"""
|
||||
import asyncio
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
import asyncpg
|
||||
|
||||
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://dcim:dcim_pass@localhost:5432/dcim")
|
||||
SEED_MINUTES = int(os.getenv("SEED_MINUTES", "30"))
|
||||
SITE_ID = "sg-01"
|
||||
INTERVAL_MINS = 5 # one data point every 5 minutes
|
||||
|
||||
ROOMS = [
|
||||
{"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)]},
|
||||
{"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_UNITS = [("hall-a", "crac-01"), ("hall-b", "crac-02")]
|
||||
UPS_UNITS = ["ups-01", "ups-02"]
|
||||
LEAK_SENSORS = ["leak-01"]
|
||||
|
||||
|
||||
async def seed() -> None:
|
||||
# Strip +asyncpg prefix if present (asyncpg needs plain postgresql://)
|
||||
url = DATABASE_URL.replace("postgresql+asyncpg://", "postgresql://")
|
||||
|
||||
print(f"Seeder: connecting to database...")
|
||||
conn = await asyncpg.connect(url)
|
||||
|
||||
try:
|
||||
count = await conn.fetchval(
|
||||
"SELECT COUNT(*) FROM readings WHERE site_id = $1", SITE_ID
|
||||
)
|
||||
if count > 0:
|
||||
print(f"Seeder: {count} rows already exist — skipping.")
|
||||
return
|
||||
|
||||
print(f"Seeder: generating {SEED_MINUTES} minutes of history...")
|
||||
rows: list[tuple] = []
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
for minutes_ago in range(SEED_MINUTES, -1, -INTERVAL_MINS):
|
||||
t = now - timedelta(minutes=minutes_ago)
|
||||
hour = t.hour
|
||||
day_factor = 1.0 + 0.04 * math.sin(math.pi * (hour - 6) / 12)
|
||||
biz_factor = (1.0 + 0.25 * math.sin(math.pi * max(0, hour - 8) / 12)
|
||||
if 8 <= hour <= 20 else 0.85)
|
||||
|
||||
for room in ROOMS:
|
||||
room_id = room["room_id"]
|
||||
for rack_id in room["racks"]:
|
||||
num = int(rack_id[-2:])
|
||||
base_temp = 21.5 + num * 0.15
|
||||
base_load = 2.0 + (num % 5) * 0.8
|
||||
base_id = f"{SITE_ID}/{room_id}/{rack_id}"
|
||||
|
||||
temp = base_temp * day_factor + random.gauss(0, 0.2)
|
||||
humidity = 44.0 + random.gauss(0, 1.0)
|
||||
load = base_load * biz_factor + random.gauss(0, 0.1)
|
||||
|
||||
rows += [
|
||||
(t, f"{base_id}/temperature", "temperature", SITE_ID, room_id, rack_id, round(temp, 2), "°C"),
|
||||
(t, f"{base_id}/humidity", "humidity", SITE_ID, room_id, rack_id, round(humidity, 1), "%"),
|
||||
(t, f"{base_id}/power_kw", "power_kw", SITE_ID, room_id, rack_id, round(max(0.5, load), 2), "kW"),
|
||||
]
|
||||
|
||||
for _, crac_id in CRAC_UNITS:
|
||||
base = f"{SITE_ID}/cooling/{crac_id}"
|
||||
rows += [
|
||||
(t, f"{base}/supply_temp", "cooling_supply", SITE_ID, None, None, round(17.5 + random.gauss(0, 0.2), 2), "°C"),
|
||||
(t, f"{base}/return_temp", "cooling_return", SITE_ID, None, None, round(28.0 + random.gauss(0, 0.3), 2), "°C"),
|
||||
(t, f"{base}/fan_pct", "cooling_fan", SITE_ID, None, None, round(62.0 + random.gauss(0, 2.0), 1), "%"),
|
||||
]
|
||||
|
||||
for ups_id in UPS_UNITS:
|
||||
base = f"{SITE_ID}/power/{ups_id}"
|
||||
rows += [
|
||||
(t, f"{base}/charge_pct", "ups_charge", SITE_ID, None, None, round(94.0 + random.gauss(0, 0.5), 1), "%"),
|
||||
(t, f"{base}/load_pct", "ups_load", SITE_ID, None, None, round(63.0 + random.gauss(0, 1.0), 1), "%"),
|
||||
(t, f"{base}/runtime_min", "ups_runtime", SITE_ID, None, None, round(57.0 + random.gauss(0, 1.0), 0), "min"),
|
||||
]
|
||||
|
||||
for leak_id in LEAK_SENSORS:
|
||||
rows.append((t, f"{SITE_ID}/leak/{leak_id}", "leak", SITE_ID, None, None, 0.0, ""))
|
||||
|
||||
await conn.executemany("""
|
||||
INSERT INTO readings
|
||||
(recorded_at, sensor_id, sensor_type, site_id, room_id, rack_id, value, unit)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
""", rows)
|
||||
|
||||
print(f"Seeder: inserted {len(rows)} readings. Done.")
|
||||
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed())
|
||||
Loading…
Add table
Add a link
Reference in a new issue