diff --git a/repeater/web/http_server.py b/repeater/web/http_server.py index 8457db6..817be27 100644 --- a/repeater/web/http_server.py +++ b/repeater/web/http_server.py @@ -66,206 +66,11 @@ class StatsApp: # Create nested API object for routing self.api = APIEndpoints(stats_getter, send_advert_func, self.config, event_loop, daemon_instance, config_path) - # Load template on init - if template_dir: - template_path = os.path.join(template_dir, "dashboard.html") - try: - with open(template_path, "r") as f: - self.dashboard_template = f.read() - logger.info(f"Loaded template from {template_path}") - except FileNotFoundError: - logger.error(f"Template not found: {template_path}") - @cherrypy.expose - def index(self): - """Serve dashboard HTML.""" - return self._serve_template("dashboard.html") - - @cherrypy.expose - def neighbors(self): - """Serve neighbors page.""" - return self._serve_template("neighbors.html") - - @cherrypy.expose - def statistics(self): - """Serve statistics page.""" - return self._serve_template("statistics.html") - - @cherrypy.expose - def configuration(self): - """Serve configuration page.""" - return self._serve_template("configuration.html") - - @cherrypy.expose - def logs(self): - """Serve logs page.""" - return self._serve_template("logs.html") - - @cherrypy.expose - def help(self): - """Serve help documentation.""" - return self._serve_template("help.html") - - @cherrypy.expose - def cad_calibration(self): - """Serve CAD calibration page.""" - return self._serve_template("cad-calibration.html") - - def _serve_template(self, template_name: str): - """Serve HTML template with stats.""" - if not self.template_dir: - return "
Template directory not configured
" - - if not self.dashboard_template: - return "Template not loaded
" - - try: - - template_path = os.path.join(self.template_dir, template_name) - with open(template_path, "r") as f: - template_content = f.read() - - nav_path = os.path.join(self.template_dir, "nav.html") - nav_content = "" - try: - with open(nav_path, "r") as f: - nav_content = f.read() - except FileNotFoundError: - logger.warning(f"Navigation template not found: {nav_path}") - - stats = self.stats_getter() if self.stats_getter else {} - - if "uptime_seconds" not in stats or not isinstance( - stats.get("uptime_seconds"), (int, float) - ): - stats["uptime_seconds"] = 0 - - # Calculate uptime in hours - uptime_seconds = stats.get("uptime_seconds", 0) - uptime_hours = int(uptime_seconds // 3600) if uptime_seconds else 0 - - # Determine current page for nav highlighting - page_map = { - "dashboard.html": "dashboard", - "neighbors.html": "neighbors", - "statistics.html": "statistics", - "configuration.html": "configuration", - "cad-calibration.html": "cad-calibration", - "logs.html": "logs", - "help.html": "help", - } - current_page = page_map.get(template_name, "") - - # Prepare basic substitutions - html = template_content - html = html.replace("{{ node_name }}", str(self.node_name)) - html = html.replace("{{ last_updated }}", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - html = html.replace("{{ page }}", current_page) - - # Replace navigation placeholder with actual nav content - if "" in html: - nav_substitutions = nav_content - nav_substitutions = nav_substitutions.replace( - "{{ node_name }}", str(self.node_name) - ) - nav_substitutions = nav_substitutions.replace("{{ pub_key }}", str(self.pub_key)) - nav_substitutions = nav_substitutions.replace( - "{{ last_updated }}", datetime.now().strftime("%Y-%m-%d %H:%M:%S") - ) - - # Handle active state for nav items - nav_substitutions = nav_substitutions.replace( - "{{ ' active' if page == 'dashboard' else '' }}", - " active" if current_page == "dashboard" else "", - ) - nav_substitutions = nav_substitutions.replace( - "{{ ' active' if page == 'neighbors' else '' }}", - " active" if current_page == "neighbors" else "", - ) - nav_substitutions = nav_substitutions.replace( - "{{ ' active' if page == 'statistics' else '' }}", - " active" if current_page == "statistics" else "", - ) - nav_substitutions = nav_substitutions.replace( - "{{ ' active' if page == 'configuration' else '' }}", - " active" if current_page == "configuration" else "", - ) - nav_substitutions = nav_substitutions.replace( - "{{ ' active' if page == 'logs' else '' }}", - " active" if current_page == "logs" else "", - ) - nav_substitutions = nav_substitutions.replace( - "{{ ' active' if page == 'help' else '' }}", - " active" if current_page == "help" else "", - ) - - html = html.replace("", nav_substitutions) - - # Build packets table HTML for dashboard - if template_name == "dashboard.html": - recent_packets = stats.get("recent_packets", []) - packets_table = "" - - if recent_packets: - for pkt in recent_packets[-20:]: # Last 20 packets - time_obj = datetime.fromtimestamp(pkt.get("timestamp", 0)) - time_str = time_obj.strftime("%H:%M:%S") - pkt_type = PAYLOAD_TYPES.get( - pkt.get("type", 0), f"0x{pkt.get('type', 0): 02x}" - ) - route_type = pkt.get("route", 0) - route = ROUTE_TYPES.get(route_type, f"UNKNOWN_{route_type}") - status = "OK TX" if pkt.get("transmitted") else "WAIT" - - # Get proper CSS class for route type - route_class = route.lower().replace("_", "-") - snr_val = pkt.get("snr", 0.0) - score_val = pkt.get("score", 0) - delay_val = pkt.get("tx_delay_ms", 0) - - packets_table += ( - "{str(e)}
" + # @cherrypy.expose + # def index(self): + # """Serve dashboard HTML.""" + # return self._serve_template("dashboard.html") class HTTPStatsServer: diff --git a/repeater/web/stats-data-collection.md b/repeater/web/stats-data-collection.md deleted file mode 100644 index b520fd8..0000000 --- a/repeater/web/stats-data-collection.md +++ /dev/null @@ -1,1929 +0,0 @@ -# Stats Data Collection & Charting Examples - -This document provides examples for using the pyMC_Repeater API endpoints to create charts and visualizations for network monitoring. - -## Available API Endpoints - -### Basic Statistics -- `/api/packet_stats` - Get packet statistics for a time period -- `/api/recent_packets` - Get recent packets with all fields -- `/api/filtered_packets` - Get packets with filtering options -- `/api/packet_by_hash` - Get specific packet by hash - -### Time Series Data for Charts -- `/api/packet_type_graph_data` - Get packet type data for graphing -- `/api/metrics_graph_data` - Get metrics data for graphing -- `/api/packet_type_stats` - Get packet type distribution -- `/api/rrd_data` - Get raw RRD time series data - -### Noise Floor Monitoring -- `/api/noise_floor_history` - Get noise floor history (SQLite data) -- `/api/noise_floor_stats` - Get noise floor statistics -- `/api/noise_floor_chart_data` - Get noise floor RRD chart data - -## Noise Floor API Examples - -### Fetch Noise Floor History -```javascript -// Get last 24 hours of noise floor data from SQLite -async function fetchNoiseFloorHistory() { - const response = await fetch('/api/noise_floor_history?hours=24'); - const result = await response.json(); - - if (result.success) { - const history = result.data.history; - console.log(`Found ${history.length} noise floor measurements`); - - // Each record: { timestamp: 1234567890.123, noise_floor_dbm: -95.5 } - history.forEach(record => { - console.log(`${new Date(record.timestamp * 1000).toISOString()}: ${record.noise_floor_dbm} dBm`); - }); - } else { - console.error('Error:', result.error); - } -} -``` - -### Fetch Noise Floor Statistics -```javascript -// Get statistical summary of noise floor data -async function fetchNoiseFloorStats() { - const response = await fetch('/api/noise_floor_stats?hours=24'); - const result = await response.json(); - - if (result.success) { - const stats = result.data.stats; - console.log('Noise Floor Statistics:'); - console.log(`Count: ${stats.count}`); - console.log(`Average: ${stats.average?.toFixed(1)} dBm`); - console.log(`Min: ${stats.min} dBm`); - console.log(`Max: ${stats.max} dBm`); - console.log(`Std Dev: ${stats.std_dev?.toFixed(2)}`); - } -} -``` - -### Fetch Chart-Ready Noise Floor Data -```javascript -// Get RRD-based noise floor data optimized for charts -async function fetchNoiseFloorChartData() { - const response = await fetch('/api/noise_floor_chart_data?hours=24'); - const result = await response.json(); - - if (result.success) { - const chartData = result.data.chart_data; - - // Data points: [[timestamp_ms, noise_floor_dbm], ...] - chartData.data_points.forEach(point => { - const [timestamp_ms, value] = point; - console.log(`${new Date(timestamp_ms).toISOString()}: ${value} dBm`); - }); - - console.log('Statistics:', chartData.statistics); - } -} -``` - -## Noise Floor Chart Examples - -### 1. Noise Floor Time Series (Chart.js) - -```html - - - - - - - - - - - - -``` - -### 2. Noise Floor Distribution Histogram - -```html - - - - - - - - - - - -``` - -### 3. Real-time Noise Floor Monitor - -```html - - - - - - - - -| Time | -Type | -Route | -RSSI | -SNR | -Status | -
|---|---|---|---|---|---|
| {{ formatTime(packet.timestamp) }} | -{{ getPacketTypeName(packet.type) }} | -{{ packet.route }} | -{{ packet.rssi }} | -{{ packet.snr?.toFixed(1) }} | -- - {{ packet.transmitted ? 'TX' : 'DROP' }} - - | -