import json import time import threading import sqlite3 from flask import Flask, jsonify, request import paho.mqtt.client as mqtt # ========= CONFIGURATION ========= MQTT_BROKER = 'localhost' MQTT_PORT = 1883 MQTT_TOPICS = ['lte_message', 'nr_message', 'wifi_message'] MQTT_CONTROL_TOPIC = 'test_control_command' DB_FILE = 'roadtest.db' WEB_PORT = 5000 status_context = { 'testing': False, 'path_id': None, 'start_time': None } # ========= DATABASE SETUP ========= def init_db(): conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS test_paths ( path_id TEXT PRIMARY KEY, start_time TEXT, end_time TEXT, remark TEXT )''') cursor.execute('''CREATE TABLE IF NOT EXISTS network_measurements ( id INTEGER PRIMARY KEY AUTOINCREMENT, path_id TEXT, message_type TEXT, data_json TEXT, ts TEXT )''') conn.commit() conn.close() # ========= MQTT CALLBACKS ========= def on_connect(client, userdata, flags, rc): print(f"[MQTT] Connected with result code {rc}") client.subscribe(MQTT_CONTROL_TOPIC) for topic in MQTT_TOPICS: client.subscribe(topic) def on_message(client, userdata, msg): topic = msg.topic payload = msg.payload.decode('utf-8') if topic == MQTT_CONTROL_TOPIC: handle_control_message(payload) return if not status_context['testing']: return # Save measurement try: data = json.loads(payload) insert_measurement( status_context['path_id'], topic, payload, time.strftime("%Y-%m-%d %H:%M:%S") ) print(f"[DATA] Saved data on {topic} for path {status_context['path_id']}") except Exception as e: print(f"[ERROR] Failed to parse or save message: {e}") def handle_control_message(payload): try: cmd = json.loads(payload) action = cmd.get('action') if action == 'start': status_context['testing'] = True status_context['path_id'] = cmd['path_id'] status_context['start_time'] = time.strftime("%Y-%m-%d %H:%M:%S") insert_path(status_context['path_id'], status_context['start_time']) print(f"[CONTROL] Test started with path_id={status_context['path_id']}") elif action == 'stop': status_context['testing'] = False update_path_endtime(status_context['path_id'], time.strftime("%Y-%m-%d %H:%M:%S")) print(f"[CONTROL] Test ended for path_id={status_context['path_id']}") status_context['path_id'] = None except Exception as e: print(f"[ERROR] Control message error: {e}") # ========= DATABASE FUNCTIONS ========= def insert_path(path_id, start_time): conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute('INSERT OR REPLACE INTO test_paths (path_id, start_time) VALUES (?, ?)', (path_id, start_time)) conn.commit() conn.close() def update_path_endtime(path_id, end_time): conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute('UPDATE test_paths SET end_time = ? WHERE path_id = ?', (end_time, path_id)) conn.commit() conn.close() def insert_measurement(path_id, message_type, data_json, ts): conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute('''INSERT INTO network_measurements (path_id, message_type, data_json, ts) VALUES (?, ?, ?, ?)''', (path_id, message_type, data_json, ts)) conn.commit() conn.close() # ========= WEB SERVER ========= app = Flask(__name__) @app.route('/status', methods=['GET']) def get_status(): return jsonify({ 'testing': status_context['testing'], 'path_id': status_context['path_id'], 'start_time': status_context['start_time'] }) @app.route('/paths', methods=['GET']) def list_paths(): conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute('SELECT * FROM test_paths ORDER BY start_time DESC') rows = cursor.fetchall() conn.close() return jsonify(rows) # ========= MAIN ========= def main(): init_db() mqtt_client = mqtt.Client() mqtt_client.on_connect = on_connect mqtt_client.on_message = on_message mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60) threading.Thread(target=app.run, kwargs={'port': WEB_PORT, 'debug': False, 'use_reloader': False}, daemon=True).start() print("[SYSTEM] Web UI running on http://localhost:5000") mqtt_client.loop_forever() if __name__ == '__main__': main()