Browse Source

fix: stop_execution and send_coordinate wait for idle before returning

- stop_actions() now waits for hardware to reach idle state (30s timeout)
- send_coordinate endpoint waits for idle after move (60s timeout)
- Clear stop_requested flag before idle check to allow check_idle_async to work

This ensures API responses only return after the machine has physically
stopped moving, consistent with move_to_center and move_to_perimeter.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
tuanchris 1 week ago
parent
commit
0938a971a8
3 changed files with 19 additions and 1 deletions
  1. 2 1
      .gitignore
  2. 6 0
      main.py
  3. 11 0
      modules/core/pattern_manager.py

+ 2 - 1
.gitignore

@@ -30,4 +30,5 @@ static/custom/*
 !static/custom/.gitkeep
 !static/custom/.gitkeep
 .claude/
 .claude/
 static/dist/
 static/dist/
-.planning
+.planning
+.coverage

+ 6 - 0
main.py

@@ -2150,6 +2150,12 @@ async def send_coordinate(request: CoordinateRequest):
     try:
     try:
         logger.debug(f"Sending coordinate: theta={request.theta}, rho={request.rho}")
         logger.debug(f"Sending coordinate: theta={request.theta}, rho={request.rho}")
         await pattern_manager.move_polar(request.theta, request.rho)
         await pattern_manager.move_polar(request.theta, request.rho)
+
+        # Wait for machine to reach idle before returning
+        idle = await connection_manager.check_idle_async(timeout=60)
+        if not idle:
+            logger.warning("Machine did not reach idle after send_coordinate")
+
         return {"success": True}
         return {"success": True}
     except Exception as e:
     except Exception as e:
         logger.error(f"Failed to send coordinate: {str(e)}")
         logger.error(f"Failed to send coordinate: {str(e)}")

+ 11 - 0
modules/core/pattern_manager.py

@@ -1647,6 +1647,17 @@ async def stop_actions(clear_playlist = True, wait_for_lock = True):
             state.current_playing_file = None
             state.current_playing_file = None
             state.execution_progress = None
             state.execution_progress = None
 
 
+        # Clear stop_requested now that the pattern has stopped - this allows
+        # check_idle_async to work (it exits early if stop_requested is True)
+        state.stop_requested = False
+
+        # Wait for hardware to reach idle state before returning
+        # This ensures the machine has physically stopped moving
+        if not timed_out:
+            idle = await connection_manager.check_idle_async(timeout=30.0)
+            if not idle:
+                logger.warning("Machine did not reach idle after stop")
+
         # Call async function directly since we're in async context
         # Call async function directly since we're in async context
         await connection_manager.update_machine_position()
         await connection_manager.update_machine_position()
         return not timed_out
         return not timed_out