Exemplo n.º 1
0
 def imageSite(self, siteId, cycleNum, experimentStart):
     events.publish(events.UPDATE_STATUS_LIGHT, 'device waiting',
                    'Waiting for stage motion')
     cockpit.interfaces.stageMover.waitForStop()
     cockpit.interfaces.stageMover.goToSite(siteId, shouldBlock = True)
     self.waitFor(float(self.delayBeforeImaging.GetValue()))
     if self.shouldAbort:
         return
     # Try casting the site ID to an int, which it probably is, so that
     # we can use %03d (fills with zeros) instead of %s (variable width,
     # so screws with sorting).
     try:
         siteId = '%03d' % int(siteId)
     except ValueError:
         # Not actually an int.
         pass
     self.experimentPanel.filepath_panel.UpdateFilename({
         "date": time.strftime('%Y%m%d', experimentStart),
         "time": time.strftime('%H%M', experimentStart),
         "cycle": ("%03d" % cycleNum),
         "site": siteId,
     })
     start = time.time()
     events.executeAndWaitFor(events.EXPERIMENT_COMPLETE,
             self.experimentPanel.runExperiment)
     print(f"Imaging took {(time.time() - start):.2f} seconds")
Exemplo n.º 2
0
    def executeTable(self, table, startIndex, stopIndex, numReps, repDuration):

        actions = actions_from_table(table, startIndex, stopIndex, repDuration)

        events.publish(events.UPDATE_STATUS_LIGHT, 'device waiting',
                       'Waiting for DSP to finish')
        self.connection.PrepareActions(actions, numReps)
        events.executeAndWaitFor(events.EXECUTOR_DONE % self.name, self.connection.RunActions)
        events.publish(events.EXPERIMENT_EXECUTION)
        return
Exemplo n.º 3
0
    def snapImage(self):
        #check that we have a camera and light source
        cams = 0
        lights = 0
        cams = len(depot.getActiveCameras())
        for light in depot.getHandlersOfType(depot.LIGHT_TOGGLE):
            if light.getIsEnabled():
                lights = lights + 1
        if (cams is 0) or (lights is 0):
            print("Snap needs a light and a camera to opperate")
            return

        #take the image
        events.executeAndWaitFor(
            events.NEW_IMAGE %
            (list(cockpit.interfaces.imager.imager.activeCameras)[0].name),
            cockpit.interfaces.imager.imager.takeImage,
            shouldStopVideo=False)
        mosaic.transferCameraImage()
        self.Refresh()
Exemplo n.º 4
0
 def imageSite(self, siteId, cycleNum, experimentStart):
     events.publish('update status light', 'device waiting',
                    'Waiting for stage motion')
     cockpit.interfaces.stageMover.waitForStop()
     cockpit.interfaces.stageMover.goToSite(siteId, shouldBlock=True)
     self.waitFor(float(self.delayBeforeImaging.GetValue()))
     if self.shouldAbort:
         return
     # Try casting the site ID to an int, which it probably is, so that
     # we can use %03d (fills with zeros) instead of %s (variable width,
     # so screws with sorting).
     try:
         siteId = '%03d' % int(siteId)
     except ValueError:
         # Not actually an int.
         pass
     filename = "%s_t%03d_p%s_%s" % (time.strftime(
         '%Y%m%d-%H%M',
         experimentStart), cycleNum, siteId, self.fileBase.GetValue())
     self.experimentPanel.setFilename(filename)
     start = time.time()
     events.executeAndWaitFor('experiment complete',
                              self.experimentPanel.runExperiment)
     print("Imaging took %.2fs" % (time.time() - start))
Exemplo n.º 5
0
    def videoMode(self):
        if not self.activeCameras:
            # No cameras, no video mode.
            events.publish(cockpit.events.VIDEO_MODE_TOGGLE, False)
            return
        if self.amInVideoMode:
            # Just cancel the current video mode.
            events.publish(cockpit.events.VIDEO_MODE_TOGGLE, False)
            self.stopVideo()
            return

        events.publish(cockpit.events.VIDEO_MODE_TOGGLE, True)
        self.shouldStopVideoMode = False
        self.amInVideoMode = True
        while not self.shouldStopVideoMode:
            if not self.activeLights:
                break
            start = time.time()
            try:
                # HACK: only wait for one camera.
                events.executeAndWaitFor("new image %s" %
                                         (list(self.activeCameras)[0].name),
                                         self.takeImage,
                                         shouldBlock=True,
                                         shouldStopVideo=False)
            except Exception as e:
                print("Video mode failed:", e)
                events.publish(cockpit.events.VIDEO_MODE_TOGGLE, False)
                traceback.print_exc()
                break
        self.amInVideoMode = False
        events.publish(cockpit.events.VIDEO_MODE_TOGGLE, False)
        # Our thread could be blocked waiting for an image.
        # Clear one shot new image subscribers to make sure it
        # is unblocked.
        events.clearOneShotSubscribers(pattern="new image")
Exemplo n.º 6
0
    def execute(self):
        cockpit.util.logger.log.info("Experiment.execute started.")
        # Iteratively find the ExperimentExecutor that can tackle the largest
        # portion of self.table, have them run it, and wait for them to finish.
        executors = depot.getHandlersOfType(depot.EXECUTOR)
        self.shouldAbort = False
        for rep in range(self.numReps):
            startTime = time.time()
            repDuration = None
            curIndex = 0
            shouldStop = False
            # Need to track delay introduced by dropping back to software timing.
            delay = 0.
            while curIndex < len(self.table):
                if curIndex > 0:
                    # Update the delay
                    nextTime = delay + startTime + float(
                        self.table[curIndex][0]) / 1000.
                    delay += max(0, time.time() - nextTime)

                if self.shouldAbort:
                    cockpit.util.logger.log.error(
                        "Cancelling on rep %d after %d actions due to user abort"
                        % (rep, curIndex))
                    break
                best = None
                bestLen = 0
                for executor in executors:
                    numLines = executor.getNumRunnableLines(
                        self.table, curIndex)
                    if best is None or numLines > bestLen:
                        best = executor
                        bestLen = numLines
                numReps = 1
                if bestLen == len(self.table):
                    # This executor can handle the entire experiment, so we
                    # should tell them to handle the repeats as well.
                    numReps = self.numReps
                    shouldStop = True
                    # Expand from seconds to milliseconds
                    repDuration = self.repDuration * 1000

                if bestLen == 0:
                    # No executor can run this line. See if we can fall back to software.
                    fn = None
                    t, h, action = self.table[curIndex]
                    if h.deviceType == depot.CAMERA and 'softTrigger' in h.callbacks:
                        fn = lambda: h.callbacks['softTrigger']()
                    elif h.deviceType == depot.STAGE_POSITIONER:
                        fn = lambda: h.moveAbsolute(action)

                    if fn is None:
                        raise RuntimeError(
                            "Found a line that no executor could handle: %s" %
                            str(self.table.actions[curIndex]))
                    # Wait until this action is due.
                    if curIndex > 0:
                        timeToNext = delay + startTime + float(
                            self.table[curIndex][0]) / 1000. - time.time()
                        time.sleep(max(0, timeToNext))
                    fn()
                    curIndex += 1
                else:
                    # Don't resume execution too early.
                    # TODO: would be better to pass a 'do not start before' argument
                    # to the handler, so any work it has to do does not add further
                    # delays.
                    if curIndex > 0:
                        timeToNext = delay + startTime + float(
                            self.table[curIndex][0]) / 1000. - time.time()
                        time.sleep(max(0, timeToNext))

                    events.executeAndWaitFor('experiment execution',
                                             best.executeTable, self.table,
                                             curIndex, curIndex + bestLen,
                                             numReps, repDuration)
                    curIndex += bestLen

            if shouldStop:
                # All reps handled by an executor.
                cockpit.util.logger.log.debug("Stopping now at %.2f" %
                                              time.time())
                break
            # Wait for the end of the rep.
            if rep != self.numReps - 1:
                waitTime = self.repDuration - (time.time() - startTime)
                time.sleep(max(0, waitTime))
        ## TODO: figure out how long we should wait for the last captures to complete.
        # For now, wait 1s.
        time.sleep(1.)
        cockpit.util.logger.log.info("Experiment.execute completed.")
        return True
Exemplo n.º 7
0
    def executeTable(self, table, startIndex, stopIndex, numReps, repDuration):
        # Take time and arguments (i.e. omit handler) from table to generate actions.
        # For the UCSF m6x DSP device, we also need to:
        #  - make the analogue values offsets from the current position;
        #  - convert float in ms to integer clock ticks and ensure digital
        #    lines are not changed twice on the same tick;
        #  - separate analogue and digital events into different lists;
        #  - generate a structure that describes the profile.

        actions = actions_from_table(table, startIndex, stopIndex, repDuration)

        # Profiles
        analogs = [
            [], [], [], []
        ]  # A list of lists (one per channel) of tuples (ticks, (analog values))
        digitals = []  # A list of tuples (ticks, digital state)
        # Need to track time of last analog events to workaround a
        # DSP bug later. Also used to detect when events exceed timing
        # resolution
        tLastA = None

        # The DSP executes an analogue movement profile, which is defined using
        # offsets relative to a baseline at the time the profile was initialized.
        # These offsets are encoded as unsigned integers, so at profile
        # intialization, each analogue channel must be at or below the lowest
        # value it needs to reach in the profile.
        lowestAnalogs = list(np.amin([x[1][1] for x in actions], axis=0))
        for line, lowest in enumerate(lowestAnalogs):
            if lowest < self._lastAnalogs[line]:
                self._lastAnalogs[line] = lowest
                self.setAnalog(line, lowest)

        for (t, (darg, aargs)) in actions:
            # Convert t to ticks as int while rounding up. The rounding is
            # necessary, otherwise e.g. 10.1 and 10.1999999... both result in 101.
            ticks = int(float(t) * self.tickrate + 0.5)

            # Digital actions - one at every time point.
            if len(digitals) == 0:
                digitals.append((ticks, darg))
            elif ticks == digitals[-1][0]:
                # Used to check for conflicts here, but that's not so trivial.
                # We need to allow several bits to change at the same time point, but
                # they may show up as multiple events in the actionTable. For now, just
                # take the most recent state.
                if darg != digitals[-1][1]:
                    digitals[-1] = (ticks, darg)
                else:
                    pass
            else:
                digitals.append((ticks, darg))

            # Analogue actions - only enter into profile on change.
            # DSP uses offsets from value when the profile was loaded.
            offsets = map(lambda base, new: new - base, self._lastAnalogs,
                          aargs)
            for offset, a in zip(offsets, analogs):
                if ((len(a) == 0) or (len(a) > 0 and offset != a[-1][1])):
                    a.append((ticks, offset))
                    tLastA = t

        # Work around some DSP bugs:
        # * The action table needs at least two events to execute correctly.
        # * Last action must be digital --- if the last analog action is at the same
        #   time or after the last digital action, it will not be performed.
        # Both can be avoided by adding a digital action that does nothing.
        if len(digitals) == 1 or tLastA >= digitals[-1][0]:
            # Just duplicate the last digital action, one tick later.
            digitals.append((digitals[-1][0] + 1, digitals[-1][1]))

        # Update records of last positions.
        self._lastDigital = digitals[-1][1]
        self._lastAnalogs = list(
            map(lambda x, y: x - (y[-1:][1:] or 0), self._lastAnalogs,
                analogs))

        events.publish(events.UPDATE_STATUS_LIGHT, 'device waiting',
                       'Waiting for DSP to finish')
        # Convert digitals to array of uints.
        digitalsArr = np.array(digitals, dtype=np.uint32).reshape(-1, 2)
        # Convert analogs to array of uints.
        analogsArr = [
            np.array(a, dtype=np.uint32).reshape(-1, 2) for a in analogs
        ]

        # Create a description dict. Will be byte-packed by server-side code.
        maxticks = max(
            chain([d[0] for d in digitals],
                  [a[0] for a in chain.from_iterable(analogs)]))
        description = {}
        description['count'] = maxticks
        description['clock'] = 1000. / float(self.tickrate)
        description['InitDio'] = self._lastDigital
        description['nDigital'] = len(digitals)
        description['nAnalog'] = [len(a) for a in analogs]

        self._lastProfile = (description, digitalsArr, analogsArr)

        self.connection.profileSet(description, digitalsArr, *analogsArr)
        self.connection.DownloadProfile()
        self.connection.InitProfile(numReps)
        events.executeAndWaitFor(events.EXECUTOR_DONE % self.name,
                                 self.connection.trigCollect)
        events.publish(events.EXPERIMENT_EXECUTION)
Exemplo n.º 8
0
    def executeRep(self, repNum):
        # Get all light sources that the microscope has.
        allLights = depot.getHandlersOfType(depot.LIGHT_TOGGLE)
        # getHandlersOfType returns an unordered set datatype. If we want to
        # index into allLights, we need to convert it to a list first.
        allLights = list(allLights)
        # Print the names of all light sources.
        for light in allLights:
            print(light.name)
        # Get all power controls for light sources.
        allLightPowers = depot.getHandlersOfType(depot.LIGHT_POWER)

        # Get all camera handlers that the microscope has, and filter it
        # down to the ones that are currently active.
        allCameras = depot.getHandlersOfType(depot.CAMERA)
        # Create a new empty list.
        activeCams = []
        for camera in allCameras:
            if camera.getIsEnabled():
                # Camera is enabled.
                activeCams.append(camera)

        # Get a specific light.
        led650 = depot.getHandlerWithName("650 LED")

        # Get a specific light's power control (ditto).
        led650power = depot.getHandlerWithName("650 LED power")

        # Set the output power to use for this light source, when it is active.
        led650power.setPower(2.5)

        # Set this light source to be continually exposing.
        led650.setExposing(True)

        # Wait for some time (1.5 seconds in this case).
        time.sleep(1.5)

        # Set this light source to stop continually exposing.
        led650.setExposing(False)

        # Get another light source.
        laser488 = depot.getHandlerWithName("488 L")

        # Set this light source to be enabled when we take images.
        # Note: for lasers, an AOM in the laser box that acts as a light
        # shutter is automatically adjusted when you enable/disable lights.
        # I don't know how well enabling multiple lasers simultaneously works.
        # Note: lasers, the DIA light source, and the EPI light source, are
        # mutually exclusive as they use different shutters and only one
        # shutter can be active at a time for some unknown reason.
        laser488.setEnabled(True)

        # Take images, using all current active camera views and light
        # sources; wait for the image (and time of acquisition) from the named
        # camera to be available.
        # Note: The light sources selected automatically use the emission
        # filter you have set in the UI. If multiple lights use the same
        # emission filter, then they will expose simultaneously (if possible).
        # Note: that if you try to wait for an image
        # that will never arrive (e.g. for the wrong camera name) then your
        # script will get stuck at this point.
        # Note: you must have at least one light source enabled for any
        # image to be taken!
        eventName = 'new image %s' % activeCams[0].name
        image, timestamp = events.executeAndWaitFor(
            eventName, cockpit.interfaces.imager.takeImage, shouldBlock=True)

        # Get the min, max, median, and standard deviation of the image
        imageMin = image.min()
        imageMax = image.max()
        imageMedian = numpy.median(image)
        imageStd = numpy.std(image)

        print("Image stats:", imageMin, imageMax, imageMedian, imageStd)

        # Some miscellaneous functions below.

        # Get the current stage position; positions are in microns.
        curX, curY, curZ = cockpit.interfaces.stageMover.getPosition()
        # Move to a new Z position, and wait until we arrive.
        cockpit.interfaces.stageMover.goToZ(curZ + 5, shouldBlock=True)
        # Move to a new XY position.
        # Note: the goToXY function expects a "tuple" for the position,
        # hence the extra parentheses (i.e. "goToXY(x, y)" is invalid;
        # "goToXY((x, y))" is correct).
        cockpit.interfaces.stageMover.goToXY((curX + 50, curY - 50),
                                             shouldBlock=True)

        # Get the device responsible for the dichroics and light sources
        lightsDevice = depot.getDevice(cockpit.devices.lights)
        # Set a new filter/dichroic for the lower turret.
        lightsDevice.setFilter(isFirstFilter=True, label="2-488 L")
        # Set a new filter/dichroic for the upper turret.
        lightsDevice.setFilter(isFirstFilter=False, label="6-600bp")
Exemplo n.º 9
0
    def executeRep(self, repNum):
        # Get all light sources that the microscope has.
        allLights = depot.getHandlersOfType(depot.LIGHT_TOGGLE)
        # getHandlersOfType returns an unordered set datatype. If we want to
        # index into allLights, we need to convert it to a list first.
        allLights = list(allLights)
        # Print the names of all light sources.
        for light in allLights:
            print(light.name)
        # Get all power controls for light sources.
        allLightPowers = depot.getHandlersOfType(depot.LIGHT_POWER)
        # Get all light source filters.
        allLightFilters = depot.getHandlersOfType(depot.LIGHT_FILTER)

        # Get all camera handlers that the microscope has, and filter it
        # down to the ones that are currently active.
        allCameras = depot.getHandlersOfType(depot.CAMERA)
        # Create a new empty list.
        activeCams = []
        for camera in allCameras:
            if camera.getIsEnabled():
                # Camera is enabled.
                activeCams.append(camera)

        # Get a specific light.
        deepstar405 = depot.getHandlerWithName("488 Deepstar")

        deepstar405power = depot.getHandlerWithName("488 Deepstar power")

        # Set the output power to use for this light source, when it is active.
        deepstar405power.setPower(15)

        # Get another light source. The "\n" in the name is a newline, which
        # was inserted (when this light source handler was created) to make
        # the light control button look nice.
        laser488 = depot.getHandlerWithName("488\nlight")

        # Set this light source to be enabled when we take images.
        laser488.setEnabled(True)

        # Take images, using all current active camera views and light
        # sources; wait for the image (and time of acquisition) from the named
        # camera to be available.
        # Note: that if you try to wait for an image
        # that will never arrive (e.g. for the wrong camera name) then your
        # script will get stuck at this point.
        eventName = 'new image %s' % activeCams[0].name
        image, timestamp = events.executeAndWaitFor(
            eventName, cockpit.interfaces.imager.takeImage, shouldBlock=True)

        # Get the min, max, median, and standard deviation of the image
        imageMin = image.min()
        imageMax = image.max()
        imageMedian = numpy.median(image)
        imageStd = numpy.std(image)

        print("Image stats:", imageMin, imageMax, imageMedian, imageStd)

        # Some miscellaneous functions below.

        # Get the current stage position; positions are in microns.
        curX, curY, curZ = cockpit.interfaces.stageMover.getPosition()
        # Move to a new Z position, and wait until we arrive.
        cockpit.interfaces.stageMover.goToZ(curZ + 5, shouldBlock=True)
        # Move to a new XY position.
        # Note: the goToXY function expects a "tuple" for the position,
        # hence the extra parentheses (i.e. "goToXY(x, y)" is invalid;
        # "goToXY((x, y))" is correct).
        cockpit.interfaces.stageMover.goToXY((curX + 50, curY - 50),
                                             shouldBlock=True)