SHELLAPANDIANGANHUNGING commited on
Commit
aef3f26
·
verified ·
1 Parent(s): 0e0a26a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +174 -138
app.py CHANGED
@@ -2,111 +2,112 @@ import gradio as gr
2
  import pandas as pd
3
  import numpy as np
4
 
5
- # ================= LOAD DATA =================
 
 
 
6
  try:
7
  df = pd.read_excel("df_final.xlsx", sheet_name="Sheet1")
8
- # Fix encoding
9
- df.columns = df.columns.str.replace("Â", "")
10
  for col in df.select_dtypes(include='object').columns:
11
- df[col] = df[col].astype(str).str.replace("Â", "")
12
- # Parse datetime
13
  df['Time'] = pd.to_datetime(df['Time'], errors='coerce')
14
  df = df.dropna(subset=['Time'])
15
- df['hour'] = df['Time'].dt.hour
16
- # Alarm flag
17
- df['is_alarm'] = (~df['Alarm Status'].str.contains('No Alarm', na=False)).astype(int)
18
- except FileNotFoundError:
19
- df = pd.DataFrame() # Empty if file not found
20
- print("⚠️ File df_final.xlsx not found. Running with dummy data.")
21
-
22
- # ================= SIMULASI PRESSURE BERDASARKAN GRADIEN =================
23
- pressure_formulas = {
24
- (10, 'High', 'Min'): (76.658370666499, 0.424510125965938),
25
- (10, 'High', 'Max'): (81.0954797166173, 0.445533183),
26
- (10, 'Low', 'Min'): (68.02273668, 0.375071782),
27
- (10, 'Low', 'Max'): (63.6496681312519, 0.351500420678697),
28
- (11, 'High', 'Min'): (102.275273585809, 0.496334109689563),
29
- (11, 'High', 'Max'): (108.013017260632, 0.525284752576333),
30
- (11, 'Low', 'Min'): (90.4977325517091, 0.446382251463767),
31
- (11, 'Low', 'Max'): (84.9683663311582, 0.410110214756436),
32
- }
33
-
34
- def calculate_pressure(tyre_type, temp, condition, min_max):
35
- if (tyre_type, condition, min_max) in pressure_formulas:
36
- intercept, gradient = pressure_formulas[(tyre_type, condition, min_max)]
37
- return intercept + gradient * temp
38
- return None
39
-
40
- # ================= CUSTOM CSS (Dark Theme Premium) =================
41
  custom_css = """
42
- body {
43
- background: #000000;
44
- color: #ffffff;
45
  font-family: 'Segoe UI', system-ui, sans-serif;
 
 
46
  }
47
  .gradio-container {
48
- background: #000000 !important;
49
- padding: 20px;
50
- }
51
- h1, h2, h3, h4, h5, h6 {
52
- color: #ffffff !important;
53
- }
54
- label {
55
- color: #cccccc !important;
56
- }
57
- input, select, button {
58
- background: #1a1a1a !important;
59
- color: #ffffff !important;
60
- border: 1px solid #333333 !important;
61
- border-radius: 8px !important;
62
- }
63
- button {
64
- background: #003A8F !important;
65
- color: white !important;
66
- font-weight: bold;
67
- padding: 10px 20px;
68
- transition: all 0.2s ease;
69
- }
70
- button:hover {
71
- background: #002d6b !important;
72
- transform: translateY(-1px);
73
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
74
- }
75
- .gradio-box {
76
- background: #121212 !important;
77
- border: 1px solid #333333 !important;
78
- border-radius: 12px !important;
79
- padding: 20px !important;
80
- margin: 10px 0 !important;
81
- }
82
- .gradio-markdown {
83
- color: #ffffff !important;
84
- }
85
- .gradio-slider .track {
86
- background: #FFD100 !important;
87
- }
88
- .gradio-slider .thumb {
89
- background: #FFD100 !important;
90
- border: 2px solid #000000 !important;
91
- }
92
- .output-area {
93
- background: #1e1e1e !important;
94
- border: 1px solid #333333 !important;
95
- border-radius: 12px !important;
96
- padding: 20px !important;
97
- margin-top: 20px !important;
98
- font-family: 'Courier New', monospace;
99
- font-size: 14px;
100
  line-height: 1.6;
101
  }
 
 
 
 
 
 
102
  .output-area p {
103
- margin: 5px 0;
104
  }
105
- .output-area .recommendation {
106
  color: #00FF00;
107
  font-weight: bold;
108
  }
109
- .output-area .warning {
110
  color: #FFA500;
111
  font-weight: bold;
112
  }
@@ -114,64 +115,99 @@ button:hover {
114
  color: #FF0000;
115
  font-weight: bold;
116
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  """
118
 
119
- # ================= GRADIO INTERFACE =================
120
  with gr.Blocks(title="Michelin Mining Tyre Analytics — Objective 6", css=custom_css) as demo:
121
- gr.Markdown("""
122
- # 🛞 Michelin Mining Tyre Analytics — Objective 6
123
- ### Simulasi Tekanan Ban Berdasarkan Suhu & Jenis Ban
124
- Masukkan suhu saat ini dan pilih jenis ban untuk mendapatkan rekomendasi tekanan ideal.
125
  """)
126
 
127
- with gr.Row():
128
- tyre_type = gr.Radio(
129
- choices=["10 (Depan)", "11 (Belakang)"],
130
- label="Jenis Ban",
131
- value="10 (Depan)",
132
- interactive=True
133
- )
134
- temperature = gr.Slider(
135
- minimum=40,
136
- maximum=70,
137
- value=54,
138
- step=0.5,
139
- label="Suhu Saat Ini (°C)",
140
- interactive=True
 
 
 
 
 
141
  )
142
 
143
- with gr.Row():
144
- btn_simulate = gr.Button("🔍 Simulasikan Tekanan")
145
-
146
- with gr.Row():
147
- output = gr.HTML(label="Hasil Simulasi")
148
-
149
- def simulate_pressure(tyres, temp):
150
- tyre_num = int(tyres.split()[0])
151
- p_min_high = calculate_pressure(tyre_num, temp, 'High', 'Min')
152
- p_max_high = calculate_pressure(tyre_num, temp, 'High', 'Max')
153
- p_min_low = calculate_pressure(tyre_num, temp, 'Low', 'Min')
154
- p_max_low = calculate_pressure(tyre_num, temp, 'Low', 'Max')
155
-
156
- if p_min_high is None or p_max_high is None or p_min_low is None or p_max_low is None:
157
- return "<p style='color:#FF0000;'>❌ Tidak dapat menghitung tekanan. Periksa input.</p>"
158
-
159
- # Rekomendasi: Jaga tekanan antara P Min Low dan P Max High
160
- recommendation = f"""
161
- <div class="output-area">
162
- <h4>📊 Hasil Simulasi untuk Ban {tyres} pada Suhu {temp}°C</h4>
163
- <p><strong>P Min saat Low Pressure:</strong> <span style="color:#FFD100;">{p_min_low:.1f} psi</span></p>
164
- <p><strong>P Max saat Low Pressure:</strong> <span style="color:#FFD100;">{p_max_low:.1f} psi</span></p>
165
- <p><strong>P Min saat High Pressure:</strong> <span style="color:#FFD100;">{p_min_high:.1f} psi</span></p>
166
- <p><strong>P Max saat High Pressure:</strong> <span style="color:#FFD100;">{p_max_high:.1f} psi</span></p>
 
 
 
167
  <hr>
168
- <p class="recommendation">✅ Rekomendasi Tekanan Ideal:</strong> Isi angin antara <strong>{p_min_low:.1f} – {p_max_high:.1f} psi</strong> untuk menghindari alarm Amber/Red.</p>
169
- <p class="warning">⚠️ Jika tekanan saat ini < {p_min_low:.1f} psi risiko <strong>Low Pressure</strong>.</p>
170
- <p class="danger">⚠️ Jika tekanan saat ini > {p_max_high:.1f} psi → risiko <strong>High Pressure</strong>.</p>
171
- </div>
 
 
 
172
  """
173
- return recommendation
174
 
175
- btn_simulate.click(simulate_pressure, inputs=[tyre_type, temperature], outputs=output)
176
 
177
  demo.launch()
 
2
  import pandas as pd
3
  import numpy as np
4
 
5
+ # ================= LOAD DATA — AMBIL TEKANAN & WAKTU TERAKHIR =================
6
+ last_pressure = 85.0
7
+ last_time = "–"
8
+
9
  try:
10
  df = pd.read_excel("df_final.xlsx", sheet_name="Sheet1")
11
+ df.columns = df.columns.str.replace("Â", "", regex=False)
 
12
  for col in df.select_dtypes(include='object').columns:
13
+ df[col] = df[col].astype(str).str.replace("Â", "", regex=False)
 
14
  df['Time'] = pd.to_datetime(df['Time'], errors='coerce')
15
  df = df.dropna(subset=['Time'])
16
+
17
+ if not df.empty:
18
+ last_row = df.sort_values('Time').iloc[-1]
19
+ last_pressure = float(last_row['Pressure']) if pd.notna(last_row.get('Pressure')) else 85.0
20
+ last_time = last_row['Time'].strftime("%H:%M")
21
+ except Exception as e:
22
+ pass # tetap pakai default jika gagal
23
+
24
+ # ================= BATAS AMAN (SESUAI STANDAR OPERASIONAL) =================
25
+ red_low = 70.0
26
+ amber_low = 75.0
27
+ amber_high = 95.0
28
+ red_high = 100.0
29
+
30
+ def get_safe_fill_range(last_p):
31
+ lower = max(last_p - 10, amber_low + 0.5)
32
+ upper = min(last_p + 10, amber_high - 0.5)
33
+ return round(lower, 1), round(upper, 1)
34
+
35
+ # ================= CUSTOM CSS — BERSIH, PROFESIONAL, SATU HALAMAN =================
 
 
 
 
 
 
36
  custom_css = """
37
+ body, .gradio-container {
38
+ background: #000000 !important;
39
+ color: #FFFFFF !important;
40
  font-family: 'Segoe UI', system-ui, sans-serif;
41
+ margin: 0;
42
+ padding: 0;
43
  }
44
  .gradio-container {
45
+ max-width: 100% !important;
46
+ height: 100vh !important;
47
+ display: flex;
48
+ flex-direction: column;
49
+ align-items: center;
50
+ justify-content: flex-start;
51
+ }
52
+ #title-box {
53
+ background: #003A8F;
54
+ color: white;
55
+ padding: 16px 32px;
56
+ border-radius: 12px;
57
+ margin-bottom: 32px;
58
+ text-align: center;
59
+ width: 100%;
60
+ max-width: 800px;
61
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
62
+ }
63
+ #title-box h1 {
64
+ margin: 0;
65
+ font-size: 28px;
66
+ font-weight: 700;
67
+ }
68
+ .form-section {
69
+ display: flex;
70
+ flex-direction: column;
71
+ align-items: flex-start;
72
+ width: 100%;
73
+ max-width: 800px;
74
+ gap: 24px;
75
+ }
76
+ .control-row {
77
+ display: flex;
78
+ gap: 24px;
79
+ width: 100%;
80
+ align-items: center;
81
+ }
82
+ .control-row > div {
83
+ flex: 1;
84
+ min-width: 200px;
85
+ }
86
+ .gradio-html, .output-area {
87
+ width: 100%;
88
+ max-width: 800px;
89
+ background: #121212;
90
+ border: 1px solid #333;
91
+ border-radius: 12px;
92
+ padding: 24px;
93
+ margin-top: 16px;
94
+ font-size: 18px;
 
 
95
  line-height: 1.6;
96
  }
97
+ .output-area h3 {
98
+ color: #FFD100;
99
+ margin: 16px 0 12px 0;
100
+ font-size: 22px;
101
+ font-weight: 600;
102
+ }
103
  .output-area p {
104
+ margin: 10px 0;
105
  }
106
+ .output-area .safe {
107
  color: #00FF00;
108
  font-weight: bold;
109
  }
110
+ .output-area .caution {
111
  color: #FFA500;
112
  font-weight: bold;
113
  }
 
115
  color: #FF0000;
116
  font-weight: bold;
117
  }
118
+ .gr-button {
119
+ background: #003A8F !important;
120
+ color: white !important;
121
+ font-weight: bold;
122
+ font-size: 18px !important;
123
+ padding: 12px 32px !important;
124
+ border-radius: 8px !important;
125
+ border: none !important;
126
+ width: auto !important;
127
+ margin-top: 12px;
128
+ }
129
+ .gr-button:hover {
130
+ background: #002D6B !important;
131
+ transform: scale(1.03);
132
+ box-shadow: 0 4px 12px rgba(0, 58, 143, 0.4);
133
+ }
134
+ label {
135
+ color: #CCCCCC !important;
136
+ font-size: 18px !important;
137
+ font-weight: 500;
138
+ }
139
+ input, select {
140
+ font-size: 18px !important;
141
+ }
142
  """
143
 
144
+ # ================= GRADIO LAYOUT — FULL KIRI, TANPA SIMBOL =================
145
  with gr.Blocks(title="Michelin Mining Tyre Analytics — Objective 6", css=custom_css) as demo:
146
+ gr.HTML("""
147
+ <div id="title-box">
148
+ <h1>Michelin Mining Tyre Simulation Pressure</h1>
149
+ </div>
150
  """)
151
 
152
+ with gr.Column(elem_classes="form-section"):
153
+ with gr.Row(elem_classes="control-row"):
154
+ temperature = gr.Slider(
155
+ label="Suhu Saat Ini (°C)",
156
+ minimum=40,
157
+ maximum=70,
158
+ value=54,
159
+ step=0.5,
160
+ interactive=True
161
+ )
162
+ tyre_type = gr.Radio(
163
+ choices=["10 (Depan)", "11 (Belakang)"],
164
+ label="Jenis Ban",
165
+ value="10 (Depan)",
166
+ interactive=True
167
+ )
168
+
169
+ last_info = gr.Markdown(
170
+ f"Tekanan Terakhir: {last_pressure} psi | Jam: {last_time}"
171
  )
172
 
173
+ btn_submit = gr.Button("Submit", elem_classes="gr-button")
174
+ output = gr.HTML(elem_classes="output-area")
175
+
176
+ # ================= LOGIKA SIMULASI — BERSIH & BERBASIS DATA NYATA =================
177
+ def simulate(tyre_str, temp):
178
+ # Interpolasi sederhana: baseline 54°C → 85 psi, slope 0.42 psi/°C
179
+ ideal = 85.0 + 0.42 * (temp - 54.0)
180
+ ideal = round(ideal, 1)
181
+
182
+ lower_safe, upper_safe = get_safe_fill_range(last_pressure)
183
+
184
+ # Status penilaian
185
+ if ideal < red_low:
186
+ status = '<span class="danger">Risiko Red Tekanan terlalu rendah</span>'
187
+ elif ideal < amber_low:
188
+ status = '<span class="caution">Risiko Amber — Tekanan mendekati batas bawah</span>'
189
+ elif ideal > red_high:
190
+ status = '<span class="danger">Risiko Red — Tekanan terlalu tinggi</span>'
191
+ elif ideal > amber_high:
192
+ status = '<span class="caution">Risiko Amber Tekanan mendekati batas atas</span>'
193
+ else:
194
+ status = '<span class="safe">Dalam zona aman</span>'
195
+
196
+ rec_html = f"""
197
+ <h3>Hasil Simulasi</h3>
198
+ <p>Ideal Tekanan pada {temp}°C: {ideal} psi</p>
199
+ <p>Status: {status}</p>
200
  <hr>
201
+ <h3>Rekomendasi Isi Ulang</h3>
202
+ <p>Berdasarkan tekanan terakhir ({last_pressure} psi pukul {last_time}):</p>
203
+ <p class="safe">Isi tekanan antara {lower_safe} {upper_safe} psi</p>
204
+ <p class="caution">Jangan isi kurang dari {amber_low} psi (batas Amber bawah)</p>
205
+ <p class="caution">Jangan isi lebih dari {amber_high} psi (batas Amber atas)</p>
206
+ <p class="danger">Hindari tekanan di bawah {red_low} psi atau di atas {red_high} psi (zona Red)</p>
207
+ <p style="margin-top:16px;"><em>Catatan: Rekomendasi ini mempertahankan margin aman minimal 0.5 psi dari zona Amber.</em></p>
208
  """
209
+ return rec_html
210
 
211
+ btn_submit.click(simulate, inputs=[tyre_type, temperature], outputs=output)
212
 
213
  demo.launch()