doppelt senden unterbinden. CSV
This commit is contained in:
21
docker-compose.yaml
Executable file
21
docker-compose.yaml
Executable 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
|
||||||
|
|
||||||
@@ -19,12 +19,42 @@
|
|||||||
}catch(e){ connEl.textContent = 'unbekannt'; connEl.className = 'badge'; }
|
}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(){
|
function connectSSE(){
|
||||||
const es = new EventSource('/api/events');
|
const es = new EventSource('/api/events');
|
||||||
es.onmessage = (ev)=>{
|
es.onmessage = (ev)=>{
|
||||||
try{
|
try{
|
||||||
const p = JSON.parse(ev.data);
|
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 if (p.level === 'tx') append(`TX → ${JSON.stringify(p.data)}`);
|
||||||
else append(`${p.level?.toUpperCase?.()}: ${p.message}`);
|
else append(`${p.level?.toUpperCase?.()}: ${p.message}`);
|
||||||
}catch{ append(ev.data); }
|
}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();
|
bindButtons();
|
||||||
connectSSE();
|
connectSSE();
|
||||||
refreshStatus();
|
refreshStatus();
|
||||||
|
loadLatestSnapshot();
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -33,6 +33,12 @@
|
|||||||
<label for="log">Ausgabe</label>
|
<label for="log">Ausgabe</label>
|
||||||
<textarea id="log" readonly></textarea>
|
<textarea id="log" readonly></textarea>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="snapshot">
|
||||||
|
<label for="snapshot-content">Neuester Snapshot</label>
|
||||||
|
<div id="snapshot-info"></div>
|
||||||
|
<table id="snapshot-table"></table>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@@ -7,10 +7,15 @@ h1{ margin:0; font-size:20px; }
|
|||||||
.badge.ok{ background: #064e3b; color:#a7f3d0; }
|
.badge.ok{ background: #064e3b; color:#a7f3d0; }
|
||||||
.badge.warn{ background:#3f1b00; color:#fdba74; }
|
.badge.warn{ background:#3f1b00; color:#fdba74; }
|
||||||
.badge.err{ background:#3f0d0d; color:#fecaca; }
|
.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{ 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{ background:#1e293b; color:var(--fg); border:1px solid #334155; padding:10px 16px; border-radius:8px; cursor:pointer; }
|
||||||
.controls button:hover{ border-color: var(--accent); }
|
.controls button:hover{ border-color: var(--accent); }
|
||||||
.log{ display:flex; flex-direction:column; gap:8px; }
|
.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; }
|
#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); }
|
footer{ padding:12px 24px; border-top:1px solid #1f2937; color:var(--muted); }
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ app.post('/api/send', (req, res) => {
|
|||||||
console.log(`G0 ${arrayMsg[1].toUpperCase()} F1000 gesendet`);
|
console.log(`G0 ${arrayMsg[1].toUpperCase()} F1000 gesendet`);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
return res.json({ ok: true, sent: msg.payload});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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
|
// Statisches Frontend
|
||||||
app.use('/', express.static(path.resolve('public')));
|
app.use('/', express.static(path.resolve('public')));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user