Design E-Stop
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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'; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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; }
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user