Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,12 @@ class ActiveSessionEngine(
// motion; let normal AMRAP / stall auto-stop resume by zeroing the
// deadline (the source-of-truth field).
deferAutoStopDeadlineMs = 0L
// Issue #652: the same completed-rep boundary is authoritative for
// the shared stall countdown (both DELOAD_OCCURRED and low-velocity
// arms write to coordinator.stallStartTime). Without this, a stale
// countdown started at a turnaround, brief pause, or firmware de-load
// can survive a subsequent valid rep and later auto-complete the set.
resetStallTimer()

// Capture rep boundary timestamp BEFORE scoring so scoreCurrentRep()
// and processBiomechanicsForRep() both see the correct metric window.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,58 @@ class DWSMWorkoutLifecycleTest {
harness.cleanup()
}

@Test
fun `Issue 652 - completed Echo rep cancels an armed stall timer`() = runTest {
val harness = DWSMTestHarness(this)
harness.fakeBleRepo.simulateConnect("Vee_Test")
harness.dwsm.updateWorkoutParameters(
WorkoutParameters(
programMode = ProgramMode.Echo,
echoLevel = EchoLevel.HARDER,
reps = 10,
warmupReps = 0,
stallDetectionEnabled = true,
isAMRAP = false,
isJustLift = false,
),
)
harness.dwsm.startWorkout(skipCountdown = true)
advanceUntilIdle()
completeWarmupReps(harness, warmupTarget = 3, workingTarget = 10)
completeFirstWorkingRep(harness, warmupTarget = 3, workingTarget = 10)
advanceUntilIdle()

harness.fakeBleRepo.emitDeloadOccurred()
advanceUntilIdle()
assertNotNull(harness.dwsm.coordinator.stallStartTime)

harness.fakeBleRepo.emitRepNotification(
RepNotification(
topCounter = 5,
completeCounter = 5,
repsRomCount = 3,
repsRomTotal = 3,
repsSetCount = 2,
repsSetTotal = 10,
rangeTop = 800f,
rangeBottom = 0f,
rawData = ByteArray(24),
timestamp = 5L,
),
)
advanceUntilIdle()

assertEquals(2, harness.dwsm.coordinator.repCount.value.workingReps)
assertEquals(
null,
harness.dwsm.coordinator.stallStartTime,
"A completed working rep proves motion resumed and must cancel the stale stall countdown",
)
assertFalse(harness.dwsm.coordinator.isCurrentlyStalled)
assertIs<WorkoutState.Active>(harness.dwsm.coordinator.workoutState.value)
harness.cleanup()
}

@Test
fun `Issue 267 Just Lift warmup to working rep transitions without failed stall state`() = runTest {
val harness = DWSMTestHarness(this)
Expand Down
Loading