Exemple #1
0
class ALBAPilatus(AbstractDetector, HardwareObject):
    """Detector class. Contains all information about detector
       - states are 'OK', and 'BAD'
       - status is busy, exposing, ready, etc.
       - physical property is RH for pilatus, P for rayonix
    """
    def __init__(self, name):
        AbstractDetector.__init__(self)
        HardwareObject.__init__(self, name)

        self.distance_motor_hwobj = None
        self.default_distance = None
        self.default_distance_limits = None

        self.default_latency_time = 0.003

        self.exp_time_limits = None

        self.headers = {}

    def init(self):
        self.distance_motor_hwobj = self.getObjectByRole("distance_motor")
        self.devname = self.getProperty("tangoname")

        try:
            self.latency_time = float(self.getProperty("latency_time"))
        except Exception:
            self.latency_time = None

        if self.latency_time is None:
            logging.getLogger("HWR").debug(
                "Cannot obtain latency time from Pilatus XML. Using %s" %
                self.default_latency_time)
            self.latency_time = self.default_latency_time

        self.devspecific = self.getProperty("device_specific")

        exp_time_limits = self.getProperty("exposure_limits")
        self.exp_time_limits = map(float, exp_time_limits.strip().split(","))

        self.device = DeviceProxy(self.devname)
        self.device_specific = DeviceProxy(self.devspecific)
        self.device.set_timeout_millis(30000)

        self.beamx_chan = self.get_channel_object("beamx")
        self.beamy_chan = self.get_channel_object("beamy")

    def start_acquisition(self):
        self.device.startAcq()

    def stop_acquisition(self):
        self.device.abortAcq()

    def wait_move_distance_done(self):
        self.distance_motor_hwobj.wait_end_of_move()

    def get_threshold(self):
        return self.device_specific.threshold

    def get_threshold_gain(self):
        return self.device_specific.threshold_gain

    def has_shutterless(self):
        """Return True if has shutterless mode"""
        return True

    def get_beam_centre(self):
        """Returns beam center coordinates"""
        beam_x = 0
        beam_y = 0
        try:
            beam_x = self.beamx_chan.getValue()
            beam_y = self.beamy_chan.getValue()
        except Exception:
            pass
        return beam_x, beam_y

    def get_manufacturer(self):
        return self.getProperty("manufacturer")
        return "Dectris"

    def get_model(self):
        return self.getProperty("model")

    def get_detector_type(self):
        return self.getProperty("type")

    def get_default_exposure_time(self):
        return self.getProperty("default_exposure_time")

    def get_minimum_exposure_time(self):
        return self.getProperty("minimum_exposure_time")

    def get_exposure_time_limits(self):
        """Returns exposure time limits as list with two floats"""
        return self.exp_time_limits

    def get_file_suffix(self):
        return self.getProperty("file_suffix")

    def get_pixel_size(self):
        return self.getProperty("px"), self.getProperty("py")

    # methods for data collection
    def set_energy_threshold(self):
        eugap_ch = self.get_channel_object("eugap")

        try:
            currentenergy = eugap_ch.getValue()
        except Exception:
            currentenergy = 12.6

        det_energy = self.get_threshold()

        # threshold = det_energy  / 2.
        # limitenergy = threshold / 0.8

        if round(currentenergy, 6) < 7.538:
            currentenergy = 7.538

        kev_diff = abs(det_energy - currentenergy)

        if kev_diff > 1.2:
            logging.getLogger("HWR").debug(
                "programming energy_threshold on pilatus to: %s" %
                currentenergy)
            # if self.wait_standby():
            # self.device_specific.energy_threshold = currentenergy

    def get_latency_time(self):
        return self.latency_time

    def wait_standby(self, timeout=300):
        t0 = time.time()
        while self.device_specific.cam_state == "STANDBY":
            if time.time() - t0 > timeout:
                print("timeout waiting for Pilatus to be on STANDBY")
                return False
            time.sleep(0.1)
        return True

    def prepare_acquisition(self, dcpars):

        self.set_energy_threshold()
        # self.wait_standby()

        osc_seq = dcpars["oscillation_sequence"][0]
        file_pars = dcpars["fileinfo"]

        basedir = file_pars["directory"]
        prefix = "%s_%s_" % (file_pars["prefix"], file_pars["run_number"])

        first_img_no = osc_seq["start_image_number"]
        nb_frames = osc_seq["number_of_images"]
        exp_time = osc_seq["exposure_time"]

        fileformat = "CBF"
        trig_mode = "EXTERNAL_TRIGGER"
        # latency_time = 0.003

        logging.getLogger("HWR").debug(
            " Preparing detector (dev=%s) for data collection" % self.devname)

        logging.getLogger("HWR").debug("    /saving directory: %s" % basedir)
        logging.getLogger("HWR").debug("    /prefix          : %s" % prefix)
        logging.getLogger("HWR").debug("    /saving_format   : %s" %
                                       fileformat)
        logging.getLogger("HWR").debug("    /trigger_mode    : %s" % trig_mode)
        logging.getLogger("HWR").debug("    /acq_nb_frames   : %s" % nb_frames)
        logging.getLogger("HWR").debug("    /acq_expo_time   : %s" %
                                       str(exp_time - self.latency_time))
        logging.getLogger("HWR").debug("    /latency_time    : %s" %
                                       self.latency_time)

        self.device.write_attribute("saving_mode", "AUTO_FRAME")
        self.device.write_attribute("saving_directory", basedir)
        self.device.write_attribute("saving_prefix", prefix)
        self.device.write_attribute("saving_format", fileformat)

        # set ROI and header in limaserver
        #  TODO

        TrigList = [
            "INTERNAL_TRIGGER",
            "EXTERNAL_TRIGGER",
            "EXTERNAL_TRIGGER_MULTI",
            "EXTERNAL_GATE",
            "EXTERNAL_START_STOP",
        ]

        self.device.write_attribute("acq_trigger_mode", trig_mode)
        self.device.write_attribute("acq_expo_time",
                                    exp_time - self.latency_time)
        self.device.write_attribute("latency_time", self.latency_time)

        return True

    def prepare_collection(self, nb_frames, first_img_no):
        logging.getLogger("HWR").debug(
            "ALBAPilatus. preparing collection. nb_images: %s, first_no: %s" %
            (nb_frames, first_img_no))
        self.device.write_attribute("acq_nb_frames", nb_frames)
        self.device.write_attribute("saving_next_number", first_img_no)
        self.device.prepareAcq()
        return True

    def start_collection(self):
        self.start_acquisition()

    def stop_collection(self):
        self.stop_acquisition()

    def set_image_headers(self, image_headers, angle_info):

        nb_images = image_headers["nb_images"]
        angle_inc = image_headers["Angle_increment"]
        start_angle = image_headers["Start_angle"]

        startangles_list = list()
        ang_start, ang_inc, spacing = angle_info
        for i in range(nb_images):
            startangles_list.append("%0.4f deg." % (ang_start + spacing * i))

        headers = list()
        for i, sa in enumerate(startangles_list):
            header = ("_array_data.header_convention PILATUS_1.2\n"
                      "# Detector: PILATUS 6M, S/N 60-0108, Alba\n"
                      "# %s\n"
                      "# Pixel_size 172e-6 m x 172e-6 m\n"
                      "# Silicon sensor, thickness 0.000320 m\n" %
                      time.strftime("%Y/%b/%d %T"))

            # Acquisition values (headers dictionary) but overwrites start angle
            image_headers["Start_angle"] = sa
            for key, value in image_headers.items():
                if key == "nb_images":
                    continue
                header += "# %s %s\n" % (key, value)
            headers.append("%d : array_data/header_contents|%s;" % (i, header))

        self.device.write_attribute("saving_header_delimiter", ["|", ";", ":"])
        self.device.resetCommonHeader()
        self.device.resetFrameHeaders()
        self.device.setImageHeader(headers)
Exemple #2
0
class Undulator(Controller):
    def __init__(self, name, config, axes, encoders):
        Controller.__init__(self, name, config, axes, encoders)

        self.axis_info = dict()

        try:
            self.ds_name = self.config.get("ds_name")
        except:
            elog.debug("no 'ds_name' defined in config for %s" % name)

    """
    Controller initialization actions.
    """
    def initialize(self):
        # Get a proxy on Insertion Device device server of the beamline.
        self.device = DeviceProxy(self.ds_name)

    """
    Axes initialization actions.
    """
    def initialize_axis(self, axis):
        attr_pos_name = axis.config.get("attribute_position", str)
        attr_vel_name = axis.config.get("attribute_velocity", str)
        attr_acc_name = axis.config.get("attribute_acceleration", str)
        self.axis_info[axis] = {"attr_pos_name": attr_pos_name,
                                "attr_vel_name": attr_vel_name,
                                "attr_acc_name": attr_acc_name}
        elog.debug("axis initilized--------------------------")

    """
    Actions to perform at controller closing.
    """
    def finalize(self):
        pass

    def _set_attribute(self, axis, attribute_name, value):
        self.device.write_attribute(self.axis_info[axis][attribute_name], value)

    def _get_attribute(self, axis, attribute_name):
        return self.device.read_attribute(self.axis_info[axis][attribute_name]).value

    def start_one(self, motion, t0=None):
        self._set_attribute(motion.axis, "attr_pos_name",
                            float(motion.target_pos / motion.axis.steps_per_unit))

    def read_position(self, axis):
        """
        Returns the position taken from controller
        in controller unit (steps).
        """
        return self._get_attribute(axis, "attr_pos_name")

    """
    VELOCITY
    """
    def read_velocity(self, axis):
        """
        Returns the current velocity taken from controller
        in motor units.
        """
        return self._get_attribute(axis, "attr_vel_name")

    def set_velocity(self, axis, new_velocity):
        """
        <new_velocity> is in motor units
        """
        self._set_attribute(axis, "attr_vel_name", new_velocity)

    """
    ACCELERATION
    """
    def read_acceleration(self, axis):
        return self._get_attribute(axis, "attr_acc_name")

    def set_acceleration(self, axis, new_acceleration):
        self._set_attribute(axis, "attr_acc_name", new_acceleration)

    """
    STATE
    """
    def state(self, axis):
        _state = self.device.state()

        if _state == DevState.ON:
            return AxisState("READY")
        elif _state == DevState.MOVING:
            return AxisState("MOVING")
        else:
            return AxisState("READY")

    """
    Must send a command to the controller to abort the motion of given axis.
    """
    def stop(self, axis):
        self.device.abort()

    def stop_all(self, *motion_list):
        self.device.abort()

    def get_info(self, axis):
        info_str = ""
        info_str = "DEVICE SERVER : %s \n" % self.ds_name
        info_str += self.ds.state() + "\n"
        info_str += "status=\"%s\"\n" % str(self.ds.status()).strip()
        info_str += "state=%s\n" % self.ds.state()
        info_str += "mode=%s\n" % str(self.ds.mode)
        info_str += ("undu states= %s" % " ".join(map(str, self.ds.UndulatorStates)))

        return info_str
Exemple #3
0
class TangoChannel(ChannelObject):
    _tangoEventsQueue = Queue.Queue()
    _eventReceivers = {}
    _tangoEventsProcessingTimer = gevent.get_hub().loop.async()

    # start Tango events processing timer
    _tangoEventsProcessingTimer.start(processTangoEvents)

    def __init__(
        self,
        name,
        attribute_name,
        tangoname=None,
        username=None,
        polling=None,
        timeout=10000,
        **kwargs
    ):
        ChannelObject.__init__(self, name, username, **kwargs)

        self.attributeName = attribute_name
        self.deviceName = tangoname
        self.device = None
        self.value = Poller.NotInitializedValue
        self.polling = polling
        self.pollingTimer = None
        self.pollingEvents = False
        self.timeout = int(timeout)
        self.read_as_str = kwargs.get("read_as_str", False)
        self._device_initialized = gevent.event.Event()
        logging.getLogger("HWR").debug(
            "creating Tango attribute %s/%s, polling=%s, timeout=%d",
            self.deviceName,
            self.attributeName,
            polling,
            self.timeout,
        )
        self.init_device()
        self.continue_init(None)
        """
        self.init_poller = Poller.poll(self.init_device,
                                       polling_period = 3000,
                                       value_changed_callback = self.continue_init,
                                       error_callback = self.init_poll_failed,
                                       start_delay=100)
        """

    def init_poll_failed(self, e, poller_id):
        self._device_initialized.clear()
        logging.warning(
            "%s/%s (%s): could not complete init. (hint: device server is not running, or has to be restarted)",
            self.deviceName,
            self.attributeName,
            self.name(),
        )
        self.init_poller = self.init_poller.restart(3000)

    def continue_init(self, _):
        # self.init_poller.stop()

        if isinstance(self.polling, types.IntType):
            self.raw_device = RawDeviceProxy(self.deviceName)
            Poller.poll(
                self.poll,
                polling_period=self.polling,
                value_changed_callback=self.update,
                error_callback=self.pollFailed,
            )
        else:
            if self.polling == "events":
                # try to register event
                try:
                    self.pollingEvents = True
                    # logging.getLogger("HWR").debug("subscribing to CHANGE event for %s", self.attributeName)
                    self.device.subscribe_event(
                        self.attributeName,
                        PyTango.EventType.CHANGE_EVENT,
                        self,
                        [],
                        True,
                    )
                    # except PyTango.EventSystemFailed:
                    #   pass
                except BaseException:
                    logging.getLogger("HWR").exception("could not subscribe event")
        self._device_initialized.set()

    def init_device(self):
        try:
            self.device = DeviceProxy(self.deviceName)
        except PyTango.DevFailed as traceback:
            self.imported = False
            last_error = traceback[-1]
            logging.getLogger("HWR").error(
                "%s: %s", str(self.name()), last_error["desc"]
            )
        else:
            self.imported = True
            try:
                self.device.ping()
            except PyTango.ConnectionFailed:
                self.device = None
                raise ConnectionError
            else:
                self.device.set_timeout_millis(self.timeout)

                # check that the attribute exists (to avoid Abort in PyTango grrr)
                if not self.attributeName.lower() in [
                    attr.name.lower() for attr in self.device.attribute_list_query()
                ]:
                    logging.getLogger("HWR").error(
                        "no attribute %s in Tango device %s",
                        self.attributeName,
                        self.deviceName,
                    )
                    self.device = None

    def push_event(self, event):
        # logging.getLogger("HWR").debug("%s | attr_value=%s, event.errors=%s, quality=%s", self.name(), event.attr_value, event.errors,event.attr_value is None and "N/A" or event.attr_value.quality)
        if (
            event.attr_value is None
            or event.err
            or event.attr_value.quality != PyTango.AttrQuality.ATTR_VALID
        ):
            # logging.getLogger("HWR").debug("%s, receving BAD event... attr_value=%s, event.errors=%s, quality=%s", self.name(), event.attr_value, event.errors, event.attr_value is None and "N/A" or event.attr_value.quality)
            return
        else:
            pass
            # logging.getLogger("HWR").debug("%s, receiving good event", self.name())
        ev = E(event)
        TangoChannel._eventReceivers[id(ev)] = saferef.safe_ref(self.update)
        TangoChannel._tangoEventsQueue.put(ev)
        TangoChannel._tangoEventsProcessingTimer.send()

    def poll(self):
        if self.read_as_str:
            value = self.raw_device.read_attribute(
                self.attributeName, PyTango.DeviceAttribute.ExtractAs.String
            ).value
            # value = self.device.read_attribute_as_str(self.attributeName).value
        else:
            value = self.raw_device.read_attribute(self.attributeName).value

        return value

    def pollFailed(self, e, poller_id):
        self.emit("update", None)
        """
        emit_update = True
        if self.value is None:
          emit_update = False
        else:
          self.value = None

        try:
            self.init_device()
        except:
            pass

        poller = Poller.get_poller(poller_id)
        if poller is not None:
            poller.restart(1000)

        try:
          raise e
        except:
          logging.exception("%s: Exception happened while polling %s", self.name(), self.attributeName)

        if emit_update:
          # emit at the end => can raise exceptions in callbacks
          self.emit('update', None)
        """

    def getInfo(self):
        self._device_initialized.wait(timeout=3)
        return self.device.get_attribute_config(self.attributeName)

    def update(self, value=Poller.NotInitializedValue):
        if value == Poller.NotInitializedValue:
            value = self.getValue()
        if isinstance(value, types.TupleType):
            value = list(value)

        self.value = value
        self.emit("update", value)

    def getValue(self):
        self._device_initialized.wait(timeout=3)

        if self.read_as_str:
            value = self.device.read_attribute(
                self.attributeName, PyTango.DeviceAttribute.ExtractAs.String
            ).value
        else:
            value = self.device.read_attribute(self.attributeName).value

        return value

    def setValue(self, newValue):
        self.device.write_attribute(self.attributeName, newValue)
        # attr = PyTango.AttributeProxy(self.deviceName + "/" + self.attributeName)
        # a = attr.read()
        # a.value = newValue
        # attr.write(a)

    def isConnected(self):
        return self.device is not None
Exemple #4
0
class TangoChannel(ChannelObject):
    _tangoEventsQueue = queue.Queue()
    _eventReceivers = {}
    _tangoEventsProcessingTimer = gevent.get_hub().loop. async ()

    # start Tango events processing timer
    _tangoEventsProcessingTimer.start(processTangoEvents)

    def __init__(self,
                 name,
                 attribute_name,
                 tangoname=None,
                 username=None,
                 polling=None,
                 timeout=10000,
                 **kwargs):
        ChannelObject.__init__(self, name, username, **kwargs)

        self.attributeName = attribute_name
        self.deviceName = tangoname
        self.device = None
        self.value = Poller.NotInitializedValue
        self.polling = polling
        self.pollingTimer = None
        self.pollingEvents = False
        self.timeout = int(timeout)
        self.read_as_str = kwargs.get("read_as_str", False)
        self._device_initialized = gevent.event.Event()

        logging.getLogger("HWR").debug(
            "creating Tango attribute %s/%s, polling=%s, timeout=%d",
            self.deviceName, self.attributeName, polling, self.timeout)
        self.init_poller = Poller.poll(
            self.init_device,
            polling_period=3000,
            value_changed_callback=self.continue_init,
            error_callback=self.init_poll_failed,
            start_delay=100)

    def init_poll_failed(self, e, poller_id):
        self._device_initialized.clear()
        logging.warning(
            "%s/%s (%s): could not complete init. (hint: device server is not running, or has to be restarted)",
            self.deviceName, self.attributeName, self.name())
        self.init_poller = self.init_poller.restart(3000)

    def continue_init(self, _):
        self.init_poller.stop()

        if type(self.polling) == int:
            Poller.poll(self.poll,
                        polling_period=self.polling,
                        value_changed_callback=self.update,
                        error_callback=self.pollFailed)
        else:
            if self.polling == "events":
                # try to register event
                try:
                    self.pollingEvents = True
                    #logging.getLogger("HWR").debug("subscribing to CHANGE event for %s", self.attributeName)
                    self.device.subscribe_event(self.attributeName,
                                                PyTango.EventType.CHANGE_EVENT,
                                                self, [], True)
                    #except PyTango.EventSystemFailed:
                    #   pass
                except:
                    logging.getLogger("HWR").exception(
                        "could not subscribe event")
        self._device_initialized.set()

    def init_device(self):
        try:
            self.device = DeviceProxy(self.deviceName)
        except PyTango.DevFailed as traceback:
            self.imported = False
            last_error = traceback[-1]
            logging.getLogger('HWR').error("%s: %s", str(self.name()),
                                           last_error['desc'])
        else:
            self.imported = True
            try:
                self.device.ping()
            except PyTango.ConnectionFailed:
                self.device = None
                raise ConnectionError
            else:
                self.device.set_timeout_millis(self.timeout)

                # check that the attribute exists (to avoid Abort in PyTango grrr)
                if not self.attributeName.lower() in [
                        attr.name.lower()
                        for attr in self.device.attribute_list_query()
                ]:
                    logging.getLogger("HWR").error(
                        "no attribute %s in Tango device %s",
                        self.attributeName, self.deviceName)
                    self.device = None

    def push_event(self, event):
        #logging.getLogger("HWR").debug("%s | attr_value=%s, event.errors=%s, quality=%s", self.name(), event.attr_value, event.errors,event.attr_value is None and "N/A" or event.attr_value.quality)
        if event.attr_value is None or event.err or event.attr_value.quality != PyTango.AttrQuality.ATTR_VALID:
            #logging.getLogger("HWR").debug("%s, receving BAD event... attr_value=%s, event.errors=%s, quality=%s", self.name(), event.attr_value, event.errors, event.attr_value is None and "N/A" or event.attr_value.quality)
            return
        else:
            pass
            #logging.getLogger("HWR").debug("%s, receiving good event", self.name())
        ev = E(event)
        TangoChannel._eventReceivers[id(ev)] = saferef.safe_ref(self.update)
        TangoChannel._tangoEventsQueue.put(ev)
        TangoChannel._tangoEventsProcessingTimer.send()

    def poll(self):
        if self.read_as_str:
            value = self.device.read_attribute(
                self.attributeName,
                PyTango.DeviceAttribute.ExtractAs.String).value
            #value = self.device.read_attribute_as_str(self.attributeName).value
        else:
            value = self.device.read_attribute(self.attributeName).value

        return value

    def pollFailed(self, e, poller_id):
        emit_update = True
        if self.value is None:
            emit_update = False
        else:
            self.value = None

        try:
            self.init_device()
        except:
            pass

        poller = Poller.get_poller(poller_id)
        if poller is not None:
            poller.restart(1000)

        try:
            raise e
        except:
            logging.exception("%s: Exception happened while polling %s",
                              self.name(), self.attributeName)

        if emit_update:
            # emit at the end => can raise exceptions in callbacks
            self.emit('update', None)

    def getInfo(self):
        self._device_initialized.wait(timeout=3)
        return self.device.get_attribute_config(self.attributeName)

    def update(self, value=Poller.NotInitializedValue):
        if value == Poller.NotInitializedValue:
            value = self.getValue()
        if type(value) == tuple:
            value = list(value)

        self.value = value
        self.emit('update', value)

    def getValue(self):
        self._device_initialized.wait(timeout=3)

        if self.read_as_str:
            value = self.device.read_attribute(
                self.attributeName,
                PyTango.DeviceAttribute.ExtractAs.String).value
        else:
            value = self.device.read_attribute(self.attributeName).value

        return value

    def setValue(self, newValue, wait=False):
        self.device.write_attribute(self.attributeName, newValue)
        #attr = PyTango.AttributeProxy(self.deviceName + "/" + self.attributeName)
        #a = attr.read()
        #a.value = newValue
        #attr.write(a)

    def isConnected(self):
        return self.device is not None
class ALBAPilatus(AbstractDetector, HardwareObject):
    """Detector class. Contains all information about detector
       - states are 'OK', and 'BAD'
       - status is busy, exposing, ready, etc.
       - physical property is RH for pilatus, P for rayonix
    """

    def __init__(self, name):
        AbstractDetector.__init__(self)
        HardwareObject.__init__(self, name)

        self.distance_motor_hwobj = None
        self.default_distance = None
        self.default_distance_limits = None

        self.default_latency_time = 0.003

        self.exp_time_limits = None

        self.headers = {}

    def init(self):
        self.distance_motor_hwobj = self.getObjectByRole("distance_motor")
        self.devname = self.getProperty("tangoname")

        try:
            self.latency_time = float(self.getProperty("latency_time"))
        except BaseException:
            self.latency_time = None

        if self.latency_time is None:
            logging.getLogger("HWR").debug(
                "Cannot obtain latency time from Pilatus XML. Using %s"
                % self.default_latency_time
            )
            self.latency_time = self.default_latency_time

        self.devspecific = self.getProperty("device_specific")

        exp_time_limits = self.getProperty("exposure_limits")
        self.exp_time_limits = map(float, exp_time_limits.strip().split(","))

        self.device = DeviceProxy(self.devname)
        self.device_specific = DeviceProxy(self.devspecific)
        self.device.set_timeout_millis(30000)

        self.beamx_chan = self.getChannelObject("beamx")
        self.beamy_chan = self.getChannelObject("beamy")

    def start_acquisition(self):
        self.device.startAcq()

    def stop_acquisition(self):
        self.device.abortAcq()

    def get_distance(self):
        """Returns detector distance in mm"""
        if self.distance_motor_hwobj is not None:
            return float(self.distance_motor_hwobj.getPosition())
        else:
            return self.default_distance

    def move_distance(self, value):
        if self.distance_motor_hwobj is not None:
            self.distance_motor_hwobj.move(value)

    def wait_move_distance_done(self):
        self.distance_motor_hwobj.wait_end_of_move()

    def get_distance_limits(self):
        """Returns detector distance limits"""
        if self.distance_motor_hwobj is not None:
            return self.distance_motor_hwobj.getLimits()
        else:
            return self.default_distance_limits

    def get_threshold(self):
        return self.device_specific.threshold

    def get_threshold_gain(self):
        return self.device_specific.threshold_gain

    def has_shutterless(self):
        """Return True if has shutterless mode"""
        return True

    def get_beam_centre(self):
        """Returns beam center coordinates"""
        beam_x = 0
        beam_y = 0
        try:
            beam_x = self.beamx_chan.getValue()
            beam_y = self.beamy_chan.getValue()
        except BaseException:
            pass
        return beam_x, beam_y

    def get_manufacturer(self):
        return self.getProperty("manufacturer")
        return "Dectris"

    def get_model(self):
        return self.getProperty("model")

    def get_detector_type(self):
        return self.getProperty("type")

    def get_default_exposure_time(self):
        return self.getProperty("default_exposure_time")

    def get_minimum_exposure_time(self):
        return self.getProperty("minimum_exposure_time")

    def get_exposure_time_limits(self):
        """Returns exposure time limits as list with two floats"""
        return self.exp_time_limits

    def get_file_suffix(self):
        return self.getProperty("file_suffix")

    def get_pixel_size(self):
        return self.getProperty("px"), self.getProperty("py")

    # methods for data collection
    def set_energy_threshold(self):
        eugap_ch = self.getChannelObject("eugap")

        try:
            currentenergy = eugap_ch.getValue()
        except BaseException:
            currentenergy = 12.6

        det_energy = self.get_threshold()

        # threshold = det_energy  / 2.
        # limitenergy = threshold / 0.8

        if round(currentenergy, 6) < 7.538:
            currentenergy = 7.538

        kev_diff = abs(det_energy - currentenergy)

        if kev_diff > 1.2:
            logging.getLogger("HWR").debug(
                "programming energy_threshold on pilatus to: %s" % currentenergy
            )
            # if self.wait_standby():
            # self.device_specific.energy_threshold = currentenergy

    def get_latency_time(self):
        return self.latency_time

    def wait_standby(self, timeout=300):
        t0 = time.time()
        while self.device_specific.cam_state == "STANDBY":
            if time.time() - t0 > timeout:
                print("timeout waiting for Pilatus to be on STANDBY")
                return False
            time.sleep(0.1)
        return True

    def prepare_acquisition(self, dcpars):

        self.set_energy_threshold()
        # self.wait_standby()

        osc_seq = dcpars["oscillation_sequence"][0]
        file_pars = dcpars["fileinfo"]

        basedir = file_pars["directory"]
        prefix = "%s_%s_" % (file_pars["prefix"], file_pars["run_number"])

        first_img_no = osc_seq["start_image_number"]
        nb_frames = osc_seq["number_of_images"]
        exp_time = osc_seq["exposure_time"]

        fileformat = "CBF"
        trig_mode = "EXTERNAL_TRIGGER"
        # latency_time = 0.003

        logging.getLogger("HWR").debug(
            " Preparing detector (dev=%s) for data collection" % self.devname
        )

        logging.getLogger("HWR").debug("    /saving directory: %s" % basedir)
        logging.getLogger("HWR").debug("    /prefix          : %s" % prefix)
        logging.getLogger("HWR").debug("    /saving_format   : %s" % fileformat)
        logging.getLogger("HWR").debug("    /trigger_mode    : %s" % trig_mode)
        logging.getLogger("HWR").debug("    /acq_nb_frames   : %s" % nb_frames)
        logging.getLogger("HWR").debug(
            "    /acq_expo_time   : %s" % str(exp_time - self.latency_time)
        )
        logging.getLogger("HWR").debug("    /latency_time    : %s" % self.latency_time)

        self.device.write_attribute("saving_mode", "AUTO_FRAME")
        self.device.write_attribute("saving_directory", basedir)
        self.device.write_attribute("saving_prefix", prefix)
        self.device.write_attribute("saving_format", fileformat)

        # set ROI and header in limaserver
        #  TODO

        TrigList = [
            "INTERNAL_TRIGGER",
            "EXTERNAL_TRIGGER",
            "EXTERNAL_TRIGGER_MULTI",
            "EXTERNAL_GATE",
            "EXTERNAL_START_STOP",
        ]

        self.device.write_attribute("acq_trigger_mode", trig_mode)
        self.device.write_attribute("acq_expo_time", exp_time - self.latency_time)
        self.device.write_attribute("latency_time", self.latency_time)

        return True

    def prepare_collection(self, nb_frames, first_img_no):
        logging.getLogger("HWR").debug(
            "ALBAPilatus. preparing collection. nb_images: %s, first_no: %s"
            % (nb_frames, first_img_no)
        )
        self.device.write_attribute("acq_nb_frames", nb_frames)
        self.device.write_attribute("saving_next_number", first_img_no)
        self.device.prepareAcq()
        return True

    def start_collection(self):
        self.start_acquisition()

    def stop_collection(self):
        self.stop_acquisition()

    def set_image_headers(self, image_headers, angle_info):

        nb_images = image_headers["nb_images"]
        angle_inc = image_headers["Angle_increment"]
        start_angle = image_headers["Start_angle"]

        startangles_list = list()
        ang_start, ang_inc, spacing = angle_info
        for i in range(nb_images):
            startangles_list.append("%0.4f deg." % (ang_start + spacing * i))

        headers = list()
        for i, sa in enumerate(startangles_list):
            header = (
                "_array_data.header_convention PILATUS_1.2\n"
                "# Detector: PILATUS 6M, S/N 60-0108, Alba\n"
                "# %s\n"
                "# Pixel_size 172e-6 m x 172e-6 m\n"
                "# Silicon sensor, thickness 0.000320 m\n"
                % time.strftime("%Y/%b/%d %T")
            )

            # Acquisition values (headers dictionary) but overwrites start angle
            image_headers["Start_angle"] = sa
            for key, value in image_headers.iteritems():
                if key == "nb_images":
                    continue
                header += "# %s %s\n" % (key, value)
            headers.append("%d : array_data/header_contents|%s;" % (i, header))

        self.device.write_attribute("saving_header_delimiter", ["|", ";", ":"])
        self.device.resetCommonHeader()
        self.device.resetFrameHeaders()
        self.device.setImageHeader(headers)