Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

179 righe
6.4 KiB

  1. import streamlit as st
  2. import yaml
  3. import subprocess
  4. import os
  5. import time
  6. import signal
  7. import web_status
  8. import psutil
  9. # --- 1. CONFIGURAZIONE PAGINA ---
  10. st.set_page_config(
  11. page_title="BLE Localizer Manager",
  12. layout="wide",
  13. initial_sidebar_state="auto"
  14. )
  15. # --- 2. CONFIGURAZIONE PERCORSI ---
  16. REAL_CONFIG_PATH = "/config/config.yaml"
  17. if not os.path.exists(REAL_CONFIG_PATH):
  18. REAL_CONFIG_PATH = "/app/config/config.yaml"
  19. LOG_FILE = "/tmp/main_process.log"
  20. def load_yaml(path):
  21. if not os.path.exists(path): return {}
  22. with open(path, 'r', encoding='utf-8') as f: return yaml.safe_load(f) or {}
  23. def save_yaml(path, data):
  24. with open(path, 'w', encoding='utf-8') as f: yaml.dump(data, f, default_flow_style=False)
  25. cfg = load_yaml(REAL_CONFIG_PATH)
  26. def stop_core_engine():
  27. for proc in psutil.process_iter(['cmdline']):
  28. try:
  29. cmd = proc.info['cmdline']
  30. if cmd and ('app.main' in ' '.join(cmd) or 'main.py' in ' '.join(cmd)):
  31. proc.terminate()
  32. proc.wait(timeout=2)
  33. except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.TimeoutExpired):
  34. try: proc.kill()
  35. except: pass
  36. def start_core_engine():
  37. env = os.environ.copy()
  38. env["CONFIG"] = REAL_CONFIG_PATH
  39. env["PYTHONPATH"] = "/app"
  40. with open(LOG_FILE, "a") as log_f:
  41. subprocess.Popen(
  42. ["python3", "-m", "app.main"],
  43. env=env,
  44. cwd="/app",
  45. stdout=log_f,
  46. stderr=log_f,
  47. start_new_session=True
  48. )
  49. # --- 3. TITOLO FISSO ---
  50. st.title("🛰️ BLE AI Localizer - Suite")
  51. # --- 4. LOGICA LOGIN ---
  52. if "password_correct" not in st.session_state:
  53. st.session_state["password_correct"] = False
  54. if not st.session_state["password_correct"]:
  55. user = st.text_input("Username")
  56. pw = st.text_input("Password", type="password")
  57. if st.button("ACCEDI"):
  58. if user == os.environ.get("UI_USER", "admin") and pw == os.environ.get("UI_PASSWORD", "password"):
  59. st.session_state["password_correct"] = True
  60. st.rerun()
  61. else:
  62. st.error("Credenziali errate")
  63. st.stop()
  64. # --- 5. AUTO-START ---
  65. if not web_status.is_main_running():
  66. if "core_auto_launched" not in st.session_state:
  67. start_core_engine()
  68. st.session_state["core_auto_launched"] = True
  69. time.sleep(1)
  70. st.rerun()
  71. # --- 6. SIDEBAR: CONTROLLI SISTEMA ---
  72. with st.sidebar:
  73. st.header("🛠️ Core Control")
  74. is_running = web_status.is_main_running()
  75. if is_running:
  76. st.markdown("STATO: :green[**CORE ATTIVO**]")
  77. else:
  78. st.markdown("STATO: :red[**CORE FERMO**]")
  79. if st.button("🚀 RIAVVIA CORE" if is_running else "▶️ AVVIA CORE", use_container_width=True):
  80. with st.spinner("Gestione Core..."):
  81. stop_core_engine()
  82. time.sleep(1)
  83. start_core_engine()
  84. time.sleep(2)
  85. st.rerun()
  86. st.divider()
  87. # --- GESTIONE MQTT RAW (DINAMICA DA CONFIG) ---
  88. m_cfg = cfg.get('mqtt', {})
  89. m_host = m_cfg.get('host', '127.0.0.1')
  90. m_port = m_cfg.get('port', 1883)
  91. m_proto = m_cfg.get('protocol', 'mqttv311')
  92. # Estraiamo la parola chiave dal topic (es: 'publish_out' da 'publish_out/#')
  93. m_filter = m_cfg.get('topic', 'publish_out').split('/')[0]
  94. RAW_LOG_DIR = cfg.get('paths', {}).get('mqtt_raw_dir', '/data/mqtt_raw/')
  95. os.makedirs(RAW_LOG_DIR, exist_ok=True)
  96. raw_active = st.toggle("🔴 MQTT RAW RECORDING", value=st.session_state.get("mqtt_logging", False))
  97. if raw_active and not st.session_state.get("mqtt_logging"):
  98. dt = time.strftime("%Y%m%d_%H%M%S")
  99. filepath = os.path.join(RAW_LOG_DIR, f"mqtt_raw_{dt}.log")
  100. # Comando generato dinamicamente dai parametri YAML
  101. cmd = f"stdbuf -oL mosquitto_sub -v -h {m_host} -p {m_port} -t '#' -V {m_proto} | stdbuf -oL grep '{m_filter}' > {filepath}"
  102. proc = subprocess.Popen(cmd, shell=True, preexec_fn=os.setsid)
  103. st.session_state["mqtt_logging"] = True
  104. st.session_state["mqtt_proc_pid"] = proc.pid
  105. st.success(f"Log avviato: {m_host}")
  106. if not raw_active and st.session_state.get("mqtt_logging"):
  107. try:
  108. os.killpg(os.getpgid(st.session_state["mqtt_proc_pid"]), signal.SIGTERM)
  109. except:
  110. pass
  111. st.session_state["mqtt_logging"] = False
  112. # --- 7. IMPORT MODULI E TABS ---
  113. from map_manager import show_mapper
  114. from web_gateway import show_gateway_manager
  115. from web_beacon import show_beacon_manager
  116. from web_test_inference import show_test_inference
  117. import web_training_data
  118. import web_inference
  119. tab_log, tab_cfg, tab_sec, tab_gw, tab_beac, tab_map, tab_model, tab_infer_test, tab_infer, tab_status = st.tabs([
  120. "📜 Log", "⚙️ Config", "🔑 Secrets", "🌐 Gateway", "🏷️ Beacon", "📍 Rilevamento", "🧠 Modello", "🧪 InferTest", "🤖 Inferenza", "🖥️ Stato"
  121. ])
  122. with tab_log:
  123. col_l1, col_l2 = st.columns(2)
  124. if col_l1.button("🔄 AGGIORNA LOG", use_container_width=True): st.rerun()
  125. if col_l2.button("🗑️ AZZERA LOG", use_container_width=True):
  126. try:
  127. with open(LOG_FILE, "w") as f: f.write(f"--- Log resettato il {time.strftime('%Y-%m-%d %H:%M:%S')} ---\n")
  128. st.success("Log azzerato!"); time.sleep(1); st.rerun()
  129. except: st.error("Errore pulizia log")
  130. if os.path.exists(LOG_FILE):
  131. with open(LOG_FILE, "r") as f: st.code("".join(f.readlines()[-100:]))
  132. with tab_cfg:
  133. st.subheader("🚀 Configurazione Operativa")
  134. cfg_text = st.text_area("Expert Mode (YAML)", yaml.dump(cfg), height=400)
  135. if st.button("💾 SALVA CONFIG"):
  136. save_yaml(REAL_CONFIG_PATH, yaml.safe_load(cfg_text)); st.success("Salvato!")
  137. with tab_sec:
  138. CURR_SECRETS = os.environ.get("SECRETS_FILE") or "/config/secrets.yaml"
  139. sec = load_yaml(CURR_SECRETS)
  140. sec_edit = st.text_area("Secrets YAML", yaml.dump(sec), height=200)
  141. if st.button("SALVA SECRETS"):
  142. save_yaml(CURR_SECRETS, yaml.safe_load(sec_edit)); st.success("OK!")
  143. with tab_gw: show_gateway_manager(cfg)
  144. with tab_beac: show_beacon_manager(cfg)
  145. with tab_map: show_mapper(cfg)
  146. with tab_model: web_training_data.show_training_data_manager(cfg)
  147. with tab_infer_test: show_test_inference(cfg)
  148. with tab_infer: web_inference.show_inference_page(cfg)
  149. with tab_status:
  150. sec_data = load_yaml(os.environ.get("SECRETS_FILE") or "/config/secrets.yaml")
  151. web_status.show_system_status(cfg, sec_data)