doppelt senden unterbinden. CSV

This commit is contained in:
ChK
2026-03-17 08:15:25 +01:00
parent 427d29b43b
commit aa8d3b82fb
5 changed files with 134 additions and 2 deletions

21
docker-compose.yaml Executable file
View File

@@ -0,0 +1,21 @@
services:
appRobotHoming:
image: node:20-bullseye
container_name: appRobot_Homing
working_dir: /app
volumes:
- /home/chk/Documents/appRobotHoming:/app
environment:
- WSS_VIDEO_DRIVER=wss://localhost:8448
- WSS_URL=wss://appRobot_Driver:2095
- HTTPS_PORT=2093
ports:
- "2093:2093"
depends_on:
- appRobotDriver
command: >
/bin/bash -lc "npm ci || npm install && node server/server.js"
networks:
- default
restart: unless-stopped

View File

@@ -19,12 +19,42 @@
}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') append(`WSS → ${p.data?.text ?? ''}`);
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); }
@@ -60,7 +90,47 @@
});
}
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 = '<tr><td>Keine Daten</td></tr>';
return;
}
const headers = lines[0].split(',');
let html = '<thead><tr>' + headers.map(h => `<th>${h.trim()}</th>`).join('') + '</tr></thead><tbody>';
for (let i = 1; i < lines.length; i++) {
const cells = lines[i].split(',');
html += '<tr>' + cells.map(c => `<td>${c.trim()}</td>`).join('') + '</tr>';
}
html += '</tbody>';
tableEl.innerHTML = html;
} catch (err) {
document.getElementById('snapshot-info').textContent = 'Fehler: ' + err.message;
document.getElementById('snapshot-table').innerHTML = '';
}
}
bindButtons();
connectSSE();
refreshStatus();
loadLatestSnapshot();
})();

View File

@@ -33,6 +33,12 @@
<label for="log">Ausgabe</label>
<textarea id="log" readonly></textarea>
</section>
<section class="snapshot">
<label for="snapshot-content">Neuester Snapshot</label>
<div id="snapshot-info"></div>
<table id="snapshot-table"></table>
</section>
</main>
<footer>

View File

@@ -7,10 +7,15 @@ h1{ margin:0; font-size:20px; }
.badge.ok{ background: #064e3b; color:#a7f3d0; }
.badge.warn{ background:#3f1b00; color:#fdba74; }
.badge.err{ background:#3f0d0d; color:#fecaca; }
main{ display:grid; grid-template-columns:1fr; gap:16px; padding:16px; max-width:900px; margin:0 auto; }
main{ display:grid; grid-template-columns:1fr; gap:16px; padding:16px; max-width:1400px; margin:0 auto; }
.controls{ display:flex; gap:12px; flex-wrap:wrap; }
.controls button{ background:#1e293b; color:var(--fg); border:1px solid #334155; padding:10px 16px; border-radius:8px; cursor:pointer; }
.controls button:hover{ border-color: var(--accent); }
.log{ display:flex; flex-direction:column; gap:8px; }
#log{ width:100%; height:360px; background:#0b1220; color:var(--fg); border:1px solid #1f2937; border-radius:8px; padding:8px; font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size:12px; }
.snapshot{ display:flex; flex-direction:column; gap:8px; }
#snapshot-info{ font-size:14px; color:var(--muted); }
#snapshot-table{ width:100%; border-collapse:collapse; background:#0b1220; color:var(--fg); border:1px solid #1f2937; border-radius:8px; overflow:hidden; }
#snapshot-table th, #snapshot-table td{ padding:4px 8px; border:1px solid #334155; text-align:left; font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size:12px; }
#snapshot-table th{ background:#1e293b; }
footer{ padding:12px 24px; border-top:1px solid #1f2937; color:var(--muted); }

View File

@@ -149,6 +149,7 @@ app.post('/api/send', (req, res) => {
console.log(`G0 ${arrayMsg[1].toUpperCase()} F1000 gesendet`);
}
*/
return res.json({ ok: true, sent: msg.payload});
}
try {
@@ -186,6 +187,35 @@ app.get('/api/events', (req, res) => {
});
});
// Neuester Snapshot-Endpunkt
app.get('/api/latest-snapshot', (req, res) => {
const snapshotsDir = path.join(path.resolve('public'), 'snapshots');
fs.readdir(snapshotsDir, (err, files) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Lesen des Snapshots-Verzeichnisses' });
}
const csvFiles = files.filter(file => file.endsWith('.csv')).map(file => ({
name: file,
path: path.join(snapshotsDir, file),
mtime: fs.statSync(path.join(snapshotsDir, file)).mtime
})).sort((a, b) => b.mtime - a.mtime);
if (csvFiles.length === 0) {
return res.status(404).json({ error: 'Keine CSV-Dateien gefunden' });
}
const latestFile = csvFiles[0];
fs.readFile(latestFile.path, 'utf8', (err, data) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Lesen der Datei' });
}
res.json({
filename: latestFile.name,
mtime: latestFile.mtime.toISOString(),
content: data
});
});
});
});
// Statisches Frontend
app.use('/', express.static(path.resolve('public')));