mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
better dockdrive info labels
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
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-DWCUBkjO.js"></script>
|
<script type="module" crossorigin src="/assets/index-CuKbE2TD.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D-qq3X_S.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-CVqWOsms.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -21,15 +21,21 @@ export function useDriveDockState(roverId) {
|
|||||||
return useMemo(() => deriveDriveDockState(frame), [frame]);
|
return useMemo(() => deriveDriveDockState(frame), [frame]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatusPill({ label, active }) {
|
function StatusRow({ label, value, tone = 'neutral' }) {
|
||||||
|
const toneClasses =
|
||||||
|
tone === 'good'
|
||||||
|
? 'border-emerald-200 bg-emerald-600 text-white'
|
||||||
|
: tone === 'warn'
|
||||||
|
? 'border-amber-200 bg-amber-600 text-white'
|
||||||
|
: tone === 'bad'
|
||||||
|
? 'border-red-200 bg-red-600 text-white'
|
||||||
|
: 'border-slate-200/30 bg-slate-700 text-slate-100';
|
||||||
return (
|
return (
|
||||||
<span
|
<div
|
||||||
className={`rounded-xl px-1.5 py-0.5 text-[0.75rem] font-semibold ${
|
className={`flex flex-1 flex-col items-center justify-center rounded-lg border px-0.75 py-0.5 text-center ${toneClasses}`}
|
||||||
active ? 'bg-emerald-600 text-white' : 'bg-red-600 text-white'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{label}
|
<span className="text-base font-semibold md:text-lg">{value}</span>
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +99,7 @@ export default function DriveDockAction({ layout = 'desktop', expand = false, dr
|
|||||||
} = useControlSystem();
|
} = useControlSystem();
|
||||||
const frame = useTelemetryFrame(roverId);
|
const frame = useTelemetryFrame(roverId);
|
||||||
const state = driveDockState ?? deriveDriveDockState(frame);
|
const state = driveDockState ?? deriveDriveDockState(frame);
|
||||||
const { driving, docked, charging, dockedNotCharging, dockingInProgress, oiLabel } = state;
|
const { driving, docked, charging, dockingInProgress } = state;
|
||||||
const [pending, setPending] = useState(null);
|
const [pending, setPending] = useState(null);
|
||||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
@@ -114,6 +120,10 @@ export default function DriveDockAction({ layout = 'desktop', expand = false, dr
|
|||||||
summary: 'You must enable driving mode before you can move the rover.',
|
summary: 'You must enable driving mode before you can move the rover.',
|
||||||
steps: ['Press the keybind or tap this button to enable driving mode', 'Once ready (should be instant), this dialog will change, and you will be able to move'],
|
steps: ['Press the keybind or tap this button to enable driving mode', 'Once ready (should be instant), this dialog will change, and you will be able to move'],
|
||||||
};
|
};
|
||||||
|
const dockValue = docked ? 'Docked' : 'Undocked';
|
||||||
|
const dockTone = docked ? 'good' : 'bad';
|
||||||
|
const chargeValue = charging ? 'Charging' : docked ? 'Not charging yet' : '—';
|
||||||
|
const chargeTone = charging ? 'good' : docked ? 'warn' : 'bad';
|
||||||
|
|
||||||
const handleReturnToDrive = async () => {
|
const handleReturnToDrive = async () => {
|
||||||
if (!roverId || pending) return;
|
if (!roverId || pending) return;
|
||||||
@@ -187,18 +197,21 @@ export default function DriveDockAction({ layout = 'desktop', expand = false, dr
|
|||||||
className={`${baseCardClasses} ${filledHeight} ${ctaTextAndLayout} ${ctaSize} ${emeraldCta}`}
|
className={`${baseCardClasses} ${filledHeight} ${ctaTextAndLayout} ${ctaSize} ${emeraldCta}`}
|
||||||
>
|
>
|
||||||
<div className="space-y-0.5 w-full">
|
<div className="space-y-0.5 w-full">
|
||||||
<div className="flex flex-wrap items-center justify-center gap-0.5">
|
<div className="flex w-full flex-col items-center gap-0.25">
|
||||||
<span className="text-base font-semibold text-emerald-50">Start Driving</span>
|
<span className="text-base font-semibold text-emerald-50 md:text-lg">Start Driving</span>
|
||||||
{!isMobile && driveKeyLabel ? <KeyPill label={driveKeyLabel} /> : null}
|
{!isMobile ? (
|
||||||
{!isMobile ? <ActionPill label="Click to start" tone="emerald" /> : null}
|
<div className="flex flex-wrap items-center justify-center gap-0.5">
|
||||||
|
{driveKeyLabel ? <KeyPill label={driveKeyLabel} /> : null}
|
||||||
|
<ActionPill label="Click to start" tone="emerald" />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-emerald-50/90">{startDriveInstructions.summary}</p>
|
<p className="text-sm text-emerald-50/90">{startDriveInstructions.summary}</p>
|
||||||
<StepList steps={startDriveInstructions.steps} tone="emerald" />
|
<StepList steps={startDriveInstructions.steps} tone="emerald" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-0.5">
|
<div className="flex w-full flex-1 flex-col gap-0.5 self-stretch">
|
||||||
<StatusPill label={`OI: ${oiLabel}`} active={oiLabel.toLowerCase() === 'full'} />
|
<StatusRow label="Dock" value={dockValue} tone={dockTone} />
|
||||||
<StatusPill label={docked ? 'Docked!' : 'Not docked'} active={docked} />
|
<StatusRow label="Charge" value={chargeValue} tone={chargeTone} />
|
||||||
<StatusPill label={charging ? 'Charging!' : 'Not charging'} active={charging} />
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@@ -218,15 +231,19 @@ export default function DriveDockAction({ layout = 'desktop', expand = false, dr
|
|||||||
className={`${baseCardClasses} ${filledHeight} ${ctaTextAndLayout} ${ctaSize} ${amberCta}`}
|
className={`${baseCardClasses} ${filledHeight} ${ctaTextAndLayout} ${ctaSize} ${amberCta}`}
|
||||||
>
|
>
|
||||||
<div className="space-y-0.5 w-full">
|
<div className="space-y-0.5 w-full">
|
||||||
<div className="flex flex-wrap items-center justify-center gap-0.5">
|
<div className="flex w-full flex-col items-center gap-0.25">
|
||||||
<span className="text-base font-semibold text-amber-50">Docking in Progress</span>
|
<span className="text-base font-semibold text-amber-50 md:text-lg">Docking in Progress</span>
|
||||||
{!isMobile ? <ActionPill label="Click to return to driving mode" tone="amber" /> : null}
|
{!isMobile ? (
|
||||||
|
<div className="flex flex-wrap items-center justify-center gap-0.5">
|
||||||
|
<ActionPill label="Click to return to driving mode" tone="amber" />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-amber-50/90">{inProgressCopy.summary}</p>
|
<p className="text-sm text-amber-50/90">{inProgressCopy.summary}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-0.5">
|
<div className="flex w-full flex-1 flex-col gap-0.5 self-stretch">
|
||||||
<StatusPill label={docked ? 'Docked' : 'Not docked'} active={docked} />
|
<StatusRow label="Dock" value={dockValue} tone={dockTone} />
|
||||||
<StatusPill label={charging ? 'Charging' : 'Not charging'} active={charging} />
|
<StatusRow label="Charge" value={chargeValue} tone={chargeTone} />
|
||||||
</div>
|
</div>
|
||||||
<StepList steps={inProgressCopy.steps} tone="amber" />
|
<StepList steps={inProgressCopy.steps} tone="amber" />
|
||||||
</button>
|
</button>
|
||||||
@@ -245,20 +262,21 @@ export default function DriveDockAction({ layout = 'desktop', expand = false, dr
|
|||||||
: `${baseCardClasses} ${filledHeight} ${ctaTextAndLayout} ${ctaSize} ${indigoCta}`
|
: `${baseCardClasses} ${filledHeight} ${ctaTextAndLayout} ${ctaSize} ${indigoCta}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="flex flex-wrap items-center justify-center gap-0.5 w-full">
|
<div className="flex w-full flex-col items-center gap-0.25">
|
||||||
<span className="text-base font-semibold text-indigo-50">Dock and Charge</span>
|
<span className="text-base font-semibold text-indigo-50 md:text-lg">Dock and Charge</span>
|
||||||
{!isMobile && dockKeyLabel ? <KeyPill label={dockKeyLabel} /> : null}
|
{!isMobile ? (
|
||||||
{!isMobile ? <ActionPill label="Click to begin docking" tone="indigo" /> : null}
|
<div className="flex flex-wrap items-center justify-center gap-0.5">
|
||||||
|
{dockKeyLabel ? <KeyPill label={dockKeyLabel} /> : null}
|
||||||
|
<ActionPill label="Click to begin docking" tone="indigo" />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<>
|
<>
|
||||||
<p className="text-sm text-indigo-50/90">{dockButtonCaption}</p>
|
<p className="text-sm text-indigo-50/90">{dockButtonCaption}</p>
|
||||||
<div className="flex w-full flex-col gap-0.5">
|
<div className="flex w-full flex-1 flex-col gap-0.5 self-stretch">
|
||||||
<StatusPill label={docked ? 'Docked' : 'Not docked'} active={docked} />
|
<StatusRow label="Dock" value={dockValue} tone={dockTone} />
|
||||||
<StatusPill label={charging ? 'Charging' : 'Not charging'} active={charging} />
|
<StatusRow label="Charge" value={chargeValue} tone={chargeTone} />
|
||||||
{dockedNotCharging ? (
|
|
||||||
<StatusPill label="Not charging yet" active={false} />
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-indigo-100/80">
|
<div className="text-xs text-indigo-100/80">
|
||||||
Line up straight, about 1 foot away, before docking.
|
Line up straight, about 1 foot away, before docking.
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const ACTIONS = [
|
|||||||
{ id: 'sideReverse', label: 'Side reverse toggle', kind: 'button', section: 'Brush toggles' },
|
{ id: 'sideReverse', label: 'Side reverse toggle', kind: 'button', section: 'Brush toggles' },
|
||||||
{ id: 'driveMacro', label: 'Drive macro', kind: 'button', section: 'Mode macros' },
|
{ id: 'driveMacro', label: 'Drive macro', kind: 'button', section: 'Mode macros' },
|
||||||
{ id: 'dockMacro', label: 'Dock macro', kind: 'button', section: 'Mode macros' },
|
{ id: 'dockMacro', label: 'Dock macro', kind: 'button', section: 'Mode macros' },
|
||||||
|
{ id: 'nightVisionToggle', label: 'Night vision toggle', kind: 'button', section: 'Camera' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const CAPTURE_AXIS_THRESHOLD = 0.45;
|
const CAPTURE_AXIS_THRESHOLD = 0.45;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default function GamepadInputManager() {
|
|||||||
setAuxMotors,
|
setAuxMotors,
|
||||||
setServoAngle,
|
setServoAngle,
|
||||||
runMacro,
|
runMacro,
|
||||||
|
toggleNightVision,
|
||||||
registerInputState,
|
registerInputState,
|
||||||
},
|
},
|
||||||
} = useControlSystem();
|
} = useControlSystem();
|
||||||
@@ -212,6 +213,12 @@ export default function GamepadInputManager() {
|
|||||||
handleButtonEdge('dockMacro', false);
|
handleButtonEdge('dockMacro', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outputs.buttons.nightVisionToggle && handleButtonEdge('nightVisionToggle', true)) {
|
||||||
|
toggleNightVision();
|
||||||
|
} else if (!outputs.buttons.nightVisionToggle) {
|
||||||
|
handleButtonEdge('nightVisionToggle', false);
|
||||||
|
}
|
||||||
|
|
||||||
if (Math.abs(outputs.cameraAxis) > 0.001) {
|
if (Math.abs(outputs.cameraAxis) > 0.001) {
|
||||||
handleCameraAxis(outputs.cameraAxis, profile.calibration);
|
handleCameraAxis(outputs.cameraAxis, profile.calibration);
|
||||||
}
|
}
|
||||||
@@ -241,6 +248,7 @@ export default function GamepadInputManager() {
|
|||||||
setAuxMotors,
|
setAuxMotors,
|
||||||
setDriveVector,
|
setDriveVector,
|
||||||
setMode,
|
setMode,
|
||||||
|
toggleNightVision,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ export function computeGamepadOutputs(padState, profile) {
|
|||||||
const sideReverseSource = resolveButtonSource(padState, bindings.sideReverse?.sources);
|
const sideReverseSource = resolveButtonSource(padState, bindings.sideReverse?.sources);
|
||||||
const driveMacroSource = resolveButtonSource(padState, bindings.driveMacro?.sources);
|
const driveMacroSource = resolveButtonSource(padState, bindings.driveMacro?.sources);
|
||||||
const dockMacroSource = resolveButtonSource(padState, bindings.dockMacro?.sources);
|
const dockMacroSource = resolveButtonSource(padState, bindings.dockMacro?.sources);
|
||||||
|
const nightVisionSource = resolveButtonSource(padState, bindings.nightVisionToggle?.sources);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
driveVector: { x: driveX, y: driveY, boost: false },
|
driveVector: { x: driveX, y: driveY, boost: false },
|
||||||
@@ -173,6 +174,7 @@ export function computeGamepadOutputs(padState, profile) {
|
|||||||
sideReverse: sideReverseSource.pressed,
|
sideReverse: sideReverseSource.pressed,
|
||||||
driveMacro: driveMacroSource.pressed,
|
driveMacro: driveMacroSource.pressed,
|
||||||
dockMacro: dockMacroSource.pressed,
|
dockMacro: dockMacroSource.pressed,
|
||||||
|
nightVisionToggle: nightVisionSource.pressed,
|
||||||
},
|
},
|
||||||
sources: {
|
sources: {
|
||||||
drive: driveSource.source,
|
drive: driveSource.source,
|
||||||
@@ -185,6 +187,7 @@ export function computeGamepadOutputs(padState, profile) {
|
|||||||
sideReverse: sideReverseSource.source,
|
sideReverse: sideReverseSource.source,
|
||||||
driveMacro: driveMacroSource.source,
|
driveMacro: driveMacroSource.source,
|
||||||
dockMacro: dockMacroSource.source,
|
dockMacro: dockMacroSource.source,
|
||||||
|
nightVisionToggle: nightVisionSource.source,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ export const GAMEPAD_PROFILE_DEFAULT = {
|
|||||||
kind: 'button',
|
kind: 'button',
|
||||||
sources: [{ kind: 'button', index: 3 }],
|
sources: [{ kind: 'button', index: 3 }],
|
||||||
},
|
},
|
||||||
|
nightVisionToggle: {
|
||||||
|
kind: 'button',
|
||||||
|
sources: [{ kind: 'button', index: 9 }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user