Files
MultiRoombaRover/webui/src/display/ServerDisplayApp/components/DisplayRoverCell.jsx
T
legop3 aeeff6fe1b
Container image / image (push) Failing after 55s
spectate pages have been upgraded
2026-09-23 13:54:41 -04:00

114 lines
4.7 KiB
React

// Display Rover Cell
// Purpose: Shows one rover's room-readable driver and battery status.
// Scope: Reuses shared rover/battery presentation primitives while avoiding controls, queues, and video.
import { useVisualTelemetrySelector } from '../../../context/TelemetryContext.jsx';
import { batteryTelemetryEqual, selectBatteryTelemetry } from '../../../context/telemetryViews.js';
import BatteryBar from '../../../components/BatteryBar/index.jsx';
import RoverLabel from '../../../components/RoverLabel/index.jsx';
import AutoFitText from '../../../components/AutoFitText/index.jsx';
import RoverHelpOverlay from '../../../components/RoverHelpOverlay/index.jsx';
import {
buildRoverStateText,
findDriverForRover,
formatBatteryText,
getDisplayBatteryVisual,
} from '../utils.js';
function classNames(...values) {
return values.filter(Boolean).join(' ');
}
export default function DisplayRoverCell({ rover, session }) {
const batteryTelemetry = useVisualTelemetrySelector(rover?.id, selectBatteryTelemetry, batteryTelemetryEqual);
const frame = { sensors: batteryTelemetry };
const visual = getDisplayBatteryVisual({ rover, frame });
const driver = findDriverForRover({ roverId: rover?.id, session });
const stateText = buildRoverStateText(rover, visual);
const batteryText = formatBatteryText(visual);
const batteryVoltage = frame?.sensors.batteryVoltage;
const batteryCurrent = frame?.sensors.batteryCurrent;
const active = Boolean(driver);
const urgent = Boolean(visual?.urgentActive);
const warn = Boolean(visual?.warnActive);
const locked = Boolean(rover?.locked);
// console.log(batteryTelemetry)
// console.log(batteryVoltage)
return (
<article
className={classNames(
'relative min-h-0 overflow-hidden border border-slate-800 bg-black',
locked ? 'border-red-400 ring-4 ring-red-500/80' : '',
!locked && active ? 'ring-2 ring-sky-400/80' : '',
!locked && urgent ? 'ring-4 ring-red-500/90' : !locked && warn ? 'ring-2 ring-amber-300/80' : '',
)}
>
<RoverHelpOverlay active={Boolean(rover?.needsHelp)} />
<BatteryBar visual={visual} variant="background" orientation="vertical" />
<div className="relative z-10 grid min-w-0 gap-[0.55vh] p-[0.85vw] text-center">
<div className="grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-[0.8vw]">
<div className="min-w-0">
<RoverLabel
rover={rover}
fallback={rover?.id}
as={AutoFitText}
className="leading-none"
maxSize={108}
minSize={26}
/>
</div>
{locked ? (
// Keep the lock warning adjacent to the rover identity so the cell
// remains readable while still making the locked state impossible
// to miss at a glance.
<div className="max-w-[40vw] border-4 border-red-100 bg-red-700 px-[1vw] py-[0.55vh] text-center text-[clamp(2rem,5vh,5.2rem)] font-black leading-none text-white">
<div>LOCKED</div>
{rover?.lockReason ? (
<div className="mt-[0.35vh] text-[clamp(1.2rem,2.9vh,3rem)] leading-none text-red-50">
{rover.lockReason}
</div>
) : null}
</div>
) : null}
</div>
<div className="flex min-w-0 flex-wrap items-center justify-center gap-[1vw]">
<div className="min-w-0 flex-[1.45_1_12rem]">
<AutoFitText
className={classNames(
'font-black leading-none',
active ? 'text-white' : 'text-slate-400',
)}
maxSize={172}
minSize={38}
>
{driver?.label || 'Idle'}
</AutoFitText>
</div>
<div className="min-w-0 max-w-[18rem] flex-[0.85_1_9rem] bg-black/50 rounded-sm text-center">
<AutoFitText
className={classNames(
'font-black leading-none',
urgent ? 'text-red-100' : warn ? 'text-amber-100' : 'text-slate-100',
)}
maxSize={70}
minSize={32}
>
<div className='border-b'>{batteryText}</div>
<div>{batteryVoltage}v</div>
{/* <div className=''>{batteryCurrent}mA</div> */}
{(Math.abs(batteryCurrent) > 10) ? (<div className='border-t'> {batteryCurrent + 'mA'}</div>) : null}
</AutoFitText>
</div>
</div>
{stateText ? (
<div className="min-w-0 text-[clamp(1.25rem,2.5vh,2.6rem)] font-black leading-none text-amber-100">
{stateText}
</div>
) : (
<div aria-hidden="true" />
)}
</div>
</article>
);
}