Example #1
0
class NewportMotionController(MotionController):
    '''
    '''

    mode = Enum('normal', 'grouped')

    groupobj = Instance(NewportGroup)

    group_commands = True
    _trajectory_mode = None


    def initialize(self, *args, **kw):
        '''
        '''

        # try to get x position to test comms
        if super(NewportMotionController, self).initialize(*args, **kw):
            r = True if self.get_current_position('x') is not None else False

            # force group destruction
            self.destroy_group(force=True)

            # get and clear any error
            self.read_error()

            self.setup_consumer()
            return r

    def load_additional_args(self, config):
        """
        """
        self.axes_factory(config)
        config_path = self.configuration_dir_path
        group = config.get('Optional', 'group')
        joystick = config.get('Optional', 'joystick')
        if group:
            self.load_optional_parameters(config_path, '{}.cfg'.format(group), 'group', '''NEWPORT GROUP PARAMETERS NOT SPECIFIED. YOU WILL NOT BE
ABLE TO PERFORM GROUPED MOVES I.E. LINEAR OR CIRCULAR
            ''')
        if joystick:

            self.load_optional_parameters(config_path, '{}.txt'.format(joystick), 'joystick', '''NEWPORT JOYSTICK PARAMETERS NOT SPECIFIED. YOU WILL NOT BE
ABLE TO USE THE HARDWARE JOYSTICK
            ''')

        # the esp 300 doesnt like grouped commands
        # ie 1PA1.0;2PA1.0
        self.set_attribute(config, 'group_commands', 'General', 'group_commands', cast='boolean', optional=True)

        # set the drive calibration values

        return True

    def load_optional_parameters(self, base, path, paramname, warning):
        '''
        '''
        p = os.path.join(base, path)
        if os.path.isfile(p):
            with open(p, 'r') as f:
                func = getattr(self, 'load_{}_parameters'.format(paramname))
                func(f, p)
        else:
            self.warning(warning)

    def load_joystick_parameters(self, f, p):
        '''
        '''

        self.joystick = Joystick(parent=self)
        self.joystick.load_parameters(f)

#        self.joystick_bits=[]

    def load_group_parameters(self, f, p):
        '''
        '''
        self.groupobj = NewportGroup()
        self.groupobj.load(p)


    def load_commands_from_file(self, p):
        '''
        '''

        with open(p, 'r') as f:
            for line in f:
                line = line[:-1]
                if line:
                    if line[0] != '#':
                        self.ask(line)

    def save_axes_parameters(self, axis=None):
        '''
        '''
        if axis is None:
            axes = self.axes.itervalues()
        else:
            axes = [axis]

        for a in axes:
            a.save()

        com = 'SM'
        return self.tell(com)

    def enable_joystick(self):
        '''
        '''
        self.joystick.enable()

    def disable_joystick(self):
        '''
        '''
        self.joystick.disable_laser()

    def xy_swapped(self):
        if self.axes.has_key('x'):
            return self.axes['x'].id == 2

#===============================================================================
# RPCable
#===============================================================================
    def get_current_position(self, aid):
        ''' 
        '''
        if isinstance(aid, str):
            if aid in self.axes:
                ax = self.axes[aid]
                aid = ax.id
                axis = ax.name
            else:
                return
        else:
            ax = self._get_axis_by_id(aid)
            axis = ax.name

        cmd = self._build_query('TP', xx=aid)
        f = self.ask(cmd, verbose=False)

        aname = '_{}_position'.format(axis)
        if f != 'simulation' and f is not None:
            try:
                f = float(f)

                f = self._sign_correct(f, axis, ratio=False) / ax.drive_ratio

            except ValueError:
                f = None
        else:
#            time.sleep(0.5)
            f = getattr(self, aname)

        self.axes[axis].position = f
        return f

    def relative_move(self, ax_key, direction):
        # move one pixel in the specified direction
        ax = self.axes[ax_key]

        v1 = self.parent.canvas.map_data((0, 0))
        v2 = self.parent.canvas.map_data((5, 5))

        if ax_key == 'y':
            v = (v2[1] - v1[1]) * direction * ax.sign  # + self._y_position

        else:
            v = (v2[0] - v1[0]) * direction * ax.sign  # + self._x_position

        self.single_axis_move(ax_key, v, block=False, mode='relative',
                              verbose=False,
                              update=False)

    def multiple_point_move(self, points, nominal_displacement=0.5):
        gid = self.groupobj.id
        self.timer = self.timer_factory()
        # use a nominal displacement to set the motion params
        self.configure_group(True, displacement=nominal_displacement)

        # is this command necessary or is it
        cmd = self._build_command('HQ', xx=self.groupobj.id, nn=10)
        self.tell(cmd, verbose=True)

        avaliable_spaces = 10
        while 1:
            # issue the first 10 points and wait
            cmd = ';'.join([self._build_command('HL', xx=gid,
                                                nn='{:0.5f},{:0.5f}'.format(self._sign_correct(x, 'x'),
                                                                            self._sign_correct(y, 'y')))
                            for x, y in points[:avaliable_spaces]
                            ])
            self.tell(cmd, verbose=True)

            waiting = True
            resp = 0
            cmd = self._build_query('HQ', xx=gid)
            while waiting and not self.simulation:
                # wait until the via point buffer is empty
                # e.i HQ?=10 10 via point spaces available in buffer
                resp = self.ask(cmd, verbose=False)
                if resp is not None:
                    resp = resp.strip()
                    resp = int(resp)
                    waiting = resp != 10
                else:
                    break
                time.sleep(0.1)

            points = points[avaliable_spaces:]
            if not points:
                break

    def linear_move(self, x, y, **kw):

        # calc the displacement
        dx = self._x_position - x
        dy = self._y_position - y
        tol = 0.033

        if abs(dx) < tol:
            if 'grouped_move' in kw:
                kw.pop('grouped_move')

            self.info('x displacement {} doing a hack axis move'.format(dx))
#            points = [(x + 0.001, y / 2.0), (x, y)]
#            self.multiple_point_move(points, nominal_displacement = d)
            self.single_axis_move('y', y, **kw)
            # self._y_position = y
            return
        if abs(dy) < tol:
            if 'grouped_move' in kw:
                kw.pop('grouped_move')
            self.info('y displacement {} doing a hack axis move'.format(dy))
#            points = [(x / 2.0, y + 0.001), (x, y)]
#            self.multiple_point_move(points, nominal_displacement = d)
            self.single_axis_move('x', x, **kw)
            # self._x_position = x
            return

        errx = self._validate(x, 'x', cur=self._x_position)
        erry = self._validate(y, 'y', cur=self._y_position)
        if errx is None and erry is None:
            return 'invalid position {},{}'.format(x, y)

        d = math.sqrt(math.pow(dx, 2) + math.pow(dy, 2))
        tol = 0.001  # should be set to the motion controllers resolution
        if d > tol:
            kw['displacement'] = d
            self.parent.canvas.set_desired_position(x, y)
            self._x_position = x
            self._y_position = y

            self.debug('doing linear move')
            self.timer = self.timer_factory()
            self._linear_move_(dict(x=x, y=y), **kw)
        else:
            self.info('displacement of move too small {} < {}'.format(d, tol))

    def single_axis_move(self, key, value, block=False, mode='absolute',
                         velocity=None, update=250,
                         immediate=False, **kw):
        args = (key, value, block, mode, velocity, update, kw)
        if block or immediate:
            self._single_axis_move(args)
        else:
            self.debug('add {} to consume queue'.format(args))
            self.add_consumable((self._single_axis_move, args))

    def _single_axis_move(self, args):
        key, value, block, mode, velocity, update, kw = args
        self.debug('single axis move {}'.format(args))
        x = None
        y = None
        try:
            ax = self.axes[key]
        except KeyError:
            self.debug('No axes key. {} keys={}'.format(key, self.axes.keys))
        aid = ax.id
        if self.groupobj is not None:
            if aid in map(int, self.groupobj.axes):
                self.destroy_group()

        if mode == 'absolute':
            cmd = 'PA'
        else:
            cmd = 'PR'

        cmd = self._build_command(cmd, xx=aid,
                                  nn='{:0.5f}'.format(self._sign_correct(value, key) * ax.drive_ratio))
        self.debug('command {}'.format(cmd))
        func = None

        if key != 'z':
            if key == 'x':
                x = value
                y = self._y_position
                o = self._x_position
            else:
                x = self._x_position
                y = value
                o = self._y_position

            if self._validate(value, key, cur=o) is None:
                value = float(value)
                if abs(value - o) <= 0.001:
                    return 'At desired position. cur={} desired={}'.format(o, value)
                return 'invalid position {}'.format(value)
            if mode == 'absolute':
                self.parent.canvas.set_desired_position(x, y)
                dx = self._x_position - self._sign_correct(x, 'x')
                dy = self._y_position - self._sign_correct(y, 'y')
            else:
                dx, dy = x, y

            disp = math.sqrt(math.pow(dx, 2) + math.pow(dy, 2))

            if velocity is not None:
                ax.velocity = velocity
                self.set_single_axis_motion_parameters(ax)
            elif self._check_motion_parameters(disp, ax):
                self.set_single_axis_motion_parameters(ax)
        else:
            func = self._z_inprogress_update

        if block:
            block = key

        if update:
            self.timer = self.timer_factory(func=func, period=update)
        else:
            if mode == 'absolute':
                if x is not None and y is not None:
                    self.parent.canvas.set_stage_position(x, y)
                else:
                    self._z_position = value
                    self.z_progress = value

        self.debug('command={} block={}. kw={}'.format(cmd, block, kw))
        setattr(self, '_{}_position'.format(key), value)
        self._axis_move(cmd, block=block, **kw)

    def multiple_axis_move(self, axes_list, block=False):
        """
            block - wait for move to complete before returning control
        """
        self.configure_group(False)

        cmd = ';'.join(['{}PA{:0.3f}'.format(ax, val)
#                                  self._sign_correct(val,
#                                                    self._get_axis_by_id(ax).name))

                for ax, val in axes_list])



        self._axis_move(cmd, block=block)

    def arc_move(self, cx, cy, angle, block=True, sign_correct=True):
        ''' 
            the radius of the resultant arc is
            the distance from the center to the current point
            
        '''
        self.configure_group(True)

        if self.mode == 'group':
            if sign_correct:
                cx = self._sign_correct(cx, 'x')
                cy = self._sign_correct(cy, 'y')

            cmd = '1HC{:0.3f,:0.3f,:0.3f}'.format (cx, cy , angle)
            self.tell(cmd)

        if block:
            self._block_()

    def get_load_position(self):

        x = self.axes['x'].loadposition
        y = self.axes['y'].loadposition
        z = self.axes['z'].loadposition
        return x, y, z

    def home(self, axes, search_mode=4, block=True):
        '''
        '''
        # manual 3-104
        # If nn = 0, the axes will search for zero position count.
        # If nn = 1, the axis will search for combined Home and
        # Index signal transitions.
        # If nn = 2, the axes will search for Home signal
        # transition only.
        # If nn = 3, the axes willsearch for positive limit signal transition.
        # If nn = 4, the axes will search for negative limit signal
        # transition.
        # If nn = 5, the axes will search for positive limit and index signal
        # transition.
        # If nn = 6, the axes will search for negative limit and
        # index signal transition.

        # destroy the grouping
        self.destroy_group(force=True)


#        cmd = ';'.join(['{}OR{{}}'.format(k.id) for k in self.axes.itervalues()])
        # if all:
#            cmd = ';'.join(['{}OR{{}}'.format(k.id) for k in self.axes.itervalues()])
        cmd = ';'.join([self._build_command('OR', a.id, nn=search_mode if a.name.lower() != 'z' else 3)
                         for a in self.axes.itervalues() if a.name in axes])


        # force z axis home positive
        # cmd = '1OR{};2OR{};3OR{}' .format(search_mode, search_mode, 3)
#        cmd = cmd.format(*[search_mode if v.id != 3 else 3 for k, v in self.axes.iteritems()])
#        cmd = cmd.format(*[search_mode if v.id != 3 else 3 for v in axes])
#        if 'z' in axes:
#            axis = self.axes.keys().index('z')
#            search_mode = 3

#            cmd = self._build_command('OR', xx = axis, nn = )

        self.timer = self.timer_factory()

        if self.group_commands:
            self.tell(cmd)

            if block:
                self._block_()
        else:
            for c in cmd.split(';'):
                self.tell(c)
                if block:
                    self._block_()

    def block_group(self, n=10):
        cmd = '1HQ%i' % n
        self.tell(cmd)

        while self.group_moving() and not self.simulation:
            time.sleep(0.1)

    def group_moving(self):
#        cmd = '1HS?'

        cmd = self._build_query('HS', xx=self.groupobj.id)
        m = True if self.ask(cmd) == '0' else False
        return m

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

        if self.timer is not None:
            self.timer.Stop()

        if self.mode == 'grouped':
            # check group is moving
            cmd = '1HS'
            while self.group_moving() and not self.simulation:
                self.tell(cmd, verbose=verbose)
                time.sleep(0.35)
        else:
            if isinstance(ax_key, str):
                cmd = self._build_command('ST', xx=self.axes[ax_key].id)
            else:
                cmd = 'ST'
            self.tell(cmd, verbose=verbose)

    def destroy_group(self, force=False, mode=1):
        '''
        '''
        if self.mode == 'grouped' or force:
            self.info('destroying group')
            com = '1HX'
            self.tell(com)


        self.set_trajectory_mode(mode)
        self.mode = 'normal'
#            if self.speed_mode == 'low':
#                self.set_low_speed()

    def set_home_position(self, **kw):

        cmd = []
        for k in kw:
            if k in self.axes:
                aid = self.axes[k].id
                cmd.append(self._build_command('SH', xx=aid, nn=kw[k]))

        self.tell(';'.join(cmd))

    def define_home(self, axis=None):
        '''
        '''
        if axis is not None:
            aid = self.axes[axis].id
            cmd = self._build_command('DH', xx=aid, nn=0)
        else:
            cmd = []
            for k in ['x', 'y']:
                aid = self.axes[k].id
                cmd.append(self._build_command('DH', xx=aid, nn=0))
            cmd = ';'.join(cmd)

        self.tell(cmd)

        err = self.read_error()
        if err is None:
            err = True

        return err

    def set_trajectory_mode(self, mode):
        '''
            modes see ESP301 manual page 3-137
        
            1. trapezoidal
            2. s-curve
            3. jog mode
            4. slave to master's desired position (trajectory)
            5. slave to master's actual position (feedback)
            6. slave to master's actual velocity for jogging
        '''
        if mode is self._trajectory_mode:
            return

        self._trajectory_mode = mode
        coms = []
        for k in self.axes:
            coms.append(self._build_command('TJ', xx=self.axes[k].id, nn=mode))

        if self.group_commands:
            com = ';'.join(coms)
            self.tell(com)
        else:
            for c in coms:
                self.tell(c)

    def read_assigned_groups(self):
        '''
        '''
        com = 'HB'
        r = self.ask(com)
        if not r or r == 'simulation':
            r = []

        return r

    def read_error(self):
        error = None
#        cmd = 'TB?'
        cmd = self._build_query('TB')
        r = self.ask(cmd)
        if r is not None and r != 'simulation':
            eargs = r.split(',')
            try:
                error_code = int(eargs[0])
#                error_msg = eargs[-1]
                if error_code == 0:
                    error = None
                else:
                    error = error_code
                    self.warning('Newport Motion Controller:{} {}'.format(self.name, r))
#                    gWarningDisplay.add_text('%s - %s' % (self.name, error_msg))
            except:
                pass
        return error

    def set_group_motion_parameters(self, acceleration=None, deceleration=None, velocity=None):
        if self.groupobj is not None:
            if acceleration is not None:
                self.groupobj.acceleration = acceleration
            if deceleration is not None:
                self.groupobj.acceleration = deceleration
            if velocity is not None:
                self.groupobj.velocity = velocity

    def _check_motion_parameters(self, displacement, obj):
        self.debug('checking motion parameters')
        if displacement <= 0:
            return
        if obj.calculate_parameters:
            change, nv, ac, dc = self.motion_profiler.check_motion(displacement, obj)
            self.debug('calculated {} {} {} {}'.format(change, nv, ac, dc))
            if change:
                obj.trait_set(acceleration=ac,
                                    deceleration=dc,
                                    velocity=nv,
                                    trait_change_notify=False
                                    )
        else:
            change = (obj.machine_acceleration != obj.acceleration or
                      obj.machine_deceleration != obj.deceleration or
                      obj.machine_velocity != obj.velocity)
        return change

    def configure_group(self, group, displacement=None, velocity=None, **kw):
        self.debug('configuring group')
        gobj = self.groupobj
        if not gobj and group:
            self.logger.warn('GROUPED MOVE NOT ENABLED')
            self.mode = 'normal'
            return False

        if group:
            if velocity is not None:
                change = abs(gobj.velocity - velocity) > 0.001
                gobj.velocity = velocity
            else:
                change = self._check_motion_parameters(displacement, gobj)

        if self.mode == 'grouped':
            if not group:
                self.destroy_group()
            elif change:  # the group needs a motion parameter change
                self._set_axis_grouping(new_group=False)
        else:  # mode==normal
            if group:
                self._set_axis_grouping()
            else:
                self.destroy_group()

        return True

    def at_velocity(self, axkey, event, tol=0.25):
        if not self.simulation:
            desired_velocity = float(self.ask(self._build_query('VA', xx=self.axes[axkey].id)))
            av = self.read_actual_velocity(axkey)
            while av is not None and abs(abs(av) - desired_velocity) > tol:
                av = self.read_actual_velocity(axkey)
                time.sleep(0.01)

            event.clear()
            desired_velocity = 0
            while av is not None and abs(abs(av) - desired_velocity) > tol:
                av = self.read_actual_velocity(axkey)
                time.sleep(0.01)

        event.set()

    def read_actual_velocity(self, axkey):
        ax = self.axes[axkey]
#        self._build_command('TV', xx = ax.id, nn = '?')
        cmd = self._build_query('TV', xx=ax.id)
        v = self.ask(cmd, verbose=False)
        if v is not None:
            return float(v)

    def _set_axis_grouping(self, new_group=True):
        # check group_parameters are acceptable
        if new_group:
            msg = 'setting new group'
            self.set_trajectory_mode(1)

        else:
            msg = 'changing group parameters'
        self.info(msg)
        self.mode = 'grouped'

        cmd = self.groupobj.build_command(new_group)
        if self.group_commands:
            self.tell(cmd)
        else:
            for c in cmd.split(';'):
                self.tell(c)
                time.sleep(0.1)
                # self.read_error()
                # time.sleep(0.1)

    def set_single_axis_motion_parameters(self, axis=None, pdict=None):
        '''
           
        '''
        pmap = dict(velocity='VA',
                  acceleration='AC',
                  deceleration='AG')

        if pdict:
            axis = self.axes[pdict['key']]
            cmds = []
            for k in pdict.keys():
                if k is not 'key':
                    cmds.append('{}{}{}'.format(axis.id, pmap[k], pdict[k]))

            cmd = ';'.join(cmds)
        else:
            param_coms = [('VA', 'velocity'),
                        ('AC', 'acceleration'),
                        ('AG', 'deceleration')]
            pdict = dict(velocity=axis.velocity,
                       acceleration=axis.acceleration,
                       deceleration=axis.deceleration)

            cmd = ';'.join(['{:n}{}{{{}:0.2f}}'.format(*(axis.id,) + p) for p in param_coms])

        # ex cmd
        # 1VA1.00;1AC2.00;1AG2.00
            cmd = cmd.format(**pdict)

        if self.group_commands:
            self.tell(cmd)
        else:
            for c in cmd.split(';'):
                self.tell(c)
                time.sleep(0.1)

    def _get_axis_by_id(self, aid):
        '''
        '''
        for k in self.axes:
            a = self.axes[k]
            if a.id == aid:
                return a

    def _linear_move_(self, kwargs, block=False, grouped_move=True, sign_correct=True, **kw):
        '''
        '''

        self.configure_group(grouped_move, **kw)
        self.debug('group configured')
        for k in kwargs:
            key = k[0]
            if sign_correct:
#                axis = self.axes[key]
                val = self._sign_correct(kwargs[k], key, ratio=True)
                kwargs[k] = val

        if self.mode == 'grouped':

            gid = self.groupobj.id
            st = '{:n}HL{x:0.5f},{y:0.5f}'.format(gid, **kwargs)
#            this switch is accomplished by the group_parameter axes orer
#            axes=2,1
#            if self.axes['x'].id == 2:
#                st = '{:n}HL{y:0.3f},{x:0.3f}'.format(id, **kwargs)

            self.tell(st, verbose=True)
        else:

            self.multiple_axis_move([
                                     (self.axes['y'].id, kwargs['y']),
                                     (self.axes['x'].id, kwargs['x'])
                                     ])

        if block:
            self.info('moving to {x:0.5f},{y:0.5f}'.format(**kwargs))
            self._block_()
            self.info('move to {x:0.5f},{y:0.5f} complete'.format(**kwargs))
            self.update_axes()

    def _axis_move(self, com, block=False, verbose=True, **kw):
        '''
        '''

        if self.group_commands:
            self.tell(com, verbose=verbose)
        else:
            for c in com.split(';'):
                self.tell(c)
                time.sleep(0.1)

        if block:
            self.debug('blocking {}'.format(block))
            self._block_(axis=block)

#    def _block_(self, axis=None, event=None):
#        '''
#        '''
#        if event is not None:
#            event.clear()
#
#        if self.timer:
#            #timer is calling self._moving_
#            func = lambda: self.timer.isRunning()
#        else:
#            func = lambda: self._moving_(axis=axis)
#
#        while func():
#            time.sleep(0.25)
#
#        if event is not None:
#            event.set()

    def _moving_(self, axis=None, verbose=False):
        '''
            use TX to read the controllers state.
            see manual 3-141
            return value of P==0101000
                      bits     7654321 
            7,6,5=reserved
            4=trajectory executing yes=1
            
            return True if moving
            
        '''
        moving = False
        if axis is not None:
            if isinstance(axis, str):
                axis = self.axes[axis].id

            if self.mode == 'grouped':
                return self.group_moving()
            else:
                r = self.repeat_command(('MD?', axis), 5, check_type=int,
                                         verbose=verbose)
                if r is not None:
                    # stage is moving if r==0
                    moving = not int(r)

        elif not self.simulation:
            r = self.repeat_command('TX', 5, check_type=str, verbose=verbose)
            if r is not None and len(r) > 0:
                controller_state = ord(r[0])
                cs = make_bitarray(controller_state, width=8)
                moving = cs[3] == '1'
        else:
            time.sleep(0.5)

        return moving

    def _build_command(self, command, xx=None, nn=None):
        '''
        '''
        if isinstance(nn, list):

            nn = ','.join([str(n) for n in nn])

        cmd = '{}'
        args = (command,)
        if xx is not None:
            cmd += '{}'
            args = (xx, command)
        if nn is not None:
            cmd += '{}'
            args = (xx, command, nn)

        return cmd.format(*args)

    def _build_query(self, command, xx=' '):
        return self._build_command(command, xx=xx, nn='?')

    def _axis_factory(self, path, **kw):
        '''
        '''
        na = NewportAxis(parent=self,
                       **kw
                         )

        p = na.load(path)
        if p:
            with open(p, 'r') as f:
                na = pickle.load(f)
                na.parent = self
                na.loaded = True

        return na

    def _joystick_inprogress_update(self):
        '''
        '''

        for a in ['x', 'y', 'z']:
            val = self.get_current_position(a)
            setattr(self, '_%s_position' % a, val)

        self.parent.canvas.set_stage_position(self._x_position, self._y_position)
Example #2
0
 def load_group_parameters(self, f, p):
     '''
     '''
     self.groupobj = NewportGroup()
     self.groupobj.load(p)