mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
fixing idle skips no longer happening!
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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/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>
|
<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>
|
<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">
|
<link rel="stylesheet" crossorigin href="/assets/index-CdgtactY.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -76,6 +76,68 @@ function setDriveCooldown(roverId, durationMs) {
|
|||||||
driveCooldowns.set(roverId, Date.now() + 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 = {
|
module.exports = {
|
||||||
issueCommand,
|
issueCommand,
|
||||||
handleAck,
|
handleAck,
|
||||||
@@ -154,11 +216,13 @@ io.on('connection', (socket) => {
|
|||||||
}
|
}
|
||||||
const id = issueCommand(roverId, { type, ...payload });
|
const id = issueCommand(roverId, { type, ...payload });
|
||||||
logger.info('Queued command', socket.id, roverId, type);
|
logger.info('Queued command', socket.id, roverId, type);
|
||||||
try {
|
if (shouldRecordTurnActivity(type, payload)) {
|
||||||
const { recordActivity } = require('../turnService');
|
try {
|
||||||
recordActivity(roverId, socket.id);
|
const { recordActivity } = require('../turnService');
|
||||||
} catch (err) {
|
recordActivity(roverId, socket.id);
|
||||||
// best effort; ignore activity update errors
|
} catch (err) {
|
||||||
|
// Activity recording is best-effort because command delivery should not fail if turn bookkeeping has a transient issue.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
reply({ id });
|
reply({ id });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ export function ControlSystemProvider({ children }) {
|
|||||||
if (pipeline.roverId) {
|
if (pipeline.roverId) {
|
||||||
pipeline.enableSensorStream();
|
pipeline.enableSensorStream();
|
||||||
}
|
}
|
||||||
}, [pipeline]);
|
}, [pipeline.roverId, pipeline.enableSensorStream]);
|
||||||
|
|
||||||
const setMode = useCallback(
|
const setMode = useCallback(
|
||||||
(mode) => {
|
(mode) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user