Beispiel #1
0
class Scientifica(Stage):
    """
    A Scientifica motorized device.

    This class supports PatchStar, MicroStar, SliceScope, objective changers, etc.
    The device may be identified either by its serial port or by its description 
    string:

        port: <serial port>  # eg. 'COM1' or '/dev/ttyACM0'
        name: <string>  # eg. 'SliceScope' or 'MicroStar 2'
        baudrate: <int>  #  may be 9600 or 38400

    The optional 'baudrate' parameter is used to set the baudrate of the device.
    Both valid rates will be attempted when initially connecting.
    """
    def __init__(self, man, config, name):
        # can specify 
        port = config.pop('port', None)
        name = config.pop('name', None)

        self.scale = config.pop('scale', (1e-6, 1e-6, 1e-6))
        baudrate = config.pop('baudrate', None)
        ctrl_version = config.pop('version', 2)
        try:
            self.dev = ScientificaDriver(port=port, name=name, baudrate=baudrate, ctrl_version=ctrl_version)
        except RuntimeError as err:
            if hasattr(err, 'dev_version'):
                raise RuntimeError(err.message + " You must add `version=%d` to the configuration for this device and double-check any speed/acceleration parameters." % int(err.dev_version))
            else:
                raise

        # Controllers reset their baud to 9600 after power cycle
        if baudrate is not None and self.dev.getBaudrate() != baudrate:
            self.dev.setBaudrate(baudrate)

        self._lastMove = None
        man.sigAbortAll.connect(self.abort)

        Stage.__init__(self, man, config, name)

        # clear cached position for this device and re-read to generate an initial position update
        self._lastPos = None
        self.getPosition(refresh=True)

        # Set approach angle
        # Disabled--this toggles the approach bit and we can't reconfigure it from here :(
        # approach = self.dev.send('APPROACH')
        # self.dev.send('ANGLE %f' % self.pitch)
        # self.dev.send('APPROACH %s' % approach)  # reset approach bit; setting angle enables it

        # set any extra parameters specified in the config
        params = config.get('params', {})
        for param, val in params.items():
            if param == 'currents':
                assert len(val) == 2
                self.dev.setCurrents(*val)
            elif param == 'axisScale':
                assert len(val) == 3
                for i, x in enumerate(val):
                    self.dev.setAxisScale(i, x)
            else:
                self.dev.setParam(param, val)

        self.setUserSpeed(config.get('userSpeed', self.dev.getSpeed() * abs(self.scale[0])))
        
        # whether to monitor for changes to a MOC
        self.monitorObj = config.get('monitorObjective', False)
        if self.monitorObj is True:
            if self.dev._version < 3:
                raise TypeError("Scientifica motion card version %s does not support reading objective position." % self.dev._version)
            self.objectiveState = None
            self._checkObjective()

        # thread for polling position changes

        self.monitor = MonitorThread(self, self.monitorObj)
        self.monitor.start()

    def capabilities(self):
        """Return a structure describing the capabilities of this device"""
        if 'capabilities' in self.config:
            return self.config['capabilities']
        else:
            return {
                'getPos': (True, True, True),
                'setPos': (True, True, True),
                'limits': (False, False, False),
            }

    def stop(self):
        """Stop the manipulator immediately.
        """
        with self.lock:
            self.dev.stop()
            if self._lastMove is not None:
                self._lastMove._stopped()
            self._lastMove = None

    def abort(self):
        """Stop the manipulator immediately.
        """
        self.dev.stop()
        if self._lastMove is not None:
            self._lastMove._stopped()
            self._lastMove = None

    def setUserSpeed(self, v):
        """Set the maximum speed of the stage (m/sec) when under manual control.

        The stage's maximum speed is reset to this value when it is not under
        programmed control.
        """
        self.userSpeed = v
        self.dev.setSpeed(v / abs(self.scale[0]))

    def _getPosition(self):
        # Called by superclass when user requests position refresh
        with self.lock:
            pos = self.dev.getPos()
            pos = [pos[i] * self.scale[i] for i in (0, 1, 2)]
            if pos != self._lastPos:
                self._lastPos = pos
                emit = True
            else:
                emit = False

        if emit:
            # don't emit signal while locked
            self.posChanged(pos)

        return pos

    def targetPosition(self):
        with self.lock:
            if self._lastMove is None or self._lastMove.isDone():
                return self.getPosition()
            else:
                return self._lastMove.targetPos

    def quit(self):
        self.monitor.stop()
        Stage.quit(self)

    def _move(self, abs, rel, speed, linear):
        with self.lock:
            if self._lastMove is not None and not self._lastMove.isDone():
                self.stop()
            pos = self._toAbsolutePosition(abs, rel)
            speed = self._interpretSpeed(speed)

            self._lastMove = ScientificaMoveFuture(self, pos, speed, self.userSpeed)
            return self._lastMove

    def deviceInterface(self, win):
        return ScientificaGUI(self, win)

    def startMoving(self, vel):
        """Begin moving the stage at a continuous velocity.
        """
        s = [int(-v * 1000. / 67. / self.scale[i]) for i,v in enumerate(vel)]
        print(s)
        self.dev.send('VJ %d %d %d C' % tuple(s))

    def _checkObjective(self):
        with self.lock:
            obj = int(self.dev.send('obj'))
            if obj != self.objectiveState:
                self.objectiveState = obj
                self.sigSwitchChanged.emit(self, {'objective': obj})

    def getSwitch(self, name):
        if name == 'objective' and self.monitorObj:
            return self.objectiveState
        else:
            return Stage.getSwitch(self, name)
Beispiel #2
0
        "Usage: python -i test.py com4 [9600|38400]\n       python -i test.py PatchStar1"
    )
    sys.exit(-1)

baudrate = int(sys.argv[2]) if len(sys.argv) > 2 else None
devname = sys.argv[1]
if devname.lower().startswith('com') or devname.startswith('/dev/'):
    ps = Scientifica(port=devname, baudrate=baudrate, ctrl_version=None)
else:
    ps = Scientifica(name=devname, baudrate=baudrate, ctrl_version=None)

print("Device type:  %s  Description:  %s" %
      (ps.getType(), ps.getDescription()))
print("Firmware version: %r" % ps.getFirmwareVersion())
print("Position: %r" % ps.getPos())
print("Max speed: %r um/sec" % ps.getSpeed())
if ps._version < 3:
    print("Min speed: %r um/sec" % (ps.getParam('minSpeed') /
                                    (2. * ps.getAxisScale(0))))
    print("Acceleration: %r um^2/sec" %
          (ps.getParam('accel') * 250. / ps.getAxisScale(0)))
else:
    print("Min speed: %r um/sec" % ps.getParam('minSpeed'))
    print("Acceleration: %r um^2/sec" % ps.getParam('accel'))

# pos1 = ps.getPos()
# pos2 = [None, None, pos1[2]]
# pos2[2] += 1000
# print("Move %s => %s" % (pos1, pos2))
# ps.moveTo(pos2, speed=300)
# c = 0