Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

101 řádky
4.3 KiB

  1. import streamlit as st
  2. import pandas as pd
  3. import json
  4. from PIL import Image, ImageDraw, ImageFont
  5. from pathlib import Path
  6. import time
  7. import os
  8. def show_inference_page(cfg):
  9. # Definizione percorsi
  10. # Assumendo che il file sia in /app/app/web_inference.py,
  11. # cerchiamo il font nella stessa cartella 'app'
  12. BASE_DIR = Path(__file__).parent
  13. MAPS_DIR = Path(cfg['maps']['map_dir'])
  14. INFER_FILE = Path(cfg['infer']['output_dir']) / cfg['infer']['out_file']
  15. # Percorso del font locale
  16. FONT_PATH = BASE_DIR / "DejaVuSans-Bold.ttf"
  17. st.subheader("🤖 Monitoraggio Inferenza Live")
  18. # --- CONTROLLI NEL TAB ---
  19. with st.expander("🎨 Opzioni e Controllo", expanded=True):
  20. col_ctrl1, col_ctrl2, col_ctrl3, col_ctrl4 = st.columns(4)
  21. with col_ctrl1:
  22. dot_size = st.slider("Dimensione Marker", 10, 100, 45, key="inf_dot")
  23. with col_ctrl2:
  24. refresh_rate = st.select_slider("Refresh (s)", options=[2, 5, 10, 30], value=5, key="inf_ref")
  25. with col_ctrl3:
  26. show_labels = st.checkbox("Mostra MAC", value=True, key="inf_show_mac")
  27. with col_ctrl4:
  28. # Monitoraggio disattivato di default per evitare login fantasma
  29. auto_refresh = st.toggle("🔄 Monitoraggio Attivo", value=False, key="inf_auto")
  30. floor_id = st.number_input("Piano (Z)", value=0, min_value=0, step=1, key="inf_z")
  31. # Caricamento Metadati
  32. meta_path = MAPS_DIR / f"{cfg['maps']['meta_prefix']}{floor_id}.json"
  33. meta = {"pixel_ratio": 1.0, "origin": [0, 0]}
  34. if meta_path.exists():
  35. with open(meta_path, "r") as f:
  36. meta.update(json.load(f))
  37. try:
  38. if INFER_FILE.exists():
  39. df = pd.read_csv(INFER_FILE, sep=";")
  40. df_active = df[(df['z'] == floor_id) & (df['x'] != -1) & (df['y'] != -1)]
  41. img_path = MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}.png"
  42. if img_path.exists():
  43. img = Image.open(img_path).convert("RGBA")
  44. draw = ImageDraw.Draw(img)
  45. # --- GESTIONE FONT XL ---
  46. # Dimensione proporzionale al pallino (1.5x)
  47. dynamic_font_size = int(dot_size * 1.5)
  48. font = None
  49. if FONT_PATH.exists():
  50. try:
  51. # Tenta il caricamento del file TTF locale
  52. font = ImageFont.truetype(str(FONT_PATH), dynamic_font_size)
  53. except Exception as e:
  54. # Se il file è corrotto (unknown file format), mostra errore e usa fallback
  55. st.error(f"Errore caricamento font: {e}. Controlla il file DejaVuSans-Bold.ttf")
  56. if font is None:
  57. font = ImageFont.load_default()
  58. for _, row in df_active.iterrows():
  59. px_x = (row['x'] * meta["pixel_ratio"]) + meta["origin"][0]
  60. px_y = (row['y'] * meta["pixel_ratio"]) + meta["origin"][1]
  61. r = dot_size
  62. # Disegno Marker
  63. draw.ellipse([px_x-(r+5), px_y-(r+5), px_x+(r+5), px_y+(r+5)], fill="black")
  64. draw.ellipse([px_x-r, px_y-r, px_x+r, px_y+r], fill="#00E5FF")
  65. if show_labels:
  66. label = f"{str(row['mac'])[-5:]}"
  67. # Posizionamento dinamico a destra del pallino
  68. text_x = px_x + r + 20
  69. text_y = px_y - (dynamic_font_size / 2)
  70. # Outline per leggibilità (bordo nero spesso 3px)
  71. for dx, dy in [(-3,-3), (3,-3), (-3,3), (3,3), (0,-3), (0,3), (-3,0), (2,0)]:
  72. draw.text((text_x + dx, text_y + dy), label, font=font, fill="black")
  73. # Testo Giallo
  74. draw.text((text_x, text_y), label, font=font, fill="#FFFF00")
  75. st.image(img, use_column_width=True)
  76. st.metric("Dispositivi Online", len(df_active))
  77. except Exception as e:
  78. st.error(f"Errore generale: {e}")
  79. # --- REFRESH CONDIZIONALE ---
  80. if auto_refresh:
  81. time.sleep(refresh_rate)
  82. st.rerun()