DevNumb commited on
Commit
d8a0147
Β·
verified Β·
1 Parent(s): 34b2711

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -168
app.py CHANGED
@@ -1,6 +1,6 @@
1
- from fastapi import FastAPI, Request, Form
2
  from fastapi.responses import HTMLResponse
3
- from fastapi.templating import Jinja2Templates
4
  import numpy as np
5
  import torch
6
  import torch.nn as nn
@@ -8,7 +8,7 @@ from sklearn.ensemble import RandomForestClassifier
8
  from sklearn.svm import SVC
9
  from sklearn.preprocessing import StandardScaler
10
  import warnings
11
- import os
12
  warnings.filterwarnings('ignore')
13
 
14
  app = FastAPI(title="Chiller Fault Detection System")
@@ -60,7 +60,7 @@ class HybridFDDModel:
60
  "Condenser Fouling"
61
  ]
62
 
63
- samples_per_class = 500
64
 
65
  for class_idx, fault_name in enumerate(fault_types):
66
  for _ in range(samples_per_class):
@@ -194,28 +194,32 @@ class HybridFDDModel:
194
  return fault_types
195
 
196
  # Initialize model
197
- print("Training model...")
198
  model = HybridFDDModel()
199
  fault_types = model.train_demo()
200
  print(f"Model ready! Trained on {len(fault_types)} classes")
201
 
202
- # HTML template
203
- HTML_TEMPLATE = """
204
- <!DOCTYPE html>
205
- <html>
206
  <head>
207
- <title>Chiller Fault Detection System</title>
208
  <meta charset="UTF-8">
209
- <meta name="viewport" content="width=device-width, initial-scale=1">
 
210
  <style>
211
- * { box-sizing: border-box; }
212
- body {
213
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
214
  margin: 0;
215
- padding: 20px;
 
 
 
 
 
216
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
217
  min-height: 100vh;
 
218
  }
 
219
  .container {
220
  max-width: 1400px;
221
  margin: 0 auto;
@@ -224,48 +228,70 @@ HTML_TEMPLATE = """
224
  box-shadow: 0 20px 60px rgba(0,0,0,0.3);
225
  overflow: hidden;
226
  }
 
227
  .header {
228
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
229
  color: white;
230
  padding: 30px;
231
  text-align: center;
232
  }
233
- h1 { margin: 0; font-size: 2em; }
234
- .subtitle { margin: 10px 0 0; opacity: 0.9; }
235
- .content { display: flex; flex-wrap: wrap; }
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  .inputs {
237
  flex: 2;
238
  padding: 30px;
239
  background: #f8f9fa;
240
  }
 
241
  .results {
242
  flex: 1;
243
  padding: 30px;
244
  background: white;
245
  border-left: 1px solid #e0e0e0;
246
  }
 
247
  .input-group {
248
  margin-bottom: 15px;
249
  display: flex;
250
  flex-wrap: wrap;
251
  align-items: center;
252
  }
 
253
  .input-group label {
254
  width: 250px;
255
  font-weight: 600;
256
  color: #333;
 
257
  }
 
258
  .input-group input {
259
  flex: 1;
260
- padding: 8px 12px;
261
- border: 1px solid #ddd;
262
- border-radius: 5px;
263
  font-size: 14px;
 
264
  }
 
265
  .input-group input:focus {
266
  outline: none;
267
  border-color: #667eea;
268
  }
 
269
  button {
270
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
271
  color: white;
@@ -277,17 +303,25 @@ HTML_TEMPLATE = """
277
  cursor: pointer;
278
  width: 100%;
279
  margin-top: 20px;
280
- transition: transform 0.2s;
281
  }
 
282
  button:hover {
283
  transform: translateY(-2px);
 
284
  }
 
 
 
 
 
285
  .result-card {
286
  background: #f8f9fa;
287
  border-radius: 10px;
288
  padding: 20px;
289
  margin-bottom: 20px;
290
  }
 
291
  .status {
292
  font-size: 24px;
293
  font-weight: bold;
@@ -296,37 +330,97 @@ HTML_TEMPLATE = """
296
  border-radius: 10px;
297
  margin-bottom: 20px;
298
  }
299
- .status.normal { background: #d4edda; color: #155724; }
300
- .status.fault { background: #f8d7da; color: #721c24; }
 
 
 
 
 
 
 
 
 
 
 
301
  .metric {
302
  display: flex;
303
  justify-content: space-between;
304
- padding: 10px 0;
305
  border-bottom: 1px solid #e0e0e0;
306
  }
307
- .metric-label { font-weight: 600; }
308
- .metric-value { color: #667eea; font-weight: bold; }
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  .severity {
310
- padding: 5px 10px;
311
  border-radius: 5px;
312
  display: inline-block;
313
  font-weight: bold;
 
314
  }
315
- .severity.HIGH { background: #dc3545; color: white; }
316
- .severity.MEDIUM { background: #ffc107; color: #333; }
317
- .severity.LOW { background: #28a745; color: white; }
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  .info {
319
  background: #e7f3ff;
320
  padding: 15px;
321
  border-radius: 10px;
322
  margin-top: 20px;
323
  font-size: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  }
325
- h3 { margin-top: 0; color: #333; }
326
  @media (max-width: 768px) {
327
- .inputs, .results { flex: 100%; }
328
- .results { border-left: none; border-top: 1px solid #e0e0e0; }
329
- .input-group label { width: 100%; margin-bottom: 5px; }
 
 
 
 
 
 
 
 
330
  }
331
  </style>
332
  </head>
@@ -338,153 +432,185 @@ HTML_TEMPLATE = """
338
  <div class="subtitle">Trained on ASHRAE RP-1043 Dataset | 95%+ Accuracy</div>
339
  </div>
340
 
341
- <form method="POST" action="/predict">
342
- <div class="content">
343
- <div class="inputs">
344
- <h3>πŸ“Š Chiller Parameters</h3>
345
-
346
- <div class="input-group">
347
- <label>🌑️ Chilled Water Supply Temp (°C):</label>
348
- <input type="number" step="0.1" name="temp_chilled_supply" value="7.2" required>
349
- </div>
350
- <div class="input-group">
351
- <label>🌑️ Chilled Water Return Temp (°C):</label>
352
- <input type="number" step="0.1" name="temp_chilled_return" value="12.1" required>
353
- </div>
354
- <div class="input-group">
355
- <label>🌑️ Condenser Water Supply Temp (°C):</label>
356
- <input type="number" step="0.1" name="temp_cond_supply" value="28.5" required>
357
- </div>
358
- <div class="input-group">
359
- <label>🌑️ Condenser Water Return Temp (°C):</label>
360
- <input type="number" step="0.1" name="temp_cond_return" value="34.8" required>
361
- </div>
362
- <div class="input-group">
363
- <label>πŸ“Š Evaporator Pressure (kPa):</label>
364
- <input type="number" step="5" name="pressure_evap" value="345" required>
365
- </div>
366
- <div class="input-group">
367
- <label>πŸ“Š Condenser Pressure (kPa):</label>
368
- <input type="number" step="5" name="pressure_cond" value="795" required>
369
- </div>
370
- <div class="input-group">
371
- <label>⚑ Compressor Power (kW):</label>
372
- <input type="number" step="5" name="power_compressor" value="148" required>
373
- </div>
374
- <div class="input-group">
375
- <label>πŸ’§ Refrigerant Flow (kg/s):</label>
376
- <input type="number" step="0.1" name="flow_refrigerant" value="5.1" required>
377
- </div>
378
- <div class="input-group">
379
- <label>πŸ›’οΈ Oil Temperature (Β°C):</label>
380
- <input type="number" step="1" name="temp_oil" value="44" required>
381
- </div>
382
- <div class="input-group">
383
- <label>πŸ”₯ Superheat (K):</label>
384
- <input type="number" step="0.1" name="superheat" value="5.2" required>
385
- </div>
386
- <div class="input-group">
387
- <label>❄️ Subcooling (K):</label>
388
- <input type="number" step="0.1" name="subcooling" value="4.1" required>
389
- </div>
390
- <div class="input-group">
391
- <label>πŸ“ Evaporator Approach (K):</label>
392
- <input type="number" step="0.1" name="approach_evap" value="2.1" required>
393
- </div>
394
- <div class="input-group">
395
- <label>πŸ“ Condenser Approach (K):</label>
396
- <input type="number" step="0.1" name="approach_cond" value="3.2" required>
397
- </div>
398
- <div class="input-group">
399
- <label>❄️ Cooling Capacity (kW):</label>
400
- <input type="number" step="10" name="capacity_cooling" value="495" required>
401
- </div>
402
- <div class="input-group">
403
- <label>πŸ“ˆ COP:</label>
404
- <input type="number" step="0.1" name="cop" value="4.6" required>
 
 
 
 
 
 
 
 
 
 
405
  </div>
406
-
407
- <button type="submit">πŸ” Diagnose System</button>
408
  </div>
409
 
410
- <div class="results">
411
- <h3>πŸ“‹ Diagnosis Result</h3>
412
- {% if result %}
413
- <div class="status {{ 'normal' if result.Status == 'βœ… NORMAL OPERATION' else 'fault' }}">
414
- {{ result.Status }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  </div>
416
 
417
  <div class="result-card">
418
  <div class="metric">
419
  <span class="metric-label">Detected Fault:</span>
420
- <span class="metric-value">{{ result.Detected_Fault }}</span>
421
  </div>
422
  <div class="metric">
423
  <span class="metric-label">Confidence:</span>
424
- <span class="metric-value">{{ result.Confidence }}</span>
425
  </div>
426
  <div class="metric">
427
  <span class="metric-label">Severity:</span>
428
  <span class="metric-value">
429
- <span class="severity {{ result.Severity }}">{{ result.Severity }}</span>
430
  </span>
431
  </div>
432
  <div class="metric">
433
  <span class="metric-label">Fault Code:</span>
434
- <span class="metric-value">{{ result.Fault_Code }}</span>
435
  </div>
436
  </div>
437
 
438
  <div class="info">
439
  <strong>πŸ“ Recommended Action:</strong><br>
440
- {{ result.Recommended_Action }}
441
- </div>
442
- {% else %}
443
- <div class="info">
444
- <strong>ℹ️ Instructions:</strong><br>
445
- Enter chiller parameters on the left and click "Diagnose System" to get fault analysis.
446
- </div>
447
- {% endif %}
448
-
449
- <div class="info" style="margin-top: 20px;">
450
- <strong>πŸ—οΈ Architecture:</strong><br>
451
- Random Forest (Feature Selection) β†’ Neural Network (Representation Learning) β†’ SVM (Classification)
452
  </div>
453
- </div>
454
- </div>
455
- </form>
456
- </div>
 
 
457
  </body>
458
  </html>
459
  """
460
 
461
  @app.get("/", response_class=HTMLResponse)
462
  async def home():
463
- return HTMLResponse(content=HTML_TEMPLATE.replace("{% if result %}", "{% if result %}").replace("{{ result.Status }}", "{{ result.Status }}").replace("{{ result.Detected_Fault }}", "{{ result.Detected_Fault }}").replace("{{ result.Confidence }}", "{{ result.Confidence }}").replace("{{ result.Severity }}", "{{ result.Severity }}").replace("{{ result.Fault_Code }}", "{{ result.Fault_Code }}").replace("{{ result.Recommended_Action }}", "{{ result.Recommended_Action }}"))
464
 
465
- @app.post("/predict", response_class=HTMLResponse)
466
- async def predict(
467
- request: Request,
468
- temp_chilled_supply: float = Form(...),
469
- temp_chilled_return: float = Form(...),
470
- temp_cond_supply: float = Form(...),
471
- temp_cond_return: float = Form(...),
472
- pressure_evap: float = Form(...),
473
- pressure_cond: float = Form(...),
474
- power_compressor: float = Form(...),
475
- flow_refrigerant: float = Form(...),
476
- temp_oil: float = Form(...),
477
- superheat: float = Form(...),
478
- subcooling: float = Form(...),
479
- approach_evap: float = Form(...),
480
- approach_cond: float = Form(...),
481
- capacity_cooling: float = Form(...),
482
- cop: float = Form(...)
483
- ):
484
- features = np.array([[temp_chilled_supply, temp_chilled_return, temp_cond_supply,
485
- temp_cond_return, pressure_evap, pressure_cond, power_compressor,
486
- flow_refrigerant, temp_oil, superheat, subcooling,
487
- approach_evap, approach_cond, capacity_cooling, cop]])
488
 
489
  features_scaled = model.scaler.transform(features)
490
  features_selected = features_scaled[:, model.top_features_idx]
@@ -513,7 +639,7 @@ async def predict(
513
 
514
  severity = "HIGH" if confidence > 80 else "MEDIUM" if confidence > 60 else "LOW"
515
 
516
- result = {
517
  "Status": "⚠️ FAULT DETECTED" if is_fault else "βœ… NORMAL OPERATION",
518
  "Detected_Fault": fault_name,
519
  "Confidence": f"{confidence:.1f}%",
@@ -521,23 +647,6 @@ async def predict(
521
  "Recommended_Action": recommendations.get(fault_name, "No action needed") if is_fault else "System operating normally",
522
  "Fault_Code": f"F{prediction}" if is_fault else "NORMAL"
523
  }
524
-
525
- # Inject result into template
526
- html = HTML_TEMPLATE.replace("{% if result %}", "{% if True %}")
527
- html = html.replace("{{ result.Status }}", result["Status"])
528
- html = html.replace("{{ result.Detected_Fault }}", result["Detected_Fault"])
529
- html = html.replace("{{ result.Confidence }}", result["Confidence"])
530
- html = html.replace("{{ result.Severity }}", result["Severity"])
531
- html = html.replace("{{ result.Fault_Code }}", result["Fault_Code"])
532
- html = html.replace("{{ result.Recommended_Action }}", result["Recommended_Action"])
533
-
534
- # Fix status class
535
- if result["Status"] == "βœ… NORMAL OPERATION":
536
- html = html.replace("{{ 'normal' if result.Status == 'βœ… NORMAL OPERATION' else 'fault' }}", "normal")
537
- else:
538
- html = html.replace("{{ 'normal' if result.Status == 'βœ… NORMAL OPERATION' else 'fault' }}", "fault")
539
-
540
- return HTMLResponse(content=html)
541
 
542
  if __name__ == "__main__":
543
  import uvicorn
 
1
+ from fastapi import FastAPI, Request
2
  from fastapi.responses import HTMLResponse
3
+ from fastapi.staticfiles import StaticFiles
4
  import numpy as np
5
  import torch
6
  import torch.nn as nn
 
8
  from sklearn.svm import SVC
9
  from sklearn.preprocessing import StandardScaler
10
  import warnings
11
+ import json
12
  warnings.filterwarnings('ignore')
13
 
14
  app = FastAPI(title="Chiller Fault Detection System")
 
60
  "Condenser Fouling"
61
  ]
62
 
63
+ samples_per_class = 400
64
 
65
  for class_idx, fault_name in enumerate(fault_types):
66
  for _ in range(samples_per_class):
 
194
  return fault_types
195
 
196
  # Initialize model
197
+ print("Training model... This may take a moment")
198
  model = HybridFDDModel()
199
  fault_types = model.train_demo()
200
  print(f"Model ready! Trained on {len(fault_types)} classes")
201
 
202
+ # HTML/CSS/JS Interface
203
+ HTML_PAGE = """<!DOCTYPE html>
204
+ <html lang="en">
 
205
  <head>
 
206
  <meta charset="UTF-8">
207
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
208
+ <title>Chiller Fault Detection System</title>
209
  <style>
210
+ * {
 
 
211
  margin: 0;
212
+ padding: 0;
213
+ box-sizing: border-box;
214
+ }
215
+
216
+ body {
217
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
218
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
219
  min-height: 100vh;
220
+ padding: 20px;
221
  }
222
+
223
  .container {
224
  max-width: 1400px;
225
  margin: 0 auto;
 
228
  box-shadow: 0 20px 60px rgba(0,0,0,0.3);
229
  overflow: hidden;
230
  }
231
+
232
  .header {
233
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
234
  color: white;
235
  padding: 30px;
236
  text-align: center;
237
  }
238
+
239
+ h1 {
240
+ font-size: 2em;
241
+ margin-bottom: 10px;
242
+ }
243
+
244
+ .subtitle {
245
+ opacity: 0.9;
246
+ margin-top: 5px;
247
+ }
248
+
249
+ .content {
250
+ display: flex;
251
+ flex-wrap: wrap;
252
+ }
253
+
254
  .inputs {
255
  flex: 2;
256
  padding: 30px;
257
  background: #f8f9fa;
258
  }
259
+
260
  .results {
261
  flex: 1;
262
  padding: 30px;
263
  background: white;
264
  border-left: 1px solid #e0e0e0;
265
  }
266
+
267
  .input-group {
268
  margin-bottom: 15px;
269
  display: flex;
270
  flex-wrap: wrap;
271
  align-items: center;
272
  }
273
+
274
  .input-group label {
275
  width: 250px;
276
  font-weight: 600;
277
  color: #333;
278
+ font-size: 14px;
279
  }
280
+
281
  .input-group input {
282
  flex: 1;
283
+ padding: 10px 12px;
284
+ border: 2px solid #e0e0e0;
285
+ border-radius: 8px;
286
  font-size: 14px;
287
+ transition: border-color 0.3s;
288
  }
289
+
290
  .input-group input:focus {
291
  outline: none;
292
  border-color: #667eea;
293
  }
294
+
295
  button {
296
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
297
  color: white;
 
303
  cursor: pointer;
304
  width: 100%;
305
  margin-top: 20px;
306
+ transition: transform 0.2s, box-shadow 0.2s;
307
  }
308
+
309
  button:hover {
310
  transform: translateY(-2px);
311
+ box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
312
  }
313
+
314
+ button:active {
315
+ transform: translateY(0);
316
+ }
317
+
318
  .result-card {
319
  background: #f8f9fa;
320
  border-radius: 10px;
321
  padding: 20px;
322
  margin-bottom: 20px;
323
  }
324
+
325
  .status {
326
  font-size: 24px;
327
  font-weight: bold;
 
330
  border-radius: 10px;
331
  margin-bottom: 20px;
332
  }
333
+
334
+ .status.normal {
335
+ background: #d4edda;
336
+ color: #155724;
337
+ border: 2px solid #c3e6cb;
338
+ }
339
+
340
+ .status.fault {
341
+ background: #f8d7da;
342
+ color: #721c24;
343
+ border: 2px solid #f5c6cb;
344
+ }
345
+
346
  .metric {
347
  display: flex;
348
  justify-content: space-between;
349
+ padding: 12px 0;
350
  border-bottom: 1px solid #e0e0e0;
351
  }
352
+
353
+ .metric:last-child {
354
+ border-bottom: none;
355
+ }
356
+
357
+ .metric-label {
358
+ font-weight: 600;
359
+ color: #555;
360
+ }
361
+
362
+ .metric-value {
363
+ color: #667eea;
364
+ font-weight: bold;
365
+ }
366
+
367
  .severity {
368
+ padding: 4px 10px;
369
  border-radius: 5px;
370
  display: inline-block;
371
  font-weight: bold;
372
+ font-size: 12px;
373
  }
374
+
375
+ .severity.HIGH {
376
+ background: #dc3545;
377
+ color: white;
378
+ }
379
+
380
+ .severity.MEDIUM {
381
+ background: #ffc107;
382
+ color: #333;
383
+ }
384
+
385
+ .severity.LOW {
386
+ background: #28a745;
387
+ color: white;
388
+ }
389
+
390
  .info {
391
  background: #e7f3ff;
392
  padding: 15px;
393
  border-radius: 10px;
394
  margin-top: 20px;
395
  font-size: 14px;
396
+ line-height: 1.5;
397
+ }
398
+
399
+ h3 {
400
+ margin-top: 0;
401
+ margin-bottom: 15px;
402
+ color: #333;
403
+ }
404
+
405
+ .loading {
406
+ text-align: center;
407
+ padding: 40px;
408
+ color: #667eea;
409
+ font-weight: bold;
410
  }
411
+
412
  @media (max-width: 768px) {
413
+ .inputs, .results {
414
+ flex: 100%;
415
+ }
416
+ .results {
417
+ border-left: none;
418
+ border-top: 1px solid #e0e0e0;
419
+ }
420
+ .input-group label {
421
+ width: 100%;
422
+ margin-bottom: 5px;
423
+ }
424
  }
425
  </style>
426
  </head>
 
432
  <div class="subtitle">Trained on ASHRAE RP-1043 Dataset | 95%+ Accuracy</div>
433
  </div>
434
 
435
+ <div class="content">
436
+ <div class="inputs">
437
+ <h3>πŸ“Š Chiller Parameters</h3>
438
+
439
+ <div class="input-group">
440
+ <label>🌑️ Chilled Water Supply Temp (°C):</label>
441
+ <input type="number" step="0.1" id="temp_chilled_supply" value="7.2">
442
+ </div>
443
+ <div class="input-group">
444
+ <label>🌑️ Chilled Water Return Temp (°C):</label>
445
+ <input type="number" step="0.1" id="temp_chilled_return" value="12.1">
446
+ </div>
447
+ <div class="input-group">
448
+ <label>🌑️ Condenser Water Supply Temp (°C):</label>
449
+ <input type="number" step="0.1" id="temp_cond_supply" value="28.5">
450
+ </div>
451
+ <div class="input-group">
452
+ <label>🌑️ Condenser Water Return Temp (°C):</label>
453
+ <input type="number" step="0.1" id="temp_cond_return" value="34.8">
454
+ </div>
455
+ <div class="input-group">
456
+ <label>πŸ“Š Evaporator Pressure (kPa):</label>
457
+ <input type="number" step="5" id="pressure_evap" value="345">
458
+ </div>
459
+ <div class="input-group">
460
+ <label>πŸ“Š Condenser Pressure (kPa):</label>
461
+ <input type="number" step="5" id="pressure_cond" value="795">
462
+ </div>
463
+ <div class="input-group">
464
+ <label>⚑ Compressor Power (kW):</label>
465
+ <input type="number" step="5" id="power_compressor" value="148">
466
+ </div>
467
+ <div class="input-group">
468
+ <label>πŸ’§ Refrigerant Flow (kg/s):</label>
469
+ <input type="number" step="0.1" id="flow_refrigerant" value="5.1">
470
+ </div>
471
+ <div class="input-group">
472
+ <label>πŸ›’οΈ Oil Temperature (Β°C):</label>
473
+ <input type="number" step="1" id="temp_oil" value="44">
474
+ </div>
475
+ <div class="input-group">
476
+ <label>πŸ”₯ Superheat (K):</label>
477
+ <input type="number" step="0.1" id="superheat" value="5.2">
478
+ </div>
479
+ <div class="input-group">
480
+ <label>❄️ Subcooling (K):</label>
481
+ <input type="number" step="0.1" id="subcooling" value="4.1">
482
+ </div>
483
+ <div class="input-group">
484
+ <label>πŸ“ Evaporator Approach (K):</label>
485
+ <input type="number" step="0.1" id="approach_evap" value="2.1">
486
+ </div>
487
+ <div class="input-group">
488
+ <label>πŸ“ Condenser Approach (K):</label>
489
+ <input type="number" step="0.1" id="approach_cond" value="3.2">
490
+ </div>
491
+ <div class="input-group">
492
+ <label>❄️ Cooling Capacity (kW):</label>
493
+ <input type="number" step="10" id="capacity_cooling" value="495">
494
+ </div>
495
+ <div class="input-group">
496
+ <label>πŸ“ˆ COP:</label>
497
+ <input type="number" step="0.1" id="cop" value="4.6">
498
+ </div>
499
+
500
+ <button onclick="diagnose()">πŸ” Diagnose System</button>
501
+ </div>
502
+
503
+ <div class="results">
504
+ <h3>πŸ“‹ Diagnosis Result</h3>
505
+ <div id="result">
506
+ <div class="info">
507
+ <strong>ℹ️ Instructions:</strong><br>
508
+ Enter chiller parameters on the left and click "Diagnose System" to get fault analysis.
509
  </div>
 
 
510
  </div>
511
 
512
+ <div class="info" style="margin-top: 20px;">
513
+ <strong>πŸ—οΈ Architecture:</strong><br>
514
+ Random Forest (Feature Selection) β†’ Neural Network (Representation Learning) β†’ SVM (Classification)
515
+ </div>
516
+ </div>
517
+ </div>
518
+ </div>
519
+
520
+ <script>
521
+ async function diagnose() {
522
+ const resultDiv = document.getElementById('result');
523
+ resultDiv.innerHTML = '<div class="loading">πŸ” Analyzing system parameters...</div>';
524
+
525
+ const data = {
526
+ temp_chilled_supply: parseFloat(document.getElementById('temp_chilled_supply').value),
527
+ temp_chilled_return: parseFloat(document.getElementById('temp_chilled_return').value),
528
+ temp_cond_supply: parseFloat(document.getElementById('temp_cond_supply').value),
529
+ temp_cond_return: parseFloat(document.getElementById('temp_cond_return').value),
530
+ pressure_evap: parseFloat(document.getElementById('pressure_evap').value),
531
+ pressure_cond: parseFloat(document.getElementById('pressure_cond').value),
532
+ power_compressor: parseFloat(document.getElementById('power_compressor').value),
533
+ flow_refrigerant: parseFloat(document.getElementById('flow_refrigerant').value),
534
+ temp_oil: parseFloat(document.getElementById('temp_oil').value),
535
+ superheat: parseFloat(document.getElementById('superheat').value),
536
+ subcooling: parseFloat(document.getElementById('subcooling').value),
537
+ approach_evap: parseFloat(document.getElementById('approach_evap').value),
538
+ approach_cond: parseFloat(document.getElementById('approach_cond').value),
539
+ capacity_cooling: parseFloat(document.getElementById('capacity_cooling').value),
540
+ cop: parseFloat(document.getElementById('cop').value)
541
+ };
542
+
543
+ try {
544
+ const response = await fetch('/api/predict', {
545
+ method: 'POST',
546
+ headers: {
547
+ 'Content-Type': 'application/json',
548
+ },
549
+ body: JSON.stringify(data)
550
+ });
551
+
552
+ const result = await response.json();
553
+
554
+ const statusClass = result.Status === 'βœ… NORMAL OPERATION' ? 'normal' : 'fault';
555
+ const severityClass = result.Severity === 'HIGH' ? 'HIGH' : (result.Severity === 'MEDIUM' ? 'MEDIUM' : 'LOW');
556
+
557
+ resultDiv.innerHTML = `
558
+ <div class="status ${statusClass}">
559
+ ${result.Status}
560
  </div>
561
 
562
  <div class="result-card">
563
  <div class="metric">
564
  <span class="metric-label">Detected Fault:</span>
565
+ <span class="metric-value">${result.Detected_Fault}</span>
566
  </div>
567
  <div class="metric">
568
  <span class="metric-label">Confidence:</span>
569
+ <span class="metric-value">${result.Confidence}</span>
570
  </div>
571
  <div class="metric">
572
  <span class="metric-label">Severity:</span>
573
  <span class="metric-value">
574
+ <span class="severity ${severityClass}">${result.Severity}</span>
575
  </span>
576
  </div>
577
  <div class="metric">
578
  <span class="metric-label">Fault Code:</span>
579
+ <span class="metric-value">${result.Fault_Code}</span>
580
  </div>
581
  </div>
582
 
583
  <div class="info">
584
  <strong>πŸ“ Recommended Action:</strong><br>
585
+ ${result.Recommended_Action}
 
 
 
 
 
 
 
 
 
 
 
586
  </div>
587
+ `;
588
+ } catch (error) {
589
+ resultDiv.innerHTML = '<div class="info" style="background: #f8d7da; color: #721c24;"><strong>❌ Error:</strong> Failed to get diagnosis. Please try again.</div>';
590
+ }
591
+ }
592
+ </script>
593
  </body>
594
  </html>
595
  """
596
 
597
  @app.get("/", response_class=HTMLResponse)
598
  async def home():
599
+ return HTMLResponse(content=HTML_PAGE)
600
 
601
+ @app.post("/api/predict")
602
+ async def predict(request: Request):
603
+ data = await request.json()
604
+
605
+ features = np.array([[
606
+ data['temp_chilled_supply'], data['temp_chilled_return'],
607
+ data['temp_cond_supply'], data['temp_cond_return'],
608
+ data['pressure_evap'], data['pressure_cond'],
609
+ data['power_compressor'], data['flow_refrigerant'],
610
+ data['temp_oil'], data['superheat'], data['subcooling'],
611
+ data['approach_evap'], data['approach_cond'],
612
+ data['capacity_cooling'], data['cop']
613
+ ]])
 
 
 
 
 
 
 
 
 
 
614
 
615
  features_scaled = model.scaler.transform(features)
616
  features_selected = features_scaled[:, model.top_features_idx]
 
639
 
640
  severity = "HIGH" if confidence > 80 else "MEDIUM" if confidence > 60 else "LOW"
641
 
642
+ return {
643
  "Status": "⚠️ FAULT DETECTED" if is_fault else "βœ… NORMAL OPERATION",
644
  "Detected_Fault": fault_name,
645
  "Confidence": f"{confidence:.1f}%",
 
647
  "Recommended_Action": recommendations.get(fault_name, "No action needed") if is_fault else "System operating normally",
648
  "Fault_Code": f"F{prediction}" if is_fault else "NORMAL"
649
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
650
 
651
  if __name__ == "__main__":
652
  import uvicorn