Spaces:
Sleeping
Sleeping
| 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(""" | |
| <div id="title-box"> | |
| <h1>Michelin Mining Tyre Simulation Pressure</h1> | |
| </div> | |
| """) | |
| with gr.Column(elem_classes="center-section"): | |
| # Info data terakhir | |
| gr.HTML(f""" | |
| <div class="info-card"> | |
| <p><strong>Data Terakhir:</strong> {last_time}</p> | |
| <p>Suhu: <strong>{last_temperature} Β°C</strong> | Tekanan: <strong>{last_pressure} psi</strong></p> | |
| </div> | |
| """) | |
| # 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('<div class="footer">Michelin Mining Tyre Analytics</div>') | |
| # ================= 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 = '<span class="danger">Risiko Red β Tekanan terlalu rendah</span>' | |
| elif ideal < amber_low: | |
| status = '<span class="caution">Risiko Amber β Tekanan mendekati batas bawah</span>' | |
| elif ideal > red_high: | |
| status = '<span class="danger">Risiko Red β Tekanan terlalu tinggi</span>' | |
| elif ideal > amber_high: | |
| status = '<span class="caution">Risiko Amber β Tekanan mendekati batas atas</span>' | |
| else: | |
| status = '<span class="safe">Dalam zona aman</span>' | |
| # Rekomendasi β fokus pada last_pressure & batas amber/red | |
| rec_html = f""" | |
| <h3>Hasil Simulasi</h3> | |
| <p>Ideal Tekanan pada {temp}Β°C: {ideal} psi</p> | |
| <p>Status: {status}</p> | |
| <hr> | |
| <h3>Rekomendasi Isi Ulang</h3> | |
| <p>Berdasarkan tekanan terakhir <strong>{last_pressure} psi</strong> ({last_time}):</p> | |
| <p class="safe">Isi tekanan antara <strong>{lower_safe} β {upper_safe} psi</strong></p> | |
| <p>Maksimal penambahan: <strong>+{min(10.0, max_add):.1f} psi</strong> (agar tidak melebihi {amber_high} psi)</p> | |
| <p>Maksimal pengurangan: <strong>β{min(10.0, max_sub):.1f} psi</strong> (agar tidak turun di bawah {amber_low} psi)</p> | |
| <p class="caution">Jangan isi kurang dari {amber_low} psi (batas Amber bawah)</p> | |
| <p class="caution">Jangan isi lebih dari {amber_high} psi (batas Amber atas)</p> | |
| <p class="danger">Hindari tekanan di bawah {red_low} psi atau di atas {red_high} psi (zona Red)</p> | |
| <p style="margin-top:16px;"><em>Catatan: Rekomendasi mempertahankan margin aman minimal 0.5 psi dari zona Amber.</em></p> | |
| """ | |
| return rec_html | |
| btn_submit.click(simulate, inputs=[tyre_type], outputs=output) | |
| demo.launch() |