|
|
|
@ -2,6 +2,7 @@ from typing import List, Optional, Tuple
|
|
|
|
from uuid import UUID
|
|
|
|
from uuid import UUID
|
|
|
|
import numpy as np
|
|
|
|
import numpy as np
|
|
|
|
from sqlalchemy import and_, func, select
|
|
|
|
from sqlalchemy import and_, func, select
|
|
|
|
|
|
|
|
from sqlalchemy.engine import result
|
|
|
|
from sqlalchemy.orm import joinedload
|
|
|
|
from sqlalchemy.orm import joinedload
|
|
|
|
from src.database.core import DbSession
|
|
|
|
from src.database.core import DbSession
|
|
|
|
from src.overhaul_activity.service import get_all_by_session_id
|
|
|
|
from src.overhaul_activity.service import get_all_by_session_id
|
|
|
|
@ -12,15 +13,34 @@ from .model import CalculationParam, OverhaulReferenceType, CalculationData, Cal
|
|
|
|
from fastapi import HTTPException, status
|
|
|
|
from fastapi import HTTPException, status
|
|
|
|
from src.overhaul_scope.service import get_by_scope_name, get
|
|
|
|
from src.overhaul_scope.service import get_by_scope_name, get
|
|
|
|
from src.scope_equipment.service import get_by_assetnum
|
|
|
|
from src.scope_equipment.service import get_by_assetnum
|
|
|
|
|
|
|
|
from src.overhaul_scope.service import get as get_scope
|
|
|
|
|
|
|
|
from .schema import CalculationResultsRead
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_overhaul_cost_by_time_chart(overhaul_cost: float, days: int) -> list:
|
|
|
|
def get_overhaul_cost_by_time_chart(overhaul_cost: float, days: int) -> np.ndarray:
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
Calculate decreasing overhaul costs over time using exponential decay.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
|
|
overhaul_cost (float): Initial overhaul cost
|
|
|
|
|
|
|
|
days (int): Number of days to calculate cost for
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
np.ndarray: Array of daily costs with exponential decay
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
|
|
ValueError: If overhaul_cost is negative or days is not positive
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
if overhaul_cost < 0:
|
|
|
|
|
|
|
|
raise ValueError("Overhaul cost cannot be negative")
|
|
|
|
|
|
|
|
if days <= 0:
|
|
|
|
|
|
|
|
raise ValueError("Days must be positive")
|
|
|
|
|
|
|
|
|
|
|
|
exponents = np.arange(0, days)
|
|
|
|
exponents = np.arange(0, days)
|
|
|
|
results = overhaul_cost / (2 ** exponents)
|
|
|
|
results = overhaul_cost / (2 ** exponents)
|
|
|
|
results = np.where(np.isfinite(results), results, 0)
|
|
|
|
results = np.where(np.isfinite(results), results, 0)
|
|
|
|
return results
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_corrective_cost_time_chart(material_cost: float, service_cost: float, days: int) -> Tuple[np.ndarray, np.ndarray]:
|
|
|
|
def get_corrective_cost_time_chart(material_cost: float, service_cost: float, days: int) -> Tuple[np.ndarray, np.ndarray]:
|
|
|
|
day_points = np.arange(0, days)
|
|
|
|
day_points = np.arange(0, days)
|
|
|
|
|
|
|
|
|
|
|
|
@ -63,28 +83,62 @@ async def create_param_and_data(*, db_session: DbSession, calculation_param_in:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_calculation_result(db_session: DbSession, calculation_id: str):
|
|
|
|
async def get_calculation_result(db_session: DbSession, calculation_id: str):
|
|
|
|
|
|
|
|
days=365
|
|
|
|
scope_calculation = await get_calculation_data_by_id(db_session=db_session, calculation_id=calculation_id)
|
|
|
|
scope_calculation = await get_calculation_data_by_id(db_session=db_session, calculation_id=calculation_id)
|
|
|
|
if not scope_calculation:
|
|
|
|
if not scope_calculation:
|
|
|
|
raise HTTPException(
|
|
|
|
raise HTTPException(
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
detail="A data with this id does not exist.",
|
|
|
|
detail="A data with this id does not exist.",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scope_overhaul = await get_scope(db_session=db_session, overhaul_session_id=scope_calculation.overhaul_session_id)
|
|
|
|
|
|
|
|
if not scope_overhaul:
|
|
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
|
|
|
detail="A data with this id does not exist.",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
calculation_results = []
|
|
|
|
|
|
|
|
for i in range(days):
|
|
|
|
|
|
|
|
result = {
|
|
|
|
|
|
|
|
"overhaul_cost": 0,
|
|
|
|
|
|
|
|
"corrective_cost": 0,
|
|
|
|
|
|
|
|
"num_failures": 0,
|
|
|
|
|
|
|
|
"day": i + 1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for eq in scope_calculation.equipment_results:
|
|
|
|
|
|
|
|
if not eq.is_included:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
result["corrective_cost"] += float(eq.corrective_costs[i])
|
|
|
|
|
|
|
|
result["overhaul_cost"] += float(eq.overhaul_costs[i])
|
|
|
|
|
|
|
|
result["num_failures"] += int(eq.daily_failures[i])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
calculation_results.append(CalculationResultsRead(**result))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Check if calculation already exist
|
|
|
|
# Check if calculation already exist
|
|
|
|
return CalculationTimeConstrainsRead(
|
|
|
|
return CalculationTimeConstrainsRead(
|
|
|
|
id=scope_calculation.id,
|
|
|
|
id=scope_calculation.id,
|
|
|
|
reference=scope_calculation.overhaul_session_id,
|
|
|
|
reference=scope_calculation.overhaul_session_id,
|
|
|
|
results=scope_calculation.results,
|
|
|
|
scope=scope_overhaul.type,
|
|
|
|
|
|
|
|
results=calculation_results,
|
|
|
|
optimum_oh=scope_calculation.optimum_oh_day,
|
|
|
|
optimum_oh=scope_calculation.optimum_oh_day,
|
|
|
|
equipment_results=scope_calculation.equipment_results
|
|
|
|
equipment_results=scope_calculation.equipment_results
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_calculation_data_by_id(db_session: DbSession, calculation_id) -> CalculationData:
|
|
|
|
async def get_calculation_data_by_id(db_session: DbSession, calculation_id) -> CalculationData:
|
|
|
|
stmt = select(CalculationData).filter(CalculationData.id ==
|
|
|
|
stmt = select(CalculationData).filter(
|
|
|
|
calculation_id).options(joinedload(CalculationData.results), joinedload(CalculationData.equipment_results))
|
|
|
|
CalculationData.id == calculation_id
|
|
|
|
|
|
|
|
).options(
|
|
|
|
|
|
|
|
joinedload(CalculationData.equipment_results), joinedload(CalculationData.parameter)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = await db_session.execute(stmt)
|
|
|
|
result = await db_session.execute(stmt)
|
|
|
|
|
|
|
|
|
|
|
|
return result.unique().scalar()
|
|
|
|
return result.unique().scalar()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -149,19 +203,21 @@ async def get_calculation_data_by_id(db_session: DbSession, calculation_id) -> C
|
|
|
|
|
|
|
|
|
|
|
|
async def create_calculation_result_service(
|
|
|
|
async def create_calculation_result_service(
|
|
|
|
db_session: DbSession,
|
|
|
|
db_session: DbSession,
|
|
|
|
calculation_id: UUID,
|
|
|
|
calculation: CalculationData,
|
|
|
|
) -> CalculationTimeConstrainsRead:
|
|
|
|
) -> CalculationTimeConstrainsRead:
|
|
|
|
days = 365 # Changed to 365 days as per requirement
|
|
|
|
days = 365 # Changed to 365 days as per requirement
|
|
|
|
calculation = await get_calculation_data_by_id(db_session=db_session, calculation_id=calculation_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Get all equipment for this calculation session
|
|
|
|
# Get all equipment for this calculation session
|
|
|
|
equipments = await get_all_by_session_id(db_session=db_session, overhaul_session_id=calculation.overhaul_session_id)
|
|
|
|
equipments = await get_all_by_session_id(db_session=db_session, overhaul_session_id=calculation.overhaul_session_id)
|
|
|
|
|
|
|
|
scope = await get_scope(db_session=db_session, overhaul_session_id=calculation.overhaul_session_id)
|
|
|
|
|
|
|
|
|
|
|
|
# Calculate overhaul costs once since it's shared
|
|
|
|
calculation_data = await get_calculation_data_by_id(db_session=db_session, calculation_id=calculation.id)
|
|
|
|
|
|
|
|
|
|
|
|
overhaul_cost_points = get_overhaul_cost_by_time_chart(
|
|
|
|
overhaul_cost_points = get_overhaul_cost_by_time_chart(
|
|
|
|
calculation.parameter.overhaul_cost,
|
|
|
|
calculation_data.parameter.overhaul_cost,
|
|
|
|
days=days
|
|
|
|
days=days
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Store results for each equipment
|
|
|
|
# Store results for each equipment
|
|
|
|
equipment_results: List[CalculationEquipmentResult] = []
|
|
|
|
equipment_results: List[CalculationEquipmentResult] = []
|
|
|
|
@ -181,13 +237,6 @@ async def create_calculation_result_service(
|
|
|
|
equipment_optimum_index = np.argmin(equipment_total_cost)
|
|
|
|
equipment_optimum_index = np.argmin(equipment_total_cost)
|
|
|
|
equipment_failure_sum = sum(daily_failures[:equipment_optimum_index])
|
|
|
|
equipment_failure_sum = sum(daily_failures[:equipment_optimum_index])
|
|
|
|
|
|
|
|
|
|
|
|
equipment_optimum = OptimumResult(
|
|
|
|
|
|
|
|
overhaul_cost=float(overhaul_cost_points[equipment_optimum_index]),
|
|
|
|
|
|
|
|
corrective_cost=float(corrective_costs[equipment_optimum_index]),
|
|
|
|
|
|
|
|
num_failures=int(equipment_failure_sum),
|
|
|
|
|
|
|
|
days=int(equipment_optimum_index + 1)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
equipment_results.append(CalculationEquipmentResult(
|
|
|
|
equipment_results.append(CalculationEquipmentResult(
|
|
|
|
corrective_costs=corrective_costs.tolist(),
|
|
|
|
corrective_costs=corrective_costs.tolist(),
|
|
|
|
overhaul_costs=overhaul_cost_points.tolist(),
|
|
|
|
overhaul_costs=overhaul_cost_points.tolist(),
|
|
|
|
@ -204,6 +253,7 @@ async def create_calculation_result_service(
|
|
|
|
total_daily_failures += daily_failures
|
|
|
|
total_daily_failures += daily_failures
|
|
|
|
|
|
|
|
|
|
|
|
db_session.add_all(equipment_results)
|
|
|
|
db_session.add_all(equipment_results)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Calculate optimum points using total costs
|
|
|
|
# Calculate optimum points using total costs
|
|
|
|
total_cost = total_corrective_costs + overhaul_cost_points
|
|
|
|
total_cost = total_corrective_costs + overhaul_cost_points
|
|
|
|
@ -217,31 +267,30 @@ async def create_calculation_result_service(
|
|
|
|
days=int(optimum_oh_index + 1)
|
|
|
|
days=int(optimum_oh_index + 1)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Create calculation results for database
|
|
|
|
# # Create calculation results for database
|
|
|
|
calculation_results = []
|
|
|
|
# calculation_results = []
|
|
|
|
for i in range(days):
|
|
|
|
# for i in range(days):
|
|
|
|
result = CalculationResult(
|
|
|
|
# result = CalculationResult(
|
|
|
|
parameter_id=calculation.parameter_id,
|
|
|
|
# parameter_id=calculation.parameter_id,
|
|
|
|
calculation_data_id=calculation.id,
|
|
|
|
# calculation_data_id=calculation.id,
|
|
|
|
day=(i + 1),
|
|
|
|
# day=(i + 1),
|
|
|
|
corrective_cost=float(total_corrective_costs[i]),
|
|
|
|
# corrective_cost=float(total_corrective_costs[i]),
|
|
|
|
overhaul_cost=float(overhaul_cost_points[i]),
|
|
|
|
# overhaul_cost=float(overhaul_cost_points[i]),
|
|
|
|
num_failures=int(total_daily_failures[i]),
|
|
|
|
# num_failures=int(total_daily_failures[i]),
|
|
|
|
)
|
|
|
|
# )
|
|
|
|
calculation_results.append(result)
|
|
|
|
# calculation_results.append(result)
|
|
|
|
|
|
|
|
|
|
|
|
# Update calculation with optimum day
|
|
|
|
# Update calculation with optimum day
|
|
|
|
calculation.optimum_oh_day = optimum.days
|
|
|
|
calculation.optimum_oh_day = optimum.days
|
|
|
|
|
|
|
|
|
|
|
|
# Save to database
|
|
|
|
|
|
|
|
db_session.add_all(calculation_results)
|
|
|
|
|
|
|
|
await db_session.commit()
|
|
|
|
await db_session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
# Return results including individual equipment data
|
|
|
|
# Return results including individual equipment data
|
|
|
|
return CalculationTimeConstrainsRead(
|
|
|
|
return CalculationTimeConstrainsRead(
|
|
|
|
id=calculation.id,
|
|
|
|
id=calculation.id,
|
|
|
|
reference=calculation.overhaul_session_id,
|
|
|
|
reference=calculation.overhaul_session_id,
|
|
|
|
results=calculation_results,
|
|
|
|
scope=scope.type,
|
|
|
|
|
|
|
|
results=[],
|
|
|
|
optimum_oh=optimum,
|
|
|
|
optimum_oh=optimum,
|
|
|
|
equipment_results=equipment_results
|
|
|
|
equipment_results=equipment_results
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|