mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
gh
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
# web UI optimization
|
||||||
|
- reduce amount of invalidation for unrelated session and socket updates, wherever possible.
|
||||||
|
- example: new log line invalidates more than just log panels
|
||||||
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||||
<title>Multi Roomba Rover</title>
|
<title>Multi Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-FrukTaHm.js"></script>
|
<script type="module" crossorigin src="/assets/index-BT4tyIib.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DYarsG-0.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DYarsG-0.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ function sanitizeLogText(value) {
|
|||||||
.replace(/\x1B\[[0-9;]*[A-Za-z]/g, '');
|
.replace(/\x1B\[[0-9;]*[A-Za-z]/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shellQuote(value) {
|
||||||
|
return `'${String(value || '').replace(/'/g, `'\"'\"'`)}'`;
|
||||||
|
}
|
||||||
|
|
||||||
function parsePayloadFromLine(line) {
|
function parsePayloadFromLine(line) {
|
||||||
const raw = sanitizeLogText(line).trim();
|
const raw = sanitizeLogText(line).trim();
|
||||||
if (!raw) return null;
|
if (!raw) return null;
|
||||||
@@ -264,20 +268,26 @@ function createLidarRuntime({ logger, host, port = 6053, key, logFile = '', shou
|
|||||||
if (state.process) return;
|
if (state.process) return;
|
||||||
ensureLogStream();
|
ensureLogStream();
|
||||||
|
|
||||||
const args = [
|
const baseCommand = [
|
||||||
|
'env',
|
||||||
|
'PYTHONUNBUFFERED=1',
|
||||||
|
'NO_COLOR=1',
|
||||||
|
'TERM=dumb',
|
||||||
|
'uvx',
|
||||||
'--from',
|
'--from',
|
||||||
'aioesphomeapi',
|
'aioesphomeapi',
|
||||||
'aioesphomeapi-logs',
|
'aioesphomeapi-logs',
|
||||||
host,
|
shellQuote(host),
|
||||||
'--port',
|
'--port',
|
||||||
String(port || 6053),
|
shellQuote(String(port || 6053)),
|
||||||
'--noise-psk',
|
'--noise-psk',
|
||||||
key,
|
shellQuote(key),
|
||||||
'--no-states',
|
'--no-states',
|
||||||
];
|
].join(' ');
|
||||||
|
const launchCommand = `if command -v script >/dev/null 2>&1; then exec script -qefc ${shellQuote(baseCommand)} /dev/null; else exec ${baseCommand}; fi`;
|
||||||
|
|
||||||
logger.info('Starting Neato lidar log stream', { host, port: Number(port || 6053) });
|
logger.info('Starting Neato lidar log stream', { host, port: Number(port || 6053) });
|
||||||
const proc = spawn('uvx', args, {
|
const proc = spawn('bash', ['-lc', launchCommand], {
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
});
|
});
|
||||||
state.process = proc;
|
state.process = proc;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export default function VipPanel() {
|
|||||||
const {
|
const {
|
||||||
session,
|
session,
|
||||||
neatoLidar,
|
neatoLidar,
|
||||||
neatoLidarLines,
|
|
||||||
identifySession,
|
identifySession,
|
||||||
requestVerification,
|
requestVerification,
|
||||||
requestPrivateRoverAccess,
|
requestPrivateRoverAccess,
|
||||||
@@ -81,7 +80,6 @@ export default function VipPanel() {
|
|||||||
<VipNeatoCard
|
<VipNeatoCard
|
||||||
neato={session?.neato || null}
|
neato={session?.neato || null}
|
||||||
lidar={neatoLidar}
|
lidar={neatoLidar}
|
||||||
lidarLines={neatoLidarLines}
|
|
||||||
onStart={neatoStart}
|
onStart={neatoStart}
|
||||||
onSendHome={neatoSendHome}
|
onSendHome={neatoSendHome}
|
||||||
onLocate={neatoLocate}
|
onLocate={neatoLocate}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Purpose: Defines the Vip Neato Card module and the local helpers/components used in this file.
|
// Purpose: Defines the Vip Neato Card module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useSocket } from '../../context/SocketContext.jsx';
|
||||||
|
|
||||||
function normalizeState(value) {
|
function normalizeState(value) {
|
||||||
return String(value || '').trim();
|
return String(value || '').trim();
|
||||||
@@ -58,7 +59,6 @@ function buildLidarDots(points = []) {
|
|||||||
export default function VipNeatoCard({
|
export default function VipNeatoCard({
|
||||||
neato,
|
neato,
|
||||||
lidar,
|
lidar,
|
||||||
lidarLines,
|
|
||||||
onStart,
|
onStart,
|
||||||
onSendHome,
|
onSendHome,
|
||||||
onLocate,
|
onLocate,
|
||||||
@@ -66,9 +66,11 @@ export default function VipNeatoCard({
|
|||||||
onPowerCycle,
|
onPowerCycle,
|
||||||
fullWidth = false,
|
fullWidth = false,
|
||||||
}) {
|
}) {
|
||||||
|
const socket = useSocket();
|
||||||
const [working, setWorking] = useState('');
|
const [working, setWorking] = useState('');
|
||||||
const [lidarFlash, setLidarFlash] = useState(false);
|
const [lidarFlash, setLidarFlash] = useState(false);
|
||||||
const [showLidarDebug, setShowLidarDebug] = useState(false);
|
const [showLidarDebug, setShowLidarDebug] = useState(false);
|
||||||
|
const [recentLidarLines, setRecentLidarLines] = useState([]);
|
||||||
const wrapClass = fullWidth ? 'w-full' : 'w-full max-w-xl';
|
const wrapClass = fullWidth ? 'w-full' : 'w-full max-w-xl';
|
||||||
|
|
||||||
const configured = Boolean(neato?.configured);
|
const configured = Boolean(neato?.configured);
|
||||||
@@ -85,7 +87,6 @@ export default function VipNeatoCard({
|
|||||||
const robotError = normalizeState(neato?.telemetry?.robotError) || '--';
|
const robotError = normalizeState(neato?.telemetry?.robotError) || '--';
|
||||||
const robotAlert = normalizeState(neato?.telemetry?.robotAlert) || '--';
|
const robotAlert = normalizeState(neato?.telemetry?.robotAlert) || '--';
|
||||||
const lidarPoints = Array.isArray(lidar?.points) ? lidar.points : [];
|
const lidarPoints = Array.isArray(lidar?.points) ? lidar.points : [];
|
||||||
const recentLidarLines = Array.isArray(lidarLines) ? lidarLines : [];
|
|
||||||
const lidarDots = buildLidarDots(lidarPoints);
|
const lidarDots = buildLidarDots(lidarPoints);
|
||||||
const lidarStatus = normalizeState(lidar?.status) || '--';
|
const lidarStatus = normalizeState(lidar?.status) || '--';
|
||||||
const lidarDebug = lidar?.debug && typeof lidar.debug === 'object' ? lidar.debug : null;
|
const lidarDebug = lidar?.debug && typeof lidar.debug === 'object' ? lidar.debug : null;
|
||||||
@@ -134,6 +135,19 @@ export default function VipNeatoCard({
|
|||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [lidar]);
|
}, [lidar]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleLidarLine(payload = null) {
|
||||||
|
const next = payload && typeof payload === 'object' ? payload : null;
|
||||||
|
if (!next?.line) return;
|
||||||
|
setRecentLidarLines((prev) => [...prev.slice(-199), next]);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on('neato:lidarLine', handleLidarLine);
|
||||||
|
return () => {
|
||||||
|
socket.off('neato:lidarLine', handleLidarLine);
|
||||||
|
};
|
||||||
|
}, [socket]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`surface text-sm text-slate-200 ${wrapClass}`}>
|
<section className={`surface text-sm text-slate-200 ${wrapClass}`}>
|
||||||
<div className="grid gap-0.5">
|
<div className="grid gap-0.5">
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ const INITIAL_STATE = {
|
|||||||
connected: false,
|
connected: false,
|
||||||
session: null,
|
session: null,
|
||||||
neatoLidar: null,
|
neatoLidar: null,
|
||||||
neatoLidarLines: [],
|
|
||||||
logs: [],
|
logs: [],
|
||||||
adminLogs: [],
|
adminLogs: [],
|
||||||
llmCommentaryState: null,
|
llmCommentaryState: null,
|
||||||
@@ -133,18 +132,8 @@ export function SessionProvider({ children }) {
|
|||||||
const next = payload && typeof payload === 'object' ? payload : null;
|
const next = payload && typeof payload === 'object' ? payload : null;
|
||||||
setState((prev) => ({ ...prev, neatoLidar: next }));
|
setState((prev) => ({ ...prev, neatoLidar: next }));
|
||||||
}
|
}
|
||||||
function handleNeatoLidarLine(payload = null) {
|
|
||||||
const next = payload && typeof payload === 'object' ? payload : null;
|
|
||||||
if (!next?.line) return;
|
|
||||||
setState((prev) => ({
|
|
||||||
...prev,
|
|
||||||
neatoLidarLines: [...prev.neatoLidarLines.slice(-199), next],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.on('session:sync', handleSession);
|
socket.on('session:sync', handleSession);
|
||||||
socket.on('neato:lidar', handleNeatoLidar);
|
socket.on('neato:lidar', handleNeatoLidar);
|
||||||
socket.on('neato:lidarLine', handleNeatoLidarLine);
|
|
||||||
socket.on('log:init', handleLogInit);
|
socket.on('log:init', handleLogInit);
|
||||||
socket.on('log:entry', handleLogEntry);
|
socket.on('log:entry', handleLogEntry);
|
||||||
socket.on('adminlog:init', handleAdminLogInit);
|
socket.on('adminlog:init', handleAdminLogInit);
|
||||||
@@ -154,7 +143,6 @@ export function SessionProvider({ children }) {
|
|||||||
return () => {
|
return () => {
|
||||||
socket.off('session:sync', handleSession);
|
socket.off('session:sync', handleSession);
|
||||||
socket.off('neato:lidar', handleNeatoLidar);
|
socket.off('neato:lidar', handleNeatoLidar);
|
||||||
socket.off('neato:lidarLine', handleNeatoLidarLine);
|
|
||||||
socket.off('log:init', handleLogInit);
|
socket.off('log:init', handleLogInit);
|
||||||
socket.off('log:entry', handleLogEntry);
|
socket.off('log:entry', handleLogEntry);
|
||||||
socket.off('adminlog:init', handleAdminLogInit);
|
socket.off('adminlog:init', handleAdminLogInit);
|
||||||
|
|||||||
Reference in New Issue
Block a user