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(""" """, 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)