mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
allow everyone to switch rovers conditionally
This commit is contained in:
@@ -54,6 +54,15 @@ roverManager.managerEvents.on('rover', ({ action }) => {
|
||||
}
|
||||
});
|
||||
|
||||
roverManager.managerEvents.on('switch', ({ socketId, roverId }) => {
|
||||
if (!socketId || !roverId) return;
|
||||
const socket = socketRefs.get(socketId);
|
||||
if (!socket) return;
|
||||
assignments.set(socketId, roverId);
|
||||
waiting.delete(socketId);
|
||||
assignmentEvents.emit('update', socketId);
|
||||
});
|
||||
|
||||
function assignSocket(socket) {
|
||||
if (!socket || isAdmin(socket) || getRole(socket) !== 'user') {
|
||||
return;
|
||||
@@ -177,4 +186,15 @@ module.exports = {
|
||||
assignmentEvents,
|
||||
describeAssignment,
|
||||
forceRelease,
|
||||
getAssignedRover: (socketId) => assignments.get(socketId) || null,
|
||||
moveAssignment: (socket, roverId, { releasePrevious = true } = {}) => {
|
||||
if (!socket || !roverId) return;
|
||||
const previous = assignments.get(socket.id);
|
||||
if (previous && previous !== roverId && releasePrevious) {
|
||||
roverManager.releaseControl(previous, socket);
|
||||
}
|
||||
assignments.set(socket.id, roverId);
|
||||
waiting.delete(socket.id);
|
||||
assignmentEvents.emit('update', socket.id);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ const { parseSensorFrame } = require('../helpers/sensorDecoder');
|
||||
const { MODES, getMode } = require('./modeManager');
|
||||
const { isAdmin, roleEvents } = require('./roleService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const videoSessions = require('./videoSessions');
|
||||
|
||||
const rovers = new Map(); // roverId -> record
|
||||
const socketToRovers = new Map(); // socketId -> Set(roverId)
|
||||
@@ -276,6 +277,41 @@ function getPrimaryRoverForSocket(socketId) {
|
||||
return first.done ? null : first.value;
|
||||
}
|
||||
|
||||
function isDockedAndCharging(record) {
|
||||
const sensors = record?.lastSensor?.decoded || record?.lastSensor?.sensors || null;
|
||||
if (!sensors) return false;
|
||||
const docked = Boolean(sensors.chargingSources?.homeBase);
|
||||
const code = sensors.chargingState?.code;
|
||||
const charging = code === 2 || code === 3 || code === 4;
|
||||
return docked && charging;
|
||||
}
|
||||
|
||||
function hasOtherDrivers(record, socketId) {
|
||||
if (!record) return false;
|
||||
for (const driver of record.drivers) {
|
||||
if (driver !== socketId) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function canSwitchRover(socket, targetRoverId) {
|
||||
const currentId = getPrimaryRoverForSocket(socket.id);
|
||||
if (!currentId || currentId === targetRoverId) {
|
||||
return { ok: true, currentId };
|
||||
}
|
||||
const currentRecord = rovers.get(currentId);
|
||||
if (!currentRecord) {
|
||||
return { ok: true, currentId };
|
||||
}
|
||||
if (hasOtherDrivers(currentRecord, socket.id)) {
|
||||
return { ok: true, currentId };
|
||||
}
|
||||
if (isDockedAndCharging(currentRecord)) {
|
||||
return { ok: true, currentId };
|
||||
}
|
||||
return { ok: false, currentId, message: 'Dock and charge your current rover before switching.' };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
upsertRover,
|
||||
removeRover,
|
||||
@@ -312,12 +348,35 @@ io.on('connection', (socket) => {
|
||||
|
||||
function handleRequestControl({ roverId, force } = {}, cb = () => {}) {
|
||||
try {
|
||||
if (socket.data?.role === 'spectator') {
|
||||
throw new Error('Spectators cannot drive');
|
||||
}
|
||||
const targetId = roverId || Array.from(rovers.keys())[0];
|
||||
if (!targetId) {
|
||||
throw new Error('No rovers available');
|
||||
}
|
||||
const previousJoined = getRoversForSocket(socket.id);
|
||||
if (!isAdmin(socket)) {
|
||||
const { ok, message } = canSwitchRover(socket, targetId);
|
||||
if (!ok) {
|
||||
throw new Error(message || 'Switch denied');
|
||||
}
|
||||
}
|
||||
logger.info('Request control', socket.id, targetId, { force });
|
||||
requestControl(targetId, socket, { force: Boolean(force) });
|
||||
requestControl(targetId, socket, { force: Boolean(force), allowUser: true });
|
||||
previousJoined.forEach((rid) => {
|
||||
if (rid !== targetId) {
|
||||
releaseControl(rid, socket);
|
||||
}
|
||||
});
|
||||
videoSessions.revokeWhere(
|
||||
(info) =>
|
||||
info.socketId === socket.id &&
|
||||
info.sourceType === 'rover' &&
|
||||
info.sourceId !== targetId &&
|
||||
info.sourceId !== `${targetId}-audio`,
|
||||
);
|
||||
managerEvents.emit('switch', { socketId: socket.id, roverId: targetId });
|
||||
socket.emit('controlGranted', { roverId: targetId });
|
||||
cb({ success: true, roverId: targetId });
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user