mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
305 lines
7.2 KiB
JavaScript
305 lines
7.2 KiB
JavaScript
// rover Manager
|
|
// Purpose: Composes rover-manager policy, lifecycle, sensor, and socket modules into the public service API.
|
|
// Scope: Keeps runtime behavior unchanged while making this entrypoint a thin wiring/orchestration layer.
|
|
const io = require('../../globals/io');
|
|
const logger = require('../../globals/logger').child('roverManager');
|
|
const { sendAlert } = require('../alertService');
|
|
const { parseSensorFrame } = require('../../helpers/sensorDecoder');
|
|
const odometerService = require('../odometerService');
|
|
const overcurrentProtectionService = require('../overcurrentProtectionService');
|
|
const { MODES, getMode } = require('../modeManager');
|
|
const { isAdmin, isLockdownAdmin, roleEvents } = require('../roleService');
|
|
const { publishEvent } = require('../eventBus');
|
|
const videoSessions = require('../videoSessions');
|
|
const turnService = require('../turnService');
|
|
const {
|
|
ALERT_COLOR,
|
|
DOCK_GUARD_WINDOW_MS,
|
|
IDLE_UNDOCKED_MS,
|
|
PASSIVE_UNDOCKED_MS,
|
|
DOCK_GUARD_RETRY_MS,
|
|
DOCK_COMMAND_BASE64,
|
|
BACKOFF_MS,
|
|
BACKOFF_SPEED,
|
|
PRIVATE_BUTTON_HOLD_MS,
|
|
PRIVATE_AUTO_CLOSE_IDLE_MS,
|
|
PRIVATE_AUTO_CLOSE_TICK_MS,
|
|
SAFETY_BACKOFF_MIN,
|
|
SAFETY_BACKOFF_MAX,
|
|
DEFAULT_PRIVATE_SAFETY,
|
|
} = require('./constants');
|
|
const {
|
|
rovers,
|
|
socketToRovers,
|
|
spectatorSockets,
|
|
managerEvents,
|
|
backoffTimers,
|
|
dockGuardStates,
|
|
dockProtectionStrikeStates,
|
|
privateButtonStates,
|
|
privateNoUsersSince,
|
|
privateSafetyTimers,
|
|
privateSafetyStates,
|
|
} = require('./state');
|
|
const { normalizePrivateSafety, clampInt, computeBatteryState } = require('./mathUtils');
|
|
const { createPrivateAccessPolicy } = require('./privateAccess');
|
|
const { createSensorPipeline } = require('./sensorPipeline');
|
|
const { createRoverLifecycle } = require('./roverLifecycle');
|
|
const { createRosterLifecycle } = require('./rosterLifecycle');
|
|
const { createSpectatorAccess } = require('./spectatorAccess');
|
|
const { registerSocketHandlers } = require('./socketHandlers');
|
|
|
|
const privateAccess = createPrivateAccessPolicy({
|
|
io,
|
|
turnService,
|
|
MODES,
|
|
getMode,
|
|
isAdmin,
|
|
isLockdownAdmin,
|
|
normalizePrivateSafety,
|
|
DEFAULT_PRIVATE_SAFETY,
|
|
});
|
|
|
|
const {
|
|
parsePrivateMeta,
|
|
isPrivateRecord,
|
|
isPrivateOpen,
|
|
getPrivateSafety,
|
|
shouldApplyPrivateSafety,
|
|
shouldApplyPrivateSensorSafety,
|
|
isRoverVisibleToSocket,
|
|
getControlDenialReason,
|
|
} = privateAccess;
|
|
|
|
const roverLifecycle = createRoverLifecycle({
|
|
io,
|
|
rovers,
|
|
socketToRovers,
|
|
managerEvents,
|
|
turnService,
|
|
isAdmin,
|
|
sendAlert,
|
|
ALERT_COLOR,
|
|
getMode,
|
|
getControlDenialReason,
|
|
});
|
|
|
|
const {
|
|
requestControl,
|
|
releaseControl,
|
|
removeRoverDrivers,
|
|
isDriver,
|
|
canDrive,
|
|
getRoversForSocket,
|
|
getPrimaryRoverForSocket,
|
|
canLeaveCurrentRover,
|
|
canSwitchRover,
|
|
} = roverLifecycle;
|
|
|
|
let stopDockGuard = () => {};
|
|
|
|
const rosterLifecycle = createRosterLifecycle({
|
|
io,
|
|
logger,
|
|
sendAlert,
|
|
publishEvent,
|
|
turnService,
|
|
ALERT_COLOR,
|
|
DEFAULT_PRIVATE_SAFETY,
|
|
rovers,
|
|
spectatorSockets,
|
|
managerEvents,
|
|
privateButtonStates,
|
|
privateNoUsersSince,
|
|
privateSafetyTimers,
|
|
privateSafetyStates,
|
|
parsePrivateMeta,
|
|
isPrivateRecord,
|
|
isPrivateOpen,
|
|
getPrivateSafety,
|
|
isRoverVisibleToSocket,
|
|
normalizePrivateSafety,
|
|
stopDockGuard: (...args) => stopDockGuard(...args),
|
|
getControlDenialReason,
|
|
removeRoverDrivers,
|
|
});
|
|
|
|
const {
|
|
upsertRover,
|
|
removeRover,
|
|
lockRover,
|
|
setPrivateOpen,
|
|
setPrivateSafety,
|
|
getRoster,
|
|
getRosterForSocket,
|
|
broadcastRoster,
|
|
setToggleState,
|
|
handleHostStats,
|
|
canSeeRover,
|
|
canRequestControl,
|
|
} = rosterLifecycle;
|
|
|
|
const spectatorAccess = createSpectatorAccess({
|
|
io,
|
|
logger,
|
|
rovers,
|
|
spectatorSockets,
|
|
privateNoUsersSince,
|
|
PRIVATE_AUTO_CLOSE_IDLE_MS,
|
|
isPrivateRecord,
|
|
isPrivateOpen,
|
|
isRoverVisibleToSocket,
|
|
setPrivateOpen,
|
|
});
|
|
|
|
const { enableSpectator, disableSpectator, tickPrivateAutoClose } = spectatorAccess;
|
|
|
|
const sensorPipeline = createSensorPipeline({
|
|
io,
|
|
logger,
|
|
rovers,
|
|
managerEvents,
|
|
dockGuardStates,
|
|
dockProtectionStrikeStates,
|
|
backoffTimers,
|
|
privateButtonStates,
|
|
privateSafetyTimers,
|
|
privateSafetyStates,
|
|
parseSensorFrame,
|
|
computeBatteryState,
|
|
clampInt,
|
|
DEFAULT_PRIVATE_SAFETY,
|
|
SAFETY_BACKOFF_MIN,
|
|
SAFETY_BACKOFF_MAX,
|
|
PRIVATE_BUTTON_HOLD_MS,
|
|
IDLE_UNDOCKED_MS,
|
|
PASSIVE_UNDOCKED_MS,
|
|
DOCK_GUARD_RETRY_MS,
|
|
DOCK_GUARD_WINDOW_MS,
|
|
DOCK_COMMAND_BASE64,
|
|
BACKOFF_MS,
|
|
BACKOFF_SPEED,
|
|
ALERT_COLOR,
|
|
sendAlert,
|
|
publishEvent,
|
|
processOdometerFrame: odometerService.processSensorFrame,
|
|
processOvercurrentTelemetry: overcurrentProtectionService.processTelemetry,
|
|
isPrivateRecord,
|
|
isPrivateOpen,
|
|
getPrivateSafety,
|
|
setPrivateOpen,
|
|
shouldApplyPrivateSafety,
|
|
shouldApplyPrivateSensorSafety,
|
|
});
|
|
|
|
const { handleSensorFrame, applyPrivateDriveSafety } = sensorPipeline;
|
|
stopDockGuard = sensorPipeline.stopDockGuard;
|
|
|
|
managerEvents.on('rover', ({ roverId, action }) => {
|
|
/*
|
|
Protection state contains the last motor intent for a specific physical
|
|
rover connection. Removing it with the roster record prevents a reconnect
|
|
from inheriting stale stress, an old administrator bypass, or a neutral
|
|
requirement from the previous connection.
|
|
*/
|
|
if (action === 'removed') {
|
|
overcurrentProtectionService.cleanupRover(roverId);
|
|
}
|
|
});
|
|
|
|
function removeSocket(socket) {
|
|
roverLifecycle.removeSocket(socket, disableSpectator);
|
|
}
|
|
|
|
function canReplayRoverId(roverId, socket = null) {
|
|
const record = rovers.get(String(roverId));
|
|
if (!record) return false;
|
|
if (!isPrivateRecord(record)) return true;
|
|
if (isPrivateOpen(record)) return true;
|
|
return socket ? isRoverVisibleToSocket(record, socket) : false;
|
|
}
|
|
|
|
function getOdometersForSocket(socket) {
|
|
const visibleIds = getRosterForSocket(socket).map((entry) => entry.id);
|
|
return odometerService.getSnapshots(visibleIds);
|
|
}
|
|
|
|
roleEvents.on('change', ({ socket, role }) => {
|
|
if (role === 'spectator') {
|
|
enableSpectator(socket);
|
|
} else {
|
|
disableSpectator(socket);
|
|
}
|
|
});
|
|
|
|
registerSocketHandlers({
|
|
io,
|
|
logger,
|
|
MODES,
|
|
ALERT_COLOR,
|
|
getMode,
|
|
isAdmin,
|
|
isLockdownAdmin,
|
|
sendAlert,
|
|
videoSessions,
|
|
rovers,
|
|
isPrivateRecord,
|
|
isRoverVisibleToSocket,
|
|
tickPrivateAutoClose,
|
|
removeSocket,
|
|
enableSpectator,
|
|
getRosterForSocket,
|
|
canRequestControl,
|
|
canSwitchRover,
|
|
getRoversForSocket,
|
|
requestControl,
|
|
releaseControl,
|
|
managerEvents,
|
|
setPrivateOpen,
|
|
lockRover,
|
|
setPrivateSafety,
|
|
getOdometersForSocket,
|
|
});
|
|
|
|
odometerService.odometerEvents.on('update', ({ roverId, odometer }) => {
|
|
io.sockets.sockets.forEach((socket) => {
|
|
const record = rovers.get(String(roverId));
|
|
if (isRoverVisibleToSocket(record, socket)) {
|
|
socket.volatile.emit('odometer:update', { roverId, odometer });
|
|
}
|
|
});
|
|
});
|
|
|
|
setInterval(tickPrivateAutoClose, PRIVATE_AUTO_CLOSE_TICK_MS);
|
|
|
|
module.exports = {
|
|
upsertRover,
|
|
removeRover,
|
|
lockRover,
|
|
setPrivateOpen,
|
|
setPrivateSafety,
|
|
getRoster,
|
|
getRosterForSocket,
|
|
broadcastRoster,
|
|
setToggleState,
|
|
handleHostStats,
|
|
handleSensorFrame,
|
|
requestControl,
|
|
releaseControl,
|
|
removeSocket,
|
|
isDriver,
|
|
canDrive,
|
|
enableSpectator,
|
|
disableSpectator,
|
|
rovers,
|
|
managerEvents,
|
|
getRoversForSocket,
|
|
getPrimaryRoverForSocket,
|
|
canLeaveCurrentRover,
|
|
canSeeRover,
|
|
canRequestControl,
|
|
applyPrivateDriveSafety,
|
|
canReplayRoverId,
|
|
getOdometersForSocket,
|
|
};
|