mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
gahew
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
// rover Connection Service
|
||||
// Purpose: Defines the rover Connection Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const roverWSS = require('../../globals/ws');
|
||||
const logger = require('../../globals/logger').child('roverConnection');
|
||||
const roverManager = require('../roverManager');
|
||||
const { sendAlert } = require('../alertService');
|
||||
const ALERT_COLOR = '#00bcd4';
|
||||
const { handleAck } = require('../commandService');
|
||||
|
||||
function coerceBool(value) {
|
||||
if (typeof value === 'boolean') return value;
|
||||
if (typeof value === 'number') return value !== 0;
|
||||
if (typeof value === 'string') {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (normalized === 'true') return true;
|
||||
if (normalized === 'false') return false;
|
||||
if (normalized === '1') return true;
|
||||
if (normalized === '0') return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const HEARTBEAT_INTERVAL_MS = 15000;
|
||||
|
||||
function handleMessage(roverId, msg) {
|
||||
switch (msg.type) {
|
||||
case 'hello':
|
||||
roverManager.upsertRover(msg, this);
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Rover Connected', message: roverId });
|
||||
break;
|
||||
case 'sensor':
|
||||
roverManager.handleSensorFrame(roverId, msg);
|
||||
break;
|
||||
case 'event': {
|
||||
const nightVisionOn = coerceBool(msg.data?.nightVisionOn);
|
||||
if (msg.event === 'nightVision.state' && nightVisionOn != null) {
|
||||
roverManager.setNightVisionState(roverId, nightVisionOn);
|
||||
break;
|
||||
}
|
||||
sendAlert({ color: ALERT_COLOR, title: `${roverId} event`, message: msg.event });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
roverWSS.on('connection', (ws) => {
|
||||
let roverId = null;
|
||||
ws.isAlive = true;
|
||||
|
||||
const heartbeat = setInterval(() => {
|
||||
if (!ws.isAlive) {
|
||||
logger.warn('Rover websocket unresponsive', roverId || 'unknown');
|
||||
ws.terminate();
|
||||
return;
|
||||
}
|
||||
ws.isAlive = false;
|
||||
ws.ping();
|
||||
}, HEARTBEAT_INTERVAL_MS);
|
||||
|
||||
ws.on('pong', () => {
|
||||
ws.isAlive = true;
|
||||
});
|
||||
|
||||
ws.on('message', (raw) => {
|
||||
ws.isAlive = true;
|
||||
let msg;
|
||||
try {
|
||||
msg = JSON.parse(raw.toString());
|
||||
} catch (err) {
|
||||
logger.warn('Invalid rover message', err.message);
|
||||
return;
|
||||
}
|
||||
if (msg.type === 'hello') {
|
||||
roverId = msg.name;
|
||||
logger.info('Received rover hello', {
|
||||
roverId,
|
||||
keys: Object.keys(msg),
|
||||
cameraServo: msg.cameraServo,
|
||||
});
|
||||
roverManager.upsertRover(msg, ws);
|
||||
roverManager.broadcastRoster();
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Rover Online', message: roverId });
|
||||
return;
|
||||
}
|
||||
if (!roverId) return;
|
||||
if (msg.type === 'sensor') {
|
||||
roverManager.handleSensorFrame(roverId, msg);
|
||||
} else if (msg.type === 'ack') {
|
||||
handleAck(msg);
|
||||
} else if (msg.type === 'event') {
|
||||
const nightVisionOn = coerceBool(msg.data?.nightVisionOn);
|
||||
if (msg.event === 'nightVision.state' && nightVisionOn != null) {
|
||||
roverManager.setNightVisionState(roverId, nightVisionOn);
|
||||
} else {
|
||||
sendAlert({ color: ALERT_COLOR, title: `${roverId}`, message: msg.event });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
clearInterval(heartbeat);
|
||||
if (roverId) {
|
||||
roverManager.removeRover(roverId);
|
||||
sendAlert({ color: ALERT_COLOR, title: 'Rover Offline', message: roverId });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user