Skip to main content
Glama
dashboard.html27 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>GeoFS MCP Dashboard</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; color: #333; } .container { max-width: 1200px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .panel { background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 20px; margin-bottom: 20px; } .full-width { grid-column: 1 / span 2; } h1, h2 { margin-top: 0; color: #2c3e50; } .map-container { height: 400px; width: 100%; border-radius: 8px; overflow: hidden; } #map { height: 100%; width: 100%; } .controls { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; } button { padding: 10px; background-color: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; } button:hover { background-color: #2980b9; } button:disabled { background-color: #95a5a6; cursor: not-allowed; } .status { padding: 10px; margin-bottom: 15px; border-radius: 4px; font-weight: bold; } .connected { background-color: #2ecc71; color: white; } .disconnected { background-color: #e74c3c; color: white; } .connecting { background-color: #f39c12; color: white; } .data-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; } .data-item { margin-bottom: 5px; } .data-label { font-weight: bold; } .code-block { background-color: #f8f9fa; border: 1px solid #ddd; border-radius: 4px; padding: 10px; font-family: monospace; white-space: pre-wrap; overflow-x: auto; max-height: 200px; overflow-y: auto; } .tabs { display: flex; margin-bottom: 15px; border-bottom: 1px solid #ddd; } .tab { padding: 10px 15px; cursor: pointer; border-bottom: 2px solid transparent; } .tab.active { border-bottom: 2px solid #3498db; font-weight: bold; } .tab-content { display: none; } .tab-content.active { display: block; } .flight-pattern-select { width: 100%; padding: 8px; margin-bottom: 10px; border-radius: 4px; border: 1px solid #ddd; } .log-container { background-color: #2c3e50; color: #ecf0f1; padding: 10px; border-radius: 4px; font-family: monospace; height: 200px; overflow-y: auto; margin-top: 10px; } .log-entry { margin-bottom: 5px; border-bottom: 1px solid #34495e; padding-bottom: 5px; } .log-time { color: #95a5a6; margin-right: 10px; } .log-info { color: #3498db; } .log-error { color: #e74c3c; } .log-success { color: #2ecc71; } .log-warning { color: #f39c12; } </style> </head> <body> <div class="container"> <div class="panel full-width"> <h1>GeoFS MCP Dashboard</h1> <div class="status" id="connection-status">Connecting to server...</div> </div> <div class="panel"> <h2>Flight Data</h2> <div class="data-grid"> <div class="data-item"> <div class="data-label">Latitude:</div> <div id="latitude">-</div> </div> <div class="data-item"> <div class="data-label">Longitude:</div> <div id="longitude">-</div> </div> <div class="data-item"> <div class="data-label">Altitude:</div> <div id="altitude">-</div> </div> <div class="data-item"> <div class="data-label">Heading:</div> <div id="heading">-</div> </div> <div class="data-item"> <div class="data-label">Airspeed:</div> <div id="airspeed">-</div> </div> <div class="data-item"> <div class="data-label">Vertical Speed:</div> <div id="vertical-speed">-</div> </div> </div> <div class="map-container"> <div id="map"></div> </div> </div> <div class="panel"> <h2>Controls</h2> <div class="tabs"> <div class="tab active" data-tab="basic-controls">Basic Controls</div> <div class="tab" data-tab="flight-patterns">Flight Patterns</div> <div class="tab" data-tab="logs">Logs</div> </div> <!-- Basic Controls Tab --> <div class="tab-content active" id="basic-controls"> <div class="controls"> <button id="get-data-btn">Get Flight Data</button> <button id="set-throttle-btn">Set Throttle (50%)</button> <button id="set-heading-btn">Set Heading (270°)</button> <button id="select-b787-btn">Select Boeing 787</button> </div> </div> <!-- Flight Patterns Tab --> <div class="tab-content" id="flight-patterns"> <h3>Flight Pattern Control</h3> <select id="pattern-select" class="flight-pattern-select"> <option value="takeoff">Takeoff</option> <option value="rightTurn">Right Turn</option> <option value="descent">Descent</option> </select> <div class="controls"> <button id="start-pattern-btn">Start Pattern</button> <button id="pause-pattern-btn" disabled>Pause</button> <button id="stop-pattern-btn" disabled>Stop</button> </div> <div class="data-item"> <div class="data-label">Pattern Status:</div> <div id="pattern-status">Ready</div> </div> </div> <!-- Logs Tab --> <div class="tab-content" id="logs"> <div class="log-container" id="log-container"> <!-- Logs will be added here --> </div> <div class="controls" style="margin-top: 10px;"> <button id="clear-logs-btn">Clear Logs</button> </div> </div> </div> <div class="panel full-width"> <h2>Raw Data</h2> <div class="code-block" id="raw-data">No data received yet</div> </div> </div> <script> let socket; let map; let marker; let flightPath; let flightCoordinates = []; let currentPatternInterval = null; let isPaused = false; let currentPatternIndex = 0; let currentPattern = []; // Connect to the WebSocket server function connectWebSocket() { const statusElement = document.getElementById('connection-status'); statusElement.className = 'status connecting'; statusElement.textContent = 'Connecting to server...'; socket = new WebSocket('ws://localhost:3000'); socket.onopen = function() { statusElement.className = 'status connected'; statusElement.textContent = 'Connected to server'; addLogEntry('Connected to MCP server', 'success'); // Get initial flight data getFlightData(); }; socket.onclose = function() { statusElement.className = 'status disconnected'; statusElement.textContent = 'Disconnected from server'; addLogEntry('Disconnected from MCP server', 'error'); // Try to reconnect after 5 seconds setTimeout(connectWebSocket, 5000); }; socket.onerror = function(error) { addLogEntry('WebSocket error: ' + error, 'error'); }; socket.onmessage = function(event) { try { const data = JSON.parse(event.data); document.getElementById('raw-data').textContent = JSON.stringify(data, null, 2); if (data.type === 'flightData') { updateFlightDataDisplay(data.data); addLogEntry('Received flight data', 'info'); } } catch (error) { addLogEntry('Error parsing message: ' + error, 'error'); } }; } // Send a command to the server function sendCommand(action, params = {}) { if (socket && socket.readyState === WebSocket.OPEN) { const command = { type: 'command', command: action, params: params, id: Date.now() }; socket.send(JSON.stringify(command)); addLogEntry(`Sent command: ${action}`, 'info'); return true; } else { addLogEntry('Cannot send command: Not connected to server', 'error'); return false; } } // Get current flight data function getFlightData() { return sendCommand('getFlightData'); } // Start a flight pattern function startFlightPattern(patternName) { if (currentPatternInterval) { stopFlightPattern(); } currentPattern = flightPatterns[patternName] || []; if (currentPattern.length === 0) { addLogEntry(`Pattern "${patternName}" not found`, 'error'); return false; } currentPatternIndex = 0; isPaused = false; // Send first state sendCommand('updateAircraftState', currentPattern[0]); addLogEntry(`Started pattern: ${patternName}`, 'success'); // Update UI document.getElementById('pattern-status').textContent = `Running: 1/${currentPattern.length}`; document.getElementById('pause-pattern-btn').disabled = false; document.getElementById('stop-pattern-btn').disabled = false; document.getElementById('start-pattern-btn').disabled = true; document.getElementById('pattern-select').disabled = true; // Set interval for subsequent states currentPatternInterval = setInterval(function() { if (isPaused) return; currentPatternIndex++; if (currentPatternIndex < currentPattern.length) { sendCommand('updateAircraftState', currentPattern[currentPatternIndex]); document.getElementById('pattern-status').textContent = `Running: ${currentPatternIndex + 1}/${currentPattern.length}`; } else { // End of pattern stopFlightPattern(); addLogEntry(`Pattern completed`, 'success'); } }, 2000); // 2-second intervals return true; } // Pause/resume flight pattern function togglePauseFlightPattern() { isPaused = !isPaused; const pauseBtn = document.getElementById('pause-pattern-btn'); pauseBtn.textContent = isPaused ? 'Resume' : 'Pause'; document.getElementById('pattern-status').textContent = isPaused ? `Paused: ${currentPatternIndex + 1}/${currentPattern.length}` : `Running: ${currentPatternIndex + 1}/${currentPattern.length}`; addLogEntry(isPaused ? 'Pattern paused' : 'Pattern resumed', 'info'); } // Stop flight pattern function stopFlightPattern() { if (currentPatternInterval) { clearInterval(currentPatternInterval); currentPatternInterval = null; } isPaused = false; currentPatternIndex = 0; // Update UI document.getElementById('pattern-status').textContent = 'Ready'; document.getElementById('pause-pattern-btn').disabled = true; document.getElementById('stop-pattern-btn').disabled = true; document.getElementById('start-pattern-btn').disabled = false; document.getElementById('pattern-select').disabled = false; document.getElementById('pause-pattern-btn').textContent = 'Pause'; addLogEntry('Pattern stopped', 'warning'); } // Add log entry function addLogEntry(message, type = 'info') { const logContainer = document.getElementById('log-container'); const entry = document.createElement('div'); entry.className = 'log-entry'; const time = new Date().toLocaleTimeString(); entry.innerHTML = `<span class="log-time">[${time}]</span> <span class="log-${type}">${message}</span>`; logContainer.appendChild(entry); logContainer.scrollTop = logContainer.scrollHeight; } // Update the position display function updatePositionDisplay(position) { if (!position) return; document.getElementById('latitude').textContent = position.latitude.toFixed(4); document.getElementById('longitude').textContent = position.longitude.toFixed(4); document.getElementById('altitude').textContent = position.altitude.toFixed(0) + ' m'; // Update map if available if (map && marker) { const pos = {lat: position.latitude, lng: position.longitude}; marker.setPosition(pos); map.setCenter(pos); // Add to flight path flightCoordinates.push(pos); if (flightPath) { flightPath.setPath(flightCoordinates); } } } // Update the flight data display function updateFlightDataDisplay(data) { if (!data) return; // Update position if (data.position) { updatePositionDisplay(data.position); } // Update attitude if (data.attitude) { document.getElementById('heading').textContent = data.attitude.heading.toFixed(0) + '°'; } // Update speed if (data.speed) { document.getElementById('airspeed').textContent = data.speed.kias.toFixed(0) + ' kts'; document.getElementById('vertical-speed').textContent = data.speed.verticalSpeed.toFixed(0) + ' fpm'; } } // Initialize Google Maps function initMap() { map = new google.maps.Map(document.getElementById('map'), { zoom: 12, center: {lat: 37.6213, lng: -122.3790}, // Default to SFO mapTypeId: 'terrain' }); marker = new google.maps.Marker({ position: {lat: 37.6213, lng: -122.3790}, map: map, title: 'Aircraft' }); flightPath = new google.maps.Polyline({ path: flightCoordinates, geodesic: true, strokeColor: '#FF0000', strokeOpacity: 1.0, strokeWeight: 2, map: map }); } // Flight patterns data const flightPatterns = { takeoff: [ { position: { latitude: 49.6233, longitude: 6.2044, altitude: 376 }, attitude: { heading: 270, pitch: 0, roll: 0 }, speed: { kias: 0, groundSpeed: 0, verticalSpeed: 0 } }, { position: { latitude: 49.6233, longitude: 6.2044, altitude: 376 }, attitude: { heading: 270, pitch: 0, roll: 0 }, speed: { kias: 20, groundSpeed: 20, verticalSpeed: 0 } }, { position: { latitude: 49.6232, longitude: 6.203, altitude: 376 }, attitude: { heading: 270, pitch: 0, roll: 0 }, speed: { kias: 40, groundSpeed: 40, verticalSpeed: 0 } }, { position: { latitude: 49.6232, longitude: 6.201, altitude: 376 }, attitude: { heading: 270, pitch: 0, roll: 0 }, speed: { kias: 60, groundSpeed: 60, verticalSpeed: 0 } }, { position: { latitude: 49.6231, longitude: 6.199, altitude: 376 }, attitude: { heading: 270, pitch: 0, roll: 0 }, speed: { kias: 80, groundSpeed: 80, verticalSpeed: 0 } }, { position: { latitude: 49.623, longitude: 6.197, altitude: 376 }, attitude: { heading: 270, pitch: 0, roll: 0 }, speed: { kias: 100, groundSpeed: 100, verticalSpeed: 0 } }, { position: { latitude: 49.6229, longitude: 6.195, altitude: 376 }, attitude: { heading: 270, pitch: 0, roll: 0 }, speed: { kias: 120, groundSpeed: 120, verticalSpeed: 0 } }, { position: { latitude: 49.6228, longitude: 6.193, altitude: 377 }, attitude: { heading: 270, pitch: 1, roll: 0 }, speed: { kias: 140, groundSpeed: 140, verticalSpeed: 100 } }, { position: { latitude: 49.6227, longitude: 6.191, altitude: 380 }, attitude: { heading: 270, pitch: 5, roll: 0 }, speed: { kias: 160, groundSpeed: 160, verticalSpeed: 500 } }, { position: { latitude: 49.6226, longitude: 6.189, altitude: 390 }, attitude: { heading: 270, pitch: 10, roll: 0 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 1000 } } ], rightTurn: [ { position: { latitude: 49.6226, longitude: 6.189, altitude: 500 }, attitude: { heading: 270, pitch: 2, roll: 0 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } }, { position: { latitude: 49.6226, longitude: 6.187, altitude: 500 }, attitude: { heading: 290, pitch: 2, roll: 15 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } }, { position: { latitude: 49.6228, longitude: 6.185, altitude: 500 }, attitude: { heading: 310, pitch: 2, roll: 20 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } }, { position: { latitude: 49.6230, longitude: 6.184, altitude: 500 }, attitude: { heading: 330, pitch: 2, roll: 20 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } }, { position: { latitude: 49.6232, longitude: 6.185, altitude: 500 }, attitude: { heading: 350, pitch: 2, roll: 15 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } }, { position: { latitude: 49.6234, longitude: 6.187, altitude: 500 }, attitude: { heading: 10, pitch: 2, roll: 5 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } }, { position: { latitude: 49.6236, longitude: 6.189, altitude: 500 }, attitude: { heading: 30, pitch: 2, roll: 0 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } } ], descent: [ { position: { latitude: 49.6236, longitude: 6.189, altitude: 500 }, attitude: { heading: 30, pitch: 2, roll: 0 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: 0 } }, { position: { latitude: 49.6238, longitude: 6.191, altitude: 490 }, attitude: { heading: 30, pitch: -2, roll: 0 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: -500 } }, { position: { latitude: 49.6240, longitude: 6.193, altitude: 470 }, attitude: { heading: 30, pitch: -3, roll: 0 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: -800 } }, { position: { latitude: 49.6242, longitude: 6.195, altitude: 450 }, attitude: { heading: 30, pitch: -3, roll: 0 }, speed: { kias: 180, groundSpeed: 180, verticalSpeed: -800 } }, { position: { latitude: 49.6244, longitude: 6.197, altitude: 430 }, attitude: { heading: 30, pitch: -2, roll: 0 }, speed: { kias: 170, groundSpeed: 170, verticalSpeed: -500 } }, { position: { latitude: 49.6246, longitude: 6.199, altitude: 420 }, attitude: { heading: 30, pitch: -1, roll: 0 }, speed: { kias: 160, groundSpeed: 160, verticalSpeed: -300 } }, { position: { latitude: 49.6248, longitude: 6.201, altitude: 410 }, attitude: { heading: 30, pitch: 0, roll: 0 }, speed: { kias: 150, groundSpeed: 150, verticalSpeed: 0 } } ] }; // Set up event listeners document.addEventListener('DOMContentLoaded', function() { // Connect to WebSocket connectWebSocket(); // Set up tab switching document.querySelectorAll('.tab').forEach(tab => { tab.addEventListener('click', function() { // Remove active class from all tabs and tab contents document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); // Add active class to clicked tab and corresponding content this.classList.add('active'); document.getElementById(this.dataset.tab).classList.add('active'); }); }); // Basic control buttons document.getElementById('get-data-btn').addEventListener('click', getFlightData); document.getElementById('set-throttle-btn').addEventListener('click', function() { sendCommand('setThrottle', { value: 0.5 }); }); document.getElementById('set-heading-btn').addEventListener('click', function() { sendCommand('setHeading', { degrees: 270 }); }); document.getElementById('select-b787-btn').addEventListener('click', function() { sendCommand('selectAircraft', { aircraftId: 18 }); }); // Flight pattern buttons document.getElementById('start-pattern-btn').addEventListener('click', function() { const patternSelect = document.getElementById('pattern-select'); startFlightPattern(patternSelect.value); }); document.getElementById('pause-pattern-btn').addEventListener('click', togglePauseFlightPattern); document.getElementById('stop-pattern-btn').addEventListener('click', stopFlightPattern); // Clear logs button document.getElementById('clear-logs-btn').addEventListener('click', function() { document.getElementById('log-container').innerHTML = ''; addLogEntry('Logs cleared', 'info'); }); // Add initial log entry addLogEntry('Dashboard initialized', 'info'); }); </script> <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"> </script> </body> </html>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lobstercare/geofs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server