fix(omny): report gripper force aborts from the v136 forceab flag
CI for csaxs_bec / test (push) Successful in 3m34s

Firmware v136 sets forceab when the force check stops all programs, so
a trip is reported even when the voltage has recovered by the time BEC
looks. ForceAbort is also shown in the status line of describe().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8zoDz1DGZxhmZy9jzaUYf
This commit is contained in:
x01dcandClaude Opus 5.5 committed 2026-11-02 13:24:12 +01:00
1 parent 5ad605c869
commit c5a9272d1f
4 files changed
+26 -23

No files matched your search

+14 -15
View File
@@ -174,10 +174,10 @@ class OMNYGalilController(GalilController):
"""Clear the init-motion flags left behind by a #FES/#FRM that was killed.
#FES and #FRM set initmv and clear it only on their last lines. If they are
killed partway (AB from the temperature, following error or gripper force
checks, AB/HX from #CMDERR), initmv stays 1 and every later #NEWPAR, #FES
and #FRM exits silently. Both routines run on thread 0, so initmv=1 with
thread 0 idle can only be such a leftover.
killed partway, initmv stays 1 and every later #NEWPAR, #FES and #FRM exits
silently. Since firmware v136 the abort paths clear it themselves, this covers
a routine stopped any other way, e.g. HX from a terminal. Both routines run on
thread 0, so initmv=1 with thread 0 idle can only be such a leftover.
"""
if float(self.socket_put_and_receive("MG initmv")) != 1 or self.is_thread_active(0):
return
@@ -193,19 +193,17 @@ class OMNYGalilController(GalilController):
return "Motor over temperature (tempab)."
if float(self.socket_put_and_receive("MG folerr")) == 1:
return "Following error (folerr)."
if (
float(self.socket_put_and_receive("MG contrID")) == 1
and float(self.socket_put_and_receive("MG _IHH2")) == -2
):
if float(self.socket_put_and_receive("MG forceab")) == 1:
# only controller 1 sets forceab, the flag stays until the next #GRFCAL
voltage = float(self.socket_put_and_receive("MG @AN[8001]"))
force0 = float(self.socket_put_and_receive("MG force0"))
limit = float(self.socket_put_and_receive("MG forcablm"))
if abs(voltage - force0) > limit:
return (
f"Gripper force check stopped all programs: sensor {voltage:.2f} V, "
f"force0 {force0:.2f} V, limit {limit:.2f} V. Check that the gripper "
"is free, then run omny_calibrate_gripper_force()."
)
return (
"Gripper force check stopped all programs since the last calibration "
f"(forceab): sensor now {voltage:.2f} V, force0 {force0:.2f} V, limit "
f"{limit:.2f} V. Check that the gripper is free, then run "
"omny_calibrate_gripper_force()."
)
if float(self.socket_put_and_receive("MG initmv")) == 1:
return "The routine was killed before it finished, no reason found."
return "The controller reports no abort."
@@ -276,9 +274,10 @@ class OMNYGalilController(GalilController):
allaxref = float(self.socket_put_and_receive("MGallaxref"))
tempab = float(self.socket_put_and_receive("MGtempab"))
timeab = float(self.socket_put_and_receive("MGtimeab"))
forceab = float(self.socket_put_and_receive("MGforceab"))
IgNoFol = float(self.socket_put_and_receive("MGIgNoFol"))
print(
f"OMNY galil firmware {swver:2.0f}, TempAbort: {tempab:1.0f}, Allaxref: {allaxref:1.0f}, TimeAbort: {timeab:1.0f}, Ignore Folerr: {IgNoFol:1.0f}\n"
f"OMNY galil firmware {swver:2.0f}, TempAbort: {tempab:1.0f}, Allaxref: {allaxref:1.0f}, TimeAbort: {timeab:1.0f}, ForceAbort: {forceab:1.0f}, Ignore Folerr: {IgNoFol:1.0f}\n"
)
if self.sock.port == 8083:
self._ogalil_switchsocket_status()
+4
View File
@@ -186,6 +186,8 @@ class SimGalilState:
"initmv": 0.0,
"folerr": 0.0,
"force0": 0.0,
"fcalok": 0.0,
"forceab": 0.0,
"forcablm": 2.0,
"_IHH2": -2.0,
# dialect status variables (LamNI / OMNY); overridable via sim_variables
@@ -652,6 +654,8 @@ class SimGalilSocket(SimSocketBase):
if routine == "#GRFCAL":
with state.lock:
state.variables["force0"] = state.analog_inputs[8001]
state.variables["fcalok"] = 1.0
state.variables["forceab"] = 0.0
return ":"
if routine == "#CRESET":
with state.lock:
+2 -2
View File
@@ -4,7 +4,7 @@ Simulated OMNY endstation devices.
Galil (controller1.dmc and siblings): reuses the shared SimGalilState — same base move
mechanics as flOMNI and the *identical* gripper transfer variable protocol
(getaprch/mntaprch/mntprgs/mntmod/confirm/atconfir, with mntax=4 and signed mntprgs),
plus OMNY status variables (swver=134, tempab/timeab, IgNoFol, grstat/relpos/toppin)
plus OMNY status variables (swver=136, tempab/timeab, IgNoFol, grstat/relpos/toppin)
and direct axis commands for the shuttle/dewar handling (AC/DC/SP/PR/BG/SH/MO/LD).
Motor resolutions are hardcoded Python-side (51200; 89565.87 for the rotation axis on
port 8083) and seeded accordingly.
@@ -102,7 +102,7 @@ class SimOMNYGalilMotor(OMNYGalilMotor):
stppermm = _OMNY_RESOLUTION
if int(port) == _OMNY_ROTATION_PORT and self.axis_Id_numeric == _OMNY_ROTATION_AXIS:
stppermm = _OMNY_ROTATION_RESOLUTION
variables = {"swver": 134, "mntax": 4}
variables = {"swver": 136, "mntax": 4}
variables.update(sim_variables or {})
state.configure_axis(
self.axis_Id_numeric,
@@ -42,8 +42,7 @@ NO_ABORT = {
"MG tempab": "0.0000",
"MG timeab": "0.0000",
"MG folerr": "0.0000",
"MG contrID": "1.0000",
"MG _IHH2": "-2.0000",
"MG forceab": "0.0000",
"MG @AN[8001]": "-2.1826",
"MG force0": "-2.1826",
"MG forcablm": "2.0000",
@@ -55,14 +54,15 @@ NO_ABORT = {
"changes, expected",
[
({}, "no abort"),
({"MG force0": "2.5488"}, "Gripper force check"),
({"MG forceab": "1.0000", "MG force0": "2.5488"}, "Gripper force check"),
# a brief spike: the flag is set although the voltage is back within the limit
({"MG forceab": "1.0000"}, "sensor now -2.18 V"),
({"MG tempab": "1.0000"}, "over temperature"),
({"MG tempab": "1.0000", "MG timeab": "1.0000"}, "timeab"),
({"MG folerr": "1.0000"}, "Following error"),
({"MG initmv": "1.0000"}, "killed"),
# the force check only exists on controller 1 and only with the RIO connected
({"MG contrID": "2.0000", "MG force0": "2.5488"}, "no abort"),
({"MG _IHH2": "0.0000", "MG force0": "2.5488"}, "no abort"),
# the flag decides, not the voltage difference
({"MG force0": "2.5488"}, "no abort"),
],
)
def test_abort_reason(changes, expected):