mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-11-14 10:42:45 -05:00
66 lines
2.6 KiB
JavaScript
66 lines
2.6 KiB
JavaScript
const EventEmitter = require('events');
|
|
|
|
const operatingModeEvents = new EventEmitter();
|
|
const transitions = new WeakMap();
|
|
|
|
function getOperatingMode(socket) {
|
|
// Handshake intent reserves the mode before role-driven assignment runs.
|
|
// It grants no camera permissions; entry still checks access and readiness.
|
|
return socket?.data?.operatingMode
|
|
|| (socket?.handshake?.auth?.operatingMode === 'ptz' ? 'ptz' : 'rover');
|
|
}
|
|
|
|
async function transition(socket, operatingMode) {
|
|
// Resolve services at the action boundary to avoid a dependency cycle with
|
|
// assignment and camera policy, which both read operating mode.
|
|
const assignment = require('../assignmentService');
|
|
const roverManager = require('../roverManager');
|
|
const ptz = require('../ptzCameraService');
|
|
if (operatingMode === 'rover' && getOperatingMode(socket) === 'rover') {
|
|
return { operatingMode, state: ptz.getPublicState(socket) };
|
|
}
|
|
if (operatingMode === 'ptz') {
|
|
await ptz.prepareParticipation(socket);
|
|
if (socket.disconnected) throw new Error('Session disconnected');
|
|
ptz.checkParticipationAccess(socket);
|
|
const leave = roverManager.canLeaveCurrentRover(socket);
|
|
if (!leave.ok) throw new Error(leave.message);
|
|
|
|
// Block new rover ownership before release events notify other services.
|
|
socket.data.operatingMode = 'ptz';
|
|
assignment.releaseForOperatingMode(socket);
|
|
ptz.claimTurn(socket);
|
|
} else {
|
|
ptz.releaseTurn(socket);
|
|
socket.data.operatingMode = 'rover';
|
|
assignment.resumeAssignment(socket);
|
|
}
|
|
operatingModeEvents.emit('change', { socket, operatingMode });
|
|
return { operatingMode, state: ptz.getPublicState(socket) };
|
|
}
|
|
|
|
function enqueueTransition(socket, action) {
|
|
// Camera initialization can yield. Serialize switches and turn release per
|
|
// socket so a late entry cannot undo a subsequent exit or release request.
|
|
const previous = transitions.get(socket) || Promise.resolve();
|
|
const next = previous.catch(() => {}).then(() => {
|
|
if (socket.disconnected) throw new Error('Session disconnected');
|
|
return action();
|
|
});
|
|
transitions.set(socket, next);
|
|
return next;
|
|
}
|
|
|
|
function setOperatingMode(socket, operatingMode) {
|
|
if (!['rover', 'ptz'].includes(operatingMode)) {
|
|
return Promise.reject(new Error('Invalid operating mode'));
|
|
}
|
|
return enqueueTransition(socket, () => transition(socket, operatingMode));
|
|
}
|
|
|
|
function releasePtzTurn(socket) {
|
|
return enqueueTransition(socket, () => require('../ptzCameraService').releaseTurn(socket));
|
|
}
|
|
|
|
module.exports = { getOperatingMode, setOperatingMode, releasePtzTurn, operatingModeEvents };
|