From 8497469d2e97a4f60c14c20ef0579375a80ccf84 Mon Sep 17 00:00:00 2001 From: Colonel Panic <90460753+colonelpanichacks@users.noreply.github.com> Date: Thu, 28 Aug 2025 20:21:54 -0400 Subject: [PATCH] Add files via upload --- api/flockyou.py | 8 +++ api/requirements.txt | 4 +- api/templates/index.html | 140 +++++++++++++++++++++++++++++++++++---- 3 files changed, 138 insertions(+), 14 deletions(-) diff --git a/api/flockyou.py b/api/flockyou.py index c7d36ac..ea3d49e 100644 --- a/api/flockyou.py +++ b/api/flockyou.py @@ -138,10 +138,18 @@ def gps_reader(): try: line = serial_connection.readline().decode('utf-8', errors='ignore') if line: + # Send raw GPS data to serial terminal + safe_socket_emit('serial_data', f"GPS: {line.strip()}", room='serial_terminal') + parsed = parse_nmea_sentence(line) if parsed: gps_data = parsed safe_socket_emit('gps_update', parsed) + + # Also send parsed GPS data to terminal + if parsed.get('fix_quality') > 0: + gps_info = f"GPS Fix: {parsed.get('latitude', 'N/A')}, {parsed.get('longitude', 'N/A')} - {parsed.get('satellites', 0)} satellites" + safe_socket_emit('serial_data', gps_info, room='serial_terminal') except Exception as e: print(f"GPS read error: {e}") with connection_lock: diff --git a/api/requirements.txt b/api/requirements.txt index 2ea9410..1d8ff29 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,7 +1,7 @@ -Flask==2.3.3 +Flask==2.2.5 Flask-SocketIO==5.3.6 python-socketio==5.8.0 python-engineio==4.7.1 pyserial==3.5 -Werkzeug==2.3.7 +Werkzeug==2.2.3 requests==2.31.0 diff --git a/api/templates/index.html b/api/templates/index.html index b33447c..7a20994 100644 --- a/api/templates/index.html +++ b/api/templates/index.html @@ -86,6 +86,37 @@ gap: 0.5rem; } + .port-controls { + display: flex; + align-items: center; + gap: 0.25rem; + } + + .refresh-btn { + background: linear-gradient(135deg, #6b7280 0%, #9ca3af 100%); + color: white; + font-weight: 600; + border: 1px solid #4b5563; + padding: 0.3rem 0.5rem; + font-size: 0.8rem; + min-width: auto; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + } + + .refresh-btn:hover { + background: linear-gradient(135deg, #9ca3af 0%, #d1d5db 100%); + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(107, 114, 128, 0.4); + } + + .refresh-btn:active { + transform: translateY(0); + } + .header-buttons { display: flex; align-items: center; @@ -605,6 +636,11 @@ 100% { opacity: 1; } } + @keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } + } + .main-content { padding: 1rem; width: 100%; @@ -914,9 +950,12 @@
- +
+ + +
@@ -924,9 +963,12 @@
- +
+ + +
@@ -1036,6 +1078,7 @@ let detections = []; let gpsConnected = false; const max_reconnect_attempts = 5; + let userInteractingWithPorts = false; // Flag to prevent auto-refresh interference // Initialize document.addEventListener('DOMContentLoaded', function() { @@ -1049,11 +1092,14 @@ // Periodic status refresh every 5 seconds setInterval(loadStatus, 5000); - // Periodic port refresh every 10 seconds + // Periodic port refresh every 30 seconds (reduced from 10 seconds) setInterval(function() { - loadFlockPorts(); - loadGpsPorts(); - }, 10000); + // Only refresh if user is not actively interacting with ports + if (!userInteractingWithPorts) { + loadFlockPorts(); + loadGpsPorts(); + } + }, 30000); // Connection health check every 30 seconds setInterval(function() { @@ -1073,6 +1119,22 @@ document.getElementById('disconnectFlockBtn').addEventListener('click', disconnectFlock); document.getElementById('connectGpsBtn').addEventListener('click', connectGps); document.getElementById('disconnectGpsBtn').addEventListener('click', disconnectGps); + + // Add event listeners for port dropdowns to prevent auto-refresh interference + const flockSelect = document.getElementById('flockDeviceSelect'); + const gpsSelect = document.getElementById('gpsPortSelect'); + + flockSelect.addEventListener('focus', () => { userInteractingWithPorts = true; }); + flockSelect.addEventListener('blur', () => { + setTimeout(() => { userInteractingWithPorts = false; }, 1000); + }); + flockSelect.addEventListener('change', () => { userInteractingWithPorts = true; }); + + gpsSelect.addEventListener('focus', () => { userInteractingWithPorts = true; }); + gpsSelect.addEventListener('blur', () => { + setTimeout(() => { userInteractingWithPorts = false; }, 1000); + }); + gpsSelect.addEventListener('change', () => { userInteractingWithPorts = true; }); } function loadDetections() { @@ -1090,11 +1152,24 @@ } function loadGpsPorts() { + const select = document.getElementById('gpsPortSelect'); + const refreshBtn = document.getElementById('refreshGpsPortsBtn'); + const currentSelection = select.value; // Preserve current selection + + // Show loading state + refreshBtn.textContent = '⟳'; + refreshBtn.style.animation = 'spin 1s linear infinite'; + + // Add a subtle visual indicator if this is an auto-refresh + if (!userInteractingWithPorts) { + refreshBtn.title = 'Auto-refreshing ports...'; + } + fetch('/api/gps/ports') .then(response => response.json()) .then(ports => { console.log('GPS ports loaded:', ports.length); - const select = document.getElementById('gpsPortSelect'); + select.innerHTML = ''; ports.forEach(port => { const option = document.createElement('option'); @@ -1102,18 +1177,45 @@ option.textContent = `${port.device} - ${port.description}`; select.appendChild(option); }); + + // Restore selection if it still exists in the new list + if (currentSelection && ports.some(p => p.device === currentSelection)) { + select.value = currentSelection; + } + + // Reset refresh button + refreshBtn.textContent = '↻'; + refreshBtn.style.animation = ''; + refreshBtn.title = 'Refresh ports'; }) .catch(error => { console.error('Error loading GPS ports:', error); + // Reset refresh button on error + refreshBtn.textContent = '↻'; + refreshBtn.style.animation = ''; + refreshBtn.title = 'Refresh ports'; }); } function loadFlockPorts() { + const select = document.getElementById('flockDeviceSelect'); + const refreshBtn = document.getElementById('refreshFlockPortsBtn'); + const currentSelection = select.value; // Preserve current selection + + // Show loading state + refreshBtn.textContent = '⟳'; + refreshBtn.style.animation = 'spin 1s linear infinite'; + + // Add a subtle visual indicator if this is an auto-refresh + if (!userInteractingWithPorts) { + refreshBtn.title = 'Auto-refreshing ports...'; + } + fetch('/api/flock/ports') .then(response => response.json()) .then(ports => { console.log('Flock ports loaded:', ports.length); - const select = document.getElementById('flockDeviceSelect'); + select.innerHTML = ''; ports.forEach(port => { const option = document.createElement('option'); @@ -1121,9 +1223,23 @@ option.textContent = `${port.device} - ${port.description}`; select.appendChild(option); }); + + // Restore selection if it still exists in the new list + if (currentSelection && ports.some(p => p.device === currentSelection)) { + select.value = currentSelection; + } + + // Reset refresh button + refreshBtn.textContent = '↻'; + refreshBtn.style.animation = ''; + refreshBtn.title = 'Refresh ports'; }) .catch(error => { console.error('Error loading Flock ports:', error); + // Reset refresh button on error + refreshBtn.textContent = '↻'; + refreshBtn.style.animation = ''; + refreshBtn.title = 'Refresh ports'; }); }