From 09ccbbcdb57c1655005517e61ff26c73f22b51ca Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Mon, 12 Aug 2024 13:53:32 -0400 Subject: [PATCH] fix(app): Debounce jog controls during DTWiz (#15968) Closes RQA-2951 In Error Recovery (and DTWiz in general), the pipette is z-homed before users move to a location to drop tips. Because this location is quite far from the deck, users will likely want to press their arrow keys to jog the pipette quickly, and this will in turn cause the robot to execute potentially 50+ moveRelative commands with no way of stopping it. While not debouncing other flows with jog controls (ex, LPC) has historical context and is less prone to users over-clicking the jog buttons, we should debounce the jog controls within dt wiz. The solution here is to allow a queue of MAX_QUEUED_JOGS. Three is reasonable after testing. --- .../hooks/useDropTipCommands.ts | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/app/src/organisms/DropTipWizardFlows/hooks/useDropTipCommands.ts b/app/src/organisms/DropTipWizardFlows/hooks/useDropTipCommands.ts index 44679342b82..c6921c5e203 100644 --- a/app/src/organisms/DropTipWizardFlows/hooks/useDropTipCommands.ts +++ b/app/src/organisms/DropTipWizardFlows/hooks/useDropTipCommands.ts @@ -21,6 +21,7 @@ import type { RunCommandByCommandTypeParams } from './useDropTipCreateCommands' const JOG_COMMAND_TIMEOUT_MS = 10000 const MAXIMUM_BLOWOUT_FLOW_RATE_UL_PER_S = 50 +const MAX_QUEUED_JOGS = 3 type UseDropTipSetupCommandsParams = UseDTWithTypeParams & { activeMaintenanceRunId: string | null @@ -35,10 +36,9 @@ type UseDropTipSetupCommandsParams = UseDTWithTypeParams & { } export interface UseDropTipCommandsResult { - /* */ handleCleanUpAndClose: (homeOnExit?: boolean) => Promise moveToAddressableArea: (addressableArea: AddressableAreaName) => Promise - handleJog: (axis: Axis, dir: Sign, step: StepSize) => Promise + handleJog: (axis: Axis, dir: Sign, step: StepSize) => void blowoutOrDropTip: ( currentStep: DropTipFlowsStep, proceed: () => void @@ -46,7 +46,6 @@ export interface UseDropTipCommandsResult { handleMustHome: () => Promise } -// Returns setup commands used in Drop Tip Wizard. export function useDropTipCommands({ issuedCommandsType, toggleIsExiting, @@ -61,6 +60,8 @@ export function useDropTipCommands({ }: UseDropTipSetupCommandsParams): UseDropTipCommandsResult { const isFlex = robotType === FLEX_ROBOT_TYPE const [hasSeenClose, setHasSeenClose] = React.useState(false) + const [jogQueue, setJogQueue] = React.useState Promise>>([]) + const [isJogging, setIsJogging] = React.useState(false) const { deleteMaintenanceRun } = useDeleteMaintenanceRunMutation({ onSuccess: () => { @@ -149,7 +150,7 @@ export function useDropTipCommands({ }) } - const handleJog = (axis: Axis, dir: Sign, step: StepSize): Promise => { + const executeJog = (axis: Axis, dir: Sign, step: StepSize): Promise => { return new Promise((resolve, reject) => { return runCommand({ command: { @@ -175,6 +176,30 @@ export function useDropTipCommands({ }) } + const processJogQueue = (): void => { + if (jogQueue.length > 0 && !isJogging) { + setIsJogging(true) + const nextJog = jogQueue[0] + setJogQueue(prevQueue => prevQueue.slice(1)) + nextJog().finally(() => { + setIsJogging(false) + }) + } + } + + React.useEffect(() => { + processJogQueue() + }, [jogQueue.length, isJogging]) + + const handleJog = (axis: Axis, dir: Sign, step: StepSize): void => { + setJogQueue(prevQueue => { + if (prevQueue.length < MAX_QUEUED_JOGS) { + return [...prevQueue, () => executeJog(axis, dir, step)] + } + return prevQueue + }) + } + const blowoutOrDropTip = ( currentStep: DropTipFlowsStep, proceed: () => void