예제 #1
0
 def _home_fired(self):
     """
     """
     t = Thread(name='stage.home', target=self._home)
     t.start()
     # need to store a reference to thread so it is not garbage collected
     self.move_thread = t
예제 #2
0
    def _do_execute(self):
        mapper = self.mapper
        mapper.laser_manager = self._laser_manager

        editor = self.editor
        padding = editor.padding

        #         if editor.discrete_scan:
        #             mapper.canvas = self.canvas
        #             self.component = self.canvas
        #         else:

        c = mapper.make_component(padding)
        self.component = c

        bd = editor.beam_diameter
        rp = editor.request_power
        cx = editor.center_x
        cy = editor.center_y
        step_len = editor.step_length

        t = Thread(target=mapper.do_power_mapping,
                   args=(bd, rp, cx, cy, padding, step_len))
        t.start()
        self._execute_thread = t

        return True
예제 #3
0
    def _do_execute(self):
        mapper = self.mapper
        mapper.laser_manager = self._laser_manager

        editor = self.editor
        padding = editor.padding

        #         if editor.discrete_scan:
        #             mapper.canvas = self.canvas
        #             self.component = self.canvas
        #         else:

        c = mapper.make_component(padding)
        self.component = c

        bd = editor.beam_diameter
        rp = editor.request_power
        cx = editor.center_x
        cy = editor.center_y
        step_len = editor.step_length

        t = Thread(target=mapper.do_power_mapping,
                   args=(bd, rp, cx, cy, padding, step_len))
        t.start()
        self._execute_thread = t

        return True
예제 #4
0
    def dump_sample(self):
        self.debug('dump sample')

        if self._dumper_thread is None:
            self._dumper_thread = Thread(name='DumpSample',
                                         target=self._dump_sample)
            self._dumper_thread.start()
예제 #5
0
    def _test1(self):
        print('test1')
        ld = LumenDetector()

        def func():
            src = copy(self.manager.video.get_cached_frame())
            # dim = self.stage_map.g_dimension
            ld.pxpermm = 31
            dim = 1.5
            mask_dim = dim * 1.05
            offx, offy = 0, 0
            cropdim = dim * 2.5
            src = ld.crop(src, cropdim, cropdim, offx, offy, verbose=False)

            ld.find_targets(
                self.display_image,
                src,
                dim,
                mask=mask_dim,
                search={
                    'start_offset_scalar': 1,
                    # 'width': 2
                })
            # self.manager.calculate_new_center(0, 0, 0, 0, dim=1.25)

        t = Thread(target=func)
        t.start()
        self.t = t
예제 #6
0
 def _home_fired(self):
     """
     """
     t = Thread(name='stage.home', target=self._home)
     t.start()
     # need to store a reference to thread so it is not garbage collected
     self.move_thread = t
예제 #7
0
    def _test1(self):
        self.test_image.setup_images(
            3,
            #                                     (475, 613)
            (640, 480))

        t = Thread(target=self._test)
        t.start()
        self._t = t
예제 #8
0
    def _test1(self):
        self.test_image.setup_images(3,
#                                     (475, 613)
                                    (640, 480)
                                     )

        t = Thread(target=self._test)
        t.start()
        self._t = t
예제 #9
0
    def _peak_center(self, save, confirm_save, warn, message, on_end, timeout):

        pc = self.peak_center
        spec = self.spectrometer
        ref = self.reference_detector
        isotope = self.reference_isotope

        if timeout:
            evt = Event()
            self.timeout_thread = Thread(target=self._timeout_func,
                                         args=(timeout, evt))
            self.timeout_thread.start()

        dac_d = pc.get_peak_center()

        self.peak_center_result = dac_d
        if dac_d:
            args = ref, isotope, dac_d
            self.info('new center pos {} ({}) @ {}'.format(*args))

            det = spec.get_detector(ref)

            dac_a = spec.uncorrect_dac(det, dac_d)
            self.info('dac uncorrected for HV and deflection {}'.format(dac_a))
            if save:
                if confirm_save:
                    msg = 'Update Magnet Field Table with new peak center- {} ({}) @ RefDetUnits= {}'.format(
                        *args)
                    save = self.confirmation_dialog(msg)

                if save:
                    spec.magnet.update_field_table(det, isotope, dac_a,
                                                   message)
                    spec.magnet.set_dac(dac_d)

        elif not self.canceled:
            msg = 'centering failed'
            if warn:
                self.warning_dialog(msg)
            self.warning(msg)

            # needs to be called on the main thread to properly update
            # the menubar actions. alive=False enables IonOptics>Peak Center
        # d = lambda:self.trait_set(alive=False)
        # still necessary with qt? and tasks

        if on_end:
            on_end()

        self.trait_set(alive=False)

        if timeout:
            evt.set()

        self.spectrometer.restore_integration()
예제 #10
0
    def do_machine_vision_degas(self, lumens, duration, new_thread=False):
        if self.use_video:
            dm = self.degasser_factory()

            def func():
                dm.degas(lumens, duration)

            if new_thread:
                self._degas_thread = Thread(target=func)
                self._degas_thread.start()
            else:
                func()
예제 #11
0
    def execute(self, block=False, duration=None, thread_safe=True):
        """
            if block is true wait for patterning to finish
            before returning
        """
        if not self.pattern:
            return

        self.start(show=self.show_patterning)
        evt = None
        # if current_thread().name != 'MainThread':
        if thread_safe:
            evt = Event()
            invoke_in_main_thread(self._pre_execute, evt)
            while not evt.is_set():
                time.sleep(0.05)
        else:
            self._pre_execute(evt)

        self.debug('execute xy pattern')

        xyp = self.pattern.xy_pattern_enabled
        if duration:
            self.pattern.external_duration = float(duration)

        if xyp:
            self._xy_thread = Thread(target=self._execute_xy_pattern)
            self._xy_thread.start()

        pp = self.pattern.power_pattern
        if pp:
            self.debug('execute power pattern')
            self._power_thread = Thread(target=self._execute_power_pattern)
            self._power_thread.start()

        zp = self.pattern.z_pattern

        if zp:
            self.debug('execute z pattern')
            self._z_thread = Thread(target=self._execute_z_pattern)
            self._z_thread.start()

        if block:
            if self._xy_thread:
                self._xy_thread.join()
            if self._z_thread:
                self._z_thread.join()
            if self._power_thread:
                self._power_thread.join()

            self.finish()
예제 #12
0
    def _move(self, func, pos, name=None, *args, **kw):
        if pos is None:
            return

        if self.move_thread and self.move_thread.isRunning():
            self._stop()

        if name is None:
            name = func.__name__

        self.move_thread = Thread(name='stage.{}'.format(name),
                                  target=func,
                                  args=(pos, ) + args,
                                  kwargs=kw)
        self.move_thread.start()
예제 #13
0
    def _test_fired(self):
        from pychron.globals import globalv

        p = '/Users/ross/Sandbox/test_target.jpg'
#         p = '/Users/ross/Sandbox/pos_err/pos_err_200_0-002.jpg'
        p = '/Users/ross/Sandbox/poserror/pos_err_221_0-007.jpg'
#         p = '/Users/ross/Sandbox/poserror/snapshot009.jpg'
        # force video to reload test image
        self.video.source_frame = None
        globalv.video_test_path = p

        im = self.setup_image()

#         self._test2(im)
        from pychron.core.ui.thread import Thread
        t = Thread(target=self._test2, args=(im,))
        t.start()
        self._t = t
예제 #14
0
    def _test_fired(self):
        from pychron.globals import globalv

        p = '/Users/ross/Sandbox/test_target.jpg'
        #         p = '/Users/ross/Sandbox/pos_err/pos_err_200_0-002.jpg'
        p = '/Users/ross/Sandbox/poserror/pos_err_221_0-007.jpg'
        #         p = '/Users/ross/Sandbox/poserror/snapshot009.jpg'
        # force video to reload test image
        self.video.source_frame = None
        globalv.video_test_path = p

        im = self.setup_image()

        #         self._test2(im)
        from pychron.core.ui.thread import Thread
        t = Thread(target=self._test2, args=(im, ))
        t.start()
        self._t = t
예제 #15
0
    def _set_point(self, v):
        if self.canvas.calibrate:
            self.warning_dialog('Cannot move while calibrating')
            return

        if self.canvas.markup:
            self.warning_dialog('Cannot move while adding/editing points')
            return

        if (self.move_thread is None or not self.move_thread.isRunning()) and v is not self._point:
            pos = self.canvas.get_item('point', int(v) - 1)
            if pos is not None:
                self._point = v
                self.move_thread = Thread(target=self._move_to_point, args=(pos,))
                self.move_thread.start()
            else:
                err = 'Invalid point {}'.format(v)
                self.warning(err)
                return err
예제 #16
0
    def do_peak_center(self,
                       save=True,
                       confirm_save=False,
                       warn=False,
                       new_thread=True):
        self.debug('doing pc')

        self.canceled = False
        self.alive = True

        args = (save, confirm_save, warn)
        if new_thread:
            t = Thread(name='ion_optics.peak_center', target=self._peak_center,
                       args=args)
            t.start()
            self._thread = t
            return t
        else:
            self._peak_center(*args)
예제 #17
0
    def do_peak_center(self,
                       save=True,
                       confirm_save=False,
                       warn=False,
                       new_thread=True):
        self.debug('doing pc')

        self.canceled = False
        self.alive = True

        args = (save, confirm_save, warn)
        if new_thread:
            t = Thread(name='ion_optics.peak_center',
                       target=self._peak_center,
                       args=args)
            t.start()
            self._thread = t
            return t
        else:
            self._peak_center(*args)
예제 #18
0
    def _move(self, func, pos, name=None, *args, **kw):
        if pos is None:
            return

        if self.move_thread and self.move_thread.isRunning():
            self.stage_controller.stop()
        if name is None:
            name = func.func_name

        self.move_thread = Thread(name='stage.{}'.format(name),
                                  target=func, args=(pos,) + args, kwargs=kw)
        self.move_thread.start()
예제 #19
0
    def do_machine_vision_degas(self, lumens, duration, new_thread=False):
        if self.use_video:
            dm = self.degasser_factory()

            def func():
                dm.degas(lumens, duration)

            if new_thread:
                self._degas_thread = Thread(target=func)
                self._degas_thread.start()
            else:
                func()
예제 #20
0
    def _execute(self):

        yd = self._read_control_path()

        if yd is None:
            sp = 1000
        else:
            sp = yd['period']

        # starts automatically
        self.debug('scan starting')
        self._timer = Timer(sp, self._scan)

        self.info('scan started')

        #            yd = self._read_control_path()
        if yd is not None:
            # start a control thread
            self._control_thread = Thread(target=self._control, args=(yd, ))
            self._control_thread.start()
            self.info('control started')
예제 #21
0
    def passive_focus(self, block=False, **kw):

        self._evt_autofocusing = TEvent()
        self._evt_autofocusing.clear()
        #        manager = self.laser_manager
        oper = self.parameters.operator
        self.info('passive focus. operator = {}'.format(oper))

        g = self.graph
        if not g:
            g = Graph(plotcontainer_dict=dict(padding=10),
                      window_x=0.70,
                      window_y=20,
                      window_width=325,
                      window_height=325,
                      window_title='Autofocus')
            self.graph = g

        g.clear()

        g.new_plot(padding=[40, 10, 10, 40],
                   xtitle='Z (mm)',
                   ytitle='Focus Measure ({})'.format(oper))
        g.new_series()
        g.new_series()

        invoke_in_main_thread(self._open_graph)

        target = self._passive_focus
        self._passive_focus_thread = Thread(name='autofocus',
                                            target=target,
                                            args=(self._evt_autofocusing, ),
                                            kwargs=kw)
        self._passive_focus_thread.start()
        if block:
            #             while 1:
            #                 if not self._passive_focus_thread.isRunning():
            #                     break
            #                 time.sleep(0.25)
            self._passive_focus_thread.join()
예제 #22
0
    def _peak_center(self, save, confirm_save, warn, message, on_end, timeout):

        pc = self.peak_center
        spec = self.spectrometer
        ref = self.reference_detector
        isotope = self.reference_isotope

        if timeout:
            evt = Event()
            self.timeout_thread = Thread(target=self._timeout_func, args=(timeout, evt))
            self.timeout_thread.start()

        dac_d = pc.get_peak_center()

        self.peak_center_result = dac_d
        if dac_d:
            args = ref, isotope, dac_d
            self.info('new center pos {} ({}) @ {}'.format(*args))

            det = spec.get_detector(ref)

            dac_a = spec.uncorrect_dac(det, dac_d)
            self.info('dac uncorrected for HV and deflection {}'.format(dac_a))
            if save:
                if confirm_save:
                    msg = 'Update Magnet Field Table with new peak center- {} ({}) @ RefDetUnits= {}'.format(*args)
                    save = self.confirmation_dialog(msg)

                if save:
                    spec.magnet.update_field_table(det, isotope, dac_a, message)
                    spec.magnet.set_dac(dac_d)

        elif not self.canceled:
            msg = 'centering failed'
            if warn:
                self.warning_dialog(msg)
            self.warning(msg)

            # needs to be called on the main thread to properly update
            # the menubar actions. alive=False enables IonOptics>Peak Center
        # d = lambda:self.trait_set(alive=False)
        # still necessary with qt? and tasks

        if on_end:
            on_end()

        self.trait_set(alive=False)

        if timeout:
            evt.set()

        self.spectrometer.restore_integration()
예제 #23
0
    def do_peak_center(self,
                       save=True,
                       confirm_save=False,
                       warn=False,
                       new_thread=True,
                       message='',
                       on_end=None,
                       timeout=None):
        self.debug('doing pc')

        self.canceled = False
        self.alive = True
        self.peak_center_result = None

        args = (save, confirm_save, warn, message, on_end, timeout)
        if new_thread:
            t = Thread(name='ion_optics.peak_center', target=self._peak_center,
                       args=args)
            t.start()
            self._centering_thread = t
            return t
        else:
            self._peak_center(*args)
예제 #24
0
    def do_peak_center(self,
                       save=True,
                       confirm_save=False,
                       warn=False,
                       new_thread=True,
                       message='',
                       on_end=None,
                       timeout=None):
        self.debug('doing pc')

        self.canceled = False
        self.alive = True
        self.peak_center_result = None

        args = (save, confirm_save, warn, message, on_end, timeout)
        if new_thread:
            t = Thread(name='ion_optics.peak_center',
                       target=self._peak_center,
                       args=args)
            t.start()
            self._centering_thread = t
            return t
        else:
            self._peak_center(*args)
예제 #25
0
    def passive_focus(self, block=False, **kw):

        self._evt_autofocusing = TEvent()
        self._evt_autofocusing.clear()
#        manager = self.laser_manager
        oper = self.parameters.operator
        self.info('passive focus. operator = {}'.format(oper))

        g = self.graph
        if not g:
            g = Graph(plotcontainer_dict=dict(padding=10),
                      window_x=0.70,
                      window_y=20,
                      window_width=325,
                      window_height=325,
                      window_title='Autofocus'
                      )
            self.graph = g

        g.clear()

        g.new_plot(padding=[40, 10, 10, 40],
                   xtitle='Z (mm)',
                   ytitle='Focus Measure ({})'.format(oper)
                   )
        g.new_series()
        g.new_series()

        invoke_in_main_thread(self._open_graph)

        target = self._passive_focus
        self._passive_focus_thread = Thread(name='autofocus', target=target,
                                            args=(self._evt_autofocusing,

                                                  ),
                                            kwargs=kw
                                            )
        self._passive_focus_thread.start()
        if block:
#             while 1:
#                 if not self._passive_focus_thread.isRunning():
#                     break
#                 time.sleep(0.25)
            self._passive_focus_thread.join()
예제 #26
0
    def execute(self, block=False, duration=None, thread_safe=True):
        """
            if block is true wait for patterning to finish
            before returning
        """
        if not self.pattern:
            return

        self.start(show=self.show_patterning)
        evt = None
        # if current_thread().name != 'MainThread':
        if thread_safe:
            evt = Event()
            invoke_in_main_thread(self._pre_execute, evt)
            while not evt.is_set():
                time.sleep(0.05)
        else:
            self._pre_execute(evt)

        self.debug('execute xy pattern')

        xyp = self.pattern.xy_pattern_enabled
        if duration:
            self.pattern.external_duration = float(duration)

        if xyp:
            self._xy_thread = Thread(target=self._execute_xy_pattern)
            self._xy_thread.start()

        pp = self.pattern.power_pattern
        if pp:
            self.debug('execute power pattern')
            self._power_thread = Thread(target=self._execute_power_pattern)
            self._power_thread.start()

        zp = self.pattern.z_pattern

        if zp:
            self.debug('execute z pattern')
            self._z_thread = Thread(target=self._execute_z_pattern)
            self._z_thread.start()

        if block:
            if self._xy_thread:
                self._xy_thread.join()
            if self._z_thread:
                self._z_thread.join()
            if self._power_thread:
                self._power_thread.join()

            self.finish()
예제 #27
0
    def _set_point(self, v):
        if self.canvas.calibrate:
            self.warning_dialog('Cannot move while calibrating')
            return

        if self.canvas.markup:
            self.warning_dialog('Cannot move while adding/editing points')
            return

        if (self.move_thread is None or not self.move_thread.isRunning()) and v is not self._point:
            pos = self.canvas.get_item('point', int(v) - 1)
            if pos is not None:
                self._point = v
                self.move_thread = Thread(target=self._move_to_point, args=(pos,))
                self.move_thread.start()
            else:
                err = 'Invalid point {}'.format(v)
                self.warning(err)
                return err
예제 #28
0
    def start_measure_grain_polygon(self):
        self._measure_grain_evt = evt = TEvent()

        def _measure_grain_polygon():
            ld = self.lumen_detector
            dim = self.stage_map.g_dimension
            ld.pxpermm = self.pxpermm

            self.debug('Starting measure grain polygon')
            masks = []
            display_image = self.autocenter_manager.display_image

            mask_dim = dim * 1.05
            mask_dim_mm = mask_dim * self.pxpermm
            ld.grain_measuring = True
            while not evt.is_set():
                src = self._get_preprocessed_src()
                if src is not None:
                    targets = ld.find_targets(
                        display_image,
                        src,
                        dim,
                        mask=mask_dim,
                        search={'start_offset_scalar': 1})
                    if targets:
                        t = time.time()
                        targets = [(t, mask_dim_mm, ti.poly_points.tolist())
                                   for ti in targets]
                        masks.extend(targets)
                sleep(0.1)
            ld.grain_measuring = False

            self.grain_polygons = (m for m in masks)
            self.debug('exiting measure grain')

        self._measure_grain_t = QThread(target=_measure_grain_polygon)
        self._measure_grain_t.start()
        return True
예제 #29
0
    def _execute(self):

        yd = self._read_control_path()

        if yd is None:
            sp = 1000
        else:
            sp = yd['period']

        # starts automatically
        self.debug('scan starting')
        self._timer = Timer(sp, self._scan)

        self.info('scan started')

        #            yd = self._read_control_path()
        if yd is not None:
            # start a control thread
            self._control_thread = Thread(target=self._control,
                                          args=(yd,)
            )
            self._control_thread.start()
            self.info('control started')
예제 #30
0
 def _back_button_fired(self):
     pos, kw = self.linear_move_history.pop(-1)
     t = Thread(target=self.stage_controller.linear_move, args=pos, kwargs=kw)
     t.start()
     self.move_thread = t
예제 #31
0
class PatternExecutor(Patternable):
    """
         a pattern is only good for one execution.
         self.pattern needs to be reset after stop or finish using load_pattern(name_or_pickle)
    """
    controller = Any
    laser_manager = Any
    show_patterning = Bool(False)
    _alive = Bool(False)

    def __init__(self, *args, **kw):
        super(PatternExecutor, self).__init__(*args, **kw)
        self._next_point = None
        self.pattern = None
        self._xy_thread = None
        self._power_thread = None
        self._z_thread = None

    def start(self, show=False):
        self._alive = True

        if show:
            self.show_pattern()

        if self.pattern:
            self.pattern.clear_graph()

    def finish(self):
        self._alive = False
        self.close_pattern()
        self.pattern = None

    def set_stage_values(self, sm):
        if self.pattern:
            self.pattern.set_stage_values(sm)

    def set_current_position(self, x, y, z):
        if self.isPatterning():
            graph = self.pattern.graph
            graph.set_data([x], series=1, axis=0)
            graph.set_data([y], series=1, axis=1)

            graph.add_datum((x, y), series=2)

            graph.redraw()

    def load_pattern(self, name_or_pickle):
        """
            look for name_or_pickle in local pattern dir

            if not found try interpreting name_or_pickle is a pickled name_or_pickle

        """
        if name_or_pickle is None:
            path = self.open_file_dialog()
            if path is None:
                return
        else:
            path = self.is_local_pattern(name_or_pickle)

        if path:
            wfile = open(path, 'rb')
        else:
            # convert name_or_pickle into a file like obj
            wfile = StringIO(name_or_pickle)

        # self._load_pattern sets self.pattern
        pattern = self._load_pattern(wfile, path)

        self.on_trait_change(self.stop, 'canceled')
        return pattern

    def is_local_pattern(self, name):

        def test_name(ni):
            path = os.path.join(paths.pattern_dir, ni)
            if os.path.isfile(path):
                return path

        for ni in (name, name + '.lp'):
            p = test_name(ni)
            if p:
                return p

    def stop(self):
        self._alive = False
        if self.controller:
            self.info('User requested stop')
            self.controller.stop()

        if self.pattern is not None:
            if self.controller:
                self.controller.linear_move(self.pattern.cx, self.pattern.cy, source='pattern stop')
            # self.pattern.close_ui()
            self.info('Pattern {} stopped'.format(self.pattern_name))

            # prevent future stops (AbortJogs from massspec) from executing
            self.pattern = None

    def isPatterning(self):
        return self._alive

    def close_pattern(self):
        pass

    def show_pattern(self):
        self.pattern.window_x = 50
        self.pattern.window_y = 50
        open_view(self.pattern, view='graph_view')

    def execute(self, block=False, duration=None, thread_safe=True):
        """
            if block is true wait for patterning to finish
            before returning
        """
        if not self.pattern:
            return

        self.start(show=self.show_patterning)
        evt = None
        # if current_thread().name != 'MainThread':
        if thread_safe:
            evt = Event()
            invoke_in_main_thread(self._pre_execute, evt)
            while not evt.is_set():
                time.sleep(0.05)
        else:
            self._pre_execute(evt)

        self.debug('execute xy pattern')

        xyp = self.pattern.xy_pattern_enabled
        if duration:
            self.pattern.external_duration = float(duration)

        if xyp:
            self._xy_thread = Thread(target=self._execute_xy_pattern)
            self._xy_thread.start()

        pp = self.pattern.power_pattern
        if pp:
            self.debug('execute power pattern')
            self._power_thread = Thread(target=self._execute_power_pattern)
            self._power_thread.start()

        zp = self.pattern.z_pattern

        if zp:
            self.debug('execute z pattern')
            self._z_thread = Thread(target=self._execute_z_pattern)
            self._z_thread.start()

        if block:
            if self._xy_thread:
                self._xy_thread.join()
            if self._z_thread:
                self._z_thread.join()
            if self._power_thread:
                self._power_thread.join()

            self.finish()

    def _pre_execute(self, evt):
        self.debug('pre execute')
        pattern = self.pattern

        kind = pattern.kind
        if kind in ('SeekPattern', 'DragonFlyPeakPattern'):
            self._info = open_view(pattern, view='execution_graph_view')

        if evt is not None:
            evt.set()
        self.debug('pre execute finished')

    def _execute_power_pattern(self):
        pat = self.pattern
        self.info('starting power pattern {}'.format(pat.name))

        def func(v):
            self.laser_manager.set_laser_power(v)

        self._execute_(func, pat.power_values(), pat.power_sample, 'power pattern setpoint={value}')

    def _execute_z_pattern(self):
        pat = self.pattern
        self.info('starting power pattern {}'.format(pat.name))

        def func(v):
            self.controller.set_z(v)

        self._execute_(func, pat.z_values(), pat.z_sample, 'z pattern z={value}')

    def _execute_(self, func, vs, period, msg):
        for v in vs:
            st = time.time()
            self.debug(msg.format(value=v))
            func(v)

            et = st - time.time()
            p = period - et
            time.sleep(p)

    def _execute_xy_pattern(self):
        pat = self.pattern
        self.info('starting pattern {}'.format(pat.name))
        st = time.time()
        self.controller.update_position()
        time.sleep(1)
        pat.cx, pat.cy = self.controller.x, self.controller.y
        try:
            for ni in range(pat.niterations):
                if not self.isPatterning():
                    break

                self.info('doing pattern iteration {}'.format(ni))
                self._execute_iteration()

            self.controller.linear_move(pat.cx, pat.cy, block=True, source='execute_xy_pattern')
            if pat.disable_at_end:
                self.laser_manager.disable_device()

            self.finish()
            self.info('finished pattern: transit time={:0.1f}s'.format(time.time() - st))

        except (TargetPositionError, PositionError) as e:
            self.finish()
            self.controller.stop()
            self.laser_manager.emergency_shutoff(str(e))

    def _execute_iteration(self):
        controller = self.controller
        pattern = self.pattern
        if controller is not None:

            kind = pattern.kind
            if kind == 'ArcPattern':
                self._execute_arc(controller, pattern)
            elif kind == 'CircularContourPattern':
                self._execute_contour(controller, pattern)
            elif kind in ('SeekPattern', 'DragonFlyPeakPattern'):
                self._execute_seek(controller, pattern)
            else:
                self._execute_points(controller, pattern, multipoint=False)

    def _execute_points(self, controller, pattern, multipoint=False):
        pts = pattern.points_factory()
        if multipoint:
            controller.multiple_point_move(pts, velocity=pattern.velocity)
        else:
            for x, y in pts:
                if not self.isPatterning():
                    break
                controller.linear_move(x, y, block=True,
                                       velocity=pattern.velocity)

    def _execute_contour(self, controller, pattern):
        for ni in range(pattern.nsteps):
            if not self.isPatterning():
                break

            r = pattern.radius * (1 + ni * pattern.percent_change)
            self.info('doing circular contour {} {}'.format(ni + 1, r))
            controller.single_axis_move('x', pattern.cx + r,
                                        block=True)
            controller.arc_move(pattern.cx, pattern.cy, 360,
                                block=True)
            time.sleep(0.1)

    def _execute_arc(self, controller, pattern):
        controller.single_axis_move('x', pattern.radius, block=True)
        controller.arc_move(pattern.cx, pattern.cy, pattern.degrees, block=True)

    def _execute_seek(self, controller, pattern):

        duration = pattern.duration
        total_duration = pattern.total_duration

        lm = self.laser_manager
        sm = lm.stage_manager
        ld = sm.lumen_detector

        ld.mask_kind = pattern.mask_kind
        ld.custom_mask = pattern.custom_mask_radius

        osdp = sm.canvas.show_desired_position
        sm.canvas.show_desired_position = False

        st = time.time()
        self.debug('Pre seek delay {}'.format(pattern.pre_seek_delay))
        time.sleep(pattern.pre_seek_delay)

        self.debug('starting seek')
        self.debug('total duration {}'.format(total_duration))
        self.debug('dwell duration {}'.format(duration))

        if pattern.kind == 'DragonFlyPeakPattern':
            try:
                self._dragonfly_peak(st, pattern, lm, controller)
            except BaseException as e:
                self.critical('Dragonfly exception. {}'.format(e))
        else:
            self._hill_climber(st, controller, pattern)

        sm.canvas.show_desired_position = osdp

        from pyface.gui import GUI
        GUI.invoke_later(self._info.dispose)

    def _dragonfly_peak(self, st, pattern, lm, controller):

        # imgplot, imgplot2, imgplot3 = pattern.setup_execution_graph()
        # imgplot, imgplot2 = pattern.setup_execution_graph()
        imgplot, imgplot2 = pattern.setup_execution_graph(nplots=2)
        cx, cy = pattern.cx, pattern.cy

        sm = lm.stage_manager

        linear_move = controller.linear_move
        in_motion = controller.in_motion
        find_lum_peak = sm.find_lum_peak
        set_data = imgplot.data.set_data
        set_data2 = imgplot2.data.set_data

        duration = pattern.duration
        sat_threshold = pattern.saturation_threshold
        total_duration = pattern.total_duration
        min_distance = pattern.min_distance
        aggressiveness = pattern.aggressiveness
        update_period = pattern.update_period / 1000.
        move_threshold = pattern.move_threshold
        blur = pattern.blur
        px, py = cx, cy

        point_gen = None
        cnt = 0
        peak = None

        while time.time() - st < total_duration:
            if not self._alive:
                break

            sats = []
            pts = []
            ist = time.time()
            npt = None
            self.debug('starting iteration={}, in_motion={}'.format(cnt, in_motion()))
            while time.time() - ist < duration or in_motion():
                args = find_lum_peak(min_distance, blur)
                if args is None:
                    continue

                pt, peakcol, peakrow, peak_img, sat, src = args

                sats.append(sat)
                if peak is None:
                    peak = peak_img
                else:
                    peak = ((peak.astype('int16') - 2) + peak_img).clip(0, 255)

                img = gray2rgb(peak).astype(uint8)
                src = gray2rgb(src).astype(uint8)
                if pt:
                    pts.append(pt)
                    c = circle(peakrow, peakcol, min_distance / 2)
                    img[c] = (255, 0, 0)
                    src[c] = (255, 0, 0)

                # set_data('imagedata', src)
                set_data2('imagedata', src)
                set_data('imagedata', img)
                sleep(update_period)

            self.debug('iteration {} finished, npts={}'.format(cnt, len(pts)))

            pattern.position_str = '---'

            if pts:
                w = array(sats)
                avg_sat_score = w.mean()
                self.debug('Average Saturation: {} threshold={}'.format(avg_sat_score, sat_threshold))
                pattern.average_saturation = avg_sat_score
                if avg_sat_score < sat_threshold:
                    pts = array(pts)
                    x, y, w = pts.T
                    ws = w.sum()
                    nx = (x * w).sum() / ws
                    ny = (y * w).sum() / ws
                    self.debug('New point {},{}'.format(nx, ny))
                    npt = nx, ny, 1
                else:
                    continue

            # if npt is None:
            #     if not point_gen:
            #         point_gen = pattern.point_generator()
            #     # wait = False
            #     npt = next(point_gen)
            #     self.debug('generating new point={}'.format(npt))
            #
            # else:
            #     point_gen = None
            # wait = True
            if npt is None:
                block = total_duration - (time.time() - st) < duration
                linear_move(cx, cy, source='recenter_dragonfly{}'.format(cnt), block=block, velocity=pattern.velocity,
                            use_calibration=False)
                pattern.position_str = 'Return to Center'
                px, py = cx, cy
                continue

            try:
                scalar = npt[2]
            except IndexError:
                scalar = 1

            ascalar = scalar * aggressiveness
            dx = npt[0] / sm.pxpermm * ascalar
            dy = npt[1] / sm.pxpermm * ascalar
            if abs(dx) < move_threshold or abs(dy) < move_threshold:
                self.debug('Deviation too small dx={},dy={}'.format(dx, dy, move_threshold))
                pattern.position_str = 'Deviation too small'
                continue
            px += dx
            py -= dy
            self.debug('i: {}. point={},{}. '
                       'Intensitiy Scalar={}, Modified Scalar={}'.format(cnt, px, py, scalar, ascalar))

            if not pattern.validate(px, py):
                self.debug('invalid position. {},{}'.format(px, py))
                px, py = pattern.reduce_vector_magnitude(px, py, 0.85)
                self.debug('reduced vector magnitude. new pos={},{}'.format(px, py))

            pattern.position_str = '{:0.5f},{:0.5f}'.format(px, py)

            # if there is less than 1 duration left then block is true
            block = total_duration - (time.time() - st) < duration
            self.debug('blocking ={}'.format(block))
            linear_move(px, py, source='dragonfly{}'.format(cnt), block=block, velocity=pattern.velocity,
                        use_calibration=False)

            # if wait:
            #     et = time.time() - ist
            #     d = duration - et
            #     if d > 0:
            #         time.sleep(d)

            cnt += 1

        # time.sleep(1)
        self.debug('dragonfly complete')
        controller.block()

    def _hill_climber(self, st, controller, pattern):
        g = pattern.execution_graph
        imgplot, cp = pattern.setup_execution_graph()

        cx, cy = pattern.cx, pattern.cy

        sm = self.laser_manager.stage_manager
        linear_move = controller.linear_move
        get_scores = sm.get_scores
        moving = sm.moving
        update_axes = sm.update_axes
        set_data = imgplot.data.set_data

        sat_threshold = pattern.saturation_threshold
        total_duration = pattern.total_duration
        duration = pattern.duration
        pattern.perimeter_radius *= sm.pxpermm

        avg_sat_score = -1
        # current_x, current_y =None, None
        for i, pt in enumerate(pattern.point_generator()):
            update_plot = True

            x, y = pt.x, pt.y
            ax, ay = cx + x, cy + y
            if not self._alive:
                break

            if time.time() - st > total_duration:
                break

            # use_update_point = False
            if avg_sat_score < sat_threshold:
                # use_update_point = False
                # current_x, current_y = x, y
                linear_move(ax, ay, block=False, velocity=pattern.velocity,
                            use_calibration=False,
                            update=False,
                            immediate=True)
            else:
                self.debug('Saturation target reached. not moving')
                update_plot = False

            density_scores = []
            ts = []
            saturation_scores = []
            positions = []

            def measure_scores(update=False):
                if update:
                    update_axes()

                positions.append((controller.x, controller.y))
                score_density, score_saturation, img = get_scores()

                density_scores.append(score_density)
                saturation_scores.append(score_saturation)

                set_data('imagedata', img)
                ts.append(time.time() - st)
                time.sleep(0.1)

            while moving(force_query=True):
                measure_scores(update=True)

            mt = time.time()
            while time.time() - mt < duration:
                measure_scores()

            if density_scores:
                n = len(density_scores)

                density_scores = array(density_scores)
                saturation_scores = array(saturation_scores)

                weights = [1 / (max(0.0001, ((xi - ax) ** 2 + (yi - ay) ** 2)) ** 0.5) for xi, yi in positions]

                avg_score = average(density_scores, weights=weights)
                avg_sat_score = average(saturation_scores, weights=weights)
                score = avg_score

                m, b = polyfit(ts, density_scores, 1)
                if m > 0:
                    score *= (1 + m)

                pattern.set_point(score, pt)

                self.debug('i:{} XY:({:0.5f},{:0.5f})'.format(i, x, y))
                self.debug('Density. AVG:{:0.3f} N:{} Slope:{:0.3f}'.format(avg_score, n, m))
                self.debug('Modified Density Score: {:0.3f}'.format(score))
                self.debug('Saturation. AVG:{:0.3f}'.format(avg_sat_score))
                if update_plot:
                    cp.add_point((x, y))
                    g.add_datum((x, y), plotid=0)

                t = time.time() - st
                g.add_datum((t, avg_score), plotid=1)

                # g.add_bulk_data(ts, density_scores, plotid=1, series=1)

                g.add_datum((t, score),
                            ypadding='0.1',
                            ymin_anchor=-0.1,
                            update_y_limits=True, plotid=1)

            update_axes()
예제 #32
0
 def execute(self):
     self.canceled = False
     t = Thread(target=self._execute)
     t.start()
     self._t = t
예제 #33
0
class AutoFocusManager(Manager):
    """
        currently uses passive focus techniques
        see

        http://en.wikipedia.org/wiki/Autofocus

    """

    video = Any
    laser_manager = Any
    stage_controller = Any
    canvas = Any
    parameters = Instance(FocusParameters)
    configure_button = Button('configure')

    autofocus_button = Event
    autofocus_label = Property(depends_on='autofocusing')
    autofocusing = Bool

    # threading event for cancel signal
    _evt_autofocusing = None

    image = Instance(Image, ())

    graph = None

    def dump_parameters(self):
        p = os.path.join(paths.hidden_dir, 'autofocus_configure')
        self.info('dumping parameters to {}'.format(p))
        with open(p, 'wb') as f:
            pickle.dump(self.parameters, f)

    def load_parameter(self):
        p = os.path.join(paths.hidden_dir, 'autofocus_configure')
        if os.path.isfile(p):
            with open(p, 'rb') as f:
                try:
                    params = pickle.load(f)
                    self.info('loading parameters from {}'.format(p))

                    if not isinstance(params, FocusParameters):
                        self.info('out of date parameters file. using default')
                        params = FocusParameters()
                    return params

                except Exception as e:
                    print('autofocus load parameter', e)
                    return FocusParameters()
        else:
            return FocusParameters()

    def passive_focus(self, block=False, **kw):

        self._evt_autofocusing = TEvent()
        self._evt_autofocusing.clear()
        #        manager = self.laser_manager
        oper = self.parameters.operator
        self.info('passive focus. operator = {}'.format(oper))

        g = self.graph
        if not g:
            g = Graph(plotcontainer_dict=dict(padding=10),
                      window_x=0.70,
                      window_y=20,
                      window_width=325,
                      window_height=325,
                      window_title='Autofocus')
            self.graph = g

        g.clear()

        g.new_plot(padding=[40, 10, 10, 40],
                   xtitle='Z (mm)',
                   ytitle='Focus Measure ({})'.format(oper))
        g.new_series()
        g.new_series()

        invoke_in_main_thread(self._open_graph)

        target = self._passive_focus
        self._passive_focus_thread = Thread(name='autofocus',
                                            target=target,
                                            args=(self._evt_autofocusing, ),
                                            kwargs=kw)
        self._passive_focus_thread.start()
        if block:
            #             while 1:
            #                 if not self._passive_focus_thread.isRunning():
            #                     break
            #                 time.sleep(0.25)
            self._passive_focus_thread.join()

    def _open_graph(self):
        ui = self.graph.edit_traits()
        self.add_window(ui)

    def stop_focus(self):

        if self.stage_controller:
            self.stage_controller.stop()

        self.info('autofocusing stopped by user')

    def _passive_focus(self, stop_signal, set_zoom=True):
        '''
            sweep z looking for max focus measure
            FMgrad= roberts or sobel (sobel removes noise)
            FMvar = intensity variance
        '''

        self.autofocusing = True

        manager = self.laser_manager
        fstart = self.parameters.fstart
        fend = self.parameters.fend
        step_scalar = self.parameters.step_scalar
        zoom = self.parameters.zoom
        operator = self.parameters.operator

        steps = step_scalar * (max(fend, fstart) - min(fend, fstart)) + 1

        prev_zoom = None
        if set_zoom and \
                manager is not None and \
                     zoom:
            motor = manager.get_motor('zoom')
            if motor:
                prev_zoom = motor.data_position
                self.info('setting zoom: {}'.format(zoom))
                manager.set_motor('zoom', zoom, block=True)
                time.sleep(1.5)

        args = self._do_focusing(fstart, fend, steps, operator)

        if manager is not None:
            if prev_zoom is not None:
                self.info('returning to previous zoom: {}'.format(prev_zoom))
                manager.set_motor('zoom', prev_zoom, block=True)

        if args:
            mi, fmi, ma, fma = args

            self.info('''passive focus results:Operator={}
ImageGradmin={} (z={})
ImageGradmax={}, (z={})'''.format(operator, mi, fmi, ma, fma))

            focus_pos = fma
            self.graph.add_vertical_rule(focus_pos)
            self.graph.redraw()
            #            self.graph.add_vertical_rule(fma)

            self.info('calculated focus z= {}'.format(focus_pos))

            #            if set_z:
            controller = self.stage_controller
            if controller is not None:
                if not stop_signal.isSet():
                    controller.single_axis_move('z', focus_pos, block=True)
                    controller._z_position = focus_pos
                    controller.z_progress = focus_pos

        self.autofocusing = False

    def _cancel_sweep(self, vo):
        if self._evt_autofocusing.isSet():
            # return to original velocity
            self.autofocusing = False
            self._reset_velocity(vo)
            return True

    def _reset_velocity(self, vo):
        if self.stage_controller:
            pdict = dict(velocity=vo, key='z')
            self.stage_controller.set_single_axis_motion_parameters(
                pdict=pdict)

    def _do_focusing(self, start, end, steps, operator):
        screen_roi = self._get_roi()
        self._add_focus_area_rect(*screen_roi)

        src = self._load_source()
        src = asarray(src)
        h, w, _d = src.shape

        cx = w / 2.
        cy = h / 2.

        cw = self.parameters.crop_width
        ch = self.parameters.crop_height

        roi = cx, cy, cw, ch
        '''
            start the z in motion and take pictures as you go
            query stage_controller to get current z
        '''

        self.info('focus sweep start={} end={}'.format(start, end))
        # move to start position
        controller = self.stage_controller
        if controller:
            vo = controller.axes['z'].velocity
            if self._cancel_sweep(vo):
                return
            self.graph.set_x_limits(min(start, end), max(start, end), pad=2)
            # sweep 1 and velocity 1
            self._do_sweep(start,
                           end,
                           velocity=self.parameters.velocity_scalar1)
            fms, focussteps = self._collect_focus_measures(operator, roi)
            if not (fms and focussteps):
                return

            # reached end of sweep
            # calculate a nominal focal point
            args = self._calculate_nominal_focal_point(fms, focussteps)
            if not args:
                return
            nfocal = args[3]

            nwin = self.parameters.negative_window
            pwin = self.parameters.positive_window

            if self._cancel_sweep(vo):
                return
            nstart, nend = max(0, nfocal - nwin), nfocal + pwin
            #            mi = min(min(nstart, nend), min(start, end))
            #            ma = max(max(nstart, nend), max(start, end))
            #            self.graph.set_x_limits(mi, ma, pad=2)
            time.sleep(1)
            # do a slow tight sweep around the nominal focal point
            self._do_sweep(nstart,
                           nend,
                           velocity=self.parameters.velocity_scalar2)
            fms, focussteps = self._collect_focus_measures(operator,
                                                           roi,
                                                           series=1)

            self._reset_velocity(vo)

        else:
            focussteps = linspace(0, 10, 11)
            fms = -(focussteps - 5)**2 + 10 + random.random(11)

        self.info('frames analyzed {}'.format(len(fms)))

        #        self.canvas.markupcontainer.pop('croprect')
        return self._calculate_nominal_focal_point(fms, focussteps)

    def _do_sweep(self, start, end, velocity=None):
        controller = self.stage_controller
        controller.single_axis_move('z', start, block=True)
        #         time.sleep(0.1)
        # explicitly check for motion
        #        controller.block(axis='z')

        if velocity:
            vo = controller.axes['z'].velocity

            controller.set_single_axis_motion_parameters(
                pdict=dict(velocity=vo * velocity, key='z'))

        self.info('starting sweep from {}'.format(controller.z_progress))
        # pause before moving to end
        time.sleep(0.25)
        controller.single_axis_move('z', end, update=100, immediate=True)

    def _collect_focus_measures(self, operator, roi, series=0):
        controller = self.stage_controller
        focussteps = []
        fms = []
        if controller.timer:
            p = controller.timer.get_interval()
            self.debug('controller timer period {}'.format(p))
            pz = controller.z_progress

            while 1:
                src = self._load_source()
                x = controller.z_progress
                if x != pz:
                    y = self._calculate_focus_measure(src, operator, roi)
                    self.graph.add_datum((x, y), series=series)

                    focussteps.append(x)
                    fms.append(y)

                    pz = x

                if not (controller.timer.isActive() and \
                        not self._evt_autofocusing.isSet()):
                    break
                time.sleep(p)

            self.debug('sweep finished')

        return fms, focussteps

    def _calculate_nominal_focal_point(self, fms, focussteps):
        if fms:
            sfms = smooth(fms)
            if sfms is not None:

                self.graph.new_series(focussteps, sfms)
                self.graph.redraw()

                fmi = focussteps[argmin(sfms)]
                fma = focussteps[argmax(sfms)]

                mi = min(sfms)
                ma = max(sfms)

                return mi, fmi, ma, fma

    def _calculate_focus_measure(self, src, operator, roi):
        '''
            see
            IMPLEMENTATION OF A PASSIVE AUTOMATIC FOCUSING ALGORITHM
            FOR DIGITAL STILL CAMERA
            DOI 10.1109/30.468047
            and
            http://cybertron.cg.tu-berlin.de/pdci10/frankencam/#autofocus
        '''

        # need to resize to 640,480. this is the space the roi is in
        #        s = resize(grayspace(pychron), 640, 480)
        src = grayspace(src)
        v = crop(src, *roi)

        di = dict(var=lambda x: variance(x),
                  laplace=lambda x: get_focus_measure(x, 'laplace'),
                  sobel=lambda x: ndsum(
                      generic_gradient_magnitude(x, sobel, mode='nearest')))

        func = di[operator]
        return func(v)

    def image_view(self):
        v = View(
            Item('image',
                 show_label=False,
                 editor=ImageEditor(),
                 width=640,
                 height=480,
                 style='custom'))
        return v

    def traits_view(self):
        v = View(
            HGroup(self._button_factory('autofocus_button', 'autofocus_label'),
                   Item('configure_button', show_label=False),
                   show_border=True,
                   label='Autofocus'))
        return v

    def configure_view(self):
        v = View(Item('parameters', style='custom', show_label=False),
                 handler=ConfigureHandler,
                 buttons=['OK', 'Cancel'],
                 kind='livemodal',
                 title='Configure Autofocus',
                 x=0.80,
                 y=0.05)
        return v

    def _load_source(self):
        src = self.video.get_frame()
        return src
#        if pychron:
#            return Image.new_frame(pychron)
#            self.image.load(pychron)

#        return self.image.source_frame

    def _get_roi(self):
        w = self.parameters.crop_width
        h = self.parameters.crop_height

        cx, cy = self.canvas.get_center_rect_position(w, h)

        #         cw, ch = self.canvas.outer_bounds
        #         print w, h, cw, ch
        #         cx = cw / 2. - w / 2.
        #         cy = ch / 2. - h / 2.
        #         cx = (cw - w) / 2.
        #         cy = (ch - h) / 2.
        #         cx = (640 * self.canvas.scaling - w) / 2
        #         cy = (480 * self.canvas.scaling - h) / 2
        roi = cx, cy, w, h

        return roi

    def _add_focus_area_rect(self, cx, cy, w, h):
        #         pl = self.canvas.padding_left
        #         pb = self.canvas.padding_bottom

        self.canvas.remove_item('croprect')
        self.canvas.add_markup_rect(cx, cy, w, h, identifier='croprect')

    def _autofocus_button_fired(self):
        if not self.autofocusing:
            self.autofocusing = True

            self.passive_focus()
        else:
            self.autofocusing = False
            self._evt_autofocusing.set()
            self.stop_focus()

    def _configure_button_fired(self):
        self._crop_rect_update()
        self.edit_traits(view='configure_view', kind='livemodal')

        self.canvas.remove_item('croprect')
#        try:
#            self.canvas.markupcontainer.pop('croprect')
#        except KeyError:
#            pass

    @on_trait_change('parameters:[_crop_width,_crop_height]')
    def _crop_rect_update(self):
        roi = self._get_roi()
        self._add_focus_area_rect(*roi)

    def _get_autofocus_label(self):
        return 'Autofocus' if not self.autofocusing else 'Stop'

    def _parameters_default(self):
        return self.load_parameter()

    def _autofocusing_changed(self, new):
        if not new:
            self.canvas.remove_item('croprect')
예제 #34
0
class IonOpticsManager(Manager):
    reference_detector = Instance(BaseDetector)
    reference_isotope = Any

    magnet_dac = Range(0.0, 6.0)
    graph = Instance(Graph)
    peak_center_button = Button('Peak Center')
    stop_button = Button('Stop')

    alive = Bool(False)
    spectrometer = Any

    peak_center = Instance(PeakCenter)
    coincidence = Instance(Coincidence)
    peak_center_config = Instance(PeakCenterConfigurer)
    # coincidence_config = Instance(CoincidenceConfig)
    canceled = False

    peak_center_result = None

    _centering_thread = None

    def close(self):
        self.cancel_peak_center()

    def cancel_peak_center(self):
        self.alive = False
        self.canceled = True
        self.peak_center.canceled = True
        self.peak_center.stop()
        self.info('peak center canceled')

    def get_mass(self, isotope_key):
        spec = self.spectrometer
        molweights = spec.molecular_weights
        return molweights[isotope_key]

    def set_mftable(self, name=None):
        """
            if mt is None set to the default mftable located at setupfiles/spectrometer/mftable.csv
        :param mt:
        :return:
        """
        if name and name != os.path.splitext(os.path.basename(paths.mftable))[0]:
            self.spectrometer.use_deflection_correction = False
        else:
            self.spectrometer.use_deflection_correction = True

        self.spectrometer.magnet.set_mftable(name)

    def get_position(self, *args, **kw):
        kw['update_isotopes'] = False
        return self._get_position(*args, **kw)

    def position(self, pos, detector, *args, **kw):
        dac = self._get_position(pos, detector, *args, **kw)
        mag = self.spectrometer.magnet

        self.info('positioning {} ({}) on {}'.format(pos, dac, detector))
        return mag.set_dac(dac)

    def do_coincidence_scan(self, new_thread=True):

        if new_thread:
            t = Thread(name='ion_optics.coincidence', target=self._coincidence)
            t.start()
            self._centering_thread = t

    def setup_coincidence(self):
        pcc = self.coincidence_config
        pcc.dac = self.spectrometer.magnet.dac

        info = pcc.edit_traits()
        if not info.result:
            return

        detector = pcc.detector
        isotope = pcc.isotope
        detectors = [d for d in pcc.additional_detectors]
        # integration_time = pcc.integration_time

        if pcc.use_nominal_dac:
            center_dac = self.get_position(isotope, detector)
        elif pcc.use_current_dac:
            center_dac = self.spectrometer.magnet.dac
        else:
            center_dac = pcc.dac

        # self.spectrometer.save_integration()
        # self.spectrometer.set_integration(integration_time)

        cs = Coincidence(spectrometer=self.spectrometer,
                         center_dac=center_dac,
                         reference_detector=detector,
                         reference_isotope=isotope,
                         additional_detectors=detectors)
        self.coincidence = cs
        return cs

    def get_center_dac(self, det, iso):
        spec = self.spectrometer
        det = spec.get_detector(det)

        molweights = spec.molecular_weights
        mass = molweights[iso]
        dac = spec.magnet.map_mass_to_dac(mass, det.name)

        # correct for deflection
        return spec.correct_dac(det, dac)

    def do_peak_center(self,
                       save=True,
                       confirm_save=False,
                       warn=False,
                       new_thread=True,
                       message='',
                       on_end=None,
                       timeout=None):
        self.debug('doing pc')

        self.canceled = False
        self.alive = True
        self.peak_center_result = None

        args = (save, confirm_save, warn, message, on_end, timeout)
        if new_thread:
            t = Thread(name='ion_optics.peak_center', target=self._peak_center,
                       args=args)
            t.start()
            self._centering_thread = t
            return t
        else:
            self._peak_center(*args)

    def setup_peak_center(self, detector=None, isotope=None,
                          integration_time=1.04,
                          directions='Increase',
                          center_dac=None, plot_panel=None, new=False,
                          standalone_graph=True, name='', show_label=False,
                          window=0.015, step_width=0.0005, min_peak_height=1.0, percent=80,
                          deconvolve=None,
                          use_interpolation=False,
                          interpolation_kind='linear',
                          dac_offset=None, calculate_all_peaks=False,
                          config_name=None,
                          use_configuration_dac=True):

        if deconvolve is None:
            n_peaks, select_peak = 1, 1

        if dac_offset is not None:
            use_dac_offset = True

        spec = self.spectrometer

        spec.save_integration()
        self.debug('setup peak center. detector={}, isotope={}'.format(detector, isotope))

        self._setup_config()

        pcc = None
        if detector is None or isotope is None:
            self.debug('ask user for peak center configuration')

            self.peak_center_config.load(dac=spec.magnet.dac)
            if config_name:
                self.peak_center_config.active_name = config_name

            info = self.peak_center_config.edit_traits()

            if not info.result:
                return
            else:
                pcc = self.peak_center_config.active_item
        elif config_name:
            self.peak_center_config.load(dac=spec.magnet.dac)
            self.peak_center_config.active_name = config_name
            pcc = self.peak_center_config.active_item

        if pcc:
            if not detector:
                detector = pcc.active_detectors

            if not isotope:
                isotope = pcc.isotope

            directions = pcc.directions
            integration_time = pcc.integration_time

            window = pcc.window
            min_peak_height = pcc.min_peak_height
            step_width = pcc.step_width
            percent = pcc.percent

            use_interpolation = pcc.use_interpolation
            interpolation_kind = pcc.interpolation_kind
            n_peaks = pcc.n_peaks
            select_peak = pcc.select_n_peak
            use_dac_offset = pcc.use_dac_offset
            dac_offset = pcc.dac_offset
            calculate_all_peaks = pcc.calculate_all_peaks
            if center_dac is None and use_configuration_dac:
                center_dac = pcc.dac

        spec.set_integration_time(integration_time)
        period = int(integration_time * 1000 * 0.9)

        if not isinstance(detector, (tuple, list)):
            detector = (detector,)

        ref = detector[0]
        ref = self.spectrometer.get_detector(ref)
        self.reference_detector = ref
        self.reference_isotope = isotope

        if center_dac is None:
            center_dac = self.get_center_dac(ref, isotope)

        if len(detector) > 1:
            ad = detector[1:]
        else:
            ad = []

        pc = self.peak_center
        if not pc or new:
            pc = PeakCenter()

        pc.trait_set(center_dac=center_dac,
                     period=period,
                     window=window,
                     percent=percent,
                     min_peak_height=min_peak_height,
                     step_width=step_width,
                     directions=directions,
                     reference_detector=ref,
                     additional_detectors=ad,
                     reference_isotope=isotope,
                     spectrometer=spec,
                     show_label=show_label,
                     use_interpolation=use_interpolation,
                     interpolation_kind=interpolation_kind,
                     n_peaks=n_peaks,
                     select_peak=select_peak,
                     use_dac_offset=use_dac_offset,
                     dac_offset=dac_offset,
                     calculate_all_peaks=calculate_all_peaks)

        self.peak_center = pc
        graph = pc.graph
        graph.name = name
        if plot_panel:
            plot_panel.set_peak_center_graph(graph)
        else:
            graph.close_func = self.close
            if standalone_graph:
                # set graph window attributes
                graph.window_title = 'Peak Center {}({}) @ {:0.3f}'.format(ref, isotope, center_dac)
                graph.window_width = 300
                graph.window_height = 250
                open_view(graph)

        return self.peak_center

    # private
    def _setup_config(self):
        config = self.peak_center_config
        config.detectors = self.spectrometer.detector_names
        keys = self.spectrometer.molecular_weights.keys()
        config.isotopes = sort_isotopes(keys)

    def _get_peak_center_config(self, config_name):
        if config_name is None:
            config_name = 'default'

        config = self.peak_center_config.get(config_name)

        config.detectors = self.spectrometer.detectors_names
        if config.detector_name:
            config.detector = next((di for di in config.detectors if di == config.detector_name), None)

        if not config.detector:
            config.detector = config.detectors[0]

        keys = self.spectrometer.molecular_weights.keys()
        config.isotopes = sort_isotopes(keys)
        return config

    def _timeout_func(self, timeout, evt):
        st = time.time()
        while not evt.is_set():
            if not self.alive:
                break

            if time.time() - st > timeout:
                self.warning('Peak Centering timed out after {}s'.format(timeout))
                self.cancel_peak_center()
                break

            time.sleep(0.01)

    def _peak_center(self, save, confirm_save, warn, message, on_end, timeout):

        pc = self.peak_center
        spec = self.spectrometer
        ref = self.reference_detector
        isotope = self.reference_isotope

        if timeout:
            evt = Event()
            self.timeout_thread = Thread(target=self._timeout_func, args=(timeout, evt))
            self.timeout_thread.start()

        dac_d = pc.get_peak_center()

        self.peak_center_result = dac_d
        if dac_d:
            args = ref, isotope, dac_d
            self.info('new center pos {} ({}) @ {}'.format(*args))

            det = spec.get_detector(ref)

            dac_a = spec.uncorrect_dac(det, dac_d)
            self.info('dac uncorrected for HV and deflection {}'.format(dac_a))
            if save:
                if confirm_save:
                    msg = 'Update Magnet Field Table with new peak center- {} ({}) @ RefDetUnits= {}'.format(*args)
                    save = self.confirmation_dialog(msg)

                if save:
                    spec.magnet.update_field_table(det, isotope, dac_a, message)
                    spec.magnet.set_dac(dac_d)

        elif not self.canceled:
            msg = 'centering failed'
            if warn:
                self.warning_dialog(msg)
            self.warning(msg)

            # needs to be called on the main thread to properly update
            # the menubar actions. alive=False enables IonOptics>Peak Center
        # d = lambda:self.trait_set(alive=False)
        # still necessary with qt? and tasks

        if on_end:
            on_end()

        self.trait_set(alive=False)

        if timeout:
            evt.set()

        self.spectrometer.restore_integration()

    def _get_position(self, pos, detector, use_dac=False, update_isotopes=True):
        """
            pos can be str or float
            "Ar40", "39.962", 39.962

            to set in DAC space set use_dac=True
        """
        if pos == NULL_STR:
            return

        spec = self.spectrometer
        mag = spec.magnet

        if isinstance(detector, str):
            det = spec.get_detector(detector)
        else:
            det = detector

        self.debug('detector {}'.format(det))

        if use_dac:
            dac = pos
        else:
            self.debug('POSITION {} {}'.format(pos, detector))
            if isinstance(pos, str):
                try:
                    pos = float(pos)
                except ValueError:
                    # pos is isotope
                    if update_isotopes:
                        # if the pos is an isotope then update the detectors
                        spec.update_isotopes(pos, detector)
                    pos = self.get_mass(pos)

                mag.mass_change(pos)

            # pos is mass i.e 39.962
            dac = mag.map_mass_to_dac(pos, det.name)

        dac = spec.correct_dac(det, dac)
        return dac

    def _coincidence(self):
        self.coincidence.get_peak_center()
        self.info('coincidence finished')
        self.spectrometer.restore_integration()

    # ===============================================================================
    # handler
    # ===============================================================================
    def _coincidence_config_default(self):
        config = None
        p = os.path.join(paths.hidden_dir, 'coincidence_config.p')
        if os.path.isfile(p):
            try:
                with open(p) as rfile:
                    config = pickle.load(rfile)
                    config.detectors = dets = self.spectrometer.detectors
                    config.detector = next((di for di in dets if di.name == config.detector_name), None)

            except Exception, e:
                print 'coincidence config', e

        if config is None:
            config = CoincidenceConfig()
            config.detectors = self.spectrometer.detectors
            config.detector = config.detectors[0]

        keys = self.spectrometer.molecular_weights.keys()
        config.isotopes = sort_isotopes(keys)

        return config
예제 #35
0
class BaseStageManager(Manager):
    keyboard_focus = Event
    canvas_editor_klass = LaserComponentEditor
    tray_calibration_manager = Instance(TrayCalibrationManager)
    stage_map_klass = LaserStageMap
    stage_map_name = Str
    stage_map_names = List
    stage_map = Instance(BaseStageMap)
    canvas = Instance(MapCanvas)

    # root = Str(paths.map_dir)
    calibrated_position_entry = String(enter_set=True, auto_set=False)

    move_thread = None
    temp_position = None
    temp_hole = None
    root = Str

    # use_modified = Bool(True)  # set true to use modified affine calculation
    def motor_event_hook(self, name, value, *args, **kw):
        pass

    def goto_position(self, pos):
        raise NotImplementedError

    def refresh_stage_map_names(self):
        sms = get_stage_map_names(root=self.root)
        self.stage_map_names = sms

    def load(self):
        self.refresh_stage_map_names()

        psm = self._load_previous_stage_map()
        sms = self.stage_map_names
        sm = None
        if psm in sms:
            sm = psm
        elif sms:
            sm = sms[0]

        if sm:
            self.stage_map_name = ''
            self.stage_map_name = sm

    def kill(self):
        r = super(BaseStageManager, self).kill()
        self._save_stage_map()
        return r

    @property
    def stage_map_path(self):
        return os.path.join(paths.hidden_dir, '{}.stage_map'.format(self.id))

    def canvas_editor_factory(self):
        return self.canvas_editor_klass(keyboard_focus='keyboard_focus')

    def move_to_hole(self, hole, **kw):
        self._move(self._move_to_hole, hole, name='move_to_hole', **kw)

    def get_calibrated_position(self, pos, key=None):
        smap = self.stage_map

        # use a affine transform object to map
        canvas = self.canvas
        ca = canvas.calibration_item

        # check if a calibration applies to this hole
        hole_calibration = get_hole_calibration(smap.name, key)
        if hole_calibration:
            self.debug('Using hole calibration')
            ca = hole_calibration

        if ca:
            rot = ca.rotation
            cpos = ca.center
            scale = ca.scale

            self.debug('Calibration parameters: '
                       'rot={:0.3f}, cpos={} scale={:0.3f}'.format(
                           rot, cpos, scale))
            pos = smap.map_to_calibration(pos, cpos, rot, scale=scale)

        return pos

    def update_axes(self):
        """
        """
        self.info('querying axis positions')
        self._update_axes()

    # private
    def _update_axes(self):
        pass

    def _move_to_hole(self, key, correct_position=True, **kw):
        pass

    def _stop(self):
        pass

    def _move(self, func, pos, name=None, *args, **kw):
        if pos is None:
            return

        if self.move_thread and self.move_thread.isRunning():
            self._stop()

        if name is None:
            name = func.__name__

        self.move_thread = Thread(name='stage.{}'.format(name),
                                  target=func,
                                  args=(pos, ) + args,
                                  kwargs=kw)
        self.move_thread.start()

    def _canvas_factory(self):
        raise NotImplementedError

    def _stage_map_changed_hook(self):
        pass

    # handlers
    def _calibrated_position_entry_changed(self, new):
        self.debug('User entered calibrated position {}'.format(new))
        self.goto_position(new)

    def _stage_map_name_changed(self, new):
        if new:
            self.debug('setting stage map to {}'.format(new))
            root = self.root
            path = os.path.join(root, add_extension(new, '.txt'))
            sm = self.stage_map_klass(file_path=path)

            self.tray_calibration_manager.load_calibration(stage_map=new)

            self.canvas.set_map(sm)
            self.canvas.request_redraw()

            self.stage_map = sm

            self._stage_map_changed_hook()

    # defaults
    def _root_default(self):
        return paths.map_dir

    def _tray_calibration_manager_default(self):
        t = TrayCalibrationManager(parent=self, canvas=self.canvas)
        return t

    def _canvas_default(self):
        return self._canvas_factory()

    def _save_stage_map(self):
        p = self.stage_map_path
        self.info('saving stage_map {} to {}'.format(self.stage_map_name, p))
        with open(p, 'wb') as f:
            pickle.dump(self.stage_map_name, f)

    def _load_previous_stage_map(self):
        p = self.stage_map_path
        if os.path.isfile(p):
            self.info('loading previous stage map from {}'.format(p))
            with open(p, 'rb') as f:
                try:
                    sm = pickle.load(f)
                    if not sm.endswith('.center'):
                        return sm
                except (pickle.PickleError, ValueError):
                    pass
예제 #36
0
class AutoFocusManager(Manager):
    """
        currently uses passive focus techniques
        see

        http://en.wikipedia.org/wiki/Autofocus

    """

    video = Any
    laser_manager = Any
    stage_controller = Any
    canvas = Any
    parameters = Instance(FocusParameters)
    configure_button = Button('configure')

    autofocus_button = Event
    autofocus_label = Property(depends_on='autofocusing')
    autofocusing = Bool

    # threading event for cancel signal
    _evt_autofocusing = None

    image = Instance(Image, ())

    graph = None

    def dump_parameters(self):
        p = os.path.join(paths.hidden_dir, 'autofocus_configure')
        self.info('dumping parameters to {}'.format(p))
        with open(p, 'wb') as f:
            pickle.dump(self.parameters, f)

    def load_parameter(self):
        p = os.path.join(paths.hidden_dir, 'autofocus_configure')
        if os.path.isfile(p):
            with open(p, 'rb') as f:
                try:
                    params = pickle.load(f)
                    self.info('loading parameters from {}'.format(p))

                    if not isinstance(params, FocusParameters):
                        self.info('out of date parameters file. using default')
                        params = FocusParameters()
                    return params

                except Exception as e:
                    print('autofocus load parameter', e)
                    return FocusParameters()
        else:
            return FocusParameters()

    def passive_focus(self, block=False, **kw):

        self._evt_autofocusing = TEvent()
        self._evt_autofocusing.clear()
#        manager = self.laser_manager
        oper = self.parameters.operator
        self.info('passive focus. operator = {}'.format(oper))

        g = self.graph
        if not g:
            g = Graph(plotcontainer_dict=dict(padding=10),
                      window_x=0.70,
                      window_y=20,
                      window_width=325,
                      window_height=325,
                      window_title='Autofocus'
                      )
            self.graph = g

        g.clear()

        g.new_plot(padding=[40, 10, 10, 40],
                   xtitle='Z (mm)',
                   ytitle='Focus Measure ({})'.format(oper)
                   )
        g.new_series()
        g.new_series()

        invoke_in_main_thread(self._open_graph)

        target = self._passive_focus
        self._passive_focus_thread = Thread(name='autofocus', target=target,
                                            args=(self._evt_autofocusing,

                                                  ),
                                            kwargs=kw
                                            )
        self._passive_focus_thread.start()
        if block:
#             while 1:
#                 if not self._passive_focus_thread.isRunning():
#                     break
#                 time.sleep(0.25)
            self._passive_focus_thread.join()

    def _open_graph(self):
        ui = self.graph.edit_traits()
        self.add_window(ui)

    def stop_focus(self):

        if self.stage_controller:
            self.stage_controller.stop()

        self.info('autofocusing stopped by user')

    def _passive_focus(self, stop_signal, set_zoom=True):
        '''
            sweep z looking for max focus measure
            FMgrad= roberts or sobel (sobel removes noise)
            FMvar = intensity variance
        '''

        self.autofocusing = True

        manager = self.laser_manager
        fstart = self.parameters.fstart
        fend = self.parameters.fend
        step_scalar = self.parameters.step_scalar
        zoom = self.parameters.zoom
        operator = self.parameters.operator

        steps = step_scalar * (max(fend, fstart) - min(fend, fstart)) + 1

        prev_zoom = None
        if set_zoom and \
                manager is not None and \
                     zoom:
            motor = manager.get_motor('zoom')
            if motor:
                prev_zoom = motor.data_position
                self.info('setting zoom: {}'.format(zoom))
                manager.set_motor('zoom', zoom, block=True)
                time.sleep(1.5)

        args = self._do_focusing(fstart, fend, steps, operator)

        if manager is not None:
            if prev_zoom is not None:
                self.info('returning to previous zoom: {}'.format(prev_zoom))
                manager.set_motor('zoom', prev_zoom, block=True)

        if args:
            mi, fmi, ma, fma = args

            self.info('''passive focus results:Operator={}
ImageGradmin={} (z={})
ImageGradmax={}, (z={})'''.format(operator, mi, fmi, ma, fma))

            focus_pos = fma
            self.graph.add_vertical_rule(focus_pos)
            self.graph.redraw()
#            self.graph.add_vertical_rule(fma)

            self.info('calculated focus z= {}'.format(focus_pos))

#            if set_z:
            controller = self.stage_controller
            if controller is not None:
                if not stop_signal.isSet():
                    controller.single_axis_move('z', focus_pos, block=True)
                    controller._z_position = focus_pos
                    controller.z_progress = focus_pos

        self.autofocusing = False

    def _cancel_sweep(self, vo):
        if self._evt_autofocusing.isSet():
            # return to original velocity
            self.autofocusing = False
            self._reset_velocity(vo)
            return True

    def _reset_velocity(self, vo):
        if self.stage_controller:
            pdict = dict(velocity=vo, key='z')
            self.stage_controller.set_single_axis_motion_parameters(pdict=pdict)

    def _do_focusing(self, start, end, steps, operator):
        screen_roi = self._get_roi()
        self._add_focus_area_rect(*screen_roi)

        src = self._load_source()
        src = asarray(src)
        h, w, _d = src.shape

        cx = w / 2.
        cy = h / 2.

        cw = self.parameters.crop_width
        ch = self.parameters.crop_height

        roi = cx, cy, cw, ch

        '''
            start the z in motion and take pictures as you go
            query stage_controller to get current z
        '''

        self.info('focus sweep start={} end={}'.format(start, end))
        # move to start position
        controller = self.stage_controller
        if controller:
            vo = controller.axes['z'].velocity
            if self._cancel_sweep(vo):
                return
            self.graph.set_x_limits(min(start, end), max(start, end), pad=2)
            # sweep 1 and velocity 1
            self._do_sweep(start, end, velocity=self.parameters.velocity_scalar1)
            fms, focussteps = self._collect_focus_measures(operator, roi)
            if not (fms and focussteps):
                return

            # reached end of sweep
            # calculate a nominal focal point
            args = self._calculate_nominal_focal_point(fms, focussteps)
            if not args:
                return
            nfocal = args[3]

            nwin = self.parameters.negative_window
            pwin = self.parameters.positive_window

            if self._cancel_sweep(vo):
                return
            nstart, nend = max(0, nfocal - nwin), nfocal + pwin
#            mi = min(min(nstart, nend), min(start, end))
#            ma = max(max(nstart, nend), max(start, end))
#            self.graph.set_x_limits(mi, ma, pad=2)
            time.sleep(1)
            # do a slow tight sweep around the nominal focal point
            self._do_sweep(nstart, nend, velocity=self.parameters.velocity_scalar2)
            fms, focussteps = self._collect_focus_measures(operator, roi, series=1)

            self._reset_velocity(vo)

        else:
            focussteps = linspace(0, 10, 11)
            fms = -(focussteps - 5) ** 2 + 10 + random.random(11)

        self.info('frames analyzed {}'.format(len(fms)))

#        self.canvas.markupcontainer.pop('croprect')
        return self._calculate_nominal_focal_point(fms, focussteps)

    def _do_sweep(self, start, end, velocity=None):
        controller = self.stage_controller
        controller.single_axis_move('z', start, block=True)
#         time.sleep(0.1)
        # explicitly check for motion
#        controller.block(axis='z')

        if velocity:
            vo = controller.axes['z'].velocity

            controller.set_single_axis_motion_parameters(pdict=dict(velocity=vo * velocity,
                                                    key='z'))

        self.info('starting sweep from {}'.format(controller.z_progress))
        # pause before moving to end
        time.sleep(0.25)
        controller.single_axis_move('z', end, update=100, immediate=True)

    def _collect_focus_measures(self, operator, roi, series=0):
        controller = self.stage_controller
        focussteps = []
        fms = []
        if controller.timer:
            p = controller.timer.get_interval()
            self.debug('controller timer period {}'.format(p))
            pz = controller.z_progress

            while 1:
                src = self._load_source()
                x = controller.z_progress
                if x != pz:
                    y = self._calculate_focus_measure(src, operator, roi)
                    self.graph.add_datum((x, y), series=series)

                    focussteps.append(x)
                    fms.append(y)

                    pz = x

                if not (controller.timer.isActive() and \
                        not self._evt_autofocusing.isSet()):
                    break
                time.sleep(p)

            self.debug('sweep finished')


        return fms, focussteps

    def _calculate_nominal_focal_point(self, fms, focussteps):
        if fms:
            sfms = smooth(fms)
            if sfms is not None:

                self.graph.new_series(focussteps, sfms)
                self.graph.redraw()

                fmi = focussteps[argmin(sfms)]
                fma = focussteps[argmax(sfms)]

                mi = min(sfms)
                ma = max(sfms)

                return mi, fmi, ma, fma

    def _calculate_focus_measure(self, src, operator, roi):
        '''
            see
            IMPLEMENTATION OF A PASSIVE AUTOMATIC FOCUSING ALGORITHM
            FOR DIGITAL STILL CAMERA
            DOI 10.1109/30.468047
            and
            http://cybertron.cg.tu-berlin.de/pdci10/frankencam/#autofocus
        '''

        # need to resize to 640,480. this is the space the roi is in
#        s = resize(grayspace(pychron), 640, 480)
        src = grayspace(src)
        v = crop(src, *roi)

        di = dict(var=lambda x:variance(x),
                  laplace=lambda x: get_focus_measure(x, 'laplace'),
                  sobel=lambda x: ndsum(generic_gradient_magnitude(x, sobel, mode='nearest'))
                  )

        func = di[operator]
        return func(v)

    def image_view(self):
        v = View(Item('image', show_label=False, editor=ImageEditor(),
                      width=640,
                      height=480,
                       style='custom'))
        return v

    def traits_view(self):
        v = View(
               HGroup(self._button_factory('autofocus_button', 'autofocus_label'),
                      Item('configure_button', show_label=False),
                      show_border=True,
                      label='Autofocus'
                      )
               )
        return v

    def configure_view(self):
        v = View(Item('parameters', style='custom', show_label=False),
               handler=ConfigureHandler,
               buttons=['OK', 'Cancel'],
               kind='livemodal',
               title='Configure Autofocus',
               x=0.80,
               y=0.05
               )
        return v

    def _load_source(self):
        src = self.video.get_frame()
        return src
#        if pychron:
#            return Image.new_frame(pychron)
#            self.image.load(pychron)

#        return self.image.source_frame

    def _get_roi(self):
        w = self.parameters.crop_width
        h = self.parameters.crop_height

        cx, cy = self.canvas.get_center_rect_position(w, h)


#         cw, ch = self.canvas.outer_bounds
#         print w, h, cw, ch
#         cx = cw / 2. - w / 2.
#         cy = ch / 2. - h / 2.
#         cx = (cw - w) / 2.
#         cy = (ch - h) / 2.
#         cx = (640 * self.canvas.scaling - w) / 2
#         cy = (480 * self.canvas.scaling - h) / 2
        roi = cx, cy, w, h

        return roi

    def _add_focus_area_rect(self, cx, cy, w, h):
#         pl = self.canvas.padding_left
#         pb = self.canvas.padding_bottom

        self.canvas.remove_item('croprect')
        self.canvas.add_markup_rect(cx, cy, w, h, identifier='croprect')

    def _autofocus_button_fired(self):
        if not self.autofocusing:
            self.autofocusing = True

            self.passive_focus()
        else:
            self.autofocusing = False
            self._evt_autofocusing.set()
            self.stop_focus()

    def _configure_button_fired(self):
        self._crop_rect_update()
        self.edit_traits(view='configure_view', kind='livemodal')

        self.canvas.remove_item('croprect')
#        try:
#            self.canvas.markupcontainer.pop('croprect')
#        except KeyError:
#            pass

    @on_trait_change('parameters:[_crop_width,_crop_height]')
    def _crop_rect_update(self):
        roi = self._get_roi()
        self._add_focus_area_rect(*roi)

    def _get_autofocus_label(self):
        return 'Autofocus' if not self.autofocusing else 'Stop'


    def _parameters_default(self):
        return self.load_parameter()

    def _autofocusing_changed(self, new):
        if not new:
            self.canvas.remove_item('croprect')
예제 #37
0
 def _position_changed(self):
     if self.position is not None:
         t = Thread(target=self._move_to_position, args=(self.position, self.use_autocenter))
         t.start()
         self._position_thread = t
 def _position_changed(self):
     if self.position is not None:
         t = Thread(target=self._move_to_position,
                    args=(self.position, self.use_autocenter))
         t.start()
         self._position_thread = t
예제 #39
0
class StageManager(BaseStageManager):
    """
    """

    stage_controller_klass = String('Newport')

    stage_controller = Instance(MotionController)
    points_programmer = Instance(PointsProgrammer)
    motion_controller_manager = Instance(MotionControllerManager)
    # canvas = Instance(LaserTrayCanvas)

    simulation = DelegatesTo('stage_controller')

    # stage_map_klass = StageMap
    # _stage_map = Instance(StageMap)
    # stage_map = Property(depends_on='_stage_map')
    # stage_maps = Property(depends_on='_stage_maps')

    # _stage_maps = List
    # ===========================================================================
    # buttons
    # ===========================================================================
    home = Button('home')
    home_option = String('Home All')
    home_options = List

    manual_override_position_button = Event

    ejoystick = Event
    joystick_label = String('Enable Joystick')
    joystick = Bool(False)
    joystick_timer = None

    back_button = Button
    stop_button = Button('Stop')

    _default_z = 0
    _cached_position = None
    _cached_current_hole = None
    _homing = False

    def __init__(self, *args, **kw):
        """

        """
        super(StageManager, self).__init__(*args, **kw)
        self.stage_controller = self._stage_controller_factory()

    def measure_grain_polygon(self):
        pass

    def stop_measure_grain_polygon(self):
        pass

    def shutdown(self):
        self._save_stage_map()

    def create_device(self, *args, **kw):
        dev = super(StageManager, self).create_device(*args, **kw)
        dev.parent = self
        return dev

    def goto_position(self, v, **kw):
        if XY_REGEX[0].match(v):
            self._move_to_calibrated_position(v)
        elif POINT_REGEX.match(v) or TRANSECT_REGEX[0].match(v):
            self.move_to_point(v)

        else:
            self.move_to_hole(v, user_entry=True, **kw)

    def get_current_position(self):
        if self.stage_controller:
            x = self.stage_controller.x
            y = self.stage_controller.y
            return x, y

    def get_current_hole(self):
        pos = self.get_current_position()
        if self.stage_map:
            if distance_threshold(pos, self._cached_position, self.stage_map.g_dimension / 4):
                h = self.get_calibrated_hole(*pos, tol=self.stage_map.g_dimension / 2.)
                if h is not None:
                    self._cached_current_hole = h
                    self._cached_position = pos

        return self._cached_current_hole

    def is_auto_correcting(self):
        return False

    def bind_preferences(self, pref_id):
        bind_preference(self.canvas, 'show_grids', '{}.show_grids'.format(pref_id))
        self.canvas.change_grid_visibility()

        bind_preference(self.canvas, 'show_laser_position', '{}.show_laser_position'.format(pref_id))
        bind_preference(self.canvas, 'show_desired_position', '{}.show_desired_position'.format(pref_id))
        bind_preference(self.canvas, 'desired_position_color', '{}.desired_position_color'.format(pref_id),
                        factory=ColorPreferenceBinding)
        #        bind_preference(self.canvas, 'render_map', '{}.render_map'.format(pref_id))
        #

        bind_preference(self.canvas, 'crosshairs_kind', '{}.crosshairs_kind'.format(pref_id))
        for tag in ('', 'aux_'):
            for key in ('line_width', 'color', 'radius', 'offsetx', 'offsety'):
                key = '{}crosshairs_{}'.format(tag, key)
                factory = ColorPreferenceBinding if key.endswith('color') else None
                pref = '{}.{}'.format(pref_id, key)
                bind_preference(self.canvas, key, pref, factory=factory)

            # bind_preference(self.canvas, '{}crosshairs_line_width', '{}.{}crosshairs_line_width'.format(pref_id))
            # bind_preference(self.canvas, 'crosshairs_color',
            #                 '{}.crosshairs_color'.format(pref_id),
            #                 factory=ColorPreferenceBinding)
            # bind_preference(self.canvas, 'crosshairs_radius', '{}.crosshairs_radius'.format(pref_id))
            # bind_preference(self.canvas, 'crosshairs_offsetx', '{}.crosshairs_offsetx'.format(pref_id))
            # bind_preference(self.canvas, 'crosshairs_offsety', '{}.crosshairs_offsety'.format(pref_id))

        bind_preference(self.canvas, 'show_hole_label', '{}.show_hole_label'.format(pref_id))
        bind_preference(self.canvas, 'hole_label_color', '{}.hole_label_color'.format(pref_id))
        bind_preference(self.canvas, 'hole_label_size', '{}.hole_label_size'.format(pref_id))
        self.canvas.handle_hole_label_size(self.canvas.hole_label_size)

        bind_preference(self.canvas, 'scaling', '{}.scaling'.format(pref_id))
        bind_preference(self.canvas, 'show_bounds_rect',
                        '{}.show_bounds_rect'.format(pref_id))

        self.canvas.request_redraw()

    def load(self):
        super(StageManager, self).load()
        config = self.get_configuration()
        if config:
            self._default_z = self.config_get(config, 'Defaults', 'z', default=13, cast='float')

        self.points_programmer.load_stage_map(self.stage_map_name)

        # load the calibration file
        # should have calibration files for each stage map
        self.tray_calibration_manager.load_calibration()

    def finish_loading(self):
        self.initialize_stage()

    def initialize_stage(self):
        self.update_axes()
        axes = self.stage_controller.axes
        self.home_options = ['Home All', 'XY'] + sorted([axes[a].name.upper() for a in axes])
        self.canvas.parent = self

    def save_calibration(self, name):
        self.tray_calibration_manager.save_calibration(name=name)

        # def add_stage_map(self, v):
        # sm = self.stage_map_klass(file_path=v)
        # psm = self._get_stage_map_by_name(sm.name)
        # if psm:
        #     self._stage_maps.remove(psm)

        # self._stage_maps.append(sm)

    def accept_point(self):
        self.points_programmer.accept_point()

    def set_stage_map(self, v):
        return self._set_stage_map(v)

    def single_axis_move(self, *args, **kw):
        return self.stage_controller.single_axis_move(*args, **kw)

    def linear_move(self, x, y, use_calibration=True, check_moving=False, abort_if_moving=False, **kw):

        if check_moving:
            if self.moving():
                self.warning('MotionController already in motion')
                if abort_if_moving:
                    self.warning('Move to {},{} aborted'.format(x, y))
                    return
                else:
                    self.stop()
                    self.debug('Motion stopped. moving to {},{}'.format(x, y))

        pos = (x, y)
        if use_calibration:
            pos = self.get_calibrated_position(pos)
            f = lambda x: '{:0.5f},{:0.5f}'.format(*x)
            self.debug('%%%%%%%%%%%%%%%%% mapped {} to {}'.format(f((x, y)), f(pos)))

        self.stage_controller.linear_move(*pos, **kw)

    def move_to_hole(self, hole, **kw):
        if self.stage_map.check_valid_hole(hole, **kw):
            self._move(self._move_to_hole, hole, name='move_to_hole', **kw)

    def move_to_point(self, pt):
        self._move(self._move_to_point, pt, name='move_to_point')

    def move_polyline(self, line):
        self._move(self._move_to_line, line, name='move_to_line')

    def move_polygon(self, poly):
        self._move(self._move_polygon, poly, name='move_polygon')

    def drill_point(self, pt):
        self._move(self._drill_point, pt, name='drill_point')

    def set_x(self, value, **kw):
        return self.stage_controller.single_axis_move('x', value, **kw)

    def set_y(self, value, **kw):
        return self.stage_controller.single_axis_move('y', value, **kw)

    def set_z(self, value, **kw):
        return self.stage_controller.single_axis_move('z', value, **kw)

    def set_xy(self, x, y, **kw):
        hole = self._get_hole_by_position(x, y)
        if hole:
            self.move_to_hole(hole)
        # self._set_hole(hole.id)
        # self.move_to_hole(hole.id)
        #            self._set_hole(hole.id)
        else:
            return self.linear_move(x, y, **kw)

    def get_hole(self, name):
        if self.stage_map:
            return self.stage_map.get_hole(name)

    def move_to_load_position(self):
        """
        """
        x, y, z = self.stage_controller.get_load_position()
        self.info('moving to load position, x={}, y={}, z={}'.format(x, y, z))

        self.stage_controller.linear_move(x, y, grouped_move=False, block=False)

        self.stage_controller.set_z(z)
        self.stage_controller.block()

    def stop(self, ax_key=None, verbose=False):
        self._stop(ax_key, verbose)

    def relative_move(self, *args, **kw):
        self.stage_controller.relative_move(*args, **kw)

    def key_released(self):
        sc = self.stage_controller
        sc.add_consumable((sc.update_axes, tuple()))

    def moving(self, force_query=False, **kw):
        moving = False
        if force_query:
            moving = self.stage_controller.moving(**kw)
        elif self.stage_controller.timer is not None:
            moving = self.stage_controller.timer.isActive()

        return moving

    def get_brightness(self, **kw):
        return 0

    def get_scores(self, **kw):
        return 0, 0

    def define_home(self, **kw):
        self.stage_controller.define_home(**kw)

    def get_z(self):
        return self.stage_controller._z_position

    def get_uncalibrated_xy(self, pos=None):
        if pos is None:
            pos = (self.stage_controller.x, self.stage_controller.y)
            if self.stage_controller.xy_swapped():
                pos = pos[1], pos[0]

        canvas = self.canvas
        ca = canvas.calibration_item
        if ca:
            pos = self.stage_map.map_to_uncalibration(pos,
                                                      ca.center,
                                                      ca.rotation,
                                                      ca.scale)

        return pos

    def get_calibrated_xy(self):
        pos = (self.stage_controller.x, self.stage_controller.y)
        if self.stage_controller.xy_swapped():
            pos = pos[1], pos[0]

        pos = self.canvas.map_offset_position(pos)
        return self.get_calibrated_position(pos)

    def get_calibrated_hole(self, x, y, tol):
        ca = self.canvas.calibration_item
        if ca is not None:
            smap = self.stage_map

            xx, yy = smap.map_to_uncalibration((x, y), ca.center, ca.rotation)
            return next((hole for hole in smap.sample_holes
                         if abs(hole.x - xx) < tol and abs(hole.y - yy) < tol), None)

    def get_hole_xy(self, key):
        pos = self.stage_map.get_hole_pos(key)
        # map the position to calibrated space
        pos = self.get_calibrated_position(pos)
        return pos

    def finish_move_to_hole(self, user_entry):
        pass

    # private
    def _update_axes(self):
        if self.stage_controller:
            self.stage_controller.update_axes()

    def _home(self):
        """
        """
        if self._homing:
            return

        self._homing = True

        if self.home_option == 'Home All':

            msg = 'homing all motors'
            homed = ['x', 'y', 'z']
            home_kwargs = dict(x=-25, y=-25, z=50)

        elif self.home_option == 'XY':
            msg = 'homing x,y'
            homed = ['x', 'y']
            home_kwargs = dict(x=-25, y=-25)
        else:
            #            define_home =
            msg = 'homing {}'.format(self.home_option)
            home_kwargs = {self.home_option: -25 if self.home_option in ['X', 'Y'] else 50}
            homed = [self.home_option.lower().strip()]

        self.info(msg)

        # if define_home:
        self.stage_controller.set_home_position(**home_kwargs)

        self.stage_controller.home(homed)

        # explicitly block
        #        self.stage_controller.block()

        if 'z' in homed and 'z' in self.stage_controller.axes:
            # will be a positive limit error in z
            #            self.stage_controller.read_error()

            time.sleep(1)
            self.info('setting z to nominal position. {} mm '.format(self._default_z))
            self.stage_controller.single_axis_move('z', self._default_z, block=True)
            self.stage_controller._z_position = self._default_z

        if self.home_option in ['XY', 'Home All']:
            time.sleep(0.25)

            # the stage controller should  think x and y are at -25,-25
            self.stage_controller._x_position = -25
            self.stage_controller._y_position = -25

            self.info('moving to center')
            try:
                self.stage_controller.linear_move(0, 0, block=True, sign_correct=False)
            except TargetPositionError as e:
                self.warning_dialog('Move Failed. {}'.format(e))

        self._homing = False

    def _get_hole_by_position(self, x, y):
        if self.stage_map:
            return self.stage_map._get_hole_by_position(x, y)

    def _get_hole_by_name(self, key):
        sm = self.stage_map
        return sm.get_hole(key)

    # ===============================================================================
    # special move
    # ===============================================================================
    def _stop(self, ax_key=None, verbose=False):
        self.stage_controller.stop(ax_key=ax_key, verbose=verbose)
        if self.parent.pattern_executor:
            self.parent.pattern_executor.stop()

    # def _move(self, func, pos, name=None, *args, **kw):
    #     if pos is None:
    #         return
    #
    #     if self.move_thread and self.move_thread.isRunning():
    #         self.stage_controller.stop()
    #     if name is None:
    #         name = func.func_name
    #
    #     self.move_thread = Thread(name='stage.{}'.format(name),
    #                               target=func, args=(pos,) + args, kwargs=kw)
    #     self.move_thread.start()

    def _drill_point(self, pt):
        zend = pt.zend
        vel = pt.velocity

        # assume already at zstart
        st = time.time()
        self.info('start drilling. move to {}. velocity={}'.format(zend, vel))
        self.set_z(zend, velocity=vel, block=True)
        et = time.time() - st

        self.info('drilling complete. drilled for {}s'.format(et))

    def _move_polygon(self, pts, velocity=5,
                      offset=50,
                      use_outline=True,
                      find_min=False,
                      scan_size=None,
                      use_move=True,
                      use_convex_hull=True,
                      motors=None,
                      verbose=True,
                      start_callback=None, end_callback=None):
        """
            motors is a dict of motor_name:value pairs
        """
        if pts is None:
            return

        if not isinstance(pts, list):
            velocity = pts.velocity
            use_convex_hull = pts.use_convex_hull
            if scan_size is None:
                scan_size = pts.scan_size
            use_outline = pts.use_outline
            offset = pts.offset
            find_min = pts.find_min
            pts = [dict(xy=(pi.x, pi.y), z=pi.z, ) for pi in pts.points]

        # set motors
        if motors is not None:
            for k, v in motors.values():
                '''
                    motor will not set if it has been locked using set_motor_lock or
                    remotely using SetMotorLock
                '''
                if use_move:
                    self.parent.set_motor(k, v, block=True)

        xy = [pi['xy'] for pi in pts]
        n = 1000
        if scan_size is None:
            scan_size = n / 2

        # convert points to um
        pts = array(xy)
        pts *= n
        pts = asarray(pts, dtype=int)
        '''
            sort clockwise ensures consistent offset behavior
            a polygon gain have a inner or outer sense depending on order of vertices

            always use sort_clockwise prior to any polygon manipulation
        '''
        pts = sort_clockwise(pts, pts)

        sc = self.stage_controller
        sc.set_program_mode('absolute')
        # do smooth transitions between points
        sc.set_smooth_transitions(True)

        if use_convex_hull:
            pts = convex_hull(pts)

        if use_outline:
            # calculate new polygon
            offset_pts = polygon_offset(pts, -offset)
            offset_pts = array(offset_pts, dtype=int)
            # polygon offset used 3D vectors.
            # trim to only x,y
            pts = offset_pts[:, (0, 1)]

            # trace perimeter
            if use_move:
                p0 = xy[0]
                self.linear_move(p0[0], p0[1], mode='absolute', block=True)

                sc.timer = sc.timer_factory()

                if start_callback is not None:
                    start_callback()

                # buf=[]
                for pi in xy[1:]:
                    self.linear_move(pi[0], pi[1],
                                     velocity=velocity,
                                     mode='absolute', set_stage=False)

                # finish at first point
                self.linear_move(p0[0], p0[1],
                                 velocity=velocity,
                                 mode='absolute', set_stage=False)

                sc.block()
                self.info('polygon perimeter trace complete')
                '''
                    have the oppurtunity here to turn off laser and change parameters i.e mask
                '''
        if use_move:
            # calculate and step thru scan lines
            self._raster(pts, velocity,
                         step=scan_size,
                         scale=n,
                         find_min=find_min,
                         start_callback=start_callback, end_callback=end_callback,
                         verbose=verbose)

        sc.set_program_mode('relative')
        if end_callback is not None:
            end_callback()
        self.info('polygon raster complete')

    def _raster(self, points, velocity,
                step=500,
                scale=1000,
                find_min=False,
                start_callback=None, end_callback=None, verbose=False):

        from pychron.core.geometry.scan_line import raster

        lines = raster(points, step=step, find_min=find_min)

        # initialize variables
        cnt = 0
        direction = 1
        flip = False
        lasing = False
        sc = self.stage_controller

        if verbose:
            self.info('start raster')

        # print lines
        # loop thru each scan line
        #        for yi, xs in lines[::skip]:
        for yi, xs in lines:
            if direction == -1:
                xs = list(reversed(xs))

            # convert odd numbers lists to even
            n = len(xs)
            if n % 2 != 0:
                xs = sorted(list(set(xs)))

            # traverse each x-intersection pair
            n = len(xs)
            for i in range(0, n, 2):
                if len(xs) <= 1:
                    continue

                x1, x2, yy = xs[i] / scale, xs[i + 1] / scale, yi / scale
                if abs(x1 - x2) > 1e-10:
                    if not lasing:
                        if verbose:
                            self.info('fast to {} {},{}'.format(cnt, x1, yy))

                        self.linear_move(x1, yy,
                                         mode='absolute', set_stage=False,
                                         block=True)
                        if start_callback is not None:
                            start_callback()

                        lasing = True
                    else:
                        if verbose:
                            self.info('slow to {} {},{}'.format(cnt, x1, yy))

                        sc.timer = sc.timer_factory()
                        self.linear_move(x1, yy,
                                         mode='absolute', set_stage=False,
                                         velocity=velocity)

                    if verbose:
                        self.info('move to {}a {},{}'.format(cnt, x2, yy))

                        #                if n > 2 and not i * 2 >= n:
                    # line this scan line has more then 1 segment turn off laser at end of segment
                    if i + 2 < n and not xs[i + 1] == xs[i + 2]:
                        self.linear_move(x2, yy, velocity=velocity,
                                         mode='absolute', set_stage=False,
                                         block=True)
                        self.info('wait for move complete')
                        if end_callback is not None:
                            end_callback()

                        lasing = False
                    else:
                        self.linear_move(x2, yy, velocity=velocity,
                                         mode='absolute', set_stage=False,
                                         )
                    cnt += 1
                    flip = True
                else:
                    flip = False

            if flip:
                direction *= -1

        sc.block()
        if verbose:
            self.info('end raster')

    def _move_polyline(self, pts, start_callback=None, end_callback=None):
        if not isinstance(pts, list):
            segs = pts.velocity_segments
            segs = segs[:1] + segs
            pts = [dict(xy=(pi.x, pi.y), z=pi.z, velocity=vi) for vi, pi in
                   zip(segs, pts.points)]

        sc = self.stage_controller
        self.linear_move(pts[0]['xy'][0], pts[0]['xy'][1],
                         update_hole=False,
                         use_calibration=False,
                         block=True)
        sc.set_z(pts[0]['z'], block=True)

        cpos = dict()
        # set motors
        for motor in ('mask', 'attenuator'):
            if motor in pts[0]:
                self.parent.set_motor(motor, pts[0][motor])
                cpos[motor] = pts[0][motor]

        sc.set_program_mode('absolute')
        sc.timer = sc.timer_factory()
        if start_callback:
            start_callback()

        npts = pts[1:]
        setmotors = dict()
        for i, di in enumerate(npts):
            xi, yi, zi, vi = di['xy'][0], di['xy'][1], di['z'], di['velocity']
            sc.set_z(zi)

            block = False
            for motor in ('mask', 'attenuator'):
                # fix next step sets motor should block
                if i + 1 < len(npts):
                    dii = npts[i + 1]
                    if motor in dii and dii[motor] != cpos[motor]:
                        m = self.parent.get_motor(motor)
                        if not m.locked:
                            block = True
                            setmotors[motor] = dii[motor]

            self.linear_move(xi, yi, velocity=vi,
                             block=block,
                             mode='absolute',  # use absolute mode because commands are queued
                             set_stage=False)
            if block:
                if end_callback:
                    end_callback()

                for k, v in setmotors.items():
                    self.parent.set_motor(k, v, block=True)

                if start_callback:
                    start_callback()

        # wait until motion complete
        sc.block()
        if end_callback:
            end_callback()

        sc.set_program_mode('relative')

    #        if start and smooth:
    #            sc.execute_command_buffer()
    #            sc.end_command_buffer()

    #    def start_enqueued(self):
    #        sc = self.stage_controller
    #        sc.execute_command_buffer()
    #        sc.end_command_buffer()

    def _move_to_point(self, pt):
        self.debug('move to point={}'.format(pt))
        if isinstance(pt, str):
            pt = self.canvas.get_point(pt)

        self.debug('move to point canvas pt={}'.format(pt))
        if pt is not None:
            pos = pt.x, pt.y

            self.info('Move to point {}: {:0.5f},{:0.5f},{:0.5f}'.format(pt.identifier,
                                                                         pt.x, pt.y, pt.z))
            self.stage_controller.linear_move(block=True, *pos)

            if hasattr(pt, 'z'):
                self.stage_controller.set_z(pt.z, block=True)

            self.debug('Not setting motors for pt')
            # self.parent.set_motors_for_point(pt)

            self._move_to_point_hook()

        self.info('Move complete')
        self.update_axes()

    def _move_to_hole(self, key, correct_position=True, user_entry=False, autocenter_only=False):
        self.info('Move to hole {} type={}'.format(key, str(type(key))))

        autocentered_position = False
        if not autocenter_only:
            self.temp_hole = key
            self.temp_position = self.stage_map.get_hole_pos(key)
            pos = self.stage_map.get_corrected_hole_pos(key)
            self.info('position {}'.format(pos))
            if pos is not None:
                if abs(pos[0]) < 1e-6:
                    pos = self.stage_map.get_hole_pos(key)
                    # map the position to calibrated space
                    pos = self.get_calibrated_position(pos, key=key)
                else:
                    # check if this is an interpolated position
                    # if so probably want to do an autocentering routine
                    hole = self.stage_map.get_hole(key)
                    if hole.interpolated:
                        self.info('using an interpolated value')
                    else:
                        self.info('using previously calculated corrected position')
                        autocentered_position = True
                try:
                    self.stage_controller.linear_move(block=True, source='move_to_hole {}'.format(pos),
                                                      raise_zero_displacement=True, *pos)
                except TargetPositionError as e:
                    self.warning('(001) Move to {} failed'.format(pos))
                    self.parent.emergency_shutoff(str(e))
                    return
                except ZeroDisplacementException:
                    correct_position = False
        try:
            self._move_to_hole_hook(key, correct_position,
                                autocentered_position)
        except TargetPositionError as e:
            self.warning('(002) Move failed. {}'.format(e))
            self.parent.emergency_shutoff(str(e))
            return

        self.finish_move_to_hole(user_entry)
        self.info('Move complete')

    def _move_to_hole_hook(self, *args):
        pass

    def _move_to_point_hook(self):
        pass

    # ===============================================================================
    # Property Get / Set
    # ===============================================================================
    def _set_stage_map(self, v):
        if v in self.stage_map_names:
            for root, ext in ((self.root, '.txt'), (paths.user_points_dir, '.yaml')):
                p = os.path.join(root, add_extension(v, ext))
                if os.path.isfile(p):
                    self.info('setting stage map to {}'.format(v))
                    sm = self.stage_map_klass(file_path=p)
                    self.canvas.set_map(sm)
                    self.tray_calibration_manager.load_calibration(stage_map=v)
                    self.points_programmer.load_stage_map(sm)

                    return True
        else:
            self.warning('No stage map named "{}"'.format(v))
            return False

    def _get_calibrate_stage_label(self):
        if self._calibration_state == 'set_center':
            r = 'Locate Center'
        elif self._calibration_state == 'set_right':
            r = 'Locate Right'
        else:
            r = 'Calibrate Stage'
        return r

    def _get_program_points_label(self):
        return 'Program Points' if not self.canvas.markup else 'End Program'

    def _validate_hole(self, v):
        nv = None
        try:
            if v.strip():
                nv = int(v)

        except TypeError:
            self.warning('invalid hole {}'.format(v))

        return nv

    #    def _get_calibrated_position_entry(self):
    #        return self._calibrated_position
    #
    #    def _set_calibrated_position_entry(self, v):
    #        self._calibrated_position = v
    #        if XY_REGEX.match(v):
    #            self._move_to_calibrated_position(v)
    #        else:
    #            self.move_to_hole(v)

    def _move_to_calibrated_position(self, pos):
        try:
            args = csv_to_floats(pos)
        except ValueError:
            self.warning('invalid calibrated position "{}". Could not convert to floats'.format(pos))
            return

        if len(args) == 2:
            x, y = args
            self.linear_move(x, y, use_calibration=True, block=False)
        else:
            self.warning('invalid calibrated position. incorrect number of arguments "{}"'.format(args))

    def _set_point(self, v):
        if self.canvas.calibrate:
            self.warning_dialog('Cannot move while calibrating')
            return

        if self.canvas.markup:
            self.warning_dialog('Cannot move while adding/editing points')
            return

        if (self.move_thread is None or not self.move_thread.isRunning()) and v is not self._point:
            pos = self.canvas.get_item('point', int(v) - 1)
            if pos is not None:
                self._point = v
                self.move_thread = Thread(target=self._move_to_point, args=(pos,))
                self.move_thread.start()
            else:
                err = 'Invalid point {}'.format(v)
                self.warning(err)
                return err

    def _get_point(self):
        return self._point

    # ===============================================================================
    # handlers
    # ===============================================================================
    def _manual_override_position_button_fired(self):
        sm = self.stage_map
        pos = self.calibrated_position_entry
        hole = self.stage_map.get_hole(pos)
        if hole is not None:
            x, y = self.stage_controller.x, self.stage_controller.y
            sm.set_hole_correction(pos, x, y)
            sm.dump_correction_file()
            self.info('updated {} correction file. Saved {}:  {},{}'.format(sm.name, pos, x, y))

    def _stop_button_fired(self):
        self._stop()

    def _ejoystick_fired(self):
        self.joystick = not self.joystick
        if self.joystick:
            self.stage_controller.enable_joystick()
            self.joystick_label = 'Disable Joystick'

            self.joystick_timer = self.timer_factory(func=self._joystick_inprogress_update)
        else:
            if self.joystick_timer is not None:
                self.joystick_timer.Stop()

            self.stage_controller.disable_joystick()
            self.joystick_label = 'Enable Joystick'

    def _home_fired(self):
        """
        """
        t = Thread(name='stage.home', target=self._home)
        t.start()
        # need to store a reference to thread so it is not garbage collected
        self.move_thread = t
        # do_later(self._home)

    def _test_fired(self):
        #        self.do_pattern('testpattern')
        self.do_pattern('pattern003')

    # ===============================================================================
    # factories
    # ===============================================================================
    def _motion_configure_factory(self, **kw):
        return MotionControllerManager(motion_controller=self.stage_controller,
                                       application=self.application,
                                       **kw)

    def _stage_controller_factory(self):
        if self.stage_controller_klass == 'Newport':
            from pychron.hardware.newport.newport_motion_controller import NewportMotionController

            factory = NewportMotionController
        elif self.stage_controller_klass == 'Aerotech':
            from pychron.hardware.aerotech.aerotech_motion_controller import AerotechMotionController

            factory = AerotechMotionController

        m = factory(name='{}controller'.format(self.name),
                    configuration_name='stage_controller',
                    configuration_dir_name=self.configuration_dir_name,
                    parent=self)
        return m

    def _canvas_factory(self):
        """
        """
        w = 640 / 2.0 / 23.2
        h = 0.75 * w

        l = LaserTrayCanvas(stage_manager=self,
                            padding=[30, 5, 5, 30],
                            map=self.stage_map,
                            view_x_range=[-w, w],
                            view_y_range=[-h, h])
        return l

    # ===============================================================================
    # defaults
    # ===============================================================================

    def _motion_controller_manager_default(self):
        return self._motion_configure_factory()

    def _title_default(self):
        return '%s Stage Manager' % self.name[:-5].capitalize()

    def _points_programmer_default(self):
        pp = PointsProgrammer(canvas=self.canvas,
                              stage_map_klass=self.stage_map_klass,
                              stage_manager=self)
        pp.on_trait_change(self.move_to_point, 'point')
        pp.on_trait_change(self.move_polygon, 'polygon')
        pp.on_trait_change(self.move_polyline, 'line')
        return pp
예제 #40
0
class NMGRLFurnaceManager(BaseFurnaceManager):
    funnel = Instance(NMGRLFunnel)
    loader_logic = Instance(LoaderLogic)
    magnets = Instance(NMGRLMagnetDumper)
    setpoint_readback_min = Float(0)
    setpoint_readback_max = Float(1600.0)

    graph = Instance(StreamGraph)
    update_period = Int(2)

    dumper_canvas = Instance(DumperCanvas)
    _alive = False
    _guide_overlay = None
    _dumper_thread = None
    mode = 'normal'

    def activate(self):
        # pref_id = 'pychron.furnace'
        # bind_preference(self, 'update_period', '{}.update_period'.format(pref_id))

        self._start_update()

    def prepare_destroy(self):
        self._stop_update()
        self.loader_logic.manager = None

    def dump_sample(self):
        self.debug('dump sample')

        if self._dumper_thread is None:
            self._dumper_thread = Thread(name='DumpSample', target=self._dump_sample)
            self._dumper_thread.start()

    def is_dump_complete(self):
        ret = self._dumper_thread is None
        return ret

    def actuate_magnets(self):
        self.debug('actuate magnets')
        if self.loader_logic.check('AM'):
            self.magnet.open()
            # wait for actuate magnets
            pass
        else:
            self.warning('actuate magnets not enabled')

    def lower_funnel(self):
        self.debug('lower funnel')
        if self.loader_logic.check('FD'):
            self.funnel.set_value(self.funnel.down_position)

            # todo: update canvas state

            return True
        else:
            self.warning('lowering funnel not enabled')

    def raise_funnel(self):
        self.debug('raise funnel')
        if self.loader_logic.check('FU'):
            self.funnel.set_value(self.funnel.up_position)

            # todo: update canvas state

            return True
        else:
            self.warning('raising funnel not enabled')

    def set_setpoint(self, v):
        if self.controller:
            # print self.controller, self.controller._cdevice
            self.controller.set_setpoint(v)
            if not self._guide_overlay:
                self._guide_overlay = self.graph.add_horizontal_rule(v)

            self._guide_overlay.visible = bool(v)
            self._guide_overlay.value = v

            # ymi, yma = self.graph.get_y_limits()
            d = self.graph.get_data(axis=1)
            self.graph.set_y_limits(min_=0, max_=max(d.max(), v * 1.1))

            self.graph.redraw()

    def read_setpoint(self, update=False):
        v = 0
        if self.controller:
            force = update and not self.controller.is_scanning()
            v = self.controller.read_setpoint(force=force)

        try:
            self.setpoint_readback = v
            return v
        except TraitError:
            pass

    # canvas
    def set_software_lock(self, name, lock):
        if self.switch_manager is not None:
            if lock:
                self.switch_manager.lock(name)
            else:
                self.switch_manager.unlock(name)

    def open_valve(self, name, **kw):
        if not self._open_logic(name):
            self.debug('logic failed')
            return False, False

        if self.switch_manager:
            return self.switch_manager.open_switch(name, **kw)

    def close_valve(self, name, **kw):
        if not self._close_logic(name):
            self.debug('logic failed')
            return False, False

        if self.switch_manager:
            return self.switch_manager.close_switch(name, **kw)

    def set_selected_explanation_item(self, item):
        pass

    # logic
    def get_switch_state(self, name):
        if self.switch_manager:
            return self.switch_manager.get_state_by_name(name, force=True)

    def get_flag_state(self, flag):
        self.debug('get_flag_state {}'.format(flag))

        if flag in ('no_motion', 'no_dump', 'funnel_up', 'funnel_down'):
            return getattr(self, flag)()
        return False

    def funnel_up(self):
        return self.funnel.in_up_position()

    def funnel_down(self):
        return self.funnel.in_down_position()

    def no_motion(self):
        v = not self.stage_manager.in_motion()
        self.debug('no motion {}'.format(v))
        return v

    def no_dump(self):
        v = not self.magnets.dump_in_progress()
        self.debug('no dump {}'.format(v))
        return v

    # private
    def _open_logic(self, name):
        """
        check the logic rules to see if its ok to open "name"

        return True if ok
        """
        return self.loader_logic.open(name)

    def _close_logic(self, name):
        """
        check the logic rules to see if its ok to close "name"

        return True if ok

        """
        return self.loader_logic.close(name)

    def _stop_update(self):
        self._alive = False

    def _start_update(self):
        self._alive = True

        self.graph = g = StreamGraph()
        # g.plotcontainer.padding_top = 5
        # g.plotcontainer.padding_right = 5
        g.new_plot(xtitle='Time (s)', ytitle='Temp. (C)', padding_top=5, padding_right=5)
        g.set_scan_width(600)
        g.new_series()

        self._update_readback()

    def _update_readback(self):
        v = self.read_setpoint(update=True)
        self.graph.record(v, track_y=False)
        if self._alive:
            do_after(self.update_period * 1000, self._update_readback)

    def _dump_sample(self):
        """
        1. open gate valve
        2. open shutters
        3. lower funnel
        4. actuate magnets
        5. raise funnel
        6. close shutters
        7. close gate valve
        :return:
        """
        self.debug('dump sample started')
        for line in self._load_dump_script():
            self.debug(line)
            self._execute_script_line(line)

            self.stage_manager.set_sample_dumped()
            self._dumper_thread = None

    def _load_dump_script(self):
        p = os.path.join(paths.device_dir, 'furnace', 'dump_sequence.txt')
        return pathtolist(p)

    def _execute_script_line(self, line):
        if ' ' in line:
            cmd, args = line.split(' ')
        else:
            cmd, args = line, None

        if cmd == 'sleep':
            time.sleep(float(args))
        elif cmd == 'open':
            self.switch_manager.open_switch(args)
            self.dumper_canvas.set_item_state(args, True)
        elif cmd == 'close':
            self.switch_manager.close_switch(args)
            self.dumper_canvas.set_item_state(args, False)
        elif cmd == 'lower_funnel':
            self.lower_funnel()
            self.dumper_canvas.set_item_state(args, True)
        elif cmd == 'raise_funnel':
            self.raise_funnel()
            self.dumper_canvas.set_item_state(args, False)
        elif cmd == 'actuate_magnets':
            self.actuate_magnets()

        self.dumper_canvas.request_redraw()

    # handlers
    def _setpoint_changed(self, new):
        self.set_setpoint(new)

    def _stage_manager_default(self):
        sm = NMGRLFurnaceStageManager(stage_manager_id='nmgrl.furnace.stage_map')
        return sm

    def _switch_manager_default(self):
        sm = SwitchManager()
        return sm

    def _dumper_canvas_default(self):
        dc = DumperCanvas(manager=self)

        pathname = os.path.join(paths.canvas2D_dir, 'dumper.xml')
        configpath = os.path.join(paths.canvas2D_dir, 'dumper_config.xml')
        valvepath = os.path.join(paths.extraction_line_dir, 'valves.xml')
        dc.load_canvas_file(pathname, configpath, valvepath, dc)
        return dc

    def _funnel_default(self):
        f = NMGRLFunnel(name='funnel', configuration_dir_name='furnace')
        return f

    def _loader_logic_default(self):
        l = LoaderLogic(manager=self)
        l.load_config()

        return l

    def _magnets_default(self):
        m = NMGRLMagnetDumper(name='magnets', configuration_dir_name='furnace')
        return m
예제 #41
0
class Scanner(Loggable):
    """
        Scanner is a base class for displaying a scan of device data

        ScanableDevices has this ability built in but more complicated scans are
        best done here. ScanableDevice scan is best used from continuous long term recording
        of a single or multiple values
    """

    _graph = Instance(StreamStackedGraph)
    manager = Instance(ILaserManager)

    data_manager = Instance(CSVDataManager, ())
    '''
        list of callables. should return a signal value for graphing
    '''
    functions = List
    static_values = List

    scan_period = Int  # ms
    stop_event = Event
    setpoint = Float(enter_set=True, auto_set=False)
    control_path = File

    _warned = False
    _scanning = Bool
    _directory = None
    _base_frame_name = None

    def new_static_value(self, name, *args, **kw):
        self.static_values.append((name, None))
        if args:
            self.set_static_value(name, *args, **kw)

    def set_static_value(self, name_or_idx, value, plotid=None):
        """
            if the plotid is not None add a horizontal guide at value
        """
        if isinstance(name_or_idx, str):
            name_or_idx = next((i for i, (e, a) in enumerate(self.static_values)), None)

        if name_or_idx is not None:
            name = self.static_values[name_or_idx][0]
            self.static_values[name_or_idx] = (name, value)

        if plotid is not None:
            self._graph.add_horizontal_rule(value, plotid=plotid)

    def setup(self, directory=None, base_frame_name=None):
        self._directory = directory
        self._base_frame_name = base_frame_name

    def new_function(self, function, name=None):
        if name is None:
            name = function.func_name

        #        g = self.graph
        #        func = self.functions
        #        kw = dict(ytitle=name,)
        #        n = len(func)
        #        if n == 0:
        #            kw['xtitle'] = 'Time'

        self.functions.append((function, name))

    def make_graph(self):
        g = StreamStackedGraph(window_x=50,
                               window_y=100)

        for i, (_, name) in enumerate(self.functions):
            kw = dict(ytitle=name, )
            if i == 0:
                kw['xtitle'] = 'Time'

            g.new_plot(
                data_limit=3000,
                **kw)
            g.new_series(plotid=i)
        self._graph = g
        return g

    def stop(self):
        self._timer.Stop()
        self._scanning = False
        self.stop_event = True
        self.info('scan stopped')
        if self.manager:
            self._stop_hook()
            self.manager.disable_device()

    def _stop_hook(self):
        pass

    def execute(self):

        if self._scanning:
            self.stop()
        else:
            self.data_manager.new_frame(directory=self._directory,
                                        base_frame_name=self._base_frame_name)
            # write metadata if available
            self._write_metadata()

            # make header row
            header = ['t'] + \
                     self._make_func_names() + \
                     [n for n, _ in self.static_values]
            self.data_manager.write_to_frame(header)

            self._scanning = True
            if self.manager:
                if self.manager.enable_device():
                    self._scanning = True

        return self._scanning

    def do_scan(self):
        self._starttime = time.time()
        self._execute()

    def _execute(self):

        yd = self._read_control_path()

        if yd is None:
            sp = 1000
        else:
            sp = yd['period']

        # starts automatically
        self.debug('scan starting')
        self._timer = Timer(sp, self._scan)

        self.info('scan started')

        #            yd = self._read_control_path()
        if yd is not None:
            # start a control thread
            self._control_thread = Thread(target=self._control,
                                          args=(yd,)
            )
            self._control_thread.start()
            self.info('control started')
        #        if self.manager:
        #            if self.manager.enable_device():
        #
        #                # starts automatically
        #                self.debug('scan starting')
        #                self._timer = Timer(sp, self._scan)
        #
        #                self.info('scan started')
        #                self._scanning = True
        #    #            yd = self._read_control_path()
        #                if yd is not None:
        #                    # start a control thread
        #                    self._control_thread = Thread(target=self._control,
        #                                                  args=(yd,)
        #                                                  )
        #                    self._control_thread.start()
        #                    self.info('control started')
        #            else:
        #                self.warning('Could not enable device')
        #        else:
        #            self.warning('no manager available')

    def _control(self, ydict):
        self.start_control_hook(ydict)

        #        if self.manager:
        #            if self.manager.temperature_controller:
        #                self.manager.temperature_controller.enable_tru_tune = True

        start_delay = ydict['start_delay']
        end_delay = ydict['end_delay']
        setpoints = ydict['setpoints']
        print setpoints
        self.set_static_value('Setpoint', 0)
        time.sleep(start_delay)
        for args in setpoints:
            t=args[0]
            if self._scanning:
                self.setpoint = t
                self._set_power_hook(t)

                self.set_static_value('Setpoint', t, plotid=0)
                self._maintain_setpoint(t, *args[1:])

        if self._scanning:
            self.setpoint = 0
            self.set_static_value('Setpoint', 0)
            self._set_power_hook(0)
            # if self.manager:
                # self.manager.set_laser_temperature(0)

            time.sleep(end_delay)
            self.stop()

        self.end_control_hook(self._scanning)

    def _set_power_hook(self, t):
        if self.manager:
            self.manager.set_laser_temperature(t)

    def _maintain_setpoint(self, t, d, *args):
        self.info('setting setpoint to {} for {}s'.format(t, d))
        st = time.time()
        while time.time() - st < d and self._scanning:
            time.sleep(1)

    def start_control_hook(self, *args, **kw):
        pass

    def end_control_hook(self, ok):
        pass

    def _make_func_names(self):
        return [name for _, name in self.functions]

    def _write_metadata(self):
        pass

    def _scan(self):
        functions = self.functions
        data = []
        record = self._graph.record
        #        record = self.graph.record

        x = time.time() - self._starttime
        for i, (func, _) in enumerate(functions):
            try:
                if hasattr(func, 'next'):
                    func = func.next

                v = float(func())
                data.append(v)

                # do_after no longer necessary with Qt
                record(v, plotid=i, x=x, do_after=None)
            except Exception, e:
                print 'exception', e

        sv = []
        for _, v in self.static_values:
            if v is None:
                v = ''
            sv.append(v)

        data = [x] + data + sv
        self.data_manager.write_to_frame(data)
예제 #42
0
    def do_coincidence_scan(self, new_thread=True):

        if new_thread:
            t = Thread(name='ion_optics.coincidence', target=self._coincidence)
            t.start()
            self._centering_thread = t
예제 #43
0
class NMGRLFurnaceManager(BaseFurnaceManager):
    funnel = Instance(NMGRLFunnel)
    loader_logic = Instance(LoaderLogic)
    magnets = Instance(NMGRLMagnetDumper)
    setpoint_readback_min = Float(0)
    setpoint_readback_max = Float(1600.0)

    graph = Instance(StreamGraph)
    update_period = Int(2)

    dumper_canvas = Instance(DumperCanvas)
    _alive = False
    _guide_overlay = None
    _dumper_thread = None
    mode = 'normal'

    def activate(self):
        # pref_id = 'pychron.furnace'
        # bind_preference(self, 'update_period', '{}.update_period'.format(pref_id))

        self._start_update()

    def prepare_destroy(self):
        self._stop_update()
        self.loader_logic.manager = None

    def dump_sample(self):
        self.debug('dump sample')

        if self._dumper_thread is None:
            self._dumper_thread = Thread(name='DumpSample',
                                         target=self._dump_sample)
            self._dumper_thread.start()

    def is_dump_complete(self):
        ret = self._dumper_thread is None
        return ret

    def actuate_magnets(self):
        self.debug('actuate magnets')
        if self.loader_logic.check('AM'):
            self.magnet.open()
            # wait for actuate magnets
            pass
        else:
            self.warning('actuate magnets not enabled')

    def lower_funnel(self):
        self.debug('lower funnel')
        if self.loader_logic.check('FD'):
            self.funnel.set_value(self.funnel.down_position)

            # todo: update canvas state

            return True
        else:
            self.warning('lowering funnel not enabled')

    def raise_funnel(self):
        self.debug('raise funnel')
        if self.loader_logic.check('FU'):
            self.funnel.set_value(self.funnel.up_position)

            # todo: update canvas state

            return True
        else:
            self.warning('raising funnel not enabled')

    def set_setpoint(self, v):
        if self.controller:
            # print self.controller, self.controller._cdevice
            self.controller.set_setpoint(v)
            if not self._guide_overlay:
                self._guide_overlay = self.graph.add_horizontal_rule(v)

            self._guide_overlay.visible = bool(v)
            self._guide_overlay.value = v

            # ymi, yma = self.graph.get_y_limits()
            d = self.graph.get_data(axis=1)
            self.graph.set_y_limits(min_=0, max_=max(d.max(), v * 1.1))

            self.graph.redraw()

    def read_setpoint(self, update=False):
        v = 0
        if self.controller:
            force = update and not self.controller.is_scanning()
            v = self.controller.read_setpoint(force=force)

        try:
            self.setpoint_readback = v
            return v
        except TraitError:
            pass

    # canvas
    def set_software_lock(self, name, lock):
        if self.switch_manager is not None:
            if lock:
                self.switch_manager.lock(name)
            else:
                self.switch_manager.unlock(name)

    def open_valve(self, name, **kw):
        if not self._open_logic(name):
            self.debug('logic failed')
            return False, False

        if self.switch_manager:
            return self.switch_manager.open_switch(name, **kw)

    def close_valve(self, name, **kw):
        if not self._close_logic(name):
            self.debug('logic failed')
            return False, False

        if self.switch_manager:
            return self.switch_manager.close_switch(name, **kw)

    def set_selected_explanation_item(self, item):
        pass

    # logic
    def get_switch_state(self, name):
        if self.switch_manager:
            return self.switch_manager.get_state_by_name(name, force=True)

    def get_flag_state(self, flag):
        self.debug('get_flag_state {}'.format(flag))

        if flag in ('no_motion', 'no_dump', 'funnel_up', 'funnel_down'):
            return getattr(self, flag)()
        return False

    def funnel_up(self):
        return self.funnel.in_up_position()

    def funnel_down(self):
        return self.funnel.in_down_position()

    def no_motion(self):
        v = not self.stage_manager.in_motion()
        self.debug('no motion {}'.format(v))
        return v

    def no_dump(self):
        v = not self.magnets.dump_in_progress()
        self.debug('no dump {}'.format(v))
        return v

    # private
    def _open_logic(self, name):
        """
        check the logic rules to see if its ok to open "name"

        return True if ok
        """
        return self.loader_logic.open(name)

    def _close_logic(self, name):
        """
        check the logic rules to see if its ok to close "name"

        return True if ok

        """
        return self.loader_logic.close(name)

    def _stop_update(self):
        self._alive = False

    def _start_update(self):
        self._alive = True

        self.graph = g = StreamGraph()
        # g.plotcontainer.padding_top = 5
        # g.plotcontainer.padding_right = 5
        g.new_plot(xtitle='Time (s)',
                   ytitle='Temp. (C)',
                   padding_top=5,
                   padding_right=5)
        g.set_scan_width(600)
        g.new_series()

        self._update_readback()

    def _update_readback(self):
        v = self.read_setpoint(update=True)
        self.graph.record(v, track_y=False)
        if self._alive:
            do_after(self.update_period * 1000, self._update_readback)

    def _dump_sample(self):
        """
        1. open gate valve
        2. open shutters
        3. lower funnel
        4. actuate magnets
        5. raise funnel
        6. close shutters
        7. close gate valve
        :return:
        """
        self.debug('dump sample started')
        for line in self._load_dump_script():
            self.debug(line)
            self._execute_script_line(line)

            self.stage_manager.set_sample_dumped()
            self._dumper_thread = None

    def _load_dump_script(self):
        p = os.path.join(paths.device_dir, 'furnace', 'dump_sequence.txt')
        return pathtolist(p)

    def _execute_script_line(self, line):
        if ' ' in line:
            cmd, args = line.split(' ')
        else:
            cmd, args = line, None

        if cmd == 'sleep':
            time.sleep(float(args))
        elif cmd == 'open':
            self.switch_manager.open_switch(args)
            self.dumper_canvas.set_item_state(args, True)
        elif cmd == 'close':
            self.switch_manager.close_switch(args)
            self.dumper_canvas.set_item_state(args, False)
        elif cmd == 'lower_funnel':
            self.lower_funnel()
            self.dumper_canvas.set_item_state(args, True)
        elif cmd == 'raise_funnel':
            self.raise_funnel()
            self.dumper_canvas.set_item_state(args, False)
        elif cmd == 'actuate_magnets':
            self.actuate_magnets()

        self.dumper_canvas.request_redraw()

    # handlers
    def _setpoint_changed(self, new):
        self.set_setpoint(new)

    def _stage_manager_default(self):
        sm = NMGRLFurnaceStageManager(
            stage_manager_id='nmgrl.furnace.stage_map')
        return sm

    def _switch_manager_default(self):
        sm = SwitchManager()
        return sm

    def _dumper_canvas_default(self):
        dc = DumperCanvas(manager=self)

        pathname = os.path.join(paths.canvas2D_dir, 'dumper.xml')
        configpath = os.path.join(paths.canvas2D_dir, 'dumper_config.xml')
        valvepath = os.path.join(paths.extraction_line_dir, 'valves.xml')
        dc.load_canvas_file(pathname, configpath, valvepath, dc)
        return dc

    def _funnel_default(self):
        f = NMGRLFunnel(name='funnel', configuration_dir_name='furnace')
        return f

    def _loader_logic_default(self):
        l = LoaderLogic(manager=self)
        l.load_config()

        return l

    def _magnets_default(self):
        m = NMGRLMagnetDumper(name='magnets', configuration_dir_name='furnace')
        return m
예제 #44
0
class IonOpticsManager(Manager):
    reference_detector = Instance(BaseDetector)
    reference_isotope = Any

    magnet_dac = Range(0.0, 6.0)
    graph = Instance(Graph)
    peak_center_button = Button('Peak Center')
    stop_button = Button('Stop')

    alive = Bool(False)
    spectrometer = Any

    peak_center = Instance(PeakCenter)
    coincidence = Instance(Coincidence)
    peak_center_config = Instance(PeakCenterConfigurer)
    # coincidence_config = Instance(CoincidenceConfig)
    canceled = False

    peak_center_result = None

    _centering_thread = None

    def close(self):
        self.cancel_peak_center()

    def cancel_peak_center(self):
        self.alive = False
        self.canceled = True
        self.peak_center.canceled = True
        self.peak_center.stop()
        self.info('peak center canceled')

    def get_mass(self, isotope_key):
        spec = self.spectrometer
        molweights = spec.molecular_weights
        return molweights[isotope_key]

    def set_mftable(self, name=None):
        """
            if mt is None set to the default mftable located at setupfiles/spectrometer/mftable.csv
        :param mt:
        :return:
        """
        if name and name != os.path.splitext(os.path.basename(
                paths.mftable))[0]:
            self.spectrometer.use_deflection_correction = False
        else:
            self.spectrometer.use_deflection_correction = True

        self.spectrometer.magnet.set_mftable(name)

    def get_position(self, *args, **kw):
        kw['update_isotopes'] = False
        return self._get_position(*args, **kw)

    def position(self, pos, detector, *args, **kw):
        dac = self._get_position(pos, detector, *args, **kw)
        mag = self.spectrometer.magnet

        self.info('positioning {} ({}) on {}'.format(pos, dac, detector))
        return mag.set_dac(dac)

    def do_coincidence_scan(self, new_thread=True):

        if new_thread:
            t = Thread(name='ion_optics.coincidence', target=self._coincidence)
            t.start()
            self._centering_thread = t

    def setup_coincidence(self):
        pcc = self.coincidence_config
        pcc.dac = self.spectrometer.magnet.dac

        info = pcc.edit_traits()
        if not info.result:
            return

        detector = pcc.detector
        isotope = pcc.isotope
        detectors = [d for d in pcc.additional_detectors]
        # integration_time = pcc.integration_time

        if pcc.use_nominal_dac:
            center_dac = self.get_position(isotope, detector)
        elif pcc.use_current_dac:
            center_dac = self.spectrometer.magnet.dac
        else:
            center_dac = pcc.dac

        # self.spectrometer.save_integration()
        # self.spectrometer.set_integration(integration_time)

        cs = Coincidence(spectrometer=self.spectrometer,
                         center_dac=center_dac,
                         reference_detector=detector,
                         reference_isotope=isotope,
                         additional_detectors=detectors)
        self.coincidence = cs
        return cs

    def get_center_dac(self, det, iso):
        spec = self.spectrometer
        det = spec.get_detector(det)

        molweights = spec.molecular_weights
        mass = molweights[iso]
        dac = spec.magnet.map_mass_to_dac(mass, det.name)

        # correct for deflection
        return spec.correct_dac(det, dac)

    def do_peak_center(self,
                       save=True,
                       confirm_save=False,
                       warn=False,
                       new_thread=True,
                       message='',
                       on_end=None,
                       timeout=None):
        self.debug('doing pc')

        self.canceled = False
        self.alive = True
        self.peak_center_result = None

        args = (save, confirm_save, warn, message, on_end, timeout)
        if new_thread:
            t = Thread(name='ion_optics.peak_center',
                       target=self._peak_center,
                       args=args)
            t.start()
            self._centering_thread = t
            return t
        else:
            self._peak_center(*args)

    def setup_peak_center(self,
                          detector=None,
                          isotope=None,
                          integration_time=1.04,
                          directions='Increase',
                          center_dac=None,
                          plot_panel=None,
                          new=False,
                          standalone_graph=True,
                          name='',
                          show_label=False,
                          window=0.015,
                          step_width=0.0005,
                          min_peak_height=1.0,
                          percent=80,
                          deconvolve=None,
                          use_interpolation=False,
                          interpolation_kind='linear',
                          dac_offset=None,
                          calculate_all_peaks=False,
                          config_name=None,
                          use_configuration_dac=True):

        if deconvolve is None:
            n_peaks, select_peak = 1, 1

        if dac_offset is not None:
            use_dac_offset = True

        spec = self.spectrometer

        spec.save_integration()
        self.debug('setup peak center. detector={}, isotope={}'.format(
            detector, isotope))

        self._setup_config()

        pcc = None
        if detector is None or isotope is None:
            self.debug('ask user for peak center configuration')

            self.peak_center_config.load(dac=spec.magnet.dac)
            if config_name:
                self.peak_center_config.active_name = config_name

            info = self.peak_center_config.edit_traits()

            if not info.result:
                return
            else:
                pcc = self.peak_center_config.active_item
        elif config_name:
            self.peak_center_config.load(dac=spec.magnet.dac)
            self.peak_center_config.active_name = config_name
            pcc = self.peak_center_config.active_item

        if pcc:
            if not detector:
                detector = pcc.active_detectors

            if not isotope:
                isotope = pcc.isotope

            directions = pcc.directions
            integration_time = pcc.integration_time

            window = pcc.window
            min_peak_height = pcc.min_peak_height
            step_width = pcc.step_width
            percent = pcc.percent

            use_interpolation = pcc.use_interpolation
            interpolation_kind = pcc.interpolation_kind
            n_peaks = pcc.n_peaks
            select_peak = pcc.select_n_peak
            use_dac_offset = pcc.use_dac_offset
            dac_offset = pcc.dac_offset
            calculate_all_peaks = pcc.calculate_all_peaks
            if center_dac is None and use_configuration_dac:
                center_dac = pcc.dac

        spec.set_integration_time(integration_time)
        period = int(integration_time * 1000 * 0.9)

        if not isinstance(detector, (tuple, list)):
            detector = (detector, )

        ref = detector[0]
        ref = self.spectrometer.get_detector(ref)
        self.reference_detector = ref
        self.reference_isotope = isotope

        if center_dac is None:
            center_dac = self.get_center_dac(ref, isotope)

        if len(detector) > 1:
            ad = detector[1:]
        else:
            ad = []

        pc = self.peak_center
        if not pc or new:
            pc = PeakCenter()

        pc.trait_set(center_dac=center_dac,
                     period=period,
                     window=window,
                     percent=percent,
                     min_peak_height=min_peak_height,
                     step_width=step_width,
                     directions=directions,
                     reference_detector=ref,
                     additional_detectors=ad,
                     reference_isotope=isotope,
                     spectrometer=spec,
                     show_label=show_label,
                     use_interpolation=use_interpolation,
                     interpolation_kind=interpolation_kind,
                     n_peaks=n_peaks,
                     select_peak=select_peak,
                     use_dac_offset=use_dac_offset,
                     dac_offset=dac_offset,
                     calculate_all_peaks=calculate_all_peaks)

        self.peak_center = pc
        graph = pc.graph
        graph.name = name
        if plot_panel:
            plot_panel.set_peak_center_graph(graph)
        else:
            graph.close_func = self.close
            if standalone_graph:
                # set graph window attributes
                graph.window_title = 'Peak Center {}({}) @ {:0.3f}'.format(
                    ref, isotope, center_dac)
                graph.window_width = 300
                graph.window_height = 250
                open_view(graph)

        return self.peak_center

    # private
    def _setup_config(self):
        config = self.peak_center_config
        config.detectors = self.spectrometer.detector_names
        keys = self.spectrometer.molecular_weights.keys()
        config.isotopes = sort_isotopes(keys)

    def _get_peak_center_config(self, config_name):
        if config_name is None:
            config_name = 'default'

        config = self.peak_center_config.get(config_name)

        config.detectors = self.spectrometer.detectors_names
        if config.detector_name:
            config.detector = next(
                (di for di in config.detectors if di == config.detector_name),
                None)

        if not config.detector:
            config.detector = config.detectors[0]

        keys = self.spectrometer.molecular_weights.keys()
        config.isotopes = sort_isotopes(keys)
        return config

    def _timeout_func(self, timeout, evt):
        st = time.time()
        while not evt.is_set():
            if not self.alive:
                break

            if time.time() - st > timeout:
                self.warning(
                    'Peak Centering timed out after {}s'.format(timeout))
                self.cancel_peak_center()
                break

            time.sleep(0.01)

    def _peak_center(self, save, confirm_save, warn, message, on_end, timeout):

        pc = self.peak_center
        spec = self.spectrometer
        ref = self.reference_detector
        isotope = self.reference_isotope

        if timeout:
            evt = Event()
            self.timeout_thread = Thread(target=self._timeout_func,
                                         args=(timeout, evt))
            self.timeout_thread.start()

        dac_d = pc.get_peak_center()

        self.peak_center_result = dac_d
        if dac_d:
            args = ref, isotope, dac_d
            self.info('new center pos {} ({}) @ {}'.format(*args))

            det = spec.get_detector(ref)

            dac_a = spec.uncorrect_dac(det, dac_d)
            self.info('dac uncorrected for HV and deflection {}'.format(dac_a))
            if save:
                if confirm_save:
                    msg = 'Update Magnet Field Table with new peak center- {} ({}) @ RefDetUnits= {}'.format(
                        *args)
                    save = self.confirmation_dialog(msg)

                if save:
                    spec.magnet.update_field_table(det, isotope, dac_a,
                                                   message)
                    spec.magnet.set_dac(dac_d)

        elif not self.canceled:
            msg = 'centering failed'
            if warn:
                self.warning_dialog(msg)
            self.warning(msg)

            # needs to be called on the main thread to properly update
            # the menubar actions. alive=False enables IonOptics>Peak Center
        # d = lambda:self.trait_set(alive=False)
        # still necessary with qt? and tasks

        if on_end:
            on_end()

        self.trait_set(alive=False)

        if timeout:
            evt.set()

        self.spectrometer.restore_integration()

    def _get_position(self,
                      pos,
                      detector,
                      use_dac=False,
                      update_isotopes=True):
        """
            pos can be str or float
            "Ar40", "39.962", 39.962

            to set in DAC space set use_dac=True
        """
        if pos == NULL_STR:
            return

        spec = self.spectrometer
        mag = spec.magnet

        if isinstance(detector, str):
            det = spec.get_detector(detector)
        else:
            det = detector

        self.debug('detector {}'.format(det))

        if use_dac:
            dac = pos
        else:
            self.debug('POSITION {} {}'.format(pos, detector))
            if isinstance(pos, str):
                try:
                    pos = float(pos)
                except ValueError:
                    # pos is isotope
                    if update_isotopes:
                        # if the pos is an isotope then update the detectors
                        spec.update_isotopes(pos, detector)
                    pos = self.get_mass(pos)

                mag.mass_change(pos)

            # pos is mass i.e 39.962
            dac = mag.map_mass_to_dac(pos, det.name)

        dac = spec.correct_dac(det, dac)
        return dac

    def _coincidence(self):
        self.coincidence.get_peak_center()
        self.info('coincidence finished')
        self.spectrometer.restore_integration()

    # ===============================================================================
    # handler
    # ===============================================================================
    def _coincidence_config_default(self):
        config = None
        p = os.path.join(paths.hidden_dir, 'coincidence_config.p')
        if os.path.isfile(p):
            try:
                with open(p) as rfile:
                    config = pickle.load(rfile)
                    config.detectors = dets = self.spectrometer.detectors
                    config.detector = next(
                        (di for di in dets if di.name == config.detector_name),
                        None)

            except Exception, e:
                print 'coincidence config', e

        if config is None:
            config = CoincidenceConfig()
            config.detectors = self.spectrometer.detectors
            config.detector = config.detectors[0]

        keys = self.spectrometer.molecular_weights.keys()
        config.isotopes = sort_isotopes(keys)

        return config
예제 #45
0
class FusionsLaserManager(LaserManager):
    """
    """

    laser_controller = Instance(FusionsLogicBoard)
    fiber_light = Instance(FiberLight)
    #    optics_view = Instance(OpticsView)

    #    beam = DelegatesTo('laser_controller')
    #    beammin = DelegatesTo('laser_controller')
    #    beammax = DelegatesTo('laser_controller')
    #    update_beam = DelegatesTo('laser_controller')
    #    beam_enabled = DelegatesTo('laser_controller')
    # #    beam_enabled = Bool(True)
    #
    #    zoom = DelegatesTo('laser_controller')
    #    zoommin = DelegatesTo('laser_controller')
    #    zoommax = DelegatesTo('laser_controller')
    #    update_zoom = DelegatesTo('laser_controller')
    #    execute_button = DelegatesTo('laser_script_executor')
    #    execute_label = DelegatesTo('laser_script_executor')

    pointer = Event
    pointer_state = Bool(False)
    pointer_label = Property(depends_on='pointer_state')

    step_heat_manager = None

    lens_configuration = Str('gaussian')
    lens_configuration_dict = Dict
    lens_configuration_names = List

    power_timer = None
    brightness_timer = None

    power_graph = None
    _prev_power = 0
    record_brightness = Bool
    #     recording_zoom = Float

    #     record = Event
    #     record_label = Property(depends_on='_recording_power_state')
    _recording_power_state = Bool(False)

    simulation = DelegatesTo('laser_controller')

    data_manager = None
    _data_manager_lock = None

    _current_rid = None

    #    brightness_meter = Instance(BrightnessPIDManager)

    chiller = Any

    motor_event = Event

    _degas_thread = None

    @on_trait_change('laser_controller:refresh_canvas')
    def refresh_canvas(self):
        if self.stage_manager:
            self.stage_manager.canvas.request_redraw()

            # ===============================================================================
            #   IExtractionDevice interface
            # ===============================================================================

    def extract(self, power, **kw):
        self.enable_laser()
        self.set_laser_power(power, **kw)

    def end_extract(self):
        self.disable_laser()
        self.stop_pattern()

    def open_motor_configure(self):
        self.laser_controller.open_motor_configure()

    #     def _record_fired(self):
    #         if self._recording_power_state:
    #             save = self.db_save_dialog()
    #             self.stop_power_recording(delay=0, save=save)
    #         else:
    #             t = Thread(name='fusions.power_record',
    #                        target=self.start_power_recording, args=('Manual',))
    #             t.start()

    #        self._recording_power_state = not self._recording_power_state

    def bind_preferences(self, pref_id):
        self.debug('binding preferences')
        super(FusionsLaserManager, self).bind_preferences(pref_id)
        bind_preference(self, 'recording_zoom',
                        '{}.recording_zoom'.format(pref_id))
        bind_preference(self, 'record_brightness',
                        '{}.record_brightness'.format(pref_id))
        self.debug('preferences bound')

    def set_light(self, value):
        self.set_light_state(value > 0)
        self.set_light_intensity(value)

    def set_light_state(self, state):
        if state:
            self.fiber_light.power_off()
        else:
            self.fiber_light.power_on()

    def set_light_intensity(self, v):
        self.fiber_light.intensity = min(max(0, v), 100)

    @on_trait_change('pointer')
    def pointer_ononff(self):
        """
        """
        self.pointer_state = not self.pointer_state
        self.laser_controller.set_pointer_onoff(self.pointer_state)

    def get_laser_watts(self):
        return self._requested_power

    def get_coolant_temperature(self, **kw):
        """
        """
        chiller = self.chiller
        if chiller is not None:
            return chiller.get_coolant_out_temperature(**kw)

    def get_coolant_status(self, **kw):

        chiller = self.chiller
        if chiller is not None:
            return chiller.get_faults(**kw)

    def do_motor_initialization(self, name):
        if self.laser_controller:
            motor = self.laser_controller.get_motor(name)
            if motor is not None:
                n = 4
                from pychron.core.ui.progress_dialog import myProgressDialog

                pd = myProgressDialog(max=n, size=(550, 15))
                pd.open()
                motor.initialize(progress=pd)
                pd.close()

    def set_beam_diameter(self, bd, force=False, **kw):
        """
        """
        result = False
        motor = self.get_motor('beam')
        if motor is not None:
            if motor.enabled or force:
                self.set_motor('beam', bd, **kw)
                result = True
            else:
                self.info('beam disabled by lens configuration {}'.format(
                    self.lens_configuration))
        return result

    def set_zoom(self, z, **kw):
        """
        """
        self.set_motor('zoom', z, **kw)

    def set_motor_lock(self, name, value):
        m = self.get_motor(name)
        if m is not None:
            m.locked = to_bool(value)
            return True

    def set_motor(self, *args, **kw):
        self.motor_event = (args, kw)
        return self.laser_controller.set_motor(*args, **kw)

    def get_motor(self, name):
        return next(
            (mi for mi in self.laser_controller.motors if mi.name == name),
            None)

    def do_autofocus(self, **kw):
        if self.use_video:
            am = self.stage_manager.autofocus_manager
            am.passive_focus(block=True, **kw)

    def take_snapshot(self, *args, **kw):
        if self.use_video:
            return self.stage_manager.snapshot(auto=True,
                                               inform=False,
                                               *args,
                                               **kw)

    def start_video_recording(self, name='video', *args, **kw):
        if self.use_video:
            self.stage_manager.start_recording(basename=name)

    def stop_video_recording(self, *args, **kw):
        if self.use_video:
            self.stage_manager.stop_recording()

    def degasser_factory(self):
        from pychron.mv.degas.degasser import Degasser

        dm = Degasser(
            laser_manager=self,
            video=self.stage_manager.video,
        )
        return dm

    def do_machine_vision_degas(self, lumens, duration, new_thread=False):
        if self.use_video:
            dm = self.degasser_factory()

            def func():
                dm.degas(lumens, duration)

            if new_thread:
                self._degas_thread = Thread(target=func)
                self._degas_thread.start()
            else:
                func()

    def get_brightness(self):
        if self.use_video:
            return self.stage_manager.get_brightness()
        else:
            return super(FusionsLaserManager, self).get_brightness()

    def is_degassing(self):
        if self._degas_thread:
            return self._degas_thread.isRunning()
            # ===============================================================================
            # pyscript interface
            # ===============================================================================

    def _move_to_position(self, position, autocenter):

        if self.stage_manager is not None:
            if isinstance(position, tuple):
                if len(position) > 1:
                    x, y = position[:2]
                    self.stage_manager.linear_move(x, y)
                    if len(position) == 3:
                        self.stage_manager.set_z(position[2])
            else:

                self.stage_manager.move_to_hole(position)
            return True

    def set_stage_map(self, mapname):
        if self.stage_manager is not None:
            self.stage_manager.set_stage_map(mapname)

    def _enable_hook(self, **kw):
        resp = self.laser_controller._enable_laser(**kw)
        if self.laser_controller.simulation:
            resp = True

        return resp

    def _disable_hook(self):
        resp = self.laser_controller._disable_laser()
        if self.laser_controller.simulation:
            resp = True

        return resp

    def show_motion_controller_manager(self):
        """
        """
        stage_controller = self.stage_manager.stage_controller
        package = 'pychron.managers.motion_controller_managers'
        if 'Aerotech' in stage_controller.__class__.__name__:
            klass = 'AerotechMotionControllerManager'
            package += '.aerotech_motion_controller_manager'
        else:
            klass = 'NewportMotionControllerManager'
            package += '.newport_motion_controller_manager'

        module = __import__(package, globals(), locals(), [klass], -1)
        factory = getattr(module, klass)
        m = factory(motion_controller=stage_controller)
        self.open_view(m)

    # ========================= views =========================

    def get_control_buttons(self):
        """
        """
        return [
            ('enable', 'enable_label', None),
        ]

    #    def get_control_items(self):
    #        '''
    #        '''
    # #        return Item('laser_controller', show_label=False,
    # #                    editor=InstanceEditor(view='control_view'),
    # #                    style='custom',
    # #                    springy=False, height= -100)
    #
    # #        return self.laser_controller.get_control_group()
    #        s = [('zoom', 'zoom', {}),
    #            ('beam', 'beam', {'enabled_when':'object.beam_enabled'})
    #            ]
    #        return self._update_slider_group_factory(s)

    #    def get_lens_configuration_group(self):
    #        return Item('lens_configuration',
    #                    editor=EnumEditor(values=self.lens_configuration_names)
    #                    )

    #    def get_optics_group(self):
    #        csliders = self.get_control_items()
    #        vg = HGroup(csliders,
    #                      show_border=True,
    #                      label='Optics', springy=False
    #                      )
    #
    #        lens_config = self.get_lens_configuration_group()
    #        if lens_config:
    #            vg.content.insert(0, lens_config)
    #
    #        return vg
    #    def get_control_button_group(self):
    #        grp = HGroup(spring, Item('enabled_led', show_label=False, style='custom', editor=LEDEditor()),
    #                        self._button_group_factory(self.get_control_buttons(), orientation='h'),
    # #                                  springy=True
    #                    )
    #        return grp
    def get_power_group(self):
        power_grp = VGroup(
            self.get_control_button_group(),
            HGroup(
                Item('requested_power',
                     style='readonly',
                     format_str='%0.2f',
                     width=100), spring,
                Item('units', show_label=False, style='readonly'), spring),
            #                           Item('laser_script_executor', show_label=False, style='custom'),
            #                           self._button_factory('execute_button', 'execute_label'),
            show_border=True,
            #                           springy=True,
            label='Power')

        ps = self.get_power_slider()
        if ps:
            #            ps.springy = True
            power_grp.content.append(ps)
        return power_grp

    #     def get_additional_group(self):
    #         og = Group(Item('laser_controller', show_label=False,
    #                     editor=InstanceEditor(view='control_view'),
    #                     style='custom'),
    #                    label='Optics',
    #                    )
    #         ac = Group(
    #                    og,
    #                    show_border=True,
    #                    label='Additional Controls',
    #                    layout='tabbed')
    #
    #         aclist = self.get_additional_controls()
    #         if aclist is None:
    #             og.label = 'Optics'
    #             og.show_border = True
    #             ac = og
    #         else:
    #             for ai in aclist:
    #                 ac.content.append(ai)
    #         return ac

    #     def get_control_group(self):
    #         '''
    #         '''
    #         power_grp = self.get_power_group()
    #         pulse_grp = Group(Item('pulse', style='custom', show_label=False),
    #                         label='Pulse', show_border=True
    #                         )
    #         power_grp = HGroup(power_grp, pulse_grp)
    #         ac = self.get_additional_group()
    #         g = HGroup(power_grp, ac)
    #
    #         return g

    def _get_pointer_label(self):
        """
        """
        return 'Pointer ON' if not self.pointer_state else 'Pointer OFF'

    def _get_record_label(self):
        return 'Record' if not self._recording_power_state else 'Stop'

    def _get_record_brightness(self):
        return self.record_brightness and self._get_machine_vision(
        ) is not None

    # ========================= defaults =======================
    # def get_power_database(self):
    # from pychron.database.adapters.power_adapter import PowerAdapter
    #
    #     db = PowerAdapter(name=self.dbname,
    #                       kind='sqlite')
    #     return db

    # def get_power_calibration_database(self):
    # from pychron.database.adapters.power_calibration_adapter import PowerCalibrationAdapter
    #
    #     db = PowerCalibrationAdapter(name=self.dbname,
    #                                  kind='sqlite')
    #     return db

    #    def _subsystem_default(self):
    #        '''
    #        '''
    #        return ArduinoSubsystem(name='arduino_subsystem_2')

    #    def _brightness_meter_default(self):
    #        if self.use_video:
    #            b = BrightnessPIDManager(parent=self)
    # #            b.brightness_manager.video = self.stage_manager.video
    #
    #            return b

    def _fiber_light_default(self):
        """
        """
        return FiberLight(name='fiber_light')
예제 #46
0
class StageManager(BaseStageManager):
    """
    """

    autocenter_button = Button

    stage_controller_klass = String('Newport')

    stage_controller = Instance(MotionController)
    points_programmer = Instance(PointsProgrammer)
    motion_controller_manager = Instance(MotionControllerManager)
    # canvas = Instance(LaserTrayCanvas)

    simulation = DelegatesTo('stage_controller')

    # stage_map_klass = StageMap
    # _stage_map = Instance(StageMap)
    # stage_map = Property(depends_on='_stage_map')
    # stage_maps = Property(depends_on='_stage_maps')

    # _stage_maps = List
    # ===========================================================================
    # buttons
    # ===========================================================================
    home = Button('home')
    home_option = String('Home All')
    home_options = List

    ejoystick = Event
    joystick_label = String('Enable Joystick')
    joystick = Bool(False)
    joystick_timer = None

    back_button = Button
    stop_button = Button('Stop')

    use_autocenter = Bool
    keep_images_open = Bool(False)

    _default_z = 0
    _cached_position = None
    _cached_current_hole = None
    _homing = False

    def __init__(self, *args, **kw):
        """

        """
        super(StageManager, self).__init__(*args, **kw)
        self.stage_controller = self._stage_controller_factory()

    def measure_grain_polygon(self):
        pass

    def stop_measure_grain_polygon(self):
        pass

    def shutdown(self):
        self._save_stage_map()

    def create_device(self, *args, **kw):
        dev = super(StageManager, self).create_device(*args, **kw)
        dev.parent = self
        return dev

    def goto_position(self, v, **kw):
        if XY_REGEX[0].match(v):
            self._move_to_calibrated_position(v)
        elif POINT_REGEX.match(v) or TRANSECT_REGEX[0].match(v):
            self.move_to_point(v)

        else:
            self.move_to_hole(v, user_entry=True, **kw)

    def get_current_position(self):
        if self.stage_controller:
            x = self.stage_controller.x
            y = self.stage_controller.y
            return x, y

    def get_current_hole(self):
        pos = self.get_current_position()
        if self.stage_map:
            if distance_threshold(pos, self._cached_position, self.stage_map.g_dimension / 4):
                h = self.get_calibrated_hole(*pos, tol=self.stage_map.g_dimension / 2.)
                if h is not None:
                    self._cached_current_hole = h
                    self._cached_position = pos

        return self._cached_current_hole

    def is_auto_correcting(self):
        return False

    def bind_preferences(self, pref_id):
        bind_preference(self.canvas, 'show_grids', '{}.show_grids'.format(pref_id))
        self.canvas.change_grid_visibility()

        bind_preference(self.canvas, 'show_laser_position', '{}.show_laser_position'.format(pref_id))
        bind_preference(self.canvas, 'show_desired_position', '{}.show_desired_position'.format(pref_id))
        bind_preference(self.canvas, 'desired_position_color', '{}.desired_position_color'.format(pref_id),
                        factory=ColorPreferenceBinding)
        #        bind_preference(self.canvas, 'render_map', '{}.render_map'.format(pref_id))
        #
        bind_preference(self.canvas, 'crosshairs_kind', '{}.crosshairs_kind'.format(pref_id))
        bind_preference(self.canvas, 'crosshairs_line_width', '{}.crosshairs_line_width'.format(pref_id))
        bind_preference(self.canvas, 'crosshairs_color',
                        '{}.crosshairs_color'.format(pref_id),
                        factory=ColorPreferenceBinding)
        bind_preference(self.canvas, 'crosshairs_radius', '{}.crosshairs_radius'.format(pref_id))
        bind_preference(self.canvas, 'crosshairs_offsetx', '{}.crosshairs_offsetx'.format(pref_id))
        bind_preference(self.canvas, 'crosshairs_offsety', '{}.crosshairs_offsety'.format(pref_id))

        bind_preference(self.canvas, 'show_hole_label', '{}.show_hole_label'.format(pref_id))
        bind_preference(self.canvas, 'hole_label_color', '{}.hole_label_color'.format(pref_id))
        bind_preference(self.canvas, 'hole_label_size', '{}.hole_label_size'.format(pref_id))
        self.canvas.handle_hole_label_size(self.canvas.hole_label_size)

        bind_preference(self.canvas, 'scaling', '{}.scaling'.format(pref_id))
        bind_preference(self.canvas, 'show_bounds_rect',
                        '{}.show_bounds_rect'.format(pref_id))

        self.canvas.request_redraw()

    def load(self):
        super(StageManager, self).load()
        config = self.get_configuration()
        if config:
            self._default_z = self.config_get(config, 'Defaults', 'z', default=13, cast='float')

        self.points_programmer.load_stage_map(self.stage_map_name)

        # load the calibration file
        # should have calibration files for each stage map
        self.tray_calibration_manager.load_calibration()

    def finish_loading(self):
        self.initialize_stage()

    def initialize_stage(self):
        self.update_axes()
        axes = self.stage_controller.axes
        self.home_options = ['Home All', 'XY'] + sorted([axes[a].name.upper() for a in axes])
        self.canvas.parent = self

    def save_calibration(self, name):
        self.tray_calibration_manager.save_calibration(name=name)

        # def add_stage_map(self, v):
        # sm = self.stage_map_klass(file_path=v)
        # psm = self._get_stage_map_by_name(sm.name)
        # if psm:
        #     self._stage_maps.remove(psm)

        # self._stage_maps.append(sm)

    def accept_point(self):
        self.points_programmer.accept_point()

    def set_stage_map(self, v):
        return self._set_stage_map(v)

    def single_axis_move(self, *args, **kw):
        return self.stage_controller.single_axis_move(*args, **kw)

    def linear_move(self, x, y, use_calibration=True, check_moving=False, abort_if_moving=False, **kw):

        if check_moving:
            if self.moving():
                self.warning('MotionController already in motion')
                if abort_if_moving:
                    self.warning('Move to {},{} aborted'.format(x, y))
                    return
                else:
                    self.stop()
                    self.debug('Motion stopped. moving to {},{}'.format(x, y))

        pos = (x, y)
        if use_calibration:
            pos = self.get_calibrated_position(pos)
            f = lambda x: '{:0.5f},{:0.5f}'.format(*x)
            self.debug('%%%%%%%%%%%%%%%%% mapped {} to {}'.format(f((x, y)), f(pos)))

        self.stage_controller.linear_move(*pos, **kw)

    def move_to_hole(self, hole, **kw):
        if self.stage_map.check_valid_hole(hole, **kw):
            self._move(self._move_to_hole, hole, name='move_to_hole', **kw)

    def move_to_point(self, pt):
        self._move(self._move_to_point, pt, name='move_to_point')

    def move_polyline(self, line):
        self._move(self._move_to_line, line, name='move_to_line')

    def move_polygon(self, poly):
        self._move(self._move_polygon, poly, name='move_polygon')

    def drill_point(self, pt):
        self._move(self._drill_point, pt, name='drill_point')

    def set_x(self, value, **kw):
        return self.stage_controller.single_axis_move('x', value, **kw)

    def set_y(self, value, **kw):
        return self.stage_controller.single_axis_move('y', value, **kw)

    def set_z(self, value, **kw):
        return self.stage_controller.single_axis_move('z', value, **kw)

    def set_xy(self, x, y, **kw):
        hole = self._get_hole_by_position(x, y)
        if hole:
            self.move_to_hole(hole)
        # self._set_hole(hole.id)
        # self.move_to_hole(hole.id)
        #            self._set_hole(hole.id)
        else:
            return self.linear_move(x, y, **kw)

    def get_hole(self, name):
        if self.stage_map:
            return self.stage_map.get_hole(name)

    def move_to_load_position(self):
        """
        """
        x, y, z = self.stage_controller.get_load_position()
        self.info('moving to load position, x={}, y={}, z={}'.format(x, y, z))

        self.stage_controller.linear_move(x, y, grouped_move=False, block=False)

        self.stage_controller.set_z(z)
        self.stage_controller.block()

    def stop(self, ax_key=None, verbose=False):
        self._stop(ax_key, verbose)

    def relative_move(self, *args, **kw):
        self.stage_controller.relative_move(*args, **kw)

    def key_released(self):
        sc = self.stage_controller
        sc.add_consumable((sc.update_axes, tuple()))

    def moving(self, force_query=False, **kw):
        moving = False
        if force_query:
            moving = self.stage_controller.moving(**kw)
        elif self.stage_controller.timer is not None:
            moving = self.stage_controller.timer.isActive()

        return moving

    def get_brightness(self, **kw):
        return 0

    def get_scores(self, **kw):
        return 0, 0

    def define_home(self, **kw):
        self.stage_controller.define_home(**kw)

    def get_z(self):
        return self.stage_controller._z_position

    def get_uncalibrated_xy(self, pos=None):
        if pos is None:
            pos = (self.stage_controller.x, self.stage_controller.y)
            if self.stage_controller.xy_swapped():
                pos = pos[1], pos[0]

        canvas = self.canvas
        ca = canvas.calibration_item
        if ca:
            pos = self.stage_map.map_to_uncalibration(pos,
                                                      ca.center,
                                                      ca.rotation,
                                                      ca.scale)

        return pos

    def get_calibrated_xy(self):
        pos = (self.stage_controller.x, self.stage_controller.y)
        if self.stage_controller.xy_swapped():
            pos = pos[1], pos[0]

        pos = self.canvas.map_offset_position(pos)
        return self.get_calibrated_position(pos)

    def get_calibrated_hole(self, x, y, tol):
        ca = self.canvas.calibration_item
        if ca is not None:
            smap = self.stage_map

            xx, yy = smap.map_to_uncalibration((x, y), ca.center, ca.rotation)
            return next((hole for hole in smap.sample_holes
                         if abs(hole.x - xx) < tol and abs(hole.y - yy) < tol), None)

    def get_hole_xy(self, key):
        pos = self.stage_map.get_hole_pos(key)
        # map the position to calibrated space
        pos = self.get_calibrated_position(pos)
        return pos

    def finish_move_to_hole(self, user_entry):
        pass

    # private
    def _update_axes(self):
        if self.stage_controller:
            self.stage_controller.update_axes()

    def _home(self):
        """
        """
        if self._homing:
            return

        self._homing = True

        if self.home_option == 'Home All':

            msg = 'homing all motors'
            homed = ['x', 'y', 'z']
            home_kwargs = dict(x=-25, y=-25, z=50)

        elif self.home_option == 'XY':
            msg = 'homing x,y'
            homed = ['x', 'y']
            home_kwargs = dict(x=-25, y=-25)
        else:
            #            define_home =
            msg = 'homing {}'.format(self.home_option)
            home_kwargs = {self.home_option: -25 if self.home_option in ['X', 'Y'] else 50}
            homed = [self.home_option.lower().strip()]

        self.info(msg)

        # if define_home:
        self.stage_controller.set_home_position(**home_kwargs)

        self.stage_controller.home(homed)

        # explicitly block
        #        self.stage_controller.block()

        if 'z' in homed and 'z' in self.stage_controller.axes:
            # will be a positive limit error in z
            #            self.stage_controller.read_error()

            time.sleep(1)
            self.info('setting z to nominal position. {} mm '.format(self._default_z))
            self.stage_controller.single_axis_move('z', self._default_z, block=True)
            self.stage_controller._z_position = self._default_z

        if self.home_option in ['XY', 'Home All']:
            time.sleep(0.25)

            # the stage controller should  think x and y are at -25,-25
            self.stage_controller._x_position = -25
            self.stage_controller._y_position = -25

            self.info('moving to center')
            try:
                self.stage_controller.linear_move(0, 0, block=True, sign_correct=False)
            except TargetPositionError as e:
                self.warning_dialog('Move Failed. {}'.format(e))

        self._homing = False

    def _get_hole_by_position(self, x, y):
        if self.stage_map:
            return self.stage_map._get_hole_by_position(x, y)

    def _get_hole_by_name(self, key):
        sm = self.stage_map
        return sm.get_hole(key)

    # ===============================================================================
    # special move
    # ===============================================================================
    def _stop(self, ax_key=None, verbose=False):
        self.stage_controller.stop(ax_key=ax_key, verbose=verbose)
        if self.parent.pattern_executor:
            self.parent.pattern_executor.stop()

    # def _move(self, func, pos, name=None, *args, **kw):
    #     if pos is None:
    #         return
    #
    #     if self.move_thread and self.move_thread.isRunning():
    #         self.stage_controller.stop()
    #     if name is None:
    #         name = func.func_name
    #
    #     self.move_thread = Thread(name='stage.{}'.format(name),
    #                               target=func, args=(pos,) + args, kwargs=kw)
    #     self.move_thread.start()

    def _drill_point(self, pt):
        zend = pt.zend
        vel = pt.velocity

        # assume already at zstart
        st = time.time()
        self.info('start drilling. move to {}. velocity={}'.format(zend, vel))
        self.set_z(zend, velocity=vel, block=True)
        et = time.time() - st

        self.info('drilling complete. drilled for {}s'.format(et))

    def _move_polygon(self, pts, velocity=5,
                      offset=50,
                      use_outline=True,
                      find_min=False,
                      scan_size=None,
                      use_move=True,
                      use_convex_hull=True,
                      motors=None,
                      verbose=True,
                      start_callback=None, end_callback=None):
        """
            motors is a dict of motor_name:value pairs
        """
        if pts is None:
            return

        if not isinstance(pts, list):
            velocity = pts.velocity
            use_convex_hull = pts.use_convex_hull
            if scan_size is None:
                scan_size = pts.scan_size
            use_outline = pts.use_outline
            offset = pts.offset
            find_min = pts.find_min
            pts = [dict(xy=(pi.x, pi.y), z=pi.z, ) for pi in pts.points]

        # set motors
        if motors is not None:
            for k, v in motors.values():
                '''
                    motor will not set if it has been locked using set_motor_lock or
                    remotely using SetMotorLock
                '''
                if use_move:
                    self.parent.set_motor(k, v, block=True)

        xy = [pi['xy'] for pi in pts]
        n = 1000
        if scan_size is None:
            scan_size = n / 2

        # convert points to um
        pts = array(xy)
        pts *= n
        pts = asarray(pts, dtype=int)
        '''
            sort clockwise ensures consistent offset behavior
            a polygon gain have a inner or outer sense depending on order of vertices

            always use sort_clockwise prior to any polygon manipulation
        '''
        pts = sort_clockwise(pts, pts)

        sc = self.stage_controller
        sc.set_program_mode('absolute')
        # do smooth transitions between points
        sc.set_smooth_transitions(True)

        if use_convex_hull:
            pts = convex_hull(pts)

        if use_outline:
            # calculate new polygon
            offset_pts = polygon_offset(pts, -offset)
            offset_pts = array(offset_pts, dtype=int)
            # polygon offset used 3D vectors.
            # trim to only x,y
            pts = offset_pts[:, (0, 1)]

            # trace perimeter
            if use_move:
                p0 = xy[0]
                self.linear_move(p0[0], p0[1], mode='absolute', block=True)

                sc.timer = sc.timer_factory()

                if start_callback is not None:
                    start_callback()

                # buf=[]
                for pi in xy[1:]:
                    self.linear_move(pi[0], pi[1],
                                     velocity=velocity,
                                     mode='absolute', set_stage=False)

                # finish at first point
                self.linear_move(p0[0], p0[1],
                                 velocity=velocity,
                                 mode='absolute', set_stage=False)

                sc.block()
                self.info('polygon perimeter trace complete')
                '''
                    have the oppurtunity here to turn off laser and change parameters i.e mask
                '''
        if use_move:
            # calculate and step thru scan lines
            self._raster(pts, velocity,
                         step=scan_size,
                         scale=n,
                         find_min=find_min,
                         start_callback=start_callback, end_callback=end_callback,
                         verbose=verbose)

        sc.set_program_mode('relative')
        if end_callback is not None:
            end_callback()
        self.info('polygon raster complete')

    def _raster(self, points, velocity,
                step=500,
                scale=1000,
                find_min=False,
                start_callback=None, end_callback=None, verbose=False):

        from pychron.core.geometry.scan_line import raster

        lines = raster(points, step=step, find_min=find_min)

        # initialize variables
        cnt = 0
        direction = 1
        flip = False
        lasing = False
        sc = self.stage_controller

        if verbose:
            self.info('start raster')

        # print lines
        # loop thru each scan line
        #        for yi, xs in lines[::skip]:
        for yi, xs in lines:
            if direction == -1:
                xs = list(reversed(xs))

            # convert odd numbers lists to even
            n = len(xs)
            if n % 2 != 0:
                xs = sorted(list(set(xs)))

            # traverse each x-intersection pair
            n = len(xs)
            for i in range(0, n, 2):
                if len(xs) <= 1:
                    continue

                x1, x2, yy = xs[i] / scale, xs[i + 1] / scale, yi / scale
                if abs(x1 - x2) > 1e-10:
                    if not lasing:
                        if verbose:
                            self.info('fast to {} {},{}'.format(cnt, x1, yy))

                        self.linear_move(x1, yy,
                                         mode='absolute', set_stage=False,
                                         block=True)
                        if start_callback is not None:
                            start_callback()

                        lasing = True
                    else:
                        if verbose:
                            self.info('slow to {} {},{}'.format(cnt, x1, yy))

                        sc.timer = sc.timer_factory()
                        self.linear_move(x1, yy,
                                         mode='absolute', set_stage=False,
                                         velocity=velocity)

                    if verbose:
                        self.info('move to {}a {},{}'.format(cnt, x2, yy))

                        #                if n > 2 and not i * 2 >= n:
                    # line this scan line has more then 1 segment turn off laser at end of segment
                    if i + 2 < n and not xs[i + 1] == xs[i + 2]:
                        self.linear_move(x2, yy, velocity=velocity,
                                         mode='absolute', set_stage=False,
                                         block=True)
                        self.info('wait for move complete')
                        if end_callback is not None:
                            end_callback()

                        lasing = False
                    else:
                        self.linear_move(x2, yy, velocity=velocity,
                                         mode='absolute', set_stage=False,
                                         )
                    cnt += 1
                    flip = True
                else:
                    flip = False

            if flip:
                direction *= -1

        sc.block()
        if verbose:
            self.info('end raster')

    def _move_polyline(self, pts, start_callback=None, end_callback=None):
        if not isinstance(pts, list):
            segs = pts.velocity_segments
            segs = segs[:1] + segs
            pts = [dict(xy=(pi.x, pi.y), z=pi.z, velocity=vi) for vi, pi in
                   zip(segs, pts.points)]

        sc = self.stage_controller
        self.linear_move(pts[0]['xy'][0], pts[0]['xy'][1],
                         update_hole=False,
                         use_calibration=False,
                         block=True)
        sc.set_z(pts[0]['z'], block=True)

        cpos = dict()
        # set motors
        for motor in ('mask', 'attenuator'):
            if motor in pts[0]:
                self.parent.set_motor(motor, pts[0][motor])
                cpos[motor] = pts[0][motor]

        sc.set_program_mode('absolute')
        sc.timer = sc.timer_factory()
        if start_callback:
            start_callback()

        npts = pts[1:]
        setmotors = dict()
        for i, di in enumerate(npts):
            xi, yi, zi, vi = di['xy'][0], di['xy'][1], di['z'], di['velocity']
            sc.set_z(zi)

            block = False
            for motor in ('mask', 'attenuator'):
                # fix next step sets motor should block
                if i + 1 < len(npts):
                    dii = npts[i + 1]
                    if motor in dii and dii[motor] != cpos[motor]:
                        m = self.parent.get_motor(motor)
                        if not m.locked:
                            block = True
                            setmotors[motor] = dii[motor]

            self.linear_move(xi, yi, velocity=vi,
                             block=block,
                             mode='absolute',  # use absolute mode because commands are queued
                             set_stage=False)
            if block:
                if end_callback:
                    end_callback()

                for k, v in setmotors.items():
                    self.parent.set_motor(k, v, block=True)

                if start_callback:
                    start_callback()

        # wait until motion complete
        sc.block()
        if end_callback:
            end_callback()

        sc.set_program_mode('relative')

    #        if start and smooth:
    #            sc.execute_command_buffer()
    #            sc.end_command_buffer()

    #    def start_enqueued(self):
    #        sc = self.stage_controller
    #        sc.execute_command_buffer()
    #        sc.end_command_buffer()

    def _move_to_point(self, pt):
        self.debug('move to point={}'.format(pt))
        if isinstance(pt, str):
            pt = self.canvas.get_point(pt)

        self.debug('move to point canvas pt={}'.format(pt))
        if pt is not None:
            pos = pt.x, pt.y

            self.info('Move to point {}: {:0.5f},{:0.5f},{:0.5f}'.format(pt.identifier,
                                                                         pt.x, pt.y, pt.z))
            self.stage_controller.linear_move(block=True, *pos)

            if hasattr(pt, 'z'):
                self.stage_controller.set_z(pt.z, block=True)

            self.debug('Not setting motors for pt')
            # self.parent.set_motors_for_point(pt)

            self._move_to_point_hook()

        self.info('Move complete')
        self.update_axes()

    def _move_to_hole(self, key, correct_position=True, user_entry=False, autocenter_only=False):
        self.info('Move to hole {} type={}'.format(key, str(type(key))))

        autocentered_position = False
        if not autocenter_only:
            self.temp_hole = key
            self.temp_position = self.stage_map.get_hole_pos(key)
            pos = self.stage_map.get_corrected_hole_pos(key)
            self.info('position {}'.format(pos))
            if pos is not None:
                if abs(pos[0]) < 1e-6:
                    pos = self.stage_map.get_hole_pos(key)
                    # map the position to calibrated space
                    pos = self.get_calibrated_position(pos, key=key)
                else:
                    # check if this is an interpolated position
                    # if so probably want to do an autocentering routine
                    hole = self.stage_map.get_hole(key)
                    if hole.interpolated:
                        self.info('using an interpolated value')
                    else:
                        self.info('using previously calculated corrected position')
                        autocentered_position = True
                try:
                    self.stage_controller.linear_move(block=True, source='move_to_hole {}'.format(pos),
                                                      raise_zero_displacement=True, *pos)
                except TargetPositionError as e:
                    self.warning('(001) Move to {} failed'.format(pos))
                    self.parent.emergency_shutoff(str(e))
                    return
                except ZeroDisplacementException:
                    correct_position = False
        try:
            self._move_to_hole_hook(key, correct_position,
                                autocentered_position)
        except TargetPositionError as e:
            self.warning('(002) Move failed. {}'.format(e))
            self.parent.emergency_shutoff(str(e))
            return

        self.finish_move_to_hole(user_entry)
        self.info('Move complete')

    def _move_to_hole_hook(self, *args):
        pass

    def _move_to_point_hook(self):
        pass

    # ===============================================================================
    # Property Get / Set
    # ===============================================================================
    def _set_stage_map(self, v):
        if v in self.stage_map_names:
            for root, ext in ((self.root, '.txt'), (paths.user_points_dir, '.yaml')):
                p = os.path.join(root, add_extension(v, ext))
                if os.path.isfile(p):
                    self.info('setting stage map to {}'.format(v))
                    sm = self.stage_map_klass(file_path=p)
                    self.canvas.set_map(sm)
                    self.tray_calibration_manager.load_calibration(stage_map=v)
                    self.points_programmer.load_stage_map(sm)

                    return True
        else:
            self.warning('No stage map named "{}"'.format(v))
            return False

    def _get_calibrate_stage_label(self):
        if self._calibration_state == 'set_center':
            r = 'Locate Center'
        elif self._calibration_state == 'set_right':
            r = 'Locate Right'
        else:
            r = 'Calibrate Stage'
        return r

    def _get_program_points_label(self):
        return 'Program Points' if not self.canvas.markup else 'End Program'

    def _validate_hole(self, v):
        nv = None
        try:
            if v.strip():
                nv = int(v)

        except TypeError:
            self.warning('invalid hole {}'.format(v))

        return nv

    #    def _get_calibrated_position_entry(self):
    #        return self._calibrated_position
    #
    #    def _set_calibrated_position_entry(self, v):
    #        self._calibrated_position = v
    #        if XY_REGEX.match(v):
    #            self._move_to_calibrated_position(v)
    #        else:
    #            self.move_to_hole(v)

    def _move_to_calibrated_position(self, pos):
        try:
            args = csv_to_floats(pos)
        except ValueError:
            self.warning('invalid calibrated position "{}". Could not convert to floats'.format(pos))
            return

        if len(args) == 2:
            x, y = args
            self.linear_move(x, y, use_calibration=True, block=False)
        else:
            self.warning('invalid calibrated position. incorrect number of arguments "{}"'.format(args))

    # def _set_hole(self, v):
    #        if v is None:
    #            return
    #
    #        if self.canvas.calibrate:
    #            self.warning_dialog('Cannot move while calibrating')
    #            return
    #
    #        if self.canvas.markup:
    #            self.warning_dialog('Cannot move while adding/editing points')
    #            return
    #
    #        v = str(v)
    #
    #        if self.move_thread is not None:
    #            self.stage_controller.stop()
    #
    # #        if self.move_thread is None:
    #
    #        pos = self._stage_map.get_hole_pos(v)
    #        if pos is not None:
    #            self.visualizer.set_current_hole(v)
    # #            self._hole = v
    #            self.move_thread = Thread(name='stage.move_to_hole',
    #                                      target=self._move_to_hole, args=(v,))
    #            self.move_thread.start()
    #        else:
    #            err = 'Invalid hole {}'.format(v)
    #            self.warning(err)
    #            return  err

    #    def _get_hole(self):
    #        return self._hole

    def _set_point(self, v):
        if self.canvas.calibrate:
            self.warning_dialog('Cannot move while calibrating')
            return

        if self.canvas.markup:
            self.warning_dialog('Cannot move while adding/editing points')
            return

        if (self.move_thread is None or not self.move_thread.isRunning()) and v is not self._point:
            pos = self.canvas.get_item('point', int(v) - 1)
            if pos is not None:
                self._point = v
                self.move_thread = Thread(target=self._move_to_point, args=(pos,))
                self.move_thread.start()
            else:
                err = 'Invalid point {}'.format(v)
                self.warning(err)
                return err

    def _get_point(self):
        return self._point

    # ===============================================================================
    # handlers
    # ===============================================================================
    def _stop_button_fired(self):
        self._stop()

    def _ejoystick_fired(self):
        self.joystick = not self.joystick
        if self.joystick:
            self.stage_controller.enable_joystick()
            self.joystick_label = 'Disable Joystick'

            self.joystick_timer = self.timer_factory(func=self._joystick_inprogress_update)
        else:
            if self.joystick_timer is not None:
                self.joystick_timer.Stop()

            self.stage_controller.disable_joystick()
            self.joystick_label = 'Enable Joystick'

    def _home_fired(self):
        """
        """
        t = Thread(name='stage.home', target=self._home)
        t.start()
        # need to store a reference to thread so it is not garbage collected
        self.move_thread = t
        # do_later(self._home)

    def _test_fired(self):
        #        self.do_pattern('testpattern')
        self.do_pattern('pattern003')

    # ===============================================================================
    # factories
    # ===============================================================================
    def _motion_configure_factory(self, **kw):
        return MotionControllerManager(motion_controller=self.stage_controller,
                                       application=self.application,
                                       **kw)

    def _stage_controller_factory(self):
        if self.stage_controller_klass == 'Newport':
            from pychron.hardware.newport.newport_motion_controller import NewportMotionController

            factory = NewportMotionController
        elif self.stage_controller_klass == 'Aerotech':
            from pychron.hardware.aerotech.aerotech_motion_controller import AerotechMotionController

            factory = AerotechMotionController

        m = factory(name='{}controller'.format(self.name),
                    configuration_name='stage_controller',
                    configuration_dir_name=self.configuration_dir_name,
                    parent=self)
        return m

    def _canvas_factory(self):
        """
        """
        w = 640 / 2.0 / 23.2
        h = 0.75 * w

        l = LaserTrayCanvas(stage_manager=self,
                            padding=[30, 5, 5, 30],
                            map=self.stage_map,
                            view_x_range=[-w, w],
                            view_y_range=[-h, h])
        return l

    # ===============================================================================
    # defaults
    # ===============================================================================

    def _motion_controller_manager_default(self):
        return self._motion_configure_factory()

    def _title_default(self):
        return '%s Stage Manager' % self.name[:-5].capitalize()

    def _points_programmer_default(self):
        pp = PointsProgrammer(canvas=self.canvas,
                              stage_map_klass=self.stage_map_klass,
                              stage_manager=self)
        pp.on_trait_change(self.move_to_point, 'point')
        pp.on_trait_change(self.move_polygon, 'polygon')
        pp.on_trait_change(self.move_polyline, 'line')
        return pp
예제 #47
0
class VideoStageManager(StageManager):
    """
    """
    video = Instance(Video)
    camera = Instance(BaseCamera)

    canvas_editor_klass = VideoComponentEditor

    camera_zoom_coefficients = Property(String(enter_set=True, auto_set=False),
                                        depends_on='_camera_zoom_coefficients')
    _camera_zoom_coefficients = String

    use_auto_center_interpolation = Bool(False)

    configure_camera_device_button = Button

    autocenter_button = Button('AutoCenter')
    configure_autocenter_button = Button('Configure')

    autocenter_manager = Instance(
        'pychron.mv.autocenter_manager.AutoCenterManager')
    autofocus_manager = Instance(
        'pychron.mv.focus.autofocus_manager.AutoFocusManager')

    # zoom_calibration_manager = Instance(
    #     'pychron.mv.zoom.zoom_calibration.ZoomCalibrationManager')

    snapshot_button = Button('Snapshot')
    snapshot_mode = Enum('Single', '3 Burst', '10 Burst')
    auto_save_snapshot = Bool(True)

    record = Event
    record_label = Property(depends_on='is_recording')
    is_recording = Bool

    use_db = False

    use_video_archiver = Bool(True)
    video_archiver = Instance('pychron.core.helpers.archiver.Archiver')
    video_identifier = Str

    # use_video_server = Bool(False)
    # video_server_port = Int
    # video_server_quality = Int
    # video_server = Instance('pychron.image.video_server.VideoServer')

    use_media_storage = Bool(False)
    auto_upload = Bool(False)
    keep_local_copy = Bool(False)

    lumen_detector = Instance(LumenDetector)

    render_with_markup = Bool(False)
    burst_delay = Int(250)

    _auto_correcting = False
    stop_timer = Event

    pxpermm = Float(23)

    _measure_grain_t = None
    _measure_grain_evt = None
    grain_polygons = None

    # test_button = Button
    # _test_state = False

    # def _test_button_fired(self):
    #     if self._test_state:
    #         # self.stop_measure_grain_polygon()
    #         #
    #         # time.sleep(2)
    #         #
    #         # d = self.get_grain_polygon_blob()
    #         # print d
    #         self.parent.disable_laser()
    #     else:
    #         self.parent.luminosity_degas_test()
    #         # self.start_measure_grain_polygon()
    #     self._test_state = not self._test_state

    def motor_event_hook(self, name, value, *args, **kw):
        if name == 'zoom':
            self._update_zoom(value)

    def bind_preferences(self, pref_id):
        self.debug('binding preferences')
        super(VideoStageManager, self).bind_preferences(pref_id)
        if self.autocenter_manager:
            self.autocenter_manager.bind_preferences(pref_id)
            # bind_preference(self.autocenter_manager, 'use_autocenter',
            #                 '{}.use_autocenter'.format(pref_id))

        bind_preference(self, 'render_with_markup',
                        '{}.render_with_markup'.format(pref_id))
        bind_preference(self, 'burst_delay', '{}.burst_delay'.format(pref_id))
        bind_preference(self, 'auto_upload', '{}.auto_upload'.format(pref_id))
        bind_preference(self, 'use_media_storage',
                        '{}.use_media_storage'.format(pref_id))
        bind_preference(self, 'keep_local_copy',
                        '{}.keep_local_copy'.format(pref_id))

        bind_preference(self, 'use_video_archiver',
                        '{}.use_video_archiver'.format(pref_id))

        bind_preference(self, 'video_identifier',
                        '{}.video_identifier'.format(pref_id))

        bind_preference(self, 'use_video_server',
                        '{}.use_video_server'.format(pref_id))

        bind_preference(self.video_archiver, 'archive_months',
                        '{}.video_archive_months'.format(pref_id))
        bind_preference(self.video_archiver, 'archive_days',
                        '{}.video_archive_days'.format(pref_id))
        bind_preference(self.video_archiver, 'archive_hours',
                        '{}.video_archive_hours'.format(pref_id))
        bind_preference(self.video_archiver, 'root',
                        '{}.video_directory'.format(pref_id))

        # bind_preference(self.video, 'output_mode',
        #                 '{}.video_output_mode'.format(pref_id))
        # bind_preference(self.video, 'ffmpeg_path',
        #                 '{}.ffmpeg_path'.format(pref_id))

    def get_grain_polygon(self):
        ld = self.lumen_detector
        l, m = ld.lum()
        return m.tostring()

    def get_grain_polygon_blob(self):
        # self.debug('Get grain polygons n={}'.format(len(self.grain_polygons)))

        try:
            t, md, p = next(self.grain_polygons)

            a = pack('ff', ((t, md), ))
            b = pack('HH', p)

            return encode_blob(a + b)

        except (StopIteration, TypeError) as e:
            self.debug('No more grain polygons. {}'.format(e))

    def stop_measure_grain_polygon(self):
        self.debug('Stop measure polygons {}'.format(self._measure_grain_evt))
        if self._measure_grain_evt:
            self._measure_grain_evt.set()
        return True

    def start_measure_grain_polygon(self):
        self._measure_grain_evt = evt = TEvent()

        def _measure_grain_polygon():
            ld = self.lumen_detector
            dim = self.stage_map.g_dimension
            ld.pxpermm = self.pxpermm

            self.debug('Starting measure grain polygon')
            masks = []
            display_image = self.autocenter_manager.display_image

            mask_dim = dim * 1.05
            mask_dim_mm = mask_dim * self.pxpermm
            ld.grain_measuring = True
            while not evt.is_set():
                src = self._get_preprocessed_src()
                if src is not None:
                    targets = ld.find_targets(
                        display_image,
                        src,
                        dim,
                        mask=mask_dim,
                        search={'start_offset_scalar': 1})
                    if targets:
                        t = time.time()
                        targets = [(t, mask_dim_mm, ti.poly_points.tolist())
                                   for ti in targets]
                        masks.extend(targets)
                sleep(0.1)
            ld.grain_measuring = False

            self.grain_polygons = (m for m in masks)
            self.debug('exiting measure grain')

        self._measure_grain_t = QThread(target=_measure_grain_polygon)
        self._measure_grain_t.start()
        return True

    def start_recording(self,
                        path=None,
                        use_dialog=False,
                        basename='vm_recording',
                        **kw):
        """
        """
        directory = None
        if os.path.sep in basename:
            args = os.path.split(basename)
            directory, basename = os.path.sep.join(args[:-1]), args[-1]

        if path is None:
            if use_dialog:
                path = self.save_file_dialog()
            else:
                vd = self.video_archiver.root
                self.debug('video archiver root {}'.format(vd))
                if not vd:
                    vd = paths.video_dir
                if directory:
                    vd = os.path.join(vd, directory)
                    if not os.path.isdir(vd):
                        os.mkdir(vd)

                path = unique_path_from_manifest(vd, basename, extension='avi')

        kw['path'] = path
        kw['basename'] = basename

        self._start_recording(**kw)
        self.is_recording = True
        return path

    def stop_recording(self, user='******', delay=None):
        """
        """
        def close():
            self.is_recording = False
            self.info('stop video recording')
            p = self.video.output_path
            if self.video.stop_recording(wait=True):
                if self.auto_upload:
                    try:
                        p = self._upload(p, inform=False)
                    except BaseException as e:
                        self.critical('Failed uploading {}. error={}'.format(
                            p, e))
            return p

        if self.video.is_recording():
            if delay:
                t = Timer(delay, close)
                t.start()
            else:
                return close()

    @property
    def video_configuration_path(self):
        if self.configuration_dir_path:
            return os.path.join(self.configuration_dir_path, 'camera.yaml')

    def initialize_video(self):
        if self.video:
            identifier = 0
            p = self.video_configuration_path
            if os.path.isfile(p):
                with open(p, 'r') as rfile:
                    yd = yaml.load(rfile)
                    vid = yd['Device']
                    identifier = vid.get('identifier', 0)

            self.video.open(identifier=identifier)

            self.video.load_configuration(p)
            self.lumen_detector.pixel_depth = self.video.pixel_depth

    def initialize_stage(self):
        super(VideoStageManager, self).initialize_stage()
        self.initialize_video()

        # s = self.stage_controller
        # if s.axes:
        #     xa = s.axes['x'].drive_ratio
        #     ya = s.axes['y'].drive_ratio

        # self._drive_xratio = xa
        # self._drive_yratio = ya

        self._update_zoom(0)

    def autocenter(self, *args, **kw):
        return self._autocenter(*args, **kw)

    def snapshot(self,
                 path=None,
                 name=None,
                 auto=False,
                 inform=True,
                 return_blob=False,
                 pic_format='.jpg',
                 include_raw=True):
        """
            path: abs path to use
            name: base name to use if auto saving in default dir
            auto: force auto save

            returns:
                    path: local abs path
                    upath: remote abs path
        """

        if path is None:
            if self.auto_save_snapshot or auto:

                if name is None:
                    name = 'snapshot'
                path = unique_path_from_manifest(paths.snapshot_dir, name,
                                                 pic_format)
            elif name is not None:
                if not os.path.isdir(os.path.dirname(name)):
                    path = unique_path_from_manifest(paths.snapshot_dir, name,
                                                     pic_format)
                else:
                    path = name

            else:
                path = self.save_file_dialog()

        if path:
            self.info('saving snapshot {}'.format(path))
            # play camera shutter sound
            # play_sound('shutter')
            if include_raw:
                frame = self.video.get_cached_frame()
                head, _ = os.path.splitext(path)
                raw_path = '{}.tif'.format(head)
                pil_save(frame, raw_path)

            self._render_snapshot(path)
            if self.auto_upload:
                if include_raw:
                    self._upload(raw_path)
                upath = self._upload(path, inform=inform)
                if upath is None:
                    upath = ''

                if inform:
                    if self.keep_local_copy:
                        self.information_dialog(
                            'Snapshot saved: "{}".\nUploaded  : "{}"'.format(
                                path, upath))
                    else:
                        self.information_dialog(
                            'Snapshot uploaded to "{}"'.format(upath))
            else:
                upath = None
                if inform:
                    self.information_dialog(
                        'Snapshot saved to "{}"'.format(path))

            if return_blob:
                with open(path, 'rb') as rfile:
                    im = rfile.read()
                    return path, upath, im
            else:
                return path, upath

    def kill(self):
        """
        """

        super(VideoStageManager, self).kill()
        if self.camera:
            self.camera.save_calibration()

        self.stop_timer = True

        self.canvas.close_video()
        if self.video:
            self.video.close(force=True)

        # if self.use_video_server:
        #            self.video_server.stop()
        # if self._stage_maps:
        #     for s in self._stage_maps:
        #         s.dump_correction_file()

        self.clean_video_archive()

    def clean_video_archive(self):
        if self.use_video_archiver:
            self.info('Cleaning video directory')
            self.video_archiver.clean(('manifest.yaml', ))

    def is_auto_correcting(self):
        return self._auto_correcting

    crop_width = 5
    crop_height = 5

    def get_scores(self, **kw):
        ld = self.lumen_detector
        src = self._get_preprocessed_src()
        return ld.get_scores(src, **kw)

    def find_lum_peak(self, min_distance, blur, **kw):
        ld = self.lumen_detector
        src = self._get_preprocessed_src()

        dim = self.stage_map.g_dimension
        mask_dim = dim * 1.05
        # mask_dim_mm = mask_dim * self.pxpermm
        if src is not None and src.ndim >= 2:
            return ld.find_lum_peak(src,
                                    dim,
                                    mask_dim,
                                    blur=blur,
                                    min_distance=min_distance,
                                    **kw)

    def get_brightness(self, **kw):
        ld = self.lumen_detector
        src = self._get_preprocessed_src()
        dim = self.stage_map.g_dimension
        return ld.get_value(src, dim, **kw)
        # src = self.video.get_cached_frame()
        # csrc = copy(src)
        # src, v = ld.get_value(csrc, **kw)
        # return csrc, src, v

    def get_frame_size(self):
        cw = 2 * self.crop_width * self.pxpermm
        ch = 2 * self.crop_height * self.pxpermm
        return cw, ch

    def close_open_images(self):
        if self.autocenter_manager:
            self.autocenter_manager.close_open_images()

    def finish_move_to_hole(self, user_entry):
        self.debug('finish move to hole')
        # if user_entry and not self.keep_images_open:
        #     self.close_open_images()

    def get_preprocessed_src(self):
        return self._get_preprocessed_src()

    # private
    def _get_preprocessed_src(self):
        ld = self.lumen_detector
        src = copy(self.video.get_cached_frame())
        dim = self.stage_map.g_dimension
        ld.pxpermm = self.pxpermm

        offx, offy = self.canvas.get_screen_offset()
        cropdim = dim * 2.5
        if src is not None:
            if len(src.shape):
                src = ld.crop(src, cropdim, cropdim, offx, offy, verbose=False)
                return src

    def _stage_map_changed_hook(self):
        self.lumen_detector.hole_radius = self.stage_map.g_dimension

    def _upload(self, src, inform=True):
        if not self.use_media_storage:
            msg = 'Use Media Storage not enabled in Laser preferences'
            if inform:
                self.warning_dialog(msg)
            else:
                self.warning(msg)
        else:
            srv = 'pychron.media_storage.manager.MediaStorageManager'
            msm = self.parent.application.get_service(srv)
            if msm is not None:
                d = os.path.split(os.path.dirname(src))[-1]
                dest = os.path.join(self.parent.name, d, os.path.basename(src))
                msm.put(src, dest)

                if not self.keep_local_copy:
                    self.debug('removing {}'.format(src))
                    if src.endswith('.avi'):
                        head, ext = os.path.splitext(src)
                        vd = '{}-images'.format(head)
                        self.debug(
                            'removing video build directory {}'.format(vd))
                        shutil.rmtree(vd)

                    os.remove(src)
                dest = '{}/{}'.format(msm.get_base_url(), dest)
                return dest
            else:
                msg = 'Media Storage Plugin not enabled'
                if inform:
                    self.warning_dialog(msg)
                else:
                    self.warning(msg)

    def _render_snapshot(self, path):
        from chaco.plot_graphics_context import PlotGraphicsContext

        c = self.canvas
        p = None
        was_visible = False
        if not self.render_with_markup:
            p = c.show_laser_position
            c.show_laser_position = False
            if self.points_programmer.is_visible:
                c.hide_all()
                was_visible = True

        gc = PlotGraphicsContext((int(c.outer_width), int(c.outer_height)))
        c.do_layout()
        gc.render_component(c)
        # gc.save(path)
        from pychron.core.helpers import save_gc
        save_gc.save(gc, path)

        if p is not None:
            c.show_laser_position = p

        if was_visible:
            c.show_all()

    def _start_recording(self, path, basename):

        self.info('start video recording {}'.format(path))
        d = os.path.dirname(path)
        if not os.path.isdir(d):
            self.warning('invalid directory {}'.format(d))
            self.warning('using default directory')
            path, _ = unique_path(paths.video_dir, basename, extension='avi')

        self.info('saving recording to path {}'.format(path))

        # if self.use_db:
        # db = self.get_video_database()
        #     db.connect()
        #
        #     v = db.add_video_record(rid=basename)
        #     db.add_path(v, path)
        #     self.info('saving {} to database'.format(basename))
        #     db.commit()

        video = self.video

        crop_to_hole = True
        dim = self.stage_map.g_dimension
        cropdim = dim * 8 * self.pxpermm
        color = self.canvas.crosshairs_color.getRgb()[:3]

        r = int(self.canvas.get_crosshairs_radius() * self.pxpermm)

        # offx, offy = self.canvas.get_screen_offset()

        def renderer(p):
            # cw, ch = self.get_frame_size()
            frame = video.get_cached_frame()
            if frame is not None:
                if not len(frame.shape):
                    return

            frame = copy(frame)
            # ch, cw, _ = frame.shape
            # ch, cw = int(ch), int(cw)

            if crop_to_hole:
                frame = video.crop(frame, 0, 0, cropdim, cropdim)

            if self.render_with_markup:
                # draw crosshairs
                if len(frame.shape) == 2:
                    frame = gray2rgb(frame)

                ch, cw, _ = frame.shape
                ch, cw = int(ch), int(cw)
                y = ch // 2
                x = cw // 2

                cp = circle_perimeter(y, x, r, shape=(ch, cw))

                frame[cp] = color

                frame[line(y, 0, y, x - r)] = color  # left
                frame[line(y, x + r, y, int(cw) - 1)] = color  # right
                frame[line(0, x, y - r, x)] = color  # bottom
                frame[line(y + r, x, int(ch) - 1, x)] = color  # top

            if frame is not None:
                pil_save(frame, p)

        self.video.start_recording(path, renderer)

    def _move_to_hole_hook(self, holenum, correct, autocentered_position):
        args = holenum, correct, autocentered_position
        self.debug('move to hole hook holenum={}, '
                   'correct={}, autocentered_position={}'.format(*args))
        if correct:
            ntries = 1 if autocentered_position else 3

            self._auto_correcting = True
            try:
                self._autocenter(holenum=holenum, ntries=ntries, save=True)
            except BaseException as e:
                self.critical('Autocentering failed. {}'.format(e))

            self._auto_correcting = False

    # def find_center(self):
    #     ox, oy = self.canvas.get_screen_offset()
    #     rpos, src = self.autocenter_manager.calculate_new_center(
    #         self.stage_controller.x,
    #         self.stage_controller.y,
    #         ox, oy,
    #         dim=self.stage_map.g_dimension, open_image=False)
    #
    #     return rpos, src

    # def find_target(self):
    #     if self.video:
    #         ox, oy = self.canvas.get_screen_offset()
    #         src = self.video.get_cached_frame()
    #
    #         ch = cw = self.pxpermm * self.stage_map.g_dimension * 2.5
    #         src = self.video.crop(src, ox, oy, cw, ch)
    #         return self.lumen_detector.find_target(src)
    #
    # def find_best_target(self):
    #     if self.video:
    #         src = self.video.get_cached_frame()
    #         src = self.autocenter_manager.crop(src)
    #         return self.lumen_detector.find_best_target(src)

    def _autocenter(self, holenum=None, ntries=3, save=False, inform=False):
        self.debug('do autocenter')
        rpos = None
        interp = False
        sm = self.stage_map
        st = time.time()
        if self.autocenter_manager.use_autocenter:
            time.sleep(0.1)

            dim = sm.g_dimension
            shape = sm.g_shape
            if holenum is not None:
                hole = sm.get_hole(holenum)
                if hole is not None:
                    dim = hole.dimension
                    shape = hole.shape

            ox, oy = self.canvas.get_screen_offset()
            for ti in range(max(1, ntries)):
                # use machine vision to calculate positioning error
                rpos = self.autocenter_manager.calculate_new_center(
                    self.stage_controller.x,
                    self.stage_controller.y,
                    ox,
                    oy,
                    dim=dim,
                    shape=shape)

                if rpos is not None:
                    self.linear_move(*rpos,
                                     block=True,
                                     source='autocenter',
                                     use_calibration=False,
                                     update_hole=False,
                                     velocity_scalar=0.1)
                    time.sleep(0.1)
                else:
                    self.snapshot(auto=True,
                                  name='pos_err_{}_{}'.format(holenum, ti),
                                  inform=inform)
                    break

                    # if use_interpolation and rpos is None:
                    #     self.info('trying to get interpolated position')
                    #     rpos = sm.get_interpolated_position(holenum)
                    #     if rpos:
                    #         s = '{:0.3f},{:0.3f}'
                    #         interp = True
                    #     else:
                    #         s = 'None'
                    #     self.info('interpolated position= {}'.format(s))

        if rpos:
            corrected = True
            # add an adjustment value to the stage map
            if save and holenum is not None:
                sm.set_hole_correction(holenum, *rpos)
                sm.dump_correction_file()
                #            f = 'interpolation' if interp else 'correction'
        else:
            #            f = 'uncorrected'
            corrected = False
            if holenum is not None:
                hole = sm.get_hole(holenum)
                if hole:
                    rpos = hole.nominal_position

        self.debug('Autocenter duration ={}'.format(time.time() - st))
        return rpos, corrected, interp

    # ===============================================================================
    # views
    # ===============================================================================
    # ===============================================================================
    # view groups
    # ===============================================================================

    # ===============================================================================
    # handlers
    # ===============================================================================
    def _configure_camera_device_button_fired(self):
        if self.video:
            self.video.load_configuration(self.video_configuration_path)

            if hasattr(self.video.cap, 'reload_configuration'):
                self.video.cap.reload_configuration(
                    self.video_configuration_path)
            self.lumen_detector.pixel_depth = self.video.pixel_depth

    def _update_zoom(self, v):
        if self.camera:
            self._update_xy_limits()

    @on_trait_change('parent:motor_event')
    def _update_motor(self, new):
        print('motor event', new, self.canvas, self.canvas.camera)
        # s = self.stage_controller
        if self.camera:
            if not isinstance(new, (int, float)):
                args, _ = new
                name, v = args[:2]
            else:
                name = 'zoom'
                v = new

            if name == 'zoom':
                self._update_xy_limits()
                # pxpermm = self.canvas.camera.set_limits_by_zoom(v, s.x, s.y)
                # self.pxpermm = pxpermm
            elif name == 'beam':
                self.lumen_detector.beam_radius = v / 2.0

    def _pxpermm_changed(self, new):
        if self.autocenter_manager:
            self.autocenter_manager.pxpermm = new
            self.lumen_detector.pxpermm = new
            # self.lumen_detector.mask_radius = new*self.stage_map.g_dimension

    def _autocenter_button_fired(self):
        self.goto_position(self.calibrated_position_entry,
                           autocenter_only=True)

    #     def _configure_autocenter_button_fired(self):
    #         info = self.autocenter_manager.edit_traits(view='configure_view',
    #                                                 kind='livemodal')
    #         if info.result:
    #             self.autocenter_manager.dump_detector()

    def _snapshot_button_fired(self):
        n = 1
        if self.snapshot_mode == '3 Burst':
            n = 3
        elif self.snapshot_mode == '10 Burst':
            n = 10

        bd = self.burst_delay * 0.001
        delay = n > 1
        for i in range(n):
            st = time.time()
            self.snapshot(inform=False)
            if delay:
                time.sleep(max(0, bd - time.time() + st))

    def _record_fired(self):
        #            time.sleep(4)
        #            self.stop_recording()
        if self.is_recording:

            self.stop_recording()
        else:

            self.start_recording()

    def _use_video_server_changed(self):
        if self.use_video_server:
            self.video_server.start()
        else:
            self.video_server.stop()

    def _get_camera_zoom_coefficients(self):
        return self.camera.zoom_coefficients

    def _set_camera_zoom_coefficients(self, v):
        self.camera.zoom_coefficients = ','.join(map(str, v))
        self._update_xy_limits()

    def _validate_camera_zoom_coefficients(self, v):
        try:
            return list(map(float, v.split(',')))
        except ValueError:
            pass

    def _update_xy_limits(self):
        z = 0
        if self.parent is not None:
            zoom = self.parent.get_motor('zoom')
            if zoom is not None:
                z = zoom.data_position

        x = self.stage_controller.get_current_position('x')
        y = self.stage_controller.get_current_position('y')
        if self.camera:
            pxpermm = self.camera.set_limits_by_zoom(z, x, y, self.canvas)
            self.pxpermm = pxpermm
            self.debug('updated xy limits zoom={}, pxpermm={}'.format(
                z, pxpermm))

        self.canvas.request_redraw()

    def _get_record_label(self):
        return 'Start Recording' if not self.is_recording else 'Stop'

    # ===============================================================================
    # factories
    # ===============================================================================
    def _canvas_factory(self):
        """
        """
        v = VideoLaserTrayCanvas(stage_manager=self, padding=30)
        return v

    def _canvas_editor_factory(self):
        e = super(VideoStageManager, self)._canvas_editor_factory()
        e.stop_timer = 'stop_timer'
        return e

    # ===============================================================================
    # defaults
    # ===============================================================================
    def _camera_default(self):
        klass = YamlCamera
        # p = os.path.join(self.configuration_dir_path, 'camera.yaml')

        p = self.video_configuration_path
        if p is not None:
            if not os.path.isfile(p):
                klass = Camera
                pp = os.path.join(self.configuration_dir_path, 'camera.cfg')
                if not os.path.isfile(pp):
                    self.warning_dialog(
                        'No Camera configuration file a {} or {}'.format(
                            p, pp))
                p = pp

            camera = klass()
            camera.load(p)
        else:
            camera = Camera()

        camera.set_limits_by_zoom(0, 0, 0, self.canvas)
        self._camera_zoom_coefficients = camera.zoom_coefficients
        return camera

    def _lumen_detector_default(self):
        ld = LumenDetector()
        ld.pixel_depth = self.video.pixel_depth
        return ld

    def _video_default(self):
        v = Video()
        self.canvas.video = v
        return v

    def _video_server_default(self):
        from pychron.image.video_server import VideoServer

        return VideoServer(video=self.video)

    def _video_archiver_default(self):
        from pychron.core.helpers.archiver import Archiver

        return Archiver()

    def _autocenter_manager_default(self):
        if self.parent.mode != 'client':
            # from pychron.mv.autocenter_manager import AutoCenterManager
            if 'co2' in self.parent.name.lower():
                from pychron.mv.autocenter_manager import CO2AutocenterManager
                klass = CO2AutocenterManager
            else:
                from pychron.mv.autocenter_manager import DiodeAutocenterManager
                klass = DiodeAutocenterManager

            return klass(video=self.video,
                         canvas=self.canvas,
                         application=self.application)

    def _autofocus_manager_default(self):
        if self.parent.mode != 'client':
            from pychron.mv.focus.autofocus_manager import AutoFocusManager

            return AutoFocusManager(video=self.video,
                                    laser_manager=self.parent,
                                    stage_controller=self.stage_controller,
                                    canvas=self.canvas,
                                    application=self.application)
예제 #48
0
    def dump_sample(self):
        self.debug('dump sample')

        if self._dumper_thread is None:
            self._dumper_thread = Thread(name='DumpSample', target=self._dump_sample)
            self._dumper_thread.start()
예제 #49
0
class BaseStageManager(Manager):
    keyboard_focus = Event
    canvas_editor_klass = LaserComponentEditor
    tray_calibration_manager = Instance(TrayCalibrationManager)
    stage_map_klass = LaserStageMap
    stage_map_name = Str
    stage_map_names = List
    stage_map = Instance(BaseStageMap)
    canvas = Instance(MapCanvas)

    # root = Str(paths.map_dir)
    calibrated_position_entry = String(enter_set=True, auto_set=False)

    move_thread = None
    temp_position = None
    temp_hole = None
    root = Str
    # use_modified = Bool(True)  # set true to use modified affine calculation
    def motor_event_hook(self, name, value, *args, **kw):
        pass

    def goto_position(self, pos):
        raise NotImplementedError

    def refresh_stage_map_names(self):
        sms = get_stage_map_names(root=self.root)
        self.stage_map_names = sms

    def load(self):
        self.refresh_stage_map_names()

        psm = self._load_previous_stage_map()
        sms = self.stage_map_names
        sm = None
        if psm in sms:
            sm = psm
        elif sms:
            sm = sms[0]

        if sm:
            self.stage_map_name = ''
            self.stage_map_name = sm

    def kill(self):
        r = super(BaseStageManager, self).kill()
        self._save_stage_map()
        return r

    @property
    def stage_map_path(self):
        return os.path.join(paths.hidden_dir, '{}.stage_map'.format(self.id))

    def canvas_editor_factory(self):
        return self.canvas_editor_klass(keyboard_focus='keyboard_focus')

    def move_to_hole(self, hole, **kw):
        self._move(self._move_to_hole, hole, name='move_to_hole', **kw)

    def get_calibrated_position(self, pos, key=None):
        smap = self.stage_map

        # use a affine transform object to map
        canvas = self.canvas
        ca = canvas.calibration_item

        # check if a calibration applies to this hole
        hole_calibration = get_hole_calibration(smap.name, key)
        if hole_calibration:
            self.debug('Using hole calibration')
            ca = hole_calibration

        if ca:
            rot = ca.rotation
            cpos = ca.center
            scale = ca.scale

            self.debug('Calibration parameters: '
                       'rot={:0.3f}, cpos={} scale={:0.3f}'.format(rot, cpos,
                                                                   scale))
            pos = smap.map_to_calibration(pos, cpos, rot,
                                          scale=scale)

        return pos

    def update_axes(self):
        """
        """
        self.info('querying axis positions')
        self._update_axes()

    # private
    def _update_axes(self):
        pass

    def _move_to_hole(self, key, correct_position=True, **kw):
        pass

    def _stop(self):
        pass

    def _move(self, func, pos, name=None, *args, **kw):
        if pos is None:
            return

        if self.move_thread and self.move_thread.isRunning():
            self._stop()

        if name is None:
            name = func.__name__

        self.move_thread = Thread(name='stage.{}'.format(name),
                                  target=func, args=(pos,) + args, kwargs=kw)
        self.move_thread.start()

    def _canvas_factory(self):
        raise NotImplementedError

    def _stage_map_changed_hook(self):
        pass

    # handlers
    def _calibrated_position_entry_changed(self, new):
        self.debug('User entered calibrated position {}'.format(new))
        self.goto_position(new)

    def _stage_map_name_changed(self, new):
        if new:
            self.debug('setting stage map to {}'.format(new))
            root = self.root
            path = os.path.join(root, add_extension(new, '.txt'))
            sm = self.stage_map_klass(file_path=path)

            self.tray_calibration_manager.load_calibration(stage_map=new)

            self.canvas.set_map(sm)
            self.canvas.request_redraw()

            self.stage_map = sm

            self._stage_map_changed_hook()

    # defaults
    def _root_default(self):
        return paths.map_dir

    def _tray_calibration_manager_default(self):
        t = TrayCalibrationManager(parent=self,
                                   canvas=self.canvas)
        return t

    def _canvas_default(self):
        return self._canvas_factory()

    def _save_stage_map(self):
        p = self.stage_map_path
        self.info('saving stage_map {} to {}'.format(self.stage_map_name, p))
        with open(p, 'wb') as f:
            pickle.dump(self.stage_map_name, f)

    def _load_previous_stage_map(self):
        p = self.stage_map_path
        if os.path.isfile(p):
            self.info('loading previous stage map from {}'.format(p))
            with open(p, 'rb') as f:
                try:
                    sm = pickle.load(f)
                    if not sm.endswith('.center'):
                        return sm
                except (pickle.PickleError, ValueError):
                    pass
예제 #50
0
class Scanner(Loggable):
    """
        Scanner is a base class for displaying a scan of device data

        ScanableDevices has this ability built in but more complicated scans are
        best done here. ScanableDevice scan is best used from continuous long term recording
        of a single or multiple values
    """

    _graph = Instance(StreamStackedGraph)
    manager = Instance(ILaserManager)

    data_manager = Instance(CSVDataManager, ())
    '''
        list of callables. should return a signal value for graphing
    '''
    functions = List
    static_values = List

    scan_period = Int  # ms
    stop_event = Event
    setpoint = Float(enter_set=True, auto_set=False)
    control_path = File

    _warned = False
    _scanning = Bool
    _directory = None
    _base_frame_name = None

    def new_static_value(self, name, *args, **kw):
        self.static_values.append((name, None))
        if args:
            self.set_static_value(name, *args, **kw)

    def set_static_value(self, name_or_idx, value, plotid=None):
        """
            if the plotid is not None add a horizontal guide at value
        """
        if isinstance(name_or_idx, str):
            name_or_idx = next(
                (i for i, (e, a) in enumerate(self.static_values)), None)

        if name_or_idx is not None:
            name = self.static_values[name_or_idx][0]
            self.static_values[name_or_idx] = (name, value)

        if plotid is not None:
            self._graph.add_horizontal_rule(value, plotid=plotid)

    def setup(self, directory=None, base_frame_name=None):
        self._directory = directory
        self._base_frame_name = base_frame_name

    def new_function(self, function, name=None):
        if name is None:
            name = function.func_name

        # g = self.graph
        #        func = self.functions
        #        kw = dict(ytitle=name,)
        #        n = len(func)
        #        if n == 0:
        #            kw['xtitle'] = 'Time'

        self.functions.append((function, name))

    def make_graph(self):
        g = StreamStackedGraph(window_x=50, window_y=100)

        for i, (_, name) in enumerate(self.functions):
            kw = dict(ytitle=name, )
            if i == 0:
                kw['xtitle'] = 'Time'

            g.new_plot(data_limit=3000, **kw)
            g.new_series(plotid=i)
        self._graph = g
        return g

    def stop(self):
        self._timer.Stop()
        self._scanning = False
        self.stop_event = True
        self.info('scan stopped')
        if self.manager:
            self._stop_hook()
            self.manager.disable_device()

    def _stop_hook(self):
        pass

    def execute(self):

        if self._scanning:
            self.stop()
        else:
            self.data_manager.new_frame(directory=self._directory,
                                        base_frame_name=self._base_frame_name)
            # write metadata if available
            self._write_metadata()

            # make header row
            header = ['t'] + self._make_func_names() + \
                     [n for n, _ in self.static_values]
            self.data_manager.write_to_frame(header)

            self._scanning = True
            if self.manager:
                if self.manager.enable_device():
                    self._scanning = True

        return self._scanning

    def do_scan(self):
        self._starttime = time.time()
        self._execute()

    def _execute(self):

        yd = self._read_control_path()

        if yd is None:
            sp = 1000
        else:
            sp = yd['period']

        # starts automatically
        self.debug('scan starting')
        self._timer = Timer(sp, self._scan)

        self.info('scan started')

        #            yd = self._read_control_path()
        if yd is not None:
            # start a control thread
            self._control_thread = Thread(target=self._control, args=(yd, ))
            self._control_thread.start()
            self.info('control started')
            #        if self.manager:
            #            if self.manager.enable_device():
            #
            #                # starts automatically
            #                self.debug('scan starting')
            #                self._timer = Timer(sp, self._scan)
            #
            #                self.info('scan started')
            #                self._scanning = True
            #    #            yd = self._read_control_path()
            #                if yd is not None:
            #                    # start a control thread
            #                    self._control_thread = Thread(target=self._control,
            #                                                  args=(yd,)
            #                                                  )
            #                    self._control_thread.start()
            #                    self.info('control started')
            #            else:
            #                self.warning('Could not enable device')
            #        else:
            #            self.warning('no manager available')

    def _control(self, ydict):
        self.start_control_hook(ydict)

        #        if self.manager:
        #            if self.manager.temperature_controller:
        #                self.manager.temperature_controller.enable_tru_tune = True

        start_delay = ydict['start_delay']
        end_delay = ydict['end_delay']
        setpoints = ydict['setpoints']
        print setpoints
        self.set_static_value('Setpoint', 0)
        time.sleep(start_delay)
        for args in setpoints:
            t = args[0]
            if self._scanning:
                self.setpoint = t
                self._set_power_hook(t)

                self.set_static_value('Setpoint', t, plotid=0)
                self._maintain_setpoint(t, *args[1:])

        if self._scanning:
            self.setpoint = 0
            self.set_static_value('Setpoint', 0)
            self._set_power_hook(0)
            # if self.manager:
            # self.manager.set_laser_temperature(0)

            time.sleep(end_delay)
            self.stop()

        self.end_control_hook(self._scanning)

    def _set_power_hook(self, t):
        if self.manager:
            self.manager.set_laser_temperature(t)

    def _maintain_setpoint(self, t, d, *args):
        self.info('setting setpoint to {} for {}s'.format(t, d))
        st = time.time()
        while time.time() - st < d and self._scanning:
            time.sleep(1)

    def start_control_hook(self, *args, **kw):
        pass

    def end_control_hook(self, ok):
        pass

    def _make_func_names(self):
        return [name for _, name in self.functions]

    def _write_metadata(self):
        pass

    def _scan(self):
        functions = self.functions
        data = []
        record = self._graph.record
        #        record = self.graph.record

        x = time.time() - self._starttime
        for i, (func, _) in enumerate(functions):
            try:
                if hasattr(func, 'next'):
                    func = func.next

                v = float(func())
                data.append(v)

                # do_after no longer necessary with Qt
                record(v, plotid=i, x=x, do_after=None)
            except Exception, e:
                print 'exception', e

        sv = []
        for _, v in self.static_values:
            if v is None:
                v = ''
            sv.append(v)

        data = [x] + data + sv
        self.data_manager.write_to_frame(data)
예제 #51
0
class FusionsLaserManager(LaserManager):
    """
    """

    laser_controller = Instance(FusionsLogicBoard)
    fiber_light = Instance(FiberLight)
    #    optics_view = Instance(OpticsView)

    #    beam = DelegatesTo('laser_controller')
    #    beammin = DelegatesTo('laser_controller')
    #    beammax = DelegatesTo('laser_controller')
    #    update_beam = DelegatesTo('laser_controller')
    #    beam_enabled = DelegatesTo('laser_controller')
    # #    beam_enabled = Bool(True)
    #
    #    zoom = DelegatesTo('laser_controller')
    #    zoommin = DelegatesTo('laser_controller')
    #    zoommax = DelegatesTo('laser_controller')
    #    update_zoom = DelegatesTo('laser_controller')
    #    execute_button = DelegatesTo('laser_script_executor')
    #    execute_label = DelegatesTo('laser_script_executor')

    pointer = Event
    pointer_state = Bool(False)
    pointer_label = Property(depends_on='pointer_state')

    step_heat_manager = None

    lens_configuration = Str('gaussian')
    lens_configuration_dict = Dict
    lens_configuration_names = List

    power_timer = None
    brightness_timer = None

    power_graph = None
    _prev_power = 0
    record_brightness = Bool
    #     recording_zoom = Float

    #     record = Event
    #     record_label = Property(depends_on='_recording_power_state')
    _recording_power_state = Bool(False)

    simulation = DelegatesTo('laser_controller')

    data_manager = None
    _data_manager_lock = None

    _current_rid = None

    #    brightness_meter = Instance(BrightnessPIDManager)

    chiller = Any

    motor_event = Event

    _degas_thread = None

    @on_trait_change('laser_controller:refresh_canvas')
    def refresh_canvas(self):
        if self.stage_manager:
            self.stage_manager.canvas.request_redraw()

            # ===============================================================================
            #   IExtractionDevice interface
            # ===============================================================================

    def extract(self, power, **kw):
        self.enable_laser()
        self.set_laser_power(power, **kw)

    def end_extract(self):
        self.disable_laser()
        self.stop_pattern()

    def open_motor_configure(self):
        self.laser_controller.open_motor_configure()

    #     def _record_fired(self):
    #         if self._recording_power_state:
    #             save = self.db_save_dialog()
    #             self.stop_power_recording(delay=0, save=save)
    #         else:
    #             t = Thread(name='fusions.power_record',
    #                        target=self.start_power_recording, args=('Manual',))
    #             t.start()

    #        self._recording_power_state = not self._recording_power_state

    def bind_preferences(self, pref_id):
        self.debug('binding preferences')
        super(FusionsLaserManager, self).bind_preferences(pref_id)
        bind_preference(self, 'recording_zoom',
                        '{}.recording_zoom'.format(pref_id))
        bind_preference(self, 'record_brightness',
                        '{}.record_brightness'.format(pref_id))
        self.debug('preferences bound')

    def set_light(self, value):
        self.set_light_state(value > 0)
        self.set_light_intensity(value)

    def set_light_state(self, state):
        if state:
            self.fiber_light.power_off()
        else:
            self.fiber_light.power_on()

    def set_light_intensity(self, v):
        self.fiber_light.intensity = min(max(0, v), 100)

    @on_trait_change('pointer')
    def pointer_ononff(self):
        """
        """
        self.pointer_state = not self.pointer_state
        self.laser_controller.set_pointer_onoff(self.pointer_state)

    def get_laser_watts(self):
        return self._requested_power

    def get_coolant_temperature(self, **kw):
        """
        """
        chiller = self.chiller
        if chiller is not None:
            return chiller.get_coolant_out_temperature(**kw)

    def get_coolant_status(self, **kw):

        chiller = self.chiller
        if chiller is not None:
            return chiller.get_faults(**kw)

    def do_motor_initialization(self, name):
        if self.laser_controller:
            motor = self.laser_controller.get_motor(name)
            if motor is not None:
                n = 4
                from pychron.core.ui.progress_dialog import myProgressDialog

                pd = myProgressDialog(max=n, size=(550, 15))
                pd.open()
                motor.initialize(progress=pd)
                pd.close()

    def set_beam_diameter(self, bd, force=False, **kw):
        """
        """
        result = False
        motor = self.get_motor('beam')
        if motor is not None:
            if motor.enabled or force:
                self.set_motor('beam', bd, **kw)
                result = True
            else:
                self.info('beam disabled by lens configuration {}'.format(self.lens_configuration))
        return result

    def set_zoom(self, z, **kw):
        """
        """
        self.set_motor('zoom', z, **kw)

    def set_motor_lock(self, name, value):
        m = self.get_motor(name)
        if m is not None:
            m.locked = to_bool(value)
            return True

    def set_motor(self, *args, **kw):
        self.motor_event = (args, kw)
        return self.laser_controller.set_motor(*args, **kw)

    def get_motor(self, name):
        return next((mi for mi in self.laser_controller.motors if mi.name == name), None)

    def do_autofocus(self, **kw):
        if self.use_video:
            am = self.stage_manager.autofocus_manager
            am.passive_focus(block=True, **kw)

    def take_snapshot(self, *args, **kw):
        if self.use_video:
            return self.stage_manager.snapshot(
                auto=True,
                inform=False,
                *args, **kw)

    def start_video_recording(self, name='video', *args, **kw):
        if self.use_video:
            self.stage_manager.start_recording(basename=name)

    def stop_video_recording(self, *args, **kw):
        if self.use_video:
            self.stage_manager.stop_recording()

    def degasser_factory(self):
        from pychron.mv.degas.degasser import Degasser

        dm = Degasser(
            laser_manager=self,
            video=self.stage_manager.video,
        )
        return dm

    def do_machine_vision_degas(self, lumens, duration, new_thread=False):
        if self.use_video:
            dm = self.degasser_factory()

            def func():
                dm.degas(lumens, duration)

            if new_thread:
                self._degas_thread = Thread(target=func)
                self._degas_thread.start()
            else:
                func()

    def get_brightness(self):
        if self.use_video:
            return self.stage_manager.get_brightness()
        else:
            return super(FusionsLaserManager, self).get_brightness()

    def is_degassing(self):
        if self._degas_thread:
            return self._degas_thread.isRunning()
            # ===============================================================================
            # pyscript interface
            # ===============================================================================

    def _move_to_position(self, position, autocenter):

        if self.stage_manager is not None:
            if isinstance(position, tuple):
                if len(position) > 1:
                    x, y = position[:2]
                    self.stage_manager.linear_move(x, y)
                    if len(position) == 3:
                        self.stage_manager.set_z(position[2])
            else:

                self.stage_manager.move_to_hole(position)
            return True

    def set_stage_map(self, mapname):
        if self.stage_manager is not None:
            self.stage_manager.set_stage_map(mapname)

    def _enable_hook(self, **kw):
        resp = self.laser_controller._enable_laser(**kw)
        if self.laser_controller.simulation:
            resp = True

        return resp

    def _disable_hook(self):
        resp = self.laser_controller._disable_laser()
        if self.laser_controller.simulation:
            resp = True

        return resp

    def show_motion_controller_manager(self):
        """
        """
        stage_controller = self.stage_manager.stage_controller
        package = 'pychron.managers.motion_controller_managers'
        if 'Aerotech' in stage_controller.__class__.__name__:
            klass = 'AerotechMotionControllerManager'
            package += '.aerotech_motion_controller_manager'
        else:
            klass = 'NewportMotionControllerManager'
            package += '.newport_motion_controller_manager'

        module = __import__(package, globals(), locals(), [klass], -1)
        factory = getattr(module, klass)
        m = factory(motion_controller=stage_controller)
        self.open_view(m)

    # ========================= views =========================

    def get_control_buttons(self):
        """
        """
        return [('enable', 'enable_label', None), ]

    #    def get_control_items(self):
    #        '''
    #        '''
    # #        return Item('laser_controller', show_label=False,
    # #                    editor=InstanceEditor(view='control_view'),
    # #                    style='custom',
    # #                    springy=False, height= -100)
    #
    # #        return self.laser_controller.get_control_group()
    #        s = [('zoom', 'zoom', {}),
    #            ('beam', 'beam', {'enabled_when':'object.beam_enabled'})
    #            ]
    #        return self._update_slider_group_factory(s)

    #    def get_lens_configuration_group(self):
    #        return Item('lens_configuration',
    #                    editor=EnumEditor(values=self.lens_configuration_names)
    #                    )

    #    def get_optics_group(self):
    #        csliders = self.get_control_items()
    #        vg = HGroup(csliders,
    #                      show_border=True,
    #                      label='Optics', springy=False
    #                      )
    #
    #        lens_config = self.get_lens_configuration_group()
    #        if lens_config:
    #            vg.content.insert(0, lens_config)
    #
    #        return vg
    #    def get_control_button_group(self):
    #        grp = HGroup(spring, Item('enabled_led', show_label=False, style='custom', editor=LEDEditor()),
    #                        self._button_group_factory(self.get_control_buttons(), orientation='h'),
    # #                                  springy=True
    #                    )
    #        return grp
    def get_power_group(self):
        power_grp = VGroup(
            self.get_control_button_group(),
            HGroup(
                Item('requested_power', style='readonly',
                     format_str='%0.2f',
                     width=100),
                spring,
                Item('units', show_label=False, style='readonly'),
                spring),
            #                           Item('laser_script_executor', show_label=False, style='custom'),
            #                           self._button_factory('execute_button', 'execute_label'),
            show_border=True,
            #                           springy=True,
            label='Power')

        ps = self.get_power_slider()
        if ps:
            #            ps.springy = True
            power_grp.content.append(ps)
        return power_grp

    #     def get_additional_group(self):
    #         og = Group(Item('laser_controller', show_label=False,
    #                     editor=InstanceEditor(view='control_view'),
    #                     style='custom'),
    #                    label='Optics',
    #                    )
    #         ac = Group(
    #                    og,
    #                    show_border=True,
    #                    label='Additional Controls',
    #                    layout='tabbed')
    #
    #         aclist = self.get_additional_controls()
    #         if aclist is None:
    #             og.label = 'Optics'
    #             og.show_border = True
    #             ac = og
    #         else:
    #             for ai in aclist:
    #                 ac.content.append(ai)
    #         return ac

    #     def get_control_group(self):
    #         '''
    #         '''
    #         power_grp = self.get_power_group()
    #         pulse_grp = Group(Item('pulse', style='custom', show_label=False),
    #                         label='Pulse', show_border=True
    #                         )
    #         power_grp = HGroup(power_grp, pulse_grp)
    #         ac = self.get_additional_group()
    #         g = HGroup(power_grp, ac)
    #
    #         return g

    def _get_pointer_label(self):
        """
        """
        return 'Pointer ON' if not self.pointer_state else 'Pointer OFF'

    def _get_record_label(self):
        return 'Record' if not self._recording_power_state else 'Stop'

    def _get_record_brightness(self):
        return self.record_brightness and self._get_machine_vision() is not None

    # ========================= defaults =======================
    # def get_power_database(self):
    # from pychron.database.adapters.power_adapter import PowerAdapter
    #
    #     db = PowerAdapter(name=self.dbname,
    #                       kind='sqlite')
    #     return db

    # def get_power_calibration_database(self):
    # from pychron.database.adapters.power_calibration_adapter import PowerCalibrationAdapter
    #
    #     db = PowerCalibrationAdapter(name=self.dbname,
    #                                  kind='sqlite')
    #     return db

    #    def _subsystem_default(self):
    #        '''
    #        '''
    #        return ArduinoSubsystem(name='arduino_subsystem_2')

    #    def _brightness_meter_default(self):
    #        if self.use_video:
    #            b = BrightnessPIDManager(parent=self)
    # #            b.brightness_manager.video = self.stage_manager.video
    #
    #            return b

    def _fiber_light_default(self):
        """
        """
        return FiberLight(name='fiber_light')
예제 #52
0
class PatternExecutor(Patternable):
    """
         a pattern is only good for one execution.
         self.pattern needs to be reset after stop or finish using load_pattern(name_or_pickle)
    """
    controller = Any
    laser_manager = Any
    show_patterning = Bool(False)
    _alive = Bool(False)

    def __init__(self, *args, **kw):
        super(PatternExecutor, self).__init__(*args, **kw)
        self._next_point = None
        self.pattern = None
        self._xy_thread = None
        self._power_thread = None
        self._z_thread = None

    def start(self, show=False):
        self._alive = True

        if show:
            self.show_pattern()

        if self.pattern:
            self.pattern.clear_graph()

    def finish(self):
        self._alive = False
        self.close_pattern()
        self.pattern = None

    def set_stage_values(self, sm):
        if self.pattern:
            self.pattern.set_stage_values(sm)

    def set_current_position(self, x, y, z):
        if self.isPatterning():
            graph = self.pattern.graph
            graph.set_data([x], series=1, axis=0)
            graph.set_data([y], series=1, axis=1)

            graph.add_datum((x, y), series=2)

            graph.redraw()

    def load_pattern(self, name_or_pickle):
        """
            look for name_or_pickle in local pattern dir

            if not found try interpreting name_or_pickle is a pickled name_or_pickle

        """
        if name_or_pickle is None:
            path = self.open_file_dialog()
            if path is None:
                return
        else:
            path = self.is_local_pattern(name_or_pickle)

        if path:
            wfile = open(path, 'rb')
        else:
            # convert name_or_pickle into a file like obj
            wfile = StringIO(name_or_pickle)

        # self._load_pattern sets self.pattern
        pattern = self._load_pattern(wfile, path)

        self.on_trait_change(self.stop, 'canceled')
        return pattern

    def is_local_pattern(self, name):
        def test_name(ni):
            path = os.path.join(paths.pattern_dir, ni)
            if os.path.isfile(path):
                return path

        for ni in (name, name + '.lp'):
            p = test_name(ni)
            if p:
                return p

    def stop(self):
        self._alive = False
        if self.controller:
            self.info('User requested stop')
            self.controller.stop()

        if self.pattern is not None:
            if self.controller:
                self.controller.linear_move(self.pattern.cx,
                                            self.pattern.cy,
                                            source='pattern stop')
            # self.pattern.close_ui()
            self.info('Pattern {} stopped'.format(self.pattern_name))

            # prevent future stops (AbortJogs from massspec) from executing
            self.pattern = None

    def isPatterning(self):
        return self._alive

    def close_pattern(self):
        pass

    def show_pattern(self):
        self.pattern.window_x = 50
        self.pattern.window_y = 50
        open_view(self.pattern, view='graph_view')

    def execute(self, block=False, duration=None, thread_safe=True):
        """
            if block is true wait for patterning to finish
            before returning
        """
        if not self.pattern:
            return

        self.start(show=self.show_patterning)
        evt = None
        # if current_thread().name != 'MainThread':
        if thread_safe:
            evt = Event()
            invoke_in_main_thread(self._pre_execute, evt)
            while not evt.is_set():
                time.sleep(0.05)
        else:
            self._pre_execute(evt)

        self.debug('execute xy pattern')

        xyp = self.pattern.xy_pattern_enabled
        if duration:
            self.pattern.external_duration = float(duration)

        if xyp:
            self._xy_thread = Thread(target=self._execute_xy_pattern)
            self._xy_thread.start()

        pp = self.pattern.power_pattern
        if pp:
            self.debug('execute power pattern')
            self._power_thread = Thread(target=self._execute_power_pattern)
            self._power_thread.start()

        zp = self.pattern.z_pattern

        if zp:
            self.debug('execute z pattern')
            self._z_thread = Thread(target=self._execute_z_pattern)
            self._z_thread.start()

        if block:
            if self._xy_thread:
                self._xy_thread.join()
            if self._z_thread:
                self._z_thread.join()
            if self._power_thread:
                self._power_thread.join()

            self.finish()

    def _pre_execute(self, evt):
        self.debug('pre execute')
        pattern = self.pattern

        kind = pattern.kind
        if kind in ('SeekPattern', 'DragonFlyPeakPattern'):
            self._info = open_view(pattern, view='execution_graph_view')

        if evt is not None:
            evt.set()
        self.debug('pre execute finished')

    def _execute_power_pattern(self):
        pat = self.pattern
        self.info('starting power pattern {}'.format(pat.name))

        def func(v):
            self.laser_manager.set_laser_power(v)

        self._execute_(func, pat.power_values(), pat.power_sample,
                       'power pattern setpoint={value}')

    def _execute_z_pattern(self):
        pat = self.pattern
        self.info('starting power pattern {}'.format(pat.name))

        def func(v):
            self.controller.set_z(v)

        self._execute_(func, pat.z_values(), pat.z_sample,
                       'z pattern z={value}')

    def _execute_(self, func, vs, period, msg):
        for v in vs:
            st = time.time()
            self.debug(msg.format(value=v))
            func(v)

            et = st - time.time()
            p = period - et
            time.sleep(p)

    def _execute_xy_pattern(self):
        pat = self.pattern
        self.info('starting pattern {}'.format(pat.name))
        st = time.time()
        self.controller.update_position()
        time.sleep(1)
        pat.cx, pat.cy = self.controller.x, self.controller.y
        try:
            for ni in range(pat.niterations):
                if not self.isPatterning():
                    break

                self.info('doing pattern iteration {}'.format(ni))
                self._execute_iteration(ni)

            self.controller.linear_move(pat.cx,
                                        pat.cy,
                                        block=True,
                                        source='execute_xy_pattern')
            if pat.disable_at_end:
                self.laser_manager.disable_device()

            self.finish()
            self.info(
                'finished pattern: transit time={:0.1f}s'.format(time.time() -
                                                                 st))

        except (TargetPositionError, PositionError) as e:
            self.finish()
            self.controller.stop()
            self.laser_manager.emergency_shutoff(str(e))

    def _execute_iteration(self, iteration):
        controller = self.controller
        pattern = self.pattern
        if controller is not None:

            kind = pattern.kind
            if kind == 'ArcPattern':
                self._execute_arc(controller, pattern)
            elif kind == 'CircularContourPattern':
                self._execute_contour(controller, pattern)
            elif kind in ('SeekPattern', 'DragonFlyPeakPattern'):
                self._execute_seek(controller, pattern)
            else:
                self._execute_points(controller,
                                     pattern,
                                     iteration,
                                     multipoint=False)

    def _execute_points(self,
                        controller,
                        pattern,
                        iteration,
                        multipoint=False):
        pts = pattern.points_factory()
        if multipoint:
            controller.multiple_point_move(pts, velocity=pattern.velocity)
        else:
            for i, (x, y) in enumerate(pts):
                self.debug('Pattern Point. {},{}'.format(iteration, i))
                if not self.isPatterning():
                    break

                # skip first point after first iteration
                if iteration and not i:
                    self.debug('skipping first point')
                    continue

                self.debug('Pattern Point. {},{}: {},{}'.format(
                    iteration, i, x, y))
                controller.linear_move(x,
                                       y,
                                       block=True,
                                       velocity=pattern.velocity)

    def _execute_contour(self, controller, pattern):
        for ni in range(pattern.nsteps):
            if not self.isPatterning():
                break

            r = pattern.radius * (1 + ni * pattern.percent_change)
            self.info('doing circular contour {} {}'.format(ni + 1, r))
            controller.single_axis_move('x', pattern.cx + r, block=True)
            controller.arc_move(pattern.cx, pattern.cy, 360, block=True)
            time.sleep(0.1)

    def _execute_arc(self, controller, pattern):
        controller.single_axis_move('x', pattern.radius, block=True)
        controller.arc_move(pattern.cx,
                            pattern.cy,
                            pattern.degrees,
                            block=True)

    def _execute_seek(self, controller, pattern):

        duration = pattern.duration
        total_duration = pattern.total_duration

        lm = self.laser_manager
        sm = lm.stage_manager
        ld = sm.lumen_detector

        ld.mask_kind = pattern.mask_kind
        ld.custom_mask = pattern.custom_mask_radius

        osdp = sm.canvas.show_desired_position
        sm.canvas.show_desired_position = False

        st = time.time()
        self.debug('Pre seek delay {}'.format(pattern.pre_seek_delay))
        time.sleep(pattern.pre_seek_delay)

        self.debug('starting seek')
        self.debug('total duration {}'.format(total_duration))
        self.debug('dwell duration {}'.format(duration))

        if pattern.kind == 'DragonFlyPeakPattern':
            try:
                self._dragonfly_peak(st, pattern, lm, controller)
            except BaseException as e:
                self.critical('Dragonfly exception. {}'.format(e))
                self.debug_exception()
        else:
            self._hill_climber(st, controller, pattern)

        sm.canvas.show_desired_position = osdp

        from pyface.gui import GUI
        GUI.invoke_later(self._info.dispose)

    def _dragonfly_peak(self, st, pattern, lm, controller):

        # imgplot, imgplot2, imgplot3 = pattern.setup_execution_graph()
        # imgplot, imgplot2 = pattern.setup_execution_graph()
        imgplot, imgplot2 = pattern.setup_execution_graph(nplots=2)
        cx, cy = pattern.cx, pattern.cy

        sm = lm.stage_manager

        linear_move = controller.linear_move
        in_motion = controller.in_motion
        find_lum_peak = sm.find_lum_peak
        pxpermm = sm.pxpermm

        set_data = imgplot.data.set_data
        set_data2 = imgplot2.data.set_data
        # set_data3 = imgplot3.data.set_data

        duration = pattern.duration
        sat_threshold = pattern.saturation_threshold
        total_duration = pattern.total_duration
        min_distance = pattern.min_distance
        aggressiveness = pattern.aggressiveness
        update_period = pattern.update_period / 1000.
        move_threshold = pattern.move_threshold
        blur = pattern.blur
        px, py = cx, cy
        ncx, ncy = cx, cy

        point_gen = None
        cnt = 0
        # peak = None
        oimg = sm.get_preprocessed_src()
        pos_img = zeros_like(oimg, dtype='int16')
        per_img = zeros_like(oimg, dtype='int16')

        img_h, img_w = pos_img.shape
        perimeter_circle = circle(img_h / 2, img_w / 2,
                                  pattern.perimeter_radius * pxpermm)

        color = 2**15 - 1
        per_img[perimeter_circle] = 50
        set_data('imagedata', gray2rgb(per_img.astype(uint8)))

        while time.time() - st < total_duration:
            if not self._alive:
                break

            sats = []
            pts = []
            ist = time.time()
            npt = None
            self.debug('starting iteration={}, in_motion={}'.format(
                cnt, in_motion()))
            while time.time() - ist < duration or in_motion():
                args = find_lum_peak(min_distance, blur)

                if args is None:
                    sleep(update_period / 5)
                    continue

                sleep(update_period)

                pt, peakcol, peakrow, peak_img, sat, src = args

                sats.append(sat)
                # if peak is None:
                #     peak = peak_img
                # else:
                #     peak = ((peak.astype('int16') - 2) + peak_img).clip(0, 255)

                # img = gray2rgb(peak).astype(uint8)
                src = gray2rgb(src).astype(uint8)
                if pt:
                    pts.append(pt)
                    c = circle(peakrow, peakcol, 2)
                    # img[c] = (255, 0, 0)
                    src[c] = (255, 0, 0)

                # set_data('imagedata', src)
                set_data2('imagedata', src)
                # set_data('imagedata', img)

            self.debug('iteration {} finished, npts={}'.format(cnt, len(pts)))

            pattern.position_str = NULL_STR

            if pts:
                w = array(sats)
                avg_sat_score = w.mean()
                self.debug('Average Saturation: {} threshold={}'.format(
                    avg_sat_score, sat_threshold))
                pattern.average_saturation = avg_sat_score
                if avg_sat_score < sat_threshold:
                    # pts = array(pts)
                    x, y, w = array(pts).T
                    ws = w.sum()
                    nx = (x * w).sum() / ws
                    ny = (y * w).sum() / ws
                    self.debug('New point {},{}'.format(nx, ny))
                    npt = nx, ny, 1
                else:
                    continue

            if npt is None:
                if not point_gen:
                    point_gen = pattern.point_generator()
                # wait = False
                x, y = next(point_gen)
                px, py = ncx + x, ncy + y
                self.debug('generating new point={},{} ---- {},{}'.format(
                    x, y, px, py))

            else:

                point_gen = None

                # wait = True
                if npt is None:
                    block = total_duration - (time.time() - st) < duration
                    linear_move(cx,
                                cy,
                                source='recenter_dragonfly{}'.format(cnt),
                                block=block,
                                velocity=pattern.velocity,
                                use_calibration=False)
                    pattern.position_str = 'Return to Center'
                    px, py = cx, cy
                    continue

                try:
                    scalar = npt[2]
                except IndexError:
                    scalar = 1

                ascalar = scalar * aggressiveness
                dx = npt[0] / pxpermm * ascalar
                dy = npt[1] / pxpermm * ascalar
                if abs(dx) < move_threshold or abs(dy) < move_threshold:
                    self.debug('Deviation too small dx={},dy={}'.format(
                        dx, dy, move_threshold))
                    pattern.position_str = 'Deviation too small'
                    continue

                px += dx
                py -= dy
                self.debug('i: {}. point={},{}. '
                           'Intensitiy Scalar={}, Modified Scalar={}'.format(
                               cnt, px, py, scalar, ascalar))

                ncx, ncy = px, py

            if not pattern.validate(px, py):
                self.debug('invalid position. {},{}'.format(px, py))

                curx = px - dx
                cury = py + dy

                vx = curx - cx
                vy = cury - cy

                px = vx * aggressiveness + cx
                py = vy * aggressiveness + cy

                self.debug('reduced vector magnitude. new pos={},{}'.format(
                    px, py))

                # for safety validate this new position
                # if above calculation is correct the new position should always be valid
                if not pattern.validate(px, py):
                    self.debug(
                        'vector calculations incorrect. moving to center position'
                    )
                    px, py = cx, cy

                ncx, ncy = px, py

            pattern.position_str = '{:0.5f},{:0.5f}'.format(px, py)

            # if there is less than 1 duration left then block is true
            block = total_duration - (time.time() - st) < duration
            self.debug('blocking ={}'.format(block))
            linear_move(px,
                        py,
                        source='dragonfly{}'.format(cnt),
                        block=block,
                        velocity=pattern.velocity,
                        use_calibration=False)

            ay, ax = py - cy, px - cx
            # self.debug('position mm ax={},ay={}'.format(ax, ay))
            ay, ax = int(-ay * pxpermm) + img_h / 2, int(
                ax * pxpermm) + img_w / 2
            # self.debug('position pixel ax={},ay={}'.format(ax, ay))

            pos_img -= 5
            pos_img = pos_img.clip(0, color)

            c = circle(ay, ax, 2)
            pos_img[c] = color - 60
            nimg = ((pos_img + per_img).astype(uint8))

            set_data('imagedata', gray2rgb(nimg))

            cnt += 1

        self.debug('dragonfly complete')
        controller.block()

    def _hill_climber(self, st, controller, pattern):
        g = pattern.execution_graph
        imgplot, cp = pattern.setup_execution_graph()

        cx, cy = pattern.cx, pattern.cy

        sm = self.laser_manager.stage_manager
        linear_move = controller.linear_move
        get_scores = sm.get_scores
        moving = sm.moving
        update_axes = sm.update_axes
        set_data = imgplot.data.set_data

        sat_threshold = pattern.saturation_threshold
        total_duration = pattern.total_duration
        duration = pattern.duration
        pattern.perimeter_radius *= sm.pxpermm

        avg_sat_score = -1
        # current_x, current_y =None, None
        for i, pt in enumerate(pattern.point_generator()):
            update_plot = True

            x, y = pt.x, pt.y
            ax, ay = cx + x, cy + y
            if not self._alive:
                break

            if time.time() - st > total_duration:
                break

            # use_update_point = False
            if avg_sat_score < sat_threshold:
                # use_update_point = False
                # current_x, current_y = x, y
                linear_move(ax,
                            ay,
                            block=False,
                            velocity=pattern.velocity,
                            use_calibration=False,
                            update=False,
                            immediate=True)
            else:
                self.debug('Saturation target reached. not moving')
                update_plot = False

            density_scores = []
            ts = []
            saturation_scores = []
            positions = []

            def measure_scores(update=False):
                if update:
                    update_axes()

                positions.append((controller.x, controller.y))
                score_density, score_saturation, img = get_scores()

                density_scores.append(score_density)
                saturation_scores.append(score_saturation)

                set_data('imagedata', img)
                ts.append(time.time() - st)
                time.sleep(0.1)

            while moving(force_query=True):
                measure_scores(update=True)

            mt = time.time()
            while time.time() - mt < duration:
                measure_scores()

            if density_scores:
                n = len(density_scores)

                density_scores = array(density_scores)
                saturation_scores = array(saturation_scores)

                weights = [
                    1 / (max(0.0001, ((xi - ax)**2 + (yi - ay)**2))**0.5)
                    for xi, yi in positions
                ]

                avg_score = average(density_scores, weights=weights)
                avg_sat_score = average(saturation_scores, weights=weights)
                score = avg_score

                m, b = polyfit(ts, density_scores, 1)
                if m > 0:
                    score *= (1 + m)

                pattern.set_point(score, pt)

                self.debug('i:{} XY:({:0.5f},{:0.5f})'.format(i, x, y))
                self.debug('Density. AVG:{:0.3f} N:{} Slope:{:0.3f}'.format(
                    avg_score, n, m))
                self.debug('Modified Density Score: {:0.3f}'.format(score))
                self.debug('Saturation. AVG:{:0.3f}'.format(avg_sat_score))
                if update_plot:
                    cp.add_point((x, y))
                    g.add_datum((x, y), plotid=0)

                t = time.time() - st
                g.add_datum((t, avg_score), plotid=1)

                # g.add_bulk_data(ts, density_scores, plotid=1, series=1)

                g.add_datum((t, score),
                            ypadding='0.1',
                            ymin_anchor=-0.1,
                            update_y_limits=True,
                            plotid=1)

            update_axes()
예제 #53
0
 def execute(self):
     self.canceled = False
     t = Thread(target=self._execute)
     t.start()
     self._t = t
예제 #54
0
    def do_coincidence_scan(self, new_thread=True):

        if new_thread:
            t = Thread(name='ion_optics.coincidence', target=self._coincidence)
            t.start()
            self._centering_thread = t
예제 #55
0
class BaseStageManager(Manager):
    keyboard_focus = Event
    canvas_editor_klass = LaserComponentEditor
    tray_calibration_manager = Instance(TrayCalibrationManager)
    stage_map_klass = LaserStageMap
    stage_map_name = Str
    stage_map_names = List
    stage_map = Instance(BaseStageMap)
    canvas = Instance(MapCanvas)

    root = Str(paths.map_dir)
    calibrated_position_entry = String(enter_set=True, auto_set=False)

    move_thread = None
    temp_position = None
    temp_hole = None

    use_modified = Bool(True)  # set true to use modified affine calculation

    def goto_position(self, pos):
        raise NotImplementedError

    def refresh_stage_map_names(self):
        sms = list_directory2(self.root, '.txt', remove_extension=True)

        us = list_directory2(paths.user_points_dir, '.yaml', remove_extension=True)
        if us:
            sms.extend(us)

        self.stage_map_names = sms

    def load(self):
        self.refresh_stage_map_names()

        psm = self._load_previous_stage_map()
        sms = self.stage_map_names
        sm = None
        if psm in sms:
            sm = psm
        elif sms:
            sm = sms[0]

        if sm:
            self.stage_map_name = sm
            if self.stage_map:
                self.canvas.set_map(self.stage_map)
                self.canvas.request_redraw()

                # self.stage_maps = []
                # config = self.get_configuration()
                # if config:
                # load the stage maps

                # mapfiles = self.config_get(config, 'General', 'mapfiles')
                # self.stage_map_names = mapfiles.split(',')
                # for mapfile in mapfiles.split(','):
                #     path = os.path.join(paths.map_dir, mapfile.strip())
                #     sm = StageMap(file_path=path)
                #     sm.load_correction_file()
                #     self.stage_maps.append(sm)

                # load user points as stage map
                # for di in os.listdir(paths.user_points_dir):
                #     if di.endswith('.yaml'):
                #         path = os.path.join(paths.user_points_dir, di)
                # sm = self.stage_map_klass(file_path=path)
                # self.stage_maps.append(sm)

                # load the saved stage map
                # sp = self._get_stage_map_by_name(self._load_previous_stage_map())
                # if sp is not None:
                #     sm = sp

                # self.stage_map_name = sm

                # load the points file
                # self.canvas.load_points_file(self.points_file)

                # load defaults
                # self._default_z = self.config_get(config, 'Defaults', 'z', default=13, cast='float')

                # self.canvas.set_map(sm)
                # self.canvas.request_redraw()

    def kill(self):
        r = super(BaseStageManager, self).kill()
        self._save_stage_map()
        return r

    @property
    def stage_map_path(self):
        return os.path.join(paths.hidden_dir, '{}.stage_map'.format(self.id))

    def canvas_editor_factory(self):
        return self.canvas_editor_klass(keyboard_focus='keyboard_focus')

    def move_to_hole(self, hole, **kw):
        self._move(self._move_to_hole, hole, name='move_to_hole', **kw)

    def get_calibrated_position(self, pos, key=None):
        smap = self.stage_map

        # use a affine transform object to map
        canvas = self.canvas
        ca = canvas.calibration_item

        # check if a calibration applies to this hole
        hole_calibration = get_hole_calibration(smap.name, key)
        if hole_calibration:
            self.debug('Using hole calibration')
            ca = hole_calibration

        if ca:
            rot = ca.rotation
            cpos = ca.center
            scale = ca.scale

            self.debug('Calibration parameters: rot={:0.3f}, cpos={} scale={:0.3f}'.format(rot, cpos, scale))
            pos = smap.map_to_calibration(pos, cpos, rot,
                                          scale=scale,
                                          use_modified=self.use_modified)

        return pos

    def update_axes(self):
        """
        """
        self.info('querying axis positions')
        self._update_axes()

    # private
    def _update_axes(self):
        pass

    def _move_to_hole(self, key, correct_position=True):
        pass

    def _stop(self):
        pass

    def _move(self, func, pos, name=None, *args, **kw):
        if pos is None:
            return

        if self.move_thread and self.move_thread.isRunning():
            self._stop()

        if name is None:
            name = func.func_name

        self.move_thread = Thread(name='stage.{}'.format(name),
                                  target=func, args=(pos,) + args, kwargs=kw)
        self.move_thread.start()

    def _canvas_factory(self):
        raise NotImplementedError

    # handlers
    def _calibrated_position_entry_changed(self, new):
        self.debug('User entered calibrated position {}'.format(new))
        self.goto_position(new)

    def _stage_map_name_changed(self, new):
        if new:
            self.debug('setting stage map to {}'.format(new))
            sm = self.stage_map_klass(file_path=os.path.join(self.root, add_extension(new, '.txt')))
            self.stage_map = sm
            self.tray_calibration_manager.load_calibration(stage_map=new)

            self.canvas.set_map(sm)
            self.canvas.request_redraw()

    # defaults
    def _tray_calibration_manager_default(self):
        t = TrayCalibrationManager(parent=self,
                                   canvas=self.canvas)
        return t

    def _canvas_default(self):
        return self._canvas_factory()

    def _save_stage_map(self):
        p = self.stage_map_path
        self.info('saving stage_map {} to {}'.format(self.stage_map_name, p))
        with open(p, 'wb') as f:
            pickle.dump(self.stage_map_name, f)

    def _load_previous_stage_map(self):
        p = self.stage_map_path
        if os.path.isfile(p):
            self.info('loading previous stage map from {}'.format(p))
            with open(p, 'rb') as f:
                try:
                    return pickle.load(f)
                except pickle.PickleError:
                    pass