class ExporterMotor(AbstractMotor): """Motor using the Exporter protocol, based on AbstractMotor""" def __init__(self, name): AbstractMotor.__init__(self, name) self.username = None self._motor_pos_suffix = None self._motor_state_suffix = None self._exporter = None self._exporter_address = None self.motor_position = None self.motor_state = None def init(self): """Initialise the motor""" AbstractMotor.init(self) self._motor_pos_suffix = self.getProperty("position_suffix", "Position") self._motor_state_suffix = self.getProperty("state_suffix", "State") self._exporter_address = self.getProperty("exporter_address") _host, _port = self._exporter_address.split(":") self._exporter = Exporter(_host, int(_port)) self.motor_position = self.add_channel( { "type": "exporter", "exporter_address": self._exporter_address, "name": "position", }, self.actuator_name + self._motor_pos_suffix, ) if self.motor_position: self.get_value() self.motor_position.connectSignal("update", self.update_value) self.motor_state = self.add_channel( { "type": "exporter", "exporter_address": self._exporter_address, "name": "motor_state", }, self.actuator_name + self._motor_state_suffix, ) if self.motor_state: self.motor_state.connectSignal("update", self._update_state) self.update_state() def get_state(self): """Get the motor state. Returns: (enum 'HardwareObjectState'): Motor state. """ try: _state = self.motor_state.get_value().upper() self.specific_state = _state return ExporterStates.__members__[_state].value except (KeyError, AttributeError): return self.STATES.UNKNOWN def _update_state(self, state): try: state = state.upper() state = ExporterStates.__members__[state].value except (AttributeError, KeyError): state = self.STATES.UNKNOWN return self.update_state(state) def _get_hwstate(self): """Get the hardware state, reported by the MD2 application. Returns: (string): The state. """ try: return self._exporter.read_property("HardwareState") except Exception: return "Ready" def _get_swstate(self): """Get the software state, reported by the MD2 application. Returns: (string): The state. """ return self._exporter.read_property("State") def _ready(self): """Get the "Ready" state - software and hardware. Returns: (bool): True if both "Ready", False otherwise. """ if (self._get_swstate() == "Ready" and self._get_hwstate() == "Ready" and self.motor_state.get_value() == "Ready"): return True return False def _wait_ready(self, timeout=3): """Wait for the state to be "Ready". Args: timeout (float): waiting time [s]. Raises: RuntimeError: Execution timeout. """ with Timeout(timeout, RuntimeError("Execution timeout")): while not self._ready(): sleep(0.01) def wait_move(self, timeout=20): """Wait until the end of move ended, using the application state. Args: timeout(float): Timeout [s]. Default value is 20 s """ self._wait_ready(timeout) def wait_motor_move(self, timeout=20): """Wait until the end of move ended using the motor state. Args: timeout(float): Timeout [s]. Default value is 20 s Raises: RuntimeError: Execution timeout. """ with Timeout(timeout, RuntimeError("Execution timeout")): while self.get_state() != self.STATES.READY: sleep(0.01) def get_value(self): """Get the motor position. Returns: (float): Motor position. """ _v = self.motor_position.get_value() if math.isnan(_v) or None: logging.getLogger("HWR").debug("Value of %s is NaN" % self.actuator_name) _v = self._nominal_value self._nominal_value = _v return self._nominal_value def __get_limits(self, cmd): """Returns motor low and high limits. Args: cmd (str): command name Returns: (tuple): two floats tuple (low limit, high limit). """ try: _low, _high = self._exporter.execute(cmd, (self.actuator_name, )) # inf is a problematic value, convert to sys.float_info.max if _low == float("-inf"): _low = -sys.float_info.max if _high == float("inf"): _high = sys.float_info.max return _low, _high except ValueError: return -1e4, 1e4 def get_limits(self): """Returns motor low and high limits. Args: cmd (str): command name Returns: (tuple): two floats tuple (low limit, high limit). """ self._nominal_limits = self.__get_limits("getMotorLimits") return self._nominal_limits def get_dynamic_limits(self): """Returns motor low and high dynamic limits. Returns: (tuple): two floats tuple (low limit, high limit). """ return self.__get_limits("getMotorDynamicLimits") def _set_value(self, value): """Move motor to absolute value. Args: value (float): target value """ self.update_state(self.STATES.BUSY) self.motor_position.set_value(value) def abort(self): """Stop the motor movement immediately.""" if self.get_state() != self.STATES.UNKNOWN: self._exporter.execute("abort") def home(self, timeout=None): """Homing procedure. Args: timeout (float): optional - timeout [s]. """ self._exporter.execute("startHomingMotor", (self.actuator_name, )) self.wait_ready(timeout) def get_max_speed(self): """Get the motor maximum speed. Returns: (float): the maximim speed [unit/s]. """ return self._exporter.execute("getMotorMaxSpeed", (self.actuator_name, )) def name(self): """Get the motor name. Should be removed when GUI ready""" return self.actuator_name
class ExporterNState(AbstractNState): """Microdiff with Exporter implementation of AbstartNState""" SPECIFIC_STATES = ExporterStates def __init__(self, name): AbstractNState.__init__(self, name) self._exporter = None self.value_channel = None self.state_channel = None def init(self): """Initialise the device""" AbstractNState.init(self) value_channel = self.get_property("value_channel_name") state_channel = self.get_property("state_channel_name", "State") _exporter_address = self.get_property("exporter_address") _host, _port = _exporter_address.split(":") self._exporter = Exporter(_host, int(_port)) self.value_channel = self.add_channel( { "type": "exporter", "exporter_address": _exporter_address, "name": value_channel.lower(), }, value_channel, ) self.value_channel.connect_signal("update", self.update_value) self.state_channel = self.add_channel( { "type": "exporter", "exporter_address": _exporter_address, "name": "state", }, state_channel, ) self.state_channel.connect_signal("update", self._update_state) self.update_state() def _update_state(self, state=None): """To be used to update the state when emiting the "update" signal. Args: state (str): optional state value Returns: (enum 'HardwareObjectState'): state. """ if not state: state = self.get_state() else: state = self._value2state(state) return self.update_state(state) def _value2state(self, state): """Convert string state to HardwareObjectState enum value Args: state (str): the state Returns: (enum 'HardwareObjectState'): state """ try: return self.SPECIFIC_STATES.__members__[state.upper()].value except (AttributeError, KeyError): return self.STATES.UNKNOWN def get_state(self): """Get the device state. Returns: (enum 'HardwareObjectState'): Device state. """ state = self.state_channel.get_value() return self._value2state(state) def abort(self): """Stop the action.""" if self.get_state() != self.STATES.UNKNOWN: self._exporter.execute("abort") def _set_value(self, value): """Set device to value Args: value (str, int, float or enum): Value to be set. """ # NB Workaround beacuse diffractomer does not send event on # change of light position self.update_state(self.STATES.BUSY) if isinstance(value, Enum): if isinstance(value.value, tuple) or isinstance(value.value, list): value = value.value[0] else: value = value.value self.value_channel.set_value(value) self.update_state(self.STATES.READY) def get_value(self): """Get the device value Returns: (Enum): Enum member, corresponding to the value or UNKNOWN. """ _val = self.value_channel.get_value() return self.value_to_enum(_val)
class ExporterNState(AbstractNState): """Microdiff with Exporter implementation of AbstartNState""" SPECIFIC_STATES = ExporterStates def __init__(self, name): AbstractNState.__init__(self, name) self._exporter = None self.value_channel = None self.state_channel = None self.use_value_as_state = None def init(self): """Initialise the device""" AbstractNState.init(self) value_channel = self.get_property("value_channel_name") # use the value to check if action finished. self.use_value_as_state = self.get_property("value_state") state_channel = self.get_property("state_channel_name", "State") _exporter_address = self.get_property("exporter_address") _host, _port = _exporter_address.split(":") self._exporter = Exporter(_host, int(_port)) self.value_channel = self.add_channel( { "type": "exporter", "exporter_address": _exporter_address, "name": value_channel.lower(), }, value_channel, ) self.value_channel.connect_signal("update", self.update_value) self.state_channel = self.add_channel( { "type": "exporter", "exporter_address": _exporter_address, "name": "state", }, state_channel, ) self.state_channel.connect_signal("update", self._update_state) self.update_state() def _wait_hardware(self, value, timeout=None): """Wait timeout seconds till hardware in place. Args: value (str, int): value to be tested. timeout(float): Timeout [s]. None means infinite timeout. """ with Timeout(timeout, RuntimeError("Timeout waiting for hardware")): while self.value_channel.get_value() != value: sleep(0.5) def _wait_ready(self, timeout=None): """Wait timeout seconds till status is ready. Args: timeout(float): Timeout [s]. None means infinite timeout. """ with Timeout(timeout, RuntimeError("Timeout waiting for status ready")): while not self.get_state() == self.STATES.READY: sleep(0.5) def _update_state(self, state=None): """To be used to update the state when emiting the "update" signal. Args: state (str): optional state value Returns: (enum 'HardwareObjectState'): state. """ if not state: state = self.get_state() else: state = self._value2state(state) return self.update_state(state) def _value2state(self, state): """Convert string state to HardwareObjectState enum value Args: state (str): the state Returns: (enum 'HardwareObjectState'): state """ try: return self.SPECIFIC_STATES.__members__[state.upper()].value except (AttributeError, KeyError): return self.STATES.UNKNOWN def get_state(self): """Get the device state. Returns: (enum 'HardwareObjectState'): Device state. """ state = self.state_channel.get_value() return self._value2state(state) def abort(self): """Stop the action.""" if self.get_state() != self.STATES.UNKNOWN: self._exporter.execute("abort") def _set_value(self, value): """Set device to value Args: value (str, int, float or enum): Value to be set. """ # NB Workaround beacuse diffractomer does not send event on # change of actuators (light, scintillator, cryostream...) self.update_state(self.STATES.BUSY) if isinstance(value, Enum): if isinstance(value.value, (tuple, list)): value = value.value[0] else: value = value.value self.value_channel.set_value(value) # wait until the hardware returns value set if self.use_value_as_state: self._wait_hardware(value, 120) self._wait_ready(120) self.update_state(self.STATES.READY) def get_value(self): """Get the device value Returns: (Enum): Enum member, corresponding to the value or UNKNOWN. """ _val = self.value_channel.get_value() return self.value_to_enum(_val)