pass 1 (broken)

This commit is contained in:
legop3
2026-05-02 15:39:38 -04:00
parent 53cf39e7fb
commit ba4b6cfc78
17 changed files with 247 additions and 182 deletions
+69 -12
View File
@@ -2,27 +2,75 @@
// Purpose: Maintains shared telemetry snapshots and rover status streams for UI consumers. Scope: Subscribes to telemetry events and exposes normalized read APIs to components.
/* eslint-disable react-refresh/only-export-components */
import { createContext, useContext, useMemo, useState, useEffect } from 'react';
import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
import { useSocket } from './SocketContext.jsx';
const TelemetryContext = createContext({ frames: {} });
const EMPTY_FRAMES = Object.freeze({});
const EMPTY_FRAME = null;
const TelemetryContext = createContext(null);
export function TelemetryProvider({ children }) {
const socket = useSocket();
const [frames, setFrames] = useState({});
const framesRef = useRef({});
const roverSubscribersRef = useRef(new Map());
const allSubscribersRef = useRef(new Set());
const notifyRover = (roverId) => {
const listeners = roverSubscribersRef.current.get(roverId);
if (listeners) {
listeners.forEach((listener) => listener());
}
allSubscribersRef.current.forEach((listener) => listener());
};
const store = useMemo(
() => ({
getFrames: () => framesRef.current,
getFrame: (roverId) => {
if (!roverId) return EMPTY_FRAME;
return framesRef.current[roverId] ?? EMPTY_FRAME;
},
subscribeAll: (listener) => {
allSubscribersRef.current.add(listener);
return () => {
allSubscribersRef.current.delete(listener);
};
},
subscribeRover: (roverId, listener) => {
if (!roverId) return () => {};
let listeners = roverSubscribersRef.current.get(roverId);
if (!listeners) {
listeners = new Set();
roverSubscribersRef.current.set(roverId, listeners);
}
listeners.add(listener);
return () => {
const current = roverSubscribersRef.current.get(roverId);
if (!current) return;
current.delete(listener);
if (!current.size) {
roverSubscribersRef.current.delete(roverId);
}
};
},
}),
[],
);
useEffect(() => {
function handleSensorFrame({ roverId, sensors = {}, frame = {} }) {
if (!roverId) return;
setFrames((prev) => ({
...prev,
framesRef.current = {
...framesRef.current,
[roverId]: {
roverId,
sensors,
raw: frame?.data || null,
receivedAt: Date.now(),
},
}));
};
notifyRover(roverId);
}
socket.on('sensorFrame', handleSensorFrame);
@@ -31,16 +79,25 @@ export function TelemetryProvider({ children }) {
};
}, [socket]);
const value = useMemo(() => ({ frames }), [frames]);
return <TelemetryContext.Provider value={value}>{children}</TelemetryContext.Provider>;
return <TelemetryContext.Provider value={store}>{children}</TelemetryContext.Provider>;
}
export function useTelemetryFrames() {
return useContext(TelemetryContext).frames;
const store = useContext(TelemetryContext);
if (!store) {
throw new Error('useTelemetryFrames must be used within TelemetryProvider');
}
return useSyncExternalStore(store.subscribeAll, store.getFrames, () => EMPTY_FRAMES);
}
export function useTelemetryFrame(roverId) {
const frames = useTelemetryFrames();
if (!roverId) return null;
return frames[roverId] ?? null;
const store = useContext(TelemetryContext);
if (!store) {
throw new Error('useTelemetryFrame must be used within TelemetryProvider');
}
return useSyncExternalStore(
(listener) => store.subscribeRover(roverId, listener),
() => store.getFrame(roverId),
() => EMPTY_FRAME,
);
}