mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 10:00:46 -04:00
gahwa
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
|||||||
<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-D8Lb2mUY.js"></script>
|
<script type="module" crossorigin src="/assets/index-ba8YMKfH.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DQ1jpCiS.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-7IHa35aL.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -4,20 +4,14 @@ const { getRoomCameras, roomCameraEvents } = require('./roomCameraService');
|
|||||||
|
|
||||||
const POLL_INTERVAL_MS = 800;
|
const POLL_INTERVAL_MS = 800;
|
||||||
const FETCH_TIMEOUT_MS = 2000;
|
const FETCH_TIMEOUT_MS = 2000;
|
||||||
const STALE_AFTER_MS = 8000;
|
|
||||||
|
|
||||||
const cameraState = new Map(); // id -> {frame, ts, stale, error, failures, fetching}
|
const cameraState = new Map(); // id -> {frame, ts, error, failures, fetching}
|
||||||
const events = new EventEmitter(); // frame, status
|
const events = new EventEmitter(); // frame, status
|
||||||
let pollTimer = null;
|
let pollTimer = null;
|
||||||
|
|
||||||
function markState(id, updates = {}) {
|
function markState(id, updates = {}) {
|
||||||
const prev = cameraState.get(id) || {};
|
const prev = cameraState.get(id) || {};
|
||||||
const next = { ...prev, ...updates };
|
const next = { ...prev, ...updates };
|
||||||
if (next.ts != null) {
|
|
||||||
next.stale = Date.now() - next.ts > STALE_AFTER_MS || !!next.stale;
|
|
||||||
} else {
|
|
||||||
next.stale = true;
|
|
||||||
}
|
|
||||||
cameraState.set(id, next);
|
cameraState.set(id, next);
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
@@ -37,8 +31,8 @@ async function fetchSnapshot(camera) {
|
|||||||
const arrayBuffer = await res.arrayBuffer();
|
const arrayBuffer = await res.arrayBuffer();
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
const ts = Date.now();
|
const ts = Date.now();
|
||||||
const next = markState(id, { frame: buffer, ts, error: null, failures: 0 });
|
markState(id, { frame: buffer, ts, error: null, failures: 0 });
|
||||||
events.emit('frame', { id, buffer, ts, stale: !!next.stale });
|
events.emit('frame', { id, buffer, ts });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const failures = (state?.failures || 0) + 1;
|
const failures = (state?.failures || 0) + 1;
|
||||||
markState(id, { error: err.message, failures });
|
markState(id, { error: err.message, failures });
|
||||||
@@ -70,11 +64,9 @@ function startAll() {
|
|||||||
function getState(id) {
|
function getState(id) {
|
||||||
const state = cameraState.get(id);
|
const state = cameraState.get(id);
|
||||||
if (!state) return null;
|
if (!state) return null;
|
||||||
const stale = state.ts == null || Date.now() - state.ts > STALE_AFTER_MS;
|
|
||||||
return {
|
return {
|
||||||
frame: state.frame || null,
|
frame: state.frame || null,
|
||||||
ts: state.ts || null,
|
ts: state.ts || null,
|
||||||
stale,
|
|
||||||
error: state.error || null,
|
error: state.error || null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,13 +82,13 @@ function sendStatus(socket, cameraId, status) {
|
|||||||
socket.emit('roomCamera:status', { id: cameraId, ...status });
|
socket.emit('roomCamera:status', { id: cameraId, ...status });
|
||||||
}
|
}
|
||||||
|
|
||||||
roomCameraStreamEvents.on('frame', ({ id, buffer, ts, stale }) => {
|
roomCameraStreamEvents.on('frame', ({ id, buffer, ts }) => {
|
||||||
const bucket = cameraSubscribers.get(id);
|
const bucket = cameraSubscribers.get(id);
|
||||||
if (!bucket || !buffer) return;
|
if (!bucket || !buffer) return;
|
||||||
bucket.forEach((socketId) => {
|
bucket.forEach((socketId) => {
|
||||||
const socket = io.sockets.sockets.get(socketId);
|
const socket = io.sockets.sockets.get(socketId);
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
sendFrame(socket, id, { ts, stale: !!stale }, buffer);
|
sendFrame(socket, id, { ts }, buffer);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,11 +123,10 @@ io.on('connection', (socket) => {
|
|||||||
validIds.forEach((cameraId) => {
|
validIds.forEach((cameraId) => {
|
||||||
const state = getRoomCameraState(cameraId);
|
const state = getRoomCameraState(cameraId);
|
||||||
if (state?.frame) {
|
if (state?.frame) {
|
||||||
sendFrame(socket, cameraId, { ts: state.ts, stale: !!state.stale }, state.frame);
|
sendFrame(socket, cameraId, { ts: state.ts }, state.frame);
|
||||||
}
|
}
|
||||||
sendStatus(socket, cameraId, {
|
sendStatus(socket, cameraId, {
|
||||||
ts: state?.ts || null,
|
ts: state?.ts || null,
|
||||||
stale: state?.stale ?? true,
|
|
||||||
error: state?.error || null,
|
error: state?.error || null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
import { useMemo } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
export default function RoomCameraFeed({ feed, label }) {
|
export default function RoomCameraFeed({ feed, label }) {
|
||||||
|
const [blink, setBlink] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!feed) return;
|
||||||
|
if (!feed.ts && !feed.objectUrl) return;
|
||||||
|
setBlink((prev) => !prev);
|
||||||
|
}, [feed?.ts, feed?.objectUrl]);
|
||||||
|
|
||||||
const statusText = useMemo(() => {
|
const statusText = useMemo(() => {
|
||||||
if (!feed) return 'Connecting…';
|
if (!feed) return 'Connecting…';
|
||||||
if (feed.error) return `Error: ${feed.error}`;
|
if (feed.error) return `Error: ${feed.error}`;
|
||||||
if (feed.status === 'playing' && feed.stale) return 'Stale frame';
|
|
||||||
return feed.status || 'Connecting…';
|
return feed.status || 'Connecting…';
|
||||||
}, [feed]);
|
}, [feed]);
|
||||||
|
|
||||||
@@ -18,8 +25,9 @@ export default function RoomCameraFeed({ feed, label }) {
|
|||||||
<div className="pointer-events-none absolute left-0 top-0 bg-black/70 px-0.5 py-0.5 text-xs font-semibold text-white">
|
<div className="pointer-events-none absolute left-0 top-0 bg-black/70 px-0.5 py-0.5 text-xs font-semibold text-white">
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
<div className="pointer-events-none absolute bottom-0 left-0 m-0.5 rounded bg-black/70 px-0.5 py-0.25 text-[0.7rem] text-slate-100">
|
<div className="pointer-events-none absolute bottom-0 left-0 m-0.5 flex items-center gap-1 rounded bg-black/70 px-0.5 py-0.25 text-[0.7rem] text-slate-100">
|
||||||
{statusText}
|
<span className={`h-2 w-2 rounded-full ${blink ? 'bg-emerald-400' : 'bg-slate-500'}`} />
|
||||||
|
<span>{statusText}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export function useRoomCameraSnapshots(sourceList = []) {
|
|||||||
...prev,
|
...prev,
|
||||||
[meta.id]: {
|
[meta.id]: {
|
||||||
status: 'playing',
|
status: 'playing',
|
||||||
stale: !!meta.stale,
|
|
||||||
ts: meta.ts || Date.now(),
|
ts: meta.ts || Date.now(),
|
||||||
error: null,
|
error: null,
|
||||||
objectUrl: url,
|
objectUrl: url,
|
||||||
@@ -60,7 +59,6 @@ export function useRoomCameraSnapshots(sourceList = []) {
|
|||||||
...(prev[meta.id] || {}),
|
...(prev[meta.id] || {}),
|
||||||
status: meta.error ? 'error' : prev[meta.id]?.status || 'connecting',
|
status: meta.error ? 'error' : prev[meta.id]?.status || 'connecting',
|
||||||
error: meta.error || null,
|
error: meta.error || null,
|
||||||
stale: meta.stale ?? prev[meta.id]?.stale ?? true,
|
|
||||||
ts: meta.ts || prev[meta.id]?.ts || null,
|
ts: meta.ts || prev[meta.id]?.ts || null,
|
||||||
objectUrl: prev[meta.id]?.objectUrl || null,
|
objectUrl: prev[meta.id]?.objectUrl || null,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user