import gradio as gr import pandas as pd import numpy as np # ================= LOAD DATA — AMBIL TEKANAN, SUHU & WAKTU TERAKHIR ================= last_pressure = 85.0 last_temperature = 54.0 last_time = "–" try: df = pd.read_excel("df_final.xlsx", sheet_name="Sheet1") df.columns = df.columns.str.replace("Â", "", regex=False) for col in df.select_dtypes(include='object').columns: df[col] = df[col].astype(str).str.replace("Â", "", regex=False) df['Time'] = pd.to_datetime(df['Time'], errors='coerce') df = df.dropna(subset=['Time']) if not df.empty: last_row = df.sort_values('Time', ascending=False).iloc[0] last_pressure = float(last_row['Pressure']) if pd.notna(last_row.get('Pressure')) else 85.0 last_temperature = float(last_row['Temperature']) if pd.notna(last_row.get('Temperature')) else 54.0 last_time = last_row['Time'].strftime("%Y-%m-%d %H:%M") except Exception as e: pass # tetap pakai default jika gagal # ================= BATAS AMAN (SESUAI STANDAR OPERASIONAL) ================= red_low = 70.0 amber_low = 75.0 amber_high = 95.0 red_high = 100.0 def get_safe_fill_range(last_p): lower = max(last_p - 10, amber_low + 0.5) upper = min(last_p + 10, amber_high - 0.5) return round(lower, 1), round(upper, 1) # ================= CUSTOM CSS — Tengah, Wide, Bersih ================= custom_css = """ body, .gradio-container { background: #000000 !important; color: #FFFFFF !important; font-family: 'Segoe UI', system-ui, sans-serif; } .gradio-container { max-width: 100% !important; padding: 20px !important; } #title-box { background: #003A8F; color: white; padding: 18px 32px; border-radius: 12px; margin: 20px auto 30px auto; text-align: center; width: fit-content; min-width: 500px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); } #title-box h1 { margin: 0; font-size: 26px; font-weight: 700; } .center-section { display: flex; flex-direction: column; align-items: center; width: 100%; max-width: 700px; margin: 0 auto; gap: 24px; } .info-card { background: #121212; border: 1px solid #333; border-radius: 12px; padding: 20px; width: 100%; text-align: center; font-size: 18px; } .gr-button { background: #003A8F !important; color: white !important; font-weight: bold; font-size: 18px !important; padding: 12px 36px !important; border-radius: 8px !important; border: none !important; margin-top: 8px; } .gr-button:hover { background: #002D6B !important; transform: translateY(-1px); box-shadow: 0 4px 10px rgba(0, 58, 143, 0.4); } label { color: #CCCCCC !important; font-size: 18px !important; font-weight: 500; } .output-area { background: #121212; border: 1px solid #333; border-radius: 12px; padding: 26px; width: 100%; font-size: 18px; line-height: 1.6; margin-top: 10px; } .output-area h3 { color: #FFD100; margin: 18px 0 12px 0; font-size: 22px; font-weight: 600; } .output-area p { margin: 10px 0; } .output-area .safe { color: #00FF00; font-weight: bold; } .output-area .caution { color: #FFA500; font-weight: bold; } .output-area .danger { color: #FF0000; font-weight: bold; } .footer { margin-top: 24px; color: #888; font-size: 15px; text-align: center; width: 100%; } """ # ================= GRADIO — MODE WIDE, UI TENGAH ================= with gr.Blocks(title="Michelin Mining Tyre Analytics — Objective 6", css=custom_css, mode="wide") as demo: gr.HTML("""

Michelin Mining Tyre Simulation Pressure

""") with gr.Column(elem_classes="center-section"): # Info data terakhir gr.HTML(f"""

Data Terakhir: {last_time}

Suhu: {last_temperature} °C | Tekanan: {last_pressure} psi

""") # Input: hanya jenis ban tyre_type = gr.Radio( choices=["10 (Depan)", "11 (Belakang)"], label="Pilih Jenis Ban", value="10 (Depan)", interactive=True ) btn_submit = gr.Button("Submit", elem_classes="gr-button") output = gr.HTML(elem_classes="output-area") # Footer gr.HTML('') # ================= LOGIKA SIMULASI — TANPA INPUT SUHU (Pakai last_temperature) ================= def simulate(tyre_str): # Gunakan suhu terakhir dari data — tidak ada input manual temp = last_temperature # Interpolasi ideal pressure ideal = 85.0 + 0.42 * (temp - 54.0) ideal = round(ideal, 1) lower_safe, upper_safe = get_safe_fill_range(last_pressure) max_add = round(amber_high - last_pressure, 1) max_sub = round(last_pressure - amber_low, 1) # Status if ideal < red_low: status = 'Risiko Red — Tekanan terlalu rendah' elif ideal < amber_low: status = 'Risiko Amber — Tekanan mendekati batas bawah' elif ideal > red_high: status = 'Risiko Red — Tekanan terlalu tinggi' elif ideal > amber_high: status = 'Risiko Amber — Tekanan mendekati batas atas' else: status = 'Dalam zona aman' # Rekomendasi — fokus pada last_pressure & batas amber/red rec_html = f"""

Hasil Simulasi

Ideal Tekanan pada {temp}°C: {ideal} psi

Status: {status}


Rekomendasi Isi Ulang

Berdasarkan tekanan terakhir {last_pressure} psi ({last_time}):

Isi tekanan antara {lower_safe} – {upper_safe} psi

Maksimal penambahan: +{min(10.0, max_add):.1f} psi (agar tidak melebihi {amber_high} psi)

Maksimal pengurangan: −{min(10.0, max_sub):.1f} psi (agar tidak turun di bawah {amber_low} psi)

Jangan isi kurang dari {amber_low} psi (batas Amber bawah)

Jangan isi lebih dari {amber_high} psi (batas Amber atas)

Hindari tekanan di bawah {red_low} psi atau di atas {red_high} psi (zona Red)

Catatan: Rekomendasi mempertahankan margin aman minimal 0.5 psi dari zona Amber.

""" return rec_html btn_submit.click(simulate, inputs=[tyre_type], outputs=output) demo.launch()