No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

179 líneas
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)