mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
new alert service colors
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||
<title>Multi Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-D8hW3Mn4.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DCrU2sZz.css">
|
||||
<script type="module" crossorigin src="/assets/index-BgRdubNv.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-aZHU9GG3.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
const io = require('../globals/io');
|
||||
|
||||
const COLORS = {
|
||||
info: '#2196f3',
|
||||
success: '#4caf50',
|
||||
warn: '#f0b651',
|
||||
error: '#e53935',
|
||||
};
|
||||
|
||||
function sendAlert({ color, title, message, ts = Date.now() }) {
|
||||
const payload = {
|
||||
color: color || COLORS.info,
|
||||
color: color || '#2196f3',
|
||||
title,
|
||||
message,
|
||||
ts,
|
||||
@@ -19,6 +12,5 @@ function sendAlert({ color, title, message, ts = Date.now() }) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
COLORS,
|
||||
sendAlert,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const { sendAlert, COLORS } = require('./alertService');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const ALERT_COLOR = '#ff9800';
|
||||
const { isAdmin, isLockdownAdmin } = require('./roleService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
|
||||
@@ -34,7 +35,7 @@ function setMode(nextMode, socket, options = {}) {
|
||||
}
|
||||
currentMode = nextMode;
|
||||
sendAlert({
|
||||
color: COLORS.info,
|
||||
color: ALERT_COLOR,
|
||||
title: 'Mode Changed',
|
||||
message: `Server mode set to ${nextMode}`,
|
||||
});
|
||||
@@ -65,7 +66,7 @@ io.on('connection', (socket) => {
|
||||
try {
|
||||
setMode(mode, socket);
|
||||
} catch (err) {
|
||||
sendAlert({ color: COLORS.error, title: 'Mode change failed', message: err.message });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Mode change failed', message: err.message });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
const roverWSS = require('../globals/ws');
|
||||
const logger = require('../globals/logger').child('roverConnection');
|
||||
const roverManager = require('./roverManager');
|
||||
const { sendAlert, COLORS } = require('./alertService');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const ALERT_COLOR = '#00bcd4';
|
||||
const { handleAck } = require('./commandService');
|
||||
|
||||
function handleMessage(roverId, msg) {
|
||||
switch (msg.type) {
|
||||
case 'hello':
|
||||
roverManager.upsertRover(msg, this);
|
||||
sendAlert({ color: COLORS.info, title: 'Rover Connected', message: roverId });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Rover Connected', message: roverId });
|
||||
break;
|
||||
case 'sensor':
|
||||
roverManager.handleSensorFrame(roverId, msg);
|
||||
break;
|
||||
case 'event':
|
||||
sendAlert({ color: COLORS.info, title: `${roverId} event`, message: msg.event });
|
||||
sendAlert({ color: ALERT_COLOR, title: `${roverId} event`, message: msg.event });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -40,7 +41,7 @@ roverWSS.on('connection', (ws) => {
|
||||
});
|
||||
roverManager.upsertRover(msg, ws);
|
||||
roverManager.broadcastRoster();
|
||||
sendAlert({ color: COLORS.success, title: 'Rover Online', message: roverId });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Rover Online', message: roverId });
|
||||
return;
|
||||
}
|
||||
if (!roverId) return;
|
||||
@@ -49,14 +50,14 @@ roverWSS.on('connection', (ws) => {
|
||||
} else if (msg.type === 'ack') {
|
||||
handleAck(msg);
|
||||
} else if (msg.type === 'event') {
|
||||
sendAlert({ color: COLORS.info, title: `${roverId}`, message: msg.event });
|
||||
sendAlert({ color: ALERT_COLOR, title: `${roverId}`, message: msg.event });
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
if (roverId) {
|
||||
roverManager.removeRover(roverId);
|
||||
sendAlert({ color: COLORS.warn, title: 'Rover Offline', message: roverId });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Rover Offline', message: roverId });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('roverManager');
|
||||
const { sendAlert, COLORS } = require('./alertService');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const ALERT_COLOR = '#8bc34a';
|
||||
const { parseSensorFrame } = require('../helpers/sensorDecoder');
|
||||
const { MODES, getMode } = require('./modeManager');
|
||||
const { isAdmin, roleEvents } = require('./roleService');
|
||||
@@ -89,7 +90,7 @@ function lockRover(id, locked, options = {}) {
|
||||
record.lockReason = reason;
|
||||
if (!silent) {
|
||||
sendAlert({
|
||||
color: COLORS.warn,
|
||||
color: ALERT_COLOR,
|
||||
title: 'Rover Locked',
|
||||
message: `${id} locked${record.lockReason ? ` (${record.lockReason})` : ''}.`,
|
||||
});
|
||||
@@ -103,7 +104,7 @@ function lockRover(id, locked, options = {}) {
|
||||
record.locked = false;
|
||||
record.lockReason = null;
|
||||
if (!silent) {
|
||||
sendAlert({ color: COLORS.success, title: 'Rover Unlocked', message: `${id} unlocked.` });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Rover Unlocked', message: `${id} unlocked.` });
|
||||
}
|
||||
publishEvent({
|
||||
source: 'roverManager',
|
||||
@@ -218,7 +219,7 @@ function handleIdleUndock(undockedRecord) {
|
||||
const bumpRecent =
|
||||
suspectRecord.lastBumpAt && now - suspectRecord.lastBumpAt <= DOCK_GUARD_WINDOW_MS;
|
||||
sendAlert({
|
||||
color: COLORS.warn,
|
||||
color: ALERT_COLOR,
|
||||
title: 'Dock protection',
|
||||
message: `${undockedRecord.id} undocked while idle; stopping ${suspect.roverId}.`,
|
||||
});
|
||||
@@ -309,7 +310,7 @@ function requestControl(roverId, socket, options = {}) {
|
||||
socket.emit('controlGranted', { roverId });
|
||||
managerEvents.emit('driver', { socketId: socket.id, roverId, action: 'add' });
|
||||
sendAlert({
|
||||
color: COLORS.success,
|
||||
color: ALERT_COLOR,
|
||||
title: 'Control Granted',
|
||||
message: `${socket.id} now driving ${roverId}`,
|
||||
});
|
||||
@@ -464,7 +465,7 @@ io.on('connection', (socket) => {
|
||||
cb({ success: true, roverId: targetId });
|
||||
} catch (err) {
|
||||
logger.warn('Request control failed', socket.id, err.message);
|
||||
sendAlert({ color: COLORS.warn, title: 'Control denied', message: err.message });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Control denied', message: err.message });
|
||||
cb({ error: err.message });
|
||||
}
|
||||
}
|
||||
@@ -490,7 +491,7 @@ io.on('connection', (socket) => {
|
||||
cb({ success: true });
|
||||
} catch (err) {
|
||||
logger.warn('Lock change failed', roverId, err.message);
|
||||
sendAlert({ color: COLORS.error, title: 'Lock failed', message: err.message });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Lock failed', message: err.message });
|
||||
cb({ error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const { sendAlert, COLORS } = require('./alertService');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const ALERT_COLOR = '#ff5722';
|
||||
const { MODES, getMode, modeEvents } = require('./modeManager');
|
||||
|
||||
const driverQueues = new Map(); // roverId -> { queue: [], current: socketId, timer: Timeout | null }
|
||||
@@ -159,7 +160,7 @@ function handleIdleTimeout(roverId, expectedDriver) {
|
||||
stopRover(roverId);
|
||||
if (skips >= MAX_IDLE_SKIPS) {
|
||||
sendAlert({
|
||||
color: COLORS.error,
|
||||
color: ALERT_COLOR,
|
||||
title: 'Driver removed',
|
||||
message: `${expectedDriver} removed from ${roverId} after ${skips} idle skips`,
|
||||
});
|
||||
@@ -167,7 +168,7 @@ function handleIdleTimeout(roverId, expectedDriver) {
|
||||
return;
|
||||
}
|
||||
sendAlert({
|
||||
color: COLORS.warn,
|
||||
color: ALERT_COLOR,
|
||||
title: 'Turn skipped',
|
||||
message: `${expectedDriver} skipped on ${roverId} (idle ${skips}/${MAX_IDLE_SKIPS})`,
|
||||
});
|
||||
@@ -207,7 +208,7 @@ function advanceTurn(roverId) {
|
||||
queue.current = queue.queue[nextIdx];
|
||||
setActiveDriver(roverId, queue.current);
|
||||
idleDisarmed.set(roverId, false);
|
||||
sendAlert({ color: COLORS.info, title: 'Turn switch', message: `${queue.current} now controls ${roverId}` });
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Turn switch', message: `${queue.current} now controls ${roverId}` });
|
||||
stopRover(roverId);
|
||||
scheduleNextTurn(roverId);
|
||||
scheduleIdleTimer(roverId);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { useSession } from '../context/SessionContext.jsx';
|
||||
|
||||
const LIFETIME_MS = 5000;
|
||||
const DEFAULT_COLOR = '#2196f3';
|
||||
|
||||
function buildKey(alert) {
|
||||
if (alert.id) return alert.id;
|
||||
@@ -38,15 +39,27 @@ export default function AlertFeed() {
|
||||
);
|
||||
}
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const safe = typeof hex === 'string' ? hex.trim() : '';
|
||||
const match = /^#?([0-9a-fA-F]{6})$/.exec(safe);
|
||||
if (!match) return null;
|
||||
const value = match[1];
|
||||
return {
|
||||
r: parseInt(value.slice(0, 2), 16),
|
||||
g: parseInt(value.slice(2, 4), 16),
|
||||
b: parseInt(value.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
function AlertToast({ alert }) {
|
||||
const colorClasses =
|
||||
alert.color === 'error'
|
||||
? 'bg-red-500/30 text-red-100'
|
||||
: alert.color === 'warn'
|
||||
? 'bg-amber-500/30 text-amber-100'
|
||||
: 'bg-emerald-500/30 text-emerald-100';
|
||||
const rgb = hexToRgb(alert.color) || hexToRgb(DEFAULT_COLOR);
|
||||
const backgroundColor = rgb ? `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.25)` : 'rgba(33, 150, 243, 0.25)';
|
||||
const borderColor = rgb ? `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.7)` : 'rgba(33, 150, 243, 0.7)';
|
||||
return (
|
||||
<div className={`pointer-events-auto px-0.5 py-0.5 text-[0.85rem] ${colorClasses}`}>
|
||||
<div
|
||||
className="pointer-events-auto px-0.5 py-0.5 text-[0.85rem] text-slate-100"
|
||||
style={{ backgroundColor, border: `1px solid ${borderColor}` }}
|
||||
>
|
||||
<p className="text-xs text-slate-200">{alert.title || 'Alert'}</p>
|
||||
<p className="text-sm text-white">{alert.message}</p>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function TurnAlertListener() {
|
||||
pushAlert({
|
||||
title: 'Your turn!',
|
||||
message: `You now control ${roverName}.`,
|
||||
color: 'success',
|
||||
color: '#ff5722',
|
||||
});
|
||||
});
|
||||
playSound();
|
||||
|
||||
Reference in New Issue
Block a user