mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
lidar reasons
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-Wmnr23kh.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Ddqa5PDb.css">
|
||||
<script type="module" crossorigin src="/assets/index-BQtp08e2.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CGvUXLso.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -72,6 +72,16 @@ function createLidarRuntime({ logger, host, port = 6053, key, logFile = '', shou
|
||||
requestInFlight: false,
|
||||
requestStartedAt: 0,
|
||||
logStream: null,
|
||||
stats: {
|
||||
headersSeen: 0,
|
||||
rotationsSeen: 0,
|
||||
scansOk: 0,
|
||||
scansTimedOut: 0,
|
||||
scansRestarted: 0,
|
||||
rotationsWithoutScan: 0,
|
||||
parseablePayloads: 0,
|
||||
pointPayloads: 0,
|
||||
},
|
||||
};
|
||||
|
||||
function emitStatus() {
|
||||
@@ -119,25 +129,56 @@ function createLidarRuntime({ logger, host, port = 6053, key, logFile = '', shou
|
||||
state.requestStartedAt = 0;
|
||||
}
|
||||
|
||||
function finalizeScan() {
|
||||
function buildPoints(scan) {
|
||||
return Array.from(scan?.points?.values?.() || []).sort((a, b) => a.angleDeg - b.angleDeg);
|
||||
}
|
||||
|
||||
function emitFrame({ status, reason, scan = state.currentScan }) {
|
||||
const points = buildPoints(scan);
|
||||
const payload = {
|
||||
points,
|
||||
rotationSpeed: scan?.rotationSpeed ?? null,
|
||||
status,
|
||||
debug: {
|
||||
reason,
|
||||
pointsReceived: points.length,
|
||||
validPoints: points.filter((point) => point?.valid).length,
|
||||
requestInFlight: state.requestInFlight,
|
||||
requestStartedAt: state.requestStartedAt || null,
|
||||
stats: { ...state.stats },
|
||||
},
|
||||
};
|
||||
events.emit('scan', payload);
|
||||
return payload;
|
||||
}
|
||||
|
||||
function finalizeScan(reason = 'rotation_complete') {
|
||||
if (!state.currentScan) {
|
||||
state.stats.rotationsWithoutScan += 1;
|
||||
emitFrame({ status: 'error', reason: 'rotation_without_scan', scan: null });
|
||||
resetScanState();
|
||||
return;
|
||||
}
|
||||
const points = Array.from(state.currentScan.points.values()).sort((a, b) => a.angleDeg - b.angleDeg);
|
||||
const payload = {
|
||||
points,
|
||||
rotationSpeed: state.currentScan.rotationSpeed,
|
||||
};
|
||||
logger.info('Neato lidar scan parsed', { points: points.length, rotationSpeed: payload.rotationSpeed });
|
||||
state.stats.scansOk += 1;
|
||||
const payload = emitFrame({ status: 'ok', reason });
|
||||
logger.info('Neato lidar scan parsed', {
|
||||
points: payload.points.length,
|
||||
rotationSpeed: payload.rotationSpeed,
|
||||
reason,
|
||||
});
|
||||
resetScanState();
|
||||
events.emit('scan', payload);
|
||||
triggerPollSoon();
|
||||
}
|
||||
|
||||
function handlePayload(payload) {
|
||||
if (!payload) return;
|
||||
state.stats.parseablePayloads += 1;
|
||||
if (payload === 'AngleInDegrees,DistInMM,Intensity,ErrorCodeHEX') {
|
||||
state.stats.headersSeen += 1;
|
||||
if (state.currentScan) {
|
||||
state.stats.scansRestarted += 1;
|
||||
emitFrame({ status: 'error', reason: 'header_restart' });
|
||||
}
|
||||
state.currentScan = {
|
||||
points: new Map(),
|
||||
rotationSpeed: null,
|
||||
@@ -146,12 +187,16 @@ function createLidarRuntime({ logger, host, port = 6053, key, logFile = '', shou
|
||||
}
|
||||
const rotationSpeed = parseRotationSpeed(payload);
|
||||
if (rotationSpeed != null) {
|
||||
if (state.currentScan) state.currentScan.rotationSpeed = rotationSpeed;
|
||||
finalizeScan();
|
||||
state.stats.rotationsSeen += 1;
|
||||
if (state.currentScan) {
|
||||
state.currentScan.rotationSpeed = rotationSpeed;
|
||||
}
|
||||
finalizeScan('rotation_complete');
|
||||
return;
|
||||
}
|
||||
const point = parsePointPayload(payload);
|
||||
if (!point || !state.currentScan) return;
|
||||
state.stats.pointPayloads += 1;
|
||||
state.currentScan.points.set(point.angleDeg, point);
|
||||
}
|
||||
|
||||
@@ -274,6 +319,8 @@ function createLidarRuntime({ logger, host, port = 6053, key, logFile = '', shou
|
||||
clearRequestTimeoutTimer();
|
||||
state.requestTimeoutTimer = setTimeout(() => {
|
||||
logger.warn('Neato lidar scan timed out; resetting parser state');
|
||||
state.stats.scansTimedOut += 1;
|
||||
emitFrame({ status: 'error', reason: 'scan_timeout' });
|
||||
resetScanState();
|
||||
triggerPollSoon();
|
||||
}, SCAN_TIMEOUT_MS);
|
||||
|
||||
@@ -67,6 +67,7 @@ export default function VipNeatoCard({
|
||||
}) {
|
||||
const [working, setWorking] = useState('');
|
||||
const [lidarFlash, setLidarFlash] = useState(false);
|
||||
const [showLidarDebug, setShowLidarDebug] = useState(false);
|
||||
const wrapClass = fullWidth ? 'w-full' : 'w-full max-w-xl';
|
||||
|
||||
const configured = Boolean(neato?.configured);
|
||||
@@ -84,6 +85,10 @@ export default function VipNeatoCard({
|
||||
const robotAlert = normalizeState(neato?.telemetry?.robotAlert) || '--';
|
||||
const lidarPoints = Array.isArray(lidar?.points) ? lidar.points : [];
|
||||
const lidarDots = buildLidarDots(lidarPoints);
|
||||
const lidarStatus = normalizeState(lidar?.status) || '--';
|
||||
const lidarDebug = lidar?.debug && typeof lidar.debug === 'object' ? lidar.debug : null;
|
||||
const lidarReason = normalizeState(lidarDebug?.reason) || '--';
|
||||
const lidarStats = lidarDebug?.stats && typeof lidarDebug.stats === 'object' ? lidarDebug.stats : null;
|
||||
|
||||
const controls = neato?.controls || {};
|
||||
const canStart = Boolean(controls?.start?.available);
|
||||
@@ -236,7 +241,13 @@ export default function VipNeatoCard({
|
||||
</div>
|
||||
|
||||
<div className={`rounded-md px-1 py-0.5 ${lidarFlash ? 'bg-emerald-900' : 'bg-slate-800'}`}>
|
||||
<div className="text-[0.72rem] text-slate-300">Lidar</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowLidarDebug((value) => !value)}
|
||||
className="w-full text-left text-[0.72rem] text-slate-300"
|
||||
>
|
||||
Lidar
|
||||
</button>
|
||||
<div className="mt-0.25 aspect-square rounded-md bg-slate-900 p-0.25">
|
||||
{lidarDots ? (
|
||||
<svg viewBox="0 0 220 220" className="h-full w-full">
|
||||
@@ -261,6 +272,24 @@ export default function VipNeatoCard({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showLidarDebug ? (
|
||||
<div className="mt-0.5 rounded-md bg-slate-900 px-0.75 py-0.5 font-mono text-[0.68rem] text-slate-300">
|
||||
<div>Status: {lidarStatus}</div>
|
||||
<div>Reason: {lidarReason}</div>
|
||||
<div>Points: {lidarPoints.length}</div>
|
||||
<div>Rotation: {Number.isFinite(lidar?.rotationSpeed) ? lidar.rotationSpeed.toFixed(2) : '--'}</div>
|
||||
<div>Request in flight: {lidarDebug?.requestInFlight ? 'yes' : 'no'}</div>
|
||||
<div>Started at: {lidarDebug?.requestStartedAt || '--'}</div>
|
||||
<div>Headers: {lidarStats?.headersSeen ?? '--'}</div>
|
||||
<div>Rotations: {lidarStats?.rotationsSeen ?? '--'}</div>
|
||||
<div>Scans ok: {lidarStats?.scansOk ?? '--'}</div>
|
||||
<div>Timeouts: {lidarStats?.scansTimedOut ?? '--'}</div>
|
||||
<div>Restarts: {lidarStats?.scansRestarted ?? '--'}</div>
|
||||
<div>Rotation no scan: {lidarStats?.rotationsWithoutScan ?? '--'}</div>
|
||||
<div>Parseable payloads: {lidarStats?.parseablePayloads ?? '--'}</div>
|
||||
<div>Point payloads: {lidarStats?.pointPayloads ?? '--'}</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user