UNPKG

advancedwind

Version:

A plugin that calculates true wind and ground wind optionally correcting for vessel motion, upwash, leeway and mast height.

301 lines (238 loc) 8.36 kB
const API_BASE_URL = "/plugins/advancedwind"; import TableRenderer from './TableRenderer.js'; let updateInterval = 1000; let updateTimer; let updatesPaused = false; let vAngle = 1; let dAngle = 2; let vSpeed = 1; let dSpeed = 1; export function handleSpeedUnitChange(value) { if (value == "knots") { vSpeed = 1.943844; dSpeed = 1; return; } if (value == "kmh") { vSpeed = 3.6; dSpeed = 1; return; } vSpeed = 1; dSpeed = 1; } export function handleAngleUnitChange(value) { if (value == "degrees") { vAngle = 180 / Math.PI; dAngle = 0; return; } vAngle = 1; dAngle = 2; } export function handleTableStyleChange(value) { if (value == "cartesian") { tableRenderer.setCellFormat(cartesian); } else { tableRenderer.setCellFormat(polar); } } function cAngle(value) { value *= vAngle; return value.toFixed(dAngle); } function cSpeed(value) { value *= vSpeed; return value.toFixed(dSpeed); } function cartesian(correction, speed, heel) { if (correction.N == 0) return null; return ` <div><strong>X:</strong> ${cSpeed(correction.x)}</div> <div><strong>Y:</strong> ${cSpeed(correction.y)}</div> <div><strong>N:</strong> ${correction.N}</div> `; } function polar(correction, speed, heel) { if (speed == 0 || correction.N == 0) return null; const factor = (correction.x + speed) / speed; return ` <div><strong>factor:</strong> ${factor.toFixed(2)}</div> <div><strong>leeway:</strong> ${cAngle(Math.atan2(correction.y, speed))}</div> <div><strong>N:</strong> ${correction.N}</div> `; } function formatCellX(value, speed, heel) { if (value.N == 0) return null; return cSpeed(value.x); } function formatCellY(value, speed, heel) { if (value.N == 0) return null; return Math.abs(cSpeed(value.y)); } function formatCellXPolar(value, speed, heel) { if (value.N == 0) return null; const factor = (correction.x + speed) / speed; return factor.toFixed(2); } function formatCellYPolar(value, speed, heel) { if (value.N == 0) return null; return Math.abs(cAngle(Math.atan2(value.y, speed))); } async function getFromServer(endpoint) { try { const response = await fetch(`${API_BASE_URL}/${endpoint}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); if (!response.ok) { switch (response.status) { case 503: document.getElementById("message").innerHTML = "Plugin is not running. Enable the plugin"; break; case 401: document.getElementById("message").innerHTML = "You are not logged on to the server. Log on to the server."; break; default: document.getElementById("message").innerHTML = "Failed to fetch data. Error: " + response.status + " " + response.statusText; break; } return null; // Prevent parsing JSON on error } else { document.getElementById("message").innerHTML = ""; } const data = await response.json(); return data; } catch (error) { console.error(error); document.getElementById("message").innerHTML = error; return null; } } function updateOptions(data) { const optionsContent = document.getElementById('options-content'); optionsContent.innerHTML = ''; // Clear previous content const table = document.createElement('table'); const headerRow = document.createElement('tr'); headerRow.innerHTML = '<th>Option Name</th><th>Value</th>'; table.appendChild(headerRow); Object.entries(data.options).forEach(([key, value]) => { const type = typeof value; if (type === 'string' || type === 'number' || type === 'boolean') { const row = document.createElement('tr'); row.innerHTML = `<td>${key}</td><td>${value}</td>`; table.appendChild(row); }; }); optionsContent.appendChild(table); } function updatePolar(data) { const stepsList = document.getElementById('speeds-container'); stepsList.innerHTML = ''; // Clear previous steps const table = document.createElement('table'); table.classList.add('polar'); const headerRow = document.createElement('tr'); headerRow.innerHTML = '<th>Label</th><th>Speed</th><th>Angle</th>'; table.appendChild(headerRow); data.polars.forEach(polar => { const row = document.createElement('tr'); if (polar.id) { row.id = polar.id; } // Add stale class if displayAttributes.stale is true if (polar.displayAttributes && polar.displayAttributes.stale) { row.classList.add('stale'); } row.innerHTML = `<td>${polar.displayAttributes.label}</td><td>${cSpeed(polar.magnitude)}</td><td>${cAngle(polar.angle)}</td>`; table.appendChild(row); }); stepsList.appendChild(table); } function updateDelta(data) { const stepsList = document.getElementById('delta-container'); stepsList.innerHTML = ''; // Clear previous steps const table = document.createElement('table'); table.classList.add('delta'); const headerRow = document.createElement('tr'); headerRow.innerHTML = '<th>Label</th><th>Value</th>'; table.appendChild(headerRow); data.deltas.forEach(delta => { const row = document.createElement('tr'); if (delta.id) { row.id = delta.id; } // Add stale class if displayAttributes.stale is true if (delta.displayAttributes && delta.displayAttributes.stale) { row.classList.add('stale'); } row.innerHTML = `<td>${delta.displayAttributes.label}</td><td>${cAngle(delta.value)}</td>`; table.appendChild(row); }); stepsList.appendChild(table); } function updateAttitude(data) { const attitudeContainer = document.getElementById('attitude-container'); attitudeContainer.innerHTML = ''; const table = document.createElement('table'); table.classList.add('attitude'); const headerRow = document.createElement('tr'); headerRow.innerHTML = '<th>Label</th><th>roll</th><th>pitch</th><th>yaw</th>'; table.appendChild(headerRow); data.attitudes.forEach(attitude => { const row = document.createElement('tr'); if (attitude.id) { row.id = attitude.id; } row.innerHTML = `<td>${attitude.displayAttributes.label}</td><td>${cAngle(attitude.value.roll)}</td><td>${cAngle(attitude.value.pitch)}</td><td>${cAngle(attitude.value.yaw)}</td>`; table.appendChild(row); }); attitudeContainer.appendChild(table); } function updateTable(data) { const tableContainer = document.getElementById('table-container'); tableContainer.innerHTML = ''; data.tables.forEach(table => { const tableElement = tableRenderer.render(table); tableContainer.appendChild(tableElement); }); } async function fetchAndUpdateData() { const data = await getFromServer('getResults'); // Updated endpoint if (data) { //updateOptions(data); updatePolar(data); updateAttitude(data); updateDelta(data); updateTable(data); } } function startUpdates() { if (updateTimer) clearInterval(updateTimer); updateTimer = setInterval(fetchAndUpdateData, updateInterval); } export function toggleUpdates() { updatesPaused = !updatesPaused; const toggleButton = document.getElementById('toggle-updates'); if (updatesPaused) { clearInterval(updateTimer); toggleButton.textContent = "Resume"; } else { toggleButton.textContent = "Pause"; startUpdates(); } } //document.getElementById('toggle-updates').addEventListener('click', toggleUpdates); handleSpeedUnitChange("knots"); handleAngleUnitChange("degrees"); const tableRenderer = new TableRenderer(); handleTableStyleChange("cartesian"); tableRenderer.setColumnHeaderFormat(cAngle); tableRenderer.setRowHeaderFormat(cSpeed); // Attach functions to the window object to make them globally accessible window.handleSpeedUnitChange = handleSpeedUnitChange; window.handleAngleUnitChange = handleAngleUnitChange; window.handleTableStyleChange = handleTableStyleChange; window.toggleUpdates = toggleUpdates; // Initial fetch and start updates startUpdates();