E-Stop Log

This commit is contained in:
chk
2026-06-12 18:47:28 +02:00
parent e5ec78ae1e
commit 3e3023fa63
7 changed files with 79 additions and 16 deletions

View File

@@ -10399,3 +10399,13 @@
2026-06-12T16:12:57.932Z ::ffff:127.0.0.1: M114
2026-06-12T16:12:58.165Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T16:12:58.438Z ::ffff:127.0.0.1: G1 X1
2026-06-12T16:34:08.466Z ::ffff:127.0.0.1: M114
2026-06-12T16:34:08.468Z ::ffff:127.0.0.1: M114
2026-06-12T16:34:08.483Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T16:34:08.701Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T16:34:08.935Z ::ffff:127.0.0.1: G1 X1
2026-06-12T16:46:45.327Z ::ffff:127.0.0.1: M114
2026-06-12T16:46:45.356Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T16:46:46.488Z ::ffff:127.0.0.1: M114
2026-06-12T16:46:46.701Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T16:46:46.923Z ::ffff:127.0.0.1: G1 X1

View File

@@ -14634,3 +14634,7 @@
2026-06-12T16:00:23.226Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:12:57.687Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:12:57.751Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:34:08.216Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:34:08.427Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:46:45.306Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:46:46.265Z ::ffff:127.0.0.1 : Ping

View File

@@ -175,7 +175,7 @@ document.addEventListener('DOMContentLoaded', function() {
// ── Emergency Stop Panel ─────────────────────────────────────────────
// SVG-Button: Farbe + Text + Click-Handler je nach armed-Zustand wechseln.
// SVG-Button: Farbe + Text je nach armed-Zustand.
// armed=true → rot "EMERGENCY STOP" → POST /api/emergency-stop
// armed=false → grün "START ROBOT" → POST /api/power-on
let _lastArmed = null;
@@ -184,7 +184,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (armed === _lastArmed) return;
_lastArmed = armed;
const stops = document.querySelectorAll('#estopGrad stop');
const stops = document.querySelectorAll('#estopGrad stop');
const textPath = document.querySelector('#emergency-stop textPath');
const btnInner = document.querySelector('#emergency-stop circle:last-of-type');
const label = document.getElementById('armed-status');
@@ -198,7 +198,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (textPath) textPath.textContent = 'EMERGENCY STOP';
if (label) { label.textContent = '● Bestromt'; label.className = 'estop-armed-label armed'; }
} else {
// Grün: Strom AUS → Klick = Strom einschalten (Start Robot)
// Grün: Strom AUS → Klick = Strom einschalten
if (stops[0]) stops[0].setAttribute('stop-color', '#88ff99');
if (stops[1]) stops[1].setAttribute('stop-color', '#00aa44');
if (stops[2]) stops[2].setAttribute('stop-color', '#005522');
@@ -206,24 +206,59 @@ document.addEventListener('DOMContentLoaded', function() {
if (textPath) textPath.textContent = 'START ROBOT';
if (label) { label.textContent = '○ Kein Strom'; label.className = 'estop-armed-label disarmed'; }
}
}
const div = document.getElementById('emergency-stop');
if (div) {
div.onclick = armed
? () => fetch('/api/emergency-stop', { method: 'POST' })
: () => fetch('/api/power-on', { method: 'POST' });
// ── Click-Handler (einmalig registriert, liest _lastArmed dynamisch) ─────────
//
// Zeigt Lade-/Erfolgs-/Fehlerstatus unter dem Button.
// Schreibt console.warn ⚠️ für den Browser-Dev-Tools-Log.
async function handleEstopClick() {
const armed = _lastArmed;
const url = armed ? '/api/emergency-stop' : '/api/power-on';
const action = armed ? 'EmergencyStop' : 'PowerOn';
const statusEl = document.getElementById('estop-action-status');
if (statusEl) { statusEl.textContent = '⏳ …'; statusEl.className = 'estop-status'; }
console.warn(`⚠️ [${action}] wird ausgeführt …`);
try {
const res = await fetch(url, { method: 'POST' });
const data = await res.json();
const ok = data.ok || (data.results || []).every(r => r.ok || r.skipped);
if (ok) {
if (statusEl) { statusEl.textContent = `${action} OK`; statusEl.className = 'estop-status ok'; }
console.warn(`⚠️ [${action}] OK`);
} else {
const failed = (data.results || [])
.filter(r => !r.ok && !r.skipped)
.map(r => `${r.name}(${r.error || '?'})`)
.join(', ');
if (statusEl) { statusEl.textContent = `⚠️ Teilfehler: ${failed}`; statusEl.className = 'estop-status err'; }
console.warn(`⚠️ [${action}] Teilfehler — ${failed}`);
}
} catch (err) {
if (statusEl) { statusEl.textContent = `❌ Netzwerkfehler`; statusEl.className = 'estop-status err'; }
console.error(`❌ [${action}] Netzwerkfehler: ${err.message}`);
}
}
// onclick einmalig setzen — bleibt dauerhaft, Handler liest _lastArmed dynamisch.
const estopDiv = document.getElementById('emergency-stop');
if (estopDiv) estopDiv.onclick = handleEstopClick;
async function pollPowerStatus() {
try {
const res = await fetch('/api/power-status');
if (!res.ok) return;
const data = await res.json();
if (data.ok) updateEmergencyStopButton(data.armed);
// Immer aktualisieren (auch bei ok:false → disarmed als sicherer Fallback)
updateEmergencyStopButton(data.ok ? data.armed : false);
} catch { /* Netzwerkfehler → Button bleibt im letzten Zustand */ }
}
// Sofort sicheren Startzustand (grün "START ROBOT") setzen, damit der onclick
// sofort aktiv ist — noch bevor der erste Poll antwortet.
updateEmergencyStopButton(false);
pollPowerStatus();
setInterval(pollPowerStatus, 2000);

View File

@@ -105,6 +105,7 @@
</svg>
</div>
<div id="armed-status" class="estop-armed-label"></div>
<div id="estop-action-status" class="estop-status"></div>
</div>
<button id="btn-alarm-unlock" class="btn btn-unlock">

View File

@@ -99,12 +99,12 @@ module.exports = class ShellyEmergencyStop extends SenderInterface {
const result = await this._httpGet(this._offUrl);
this.state = result.ok ? 'stopped' : 'error';
this.error = result.ok ? null : `HTTP ${result.status}`;
console.log(`[Shelly] power OFF → ${result.ok ? 'OK' : `HTTP ${result.status}`}`);
console.warn(`⚠️ [Shelly] power OFF → ${result.ok ? 'OK' : `HTTP ${result.status}`}`);
return result;
} catch (err) {
this.state = 'error';
this.error = err.message;
console.error(`[Shelly] power OFF failed: ${err.message}`);
console.error(`[Shelly] power OFF failed: ${err.message}`);
return { ok: false, error: err.message };
}
}
@@ -120,7 +120,7 @@ module.exports = class ShellyEmergencyStop extends SenderInterface {
const result = await this._httpGet(this._onUrl);
this.state = result.ok ? 'ready' : 'error';
this.error = result.ok ? null : `HTTP ${result.status}`;
console.log(`[Shelly] power ON → ${result.ok ? 'OK' : `HTTP ${result.status}`}`);
console.warn(`⚠️ [Shelly] power ON → ${result.ok ? 'OK' : `HTTP ${result.status}`}`);
return result;
} catch (err) {
this.state = 'error';

View File

@@ -314,6 +314,7 @@ module.exports = class TelnetSenderGRBL extends SenderInterface {
if (!this.tSocket) return { ok: false, error: 'not connected' };
try {
this.tSocket.write('!');
console.warn(`⚠️ [EmergencyStop] Feed Hold '!' → ${this.urlGRBLstr}`);
return { ok: true };
} catch (err) {
return { ok: false, error: err.message };

View File

@@ -120,8 +120,12 @@ function createInfoServer(httpsOptions, sharedState, robot, GCode, senders, opti
? r.value
: { name: senders[i].name, ok: false, error: r.reason?.message }
);
console.log(`[EmergencyStop] triggered at ${new Date().toISOString()}`);
res.json({ ok: results.every(r => r.ok || r.skipped), at: new Date().toISOString(), results });
const ok = results.every(r => r.ok || r.skipped);
const summary = results
.map(r => `${r.name}:${r.skipped ? 'skip' : r.ok ? 'ok' : `FAIL(${r.error})`}`)
.join(', ');
console.warn(`⚠️ [EmergencyStop] ${new Date().toISOString()} — [${summary}]`);
res.json({ ok, at: new Date().toISOString(), results });
});
app.post('/api/power-on', async (req, res) => {
@@ -134,7 +138,10 @@ function createInfoServer(httpsOptions, sharedState, robot, GCode, senders, opti
const results = settled.map(r =>
r.status === 'fulfilled' ? r.value : { ok: false, error: r.reason?.message }
);
res.json({ ok: results.length > 0 && results.every(r => r.ok), at: new Date().toISOString(), results });
const ok = results.length > 0 && results.every(r => r.ok);
const summary = results.map(r => `${r.name}:${r.ok ? 'ok' : `FAIL(${r.error})`}`).join(', ');
console.warn(`⚠️ [PowerOn] ${new Date().toISOString()} — [${summary || 'keine Shelly konfiguriert'}]`);
res.json({ ok, at: new Date().toISOString(), results });
});
app.post('/api/alarm-unlock', async (req, res) => {
@@ -148,7 +155,12 @@ function createInfoServer(httpsOptions, sharedState, robot, GCode, senders, opti
? r.value
: { name: senders[i].name, ok: false, error: r.reason?.message }
);
res.json({ ok: results.every(r => r.ok || r.skipped), at: new Date().toISOString(), results });
const ok = results.every(r => r.ok || r.skipped);
const summary = results
.map(r => `${r.name}:${r.skipped ? 'skip' : r.ok ? 'ok' : `FAIL(${r.error})`}`)
.join(', ');
console.warn(`⚠️ [AlarmUnlock] ${new Date().toISOString()} — [${summary}]`);
res.json({ ok, at: new Date().toISOString(), results });
});
// ── 404 ──────────────────────────────────────────────────────────────────