""" Predictive Equipment Maintenance Detect sensor anomalies and estimate maintenance urgency from time-series signals. """ from pathlib import Path import numpy as np import pandas as pd import plotly.express as px import streamlit as st st.set_page_config(page_title="Predictive Equipment Maintenance", page_icon="⚙️", layout="wide") def load_shared_css() -> None: current_dir = Path(__file__).resolve().parent candidates = [ current_dir / "shared" / "styles.css", current_dir.parent / "shared" / "styles.css", ] css_path = next(path for path in candidates if path.exists()) st.markdown(f"", unsafe_allow_html=True) load_shared_css() def make_sensor_data(hours: int, drift: float, vibration_spike: float, seed: int = 7) -> pd.DataFrame: rng = np.random.default_rng(seed) t = np.arange(hours) temperature = 68 + 0.035 * t * drift + 2.2 * np.sin(t / 13) + rng.normal(0, 0.7, hours) vibration = 1.8 + 0.009 * t * drift + 0.4 * np.sin(t / 7) + rng.normal(0, 0.12, hours) pressure = 38 - 0.015 * t * drift + rng.normal(0, 0.45, hours) spike_start = max(0, int(hours * 0.72)) vibration[spike_start:] += vibration_spike return pd.DataFrame({ "hour": t, "temperature_c": temperature, "vibration_mm_s": vibration, "pressure_bar": pressure, }) def add_anomaly_scores(df: pd.DataFrame) -> pd.DataFrame: scored = df.copy() for column in ["temperature_c", "vibration_mm_s", "pressure_bar"]: rolling_mean = scored[column].rolling(24, min_periods=6).mean() rolling_std = scored[column].rolling(24, min_periods=6).std().replace(0, np.nan) scored[f"{column}_z"] = ((scored[column] - rolling_mean) / rolling_std).fillna(0).abs() scored["anomaly_score"] = scored[["temperature_c_z", "vibration_mm_s_z", "pressure_bar_z"]].mean(axis=1) scored["risk_band"] = pd.cut( scored["anomaly_score"], bins=[-0.1, 1.2, 2.0, 99], labels=["Normal", "Watch", "Intervene"], ) return scored def maintenance_summary(scored: pd.DataFrame): latest = scored.iloc[-1] recent = scored.tail(24) high_risk_hours = int((recent["risk_band"] == "Intervene").sum()) if latest["anomaly_score"] >= 2.0 or high_risk_hours >= 4: action = "Schedule inspection within 24 hours." elif latest["anomaly_score"] >= 1.2: action = "Monitor closely and check lubrication, cooling, and mounting." else: action = "Continue normal operation and keep collecting telemetry." remaining_useful_life = max(12, int(220 - latest["anomaly_score"] * 55 - high_risk_hours * 6)) return latest, high_risk_hours, remaining_useful_life, action st.markdown("""
Explore anomaly scoring, remaining useful life estimation, and maintenance triage on sensor streams.