(function(){ const logEl = document.getElementById('log'); const connEl = document.getElementById('conn'); function append(line){ const now = new Date().toISOString(); logEl.value += `[${now}] ${line} `; logEl.scrollTop = logEl.scrollHeight; } async function refreshStatus(){ try{ const res = await fetch('/api/status'); const st = await res.json(); if (st.connected){ connEl.textContent = 'verbunden'; connEl.className = 'badge ok'; } else if (st.lastError){ connEl.textContent = 'fehler'; connEl.className = 'badge err'; } else { connEl.textContent = 'getrennt'; connEl.className = 'badge warn'; } }catch(e){ connEl.textContent = 'unbekannt'; connEl.className = 'badge'; } } function processDataShortenPosition(data){ if(data?.text){ try{ let obj = JSON.parse(data.text); if(obj?.position){ obj.position.x = parseFloat(obj.position.x.toFixed(3)); obj.position.y = parseFloat(obj.position.y.toFixed(3)); obj.position.z = parseFloat(obj.position.z.toFixed(3)); obj.position.a = parseFloat(obj.position.a.toFixed(3)); obj.position.b = parseFloat(obj.position.b.toFixed(3)); obj.position.c = parseFloat(obj.position.c.toFixed(3)); } if(obj?.motorCounts){ obj.motorCounts.x = parseFloat(obj.motorCounts.x.toFixed(3)); obj.motorCounts.y = parseFloat(obj.motorCounts.y.toFixed(3)); obj.motorCounts.z = parseFloat(obj.motorCounts.z.toFixed(3)); obj.motorCounts.a = parseFloat(obj.motorCounts.a.toFixed(3)); obj.motorCounts.b = parseFloat(obj.motorCounts.b.toFixed(3)); obj.motorCounts.c = parseFloat(obj.motorCounts.c.toFixed(3)); if(obj.motorCounts.e !== undefined) obj.motorCounts.e = parseFloat(obj.motorCounts.e.toFixed(3)); } return "text: " + JSON.stringify(obj); }catch(e){ return "text: " + data.text; } } return ""; } function connectSSE(){ const es = new EventSource('/api/events'); es.onmessage = (ev)=>{ try{ const p = JSON.parse(ev.data); if (p.level === 'msg' && p.data?.text !== 'Ping') append(`WSS → ${processDataShortenPosition(p.data)}`); //if (p.level === 'msg') append(`WSS → ${processDataShortenPosition(p.data)}`); else if (p.level === 'tx') append(`TX → ${JSON.stringify(p.data)}`); else append(`${p.level?.toUpperCase?.()}: ${p.message}`); }catch{ append(ev.data); } }; es.onerror = ()=>{ append('SSE Fehler/unterbrochen. Versuche neu zu verbinden…'); setTimeout(connectSSE, 2000); }; } function bindButtons(){ document.querySelectorAll('button[data-cmd]').forEach(btn =>{ btn.addEventListener('click', async () =>{ const cmd = btn.getAttribute('data-cmd'); let payload = null; const payloadSelector = btn.getAttribute('data-payload'); if (payloadSelector) { const field = document.querySelector(payloadSelector); if (field) payload = field.value; } try{ const res = await fetch('/api/send', { method:'POST', headers:{ 'Content-Type':'application/json' }, body: JSON.stringify({ cmd, payload }) }); const data = await res.json(); if(!res.ok){ append(`FEHLER ${res.status}: ${data.error || 'Unbekannt'}`); } else { append(`Sende: ${cmd}`); } }catch(err){ append('FEHLER: ' + (err?.message || err)); } }); }); } async function loadLatestSnapshot() { try { const res = await fetch('/api/latest-snapshot'); if (!res.ok) throw new Error('Fehler beim Laden des Snapshots'); let data; if (res.headers.get('content-type')?.includes('application/json')) { data = await res.json(); } else { const csvData = await res.text(); // Fallback: filename aus dem Pfad oder unbekannt, mtime jetzt data = { filename: 'latest.csv', mtime: new Date().toISOString(), content: csvData }; } const infoEl = document.getElementById('snapshot-info'); const tableEl = document.getElementById('snapshot-table'); // Info anzeigen const mtime = new Date(data.mtime).toLocaleString(); infoEl.textContent = `Datei: ${data.filename} | Erstellt: ${mtime}`; // CSV parsen und Tabelle bauen const lines = data.content.trim().split('\n'); if (lines.length === 0) { tableEl.innerHTML = '