import streamlit as st import pandas as pd import joblib import os import plotly.graph_objects as go from huggingface_hub import hf_hub_download from datetime import datetime # --- CONFIGURATION --- HF_USERNAME = os.getenv("HF_USERNAME", "iStillWaters") MODEL_REPO_NAME = os.getenv("MODEL_REPO_NAME", "auto_predictive_maintenance_model") MODEL_REPO_ID = f"{HF_USERNAME}/{MODEL_REPO_NAME}" MODEL_FILENAME = "best_engine_model.pkl" SCALER_FILENAME = "scaler.joblib" # --- PAGE CONFIG --- st.set_page_config( page_title="Engine Health Monitor", page_icon="🔧", layout="wide", initial_sidebar_state="collapsed" ) # --- LOAD MODEL --- @st.cache_resource def load_artifacts(): try: model_path = hf_hub_download(repo_id=MODEL_REPO_ID, filename=MODEL_FILENAME) scaler_path = hf_hub_download(repo_id=MODEL_REPO_ID, filename=SCALER_FILENAME) return joblib.load(model_path), joblib.load(scaler_path), None except Exception as e: return None, None, str(e) # --- INITIALIZE SESSION STATE --- if 'rpm' not in st.session_state: st.session_state.rpm = 750 if 'fuel_p' not in st.session_state: st.session_state.fuel_p = 6.2 if 'oil_p' not in st.session_state: st.session_state.oil_p = 3.16 if 'coolant_temp' not in st.session_state: st.session_state.coolant_temp = 80.0 if 'coolant_p' not in st.session_state: st.session_state.coolant_p = 2.16 if 'oil_temp' not in st.session_state: st.session_state.oil_temp = 80.0 # Callback functions to sync values def update_rpm_from_num(): st.session_state.rpm = st.session_state.rpm_num def update_rpm_from_sld(): st.session_state.rpm = st.session_state.rpm_sld def update_fuel_from_num(): st.session_state.fuel_p = st.session_state.fuel_num def update_fuel_from_sld(): st.session_state.fuel_p = st.session_state.fuel_sld def update_oil_p_from_num(): st.session_state.oil_p = st.session_state.oil_p_num def update_oil_p_from_sld(): st.session_state.oil_p = st.session_state.oil_p_sld def update_coolant_t_from_num(): st.session_state.coolant_temp = st.session_state.coolant_t_num def update_coolant_t_from_sld(): st.session_state.coolant_temp = st.session_state.coolant_t_sld def update_coolant_p_from_num(): st.session_state.coolant_p = st.session_state.coolant_p_num def update_coolant_p_from_sld(): st.session_state.coolant_p = st.session_state.coolant_p_sld def update_oil_t_from_num(): st.session_state.oil_temp = st.session_state.oil_t_num def update_oil_t_from_sld(): st.session_state.oil_temp = st.session_state.oil_t_sld # --- CUSTOM CSS --- st.markdown(""" """, unsafe_allow_html=True) # --- HELPER FUNCTIONS --- def create_gauge(value, max_value, color): fig = go.Figure(go.Indicator( mode="gauge+number", value=value, number={'font': {'size': 11, 'color': color}}, gauge={ 'axis': {'range': [0, max_value], 'tickwidth': 1}, 'bar': {'color': color, 'thickness': 0.5}, 'bgcolor': "rgba(26, 31, 58, 0.3)", 'borderwidth': 1, 'bordercolor': "rgba(255, 255, 255, 0.1)", 'steps': [ {'range': [0, max_value * 0.6], 'color': 'rgba(0, 255, 136, 0.1)'}, {'range': [max_value * 0.6, max_value * 0.8], 'color': 'rgba(255, 170, 0, 0.1)'}, {'range': [max_value * 0.8, max_value], 'color': 'rgba(255, 51, 102, 0.1)'} ] } )) fig.update_layout( height=70, margin=dict(l=5, r=5, t=5, b=5), paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', font={'family': 'Rajdhani'} ) return fig def validate_parameter(name, value, thresholds): if value > thresholds.get('critical_high', float('inf')): return 'critical', f"🔴 {name}: Critically high ({value})" elif value < thresholds.get('critical_low', float('-inf')): return 'critical', f"🔴 {name}: Critically low ({value})" elif value > thresholds.get('warning_high', float('inf')): return 'warning', f"⚠️ {name}: High ({value})" elif value < thresholds.get('warning_low', float('-inf')): return 'warning', f"⚠️ {name}: Low ({value})" return 'normal', None def get_status_info(probability): if probability < 0.25: return 'healthy', '🟢', 'HEALTHY', '#00ff88' elif probability < 0.50: return 'caution', '🟡', 'CAUTION', '#ffaa00' elif probability < 0.75: return 'warning', '🟠', 'WARNING', '#ff9500' else: return 'critical', '🔴', 'CRITICAL', '#ff3366' def calculate_feature_importance(params): return { 'Engine RPM': params['rpm'] / 2500, 'Coolant Temperature': params['coolant_temp'] / 200, 'Oil Pressure (Risk)': max(0, 1 - params['oil_pressure'] / 10), 'Fuel Pressure': params['fuel_pressure'] / 25 } def get_recommendations(params, probability, warnings, criticals): recs = [] if criticals: recs.append("🚨 IMMEDIATE ACTION: Critical parameters detected") recs.extend(criticals[:2]) if params['coolant_temp'] > 100: recs.append("🔧 Check cooling system - radiator and thermostat") if params['oil_pressure'] < 2.0: recs.append("🔧 Inspect oil pump and filter") if params['fuel_pressure'] < 5.0: recs.append("🔧 Examine fuel system components") if params['rpm'] > 2000: recs.append("⚙️ Reduce engine load immediately") if probability > 0.75: recs.append("📅 Emergency maintenance required") elif probability > 0.5: recs.append("📅 Schedule maintenance within 24 hours") elif probability > 0.25: recs.append("📋 Monitor closely") else: recs.append("✅ Continue normal operations") return recs[:6] if recs else ["✅ All systems normal"] # --- MAIN APP --- def main(): model, scaler, error = load_artifacts() if model is None: st.error(f"⚠️ Model Loading Error: {error}") st.stop() # Header st.markdown('

⚙️ Engine Health Monitor

', unsafe_allow_html=True) st.markdown('

AI-Powered Predictive Maintenance System

', unsafe_allow_html=True) # --- HORIZONTAL LAYOUT: LEFT (INPUTS) | RIGHT (ANALYSIS) --- left_col, right_col = st.columns([1, 1.5]) # ========== LEFT COLUMN: PARAMETER INPUTS (2x3 GRID) ========== with left_col: st.markdown('
📊 Engine Parameters
', unsafe_allow_html=True) # Parameter thresholds thresholds = { 'rpm': {'warning_high': 2000, 'critical_high': 2300}, 'fuel_pressure': {'warning_low': 5.0, 'critical_low': 4.0}, 'oil_pressure': {'warning_low': 2.0, 'critical_low': 1.5}, 'coolant_temp': {'warning_high': 100, 'critical_high': 120}, 'coolant_pressure': {'warning_low': 1.5, 'critical_low': 1.0}, 'oil_temp': {'warning_high': 110, 'critical_high': 130} } # ROW 1: RPM and FUEL row1_col1, row1_col2 = st.columns(2) with row1_col1: st.markdown('
', unsafe_allow_html=True) st.markdown('
Engine RPM
', unsafe_allow_html=True) input_col1, input_col2 = st.columns([1, 3]) with input_col1: st.number_input("", 0, 2500, value=st.session_state.rpm, step=50, key="rpm_num", label_visibility="collapsed", on_change=update_rpm_from_num) with input_col2: st.slider("", 0, 2500, value=st.session_state.rpm, step=50, key="rpm_sld", label_visibility="collapsed", on_change=update_rpm_from_sld) st.markdown(f'
{st.session_state.rpm}
', unsafe_allow_html=True) st.plotly_chart(create_gauge(st.session_state.rpm, 2500, "#00d4ff"), use_container_width=True, config={'displayModeBar': False}, key="g_rpm") st.markdown('
', unsafe_allow_html=True) with row1_col2: st.markdown('
', unsafe_allow_html=True) st.markdown('
Fuel Pressure (Bar)
', unsafe_allow_html=True) input_col1, input_col2 = st.columns([1, 3]) with input_col1: st.number_input("", 0.0, 25.0, value=st.session_state.fuel_p, step=0.1, key="fuel_num", label_visibility="collapsed", on_change=update_fuel_from_num) with input_col2: st.slider("", 0.0, 25.0, value=st.session_state.fuel_p, step=0.1, key="fuel_sld", label_visibility="collapsed", on_change=update_fuel_from_sld) st.markdown(f'
{st.session_state.fuel_p:.1f}
', unsafe_allow_html=True) st.plotly_chart(create_gauge(st.session_state.fuel_p, 25, "#ff6b35"), use_container_width=True, config={'displayModeBar': False}, key="g_fuel") st.markdown('
', unsafe_allow_html=True) # ROW 2: OIL PRESSURE and COOLANT TEMP row2_col1, row2_col2 = st.columns(2) with row2_col1: st.markdown('
', unsafe_allow_html=True) st.markdown('
🛢️Oil Pressure (Bar)
', unsafe_allow_html=True) input_col1, input_col2 = st.columns([1, 3]) with input_col1: st.number_input("", 0.0, 10.0, value=st.session_state.oil_p, step=0.1, key="oil_p_num", label_visibility="collapsed", on_change=update_oil_p_from_num) with input_col2: st.slider("", 0.0, 10.0, value=st.session_state.oil_p, step=0.1, key="oil_p_sld", label_visibility="collapsed", on_change=update_oil_p_from_sld) st.markdown(f'
{st.session_state.oil_p:.2f}
', unsafe_allow_html=True) st.plotly_chart(create_gauge(st.session_state.oil_p, 10, "#ffaa00"), use_container_width=True, config={'displayModeBar': False}, key="g_oil_p") st.markdown('
', unsafe_allow_html=True) with row2_col2: st.markdown('
', unsafe_allow_html=True) st.markdown('
🌡️Coolant Temp (°C)
', unsafe_allow_html=True) input_col1, input_col2 = st.columns([1, 3]) with input_col1: st.number_input("", 0.0, 200.0, value=st.session_state.coolant_temp, step=1.0, key="coolant_t_num", label_visibility="collapsed", on_change=update_coolant_t_from_num) with input_col2: st.slider("", 0.0, 200.0, value=st.session_state.coolant_temp, step=1.0, key="coolant_t_sld", label_visibility="collapsed", on_change=update_coolant_t_from_sld) st.markdown(f'
{st.session_state.coolant_temp:.1f}
', unsafe_allow_html=True) st.plotly_chart(create_gauge(st.session_state.coolant_temp, 200, "#ff3366"), use_container_width=True, config={'displayModeBar': False}, key="g_coolant_t") st.markdown('
', unsafe_allow_html=True) # ROW 3: COOLANT PRESSURE and OIL TEMP row3_col1, row3_col2 = st.columns(2) with row3_col1: st.markdown('
', unsafe_allow_html=True) st.markdown('
💧Coolant Pressure (Bar)
', unsafe_allow_html=True) input_col1, input_col2 = st.columns([1, 3]) with input_col1: st.number_input("", 0.0, 10.0, value=st.session_state.coolant_p, step=0.1, key="coolant_p_num", label_visibility="collapsed", on_change=update_coolant_p_from_num) with input_col2: st.slider("", 0.0, 10.0, value=st.session_state.coolant_p, step=0.1, key="coolant_p_sld", label_visibility="collapsed", on_change=update_coolant_p_from_sld) st.markdown(f'
{st.session_state.coolant_p:.2f}
', unsafe_allow_html=True) st.plotly_chart(create_gauge(st.session_state.coolant_p, 10, "#00ff88"), use_container_width=True, config={'displayModeBar': False}, key="g_coolant_p") st.markdown('
', unsafe_allow_html=True) with row3_col2: st.markdown('
', unsafe_allow_html=True) st.markdown('
🔥Oil Temp (°C)
', unsafe_allow_html=True) input_col1, input_col2 = st.columns([1, 3]) with input_col1: st.number_input("", 0.0, 150.0, value=st.session_state.oil_temp, step=1.0, key="oil_t_num", label_visibility="collapsed", on_change=update_oil_t_from_num) with input_col2: st.slider("", 0.0, 150.0, value=st.session_state.oil_temp, step=1.0, key="oil_t_sld", label_visibility="collapsed", on_change=update_oil_t_from_sld) st.markdown(f'
{st.session_state.oil_temp:.1f}
', unsafe_allow_html=True) st.plotly_chart(create_gauge(st.session_state.oil_temp, 150, "#a855f7"), use_container_width=True, config={'displayModeBar': False}, key="g_oil_t") st.markdown('
', unsafe_allow_html=True) # ========== RIGHT COLUMN: ANALYSIS ========== with right_col: st.markdown('
🔍 Analysis & Results
', unsafe_allow_html=True) # Real-time validation warnings = [] criticals = [] for param_name, value, threshold_key in [ ('RPM', st.session_state.rpm, 'rpm'), ('Fuel Pressure', st.session_state.fuel_p, 'fuel_pressure'), ('Oil Pressure', st.session_state.oil_p, 'oil_pressure'), ('Coolant Temp', st.session_state.coolant_temp, 'coolant_temp'), ('Coolant Pressure', st.session_state.coolant_p, 'coolant_pressure'), ('Oil Temp', st.session_state.oil_temp, 'oil_temp') ]: status, msg = validate_parameter(param_name, value, thresholds.get(threshold_key, {})) if status == 'critical' and msg: criticals.append(msg) elif status == 'warning' and msg: warnings.append(msg) # Display alerts if criticals or warnings: if criticals: for alert in criticals: st.markdown(f'
{alert}
', unsafe_allow_html=True) if warnings: for alert in warnings: st.markdown(f'
{alert}
', unsafe_allow_html=True) # Analyze button if st.button("🔍 ANALYZE ENGINE STATUS", type="primary", use_container_width=True): input_df = pd.DataFrame({ 'Engine rpm': [st.session_state.rpm], 'Lub oil pressure': [st.session_state.oil_p], 'Fuel pressure': [st.session_state.fuel_p], 'Coolant pressure': [st.session_state.coolant_p], 'lub oil temp': [st.session_state.oil_temp], 'Coolant temp': [st.session_state.coolant_temp] }) try: scaled = scaler.transform(input_df) pred = model.predict(scaled)[0] prob = model.predict_proba(scaled)[0][1] if hasattr(model, 'predict_proba') else 0.0 status, emoji, status_text, color = get_status_info(prob) # Engine Status st.markdown(f"""
{emoji}
{prob*100:.1f}% Failure Risk
{status_text}
""", unsafe_allow_html=True) # Feature Importance st.markdown('
⚡ Risk Factors
', unsafe_allow_html=True) params_dict = { 'rpm': st.session_state.rpm, 'coolant_temp': st.session_state.coolant_temp, 'oil_pressure': st.session_state.oil_p, 'fuel_pressure': st.session_state.fuel_p } importance = calculate_feature_importance(params_dict) sorted_features = sorted(importance.items(), key=lambda x: x[1], reverse=True) for feature, score in sorted_features: if score > 0.3: bar_color = '#ff3366' if score > 0.7 else '#ffaa00' if score > 0.5 else '#00d4ff' st.markdown(f"""
{feature}: {score*100:.1f}%
""", unsafe_allow_html=True) # Recommendations st.markdown('
📋 Recommendations
', unsafe_allow_html=True) recommendations = get_recommendations(params_dict, prob, warnings, criticals) for rec in recommendations: st.markdown(f'
{rec}
', unsafe_allow_html=True) # Analysis Details st.markdown('
📊 Details
', unsafe_allow_html=True) st.markdown(f"""
Prediction Summary
Probability: {prob*100:.2f}% | Classification: {'FAILURE RISK' if pred == 1 else 'OPERATIONAL'} | Status: {status_text}
Time: {datetime.now().strftime('%H:%M:%S')}
""", unsafe_allow_html=True) st.markdown(f"""
Parameter Check
RPM: {st.session_state.rpm} {'⚠️' if st.session_state.rpm > 2000 else '✓'} | Coolant: {st.session_state.coolant_temp:.1f}°C {'⚠️' if st.session_state.coolant_temp > 100 else '✓'}
Oil P: {st.session_state.oil_p:.2f} Bar {'⚠️' if st.session_state.oil_p < 2.0 else '✓'} | Fuel P: {st.session_state.fuel_p:.1f} Bar {'⚠️' if st.session_state.fuel_p < 5.0 else '✓'}
""", unsafe_allow_html=True) except Exception as e: st.error(f"Prediction Error: {str(e)}") else: st.markdown("""
⚙️
Awaiting Analysis

Configure parameters and click "ANALYZE"

""", unsafe_allow_html=True) if __name__ == "__main__": main()