選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 

103 行
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)