def test_acq_fine_align(self): """ try acquisition with SEM + Optical + overlay streams """ # Create the streams sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) # SEM settings are via the current hardware settings self.ebeam.dwellTime.value = self.ebeam.dwellTime.range[0] fs1 = stream.FluoStream("test orange", self.ccd, self.ccd.data, self.light, self.light_filter) fs1.excitation.value = fs1.excitation.range[0] + 5e-9 fs1.emission.value = fs1.emission.range[0] + 5e-9 fs2 = stream.FluoStream("test blue", self.ccd, self.ccd.data, self.light, self.light_filter) fs2.excitation.value = fs2.excitation.range[1] - 5e-9 fs2.emission.value = fs2.emission.range[1] - 5e-9 self.ccd.exposureTime.value = 0.1 # s ovrl = stream.OverlayStream("overlay", self.ccd, self.ebeam, self.sed) ovrl.dwellTime.value = 0.3 ovrl.repetition.value = (7, 7) streams = [sems, fs1, fs2, ovrl] est_time = acq.estimateTime(streams) sum_est_time = sum(s.estimateAcquisitionTime() for s in streams) self.assertGreaterEqual(est_time, sum_est_time) # prepare callbacks self.past = None self.left = None self.updates = 0 self.done = 0 # Run acquisition start = time.time() f = acq.acquire(streams) f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) data = f.result() dur = time.time() - start self.assertGreater(dur, est_time / 2) # Estimated time shouldn't be too small self.assertIsInstance(data[0], model.DataArray) self.assertEqual(len(data), len(streams) - 1) # No overlay correction metadata anywhere (it has all been merged) for d in data: for k in [model.MD_ROTATION_COR, model.MD_PIXEL_SIZE_COR, model.MD_POS_COR]: self.assertNotIn(k, d.metadata) # thumb = acq.computeThumbnail(st, f) # self.assertIsInstance(thumb, model.DataArray) self.assertGreaterEqual(self.updates, 1) # at least one update at end self.assertEqual(self.left, 0) self.assertEqual(self.done, 1) self.assertTrue(not f.cancelled())
def _create_overlay_stream(self, streams): for s in streams: if isinstance(s, EMStream): em_det = s.detector em_emt = s.emitter elif isinstance(s, stream.OpticalStream) and not isinstance( s, stream.ScannedFluoStream): opt_det = s.detector main_data = self.main_app.main_data st = stream.OverlayStream("Fine alignment", opt_det, em_emt, em_det, opm=main_data.opm) st.dwellTime.value = main_data.fineAlignDwellTime.value return st
def test_overlay_stream(self): # Create the stream ovrl = stream.OverlayStream("test overlay", self.ccd, self.ebeam, self.sed) ovrl.dwellTime.value = 0.3 ovrl.repetition.value = (7, 7) f = ovrl.acquire() das = f.result() cor_md = das[0].metadata for k in [model.MD_ROTATION_COR, model.MD_PIXEL_SIZE_COR, model.MD_POS_COR]: self.assertIn(k, cor_md) # Try to cancel f = ovrl.acquire() time.sleep(1) f.cancel() self.assertTrue(f.cancelled())
def __init__(self, parent, orig_tab_data): xrcfr_acq.__init__(self, parent) self.conf = get_acqui_conf() for n in presets: self.cmb_presets.Append(n) # TODO: record and reuse the preset used? self.cmb_presets.Select(0) self.filename = model.StringVA(create_filename(self.conf.last_path, self.conf.fn_ptn, self.conf.last_extension, self.conf.fn_count)) self.filename.subscribe(self._onFilename, init=True) # The name of the last file that got written to disk (used for auto viewing on close) self.last_saved_file = None # True when acquisition occurs self.acquiring = False # a ProgressiveFuture if the acquisition is going on self.acq_future = None self._acq_future_connector = None self._main_data_model = orig_tab_data.main # duplicate the interface, but with only one view self._tab_data_model = self.duplicate_tab_data_model(orig_tab_data) # Create a new settings controller for the acquisition dialog self._settings_controller = SecomSettingsController( self, self._tab_data_model, highlight_change=True # also adds a "Reset" context menu ) orig_view = orig_tab_data.focussedView.value self._view = self._tab_data_model.focussedView.value self._hidden_view = StreamView("Plugin View Hidden") self.streambar_controller = StreamBarController(self._tab_data_model, self.pnl_secom_streams, static=True, ignore_view=True) # The streams currently displayed are the one visible self.add_all_streams() # The list of streams ready for acquisition (just used as a cache) self._acq_streams = {} # FIXME: pass the fold_panels # Compute the preset values for each preset self._preset_values = {} # dict string -> dict (SettingEntries -> value) self._orig_entries = get_global_settings_entries(self._settings_controller) for sc in self.streambar_controller.stream_controllers: self._orig_entries += get_local_settings_entries(sc) self._orig_settings = preset_as_is(self._orig_entries) # to detect changes for n, preset in presets.items(): self._preset_values[n] = preset(self._orig_entries) # Presets which have been confirmed on the hardware self._presets_confirmed = set() # (string) self.start_listening_to_va() # If it could be possible to do fine alignment, allow the user to choose if self._can_fine_align(self._tab_data_model.streams.value): self.chkbox_fine_align.Show() # Set to True to make it the default, but will be automatically # disabled later if the current visible streams don't allow it. self.chkbox_fine_align.Value = True for s in self._tab_data_model.streams.value: if isinstance(s, EMStream): em_det = s.detector em_emt = s.emitter elif isinstance(s, OpticalStream) and not isinstance(s, ScannedFluoStream): opt_det = s.detector self._ovrl_stream = stream.OverlayStream("Fine alignment", opt_det, em_emt, em_det, opm=self._main_data_model.opm) self._ovrl_stream.dwellTime.value = self._main_data_model.fineAlignDwellTime.value else: self.chkbox_fine_align.Show(False) self.chkbox_fine_align.Value = False self._prev_fine_align = self.chkbox_fine_align.Value # make sure the view displays the same thing as the one we are # duplicating self._view.view_pos.value = orig_view.view_pos.value self._view.mpp.value = orig_view.mpp.value self._view.merge_ratio.value = orig_view.merge_ratio.value # attach the view to the viewport self.pnl_view_acq.canvas.fit_view_to_next_image = False self.pnl_view_acq.setView(self._view, self._tab_data_model) # The TOOL_ROA is not present because we don't allow the user to change # the ROA), so we need to explicitly request the canvas to show the ROA. if hasattr(self._tab_data_model, "roa") and self._tab_data_model.roa is not None: cnvs = self.pnl_view_acq.canvas self.roa_overlay = RepetitionSelectOverlay(cnvs, self._tab_data_model.roa, self._tab_data_model.fovComp) cnvs.add_world_overlay(self.roa_overlay) self.Bind(wx.EVT_CHAR_HOOK, self.on_key) self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_close) self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file) self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire) self.cmb_presets.Bind(wx.EVT_COMBOBOX, self.on_preset) self.Bind(wx.EVT_CLOSE, self.on_close) # on_streams_changed is compatible because it doesn't use the args self.chkbox_fine_align.Bind(wx.EVT_CHECKBOX, self.on_streams_changed) self.on_preset(None) # will force setting the current preset # To update the estimated time when streams are removed/added self._view.stream_tree.flat.subscribe(self.on_streams_changed) self._hidden_view.stream_tree.flat.subscribe(self.on_streams_changed)
def __init__(self, parent, orig_tab_data): xrcfr_acq.__init__(self, parent) self.conf = get_acqui_conf() for n in presets: self.cmb_presets.Append(n) # TODO: record and reuse the preset used? self.cmb_presets.Select(0) self.filename = model.StringVA(self._get_default_filename()) self.filename.subscribe(self._onFilename, init=True) # The name of the last file that got written to disk (used for auto viewing on close) self.last_saved_file = None # a ProgressiveFuture if the acquisition is going on self.acq_future = None self._acq_future_connector = None self._main_data_model = orig_tab_data.main # duplicate the interface, but with only one view self._tab_data_model = self.duplicate_tab_data_model(orig_tab_data) # Create a new settings controller for the acquisition dialog self._settings_controller = SecomSettingsController( self, self._tab_data_model, highlight_change=True # also adds a "Reset" context menu ) # To turn on/off the fan self._orig_fan_speed = None self._orig_fan_temp = None orig_view = orig_tab_data.focussedView.value self._view = self._tab_data_model.focussedView.value self.streambar_controller = StreamBarController( self._tab_data_model, self.pnl_secom_streams) # The streams currently displayed are the one visible self.add_all_streams() # FIXME: pass the fold_panels # Compute the preset values for each preset self._preset_values = { } # dict string -> dict (SettingEntries -> value) orig_entries = get_global_settings_entries(self._settings_controller) for sc in self.streambar_controller.stream_controllers: orig_entries += get_local_settings_entries(sc) self._orig_settings = preset_as_is(orig_entries) # to detect changes for n, preset in presets.items(): self._preset_values[n] = preset(orig_entries) # Presets which have been confirmed on the hardware self._presets_confirmed = set() # (string) # If it could be possible to do fine alignment, allow the user to choose if self._can_fine_align(self._tab_data_model.streams.value): self.chkbox_fine_align.Show() # Set to True to make it the default, but will be automatically # disabled later if the current visible streams don't allow it. self.chkbox_fine_align.Value = True for s in self._tab_data_model.streams.value: if isinstance(s, EMStream): em_det = s.detector em_emt = s.emitter elif isinstance(s, OpticalStream): opt_det = s.detector self._ovrl_stream = stream.OverlayStream("Fine alignment", opt_det, em_emt, em_det) self._ovrl_stream.dwellTime.value = self._main_data_model.fineAlignDwellTime.value else: self.chkbox_fine_align.Show(False) self.chkbox_fine_align.Value = False self._prev_fine_align = self.chkbox_fine_align.Value # make sure the view displays the same thing as the one we are # duplicating self._view.view_pos.value = orig_view.view_pos.value self._view.mpp.value = orig_view.mpp.value self._view.merge_ratio.value = orig_view.merge_ratio.value # attach the view to the viewport self.pnl_view_acq.canvas.fit_view_to_next_image = False self.pnl_view_acq.setView(self._view, self._tab_data_model) self.Bind(wx.EVT_CHAR_HOOK, self.on_key) self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_close) self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file) self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire) self.cmb_presets.Bind(wx.EVT_COMBOBOX, self.on_preset) self.Bind(wx.EVT_CLOSE, self.on_close) # on_streams_changed is compatible because it doesn't use the args self.chkbox_fine_align.Bind(wx.EVT_CHECKBOX, self.on_streams_changed) self.on_preset(None) # will force setting the current preset # TODO: use the presets VAs and subscribe to each of them, instead of # using pub/sub messages pub.subscribe(self.on_setting_change, 'setting.changed') # TODO: we should actually listen to the stream tree, but it's not # currently possible. => listen to .flat once it's there # Currently just use view.lastUpdate which should be "similar" # (but doesn't work if the stream contains no image) self._view.lastUpdate.subscribe(self.on_streams_changed)
def __init__(self, parent, orig_tab_data): xrcfr_acq.__init__(self, parent) self.conf = get_acqui_conf() for n in presets: self.cmb_presets.Append(n) # TODO: record and reuse the preset used? self.cmb_presets.Select(0) self.filename = model.StringVA(self._get_default_filename()) self.filename.subscribe(self._onFilename, init=True) # a ProgressiveFuture if the acquisition is going on self.acq_future = None self._acq_future_connector = None # duplicate the interface, but with only one view self._tab_data_model = self.duplicate_tab_data_model(orig_tab_data) # Create a new settings controller for the acquisition dialog self._settings_controller = SecomSettingsController(self, self._tab_data_model, highlight_change=True) # FIXME: pass the fold_panels # Compute the preset values for each preset self._preset_values = {} # dict string -> dict (SettingEntries -> value) orig_entries = self._settings_controller.entries self._orig_settings = preset_as_is(orig_entries) # to detect changes for n, preset in presets.items(): self._preset_values[n] = preset(orig_entries) # Presets which have been confirmed on the hardware self._presets_confirmed = set() # (string) orig_view = orig_tab_data.focussedView.value view = self._tab_data_model.focussedView.value self.stream_controller = StreamController(self._tab_data_model, self.pnl_secom_streams) # The streams currently displayed are the one visible self.add_all_streams(orig_view.getStreams()) # If it could be possible to do fine alignment, allow the user to choose if self._can_fine_align(self._tab_data_model.streams.value): self.chkbox_fine_align.Show() # Set to True to make it the default, but will be automatically # disabled later if the current visible streams don't allow it. self.chkbox_fine_align.Value = True main_data = self._tab_data_model.main self._ovrl_stream = stream.OverlayStream("fine alignment", main_data.ccd, main_data.ebeam, main_data.sed) self._ovrl_stream.dwellTime.value = main_data.fineAlignDwellTime.value else: self.chkbox_fine_align.Show(False) self.chkbox_fine_align.Value = False self._prev_fine_align = self.chkbox_fine_align.Value # make sure the view displays the same thing as the one we are # duplicating view.view_pos.value = orig_view.view_pos.value view.mpp.value = orig_view.mpp.value view.merge_ratio.value = orig_view.merge_ratio.value # attach the view to the viewport self.pnl_view_acq.setView(view, self._tab_data_model) self.Bind(wx.EVT_CHAR_HOOK, self.on_key) self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_close) self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file) self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire) self.cmb_presets.Bind(wx.EVT_COMBOBOX, self.on_preset) self.Bind(wx.EVT_CLOSE, self.on_close) # on_streams_changed is compatible because it doesn't use the args self.chkbox_fine_align.Bind(wx.EVT_CHECKBOX, self.on_streams_changed) self.on_preset(None) # will force setting the current preset pub.subscribe(self.on_setting_change, 'setting.changed') # TODO: we should actually listen to the stream tree, but it's not # currently possible. # Currently just use view.last_update which should be "similar" view.lastUpdate.subscribe(self.on_streams_changed)
def acquire_timelapse(num, period, filename): """ num (int or None): if None, will never stop, unless interrupted """ # Find components by their role ccd = model.getComponent(role="ccd") ebeam = model.getComponent(role="e-beam") sed = model.getComponent(role="se-detector") light = model.getComponent(role="light") light_filter = model.getComponent(role="filter") stage = model.getComponent(role="stage") focus = model.getComponent(role="focus") # Prepare the streams and acquisition manager # The settings of the emissions and excitation are based on the current # hardware settings. stfm = stream.FluoStream("Fluorescence image", ccd, ccd.data, light, light_filter) # Force the excitation light using that command: # stfm.excitation.value = (4.72e-07, 4.79e-07, 4.85e-07, 4.91e-07, 4.97e-07) stem = stream.SEMStream("Secondary electrons", sed, sed.data, ebeam) # Special stream that will run the overlay and update the metadata based on this # Note: if more complex overlay is needed (eg, with background subtraction, # or with saving the CCD image), we'd need to directly call FindOverlay()) stovl = stream.OverlayStream("Overlay", ccd, ebeam, sed) stovl.dwellTime.value = OVERLAY_DT acq_streams = [stem, stfm, stovl] # Prepare to save each acquisition in a separate file exporter = dataio.find_fittest_converter(filename) basename, ext = os.path.splitext(filename) fn_pattern = basename + "%04d" + ext fn_pos = basename + "pos.csv" fpos = open(fn_pos, "a") fpos.write("time\tX\tY\tZ\n") # Run acquisition every period try: i = 1 while True: logging.info("Acquiring image %d", i) start = time.time() # Acquire all the images f = acq.acquire(acq_streams) data, e = f.result() if e: logging.error("Acquisition failed with %s", e) # It can partially fail, so still allow to save the data successfully acquired # Note: the actual time of the position is the one when the position was read # by the pigcs driver. spos = stage.position.value fpos.write("%.20g\t%g\t%g\t%g\n" % (time.time(), spos["x"], spos["y"], focus.position.value["z"])) # Save the file if data: exporter.export(fn_pattern % (i,), data) # TODO: run autofocus from time to time? left = period - (time.time() - start) if left < 0: logging.warning("Acquisition took longer than the period (%g s overdue)", -left) else: logging.info("Sleeping for another %g s", left) time.sleep(left) if i == num: # will never be True if num is None break i += 1 except KeyboardInterrupt: logging.info("Closing after only %d images acquired", i) except Exception: logging.exception("Failed to acquire all the images.") raise fpos.close()