|
- import streamlit as st
- import yaml
- import subprocess
- import os
- import web_status # Nuovo modulo
-
- # --- COSTANTI E UTILS (Invariati) ---
- CONFIG_PATH = os.environ.get("CONFIG") or os.environ.get("CONFIG_FILE") or "/config/config.yaml"
- SECRETS_PATH = os.environ.get("SECRETS_FILE") or "/config/secrets.yaml"
- LOG_FILE = "/tmp/main_process.log"
- STATE_FILE = "/data/.web_state"
-
- def load_yaml(path):
- if not os.path.exists(path): return {}
- with open(path, 'r') as f: return yaml.safe_load(f) or {}
-
- def save_yaml(path, data):
- with open(path, 'w') as f: yaml.dump(data, f, default_flow_style=False)
-
- cfg = load_yaml(CONFIG_PATH)
-
- # --- CONFIGURAZIONE PAGINA E MENU ABOUT ---
- st.set_page_config(
- page_title="BLE Localizer Manager",
- layout="wide",
- initial_sidebar_state="auto",
- menu_items={
- 'Get Help': None,
- 'Report a bug': None,
- 'About': "🛰️BLE AI Localizer - Suite\nSistema professionale di posizionamento BLE."
- }
- )
-
- st.title("🛰️ BLE AI Localizer - Suite") # Forza il titolo qui
-
- # --- CSS OTTIMIZZATO (Sidebar Fina + Mobile) ---
- st.markdown("""
- <style>
- [data-testid="stSidebar"] { min-width: 200px !important; max-width: 230px !important; }
- [data-testid="stSidebar"] .stButton > button { width: 100%; height: 2.5rem; border-radius: 8px; font-size: 0.9rem !important; }
- .stTextInput input, .stSelectbox div, .stNumberInput input { height: 3rem !important; }
- button[data-baseweb="tab"] { height: 3.5rem !important; }
- .stButton > button { border-radius: 10px; font-weight: bold; }
- </style>
- """, unsafe_allow_html=True)
-
- if "main_login_user" not in st.session_state:
- st.session_state["main_login_user"] = ""
-
- # --- LOGIN (Tua versione originale) ---
- if "password_correct" not in st.session_state:
- st.session_state["password_correct"] = False
-
- if not st.session_state["password_correct"]:
- user = st.text_input("Username", key="main_login_user")
- pw = st.text_input("Password", type="password", key="main_login_pw")
- if st.button("ACCEDI"):
- if user == os.environ.get("UI_USER", "admin") and pw == os.environ.get("UI_PASSWORD", "password"):
- st.session_state["password_correct"] = True
- st.rerun()
- else: st.error("Credenziali errate")
- st.stop()
-
- # Import moduli dopo login
- from map_manager import show_mapper
- from web_gateway import show_gateway_manager
- from web_beacon import show_beacon_manager
- import web_training_data
- import web_inference
-
- def is_proc_alive():
- return st.session_state.get('proc') is not None and st.session_state.proc.poll() is None
-
- # Auto-riavvio (Tua logica)
- if os.path.exists(STATE_FILE):
- with open(STATE_FILE, "r") as f:
- if f.read().strip() == "running" and not is_proc_alive():
- log_out = open(LOG_FILE, "a")
- st.session_state.proc = subprocess.Popen(
- ["python", "-u", "-m", "app.main"],
- env={**os.environ, "PYTHONUNBUFFERED": "1", "CONFIG": CONFIG_PATH},
- stdout=log_out, stderr=subprocess.STDOUT, text=True
- )
-
- # --- SIDEBAR ---
- with st.sidebar:
- st.header("Stato Sistema")
- alive = is_proc_alive()
- if not alive:
- if st.button("▶️ AVVIA MAIN"):
- if os.path.exists(LOG_FILE): os.remove(LOG_FILE)
- st.session_state.proc = subprocess.Popen(
- ["python", "-u", "-m", "app.main"],
- env={**os.environ, "PYTHONUNBUFFERED": "1", "CONFIG": CONFIG_PATH},
- stdout=open(LOG_FILE, "w"), stderr=subprocess.STDOUT, text=True
- )
- with open(STATE_FILE, "w") as f: f.write("running")
- st.rerun()
- else:
- if st.button("⏹️ FERMA MAIN"):
- st.session_state.proc.terminate()
- st.session_state.proc = None
- with open(STATE_FILE, "w") as f: f.write("stopped")
- st.rerun()
-
- st.write(f"Stato: {':green[Running]' if alive else ':red[Stopped]'}")
- st.write(f"Modo: **{cfg.get('mode', 'N/D').upper()}**")
- if st.button("LOGOUT"):
- st.session_state["password_correct"] = False
- st.rerun()
-
- st.divider()
- st.caption("🚦 LIVE STATUS")
- c1, c2, c3 = st.columns(3)
- c1.markdown("`MQTT` 🟢")
- c2.markdown("`GWs` 🟢")
- c3.markdown("`API` 🟢")
-
- # --- TABS (Incluso il nuovo tab Stato) ---
- tab_log, tab_cfg, tab_sec, tab_gw, tab_beac, tab_map, tab_data, tab_infer, tab_status = st.tabs([
- "📜 Log", "⚙️ Config", "🔑 Secrets", "🌐 Gateway", "🏷️ Beacon", "🗺️ Mappa", "📂 Dati", "🤖 Inferenza", "🖥️ Stato"
- ])
-
- with tab_status:
- import web_status
- # Carica i segreti e passali alla funzione
- sec_data = load_yaml(SECRETS_PATH)
- web_status.show_system_status(cfg, sec_data)
-
- with tab_cfg:
- # RIPRISTINATA TUTTA LA TUA LOGICA ORIGINALE
- c_cfg = load_yaml(CONFIG_PATH)
- st.subheader("🚀 Stato Operativo")
- mode_options = ["infer", "train", "collect_train"]
- new_mode = st.selectbox("Modalità:", mode_options, index=mode_options.index(c_cfg.get('mode', 'infer')))
-
- with st.expander("📊 Parametri Raccolta Dati", expanded=True):
- p_cfg = c_cfg.get('collect_train', {})
- col_a, col_b = st.columns(2)
- win_sec = col_a.number_input("Finestra (sec)", value=int(p_cfg.get('window_seconds', 30)))
- min_gw = col_a.number_input("Min. Gateway", value=int(p_cfg.get('min_non_nan', 3)))
- rssi_min = col_b.slider("RSSI Min", -120, -50, int(p_cfg.get('rssi_min', -110)))
- rssi_max = col_b.slider("RSSI Max", -40, 0, int(p_cfg.get('rssi_max', -25)))
- outlier = col_b.selectbox("Outlier", ["mad", "iqr", "none"], index=0)
-
- with st.expander("📡 MQTT & 🌐 API", expanded=False):
- m_cfg = c_cfg.get('mqtt', {})
- mq_host = st.text_input("Broker", value=m_cfg.get('host', '127.0.0.1'))
- mq_port = st.number_input("Porta", value=int(m_cfg.get('port', 1883)))
- a_cfg = c_cfg.get('api', {})
- t_url = st.text_input("Token URL", value=a_cfg.get('token_url', ''))
- v_tls = st.checkbox("Verify TLS", value=a_cfg.get('verify_tls', False))
-
- with st.expander("🛠️ Expert Mode (YAML)", expanded=False):
- cfg_text = st.text_area("Edit manuale", yaml.dump(c_cfg), height=300)
-
- if st.button("💾 SALVA TUTTO"):
- try:
- final_cfg = yaml.safe_load(cfg_text)
- final_cfg['mode'] = new_mode
- # Aggiorna con i valori dei widget per sicurezza
- if 'collect_train' not in final_cfg: final_cfg['collect_train'] = {}
- final_cfg['collect_train'].update({'window_seconds': win_sec, 'rssi_min': rssi_min})
- save_yaml(CONFIG_PATH, final_cfg)
- st.success("Salvato!")
- except Exception as e: st.error(e)
-
- with tab_sec:
- sec = load_yaml(SECRETS_PATH)
- sec_edit = st.text_area("Secrets YAML", yaml.dump(sec), height=200)
- if st.button("SALVA SECRETS"):
- save_yaml(SECRETS_PATH, yaml.safe_load(sec_edit))
- st.success("ApiToken salvati!")
-
- with tab_log:
- if os.path.exists(LOG_FILE):
- with open(LOG_FILE, "r") as f: st.code("".join(f.readlines()[-50:]))
- if st.button("🔄 AGGIORNA LOG"): st.rerun()
-
- with tab_map: show_mapper(cfg)
- with tab_gw: show_gateway_manager(load_yaml(CONFIG_PATH))
- with tab_beac: show_beacon_manager(load_yaml(CONFIG_PATH))
- with tab_data: web_training_data.show_training_data_manager(cfg)
- with tab_infer: web_inference.show_inference_page(cfg)
|