fix(galil): poll thread 0 idle instead of guessing a sleep before XQ#FES/FRM
CI for csaxs_bec / test (pull_request) Successful in 1m53s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 1s
CI for csaxs_bec / test (push) Successful in 2m25s

XQ#NEWPAR dispatches asynchronously on controller thread 0 and returns as
soon as it starts, not once it finishes; the dispatch time varies with
controller load. drive_axis_to_limit/find_reference followed it with a
fixed sleep before starting XQ#FES/XQ#FRM (also thread 0), which has
already needed bumping twice (0.1->0.3, 0.3->0.2) and still raced on
lgalil: sending XQ#FES while thread 0 was still busy got a '?' reply
(Galil error 19, thread already running).

Replace the fixed sleep with _wait_for_thread_idle(0), reusing the
existing is_thread_active() primitive (already used the same way in
hard_abort_and_restore_positioning_mode) to poll until thread 0 is
actually free before dispatching the next routine.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit was merged in pull request #292.
This commit is contained in:
x01dc
2026-08-12 23:28:09 +02:00
committed by holler
co-authored by Claude Sonnet 5
parent bdb65d0326
commit 4c3c86183a
3 changed files with 34 additions and 10 deletions
+18 -2
View File
@@ -95,6 +95,22 @@ class GalilController(Controller):
return False
return True
def _wait_for_thread_idle(self, thread_id: int, timeout: float = 2.0) -> None:
"""Poll until the given controller thread is no longer executing.
XQ dispatches a routine asynchronously and returns as soon as it starts,
not once it finishes. #NEWPAR briefly keeps thread 0 busy dispatching a
move, and the actual dispatch time varies with controller load, so a
fixed sleep before starting the next routine on the same thread
(#FES/#FRM) is inherently racy: send it too early and the controller
replies '?' with error 19 ("thread already running") instead of ':'.
"""
start = time.time()
while self.is_thread_active(thread_id):
if time.time() - start > timeout:
raise GalilError(f"Thread {thread_id} did not become idle within {timeout} s.")
time.sleep(0.02)
def stop_all_axes(self) -> str:
if not self.is_thread_active(1):
return self.socket_put_and_receive("XQ#STOP,1")
@@ -201,7 +217,7 @@ class GalilController(Controller):
self.socket_put_confirmed(f"naxis={axis_Id_numeric}")
self.socket_put_confirmed(f"ndir={direction_flag}")
self.socket_put_confirmed("XQ#NEWPAR")
time.sleep(0.2)
self._wait_for_thread_idle(0)
self.socket_put_confirmed("XQ#FES")
time.sleep(0.1)
axis_Id = self.axis_Id_numeric_to_alpha(axis_Id_numeric)
@@ -236,7 +252,7 @@ class GalilController(Controller):
time.sleep(0.1)
self.socket_put_confirmed(f"naxis={axis_Id_numeric}")
self.socket_put_and_receive("XQ#NEWPAR")
time.sleep(0.3)
self._wait_for_thread_idle(0)
self.socket_put_confirmed("XQ#FRM")
time.sleep(0.1)
axis_Id = self.axis_Id_numeric_to_alpha(axis_Id_numeric)
+8 -4
View File
@@ -102,6 +102,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"naxis=0\r",
b"ndir=1\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FES\r",
b"MG_BGA\r",
b"MGbcklact[0]\r",
@@ -109,7 +110,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"MG_XQ2\r",
b"MG _LRA, _LFA\r",
],
[b":", b":", b":", b":", b"0", b"0", b"-1", b"-1", b"1.000 0.000"],
[b":", b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"1.000 0.000"],
),
(
1,
@@ -118,6 +119,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"naxis=1\r",
b"ndir=-1\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FES\r",
b"MG_BGB\r",
b"MGbcklact[1]\r",
@@ -125,7 +127,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"MG_XQ2\r",
b"MG _LRB, _LFB\r",
],
[b":", b":", b":", b":", b"0", b"0", b"-1", b"-1", b"0.000 1.000"],
[b":", b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"0.000 1.000"],
),
],
)
@@ -144,6 +146,7 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
[
b"naxis=0\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FRM\r",
b"MG_BGA\r",
b"MGbcklact[0]\r",
@@ -151,13 +154,14 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
b"MG_XQ2\r",
b"MG axisref[0]\r",
],
[b":", b":", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
[b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
),
(
1,
[
b"naxis=1\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FRM\r",
b"MG_BGB\r",
b"MGbcklact[1]\r",
@@ -165,7 +169,7 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
b"MG_XQ2\r",
b"MG axisref[1]\r",
],
[b":", b":", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
[b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
),
],
)
+8 -4
View File
@@ -85,6 +85,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"naxis=0\r",
b"ndir=1\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FES\r",
b"MG_XQ0\r",
b"MG _MOA\r",
@@ -92,7 +93,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"MG _MOA\r",
b"MG _LRA, _LFA\r",
],
[b":", b":", b":", b":", b"0", b"0", b"-1", b"-1", b"1.000 0.000"],
[b":", b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"1.000 0.000"],
),
(
1,
@@ -101,6 +102,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"naxis=1\r",
b"ndir=-1\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FES\r",
b"MG_XQ0\r",
b"MG _MOB\r",
@@ -108,7 +110,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
b"MG _MOB\r",
b"MG _LRB, _LFB\r",
],
[b":", b":", b":", b":", b"0", b"0", b"-1", b"-1", b"0.000 1.000"],
[b":", b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"0.000 1.000"],
),
],
)
@@ -127,6 +129,7 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
[
b"naxis=0\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FRM\r",
b"MG_XQ0\r",
b"MG _MOA\r",
@@ -134,13 +137,14 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
b"MG _MOA\r",
b"MG axisref[0]\r",
],
[b":", b":", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
[b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
),
(
1,
[
b"naxis=1\r",
b"XQ#NEWPAR\r",
b"MG_XQ0\r",
b"XQ#FRM\r",
b"MG_XQ0\r",
b"MG _MOB\r",
@@ -148,7 +152,7 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
b"MG _MOB\r",
b"MG axisref[1]\r",
],
[b":", b":", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
[b":", b":", b"-1", b":", b"0", b"0", b"-1", b"-1", b"1.00"],
),
],
)