SHELLAPANDIANGANHUNGING's picture
Update app.py
de17a01 verified
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()