Design E-Stop

This commit is contained in:
chk
2026-06-12 23:16:52 +02:00
parent bfb84fab50
commit 6de258bc01
6 changed files with 100 additions and 23 deletions

View File

@@ -10414,3 +10414,18 @@
2026-06-12T16:56:03.538Z ::ffff:127.0.0.1: M114 2026-06-12T16:56:03.538Z ::ffff:127.0.0.1: M114
2026-06-12T16:56:03.753Z ::ffff:127.0.0.1: G1 X1 Y2 Z3 2026-06-12T16:56:03.753Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T16:56:03.985Z ::ffff:127.0.0.1: G1 X1 2026-06-12T16:56:03.985Z ::ffff:127.0.0.1: G1 X1
2026-06-12T21:09:26.350Z ::ffff:127.0.0.1: M114
2026-06-12T21:09:26.458Z ::ffff:127.0.0.1: M114
2026-06-12T21:09:26.465Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T21:09:26.573Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T21:09:26.800Z ::ffff:127.0.0.1: G1 X1
2026-06-12T21:13:04.270Z ::ffff:127.0.0.1: M114
2026-06-12T21:13:04.283Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T21:13:04.452Z ::ffff:127.0.0.1: M114
2026-06-12T21:13:04.666Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T21:13:04.888Z ::ffff:127.0.0.1: G1 X1
2026-06-12T21:15:19.076Z ::ffff:127.0.0.1: M114
2026-06-12T21:15:19.091Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T21:15:19.320Z ::ffff:127.0.0.1: M114
2026-06-12T21:15:19.533Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
2026-06-12T21:15:19.762Z ::ffff:127.0.0.1: G1 X1

View File

@@ -14640,3 +14640,9 @@
2026-06-12T16:46:46.265Z ::ffff:127.0.0.1 : Ping 2026-06-12T16:46:46.265Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:56:02.816Z ::ffff:127.0.0.1 : Ping 2026-06-12T16:56:02.816Z ::ffff:127.0.0.1 : Ping
2026-06-12T16:56:03.315Z ::ffff:127.0.0.1 : Ping 2026-06-12T16:56:03.315Z ::ffff:127.0.0.1 : Ping
2026-06-12T21:09:26.116Z ::ffff:127.0.0.1 : Ping
2026-06-12T21:09:26.447Z ::ffff:127.0.0.1 : Ping
2026-06-12T21:13:04.233Z ::ffff:127.0.0.1 : Ping
2026-06-12T21:13:04.257Z ::ffff:127.0.0.1 : Ping
2026-06-12T21:15:19.049Z ::ffff:127.0.0.1 : Ping
2026-06-12T21:15:19.098Z ::ffff:127.0.0.1 : Ping

View File

@@ -56,10 +56,20 @@ document.addEventListener('DOMContentLoaded', function() {
const sendersUl = document.getElementById('senderList'); const sendersUl = document.getElementById('senderList');
sendersUl.innerHTML = ''; sendersUl.innerHTML = '';
data.senders.forEach(sender => { data.senders.forEach(sender => {
const li = document.createElement('li'); const li = document.createElement('li');
const state = sender.state || 'disconnected'; const state = sender.state || 'disconnected';
const label = sender.url ? `${sender.name} (${sender.url}): ${state}` : `${sender.name}: ${state}`;
li.textContent = label; if (sender.isGCodeReceiver === false) {
// Shelly / EmergencyStop: nur Name + Zustand, keine URL
li.textContent = `${sender.name}: ${state}`;
li.classList.add('shelly');
} else {
// Telnet / FluidNC: URL anzeigen wenn vorhanden
li.textContent = sender.url
? `${sender.name} (${sender.url}): ${state}`
: `${sender.name}: ${state}`;
}
li.classList.add(state.toLowerCase()); li.classList.add(state.toLowerCase());
sendersUl.appendChild(li); sendersUl.appendChild(li);
}); });
@@ -186,25 +196,33 @@ document.addEventListener('DOMContentLoaded', function() {
const stops = document.querySelectorAll('#estopGrad stop'); const stops = document.querySelectorAll('#estopGrad stop');
const textPath = document.querySelector('#emergency-stop textPath'); const textPath = document.querySelector('#emergency-stop textPath');
const textEl = document.querySelector('#emergency-stop text');
const btnInner = document.querySelector('#emergency-stop circle:last-of-type'); const btnInner = document.querySelector('#emergency-stop circle:last-of-type');
const btnOuter = document.querySelector('#emergency-stop circle:first-of-type');
const label = document.getElementById('armed-status'); const label = document.getElementById('armed-status');
if (armed) { if (armed) {
// Rot: Roboter bestromt → Klick = Emergency Stop // ── E-STOP: Gelber Ring + roter Pilz — laut, hervorspringend ────────────
if (btnOuter) { btnOuter.setAttribute('fill', '#FFD700'); btnOuter.setAttribute('stroke', '#C8960A'); }
if (btnInner) { btnInner.setAttribute('r', '21'); btnInner.setAttribute('stroke', '#660000'); }
if (stops[0]) stops[0].setAttribute('stop-color', '#ff5555'); if (stops[0]) stops[0].setAttribute('stop-color', '#ff5555');
if (stops[1]) stops[1].setAttribute('stop-color', '#cc0000'); if (stops[1]) stops[1].setAttribute('stop-color', '#cc0000');
if (stops[2]) stops[2].setAttribute('stop-color', '#880000'); if (stops[2]) stops[2].setAttribute('stop-color', '#880000');
if (btnInner) btnInner.setAttribute('stroke', '#660000'); if (textEl) textEl.setAttribute('fill', '#1a1000');
if (textPath) textPath.textContent = 'EMERGENCY STOP'; if (textPath) textPath.textContent = 'EMERGENCY STOP';
if (label) { label.textContent = '● Bestromt'; label.className = 'estop-armed-label armed'; } if (label) { label.textContent = '● Bestromt'; label.className = 'estop-armed-label armed'; }
} else { } else {
// Grün: Strom AUS → Klick = Strom einschalten // ── POWER ON: Dunkler Navy-Ring + blauer Knopf — ruhig, klar ────────────
if (stops[0]) stops[0].setAttribute('stop-color', '#88ff99'); // Kein gelber Ring → kein E-Stop-Charakter.
if (stops[1]) stops[1].setAttribute('stop-color', '#00aa44'); // Kompakter Knopf (r 19 statt 21) → wirkt wie klassischer Power-Switch.
if (stops[2]) stops[2].setAttribute('stop-color', '#005522'); if (btnOuter) { btnOuter.setAttribute('fill', '#1a3050'); btnOuter.setAttribute('stroke', '#0d1e33'); }
if (btnInner) btnInner.setAttribute('stroke', '#003311'); if (btnInner) { btnInner.setAttribute('r', '19'); btnInner.setAttribute('stroke', '#091929'); }
if (stops[0]) stops[0].setAttribute('stop-color', '#5c9fcf');
if (stops[1]) stops[1].setAttribute('stop-color', '#255f96');
if (stops[2]) stops[2].setAttribute('stop-color', '#0c3660');
if (textEl) textEl.setAttribute('fill', '#9dc8e8');
if (textPath) textPath.textContent = 'START ROBOT'; if (textPath) textPath.textContent = 'START ROBOT';
if (label) { label.textContent = '○ Kein Strom'; label.className = 'estop-armed-label disarmed'; } if (label) { label.textContent = '○ Kein Strom'; label.className = 'estop-armed-label disarmed'; }
} }
} }

View File

@@ -127,6 +127,40 @@ h1 {
content: "🟡"; content: "🟡";
} }
/* Shelly / EmergencyStop: CSS-Kreis statt Emoji (volle Farbkontrolle) */
.section#senders li.shelly::before {
content: "";
display: block;
position: absolute;
left: 1px;
top: 50%;
transform: translateY(-50%);
width: 11px;
height: 11px;
border-radius: 50%;
/* Default: hellblau — Strom vorhanden, Shelly bereit */
background: #89bcde;
box-shadow: 0 0 0 1px rgba(0, 30, 60, 0.4);
}
/* Gestoppt: weiß — Strom wurde abgeschaltet */
.section#senders li.shelly.stopped::before {
background: #dce9f5;
box-shadow: 0 0 0 1px rgba(100, 160, 210, 0.5);
}
/* Fehler: gedämpftes Rot */
.section#senders li.shelly.error::before {
background: #9a3a3a;
box-shadow: 0 0 0 1px rgba(60, 0, 0, 0.5);
}
/* Getrennt / unbekannt: Grau */
.section#senders li.shelly.disconnected::before {
background: #4a5a6a;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
}
.state-grid { .state-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(25px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(25px, 1fr));
@@ -302,8 +336,8 @@ h1 {
color: var(--muted); color: var(--muted);
} }
.estop-armed-label.armed { color: #e74c3c; } .estop-armed-label.armed { color: #e74c3c; } /* Rot: Gefahr, bewusst laut */
.estop-armed-label.disarmed { color: #2ecc71; } .estop-armed-label.disarmed { color: #89bcde; } /* Hellblau: ruhig, stromlos */
.btn { .btn {
display: inline-block; display: inline-block;
@@ -323,23 +357,23 @@ h1 {
cursor: not-allowed; cursor: not-allowed;
} }
/* Alarm-Unlock: Bernstein / Orange — Recovery-Aktion */ /* Alarm-Unlock: ruhiges Blau — passend zum Stromloszustand */
.btn-unlock { .btn-unlock {
background: #c87800; background: #2a6496;
color: #fff; color: #e8f2fa;
} }
.btn-unlock:hover:not(:disabled) { .btn-unlock:hover:not(:disabled) {
background: #a56200; background: #1e4d73;
} }
/* Status-Zeile unterhalb des Buttons */ /* Status-Zeile unterhalb der Buttons — immer weiß/hell, keine Signalfarben */
.estop-status { .estop-status {
font-size: 13px; font-size: 13px;
min-height: 18px; min-height: 18px;
color: var(--text); color: var(--text);
opacity: 0.7; opacity: 0.65;
} }
.estop-status.ok { color: #2ecc71; opacity: 1; } .estop-status.ok { color: var(--text); opacity: 0.9; }
.estop-status.err { color: #e74c3c; opacity: 1; } .estop-status.err { color: var(--text); opacity: 0.9; }

View File

@@ -24,7 +24,7 @@ function createInfoServer(httpsOptions, sharedState, robot, GCode, senders, opti
// ── API ────────────────────────────────────────────────────────────────── // ── API ──────────────────────────────────────────────────────────────────
app.get('/api/status', (req, res) => { app.get('/api/status', (req, res) => {
const sendersStatus = senders.map(({ name, instance }) => { const sendersStatus = senders.map(({ name, instance, isGCodeReceiver }) => {
const status = instance?.getStatus ? instance.getStatus() : { const status = instance?.getStatus ? instance.getStatus() : {
state: instance?.isTestMode ? 'connected' : instance?.tSocket ? 'connected' : 'disconnected', state: instance?.isTestMode ? 'connected' : instance?.tSocket ? 'connected' : 'disconnected',
url: instance?.url || null, url: instance?.url || null,
@@ -44,6 +44,7 @@ function createInfoServer(httpsOptions, sharedState, robot, GCode, senders, opti
return { return {
name, name,
isGCodeReceiver: isGCodeReceiver !== false, // false nur für Shelly
state, state,
url: status.url || null, url: status.url || null,
isTestMode: !!status.isTestMode, isTestMode: !!status.isTestMode,

View File

@@ -109,6 +109,7 @@ describe('InfoServer', () => {
expect(status.senders).toEqual([ expect(status.senders).toEqual([
{ {
name: 'Base', name: 'Base',
isGCodeReceiver: true,
state: 'connected', state: 'connected',
url: null, url: null,
isTestMode: false, isTestMode: false,
@@ -120,6 +121,7 @@ describe('InfoServer', () => {
}, },
{ {
name: 'Hand', name: 'Hand',
isGCodeReceiver: true,
state: 'disconnected', state: 'disconnected',
url: null, url: null,
isTestMode: false, isTestMode: false,
@@ -165,6 +167,7 @@ describe('InfoServer', () => {
expect(status.senders).toEqual([ expect(status.senders).toEqual([
{ {
name: 'Reconnect', name: 'Reconnect',
isGCodeReceiver: true,
state: 'reconnecting', state: 'reconnecting',
url: 'reconnect.test', url: 'reconnect.test',
isTestMode: false, isTestMode: false,