This commit is contained in:
legop3
2026-01-21 03:08:37 -05:00
parent fd7b858395
commit 6009056edd
4 changed files with 34 additions and 51 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-DNer42rE.js"></script> <script type="module" crossorigin src="/assets/index-ZItnCUqh.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BBEWYgmH.css"> <link rel="stylesheet" crossorigin href="/assets/index-BBEWYgmH.css">
</head> </head>
<body> <body>
@@ -39,7 +39,7 @@ export default function OvercurrentLimiterPanel() {
) : ( ) : (
<div className="space-y-0.5"> <div className="space-y-0.5">
{groups.map((key) => { {groups.map((key) => {
const temperature = overcurrentLimiter?.temperatures?.[key] ?? 0; const cap = overcurrentLimiter?.caps?.[key] ?? 0;
const over = overcurrentLimiter?.overcurrent?.groups?.[key] ?? false; const over = overcurrentLimiter?.overcurrent?.groups?.[key] ?? false;
const scale = overcurrentLimiter?.scales?.perGroup?.[key] ?? 1; const scale = overcurrentLimiter?.scales?.perGroup?.[key] ?? 1;
return ( return (
@@ -52,10 +52,10 @@ export default function OvercurrentLimiterPanel() {
</div> </div>
<div className="space-y-0.5"> <div className="space-y-0.5">
<div className="flex items-center justify-between text-[0.7rem] text-slate-400"> <div className="flex items-center justify-between text-[0.7rem] text-slate-400">
<span>Temperature</span> <span>Cap</span>
<span>{formatPct(temperature)}</span> <span>{formatPct(cap)}</span>
</div> </div>
<ProgressBar value={temperature} color="bg-amber-500" /> <ProgressBar value={cap} color="bg-amber-500" />
<div className="flex items-center justify-between text-[0.7rem] text-slate-400"> <div className="flex items-center justify-between text-[0.7rem] text-slate-400">
<span>Scale</span> <span>Scale</span>
<span>{formatPct(scale)}</span> <span>{formatPct(scale)}</span>
@@ -65,8 +65,7 @@ export default function OvercurrentLimiterPanel() {
); );
})} })}
<div className="surface text-[0.7rem] text-slate-400"> <div className="surface text-[0.7rem] text-slate-400">
<div>{`Heat up ${overcurrentLimiter?.config?.heatUpSec}s · Cool down ${overcurrentLimiter?.config?.coolDownSec}s`}</div> <div>{`Down rate ${overcurrentLimiter?.config?.downRatePerSec}/s · Up rate ${overcurrentLimiter?.config?.upRatePerSec}/s`}</div>
<div>{`Knee ${overcurrentLimiter?.config?.kneeSec}s`}</div>
<div>{`Output rate ${overcurrentLimiter?.config?.outputRateMs}ms`}</div> <div>{`Output rate ${overcurrentLimiter?.config?.outputRateMs}ms`}</div>
</div> </div>
</div> </div>
+20 -36
View File
@@ -8,15 +8,14 @@ export const OVERCURRENT_GROUPS = [
]; ];
export const DEFAULT_OVERCURRENT_LIMITS = { export const DEFAULT_OVERCURRENT_LIMITS = {
heatUpSec: 2, downRatePerSec: 0.25,
coolDownSec: 12, upRatePerSec: 0.1,
kneeSec: 0,
outputRateMs: 250, outputRateMs: 250,
}; };
function createInitialTemperatures() { function createInitialCaps() {
return OVERCURRENT_GROUPS.reduce((acc, group) => { return OVERCURRENT_GROUPS.reduce((acc, group) => {
acc[group.key] = 0; acc[group.key] = 1;
return acc; return acc;
}, {}); }, {});
} }
@@ -26,14 +25,6 @@ function clampUnit(value) {
return Math.max(0, Math.min(1, value)); return Math.max(0, Math.min(1, value));
} }
function stepValue(current, delta, seconds, direction) {
if (!Number.isFinite(seconds) || seconds <= 0) {
return direction === 'up' ? 1 : 0;
}
const amount = delta / seconds;
return direction === 'up' ? current + amount : current - amount;
}
export function useOvercurrentLimiter(roverId, options = {}) { export function useOvercurrentLimiter(roverId, options = {}) {
const { session } = useSession(); const { session } = useSession();
const frame = useTelemetryFrame(roverId); const frame = useTelemetryFrame(roverId);
@@ -43,7 +34,7 @@ export function useOvercurrentLimiter(roverId, options = {}) {
() => ({ ...DEFAULT_OVERCURRENT_LIMITS, ...(options.config || {}) }), () => ({ ...DEFAULT_OVERCURRENT_LIMITS, ...(options.config || {}) }),
[options.config], [options.config],
); );
const [temperatures, setTemperatures] = useState(() => createInitialTemperatures()); const [caps, setCaps] = useState(() => createInitialCaps());
const lastTickRef = useRef(typeof performance !== 'undefined' ? performance.now() : Date.now()); const lastTickRef = useRef(typeof performance !== 'undefined' ? performance.now() : Date.now());
const flagsRef = useRef(overcurrentFlags); const flagsRef = useRef(overcurrentFlags);
@@ -52,7 +43,7 @@ export function useOvercurrentLimiter(roverId, options = {}) {
}, [overcurrentFlags]); }, [overcurrentFlags]);
useEffect(() => { useEffect(() => {
setTemperatures(createInitialTemperatures()); setCaps(createInitialCaps());
}, [roverId]); }, [roverId]);
useEffect(() => { useEffect(() => {
@@ -62,39 +53,32 @@ export function useOvercurrentLimiter(roverId, options = {}) {
lastTickRef.current = now; lastTickRef.current = now;
const deltaSec = deltaMs / 1000; const deltaSec = deltaMs / 1000;
if (deltaSec <= 0) return; if (deltaSec <= 0) return;
setTemperatures((prev) => { setCaps((prev) => {
let changed = false; let changed = false;
const next = {}; const next = {};
const downRate = Number.isFinite(config.downRatePerSec) ? Math.max(0, config.downRatePerSec) : 0;
const upRate = Number.isFinite(config.upRatePerSec) ? Math.max(0, config.upRatePerSec) : 0;
OVERCURRENT_GROUPS.forEach((group) => { OVERCURRENT_GROUPS.forEach((group) => {
const prevEntry = Number.isFinite(prev[group.key]) ? prev[group.key] : 0; const prevEntry = Number.isFinite(prev[group.key]) ? prev[group.key] : 1;
const over = group.motors.some((motor) => Boolean(flagsRef.current?.[motor])); const over = group.motors.some((motor) => Boolean(flagsRef.current?.[motor]));
const nextTemp = clampUnit( const nextCap = clampUnit(
stepValue( over ? prevEntry - downRate * deltaSec : prevEntry + upRate * deltaSec,
prevEntry,
deltaSec,
over ? config.heatUpSec : config.coolDownSec,
over ? 'up' : 'down',
),
); );
if (Math.abs(nextTemp - prevEntry) > 0.0001) { if (Math.abs(nextCap - prevEntry) > 0.0001) {
changed = true; changed = true;
} }
next[group.key] = nextTemp; next[group.key] = nextCap;
}); });
return changed ? next : prev; return changed ? next : prev;
}); });
}, 100); }, 100);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [config.heatUpSec, config.coolDownSec]); }, [config.downRatePerSec, config.upRatePerSec]);
const scales = useMemo(() => { const scales = useMemo(() => {
const kneeSec = Number.isFinite(config.kneeSec) ? Math.max(0, config.kneeSec) : 0;
const heatUpSec = Number.isFinite(config.heatUpSec) ? Math.max(0.001, config.heatUpSec) : 0.001;
const kneeTemp = clampUnit(kneeSec / heatUpSec);
const perGroup = OVERCURRENT_GROUPS.reduce((acc, group) => { const perGroup = OVERCURRENT_GROUPS.reduce((acc, group) => {
const temp = Number.isFinite(temperatures?.[group.key]) ? temperatures[group.key] : 0; const cap = Number.isFinite(caps?.[group.key]) ? caps[group.key] : 1;
const normalized = kneeTemp >= 1 ? 0 : clampUnit((temp - kneeTemp) / (1 - kneeTemp)); acc[group.key] = clampUnit(cap);
acc[group.key] = clampUnit(1 - normalized);
return acc; return acc;
}, {}); }, {});
return { return {
@@ -109,7 +93,7 @@ export function useOvercurrentLimiter(roverId, options = {}) {
vacuum: 1, vacuum: 1,
}, },
}; };
}, [config.heatUpSec, config.kneeSec, temperatures]); }, [caps]);
const overcurrent = useMemo(() => { const overcurrent = useMemo(() => {
const motors = {}; const motors = {};
@@ -132,14 +116,14 @@ export function useOvercurrentLimiter(roverId, options = {}) {
return useMemo( return useMemo(
() => ({ () => ({
temperatures, caps,
overcurrent, overcurrent,
scales, scales,
isActive: (scales?.drive?.left ?? 1) < 1 || (scales?.drive?.right ?? 1) < 1 || (scales?.aux?.main ?? 1) < 1 || (scales?.aux?.side ?? 1) < 1, isActive: (scales?.drive?.left ?? 1) < 1 || (scales?.drive?.right ?? 1) < 1 || (scales?.aux?.main ?? 1) < 1 || (scales?.aux?.side ?? 1) < 1,
config, config,
adminImmune, adminImmune,
}), }),
[temperatures, overcurrent, scales, config, adminImmune], [caps, overcurrent, scales, config, adminImmune],
); );
} }