This commit is contained in:
legop3
2026-01-28 13:59:29 -05:00
parent 7e850dab22
commit ae81f8914f
4 changed files with 77 additions and 43 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -11,7 +11,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" /> <meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
<title>Multi Roomba Rover</title> <title>Multi Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-DCabXYAC.js"></script> <script type="module" crossorigin src="/assets/index-COFUfdDa.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-lg9md4T4.css"> <link rel="stylesheet" crossorigin href="/assets/index-lg9md4T4.css">
</head> </head>
<body> <body>
+21 -5
View File
@@ -5,6 +5,19 @@ const { sendAlert } = require('./alertService');
const ALERT_COLOR = '#00bcd4'; const ALERT_COLOR = '#00bcd4';
const { handleAck } = require('./commandService'); 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; const HEARTBEAT_INTERVAL_MS = 15000;
function handleMessage(roverId, msg) { function handleMessage(roverId, msg) {
@@ -16,13 +29,15 @@ function handleMessage(roverId, msg) {
case 'sensor': case 'sensor':
roverManager.handleSensorFrame(roverId, msg); roverManager.handleSensorFrame(roverId, msg);
break; break;
case 'event': case 'event': {
if (msg.event === 'nightVision.state' && typeof msg.data?.nightVisionOn === 'boolean') { const nightVisionOn = coerceBool(msg.data?.nightVisionOn);
roverManager.setNightVisionState(roverId, msg.data.nightVisionOn); if (msg.event === 'nightVision.state' && nightVisionOn != null) {
roverManager.setNightVisionState(roverId, nightVisionOn);
break; break;
} }
sendAlert({ color: ALERT_COLOR, title: `${roverId} event`, message: msg.event }); sendAlert({ color: ALERT_COLOR, title: `${roverId} event`, message: msg.event });
break; break;
}
default: default:
break; break;
} }
@@ -73,8 +88,9 @@ roverWSS.on('connection', (ws) => {
} else if (msg.type === 'ack') { } else if (msg.type === 'ack') {
handleAck(msg); handleAck(msg);
} else if (msg.type === 'event') { } else if (msg.type === 'event') {
if (msg.event === 'nightVision.state' && typeof msg.data?.nightVisionOn === 'boolean') { const nightVisionOn = coerceBool(msg.data?.nightVisionOn);
roverManager.setNightVisionState(roverId, msg.data.nightVisionOn); if (msg.event === 'nightVision.state' && nightVisionOn != null) {
roverManager.setNightVisionState(roverId, nightVisionOn);
} else { } else {
sendAlert({ color: ALERT_COLOR, title: `${roverId}`, message: msg.event }); sendAlert({ color: ALERT_COLOR, title: `${roverId}`, message: msg.event });
} }
+20 -2
View File
@@ -6,6 +6,7 @@ import { formatKeyLabel } from '../controls/keymapUtils.js';
import TopDownMap from './TopDownMap.jsx'; import TopDownMap from './TopDownMap.jsx';
import RoverRoster from './RoverRoster.jsx'; import RoverRoster from './RoverRoster.jsx';
import DriveDockAction, { useDriveDockState } from './DriveDockAction.jsx'; import DriveDockAction, { useDriveDockState } from './DriveDockAction.jsx';
import NightVisionControl from './NightVisionControl.jsx';
export function RoverRosterPanel({ title = 'Rovers' }) { export function RoverRosterPanel({ title = 'Rovers' }) {
const { session, requestControl } = useSession(); const { session, requestControl } = useSession();
@@ -76,11 +77,14 @@ export default function ControlSummary() {
export function InlineCameraTilt({ keymap }) { export function InlineCameraTilt({ keymap }) {
const { const {
state: { roverId, camera }, state: { roverId, camera },
actions: { setServoAngle }, pipeline,
actions: { setServoAngle, setNightVision },
} = useControlSystem(); } = useControlSystem();
const config = camera?.config; const config = camera?.config;
const enabled = Boolean(roverId && camera?.enabled && config); const enabled = Boolean(roverId && camera?.enabled && config);
const nightVisionAvailable = Boolean(roverId && pipeline?.nightVision);
const nightVisionState = pipeline?.nightVisionState;
const min = typeof config?.minAngle === 'number' ? config.minAngle : -30; const min = typeof config?.minAngle === 'number' ? config.minAngle : -30;
const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30; const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30;
const value = const value =
@@ -111,12 +115,15 @@ export function InlineCameraTilt({ keymap }) {
draggingRef.current = false; draggingRef.current = false;
}; };
if (!enabled) return null; if (!enabled && !nightVisionAvailable) return null;
const upLabel = formatKeyLabel(keymap?.cameraUp?.[0]); const upLabel = formatKeyLabel(keymap?.cameraUp?.[0]);
const downLabel = formatKeyLabel(keymap?.cameraDown?.[0]); const downLabel = formatKeyLabel(keymap?.cameraDown?.[0]);
const nightVisionLabel = formatKeyLabel(keymap?.nightVisionToggle?.[0]);
return ( return (
<div className="surface space-y-0.5 px-1 py-1 text-sm text-slate-200"> <div className="surface space-y-0.5 px-1 py-1 text-sm text-slate-200">
{enabled && (
<>
<div className="flex items-center justify-between text-xs text-slate-300"> <div className="flex items-center justify-between text-xs text-slate-300">
<span>Camera tilt</span> <span>Camera tilt</span>
<span className="font-mono text-slate-100">{formatDegrees(value)}</span> <span className="font-mono text-slate-100">{formatDegrees(value)}</span>
@@ -145,6 +152,17 @@ export function InlineCameraTilt({ keymap }) {
</span> </span>
</div> </div>
</div> </div>
</>
)}
{nightVisionAvailable && (
<NightVisionControl
nightVisionOn={nightVisionState?.nightVisionOn}
disabled={!roverId}
onToggle={setNightVision}
keyLabel={nightVisionLabel}
className={enabled ? 'mt-1' : ''}
/>
)}
</div> </div>
); );
} }