diff --git a/src/aare/daq/operations/mounting/service.py b/src/aare/daq/operations/mounting/service.py index 3dae5552..5a0142ad 100644 --- a/src/aare/daq/operations/mounting/service.py +++ b/src/aare/daq/operations/mounting/service.py @@ -120,6 +120,9 @@ class MountingService: self._magnet_position_sensor_check(timeout=360.0) self.ctx.deps.devs.tell.check_enable_motion() self.ctx.deps.devs.tell.wait_not_busy() + # enable_motion releases the door safety, so the door can only be + # validated once motion is enabled. + self.ctx.deps.devs.tell.validate_door_closed() self.ctx.deps.devs.tell.set_in_mount_position(True) def _unmount_current_sample(self, timeout: float = 60.0): diff --git a/src/aare/devices/tell_backend.py b/src/aare/devices/tell_backend.py index a4a181de..9ce68928 100644 --- a/src/aare/devices/tell_backend.py +++ b/src/aare/devices/tell_backend.py @@ -173,6 +173,8 @@ class SimTellBackend: self._system_check_msg = "OK" self._smart_magnet_state = "Ready" self._in_mount_position = False + self._manual_mode = False + self._door_closed = True @property def url(self) -> str | None: @@ -218,6 +220,12 @@ class SimTellBackend: if expr == "in_mount_position&": return "true" if self._in_mount_position else "false" + if expr == "is_manual_mode()&": + return "true" if self._manual_mode else "false" + + if expr == "is_door_closed()&": + return "true" if self._door_closed else "false" + if expr.startswith("in_mount_position = "): self._in_mount_position = "True" in expr or "true" in expr return None diff --git a/src/aare/devices/tell_client.py b/src/aare/devices/tell_client.py index 91107449..833fadba 100755 --- a/src/aare/devices/tell_client.py +++ b/src/aare/devices/tell_client.py @@ -109,49 +109,65 @@ class TellClient: def is_in_mount_position(self) -> bool: """checks to see if the robot is in the mount position and returns a boolean""" - return self.backend.eval("in_mount_position&").lower() == "true" + return self._eval_bool("in_mount_position&") def is_manual_mode(self) -> bool: - msg = self.backend.eval("is_manual_mode()&") - logger.debug(f"manual mode: {msg}") - return msg + """returns True if the robot is in manual mode""" + result = self._eval_bool("is_manual_mode()&") + logger.debug(f"manual mode: {result}") + return result def is_door_closed(self) -> bool: - msg = self.backend.eval("is_door_closed()&") - logger.debug(f"door closed: {msg}") - return msg + """returns True if the robot doors are closed""" + result = self._eval_bool("is_door_closed()&") + logger.debug(f"door closed: {result}") + return result def is_remote_mode(self) -> bool: + """returns True if the robot is in remote (non-manual) mode""" return not self.is_manual_mode() def validate_mount_start_conditions(self) -> None: + """validates preconditions that must hold *before* motion is enabled, + raising a TellCommunicationError if any fail. Uses the lightweight is_* + boolean checks rather than system_check() so the daq can still enable + motion afterwards. + + Note: the door is NOT checked here. enable_motion() releases the door + safety, so is_door_closed() is always false before that runs - the door + check belongs after enable_motion (see validate_door_closed).""" reasons: list[str] = [] - logger.warning('tell validation not in place') - pass - # try: - system_check = self.get_system_check() - # if system_check not in (None, "", "OK"): - # reasons.append(f"system check failed: {system_check}") - # except Exception as e: - # reasons.append(f"system check failed: {e}") - # try: - # if not self.is_remote_mode(): - # reasons.append("TELL is not in remote mode") - # except Exception as e: - # reasons.append(f"failed to check remote mode: {e}") + try: + # if the robot is not in remote mode enable_motion() will not power + # it and the mount will silently hang, so guard before we get there. + if not self.is_remote_mode(): + reasons.append("TELL is not in remote mode") + except Exception as e: + reasons.append(f"failed to check remote mode: {e}") - # try: - # if not self.is_door_closed(): - # reasons.append("TELL doors are open") - # except Exception as e: - # reasons.append(f"failed to check door status: {e}") + if reasons: + raise TellCommunicationError( + "Mount can't start: " + "; ".join(reasons), + operation="mount_precheck", + ) - # if reasons: - # raise TellCommunicationError( - # message="Mount can't start: " + "; ".join(reasons), - # operation="mount_precheck", - # ) + def validate_door_closed(self) -> None: + """validates that the doors are closed, raising a TellCommunicationError + if they are open. Must be called *after* enable_motion() has released + the door safety, otherwise is_door_closed() always reports false.""" + try: + door_closed = self.is_door_closed() + except Exception as e: + raise TellCommunicationError( + f"Mount can't start: failed to check door status: {e}", + operation="mount_precheck", + ) + if not door_closed: + raise TellCommunicationError( + "Mount can't start: TELL doors are open", + operation="mount_precheck", + ) def set_samples_info(self, info: List[PuckWithTellPosition]): """sets the samples in the robot dewar based on the given list of PuckWithTellPosition objects @@ -543,7 +559,6 @@ if __name__ == "__main__": tell_client = make_tell_client(bl) #tell_client.toggle_blower() #tell_client.check_enable_motion() - print("system check: ", tell_client.get_system_check()) print("status ", tell_client.get_robot_status()) print("dry mount count: ", tell_client.get_setting('dry_mount_counter')) @@ -556,6 +571,12 @@ if __name__ == "__main__": print("door closer :", tell_client.backend.eval('is_door_closed()&')) print("manual mode: ", tell_client.backend.eval('is_manual_mode()&')) print("position :", tell_client.get_robot_status()["pos"]) + state = tell_client.get_robot_state() + manual_mode = tell_client.is_manual_mode() + print("state: ", state) + print("is manual mode True: ", manual_mode == True) + print(tell_client.backend.eval('is_manual_mode()&')) + print(tell_client.is_door_closed()) #print("release safety: ", tell_client.backend.eval('release_safety()&')) # time.sleep(5) diff --git a/tests/unit/daq/operations/mounting/test_mounting_service.py b/tests/unit/daq/operations/mounting/test_mounting_service.py index 5f981d88..7fb02ea3 100644 --- a/tests/unit/daq/operations/mounting/test_mounting_service.py +++ b/tests/unit/daq/operations/mounting/test_mounting_service.py @@ -42,6 +42,7 @@ def _make_context(previous_sample=None): dry=lambda **kwargs: None, check_enable_motion=lambda: None, wait_not_busy=lambda timeout=360.0: None, + validate_door_closed=lambda: None, set_in_mount_position=lambda value: None, )