Пример #1
0
    def __init__(self,
                 name,
                 role,
                 sn=None,
                 max_power=0.1,
                 inversed=False,
                 **kwargs):
        """
        sn (None or str): serial number.
           If None, it will pick the first device found.
        max_power (0<float): maxium power emitted in W.
        """
        model.Emitter.__init__(self, name, role, **kwargs)

        self._sn = sn
        self._max_power = max_power

        # Just find the first BlinkStick led controller
        if sn is None:
            self._bstick = blinkstick.find_first()
        else:
            # Note: doesn't work with v1.1.7:
            # need fix on get_string(), reported here: https://github.com/arvydas/blinkstick-python/pull/35
            logging.warning(
                "Using sn to select the device doesn't currently work")
            self._bstick = blinkstick.find_by_serial(sn)
        if self._bstick is None:
            raise HwError(
                "Failed to find a Blinkstick for component %s. "
                "Check that the device is connected to the computer." %
                (name, ))

        self._bstick.set_inverse(inversed)
        time.sleep(0.1)  # Device apparently needs some time to recover

        self._shape = ()
        # list of 5-tuples of floats
        self.spectra = model.ListVA([(380e-9, 390e-9, 560e-9, 730e-9, 740e-9)],
                                    unit="m",
                                    readonly=True)

        self.power = model.ListContinuous([
            0.,
        ], ((0., ), (max_power, )),
                                          unit="W",
                                          cls=(int, long, float),
                                          setter=self._setPower)
        self.power.subscribe(self._updatePower, init=True)

        self._swVersion = "Blinkstick v%s" % (blinkstick.__version__, )
        # These functions report wrong values on Linux with v1.1.7
        #         man = self._bstick.get_manufacturer()
        #         desc = self._bstick.get_description()
        #         rsn = self._bstick.get_serial()
        man = self._bstick.device.manufacturer
        desc = self._bstick.device.product
        rsn = self._bstick.device.serial_number
        self._hwVersion = "%s %s (s/n: %s)" % (man, desc, rsn)
Пример #2
0
    def __init__(self, name, role, **kwargs):
        # TODO: allow the user to indicate the power and the spectrum via args?
        # This will create the .powerSupply VA
        model.Emitter.__init__(self, name, role, **kwargs)
        self.powerSupply.value = False  # immediately turn it off

        self._shape = ()
        self.power = model.ListContinuous([0], ((0,), (10,)), unit="W", cls=(int, long, float),
                                          setter=self._setPower)
        # just one band: white
        # TODO: update spectra VA to support the actual spectra of the lamp
        self.spectra = model.ListVA([(380e-9, 390e-9, 560e-9, 730e-9, 740e-9)],
                                    unit="m", readonly=True)
Пример #3
0
    def __init__(self, name, role, max_power=10.0, spectra=None, **kwargs):
        """
        max_power (0 < float): the maximum power (in W)
        spectra (list of list of 5 tuple): output spectrum, as 5 wavelengths in m
        """
        model.Emitter.__init__(self, name, role, **kwargs)

        self._shape = ()
        self.power = model.ListContinuous([0], ((0,), (max_power,)), unit="W", cls=(int, long, float),
                                          setter=self._setPower)
        # just one band: white
        # list of 5-tuples of floats
        if spectra is None:
            spectra = [(380e-9, 390e-9, 560e-9, 730e-9, 740e-9)] # White
        if len(spectra) != 1 or len(spectra[0]) != 5:
            raise ValueError("spectra argument must be a list of list of 5 values")
        self.spectra = model.ListVA([tuple(spectra[0])], unit="m", readonly=True)
Пример #4
0
    def test_lc(self):
        """
        Test ListContinuous behavior
        """
        va = model.ListContinuous([0.1, 10, .5],
                                  ((-1.3, 9, 0), (100., 150., 1.)),
                                  cls=(int, long, float))
        self.assertEqual(va.value, [0.1, 10, .5])
        self.assertEqual(va.range, ((-1.3, 9, 0), (100., 150., 1.)))

        # must convert anything to a list
        va.value = (-1, 150, .5)
        self.assertEqual(va.value, [-1, 150, .5])

        # must not accept values outside of the range
        try:
            va.value[1] = 160.
            self.fail("Assigning value not in range should not be allowed.")
        except IndexError:
            pass  # as it should be
Пример #5
0
    def __init__(self, name, role, dependencies, **kwargs):
        """
        dependencies (dict str -> Emitter): arbitrary role -> emitter to be used as
          part of this emitter. All its provided emissions will be provided.
        """
        # TODO: allow to only use a subset of the emissions from each child

        if not dependencies:
            raise ValueError("MultiplexLight needs dependencies")

        model.Emitter.__init__(self, name, role, dependencies=dependencies, **kwargs)
        self._shape = ()

        self._child_idx = {} # Emitter -> index (shift) in the power/spectra

        spectra = []
        min_power = []
        max_power = []
        for n, child in dependencies.items():
            if not (model.hasVA(child, "power") and
                    model.hasVA(child, "spectra")
                   ):
                raise ValueError("Child %s is not a light emitter" % (n,))
            self._child_idx[child] = len(spectra)
            spectra.extend(child.spectra.value)
            min_power.extend(child.power.range[0])
            max_power.extend(child.power.range[1])
            # Subscribe to each child power to update self.power
            child.power.subscribe(self._updateMultiplexPower)

        # Child with the maximum power range
        self.power = model.ListContinuous(value=[0] * len(spectra),
                                          range=(tuple(min_power), tuple(max_power)),
                                          unit="W", cls=(int, long, float))
        self.power.subscribe(self._setChildPower)
        self._updateMultiplexPower(None)
        # info on which source is which wavelength
        self.spectra = model.ListVA(spectra, unit="m", readonly=True)
Пример #6
0
    def __init__(self, name, role, device, channels, spectra, pwr_curve,
                 **kwargs):
        """
        device (string): name of the /dev comedi  device (ex: "/dev/comedi0")
        channels (list of (0<=int)): The output channel for each source, as
          numbered in the comedi subdevice.
        spectra (list of 5-tuple of float): the spectra for each output channel used.
         Each tuple represents the wavelength in m for the 99% low, 25% low,
         centre/max, 25% high, 99% high. They do no have to be extremely precise.
         The most important is the centre, and that they are all increasing values.
        pwr_curve (list of dict (float -> 0<float)): Power curve segment map for
           each source. A segment map is a  series of voltage output on the
           analog output -> emission power of the light (W).
           It represents a series of linear segments to map the voltage output
           to the light emission. At least one pair should be provided.
           If no voltage is linked to 0W, then a 0V -> 0W mapping is used.
           The total curve should be monotonic.
        """
        # TODO: allow to give the unit of the power/pwr_curve ?

        model.Emitter.__init__(self, name, role, **kwargs)
        self._shape = ()

        try:
            self._device = comedi.open(device)
        #             self._fileno = comedi.fileno(self._device)
        except comedi.ComediError:
            raise ValueError("Failed to open DAQ device '%s'" % device)

        # Look for the analog output subdevice
        try:
            self._ao_subd = comedi.find_subdevice_by_type(
                self._device, comedi.SUBD_AO, 0)
            nchan = comedi.get_n_channels(self._device, self._ao_subd)
            if nchan < max(channels):
                raise ValueError(
                    "Device only has %d channels, while needed %d" %
                    (nchan, max(channels)))
        except comedi.ComediError:
            raise ValueError(
                "Failed to find an analogue output on DAQ device '%s'" %
                device)

        if len(channels) != len(spectra):
            raise ValueError(
                "spectra argument should have the same length as channels (%d)"
                % len(channels))
        if len(channels) != len(pwr_curve):
            raise ValueError(
                "pwr_curve argument should have the same length as channels (%d)"
                % len(channels))

        self._channels = channels

        # Check and store the power curves
        self._ranges = []
        self._pwr_curve = []
        for c, crv in zip(channels, pwr_curve):
            crv = [v for v in crv.items()]
            # Add 0W = 0V if nothing = 0W
            if 0 not in [w for v, w in crv]:
                crv.append((0, 0))
                logging.info(
                    "Adding 0V -> 0W mapping to pwr_curve for channel %d", c)
            # At least beginning and end values
            if len(crv) < 2:
                raise ValueError(
                    "pwr_curve for channel %d has less than 2 values: %s" %
                    (c, crv))
            # Check it's monotonic
            crv = sorted(crv, key=lambda v: v[0])
            if crv[0][1] < 0:
                raise ValueError(
                    "pwr_curve for channel %d has negative power: %g W" %
                    (c, crv[0][1]))
            if len(crv) != len(set(v for v, w in crv)):
                raise ValueError(
                    "pwr_curve for channel %d has identical voltages: %s" %
                    (c, crv))
            if not all(
                (crv[i][1] < crv[i + 1][1]) for i in range(len(crv) - 1)):
                raise ValueError(
                    "pwr_curve for channel %d is not monotonic: %s" % (c, crv))

            self._pwr_curve.append(crv)

            # Find the best range to use
            try:
                ri = comedi.find_range(self._device, self._ao_subd, c,
                                       comedi.UNIT_volt, crv[0][0], crv[-1][0])
            except comedi.ComediError:
                raise ValueError(
                    "Data range between %g and %g V is too high for hardware."
                    % (crv[0][0], crv[-1][0]))
            self._ranges.append(ri)

        # Check the spectra
        spect = []  # list of the 5 wavelength points
        for c, wls in zip(channels, spectra):
            if len(wls) != 5:
                raise ValueError(
                    "Spectra for channel %d doesn't have exactly 5 wavelength points: %s"
                    % (c, wls))
            if list(wls) != sorted(wls):
                raise ValueError(
                    "Spectra for channel %d has unsorted wavelengths: %s" %
                    (c, wls))
            for wl in wls:
                if not 0 < wl < 100e-6:
                    raise ValueError(
                        "Spectra for channel %d has unexpected wavelength = %f nm"
                        % (c, wl * 1e9))
            spect.append(tuple(wls))

        # Maximum power for channel to be used as a range for power
        max_power = tuple([crv[-1][1] for crv in self._pwr_curve])
        # Power value for each channel of the device
        self.power = model.ListContinuous(
            value=[0.] * len(self._channels),
            range=(
                tuple([0.] * len(self._channels)),
                max_power,
            ),
            unit="W",
            cls=(int, long, float),
        )
        self.power.subscribe(self._updatePower)

        # info on which channel is which wavelength
        self.spectra = model.ListVA(spect, unit="m", readonly=True)

        # make sure everything is off (turning on the HUB will turn on the lights)
        self.power.value = self.power.range[0]

        self._metadata = {model.MD_HW_NAME: self.getHwName()}
        lnx_ver = driver.get_linux_version()
        self._swVersion = "%s (driver %s, linux %s)" % (
            odemis.__version__, self.getSwVersion(), ".".join(
                "%s" % v for v in lnx_ver))
        self._metadata[model.MD_SW_VERSION] = self._swVersion
        self._metadata[model.MD_HW_VERSION] = self._hwVersion  # unknown
Пример #7
0
    def __init__(self, name, role, port, sources, _serial=None, **kwargs):
        """
        port (string): name of the serial port to connect to. Can be a pattern,
         in which case, all the ports fitting the pattern will be tried, and the
         first one which looks like an LLE will be used.
        sources (dict string -> 5-tuple of float): the light sources (by colour).
         The string is one of the seven names for the sources: "red", "cyan",
         "green", "UV", "yellow", "blue", "teal". They correspond to fix
         number in the LLE (cf documentation). The tuple contains the wavelength
         in m for the 99% low, 25% low, centre/max, 25% high, 99% high. They do
         no have to be extremely precise. The most important is the centre, and
         that they are all increasing values. If the device doesn't have the
         source it can be skipped.
        _serial (serial): for internal use only, directly use a opened serial
         connection
        """
        # start with this opening the port: if it fails, we are done
        if _serial is not None:
            self._try_recover = False
            self._serial = _serial
            self._port = ""
        else:
            self._serial, self._port = self._findDevice(port)
            logging.info("Found LLE device on port %s", self._port)
            self._try_recover = True

        # to acquire before sending anything on the serial port
        self._ser_access = threading.Lock()

        # Init the LLE
        self._initDevice()

        if _serial is not None:  # used for port testing => only simple init
            return

        # parse source and do some sanity check
        if not sources or not isinstance(sources, dict):
            logging.error(
                "sources argument must be a dict of source name -> wavelength 5 points"
            )
            raise ValueError("Incorrect sources argument")

        self._source_id = []  # source number for each spectra
        self._gy = []  # indexes of green and yellow source
        self._rcubt = []  # indexes of other sources
        spectra = []  # list of the 5 wavelength points
        self._max_power = []
        for cn, wls in sources.items():
            cn = cn.lower()
            if cn not in COLOUR_TO_SOURCE:
                raise ValueError(
                    "Sources argument contains unknown colour '%s'" % cn)
            if len(wls) != 5:
                raise ValueError(
                    "Sources colour '%s' doesn't have exactly 5 wavelength points"
                    % cn)
            prev_wl = 0
            for wl in wls:
                if 0 > wl or wl > 100e-6:
                    raise ValueError(
                        "Sources colour '%s' has unexpected wavelength = %f nm"
                        % (cn, wl * 1e9))
                if prev_wl > wl:
                    raise ValueError(
                        "Sources colour '%s' has unsorted wavelengths" % cn)
            self._source_id.append(COLOUR_TO_SOURCE[cn])
            if cn in ["green", "yellow"]:
                self._gy.append(len(spectra))
            else:
                self._rcubt.append(len(spectra))
            self._max_power.append(DEFAULT_SOURCES_POWERS[cn])
            spectra.append(tuple(wls))

        model.Emitter.__init__(self, name, role, **kwargs)

        self._shape = ()
        self.power = model.ListContinuous(
            value=[0.0] * len(spectra),
            range=(
                (0., ) * len(spectra),
                tuple(self._max_power),
            ),
            unit="W",
            cls=(int, long, float),
        )

        self.spectra = model.ListVA(spectra, unit="m", readonly=True)

        self._prev_power = [None] * len(spectra)  # => will update for sure
        self._updateIntensities()  # turn off every source

        self.power.subscribe(self._updatePower)
        # set HW and SW version
        self._swVersion = "%s (serial driver: %s)" % (
            odemis.__version__, driver.getSerialDriver(self._port))
        self._hwVersion = "Lumencor Light Engine"  # hardware doesn't report any version

        # Update temperature every 10s
        current_temp = self.GetTemperature()
        self.temperature = model.FloatVA(current_temp,
                                         unit=u"°C",
                                         readonly=True)
        self._temp_timer = util.RepeatingTimer(10, self._updateTemperature,
                                               "LLE temperature update")
        self._temp_timer.start()