mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
remove comparisons, move panel
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
@@ -78,8 +78,8 @@
|
||||
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
|
||||
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
|
||||
<title>Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-DXjwvKfx.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BcP4x_Qk.css">
|
||||
<script type="module" crossorigin src="/assets/index-CNhxRpdM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CxLsehkZ.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -35,19 +35,6 @@ function safeNumber(value, fallback = 0) {
|
||||
return Number.isFinite(numeric) ? numeric : fallback;
|
||||
}
|
||||
|
||||
function createInitialComparison() {
|
||||
return {
|
||||
sampleCount: 0,
|
||||
missingDistanceSamples: 0,
|
||||
ignoredSamples: 0,
|
||||
encoderSessionMm: 0,
|
||||
distancePacketSessionMm: 0,
|
||||
signedEncoderSessionMm: 0,
|
||||
signedDistancePacketSessionMm: 0,
|
||||
last: null,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeStoredEntry(entry = {}) {
|
||||
const totalMm = Math.max(0, safeNumber(entry.totalMm, 0));
|
||||
const calibrationMultiplier = Math.max(0.01, safeNumber(entry.calibrationMultiplier, DEFAULT_CALIBRATION_MULTIPLIER));
|
||||
@@ -82,7 +69,6 @@ function ensureLoaded() {
|
||||
ignoredSamples: 0,
|
||||
status: 'waiting',
|
||||
statusReason: 'waiting for encoder sample',
|
||||
comparison: createInitialComparison(),
|
||||
lastEmittedAt: 0,
|
||||
lastEmittedStatus: null,
|
||||
updatedAt: normalized.updatedAt,
|
||||
@@ -114,7 +100,6 @@ function ensureState(roverId) {
|
||||
ignoredSamples: 0,
|
||||
status: 'waiting',
|
||||
statusReason: 'waiting for encoder sample',
|
||||
comparison: createInitialComparison(),
|
||||
lastEmittedAt: 0,
|
||||
lastEmittedStatus: null,
|
||||
updatedAt: null,
|
||||
@@ -186,28 +171,6 @@ function saveNow() {
|
||||
}
|
||||
}
|
||||
|
||||
function percentDifference(differenceMm, referenceMm) {
|
||||
const reference = Math.abs(safeNumber(referenceMm, 0));
|
||||
if (reference < 1) return null;
|
||||
return Math.round((Math.abs(differenceMm) / reference) * 1000) / 10;
|
||||
}
|
||||
|
||||
function snapshotComparison(comparison = createInitialComparison()) {
|
||||
const sessionDifferenceMm = comparison.distancePacketSessionMm - comparison.encoderSessionMm;
|
||||
return {
|
||||
sampleCount: comparison.sampleCount,
|
||||
missingDistanceSamples: comparison.missingDistanceSamples,
|
||||
ignoredSamples: comparison.ignoredSamples,
|
||||
encoderSessionMm: Math.round(comparison.encoderSessionMm),
|
||||
distancePacketSessionMm: Math.round(comparison.distancePacketSessionMm),
|
||||
signedEncoderSessionMm: Math.round(comparison.signedEncoderSessionMm),
|
||||
signedDistancePacketSessionMm: Math.round(comparison.signedDistancePacketSessionMm),
|
||||
sessionDifferenceMm: Math.round(sessionDifferenceMm),
|
||||
sessionDifferencePct: percentDifference(sessionDifferenceMm, comparison.encoderSessionMm),
|
||||
last: comparison.last,
|
||||
};
|
||||
}
|
||||
|
||||
function snapshotState(state) {
|
||||
if (!state) return null;
|
||||
return {
|
||||
@@ -226,52 +189,12 @@ function snapshotState(state) {
|
||||
lastDelta: state.lastDelta,
|
||||
rolloverEvents: state.rolloverEvents,
|
||||
ignoredSamples: state.ignoredSamples,
|
||||
comparison: snapshotComparison(state.comparison),
|
||||
status: state.status,
|
||||
statusReason: state.statusReason,
|
||||
updatedAt: state.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function updateDistancePacketComparison(state, sensors, encoderCenterMm, encoderDistanceMm, options = {}) {
|
||||
const comparison = state.comparison || createInitialComparison();
|
||||
state.comparison = comparison;
|
||||
const packetMm = Number(sensors?.distanceMm);
|
||||
const ignored = Boolean(options.ignored);
|
||||
if (!Number.isFinite(packetMm)) {
|
||||
comparison.missingDistanceSamples += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const packetDistanceMm = Math.abs(packetMm);
|
||||
const signedDifferenceMm = packetMm - encoderCenterMm;
|
||||
const distanceDifferenceMm = packetDistanceMm - encoderDistanceMm;
|
||||
comparison.sampleCount += 1;
|
||||
if (ignored) {
|
||||
// Ignored encoder deltas are still recorded as a point-in-time diagnostic,
|
||||
// but they are not folded into the session totals. That keeps reconnect
|
||||
// edges from making the distance-packet comparison look worse than the
|
||||
// odometer's own accepted movement stream.
|
||||
comparison.ignoredSamples += 1;
|
||||
} else {
|
||||
comparison.encoderSessionMm += encoderDistanceMm;
|
||||
comparison.distancePacketSessionMm += packetDistanceMm;
|
||||
comparison.signedEncoderSessionMm += encoderCenterMm;
|
||||
comparison.signedDistancePacketSessionMm += packetMm;
|
||||
}
|
||||
comparison.last = {
|
||||
encoderCenterMm: Math.round(encoderCenterMm),
|
||||
encoderDistanceMm: Math.round(encoderDistanceMm),
|
||||
distancePacketMm: Math.round(packetMm),
|
||||
distancePacketAbsMm: Math.round(packetDistanceMm),
|
||||
signedDifferenceMm: Math.round(signedDifferenceMm),
|
||||
distanceDifferenceMm: Math.round(distanceDifferenceMm),
|
||||
signedDifferencePct: percentDifference(signedDifferenceMm, encoderCenterMm),
|
||||
distanceDifferencePct: percentDifference(distanceDifferenceMm, encoderDistanceMm),
|
||||
ignored,
|
||||
};
|
||||
}
|
||||
|
||||
function emitSnapshot(state, snapshot, options = {}) {
|
||||
const force = Boolean(options.force);
|
||||
const emittedRecently = nowMs() - safeNumber(state.lastEmittedAt, 0) < EMIT_THROTTLE_MS;
|
||||
@@ -359,7 +282,6 @@ function processSensorFrame(roverId, sensors = {}) {
|
||||
elapsedMs,
|
||||
ignored: true,
|
||||
};
|
||||
updateDistancePacketComparison(state, sensors, centerMm, distanceMm, { ignored: true });
|
||||
state.updatedAt = sampleAt;
|
||||
const snapshot = snapshotState(state);
|
||||
emitSnapshot(state, snapshot);
|
||||
@@ -368,7 +290,6 @@ function processSensorFrame(roverId, sensors = {}) {
|
||||
|
||||
state.totalMm += distanceMm;
|
||||
state.sessionMm += distanceMm;
|
||||
updateDistancePacketComparison(state, sensors, centerMm, distanceMm);
|
||||
state.lastIntegratedAt = sampleAt;
|
||||
state.status = 'tracking';
|
||||
state.statusReason = distanceMm > 0 ? 'integrated encoder delta' : 'no movement';
|
||||
@@ -393,7 +314,6 @@ function resetSession(roverId) {
|
||||
const state = ensureState(roverId);
|
||||
if (!state) return null;
|
||||
state.sessionMm = 0;
|
||||
state.comparison = createInitialComparison();
|
||||
state.updatedAt = nowMs();
|
||||
const snapshot = snapshotState(state);
|
||||
emitSnapshot(state, snapshot, { force: true });
|
||||
|
||||
+1
-1
@@ -193,8 +193,8 @@ function MobileFeatureTabs({
|
||||
{/* activities tab */}
|
||||
<TabPanel id="activities">
|
||||
<div className={`flex flex-col ${themeGapClass}`}>
|
||||
<OdometerPanel />
|
||||
<BarcodeGamesPanel />
|
||||
<OdometerPanel />
|
||||
<ButtonBoxPanel />
|
||||
<KinectPanel />
|
||||
</div>
|
||||
|
||||
@@ -36,17 +36,6 @@ function formatCount(value) {
|
||||
return Number.isFinite(Number(value)) ? String(Math.round(Number(value))) : '--';
|
||||
}
|
||||
|
||||
function formatSignedDistance(mm) {
|
||||
const value = Number(mm);
|
||||
if (!Number.isFinite(value)) return '--';
|
||||
const sign = value > 0 ? '+' : '';
|
||||
return `${sign}${value < 0 ? '-' : ''}${formatDistance(Math.abs(value))}`;
|
||||
}
|
||||
|
||||
function formatPercent(value) {
|
||||
return Number.isFinite(Number(value)) ? `${Number(value).toFixed(1)}%` : '--';
|
||||
}
|
||||
|
||||
function formatAge(timestamp, now) {
|
||||
const value = Number(timestamp);
|
||||
if (!Number.isFinite(value)) return '--';
|
||||
@@ -124,9 +113,8 @@ export default function OdometerPanel() {
|
||||
) : (
|
||||
<>
|
||||
<OdometerSummary odometer={primary} rover={primaryRover} now={now} />
|
||||
<div className="grid gap-0.5 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div className="grid gap-0.5 md:grid-cols-2">
|
||||
<EncoderDetails odometer={primary} />
|
||||
<DistancePacketComparison odometer={primary} />
|
||||
<CalibrationDetails odometer={primary} now={now} />
|
||||
</div>
|
||||
<RoverOdometerList rows={visibleRows} primaryRoverId={primaryRoverId} />
|
||||
@@ -178,22 +166,6 @@ function EncoderDetails({ odometer }) {
|
||||
);
|
||||
}
|
||||
|
||||
function DistancePacketComparison({ odometer }) {
|
||||
const comparison = odometer?.comparison || null;
|
||||
const last = comparison?.last || null;
|
||||
return (
|
||||
<DetailCard title="Distance packet comparison">
|
||||
<ValueRow label="Last encoder" value={formatSignedDistance(last?.encoderCenterMm)} />
|
||||
<ValueRow label="Last packet" value={formatSignedDistance(last?.distancePacketMm)} />
|
||||
<ValueRow label="Last signed diff" value={`${formatSignedDistance(last?.signedDifferenceMm)} · ${formatPercent(last?.signedDifferencePct)}`} />
|
||||
<ValueRow label="Session encoder" value={formatDistance(comparison?.encoderSessionMm)} />
|
||||
<ValueRow label="Session packet" value={formatDistance(comparison?.distancePacketSessionMm)} />
|
||||
<ValueRow label="Session diff" value={`${formatSignedDistance(comparison?.sessionDifferenceMm)} · ${formatPercent(comparison?.sessionDifferencePct)}`} />
|
||||
<ValueRow label="Samples" value={`${formatCount(comparison?.sampleCount)} compared · ${formatCount(comparison?.missingDistanceSamples)} missing`} />
|
||||
</DetailCard>
|
||||
);
|
||||
}
|
||||
|
||||
function CalibrationDetails({ odometer, now }) {
|
||||
return (
|
||||
<DetailCard title="Conversion">
|
||||
|
||||
@@ -366,8 +366,8 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
{/* activities tab */}
|
||||
<TabPanel id="activities">
|
||||
<div className={`flex flex-col ${themeGapClass}`}>
|
||||
<OdometerPanel />
|
||||
<BarcodeGamesPanel />
|
||||
<OdometerPanel />
|
||||
<ButtonBoxPanel />
|
||||
<KinectPanel />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user