feat: add EAF forecasting endpoint and initialize Last Overhaul Date in master data service

main
Cizz22 3 months ago
parent 59a2001a21
commit 074fdf1276

@ -496,6 +496,96 @@ async def get_simulation_report(db_session: DbSession, simulation_id: str):
)
@router.get("/forecast/eaf", response_model=StandardResponse[dict])
async def get_forecast_eaf(
db_session: DbSession,
time_range: str = Query(..., alias="range", regex="^(weekly|monthly)$"),
):
"""
Generate a forecasted EAF value for the next week or month.
Calculates offset from 'Last Overhaul Date' in master data.
"""
from src.dashboard_model.service import _get_or_create_master_data
from .schema import SimulationInput
# 1. Get current time
now = datetime.now()
# 2. Determine Last Overhaul Date from Master Data
oh_record = await _get_or_create_master_data(db_session, "Last Overhaul Date", 0)
# If not set, use a fallback (e.g., start of current year)
if not oh_record.value_string:
oh_date_str = f"{now.year}-01-01T00:00:00"
else:
oh_date_str = oh_record.value_string
try:
oh_date = datetime.fromisoformat(oh_date_str)
except Exception:
oh_date = datetime(now.year, 1, 1)
# 3. Calculate Age in Hours (Offset)
# Using the helper from utils.py
offset_hours = hours_between(oh_date, now)
# 4. Define Forecast Window
duration = 168 if time_range == "weekly" else 720
# 5. Create Simulation Request
sim_in = SimulationInput(
SchematicName="- TJB - Unit 3 -",
SimulationName=f"Simulation{time_range}_{now.strftime('%Y%m%d')}",
SimDuration=duration,
DurationUnit="UHour",
OffSet=offset_hours,
SimSeed=1,
SimNumRun=1,
IsDefault=False
)
# 6. Run simulation (blocking call to engine)
try:
raw_result = await execute_simulation(
db_session=db_session,
sim_data=sim_in.model_dump(),
is_saved=False
)
# 7. Extract Root Node Results
node_results = raw_result.get("nodeResultOuts", [])
plant_result = next((r for r in node_results if r.get("nodeName") == "- TJB - Unit 3 -"), None)
if not plant_result:
# Fallback to any schematic result if name doesn't match perfectly
plant_result = next((r for r in node_results if r.get("nodeType") == "Schematic"), None)
if not plant_result:
raise HTTPException(status_code=500, detail="Plant node results not found in simulation output")
# 8. Return predicted metrics
eaf = plant_result.get("eaf")
total_dt = plant_result.get("totalDowntime", 0)
total_ut = plant_result.get("totalUpTime", 0)
efor = (total_dt / (total_dt + total_ut)) * 100 if (total_dt + total_ut) > 0 else 0
return StandardResponse(
data={
"range": time_range,
"eaf_predicted": float(eaf) if eaf is not None else 0.0,
"efor_predicted": float(efor),
"reliability_age_hours": offset_hours,
"sim_duration_hours": duration,
"reference_oh_date": oh_date_str
},
message="Forecast generated successfully"
)
except Exception as e:
import logging
logging.getLogger(__name__).error(f"Forecast failed: {str(e)}")
raise HTTPException(status_code=500, detail=f"Simulation failed: {str(e)}")
@router.get("/custom_parameters", response_model=StandardResponse[list])

@ -36,6 +36,7 @@ async def _get_or_create_master_data(db_session: DbSession, name: str, default_f
async def get_master_data_list(db_session: DbSession):
# Ensure Current EAF exists
await _get_or_create_master_data(db_session, "Current EAF Realization", 85.62)
await _get_or_create_master_data(db_session, "Last Overhaul Date", 0)
query = select(RBDMasterData)
result = await db_session.execute(query)
return result.scalars().all()

Loading…
Cancel
Save