fixing idle skips no longer happening!

This commit is contained in:
legop3
2026-06-22 16:05:14 -04:00
parent 988c387196
commit 0a351b9a46
4 changed files with 72 additions and 8 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -78,7 +78,7 @@
<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-CTOqrTOu.js"></script>
<script type="module" crossorigin src="/assets/index-BjmvF4mq.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CdgtactY.css">
</head>
<body>
+69 -5
View File
@@ -76,6 +76,68 @@ function setDriveCooldown(roverId, durationMs) {
driveCooldowns.set(roverId, Date.now() + durationMs);
}
function getCommandMotionMagnitude(type, payload = {}) {
if (type === 'drive') {
const driveDirect = payload?.driveDirect || {};
const left = Math.abs(Number(driveDirect.left) || 0);
const right = Math.abs(Number(driveDirect.right) || 0);
return Math.max(left, right);
}
if (type === 'motors') {
const motorPwm = payload?.motorPwm || {};
const main = Math.abs(Number(motorPwm.main) || 0);
const side = Math.abs(Number(motorPwm.side) || 0);
const vacuum = Math.abs(Number(motorPwm.vacuum) || 0);
return Math.max(main, side, vacuum);
}
return 0;
}
function shouldRecordTurnActivity(type, payload = {}) {
/*
The turn idle-skip timer is a user-intent timer, not a generic command timer.
Some commands are emitted by the browser as setup/cleanup work while the
driver is literally doing nothing. Counting those commands as activity lets
a totally idle client keep a turn forever, because recordActivity disarms
the idle skip for the rest of that turn.
*/
if (type === 'sensorStream') {
/*
Sensor streaming is enabled automatically when the UI has a rover
assignment and can also be resent after harmless React/session churn.
It is not proof that the driver touched a control.
*/
return false;
}
if (type === 'drive' || type === 'motors') {
/*
Zero drive/motor packets are safety cleanup packets. The keyboard manager,
gamepad manager, blur handlers, and turn transitions can all send zeros
without human intent, so only non-zero motion should disarm idle skip.
*/
return getCommandMotionMagnitude(type, payload) > 0;
}
if (type === 'horn') {
/*
Starting the horn is a deliberate action. Stopping it can be automatic
after a timeout or key release, so the stop packet should not be the event
that proves the driver is active.
*/
return payload?.horn?.action !== 'stop';
}
/*
Other accepted commands are left as activity because they are tied to an
explicit user control: servo moves, night vision toggles, raw OI commands,
songs, reboot/update admin actions, and similar commands.
*/
return true;
}
module.exports = {
issueCommand,
handleAck,
@@ -154,11 +216,13 @@ io.on('connection', (socket) => {
}
const id = issueCommand(roverId, { type, ...payload });
logger.info('Queued command', socket.id, roverId, type);
try {
const { recordActivity } = require('../turnService');
recordActivity(roverId, socket.id);
} catch (err) {
// best effort; ignore activity update errors
if (shouldRecordTurnActivity(type, payload)) {
try {
const { recordActivity } = require('../turnService');
recordActivity(roverId, socket.id);
} catch (err) {
// Activity recording is best-effort because command delivery should not fail if turn bookkeeping has a transient issue.
}
}
reply({ id });
} catch (err) {
+1 -1
View File
@@ -253,7 +253,7 @@ export function ControlSystemProvider({ children }) {
if (pipeline.roverId) {
pipeline.enableSensorStream();
}
}, [pipeline]);
}, [pipeline.roverId, pipeline.enableSensorStream]);
const setMode = useCallback(
(mode) => {