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ů.
 
 
 
 

103 řádky
4.1 KiB

  1. import streamlit as st
  2. import pandas as pd
  3. import os
  4. import json
  5. import folium
  6. from streamlit_folium import st_folium
  7. from pathlib import Path
  8. from PIL import Image
  9. import base64
  10. from io import BytesIO
  11. # --- UTILS ---
  12. @st.cache_data
  13. def get_image_base64(img_path):
  14. img = Image.open(img_path).convert("RGBA")
  15. w, h = img.size
  16. buffered = BytesIO()
  17. img.save(buffered, format="PNG")
  18. img_str = base64.b64encode(buffered.getvalue()).decode("ascii")
  19. return f"data:image/png;base64,{img_str}", w, h
  20. def show_inference_page(cfg):
  21. st.subheader("📡 Monitoraggio Beacon Real-Time")
  22. # --- CONFIGURAZIONE PERCORSI ---
  23. MAPS_DIR = Path(cfg['maps']['map_dir'])
  24. INFER_FILE = Path("/data/infer/infer.csv")
  25. # --- 1. SELEZIONE E STATO (RIGA COMPATTA) ---
  26. maps = sorted([f.replace(cfg['maps']['floor_prefix'], "").split('.')[0]
  27. for f in os.listdir(MAPS_DIR) if f.startswith(cfg['maps']['floor_prefix'])])
  28. if not maps:
  29. st.warning("Nessuna mappa configurata.")
  30. return
  31. # Lettura dati per conteggio
  32. df_infer = pd.DataFrame()
  33. if INFER_FILE.exists():
  34. df_infer = pd.read_csv(INFER_FILE, sep=";")
  35. # Layout riga 1
  36. c_piano, c_count, c_size = st.columns([3, 2, 2])
  37. with c_piano:
  38. sub1, sub2 = st.columns([1, 1.2])
  39. sub1.markdown("<p style='padding-top:35px; font-weight:bold; font-size:15px;'>Piano Visualizzato:</p>", unsafe_allow_html=True)
  40. floor_id = sub2.selectbox("", maps, key="inf_floor_v24", label_visibility="collapsed")
  41. # Filtro dati per il piano scelto
  42. df_active = df_infer[(df_infer['z'].astype(str) == str(floor_id)) & (df_infer['x'] != -1)] if not df_infer.empty else pd.DataFrame()
  43. with c_count:
  44. st.info(f"📡 Beacon Attivi: **{len(df_active)}**\n(Totali nel file: {len(df_infer)})")
  45. with c_size:
  46. # Slider per dimensione pallini (come nel mapper)
  47. m_size = st.slider("Dimensione Beacon:", 5, 20, 8, key="inf_msize_v24")
  48. # Caricamento Metadati
  49. meta_path = MAPS_DIR / f"{cfg['maps']['meta_prefix']}{floor_id}.json"
  50. if not meta_path.exists(): return
  51. with open(meta_path, "r") as f: meta = json.load(f)
  52. # --- 2. RENDERING MAPPA ---
  53. st.markdown("---")
  54. img_p = next((MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}{e}" for e in ['.png','.jpg'] if (MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}{e}").exists()))
  55. img_data, w, h = get_image_base64(img_p)
  56. bounds = [[0, 0], [h, w]]
  57. m = folium.Map(location=[h/2, w/2], crs="Simple", tiles=None, attribution_control=False)
  58. m.fit_bounds(bounds)
  59. m.options.update({"minZoom": -6, "maxZoom": 6, "zoomSnap": 0.25, "maxBounds": bounds, "maxBoundsViscosity": 1.0})
  60. folium.raster_layers.ImageOverlay(image=img_data, bounds=bounds).add_to(m)
  61. # Disegno Beacon
  62. if meta["calibrated"] and meta["origin"] != [0,0]:
  63. # Origine
  64. folium.CircleMarker(location=[meta["origin"][1], meta["origin"][0]], radius=4, color="black", fill=True).add_to(m)
  65. for _, row in df_active.iterrows():
  66. px_x = (row['x'] * meta["pixel_ratio"]) + meta["origin"][0]
  67. px_y = meta["origin"][1] - (row['y'] * meta["pixel_ratio"])
  68. mac_label = str(row['mac'])[-5:]
  69. folium.CircleMarker(
  70. location=[px_y, px_x], radius=m_size, color="blue",
  71. fill=True, fill_color="cyan", fill_opacity=0.8
  72. ).add_to(m)
  73. folium.Marker(
  74. location=[px_y, px_x],
  75. icon=folium.DivIcon(html=f"""<div style="font-family: sans-serif; color: black; font-weight: bold; font-size: {int(m_size*1.2)}pt; width: 80px; transform: translate({m_size+2}px, -{m_size+2}px);">{mac_label}</div>""")
  76. ).add_to(m)
  77. st_folium(m, height=700, width=None, key=f"inf_map_v24_{floor_id}", use_container_width=True)
  78. # --- 3. TABELLA RIEPILOGO COMPLETA ---
  79. if not df_infer.empty:
  80. st.subheader("Dettaglio Dispositivi (Tutti i Piani)")
  81. # Aggiunta colonna Z per chiarezza
  82. st.dataframe(df_infer[['mac', 'z', 'x', 'y']], use_container_width=True)