Esempio n. 1
0
class FlexHCD(SampleChanger):
    __TYPE__ = "HCD"

    def __init__(self, *args, **kwargs):
        super(FlexHCD, self).__init__(self.__TYPE__, True, *args, **kwargs)

    def init(self):
        sc3_pucks = self.getProperty("sc3_pucks", True)

        for i in range(8):
            cell = Cell(self, i + 1, sc3_pucks)
            self._add_component(cell)

        self.robot = self.getProperty("tango_device")
        if self.robot:
            self.robot = DeviceProxy(self.robot)

        self.exporter_addr = self.getProperty("exporter_address")
        """
        if self.exporter_addr:
            self.swstate_attr = self.add_channel(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "swstate",
                },
                "State",
            )
        
        """
        self.controller = self.getObjectByRole("controller")
        self.prepareLoad = self.get_command_object("moveToLoadingPosition")
        self.timeout = 3
        self.gripper_types = {
            -1: "No Gripper",
            1: "UNIPUCK",
            2: "MINISPINE",
            3: "FLIPPING",
            4: "UNIPUCK_DOUBLE",
            5: "PLATE",
        }

        return SampleChanger.init(self)

    @task
    def prepare_load(self):
        if self.controller:
            self.controller.hutch_actions(condition=True)
        else:
            self.prepareLoad()

    @task
    def _prepare_centring_task(self):
        if self.controller:
            self.controller.hutch_actions(condition=False, sc_loading=True)
        else:
            gevent.sleep(2)
            self.get_command_object("unlockMinidiffMotors")(wait=True)
            self.get_command_object("prepareCentring")(wait=True)

    def prepare_centring(self):
        self._prepare_centring_task()

    def get_sample_properties(self):
        return (Pin.__HOLDER_LENGTH_PROPERTY__,)

    def get_basket_list(self):
        basket_list = []
        # put here only the baskets that exist, not all the possible ones
        # if self.exporter_addr:
        #    basket_list =
        for cell in self.get_components():
            for basket in cell.get_components():
                if isinstance(basket, Basket):
                    basket_list.append(basket)

        return basket_list

    def _do_change_mode(self, *args, **kwargs):
        return

    def _do_update_info(self):
        # self._update_selection()
        self._update_state()

    def _do_scan(self, component, recursive=True, saved={"barcodes": None}):
        return

    def _execute_cmd(self, cmd, *args, **kwargs):
        timeout = kwargs.pop("timeout", None)
        if args:
            cmd_str = "flex.%s(%s)" % (cmd, ",".join(map(repr, args)))
        else:
            cmd_str = "flex.%s()" % cmd
        cmd_id = self.robot.eval(cmd_str)

        if not cmd_id:
            cmd_id = self.robot.eval(cmd_str)
        with gevent.Timeout(
            timeout, RuntimeError("Timeout while executing %s" % repr(cmd_str))
        ):
            while True:
                if self.robot.is_finished(cmd_id):
                    break
                gevent.sleep(0.2)

        res = self.robot.get_result(cmd_id)
        if res:
            res = pickle.loads(base64.decodestring(res))
            if isinstance(res, Exception):
                raise res
            else:
                return res

    def _execute_cmd_exporter(self, cmd, *args, **kwargs):
        ret = None
        timeout = kwargs.pop("timeout", 900)
        if args:
            args_str = "%s" % "\t".join(map(repr, args))
        if kwargs.pop("command", None):
            exp_cmd = self.add_command(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "%s" % cmd,
                },
                "%s" % cmd,
            )
            if args:
                ret = exp_cmd(args_str)
            else:
                ret = exp_cmd()
        if kwargs.pop("attribute", None):
            exp_attr = self.add_channel(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "%s" % cmd,
                },
                "%s" % cmd[3:],
            )
            if cmd.startswith("get"):
                return exp_attr.getValue()
            if cmd.startswith("set"):
                ret = exp_attr.setValue(args_str)

        self._wait_ready(timeout=timeout)
        return ret

    def _ready(self):
        # return self.swstate_attr.getValue() == "Ready"
        return True

    def _wait_ready(self, timeout=None):
        err_msg = "Timeout waiting for sample changer to be ready"
        # None means infinite timeout <=0 means default timeout
        if timeout is not None and timeout <= 0:
            timeout = self.timeout
        with gevent.Timeout(timeout, RuntimeError(err_msg)):
            while not self._ready():
                time.sleep(0.5)

    def _do_select(self, component):
        if isinstance(component, Cell):
            cell_pos = component.get_index() + 1
        elif isinstance(component, Basket) or isinstance(component, Pin):
            cell_pos = component.get_cell_no()

        if self.exporter_addr:
            self._execute_cmd_exporter("moveDewar", cell_pos, command=True)
        else:
            self._execute_cmd("moveDewar", cell_pos)

        self._update_selection()

    @task
    def load_sample(
        self,
        holderLength,
        sample_id=None,
        sample_location=None,
        sampleIsLoadedCallback=None,
        failureCallback=None,
        prepareCentring=True,
    ):
        cell, basket, sample = sample_location
        sample = self.get_component_by_address(Pin.get_sample_address(cell, basket, sample))
        return self.load(sample)

    def chained_load(self, old_sample, sample):
        if self.exporter_addr:
            unload_load_task = gevent.spawn(
                self._execute_cmd_exporter,
                "load_sample",
                sample.get_cell_no(),
                sample.get_basket_no(),
                sample.get_vial_no(),
                command=True,
            )
        else:
            unload_load_task = gevent.spawn(
                self._execute_cmd,
                "chainedUnldLd",
                [
                    old_sample.get_cell_no(),
                    old_sample.get_basket_no(),
                    old_sample.get_vial_no(),
                ],
                [sample.get_cell_no(), sample.get_basket_no(), sample.get_vial_no()],
            )

        gevent.sleep(15)

        err_msg = "Timeout waiting for sample changer to be in safe position"
        while not unload_load_task.ready():
            if self.exporter_addr:
                loading_state = self._execute_cmd_exporter(
                    "getCurrentLoadSampleState", attribute=True
                )
                if "on_gonio" in loading_state:
                    self._set_loaded_sample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd_exporter(
                            "getRobotIsSafe", attribute=True
                        ):
                            gevent.sleep(0.5)
                    return True
            else:
                loading_state = str(
                    self._execute_cmd("sampleStatus", "LoadSampleStatus")
                )
                if "on_gonio" in loading_state:
                    self._set_loaded_sample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while (
                            not self._execute_cmd(
                                "get_robot_cache_variable", "data:dioRobotIsSafe"
                            )
                            == "true"
                        ):
                            gevent.sleep(0.5)
                    return True
            gevent.sleep(1)

        logging.getLogger("HWR").info("unload load task done")
        for msg in self.get_robot_exceptions():
            logging.getLogger("HWR").error(msg)

        return self._check_pin_on_gonio()

    def _check_pin_on_gonio(self):
        if self.exporter_addr:
            _on_gonio = self._execute_cmd_exporter("pin_on_gonio", command=True)
        else:
            _on_gonio = self._execute_cmd("pin_on_gonio")

        if _on_gonio:
            # finish the loading actions
            self._prepare_centring_task()
            return True
        else:
            logging.getLogger("HWR").info("reset loaded sample")
            self._reset_loaded_sample()
            # if self.controller:
            #    self.controller.hutch_actions(release_interlock=True)
            return False

    def reset_loaded_sample(self):
        if self.exporter_addr:
            self._execute_cmd_exporter("reset_loaded_position", command=True)
        else:
            self._execute_cmd("reset_loaded_position")
        self._reset_loaded_sample()

    def get_robot_exceptions(self):
        if self.exporter_addr:
            """
            return self._execute_cmd_exporter('getRobotExceptions',
                                              attribute=True)
            """
            return ""
        else:
            return self._execute_cmd("getRobotExceptions")

    @task
    def load(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()
        try:
            res = SampleChanger.load(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger("HWR").error(msg)
        if res:
            self.prepare_centring()
        return res

    @task
    def unload_sample(
        self,
        holderLength,
        sample_id=None,
        sample_location=None,
        successCallback=None,
        failureCallback=None,
    ):
        cell, basket, sample = sample_location
        sample = self.get_component_by_address(Pin.get_sample_address(cell, basket, sample))
        return self.unload(sample)

    @task
    def unload(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()
        try:
            SampleChanger.unload(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger("HWR").error(msg)

    def get_gripper(self):
        if self.exporter_addr:
            gripper_type = self._execute_cmd_exporter(
                "get_gripper_type", attribute=True
            )
        else:
            gripper_type = self._execute_cmd("get_gripper_type")
        return self.gripper_types.get(gripper_type, "?")

    def get_available_grippers(self):
        grippers = []
        if self.exporter_addr:
            ret = sorted(
                self._execute_cmd_exporter("getSupportedGrippers", attribute=True)
            )
            for gripper in ret:
                grippers.append(self.gripper_types[gripper])
            return grippers

    @task
    def change_gripper(self, gripper=None):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            if gripper:
                self._execute_cmd_exporter("setGripper", gripper, command=True)
            else:
                self._execute_cmd_exporter("changeGripper", command=True)
        else:
            self._execute_cmd("changeGripper")

    @task
    def home(self):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            self._execute_cmd_exporter("homeClear", command=True)
        else:
            self._execute_cmd("homeClear")

    @task
    def enable_power(self):
        if not self.exporter_addr:
            self._execute_cmd("enablePower", 1)

    @task
    def defreeze(self):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            self._execute_cmd_exporter("defreezeGripper", command=True)
        else:
            self._execute_cmd("defreezeGripper")

    def _do_load(self, sample=None):
        self._update_state()
        if self.exporter_addr:
            load_task = gevent.spawn(
                self._execute_cmd_exporter,
                "load_sample",
                sample.get_cell_no(),
                sample.get_basket_no(),
                sample.get_vial_no(),
                command=True,
            )
        else:
            load_task = gevent.spawn(
                self._execute_cmd,
                "load_sample",
                sample.get_cell_no(),
                sample.get_basket_no(),
                sample.get_vial_no(),
            )
        gevent.sleep(5)

        err_msg = "Timeout waiting for sample changer to be in safe position"
        while not load_task.ready():
            if self.exporter_addr:
                loading_state = self._execute_cmd_exporter(
                    "getCurrentLoadSampleState", attribute=True
                )
                if "on_gonio" in loading_state:
                    self._set_loaded_sample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd_exporter(
                            "getRobotIsSafe", attribute=True
                        ):
                            gevent.sleep(0.5)
                    return True
            else:
                loading_state = str(
                    self._execute_cmd("sampleStatus", "LoadSampleStatus")
                )
                if "on_gonio" in loading_state:
                    self._set_loaded_sample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while (
                            not self._execute_cmd(
                                "get_robot_cache_variable", "data:dioRobotIsSafe"
                            )
                            == "true"
                        ):
                            gevent.sleep(0.5)
                    return True
            gevent.sleep(1)

        if self.exporter_addr:
            loaded_sample = self._execute_cmd_exporter(
                "get_loaded_sample", attribute=True
            )
        else:
            loaded_sample = self._execute_cmd("get_loaded_sample")
        if loaded_sample == (
            sample.get_cell_no(),
            sample.get_basket_no(),
            sample.get_vial_no(),
        ):
            self._set_loaded_sample(sample)
            return True
        return self._check_pin_on_gonio()

    def _do_unload(self, sample=None):
        loaded_sample = self.get_loaded_sample()
        if loaded_sample is not None and loaded_sample != sample:
            raise RuntimeError("Cannot unload another sample")

        if self.exporter_addr:
            self._execute_cmd_exporter(
                "unload_sample",
                sample.get_cell_no(),
                sample.get_basket_no(),
                sample.get_vial_no(),
                command=True,
            )
            loaded_sample = self._execute_cmd_exporter(
                "get_loaded_sample", attribute=True
            )
        else:
            self._execute_cmd(
                "unload_sample",
                sample.get_cell_no(),
                sample.get_basket_no(),
                sample.get_vial_no(),
            )
            loaded_sample = self._execute_cmd("get_loaded_sample")
        if loaded_sample == (-1, -1, -1):
            self._reset_loaded_sample()
            if self.controller:
                self.controller.hutch_actions(release_interlock=True)
            return True
        return False

    def _do_abort(self):
        if self.exporter_addr:
            self._execute_cmd_exporter("abort", command=True)
        else:
            self._execute_cmd("abort")

    def _do_reset(self):
        if self.exporter_addr:
            self._execute_cmd_exporter("homeClear", command=True)
        else:
            self._execute_cmd("homeClear")

    def clear_basket_info(self, basket):
        return self._reset_basket_info(basket)

    def _reset_basket_info(self, basket):
        pass

    def clear_cell_info(self, cell):
        return self._reset_cell_info(cell)

    def _reset_cell_info(self, cell):
        pass

    def _update_state(self):
        # see if the command exists for exporter
        if not self.exporter_addr:
            defreezing = self._execute_cmd("isDefreezing")
            if defreezing:
                self._set_state(SampleChangerState.Ready)

        try:
            state = self._read_state()
        except Exception:
            state = SampleChangerState.Unknown

        self._set_state(state)

    def is_sequencer_ready(self):
        if self.prepareLoad:
            cmdobj = self.get_command_object
            return all(
                [cmd.isSpecReady() for cmd in (cmdobj("moveToLoadingPosition"),)]
            )
        return True

    def _read_state(self):
        # should read state from robot
        if self.exporter_addr:
            # state = self.swstate_attr.get_value().upper()
            state = self._execute_cmd_exporter("State", attrubute=True)
        else:
            state = "RUNNING" if self._execute_cmd("robot.isBusy") else "STANDBY"
            if state == "STANDBY" and not self.is_sequencer_ready():
                state = "RUNNING"

        state = "READY"
        state_converter = {
            "ALARM": SampleChangerState.Alarm,
            "FAULT": SampleChangerState.Fault,
            "RUNNING": SampleChangerState.Moving,
            "READY": SampleChangerState.Ready,
            "STANDBY": SampleChangerState.Ready,
        }

        return state_converter.get(state, SampleChangerState.Unknown)

    def _is_device_busy(self, state=None):
        if state is None:
            state = self._read_state()
        return state not in (
            SampleChangerState.Ready,
            SampleChangerState.Loaded,
            SampleChangerState.Alarm,
            SampleChangerState.Disabled,
            SampleChangerState.Fault,
            SampleChangerState.StandBy,
        )

    def _is_device_ready(self):
        state = self._read_state()
        return state in (SampleChangerState.Ready, SampleChangerState.Charging)

    def _wait_device_ready(self, timeout=None):
        with gevent.Timeout(timeout, Exception("Timeout waiting for device ready")):
            while not self._is_device_ready():
                gevent.sleep(0.01)

    def _update_selection(self):
        if self.exporter_addr:
            sample_cell, sample_puck, sample = self._execute_cmd_exporter(
                "get_loaded_sample", attribute=True
            )
            cell = sample_cell
            puck = sample_puck
        else:
            cell, puck = self._execute_cmd("get_cell_position")
            sample_cell, sample_puck, sample = self._execute_cmd("get_loaded_sample")

        for c in self.get_components():
            i = c.get_index()
            if cell == i + 1:
                self._set_selected_component(c)
                break

        # find sample
        for s in self.get_sample_list():
            if s.get_coords() == (sample_cell, sample_puck, sample):
                self._set_loaded_sample(s)
                # self._set_selected_sample(s)
                return

        for s in self.get_sample_list():
            s._set_loaded(False)
        self._set_selected_sample(None)

    def prepare_hutch(self, **kwargs):
        if self.exporter_addr:
            return

        user_port = kwargs.get("user_port")
        robot_port = kwargs.get("robot_port")
        if user_port is not None:
            self._execute_cmd("robot.user_port(user_port)")

        if robot_port is not None:
            self._execute_cmd("robot.robot_port(robot_port)")
Esempio n. 2
0
class EMBLFlexHCD(SampleChanger):
    __TYPE__ = "HCD"

    def __init__(self, *args, **kwargs):
        super(EMBLFlexHCD, self).__init__(self.__TYPE__, True, *args, **kwargs)

    def init(self):
        sc3_pucks = self.getProperty("sc3_pucks", True)

        for i in range(8):
            cell = Cell(self, i + 1, sc3_pucks)
            self._add_component(cell)

        self.robot = self.getProperty("tango_device")
        if self.robot:
            self.robot = DeviceProxy(self.robot)

        self.exporter_addr = self.getProperty("exporter_address")

        self.swstate_attr = self.add_channel(
            {
                "type": "exporter",
                "exporter_address": self.exporter_addr,
                "name": "swstate",
            },
            "State",
        )

        self.controller = self.getObjectByRole("controller")
        self.prepareLoad = self.get_command_object("moveToLoadingPosition")
        self.timeout = 3
        self.gripper_types = {
            -1: "No Gripper",
            1: "UNIPUCK",
            2: "MINISPINE",
            3: "FLIPPING",
            4: "UNIPUCK_DOUBLE",
            5: "PLATE",
        }

        SampleChanger.init(self)
        # self._set_state(SampleChangerState.Disabled)
        self._update_selection()
        self.state = self._read_state()

    def get_sample_list(self):
        sample_list = super().get_sample_list()
        sc_present_sample_list = self._execute_cmd_exporter(
            "getPresentSamples", attribute=True).split(":")
        present_sample_list = []

        for sample in sample_list:
            for present_sample_str in sc_present_sample_list:
                present_sample = present_sample_str.split(",")
                if sample.get_address() == (str(present_sample[0]) + ":" +
                                            str(present_sample[1]) + ":" +
                                            "%02d" % int(present_sample[4])):
                    present_sample_list.append(sample)

        return present_sample_list

    @task
    def prepare_load(self):
        if self.controller:
            self.controller.hutch_actions(enter=True)
        else:
            self.prepareLoad()

    @task
    def _prepare_centring_task(self):
        if self.controller:
            #gevent.sleep(2)
            self.controller.hutch_actions(enter=False, sc_loading=True)
        else:
            gevent.sleep(2)
            self.get_command_object("unlockMinidiffMotors")(wait=True)
            self.get_command_object("prepareCentring")(wait=True)

    def prepare_centring(self):
        self._prepare_centring_task()

    def get_sample_properties(self):
        return (Pin.__HOLDER_LENGTH_PROPERTY__, )

    def get_basket_list(self):
        basket_list = []
        # put here only the baskets that exist, not all the possible ones
        for cell in self.get_components():
            for basket in cell.get_components():
                if isinstance(basket, Basket):
                    basket_list.append(basket)

        return basket_list

    def _do_change_mode(self, *args, **kwargs):
        return

    def _do_update_info(self):
        # self._update_selection()
        self._update_state()

    def _do_scan(self, component, recursive=True, saved={"barcodes": None}):
        return

    def _execute_cmd(self, cmd, *args, **kwargs):
        timeout = kwargs.pop("timeout", None)
        if args:
            cmd_str = "flex.%s(%s)" % (cmd, ",".join(map(repr, args)))
        else:
            cmd_str = "flex.%s()" % cmd
        cmd_id = self.robot.eval(cmd_str)

        if not cmd_id:
            cmd_id = self.robot.eval(cmd_str)
        with gevent.Timeout(
                timeout,
                RuntimeError("Timeout while executing %s" % repr(cmd_str))):
            while True:
                if self.robot.is_finished(cmd_id):
                    break
                gevent.sleep(0.2)

        res = self.robot.get_result(cmd_id)
        if res:
            res = pickle.loads(base64.b64decode(res))
            if isinstance(res, Exception):
                raise res
            else:
                return res

    def _execute_cmd_exporter(self, cmd, *args, **kwargs):
        ret = None
        timeout = kwargs.pop("timeout", 900)
        if args:
            args_str = "%s" % "\t".join(map(repr, args))
        if kwargs.pop("command", None):
            exp_cmd = self.add_command(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "%s" % cmd,
                },
                "%s" % cmd,
            )
            if args:
                ret = exp_cmd(args_str)
            else:
                ret = exp_cmd()
        if kwargs.pop("attribute", None):
            exp_attr = self.add_channel(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "%s" % cmd,
                },
                "%s" % cmd[3:],
            )
            if cmd.startswith("get"):
                return exp_attr.get_value()
            if cmd.startswith("set"):
                ret = exp_attr.set_value(args_str)

        self._wait_ready(timeout=timeout)
        return ret

    def _assert_ready(self):
        if not self._ready():
            raise RuntimeError("Sample changer is busy cant mount/unmount")

    def _ready(self):
        return self._execute_cmd_exporter("getState",
                                          attribute=True) == "Ready"

    def _busy(self):
        return self._execute_cmd_exporter("getState",
                                          attribute=True) != "Ready"

    def _wait_ready(self, timeout=None):
        # None means wait forever timeout <=0 use default timeout
        if timeout is not None and timeout <= 0:
            timeout = self.timeout

        err_msg = "Timeout waiting for sample changer to be ready"

        with gevent.Timeout(timeout, RuntimeError(err_msg)):
            while not self._ready():
                gevent.sleep(0.5)

    def _wait_busy(self, timeout=None):
        # None means wait forever timeout <=0 use default timeout
        if timeout is not None and timeout <= 0:
            timeout = self.timeout

        err_msg = "Timeout waiting for sample changer action to start"

        with gevent.Timeout(timeout, RuntimeError(err_msg)):
            while not self._busy():
                gevent.sleep(0.5)

    def _do_select(self, component):
        if isinstance(component, Cell):
            cell_pos = component.get_index() + 1
        elif isinstance(component, Basket) or isinstance(component, Pin):
            cell_pos = component.get_cell_no()

        self._execute_cmd_exporter("moveDewar", cell_pos, command=True)

        self._update_selection()

    @task
    def load_sample(
        self,
        holderLength,
        sample_id=None,
        sample_location=None,
        sampleIsLoadedCallback=None,
        failureCallback=None,
        prepareCentring=True,
    ):
        # self._assert_ready()
        cell, basket, sample = sample_location
        sample = self.get_component_by_address(
            Pin.get_sample_address(cell, basket, sample))
        return self.load(sample)

    def chained_load(self, old_sample, sample):
        return self._do_load(sample)

    def _set_loaded_sample_and_prepare(self, sample):
        res = False

        if not -1 in sample:
            self._set_loaded_sample(self.get_sample_with_address(sample))
            self._prepare_centring_task()
            res = True

        return res

    def _hw_get_mounted_sample(self):
        loaded_sample = tuple(
            self._execute_cmd_exporter("getMountedSamplePosition",
                                       attribute=True))

        return (str(loaded_sample[0]) + ":" + str(loaded_sample[1]) + ":" +
                "%02d" % loaded_sample[2])

    def get_loaded_sample(self):
        sample = None

        loaded_sample_addr = self._hw_get_mounted_sample()

        for s in self.get_sample_list():
            if s.get_address() == loaded_sample_addr:
                sample = s

        return sample

    def get_sample_with_address(self, address):
        sample = None
        address = str(address[0]) + ":" + str(
            address[1]) + ":" + "%02d" % address[2]

        for s in self.get_sample_list():
            if s.get_address() == address:
                sample = s

        return sample

    def reset_loaded_sample(self):
        self._execute_cmd_exporter("resetLoadedPosition", command=True)
        self._reset_loaded_sample()

    def get_robot_exceptions(self):
        return [
            self._execute_cmd_exporter('getLastTaskException', attribute=True)
        ] or [""]

    @task
    def load(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()

        try:
            res = SampleChanger.load(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger("HWR").error(msg)

        if res:
            self.prepare_centring()

        return res

    @task
    def unload_sample(
        self,
        holderLength,
        sample_id=None,
        sample_location=None,
        successCallback=None,
        failureCallback=None,
    ):
        self._assert_ready()
        cell, basket, sample = sample_location
        sample = self.get_component_by_address(
            Pin.get_sample_address(cell, basket, sample))
        return self.unload(sample)

    @task
    def unload(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()

        if not sample:
            sample = self._hw_get_mounted_sample()

        try:
            SampleChanger.unload(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger("HWR").error(msg)

    def get_gripper(self):
        gripper_type = self._execute_cmd_exporter("get_gripper_type",
                                                  attribute=True)

        return self.gripper_types.get(gripper_type, "?")

    def get_available_grippers(self):
        grippers = []
        try:
            ret = sorted(
                self._execute_cmd_exporter("getSupportedGrippers",
                                           attribute=True))
            for gripper in ret:
                grippers.append(self.gripper_types[gripper])
        except Exception:
            grippers = [-1]

        return grippers

    @task
    def change_gripper(self, gripper=None):
        self.prepare_load(wait=True)
        self.enable_power()

        if gripper:
            self._execute_cmd_exporter("setGripper", gripper, command=True)
        else:
            self._execute_cmd_exporter("changeGripper", command=True)

    @task
    def home(self):
        self.prepare_load(wait=True)
        self.enable_power()
        self._execute_cmd_exporter("homeClear", command=True)

    @task
    def enable_power(self):
        if not self.exporter_addr:
            self._execute_cmd("enablePower", 1)

    @task
    def defreeze(self):
        self.prepare_load(wait=True)
        self.enable_power()
        self._execute_cmd_exporter("defreezeGripper", command=True)

    def _do_load(self, sample=None):
        self._update_state()

        # We wait for the sample changer if its already doing something, like defreezing
        # wait for 6 minutes then timeout !
        self._wait_ready(600)

        # Start loading
        load_task = gevent.spawn(
            self._execute_cmd_exporter,
            "loadSample",
            sample.get_cell_no(),
            sample.get_basket_no(),
            sample.get_vial_no(),
            command=True,
        )

        # Wait for sample changer to start activity
        self._wait_busy(30)

        # Wait for the sample to be loaded, (put on the goniometer)
        err_msg = "Timeout while waiting to sample to be loaded"
        with gevent.Timeout(600, RuntimeError(err_msg)):
            while not load_task.ready():
                loaded_sample = tuple(
                    self._execute_cmd_exporter("getMountedSamplePosition",
                                               attribute=True))

                if loaded_sample == (
                        sample.get_cell_no(),
                        sample.get_basket_no(),
                        sample.get_vial_no(),
                ):
                    break

                gevent.sleep(2)

        with gevent.Timeout(600, RuntimeError(err_msg)):
            while True:
                is_safe = self._execute_cmd_exporter("getRobotIsSafe",
                                                     attribute=True)

                if is_safe:
                    break

                gevent.sleep(2)

        for msg in self.get_robot_exceptions():
            logging.getLogger("HWR").error(msg)

        return self._set_loaded_sample_and_prepare(loaded_sample)

    def _do_unload(self, sample=None):
        self._execute_cmd_exporter(
            "unloadSample",
            sample.get_cell_no(),
            sample.get_basket_no(),
            sample.get_vial_no(),
            command=True,
        )

        loaded_sample = tuple(
            self._execute_cmd_exporter("getMountedSamplePosition",
                                       attribute=True))

        for msg in self.get_robot_exceptions():
            logging.getLogger("HWR").error(msg)

        if loaded_sample == (-1, -1, -1):
            self._reset_loaded_sample()

            if self.controller:
                self.controller.hutch_actions(release_interlock=True)

            return True

        return False

    def _do_abort(self):
        self._execute_cmd_exporter("abort", command=True)

    def _do_reset(self):
        self._execute_cmd_exporter("homeClear", command=True)

    def clear_basket_info(self, basket):
        return self._reset_basket_info(basket)

    def _reset_basket_info(self, basket):
        pass

    def clear_cell_info(self, cell):
        return self._reset_cell_info(cell)

    def _reset_cell_info(self, cell):
        pass

    def _update_state(self):
        try:
            state = self._read_state()
            status = self._execute_cmd_exporter("getStatus", attribute=True)
        except Exception:
            state = SampleChangerState.Unknown
            status = "Unknown"

        self._set_state(state, status)

    def is_sequencer_ready(self):
        if self.prepareLoad:
            cmdobj = self.get_command_object
            return all([
                cmd.isSpecReady()
                for cmd in (cmdobj("moveToLoadingPosition"), )
            ])
        return True

    def _read_state(self):
        state = self._execute_cmd_exporter("getState", attribute=True).upper()

        state_converter = {
            "ALARM": SampleChangerState.Alarm,
            "FAULT": SampleChangerState.Fault,
            "RUNNING": SampleChangerState.Moving,
            "READY": SampleChangerState.Ready,
            "STANDBY": SampleChangerState.Ready,
        }

        return state_converter.get(state, SampleChangerState.Unknown)

    def _is_device_busy(self, state=None):
        if state is None:
            state = self._read_state()
        return state not in (
            SampleChangerState.Ready,
            SampleChangerState.Loaded,
            SampleChangerState.Alarm,
            SampleChangerState.Disabled,
            SampleChangerState.Fault,
            SampleChangerState.StandBy,
        )

    def _is_device_ready(self):
        state = self._read_state()
        return state in (SampleChangerState.Ready, SampleChangerState.Charging)

    def _wait_device_ready(self, timeout=None):
        with gevent.Timeout(timeout,
                            Exception("Timeout waiting for device ready")):
            while not self._is_device_ready():
                gevent.sleep(0.01)

    def _update_selection(self):
        sample_cell, sample_puck, sample = self._execute_cmd_exporter(
            "getMountedSamplePosition", attribute=True)

        cell = sample_cell
        puck = sample_puck

        for c in self.get_components():
            i = c.get_index()
            if cell == i + 1:
                self._set_selected_component(c)
                break

        # find sample
        for s in self.get_sample_list():
            if s.get_coords() == (sample_cell, sample_puck, sample):
                self._set_loaded_sample(s)
                self._set_selected_sample(s)
            else:
                s._set_loaded(False)

        self._set_selected_sample(None)

    def prepare_hutch(self, **kwargs):
        return
Esempio n. 3
0
class FlexHCD(SampleChanger):
    __TYPE__ = 'HCD'

    def __init__(self, *args, **kwargs):
        super(FlexHCD, self).__init__(self.__TYPE__, True, *args, **kwargs)

    def init(self):
        sc3_pucks = self.getProperty('sc3_pucks', True)

        for i in range(8):
            cell = Cell(self, i + 1, sc3_pucks)
            self._addComponent(cell)

        self.robot = self.getProperty('tango_device')
        if self.robot:
            self.robot = DeviceProxy(self.robot)

        self.exporter_addr = self.getProperty('exporter_address')
        if self.exporter_addr:
            self.swstate_attr = self.addChannel(
                {
                    'type': 'exporter',
                    'exporter_address': self.exporter_addr,
                    'name': 'swstate'
                }, 'State')

        self.controller = self.getObjectByRole('controller')
        self.prepareLoad = self.getCommandObject('moveToLoadingPosition')
        self.timeout = 3
        self.gripper_types = {
            -1: "No Gripper",
            1: "UNIPUCK",
            2: "MINISPINE",
            3: "FLIPPING",
            4: "UNIPUCK_DOUBLE",
            5: "PLATE"
        }

        return SampleChanger.init(self)

    @task
    def prepare_load(self):
        if self.controller:
            self.controller.hutch_actions(condition=True)
        else:
            self.prepareLoad()

    @task
    def prepare_centring(self):
        if self.controller:
            self.controller.hutch_actions(condition=False, sc_loading=True)
        else:
            gevent.sleep(2)
            self.getCommandObject('unlockMinidiffMotors')(wait=True)
            self.getCommandObject('prepareCentring')(wait=True)

    def prepareCentring(self):
        self.prepare_centring()

    def getSampleProperties(self):
        return (Pin.__HOLDER_LENGTH_PROPERTY__, )

    def getBasketList(self):
        basket_list = []
        # put here only the baskets that exist, not all the possible ones
        # if self.exporter_addr:
        #    basket_list =
        for cell in self.getComponents():
            for basket in cell.getComponents():
                if isinstance(basket, Basket):
                    basket_list.append(basket)

        return basket_list

    def _doChangeMode(self, *args, **kwargs):
        return

    def _doUpdateInfo(self):
        # self._updateSelection()
        self._updateState()

    def _doScan(self, component, recursive=True, saved={'barcodes': None}):
        return

    def _execute_cmd(self, cmd, *args, **kwargs):
        timeout = kwargs.pop('timeout', None)
        if args:
            cmd_str = 'flex.%s(%s)' % (cmd, ",".join(map(repr, args)))
        else:
            cmd_str = 'flex.%s()' % cmd
        cmd_id = self.robot.eval(cmd_str)

        if not cmd_id:
            cmd_id = self.robot.eval(cmd_str)
        with gevent.Timeout(
                timeout,
                RuntimeError("Timeout while executing %s" % repr(cmd_str))):
            while True:
                if self.robot.is_finished(cmd_id):
                    break
                gevent.sleep(0.2)

        res = self.robot.get_result(cmd_id)
        if res:
            res = cPickle.loads(base64.decodestring(res))
            if isinstance(res, Exception):
                raise res
            else:
                return res

    def _execute_cmd_exporter(self, cmd, *args, **kwargs):
        timeout = kwargs.pop('timeout', 900)
        if args:
            args_str = "%s" % "\t".join(map(repr, args))
        if kwargs.pop('command', None):
            exp_cmd = self.addCommand(
                {
                    'type': 'exporter',
                    'exporter_address': self.exporter_addr,
                    'name': '%s' % cmd
                }, "%s" % cmd)
            if args:
                ret = exp_cmd(args_str)
            else:
                ret = exp_cmd()
        if kwargs.pop('attribute', None):
            exp_attr = self.addChannel(
                {
                    'type': 'exporter',
                    'exporter_address': self.exporter_addr,
                    'name': '%s' % cmd
                }, "%s" % cmd[3:])
            if cmd.startswith('get'):
                return exp_attr.getValue()
            if cmd.startswith('set'):
                ret = exp_attr.setValue(args_str)

        self._wait_ready(timeout=timeout)
        return ret

    def _ready(self):
        return self.swstate_attr.getValue() == 'Ready'

    def _wait_ready(self, timeout=None):
        err_msg = "Timeout waiting for sample changer to be ready"
        # None means infinite timeout <=0 means default timeout
        if timeout is not None and timeout <= 0:
            timeout = self.timeout
        with gevent.Timeout(timeout, RuntimeError(err_msg)):
            while not self._ready():
                time.sleep(0.5)

    def _doSelect(self, component):
        if isinstance(component, Cell):
            cell_pos = component.getIndex() + 1
        elif isinstance(component, Basket) or isinstance(component, Pin):
            cell_pos = component.getCellNo()

        if self.exporter_addr:
            self._execute_cmd_exporter('moveDewar', cell_pos, command=True)
        else:
            self._execute_cmd('moveDewar', cell_pos)

        self._updateSelection()

    @task
    def load_sample(self,
                    holderLength,
                    sample_id=None,
                    sample_location=None,
                    sampleIsLoadedCallback=None,
                    failureCallback=None,
                    prepareCentring=True):
        cell, basket, sample = sample_location
        sample = self.getComponentByAddress(
            Pin.getSampleAddress(cell, basket, sample))
        return self.load(sample)

    def chained_load(self, old_sample, sample):
        if self.exporter_addr:
            unload_load_task = gevent.spawn(self._execute_cmd_exporter,
                                            'loadSample',
                                            sample.getCellNo(),
                                            sample.getBasketNo(),
                                            sample.getVialNo(),
                                            command=True)
        else:
            unload_load_task = gevent.spawn(
                self._execute_cmd, 'chainedUnldLd', [
                    old_sample.getCellNo(),
                    old_sample.getBasketNo(),
                    old_sample.getVialNo()
                ],
                [sample.getCellNo(),
                 sample.getBasketNo(),
                 sample.getVialNo()])

        gevent.sleep(15)

        err_msg = "Timeout waiting for sample changer to be in safe position"
        while not unload_load_task.ready():
            if self.exporter_addr:
                loading_state = self._execute_cmd_exporter(
                    'getCurrentLoadSampleState', attribute=True)
                if 'on_gonio' in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd_exporter('getRobotIsSafe',
                                                             attribute=True):
                            gevent.sleep(0.5)
                    return True
            else:
                loading_state = str(
                    self._execute_cmd('sampleStatus', 'LoadSampleStatus'))
                if 'on_gonio' in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd(
                                'get_robot_cache_variable',
                                'data:dioRobotIsSafe') == 'true':
                            gevent.sleep(0.5)
                    return True
            gevent.sleep(1)

        logging.getLogger('HWR').info("unload load task done")
        for msg in self.get_robot_exceptions():
            logging.getLogger('HWR').error(msg)

        return self._check_pin_on_gonio()

    def _check_pin_on_gonio(self):
        print "_check_pin_on_gonio"
        if self.exporter_addr:
            _on_gonio = self._execute_cmd_exporter('pin_on_gonio',
                                                   command=True)
        else:
            _on_gonio = self._execute_cmd('pin_on_gonio')

        if _on_gonio:
            # finish the loading actions
            self.prepare_centring()
            return True
        else:
            logging.getLogger('HWR').info("reset loaded sample")
            self._resetLoadedSample()
            # if self.controller:
            #    self.controller.hutch_actions(release_interlock=True)
            return False

    def reset_loaded_sample(self):
        if self.exporter_addr:
            self._execute_cmd_exporter('reset_loaded_position', command=True)
        else:
            self._execute_cmd('reset_loaded_position')
        self._resetLoadedSample()

    def get_robot_exceptions(self):
        if self.exporter_addr:
            """
            return self._execute_cmd_exporter('getRobotExceptions',
                                              attribute=True)
            """
            return ""
        else:
            return self._execute_cmd('getRobotExceptions')

    @task
    def load(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()
        try:
            res = SampleChanger.load(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger('HWR').error(msg)
        if res:
            self.prepareCentring()
        return res

    @task
    def unload_sample(self,
                      holderLength,
                      sample_id=None,
                      sample_location=None,
                      successCallback=None,
                      failureCallback=None):
        cell, basket, sample = sample_location
        sample = self.getComponentByAddress(
            Pin.getSampleAddress(cell, basket, sample))
        return self.unload(sample)

    @task
    def unload(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()
        try:
            SampleChanger.unload(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger('HWR').error(msg)

    def get_gripper(self):
        if self.exporter_addr:
            gripper_type = self._execute_cmd_exporter('get_gripper_type',
                                                      attribute=True)
        else:
            gripper_type = self._execute_cmd('get_gripper_type')
        return self.gripper_types.get(gripper_type, '?')

    def get_available_grippers(self):
        grippers = []
        if self.exporter_addr:
            ret = self._execute_cmd_exporter('getSupportedGrippers',
                                             attribute=True)
            ret.sort()
            for gripper in ret:
                grippers.append(self.gripper_types[gripper])
            return grippers

    @task
    def change_gripper(self, gripper=None):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            if gripper:
                self._execute_cmd_exporter('setGripper', gripper, command=True)
            else:
                self._execute_cmd_exporter('changeGripper', command=True)
        else:
            self._execute_cmd('changeGripper')

    @task
    def home(self):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            self._execute_cmd_exporter('homeClear', command=True)
        else:
            self._execute_cmd('homeClear')

    @task
    def enable_power(self):
        if not self.exporter_addr:
            self._execute_cmd('enablePower', 1)

    @task
    def defreeze(self):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            self._execute_cmd_exporter('defreezeGripper', command=True)
        else:
            self._execute_cmd('defreezeGripper')

    def _doLoad(self, sample=None):
        self._updateState()
        if self.exporter_addr:
            load_task = gevent.spawn(self._execute_cmd_exporter,
                                     'loadSample',
                                     sample.getCellNo(),
                                     sample.getBasketNo(),
                                     sample.getVialNo(),
                                     command=True)
        else:
            load_task = gevent.spawn(self._execute_cmd, 'loadSample',
                                     sample.getCellNo(), sample.getBasketNo(),
                                     sample.getVialNo())
        gevent.sleep(5)

        err_msg = "Timeout waiting for sample changer to be in safe position"
        while not load_task.ready():
            if self.exporter_addr:
                loading_state = self._execute_cmd_exporter(
                    'getCurrentLoadSampleState', attribute=True)
                if 'on_gonio' in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd_exporter('getRobotIsSafe',
                                                             attribute=True):
                            gevent.sleep(0.5)
                    return True
            else:
                loading_state = str(
                    self._execute_cmd('sampleStatus', 'LoadSampleStatus'))
                if 'on_gonio' in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd(
                                'get_robot_cache_variable',
                                'data:dioRobotIsSafe') == 'true':
                            gevent.sleep(0.5)
                    return True
            gevent.sleep(1)

        if self.exporter_addr:
            loaded_sample = self._execute_cmd_exporter('get_loaded_sample',
                                                       attribute=True)
        else:
            loaded_sample = self._execute_cmd('get_loaded_sample')
        if loaded_sample == (sample.getCellNo(), sample.getBasketNo(),
                             sample.getVialNo()):
            self._setLoadedSample(sample)
            return True
        return self._check_pin_on_gonio()

    def _doUnload(self, sample=None):
        loaded_sample = self.getLoadedSample()
        if loaded_sample is not None and loaded_sample != sample:
            raise RuntimeError("Cannot unload another sample")

        if self.exporter_addr:
            self._execute_cmd_exporter('unloadSample',
                                       sample.getCellNo(),
                                       sample.getBasketNo(),
                                       sample.getVialNo(),
                                       command=True)
            loaded_sample = self._execute_cmd_exporter('get_loaded_sample',
                                                       attribute=True)
        else:
            self._execute_cmd('unloadSample', sample.getCellNo(),
                              sample.getBasketNo(), sample.getVialNo())
            loaded_sample = self._execute_cmd('get_loaded_sample')
        if loaded_sample == (-1, -1, -1):
            self._resetLoadedSample()
            if self.controller:
                self.controller.hutch_actions(release_interlock=True)
            return True
        return False

    def _doAbort(self):
        if self.exporter_addr:
            self._execute_cmd_exporter('abort', command=True)
        else:
            self._execute_cmd('abort')

    def _doReset(self):
        if self.exporter_addr:
            self._execute_cmd_exporter('homeClear', command=True)
        else:
            self._execute_cmd('homeClear')

    def clearBasketInfo(self, basket):
        return self._reset_basket_info(basket)

    def _reset_basket_info(self, basket):
        pass

    def clearCellInfo(self, cell):
        return self._reset_cell_info(cell)

    def _reset_cell_info(self, cell):
        pass

    def _updateState(self):
        # see if the command exists for exporter
        if not self.exporter_addr:
            defreezing = self._execute_cmd('isDefreezing')
            if defreezing:
                self._setState(SampleChangerState.Ready)

        try:
            state = self._readState()
        except Exception:
            state = SampleChangerState.Unknown

        self._setState(state)

    def isSequencerReady(self):
        if self.prepareLoad:
            cmdobj = self.getCommandObject
            return all([
                cmd.isSpecReady()
                for cmd in (cmdobj('moveToLoadingPosition'), )
            ])
        return True

    def _readState(self):
        # should read state from robot
        if self.exporter_addr:
            state = self.swstate_attr.getValue().upper()
        else:
            state = 'RUNNING' if self._execute_cmd(
                'robot.isBusy') else 'STANDBY'
            if state == 'STANDBY' and not self.isSequencerReady():
                state = 'RUNNING'

        state_converter = {
            'ALARM': SampleChangerState.Alarm,
            'FAULT': SampleChangerState.Fault,
            'RUNNING': SampleChangerState.Moving,
            'READY': SampleChangerState.Ready,
            'STANDBY': SampleChangerState.Ready
        }

        return state_converter.get(state, SampleChangerState.Unknown)

    def _isDeviceBusy(self, state=None):
        if state is None:
            state = self._readState()
        return state not in (SampleChangerState.Ready,
                             SampleChangerState.Loaded,
                             SampleChangerState.Alarm,
                             SampleChangerState.Disabled,
                             SampleChangerState.Fault,
                             SampleChangerState.StandBy)

    def _isDeviceReady(self):
        state = self._readState()
        return state in (SampleChangerState.Ready, SampleChangerState.Charging)

    def _waitDeviceReady(self, timeout=None):
        with gevent.Timeout(timeout,
                            Exception("Timeout waiting for device ready")):
            while not self._isDeviceReady():
                gevent.sleep(0.01)

    def _updateSelection(self):
        if self.exporter_addr:
            sample_cell, sample_puck, sample = self._execute_cmd_exporter(
                'get_loaded_sample', attribute=True)
            cell = sample_cell
            puck = sample_puck
        else:
            cell, puck = self._execute_cmd('get_cell_position')
            sample_cell, sample_puck, sample = self._execute_cmd(
                'get_loaded_sample')

        for c in self.getComponents():
            i = c.getIndex()
            if cell == i + 1:
                self._setSelectedComponent(c)
                break

        # find sample
        for s in self.getSampleList():
            if s.getCoords() == (sample_cell, sample_puck, sample):
                self._setLoadedSample(s)
                # self._setSelectedSample(s)
                return

        for s in self.getSampleList():
            s._setLoaded(False)
        self._setSelectedSample(None)

    def prepare_hutch(self, **kwargs):
        if self.exporter_addr:
            return

        user_port = kwargs.get('user_port')
        robot_port = kwargs.get('robot_port')
        if user_port is not None:
            self._execute_cmd('robot.user_port(user_port)')

        if robot_port is not None:
            self._execute_cmd('robot.robot_port(robot_port)')
Esempio n. 4
0
class FlexHCD(SampleChanger):
    __TYPE__ = "HCD"

    def __init__(self, *args, **kwargs):
        super(FlexHCD, self).__init__(self.__TYPE__, True, *args, **kwargs)

    def init(self):
        sc3_pucks = self.getProperty("sc3_pucks", True)

        for i in range(8):
            cell = Cell(self, i + 1, sc3_pucks)
            self._addComponent(cell)

        self.robot = self.getProperty("tango_device")
        if self.robot:
            self.robot = DeviceProxy(self.robot)

        self.exporter_addr = self.getProperty("exporter_address")
        if self.exporter_addr:
            self.swstate_attr = self.addChannel(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "swstate",
                },
                "State",
            )

        self.controller = self.getObjectByRole("controller")
        self.prepareLoad = self.getCommandObject("moveToLoadingPosition")
        self.timeout = 3
        self.gripper_types = {
            -1: "No Gripper",
            1: "UNIPUCK",
            2: "MINISPINE",
            3: "FLIPPING",
            4: "UNIPUCK_DOUBLE",
            5: "PLATE",
        }

        return SampleChanger.init(self)

    @task
    def prepare_load(self):
        if self.controller:
            self.controller.hutch_actions(condition=True)
        else:
            self.prepareLoad()

    @task
    def prepare_centring(self):
        if self.controller:
            self.controller.hutch_actions(condition=False, sc_loading=True)
        else:
            gevent.sleep(2)
            self.getCommandObject("unlockMinidiffMotors")(wait=True)
            self.getCommandObject("prepareCentring")(wait=True)

    def prepareCentring(self):
        self.prepare_centring()

    def getSampleProperties(self):
        return (Pin.__HOLDER_LENGTH_PROPERTY__,)

    def getBasketList(self):
        basket_list = []
        # put here only the baskets that exist, not all the possible ones
        # if self.exporter_addr:
        #    basket_list =
        for cell in self.getComponents():
            for basket in cell.getComponents():
                if isinstance(basket, Basket):
                    basket_list.append(basket)

        return basket_list

    def _doChangeMode(self, *args, **kwargs):
        return

    def _doUpdateInfo(self):
        # self._updateSelection()
        self._updateState()

    def _doScan(self, component, recursive=True, saved={"barcodes": None}):
        return

    def _execute_cmd(self, cmd, *args, **kwargs):
        timeout = kwargs.pop("timeout", None)
        if args:
            cmd_str = "flex.%s(%s)" % (cmd, ",".join(map(repr, args)))
        else:
            cmd_str = "flex.%s()" % cmd
        cmd_id = self.robot.eval(cmd_str)

        if not cmd_id:
            cmd_id = self.robot.eval(cmd_str)
        with gevent.Timeout(
            timeout, RuntimeError("Timeout while executing %s" % repr(cmd_str))
        ):
            while True:
                if self.robot.is_finished(cmd_id):
                    break
                gevent.sleep(0.2)

        res = self.robot.get_result(cmd_id)
        if res:
            res = cPickle.loads(base64.decodestring(res))
            if isinstance(res, Exception):
                raise res
            else:
                return res

    def _execute_cmd_exporter(self, cmd, *args, **kwargs):
        timeout = kwargs.pop("timeout", 900)
        if args:
            args_str = "%s" % "\t".join(map(repr, args))
        if kwargs.pop("command", None):
            exp_cmd = self.addCommand(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "%s" % cmd,
                },
                "%s" % cmd,
            )
            if args:
                ret = exp_cmd(args_str)
            else:
                ret = exp_cmd()
        if kwargs.pop("attribute", None):
            exp_attr = self.addChannel(
                {
                    "type": "exporter",
                    "exporter_address": self.exporter_addr,
                    "name": "%s" % cmd,
                },
                "%s" % cmd[3:],
            )
            if cmd.startswith("get"):
                return exp_attr.getValue()
            if cmd.startswith("set"):
                ret = exp_attr.setValue(args_str)

        self._wait_ready(timeout=timeout)
        return ret

    def _ready(self):
        return self.swstate_attr.getValue() == "Ready"

    def _wait_ready(self, timeout=None):
        err_msg = "Timeout waiting for sample changer to be ready"
        # None means infinite timeout <=0 means default timeout
        if timeout is not None and timeout <= 0:
            timeout = self.timeout
        with gevent.Timeout(timeout, RuntimeError(err_msg)):
            while not self._ready():
                time.sleep(0.5)

    def _doSelect(self, component):
        if isinstance(component, Cell):
            cell_pos = component.getIndex() + 1
        elif isinstance(component, Basket) or isinstance(component, Pin):
            cell_pos = component.getCellNo()

        if self.exporter_addr:
            self._execute_cmd_exporter("moveDewar", cell_pos, command=True)
        else:
            self._execute_cmd("moveDewar", cell_pos)

        self._updateSelection()

    @task
    def load_sample(
        self,
        holderLength,
        sample_id=None,
        sample_location=None,
        sampleIsLoadedCallback=None,
        failureCallback=None,
        prepareCentring=True,
    ):
        cell, basket, sample = sample_location
        sample = self.getComponentByAddress(Pin.getSampleAddress(cell, basket, sample))
        return self.load(sample)

    def chained_load(self, old_sample, sample):
        if self.exporter_addr:
            unload_load_task = gevent.spawn(
                self._execute_cmd_exporter,
                "loadSample",
                sample.getCellNo(),
                sample.getBasketNo(),
                sample.getVialNo(),
                command=True,
            )
        else:
            unload_load_task = gevent.spawn(
                self._execute_cmd,
                "chainedUnldLd",
                [
                    old_sample.getCellNo(),
                    old_sample.getBasketNo(),
                    old_sample.getVialNo(),
                ],
                [sample.getCellNo(), sample.getBasketNo(), sample.getVialNo()],
            )

        gevent.sleep(15)

        err_msg = "Timeout waiting for sample changer to be in safe position"
        while not unload_load_task.ready():
            if self.exporter_addr:
                loading_state = self._execute_cmd_exporter(
                    "getCurrentLoadSampleState", attribute=True
                )
                if "on_gonio" in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd_exporter(
                            "getRobotIsSafe", attribute=True
                        ):
                            gevent.sleep(0.5)
                    return True
            else:
                loading_state = str(
                    self._execute_cmd("sampleStatus", "LoadSampleStatus")
                )
                if "on_gonio" in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while (
                            not self._execute_cmd(
                                "get_robot_cache_variable", "data:dioRobotIsSafe"
                            )
                            == "true"
                        ):
                            gevent.sleep(0.5)
                    return True
            gevent.sleep(1)

        logging.getLogger("HWR").info("unload load task done")
        for msg in self.get_robot_exceptions():
            logging.getLogger("HWR").error(msg)

        return self._check_pin_on_gonio()

    def _check_pin_on_gonio(self):
        if self.exporter_addr:
            _on_gonio = self._execute_cmd_exporter("pin_on_gonio", command=True)
        else:
            _on_gonio = self._execute_cmd("pin_on_gonio")

        if _on_gonio:
            # finish the loading actions
            self.prepare_centring()
            return True
        else:
            logging.getLogger("HWR").info("reset loaded sample")
            self._resetLoadedSample()
            # if self.controller:
            #    self.controller.hutch_actions(release_interlock=True)
            return False

    def reset_loaded_sample(self):
        if self.exporter_addr:
            self._execute_cmd_exporter("reset_loaded_position", command=True)
        else:
            self._execute_cmd("reset_loaded_position")
        self._resetLoadedSample()

    def get_robot_exceptions(self):
        if self.exporter_addr:
            """
            return self._execute_cmd_exporter('getRobotExceptions',
                                              attribute=True)
            """
            return ""
        else:
            return self._execute_cmd("getRobotExceptions")

    @task
    def load(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()
        try:
            res = SampleChanger.load(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger("HWR").error(msg)
        if res:
            self.prepareCentring()
        return res

    @task
    def unload_sample(
        self,
        holderLength,
        sample_id=None,
        sample_location=None,
        successCallback=None,
        failureCallback=None,
    ):
        cell, basket, sample = sample_location
        sample = self.getComponentByAddress(Pin.getSampleAddress(cell, basket, sample))
        return self.unload(sample)

    @task
    def unload(self, sample):
        self.prepare_load(wait=True)
        self.enable_power()
        try:
            SampleChanger.unload(self, sample)
        finally:
            for msg in self.get_robot_exceptions():
                logging.getLogger("HWR").error(msg)

    def get_gripper(self):
        if self.exporter_addr:
            gripper_type = self._execute_cmd_exporter(
                "get_gripper_type", attribute=True
            )
        else:
            gripper_type = self._execute_cmd("get_gripper_type")
        return self.gripper_types.get(gripper_type, "?")

    def get_available_grippers(self):
        grippers = []
        if self.exporter_addr:
            ret = sorted(
                self._execute_cmd_exporter("getSupportedGrippers", attribute=True)
            )
            for gripper in ret:
                grippers.append(self.gripper_types[gripper])
            return grippers

    @task
    def change_gripper(self, gripper=None):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            if gripper:
                self._execute_cmd_exporter("setGripper", gripper, command=True)
            else:
                self._execute_cmd_exporter("changeGripper", command=True)
        else:
            self._execute_cmd("changeGripper")

    @task
    def home(self):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            self._execute_cmd_exporter("homeClear", command=True)
        else:
            self._execute_cmd("homeClear")

    @task
    def enable_power(self):
        if not self.exporter_addr:
            self._execute_cmd("enablePower", 1)

    @task
    def defreeze(self):
        self.prepare_load(wait=True)
        self.enable_power()
        if self.exporter_addr:
            self._execute_cmd_exporter("defreezeGripper", command=True)
        else:
            self._execute_cmd("defreezeGripper")

    def _doLoad(self, sample=None):
        self._updateState()
        if self.exporter_addr:
            load_task = gevent.spawn(
                self._execute_cmd_exporter,
                "loadSample",
                sample.getCellNo(),
                sample.getBasketNo(),
                sample.getVialNo(),
                command=True,
            )
        else:
            load_task = gevent.spawn(
                self._execute_cmd,
                "loadSample",
                sample.getCellNo(),
                sample.getBasketNo(),
                sample.getVialNo(),
            )
        gevent.sleep(5)

        err_msg = "Timeout waiting for sample changer to be in safe position"
        while not load_task.ready():
            if self.exporter_addr:
                loading_state = self._execute_cmd_exporter(
                    "getCurrentLoadSampleState", attribute=True
                )
                if "on_gonio" in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while not self._execute_cmd_exporter(
                            "getRobotIsSafe", attribute=True
                        ):
                            gevent.sleep(0.5)
                    return True
            else:
                loading_state = str(
                    self._execute_cmd("sampleStatus", "LoadSampleStatus")
                )
                if "on_gonio" in loading_state:
                    self._setLoadedSample(sample)
                    with gevent.Timeout(20, RuntimeError(err_msg)):
                        while (
                            not self._execute_cmd(
                                "get_robot_cache_variable", "data:dioRobotIsSafe"
                            )
                            == "true"
                        ):
                            gevent.sleep(0.5)
                    return True
            gevent.sleep(1)

        if self.exporter_addr:
            loaded_sample = self._execute_cmd_exporter(
                "get_loaded_sample", attribute=True
            )
        else:
            loaded_sample = self._execute_cmd("get_loaded_sample")
        if loaded_sample == (
            sample.getCellNo(),
            sample.getBasketNo(),
            sample.getVialNo(),
        ):
            self._setLoadedSample(sample)
            return True
        return self._check_pin_on_gonio()

    def _doUnload(self, sample=None):
        loaded_sample = self.getLoadedSample()
        if loaded_sample is not None and loaded_sample != sample:
            raise RuntimeError("Cannot unload another sample")

        if self.exporter_addr:
            self._execute_cmd_exporter(
                "unloadSample",
                sample.getCellNo(),
                sample.getBasketNo(),
                sample.getVialNo(),
                command=True,
            )
            loaded_sample = self._execute_cmd_exporter(
                "get_loaded_sample", attribute=True
            )
        else:
            self._execute_cmd(
                "unloadSample",
                sample.getCellNo(),
                sample.getBasketNo(),
                sample.getVialNo(),
            )
            loaded_sample = self._execute_cmd("get_loaded_sample")
        if loaded_sample == (-1, -1, -1):
            self._resetLoadedSample()
            if self.controller:
                self.controller.hutch_actions(release_interlock=True)
            return True
        return False

    def _doAbort(self):
        if self.exporter_addr:
            self._execute_cmd_exporter("abort", command=True)
        else:
            self._execute_cmd("abort")

    def _doReset(self):
        if self.exporter_addr:
            self._execute_cmd_exporter("homeClear", command=True)
        else:
            self._execute_cmd("homeClear")

    def clearBasketInfo(self, basket):
        return self._reset_basket_info(basket)

    def _reset_basket_info(self, basket):
        pass

    def clearCellInfo(self, cell):
        return self._reset_cell_info(cell)

    def _reset_cell_info(self, cell):
        pass

    def _updateState(self):
        # see if the command exists for exporter
        if not self.exporter_addr:
            defreezing = self._execute_cmd("isDefreezing")
            if defreezing:
                self._setState(SampleChangerState.Ready)

        try:
            state = self._readState()
        except Exception:
            state = SampleChangerState.Unknown

        self._setState(state)

    def isSequencerReady(self):
        if self.prepareLoad:
            cmdobj = self.getCommandObject
            return all(
                [cmd.isSpecReady() for cmd in (cmdobj("moveToLoadingPosition"),)]
            )
        return True

    def _readState(self):
        # should read state from robot
        if self.exporter_addr:
            state = self.swstate_attr.getValue().upper()
        else:
            state = "RUNNING" if self._execute_cmd("robot.isBusy") else "STANDBY"
            if state == "STANDBY" and not self.isSequencerReady():
                state = "RUNNING"

        state_converter = {
            "ALARM": SampleChangerState.Alarm,
            "FAULT": SampleChangerState.Fault,
            "RUNNING": SampleChangerState.Moving,
            "READY": SampleChangerState.Ready,
            "STANDBY": SampleChangerState.Ready,
        }

        return state_converter.get(state, SampleChangerState.Unknown)

    def _isDeviceBusy(self, state=None):
        if state is None:
            state = self._readState()
        return state not in (
            SampleChangerState.Ready,
            SampleChangerState.Loaded,
            SampleChangerState.Alarm,
            SampleChangerState.Disabled,
            SampleChangerState.Fault,
            SampleChangerState.StandBy,
        )

    def _isDeviceReady(self):
        state = self._readState()
        return state in (SampleChangerState.Ready, SampleChangerState.Charging)

    def _waitDeviceReady(self, timeout=None):
        with gevent.Timeout(timeout, Exception("Timeout waiting for device ready")):
            while not self._isDeviceReady():
                gevent.sleep(0.01)

    def _updateSelection(self):
        if self.exporter_addr:
            sample_cell, sample_puck, sample = self._execute_cmd_exporter(
                "get_loaded_sample", attribute=True
            )
            cell = sample_cell
            puck = sample_puck
        else:
            cell, puck = self._execute_cmd("get_cell_position")
            sample_cell, sample_puck, sample = self._execute_cmd("get_loaded_sample")

        for c in self.getComponents():
            i = c.getIndex()
            if cell == i + 1:
                self._setSelectedComponent(c)
                break

        # find sample
        for s in self.getSampleList():
            if s.getCoords() == (sample_cell, sample_puck, sample):
                self._setLoadedSample(s)
                # self._setSelectedSample(s)
                return

        for s in self.getSampleList():
            s._setLoaded(False)
        self._setSelectedSample(None)

    def prepare_hutch(self, **kwargs):
        if self.exporter_addr:
            return

        user_port = kwargs.get("user_port")
        robot_port = kwargs.get("robot_port")
        if user_port is not None:
            self._execute_cmd("robot.user_port(user_port)")

        if robot_port is not None:
            self._execute_cmd("robot.robot_port(robot_port)")