210 lines
8.3 KiB
JavaScript
210 lines
8.3 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function() {
|
||
|
||
function fmt(v) {
|
||
if (v === undefined || v === null || isNaN(v)) return '–';
|
||
return Number(v).toFixed(0);
|
||
}
|
||
|
||
function updatePosition() {
|
||
fetch('/api/position')
|
||
.then(res => res.json())
|
||
.then(data => {
|
||
const p = data.position || {};
|
||
const m = data.motorCounts || {};
|
||
|
||
document.getElementById('state-x').textContent = fmt(p.x);
|
||
document.getElementById('state-y').textContent = fmt(p.y);
|
||
document.getElementById('state-z').textContent = fmt(p.z);
|
||
|
||
document.getElementById('state-phi').textContent = fmt(p.a*180/Math.PI);
|
||
document.getElementById('state-theta').textContent = fmt(p.b*180/Math.PI);
|
||
document.getElementById('state-psi').textContent = fmt(p.c*180/Math.PI);
|
||
|
||
document.getElementById('state-e').textContent = fmt(m.e*180/Math.PI);
|
||
|
||
|
||
|
||
/* Motor-Zustand */
|
||
document.getElementById('motor-x').textContent = fmt(m.x);
|
||
document.getElementById('motor-y').textContent = fmt(m.y*180/Math.PI);
|
||
document.getElementById('motor-z').textContent = fmt(m.z*180/Math.PI);
|
||
|
||
document.getElementById('motor-a').textContent = fmt(m.a*180/Math.PI);
|
||
document.getElementById('motor-b').textContent = fmt(m.b*180/Math.PI);
|
||
document.getElementById('motor-c').textContent = fmt(m.c*180/Math.PI);
|
||
|
||
document.getElementById('motor-e').textContent = fmt(m.e);
|
||
|
||
})
|
||
.catch(err => console.error('Error fetching position:', err));
|
||
}
|
||
|
||
function updateStatus() {
|
||
fetch('/api/status')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
// WebClients
|
||
const clientsUl = document.getElementById('clients');
|
||
clientsUl.innerHTML = '';
|
||
data.clients.forEach(client => {
|
||
const li = document.createElement('li');
|
||
li.textContent = client;
|
||
clientsUl.appendChild(li);
|
||
});
|
||
|
||
// Sender
|
||
const sendersUl = document.getElementById('senderList');
|
||
sendersUl.innerHTML = '';
|
||
data.senders.forEach(sender => {
|
||
const li = document.createElement('li');
|
||
const state = sender.state || 'disconnected';
|
||
const label = sender.url ? `${sender.name} (${sender.url}): ${state}` : `${sender.name}: ${state}`;
|
||
li.textContent = label;
|
||
li.classList.add(state.toLowerCase());
|
||
sendersUl.appendChild(li);
|
||
});
|
||
|
||
// Letzte Commands
|
||
const commandsUl = document.getElementById('commandList');
|
||
commandsUl.innerHTML = '';
|
||
data.lastCommands.forEach(cmd => {
|
||
const li = document.createElement('li');
|
||
li.textContent = cmd;
|
||
commandsUl.appendChild(li);
|
||
});
|
||
|
||
// Letzte Pings
|
||
const pingsUl = document.getElementById('pingList');
|
||
pingsUl.innerHTML = '';
|
||
data.lastPings.forEach(ping => {
|
||
const li = document.createElement('li');
|
||
li.textContent = ping;
|
||
pingsUl.appendChild(li);
|
||
});
|
||
})
|
||
.catch(error => console.error('Error fetching status:', error));
|
||
}
|
||
|
||
|
||
// ── Robot.json + History ─────────────────────────────────────────────────
|
||
|
||
let robotJsonActive = 'current';
|
||
let robotJsonLastSerialized = null;
|
||
|
||
function renderJsonTree(data, container) {
|
||
container.innerHTML = '';
|
||
const tree = document.createElement('div');
|
||
tree.className = 'json-tree';
|
||
for (const [key, value] of Object.entries(data)) {
|
||
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
||
const details = document.createElement('details');
|
||
const summary = document.createElement('summary');
|
||
summary.textContent = key;
|
||
details.appendChild(summary);
|
||
const pre = document.createElement('pre');
|
||
pre.textContent = JSON.stringify(value, null, 2);
|
||
details.appendChild(pre);
|
||
tree.appendChild(details);
|
||
} else {
|
||
const row = document.createElement('div');
|
||
row.className = 'json-scalar';
|
||
row.innerHTML =
|
||
`<span class="json-key">${key}</span>` +
|
||
`<span class="json-val">${JSON.stringify(value)}</span>`;
|
||
tree.appendChild(row);
|
||
}
|
||
}
|
||
container.appendChild(tree);
|
||
}
|
||
|
||
function updateRobotJson() {
|
||
const url = robotJsonActive === 'current'
|
||
? '/api/robot'
|
||
: `/api/robot/history/${robotJsonActive}`;
|
||
fetch(url)
|
||
.then(res => res.ok ? res.json() : Promise.reject(res.status))
|
||
.then(data => {
|
||
const serialized = JSON.stringify(data);
|
||
document.getElementById('robotJsonLabel').textContent =
|
||
robotJsonActive === 'current' ? '(aktuell)' : `(${robotJsonActive})`;
|
||
if (serialized !== robotJsonLastSerialized) {
|
||
robotJsonLastSerialized = serialized;
|
||
renderJsonTree(data, document.getElementById('robotJsonTree'));
|
||
}
|
||
})
|
||
.catch(err => {
|
||
document.getElementById('robotJsonTree').textContent = `Fehler: ${err}`;
|
||
});
|
||
}
|
||
|
||
function setHistoryActive(ts) {
|
||
robotJsonActive = ts;
|
||
robotJsonLastSerialized = null; // Neuaufbau erzwingen beim Snapshot-Wechsel
|
||
updateRobotJson();
|
||
document.querySelectorAll('#robotHistoryList li').forEach(l => {
|
||
l.classList.toggle('rh-active', l.dataset.ts === ts);
|
||
});
|
||
}
|
||
|
||
function updateRobotHistory() {
|
||
fetch('/api/robot/history')
|
||
.then(res => res.json())
|
||
.then(({ history }) => {
|
||
const ul = document.getElementById('robotHistoryList');
|
||
ul.innerHTML = '';
|
||
|
||
const liCurrent = document.createElement('li');
|
||
liCurrent.textContent = 'robot.json (aktuell)';
|
||
liCurrent.dataset.ts = 'current';
|
||
if (robotJsonActive === 'current') liCurrent.classList.add('rh-active');
|
||
liCurrent.addEventListener('click', () => setHistoryActive('current'));
|
||
ul.appendChild(liCurrent);
|
||
|
||
history.forEach(entry => {
|
||
const ts = entry.filename.slice(6, -5); // robot_YYYYMMDD_HHmmss.json → YYYYMMDD_HHmmss
|
||
const li = document.createElement('li');
|
||
li.textContent = entry.filename;
|
||
li.dataset.ts = ts;
|
||
if (robotJsonActive === ts) li.classList.add('rh-active');
|
||
li.addEventListener('click', () => setHistoryActive(ts));
|
||
ul.appendChild(li);
|
||
});
|
||
})
|
||
.catch(err => console.error('Error fetching robot history:', err));
|
||
}
|
||
|
||
updateStatus();
|
||
updatePosition();
|
||
updateRobotJson();
|
||
updateRobotHistory();
|
||
|
||
setInterval(() => {
|
||
updateStatus();
|
||
updatePosition();
|
||
if (robotJsonActive === 'current') updateRobotJson();
|
||
}, 1000);
|
||
|
||
setInterval(updateRobotHistory, 30000);
|
||
|
||
});
|
||
|
||
document.querySelectorAll('.section').forEach(sec => {
|
||
const id = sec.dataset.id;
|
||
const saved = localStorage.getItem('section_' + id);
|
||
if (saved === 'collapsed') sec.classList.add('collapsed');
|
||
|
||
sec.querySelector('h2').addEventListener('click', () => {
|
||
sec.classList.toggle('collapsed');
|
||
localStorage.setItem(
|
||
'section_' + id,
|
||
sec.classList.contains('collapsed') ? 'collapsed' : 'open'
|
||
);
|
||
});
|
||
});
|
||
|
||
/* Initial-Zustand
|
||
['clients','pings'].forEach(id => {
|
||
if (!localStorage.getItem('section_' + id))
|
||
localStorage.setItem('section_' + id, 'collapsed');
|
||
});
|
||
*/ |