mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
horn button light up
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
@@ -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-DfpLHSrv.js"></script>
|
<script type="module" crossorigin src="/assets/index-CqhqXcs8.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CQCz6Dh4.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-CQCz6Dh4.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ function formatDegrees(value) {
|
|||||||
|
|
||||||
export default function CameraServoPanel() {
|
export default function CameraServoPanel() {
|
||||||
const {
|
const {
|
||||||
state: { roverId, camera, keymap },
|
state: { roverId, camera, keymap, horn },
|
||||||
pipeline,
|
pipeline,
|
||||||
actions: { setServoAngle, nudgeServo, goServoHome, setNightVision, startHorn, stopHorn },
|
actions: { setServoAngle, nudgeServo, goServoHome, setNightVision, startHorn, stopHorn },
|
||||||
} = useControlSystem();
|
} = useControlSystem();
|
||||||
@@ -110,6 +110,7 @@ export default function CameraServoPanel() {
|
|||||||
onStart={startHorn}
|
onStart={startHorn}
|
||||||
onStop={stopHorn}
|
onStop={stopHorn}
|
||||||
keyLabel={hornKey}
|
keyLabel={hornKey}
|
||||||
|
active={horn?.active}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{enabled && (
|
{enabled && (
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export default function ControlSummary() {
|
|||||||
|
|
||||||
export function InlineCameraTilt({ keymap }) {
|
export function InlineCameraTilt({ keymap }) {
|
||||||
const {
|
const {
|
||||||
state: { roverId, camera },
|
state: { roverId, camera, horn },
|
||||||
pipeline,
|
pipeline,
|
||||||
actions: { setServoAngle, setNightVision, startHorn, stopHorn },
|
actions: { setServoAngle, setNightVision, startHorn, stopHorn },
|
||||||
} = useControlSystem();
|
} = useControlSystem();
|
||||||
@@ -132,7 +132,13 @@ export function InlineCameraTilt({ keymap }) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{hornAvailable && (
|
{hornAvailable && (
|
||||||
<HornControl disabled={!roverId} onStart={startHorn} onStop={stopHorn} keyLabel={hornLabel} />
|
<HornControl
|
||||||
|
disabled={!roverId}
|
||||||
|
onStart={startHorn}
|
||||||
|
onStop={stopHorn}
|
||||||
|
keyLabel={hornLabel}
|
||||||
|
active={horn?.active}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{enabled && (
|
{enabled && (
|
||||||
<div className="space-y-0.5 px-1 py-1">
|
<div className="space-y-0.5 px-1 py-1">
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export default function HornControl({
|
|||||||
onStart,
|
onStart,
|
||||||
onStop,
|
onStop,
|
||||||
keyLabel,
|
keyLabel,
|
||||||
|
active,
|
||||||
className = '',
|
className = '',
|
||||||
}) {
|
}) {
|
||||||
const [pressed, setPressed] = useState(false);
|
const [pressed, setPressed] = useState(false);
|
||||||
@@ -34,15 +35,16 @@ export default function HornControl({
|
|||||||
}
|
}
|
||||||
}, [hornSettings?.freqs, hornSettings?.waveform]);
|
}, [hornSettings?.freqs, hornSettings?.waveform]);
|
||||||
|
|
||||||
|
const isActive = Boolean(active);
|
||||||
const buttonClasses = useMemo(() => {
|
const buttonClasses = useMemo(() => {
|
||||||
const base =
|
const base =
|
||||||
'group flex w-full flex-col gap-0.5 rounded-xl border-2 px-1 py-1.5 text-xs font-semibold';
|
'group flex w-full flex-col gap-0.5 rounded-xl border-2 px-1 py-1.5 text-xs font-semibold';
|
||||||
const active = 'border-fuchsia-300/70 bg-fuchsia-700 text-fuchsia-50';
|
const active = 'border-fuchsia-300/70 bg-fuchsia-700 text-fuchsia-50';
|
||||||
const inactive = 'border-cyan-300/70 bg-cyan-900 text-cyan-50 hover:bg-cyan-800';
|
const inactive = 'border-cyan-300/70 bg-cyan-900 text-cyan-50 hover:bg-cyan-800';
|
||||||
return [base, pressed ? active : inactive, 'disabled:opacity-50', className]
|
return [base, isActive ? active : inactive, 'disabled:opacity-50', className]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}, [className, pressed]);
|
}, [className, isActive]);
|
||||||
|
|
||||||
const start = () => {
|
const start = () => {
|
||||||
if (disabled) return;
|
if (disabled) return;
|
||||||
@@ -120,10 +122,10 @@ export default function HornControl({
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`rounded px-1 py-0.5 text-[0.65rem] font-semibold ${
|
className={`rounded px-1 py-0.5 text-[0.65rem] font-semibold ${
|
||||||
pressed ? 'bg-fuchsia-200 text-fuchsia-900' : 'bg-slate-800 text-slate-200'
|
isActive ? 'bg-fuchsia-200 text-fuchsia-900' : 'bg-slate-800 text-slate-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{pressed ? 'Honk' : 'Hold'}
|
{isActive ? 'Honk' : 'Hold'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-[minmax(0,1.2fr)_repeat(4,minmax(0,1fr))] items-center gap-0.5 text-[0.65rem]">
|
<div className="grid grid-cols-[minmax(0,1.2fr)_repeat(4,minmax(0,1fr))] items-center gap-0.5 text-[0.65rem]">
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ function FloatingJoystick({ disabled, layout, radius, onMove, onStop }) {
|
|||||||
|
|
||||||
function MobileJoystickPanel({ layout }) {
|
function MobileJoystickPanel({ layout }) {
|
||||||
const {
|
const {
|
||||||
state: { roverId, camera },
|
state: { roverId, camera, horn },
|
||||||
pipeline,
|
pipeline,
|
||||||
actions: { setDriveVector, registerInputState, setServoAngle, setNightVision, startHorn, stopHorn },
|
actions: { setDriveVector, registerInputState, setServoAngle, setNightVision, startHorn, stopHorn },
|
||||||
} = useControlSystem();
|
} = useControlSystem();
|
||||||
@@ -226,7 +226,7 @@ function MobileJoystickPanel({ layout }) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{hornAvailable && (
|
{hornAvailable && (
|
||||||
<HornControl disabled={disabled} onStart={startHorn} onStop={stopHorn} />
|
<HornControl disabled={disabled} onStart={startHorn} onStop={stopHorn} active={horn?.active} />
|
||||||
)}
|
)}
|
||||||
{cameraEnabled && (
|
{cameraEnabled && (
|
||||||
<div className="bg-zinc-950 p-0.5 text-xs">
|
<div className="bg-zinc-950 p-0.5 text-xs">
|
||||||
|
|||||||
@@ -365,13 +365,15 @@ export function ControlSystemProvider({ children }) {
|
|||||||
const startHorn = useCallback(() => {
|
const startHorn = useCallback(() => {
|
||||||
if (!pipeline.horn) return;
|
if (!pipeline.horn) return;
|
||||||
pipeline.sendHorn({ action: 'start', ...normalizedHornSettings });
|
pipeline.sendHorn({ action: 'start', ...normalizedHornSettings });
|
||||||
|
dispatch({ type: 'control/set-horn-active', payload: true });
|
||||||
recordControlIntent();
|
recordControlIntent();
|
||||||
}, [normalizedHornSettings, pipeline, recordControlIntent]);
|
}, [dispatch, normalizedHornSettings, pipeline, recordControlIntent]);
|
||||||
|
|
||||||
const stopHorn = useCallback(() => {
|
const stopHorn = useCallback(() => {
|
||||||
if (!pipeline.horn) return;
|
if (!pipeline.horn) return;
|
||||||
pipeline.sendHorn({ action: 'stop' });
|
pipeline.sendHorn({ action: 'stop' });
|
||||||
}, [pipeline]);
|
dispatch({ type: 'control/set-horn-active', payload: false });
|
||||||
|
}, [dispatch, pipeline]);
|
||||||
|
|
||||||
const registerInputState = useCallback((source, data) => {
|
const registerInputState = useCallback((source, data) => {
|
||||||
dispatch({ type: 'control/register-input-state', payload: { source, state: data } });
|
dispatch({ type: 'control/register-input-state', payload: { source, state: data } });
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ function createSongState() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createHornState() {
|
||||||
|
return {
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const initialControlState = {
|
export const initialControlState = {
|
||||||
roverId: null,
|
roverId: null,
|
||||||
mode: 'drive',
|
mode: 'drive',
|
||||||
@@ -34,6 +40,7 @@ export const initialControlState = {
|
|||||||
aux: createAuxState(),
|
aux: createAuxState(),
|
||||||
camera: createCameraState(),
|
camera: createCameraState(),
|
||||||
song: createSongState(),
|
song: createSongState(),
|
||||||
|
horn: createHornState(),
|
||||||
lastControlIntentAt: 0,
|
lastControlIntentAt: 0,
|
||||||
macros: DEFAULT_MACROS,
|
macros: DEFAULT_MACROS,
|
||||||
keymap: DEFAULT_KEYMAP,
|
keymap: DEFAULT_KEYMAP,
|
||||||
@@ -49,6 +56,7 @@ export function controlReducer(state, action) {
|
|||||||
drive: action.payload ? state.drive : createDriveState(),
|
drive: action.payload ? state.drive : createDriveState(),
|
||||||
aux: action.payload ? state.aux : createAuxState(),
|
aux: action.payload ? state.aux : createAuxState(),
|
||||||
song: action.payload ? state.song : createSongState(),
|
song: action.payload ? state.song : createSongState(),
|
||||||
|
horn: action.payload ? state.horn : createHornState(),
|
||||||
lastControlIntentAt: action.payload ? state.lastControlIntentAt : 0,
|
lastControlIntentAt: action.payload ? state.lastControlIntentAt : 0,
|
||||||
};
|
};
|
||||||
case 'control/set-mode':
|
case 'control/set-mode':
|
||||||
@@ -125,8 +133,17 @@ export function controlReducer(state, action) {
|
|||||||
drive: createDriveState(),
|
drive: createDriveState(),
|
||||||
aux: createAuxState(),
|
aux: createAuxState(),
|
||||||
song: createSongState(),
|
song: createSongState(),
|
||||||
|
horn: createHornState(),
|
||||||
lastControlIntentAt: 0,
|
lastControlIntentAt: 0,
|
||||||
};
|
};
|
||||||
|
case 'control/set-horn-active':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
horn: {
|
||||||
|
...(state.horn || createHornState()),
|
||||||
|
active: Boolean(action.payload),
|
||||||
|
},
|
||||||
|
};
|
||||||
case 'control/record-intent':
|
case 'control/record-intent':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
Reference in New Issue
Block a user