first commit
This commit is contained in:
commit
4b98219bf7
144 changed files with 31561 additions and 0 deletions
128
simulators/bots/generator.py
Normal file
128
simulators/bots/generator.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import random
|
||||
from bots.base import BaseBot
|
||||
|
||||
|
||||
class GeneratorBot(BaseBot):
|
||||
"""Diesel generator — normally on standby, runs on GENERATOR_FAILURE / ATS_TRANSFER scenarios."""
|
||||
|
||||
interval = 60
|
||||
|
||||
RATED_KW = 500.0
|
||||
TANK_LITRES = 2_000.0 # full tank
|
||||
BURN_RATE_LH = 90.0 # litres/hour at full load (~45 l/h at 50% load)
|
||||
|
||||
def __init__(self, site_id: str, gen_id: str) -> None:
|
||||
super().__init__()
|
||||
self.site_id = site_id
|
||||
self.gen_id = gen_id
|
||||
self._running = False
|
||||
self._testing = False
|
||||
self._fault = False
|
||||
self._fuel_l = self.TANK_LITRES * (0.75 + random.uniform(-0.05, 0.10))
|
||||
self._run_hours = 1_200 + random.randint(0, 400)
|
||||
self._load_kw = 0.0
|
||||
|
||||
def get_topic(self) -> str:
|
||||
return f"bms/{self.site_id}/generator/{self.gen_id}"
|
||||
|
||||
def set_scenario(self, name: str | None) -> None:
|
||||
super().set_scenario(name)
|
||||
self._running = name in ("GENERATOR_FAILURE", "ATS_TRANSFER")
|
||||
self._testing = (name == "GENERATOR_TEST")
|
||||
self._fault = (name == "GENERATOR_FAULT")
|
||||
if name is None:
|
||||
self._running = False
|
||||
self._testing = False
|
||||
self._fault = False
|
||||
|
||||
def get_payload(self) -> dict:
|
||||
hour_inc = self.interval / 3_600
|
||||
|
||||
if self._fault:
|
||||
return {
|
||||
"state": "fault",
|
||||
"fuel_pct": round(self._fuel_l / self.TANK_LITRES * 100, 1),
|
||||
"fuel_litres": round(self._fuel_l, 0),
|
||||
"fuel_rate_lph": 0.0,
|
||||
"load_kw": 0.0,
|
||||
"load_pct": 0.0,
|
||||
"run_hours": round(self._run_hours, 1),
|
||||
"voltage_v": 0.0,
|
||||
"frequency_hz": 0.0,
|
||||
"engine_rpm": 0.0,
|
||||
"oil_pressure_bar": 0.0,
|
||||
"coolant_temp_c": 0.0,
|
||||
"exhaust_temp_c": 28.0,
|
||||
"alternator_temp_c": 30.0,
|
||||
"power_factor": 0.0,
|
||||
"battery_v": 0.0,
|
||||
}
|
||||
|
||||
if self._running or self._testing:
|
||||
self._run_hours += hour_inc
|
||||
target_load = (
|
||||
self.RATED_KW * (0.55 + random.gauss(0, 0.02))
|
||||
if self._running
|
||||
else self.RATED_KW * (0.20 + random.gauss(0, 0.01))
|
||||
)
|
||||
self._load_kw = target_load
|
||||
load_frac = self._load_kw / self.RATED_KW
|
||||
# Burn fuel
|
||||
burn = self.BURN_RATE_LH * load_frac * hour_inc
|
||||
self._fuel_l = max(0.0, self._fuel_l - burn)
|
||||
|
||||
# Low fuel scenario
|
||||
if self._scenario == "GENERATOR_LOW_FUEL":
|
||||
self._fuel_l = max(0.0, self._fuel_l - 8.0)
|
||||
|
||||
state = "running" if self._running else "test"
|
||||
voltage = 415.0 + random.gauss(0, 1.5)
|
||||
frequency = 50.0 + random.gauss(0, 0.05)
|
||||
oil_p = 4.2 + random.gauss(0, 0.1)
|
||||
coolant = 78.0 + random.gauss(0, 2.0)
|
||||
battery_v = 24.3 + random.gauss(0, 0.1)
|
||||
# 1500 RPM at 50 Hz; slight droop under load
|
||||
engine_rpm = 1500.0 - (load_frac * 8.0) + random.gauss(0, 2.0)
|
||||
# Exhaust rises with load: ~180°C at idle, ~430°C at full load
|
||||
exhaust_temp = 180.0 + load_frac * 250.0 + random.gauss(0, 5.0)
|
||||
# Alternator winding temperature
|
||||
alt_temp = 45.0 + load_frac * 35.0 + random.gauss(0, 1.5)
|
||||
# Power factor typical for resistive/inductive DC loads
|
||||
pf = 0.87 + load_frac * 0.05 + random.gauss(0, 0.005)
|
||||
pf = round(min(0.99, max(0.80, pf)), 3)
|
||||
else:
|
||||
self._load_kw = 0.0
|
||||
load_frac = 0.0
|
||||
state = "standby"
|
||||
voltage = 0.0
|
||||
frequency = 0.0
|
||||
oil_p = 0.0
|
||||
coolant = 25.0 + random.gauss(0, 1.0)
|
||||
battery_v = 27.1 + random.gauss(0, 0.1) # trickle-charged standby battery
|
||||
engine_rpm = 0.0
|
||||
exhaust_temp = 28.0 + random.gauss(0, 1.0) # ambient exhaust stack temp
|
||||
alt_temp = 30.0 + random.gauss(0, 0.5)
|
||||
pf = 0.0
|
||||
|
||||
fuel_pct = min(100.0, self._fuel_l / self.TANK_LITRES * 100)
|
||||
# Actual fuel consumption rate for this interval
|
||||
fuel_rate = self.BURN_RATE_LH * load_frac if load_frac > 0 else 0.0
|
||||
|
||||
return {
|
||||
"state": state,
|
||||
"fuel_pct": round(fuel_pct, 1),
|
||||
"fuel_litres": round(self._fuel_l, 0),
|
||||
"fuel_rate_lph": round(fuel_rate, 1),
|
||||
"load_kw": round(self._load_kw, 1),
|
||||
"load_pct": round(load_frac * 100, 1),
|
||||
"run_hours": round(self._run_hours, 1),
|
||||
"voltage_v": round(voltage, 1),
|
||||
"frequency_hz": round(frequency, 2),
|
||||
"engine_rpm": round(engine_rpm, 0),
|
||||
"oil_pressure_bar": round(oil_p, 2),
|
||||
"coolant_temp_c": round(coolant, 1),
|
||||
"exhaust_temp_c": round(exhaust_temp, 1),
|
||||
"alternator_temp_c": round(alt_temp, 1),
|
||||
"power_factor": pf,
|
||||
"battery_v": round(battery_v, 2),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue