class CoherentThread(QtCore.QThread): sigPowerChanged = QtCore.Signal(object) sigWavelengthChanged = QtCore.Signal(object) sigError = QtCore.Signal(object) def __init__(self, dev, driver, lock): QtCore.QThread.__init__(self) self.lock = Mutex(QtCore.QMutex.Recursive) self.dev = dev self.driver = driver self.driverLock = lock self.cmds = {} def setWavelength(self, wl): pass def setShutter(self, opened): wait = QtCore.QWaitCondition() cmd = ['setShutter', opened] with self.lock: self.cmds.append(cmd) def run(self): self.stopThread = False with self.driverLock: self.sigWavelengthChanged.emit(self.driver.getWavelength()*1e-9) while True: try: with self.driverLock: power = self.driver.getPower() * 1e-3 wl = self.driver.getWavelength()*1e-9 self.sigPowerChanged.emit(power) self.sigWavelengthChanged.emit(wl) time.sleep(0.5) except: debug.printExc("Error in Coherent laser communication thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.driver.close() def stop(self, block=False): with self.lock: self.stopThread = True if block: if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!")
class CoherentThread(Thread): sigPowerChanged = QtCore.Signal(object) sigWavelengthChanged = QtCore.Signal(object) sigError = QtCore.Signal(object) def __init__(self, dev, driver, lock): Thread.__init__(self) self.lock = Mutex(QtCore.QMutex.Recursive) self.dev = dev self.driver = driver self.driverLock = lock self.cmds = {} def setWavelength(self, wl): pass def setShutter(self, opened): wait = QtCore.QWaitCondition() cmd = ['setShutter', opened] with self.lock: self.cmds.append(cmd) def run(self): self.stopThread = False with self.driverLock: self.sigWavelengthChanged.emit(self.driver.getWavelength() * 1e-9) while True: try: with self.driverLock: power = self.driver.getPower() * 1e-3 wl = self.driver.getWavelength() * 1e-9 self.sigPowerChanged.emit(power) self.sigWavelengthChanged.emit(wl) time.sleep(0.5) except: debug.printExc("Error in Coherent laser communication thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.driver.close() def stop(self, block=False): with self.lock: self.stopThread = True if block: if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!")
class FilterWheelThread(Thread): fwPosChanged = QtCore.Signal(object) def __init__(self, dev, driver, lock): Thread.__init__(self) self.lock = Mutex(QtCore.QMutex.Recursive) self.dev = dev self.driver = driver self.driverLock = lock self.cmds = {} def run(self): self.stopThread = False with self.driverLock: self.fwPosChanged.emit(self.driver.getPos()) while True: try: with self.driverLock: pos = self.driver.getPos() self.fwPosChanged.emit(pos) time.sleep(0.5) except: debug.printExc("Error in Filter Wheel communication thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.driver.close() def stop(self, block=False): with self.lock: self.stopThread = True if block: if not self.wait(10000): raise Exception( "Timed out while waiting for Filter Wheel thread exit!")
class MouseThread(QtCore.QThread): sigButtonChanged = QtCore.Signal(object) sigPositionChanged = QtCore.Signal(object) def __init__(self, dev, startState=None): QtCore.QThread.__init__(self) self.lock = Mutex(QtCore.QMutex.Recursive) self.dev = dev self.port = self.dev.port if startState is None: self.pos = [0, 0] self.btns = [0, 0] else: self.pos = startState['pos'] self.btns = startState['btns'] def run(self): self.stopThread = False self.sp = serial.Serial(int(self.port), baudrate=1200, bytesize=serial.SEVENBITS) time.sleep(3) ## Wait a few seconds for the mouse to say hello if self.sp.inWaiting() > 0: #print "Discarding %d bytes" % self.sp.inWaiting() self.sp.read(self.sp.inWaiting()) while True: tdx = tdy = 0 if self.sp.inWaiting() > 0: if self.sp.inWaiting() < 3: ## could be in the middle of a packet, wait for more bytes to arrive time.sleep(100e-3) bytesWaiting = self.sp.inWaiting() if bytesWaiting < 3: ## More bytes have not arrived, probably there is data corruption! print "WARNING: possible corrupt data from serial mouse." self.sp.read(bytesWaiting) elif self.sp.inWaiting() >= 3: ## at least one packet is available. while self.sp.inWaiting() >= 3: (dx, dy, b0, b1) = self.readPacket() tdx += dx tdy += dy self.pos = [self.pos[0] + tdx, self.pos[1] + tdy] if tdx != 0 or tdy != 0: #self.emit(QtCore.SIGNAL('positionChanged'), {'rel': (tdx, tdy), 'abs': self.pos}) self.sigPositionChanged.emit({'rel': (tdx, tdy), 'abs': self.pos}) if b0 != self.btns[0] or b1 != self.btns[1]: self.btns = [b0, b1] #self.emit(QtCore.SIGNAL('buttonChanged'), self.btns) self.sigButtonChanged.emit(self.btns) self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(1e-3) self.sp.close() ## convert byte to signed byte @staticmethod def sint(x): return ((x+128)%256)-128 def readPacket(self): d = self.sp.read(3) #print "%s %s %s" % (bin(ord(d[0])), bin(ord(d[1])), bin(ord(d[2]))) b0 = (ord(d[0]) & 32) >> 5 b1 = (ord(d[0]) & 16) >> 4 xh = (ord(d[0]) & 3) << 6 yh = (ord(d[0]) & 12) << 4 xl = (ord(d[1]) & 63) yl = (ord(d[2]) & 63) #print "%s %s %s %s" % (bin(xh), bin(xl), bin(yh), bin(yl)) return (MouseThread.sint(xl | xh), MouseThread.sint(yl | yh), b0, b1) def stop(self, block=False): #print " stop: locking" with MutexLocker(self.lock): #print " stop: requesting stop" self.stopThread = True if block: #print " stop: waiting" if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!")
class AcquireThread(Thread): sigNewFrame = QtCore.Signal(object) sigShowMessage = QtCore.Signal(object) def __init__(self, dev): Thread.__init__(self) self.dev = dev #self.cam = self.dev.getCamera() self.camLock = self.dev.camLock #size = self.cam.getSize() #self.state = {'binning': 1, 'exposure': .001, 'region': [0, 0, size[0]-1, size[1]-1], 'mode': 'No Trigger'} #self.state = self.dev.getParams(['binning', 'exposure', 'region', 'triggerMode']) self.stopThread = False self.lock = Mutex() self.acqBuffer = None #self.frameId = 0 self.bufferTime = 5.0 #self.ringSize = 30 self.tasks = [] ## This thread does not run an event loop, ## so we may need to deliver frames manually to some places self.connections = set() self.connectMutex = Mutex() def __del__(self): if hasattr(self, 'cam'): #self.cam.stop() self.dev.stopCamera() def start(self, *args): self.lock.lock() self.stopThread = False self.lock.unlock() Thread.start(self, *args) def connectCallback(self, method): with self.connectMutex: self.connections.add(method) def disconnectCallback(self, method): with self.connectMutex: if method in self.connections: self.connections.remove(method) # #def setParam(self, param, value): # #print "PVCam:setParam", param, value # start = False # if self.isRunning(): # start = True # #print "Camera.setParam: Stopping camera before setting parameter.." # self.stop(block=True) # #print "Camera.setParam: camera stopped" # with self.lock: # self.state[param] = value # if start: # #self.start(QtCore.QThread.HighPriority) # self.start() # def run(self): #import cProfile ##cProfile.runctx('self._run()', globals(), locals(), sort='cumulative') #pr = cProfile.Profile() #pr.enable() #self._run() #pr.disable() #pr.print_stats(sort='cumulative') #def _run(self): size = self.dev.getParam('sensorSize') lastFrame = None lastFrameTime = None lastFrameId = None fps = None camState = dict( self.dev.getParams( ['binning', 'exposure', 'region', 'triggerMode'])) binning = camState['binning'] exposure = camState['exposure'] region = camState['region'] mode = camState['triggerMode'] try: #self.dev.setParam('ringSize', self.ringSize, autoRestart=False) self.dev.startCamera() lastFrameTime = lastStopCheck = ptime.time() frameInfo = {} scopeState = None while True: ti = 0 now = ptime.time() frames = self.dev.newFrames() ## If a new frame is available, process it and inform other threads if len(frames) > 0: if lastFrameId is not None: drop = frames[0]['id'] - lastFrameId - 1 if drop > 0: print "WARNING: Camera dropped %d frames" % drop ## Build meta-info for this frame(s) info = camState.copy() ss = self.dev.getScopeState() if ss['id'] != scopeState: scopeState = ss['id'] ## regenerate frameInfo here ps = ss['pixelSize'] ## size of CCD pixel transform = pg.SRTTransform3D(ss['transform']) frameInfo = { 'pixelSize': [ps[0] * binning[0], ps[1] * binning[1]], ## size of image pixel 'objective': ss.get('objective', None), 'deviceTransform': transform, } ## Copy frame info to info array info.update(frameInfo) ## Process all waiting frames. If there is more than one frame waiting, guess the frame times. dt = (now - lastFrameTime) / len(frames) if dt > 0: info['fps'] = 1.0 / dt else: info['fps'] = None for frame in frames: frameInfo = info.copy() data = frame.pop('data') frameInfo.update( frame) # copies 'time' key supplied by camera out = Frame(data, frameInfo) with self.connectMutex: conn = list(self.connections) for c in conn: c(out) self.sigNewFrame.emit(out) lastFrameTime = now lastFrameId = frames[-1]['id'] loopCount = 0 time.sleep(1e-3) ## check for stop request every 10ms if now - lastStopCheck > 10e-3: lastStopCheck = now ## If no frame has arrived yet, do NOT allow the camera to stop (this can hang the driver) << bug should be fixed in pvcam driver, not here. self.lock.lock() if self.stopThread: self.stopThread = False self.lock.unlock() break self.lock.unlock() diff = ptime.time() - lastFrameTime if diff > (10 + exposure): if mode == 'Normal': self.dev.noFrameWarning(diff) break else: pass ## do not exit loop if there is a possibility we are waiting for a trigger #from debug import Profiler #prof = Profiler() with self.camLock: #self.cam.stop() self.dev.stopCamera() #prof.mark(' camera stop:') except: printExc("Error starting camera acquisition:") try: with self.camLock: #self.cam.stop() self.dev.stopCamera() except: pass self.sigShowMessage.emit( "ERROR starting acquisition (see console output)") finally: pass def stop(self, block=False): #print "AcquireThread.stop: Requesting thread stop, acquiring lock first.." with self.lock: self.stopThread = True #print "AcquireThread.stop: got lock, requested stop." #print "AcquireThread.stop: Unlocked, waiting for thread exit (%s)" % block if block: if not self.wait(10000): raise Exception("Timed out waiting for thread exit!") #print "AcquireThread.stop: thread exited" def reset(self): if self.isRunning(): self.stop() if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!") #self.start(QtCore.QThread.HighPriority) self.start()
class Device(QtCore.QObject): """Abstract class defining the standard interface for Device subclasses.""" def __init__(self, deviceManager, config, name): QtCore.QObject.__init__(self) self._lock_ = Mutex(QtCore.QMutex.Recursive) ## no, good idea self._lock_tb_ = None self.dm = deviceManager self.dm.declareInterface(name, ['device'], self) #self.config = config self._name = name def name(self): return self._name def createTask(self, cmd, task): ### Read configuration, configure tasks ### Return a handle unique to this task pass #def __del__(self): #self.quit() def quit(self): pass def deviceInterface(self, win): """Return a widget with a UI to put in the device rack""" return None def taskInterface(self, task): """Return a widget with a UI to put in the task rack""" return TaskGui(self, task) def reserve(self, block=True, timeout=20): #print "Device %s attempting lock.." % self.name() if block: l = self._lock_.tryLock(int(timeout*1000)) if not l: print "Timeout waiting for device lock for %s" % self.name() print " Device is currently locked from:" print self._lock_tb_ raise Exception("Timed out waiting for device lock for %s" % self.name()) else: l = self._lock_.tryLock() if not l: #print "Device %s lock failed." % self.name() return False #print " Device is currently locked from:" #print self._lock_tb_ #raise Exception("Could not acquire lock", 1) ## 1 indicates failed non-blocking attempt self._lock_tb_ = ''.join(traceback.format_stack()[:-1]) #print "Device %s lock ok" % self.name() return True def release(self): try: self._lock_.unlock() self._lock_tb_ = None except: printExc("WARNING: Failed to release device lock for %s" % self.name()) def getTriggerChannel(self, daq): """Return the name of the channel on daq that this device raises when it starts. Allows the DAQ to trigger off of this device.""" return None
class MouseThread(Thread): sigButtonChanged = QtCore.Signal(object) sigPositionChanged = QtCore.Signal(object) def __init__(self, dev, startState=None): Thread.__init__(self) self.lock = Mutex(QtCore.QMutex.Recursive) self.dev = dev self.port = self.dev.port if startState is None: self.pos = [0, 0] self.btns = [0, 0] else: self.pos = startState['pos'] self.btns = startState['btns'] def run(self): self.stopThread = False self.sp = serial.Serial(int(self.port), baudrate=1200, bytesize=serial.SEVENBITS) time.sleep(3) ## Wait a few seconds for the mouse to say hello if self.sp.inWaiting() > 0: #print "Discarding %d bytes" % self.sp.inWaiting() self.sp.read(self.sp.inWaiting()) while True: tdx = tdy = 0 if self.sp.inWaiting() > 0: if self.sp.inWaiting( ) < 3: ## could be in the middle of a packet, wait for more bytes to arrive time.sleep(100e-3) bytesWaiting = self.sp.inWaiting() if bytesWaiting < 3: ## More bytes have not arrived, probably there is data corruption! print "WARNING: possible corrupt data from serial mouse." self.sp.read(bytesWaiting) elif self.sp.inWaiting( ) >= 3: ## at least one packet is available. while self.sp.inWaiting() >= 3: (dx, dy, b0, b1) = self.readPacket() tdx += dx tdy += dy self.pos = [self.pos[0] + tdx, self.pos[1] + tdy] if tdx != 0 or tdy != 0: #self.emit(QtCore.SIGNAL('positionChanged'), {'rel': (tdx, tdy), 'abs': self.pos}) self.sigPositionChanged.emit({ 'rel': (tdx, tdy), 'abs': self.pos }) if b0 != self.btns[0] or b1 != self.btns[1]: self.btns = [b0, b1] #self.emit(QtCore.SIGNAL('buttonChanged'), self.btns) self.sigButtonChanged.emit(self.btns) self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(1e-3) self.sp.close() ## convert byte to signed byte @staticmethod def sint(x): return ((x + 128) % 256) - 128 def readPacket(self): d = self.sp.read(3) #print "%s %s %s" % (bin(ord(d[0])), bin(ord(d[1])), bin(ord(d[2]))) b0 = (ord(d[0]) & 32) >> 5 b1 = (ord(d[0]) & 16) >> 4 xh = (ord(d[0]) & 3) << 6 yh = (ord(d[0]) & 12) << 4 xl = (ord(d[1]) & 63) yl = (ord(d[2]) & 63) #print "%s %s %s %s" % (bin(xh), bin(xl), bin(yh), bin(yl)) return (MouseThread.sint(xl | xh), MouseThread.sint(yl | yh), b0, b1) def stop(self, block=False): #print " stop: locking" with self.lock: #print " stop: requesting stop" self.stopThread = True if block: #print " stop: waiting" if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!")
class SutterMP285Thread(Thread): sigPositionChanged = QtCore.Signal(object) sigError = QtCore.Signal(object) def __init__(self, dev, driver, driverLock, scale, limits, maxSpd): Thread.__init__(self) self.lock = Mutex(QtCore.QMutex.Recursive) self.scale = scale self.mp285 = driver self.driverLock = driverLock #self.monitor = True self.update = False self.resolution = 'fine' self.dev = dev #self.port = port #self.pos = [0, 0, 0] #self.baud = baud self.velocity = [0, 0, 0] self.limits = deepcopy(limits) self.limitChanged = False self.maxSpeed = maxSpd #self.posxyz = [ 0, 0, 0] def setResolution(self, res): with self.lock: self.resolution = res def setLimits(self, limits): with self.lock: self.limits = deepcopy(limits) self.limitChanged = True def setMaxSpeed(self, s): with self.lock: self.maxSpeed = s #def setMonitor(self, mon): #with self.lock: #self.monitor = mon def updatePos(self): with self.lock: self.update = True def setVelocity(self, v): with self.lock: self.velocity = v def run(self): self.stopThread = False #self.sp = serial.Serial(int(self.port), baudrate=self.baud, bytesize=serial.EIGHTBITS) #time.sleep(3) ## Wait a few seconds for the mouse to say hello ## clear buffer before starting #if self.sp.inWaiting() > 0: #print "Discarding %d bytes" % self.sp.inWaiting() #self.sp.read(self.sp.inWaiting()) #import wingdbstub print " Starting MP285 thread: 0x%x" % int( QtCore.QThread.currentThreadId()) #import sip #print " also known as 0x%x" % sip.unwrapinstance(self) velocity = np.array([0, 0, 0]) pos = [0, 0, 0] try: self.getImmediatePos() monitor = True except: debug.printExc("Sutter MP285: Cannot determine position:") monitor = False while True: try: ## Lock and copy state to local variables with self.lock: update = self.update self.update = False limits = deepcopy(self.limits) maxSpeed = self.maxSpeed newVelocity = np.array(self.velocity[:]) resolution = self.resolution limitChanged = self.limitChanged self.limitChanged = False ## if limits have changed, inform the device if monitor and limitChanged: ## monitor is only true if this is a customized device with limit checking self.sendLimits() ## If requested velocity is different from the current velocity, handle that. if np.any(newVelocity != velocity): speed = np.clip(np.sum(newVelocity**2)**0.5, 0., 1.) ## should always be 0.0-1.0 #print "new velocity:", newVelocity, "speed:", speed if speed == 0: nv = np.array([0, 0, 0]) else: nv = newVelocity / speed speed = np.clip(speed, 0, maxSpeed) #print "final speed:", speed ## stop current move, get position, start new move #print "stop.." self.stopMove() #print "stop done." #print "getpos..." pos1 = self.readPosition() if pos1 is not None: if speed > 0: #print " set new velocity" self.writeVelocity(speed, nv, limits=limits, pos=pos1, resolution=resolution) #print " done" ## report current position velocity = newVelocity #print "done" ## If velocity is 0, we can do some position checks if np.all(velocity == 0): newPos = None if update: newPos = self.readPosition() elif monitor: newPos = self.getImmediatePos() if newPos is not None: ## If position has changed, emit a signal. change = [ newPos[i] - pos[i] for i in range(len(newPos)) ] pos = newPos if any(change): #self.emit(QtCore.SIGNAL('positionChanged'), {'rel': change, 'abs': self.pos}) self.sigPositionChanged.emit({ 'rel': change, 'abs': pos }) else: ## moving; make a guess about the current position pass except: pass debug.printExc("Error in MP285 thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.mp285.close() def stop(self, block=False): #print " stop: locking" with self.lock: #print " stop: requesting stop" self.stopThread = True if block: #print " stop: waiting" if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!") #print " stop: done" def readPosition(self): try: with self.driverLock: pos = np.array(self.mp285.getPos()) return pos * self.scale except TimeoutError: self.sigError.emit("Read timeout--press reset button on MP285.") return None except: self.sigError.emit("Read error--see console.") debug.printExc("Error getting packet:") return None def writeVelocity(self, spd, v, limits, pos, resolution): if not self.checkLimits(pos, limits): return ## check all limits for closest stop point pt = self.vectorLimit(pos, v, limits) if pt is None: return ## set speed #print " SPD:", spd, "POS:", pt self._setSpd(spd, fineStep=(resolution == 'fine')) self._setPos(pt, block=False) def vectorLimit(self, pos, v, limits): ## return position of closest limit intersection given starting point and vector pts = [] pos = np.array(pos) v = np.array(v) for i in [0, 1, 2]: if v[i] < 0: p = self.intersect(pos, v, limits[i][0], i) else: p = self.intersect(pos, v, limits[i][1], i) if p is not None: pts.append(p) if len(pts) == 0: return None lengths = [np.sum((p - pos)**2)**0.5 for p in pts] i = np.argmin(lengths) return pts[i] def intersect(self, pos, v, x, ax): ## return the point where vector pos->v intersects plane x along axis ax if v[ax] == 0: return None dx = x - pos[ax] return pos + dx * v / v[ax] def checkLimits(self, pos, limits): for i in [0, 1, 2]: #print 'pos, limits', pos[i] #print limits[i] #print limits[i][0] #print limits[i][1] if pos[i] < limits[i][0] or pos[i] > limits[i][1]: return False return True def sendLimits(self): limits = [] for ax in [0, 1, 2]: for lim in [1, 0]: pos, en = self.limits[ax][lim] limits.append(pos / self.scale[ax] if en else None) with self.driverLock: self.mp285.setLimits(limits) def _setPos(self, pos, block=True): pos = np.array(pos) / self.scale with self.driverLock: self.mp285.setPos(pos, block=block) def stopMove(self): with self.driverLock: self.mp285.stop() def _setSpd(self, v, fineStep=True): ## This should be done with a calibrated speed table instead. v = int(v * 1e6) with self.driverLock: self.mp285.setSpeed(v, fineStep) def getImmediatePos(self): with self.driverLock: pos = np.array(self.mp285.getImmediatePos()) return pos * self.scale
class Device(QtCore.QObject): """Abstract class defining the standard interface for Device subclasses.""" def __init__(self, deviceManager, config, name): QtCore.QObject.__init__(self) # task reservation lock -- this is a recursive lock to allow a task to run its own subtasks # (for example, setting a holding value before exiting a task). # However, under some circumstances we might try to run two concurrent tasks from the same # thread (eg, due to calling processEvents() while waiting for the task to complete). We # don't have a good solution for this problem at present.. self._lock_ = Mutex(QtCore.QMutex.Recursive) self._lock_tb_ = None self.dm = deviceManager self.dm.declareInterface(name, ['device'], self) self._name = name def name(self): return self._name def createTask(self, cmd, task): ### Read configuration, configure tasks ### Return a handle unique to this task pass def quit(self): pass def deviceInterface(self, win): """Return a widget with a UI to put in the device rack""" return None def taskInterface(self, task): """Return a widget with a UI to put in the task rack""" return TaskGui(self, task) def configPath(self): """Return the path used for storing configuration data for this device. This path should resolve to `acq4/config/devices/DeviceName_config`. """ return os.path.join('devices', self.name() + '_config') def configFileName(self, filename): """Return the full path to a config file for this device. """ filename = os.path.join(self.configPath(), filename) return self.dm.configFileName(filename) def readConfigFile(self, filename): """Read a config file from this device's configuration directory. """ fileName = os.path.join(self.configPath(), filename) return self.dm.readConfigFile(fileName) def writeConfigFile(self, data, filename): """Write data to a config file in this device's configuration directory. """ fileName = os.path.join(self.configPath(), filename) return self.dm.writeConfigFile(data, fileName) def appendConfigFile(self, data, filename): """Append data to a config file in this device's configuration directory. """ fileName = os.path.join(self.configPath(), filename) return self.dm.appendConfigFile(data, fileName) def reserve(self, block=True, timeout=20): #print "Device %s attempting lock.." % self.name() if block: l = self._lock_.tryLock(int(timeout * 1000)) if not l: print "Timeout waiting for device lock for %s" % self.name() print " Device is currently locked from:" print self._lock_tb_ raise Exception("Timed out waiting for device lock for %s" % self.name()) else: l = self._lock_.tryLock() if not l: #print "Device %s lock failed." % self.name() return False #print " Device is currently locked from:" #print self._lock_tb_ #raise Exception("Could not acquire lock", 1) ## 1 indicates failed non-blocking attempt self._lock_tb_ = ''.join(traceback.format_stack()[:-1]) #print "Device %s lock ok" % self.name() return True def release(self): try: self._lock_.unlock() self._lock_tb_ = None except: printExc("WARNING: Failed to release device lock for %s" % self.name()) def getTriggerChannel(self, daq): """Return the name of the channel on daq that this device raises when it starts. Allows the DAQ to trigger off of this device.""" return None def __repr__(self): return '<%s "%s">' % (self.__class__.__name__, self.name())
class AcquireThread(QtCore.QThread): sigNewFrame = QtCore.Signal(object) sigShowMessage = QtCore.Signal(object) def __init__(self, dev): QtCore.QThread.__init__(self) self.dev = dev #self.cam = self.dev.getCamera() self.camLock = self.dev.camLock #size = self.cam.getSize() #self.state = {'binning': 1, 'exposure': .001, 'region': [0, 0, size[0]-1, size[1]-1], 'mode': 'No Trigger'} #self.state = self.dev.getParams(['binning', 'exposure', 'region', 'triggerMode']) self.stopThread = False self.lock = Mutex() self.acqBuffer = None #self.frameId = 0 self.bufferTime = 5.0 #self.ringSize = 30 self.tasks = [] ## This thread does not run an event loop, ## so we may need to deliver frames manually to some places self.connections = set() self.connectMutex = Mutex() def __del__(self): if hasattr(self, 'cam'): #self.cam.stop() self.dev.stopCamera() def start(self, *args): self.lock.lock() self.stopThread = False self.lock.unlock() QtCore.QThread.start(self, *args) def connectCallback(self, method): with self.connectMutex: self.connections.add(method) def disconnectCallback(self, method): with self.connectMutex: if method in self.connections: self.connections.remove(method) # #def setParam(self, param, value): # #print "PVCam:setParam", param, value # start = False # if self.isRunning(): # start = True # #print "Camera.setParam: Stopping camera before setting parameter.." # self.stop(block=True) # #print "Camera.setParam: camera stopped" # with self.lock: # self.state[param] = value # if start: # #self.start(QtCore.QThread.HighPriority) # self.start() # def run(self): #import cProfile ##cProfile.runctx('self._run()', globals(), locals(), sort='cumulative') #pr = cProfile.Profile() #pr.enable() #self._run() #pr.disable() #pr.print_stats(sort='cumulative') #def _run(self): size = self.dev.getParam('sensorSize') lastFrame = None lastFrameTime = None lastFrameId = None fps = None camState = dict(self.dev.getParams(['binning', 'exposure', 'region', 'triggerMode'])) binning = camState['binning'] exposure = camState['exposure'] region = camState['region'] mode = camState['triggerMode'] try: #self.dev.setParam('ringSize', self.ringSize, autoRestart=False) self.dev.startCamera() lastFrameTime = lastStopCheck = ptime.time() frameInfo = {} scopeState = None while True: ti = 0 now = ptime.time() frames = self.dev.newFrames() #with self.camLock: #frame = self.cam.lastFrame() ## If a new frame is available, process it and inform other threads if len(frames) > 0: if lastFrameId is not None: drop = frames[0]['id'] - lastFrameId - 1 if drop > 0: print "WARNING: Camera dropped %d frames" % drop ## Build meta-info for this frame(s) info = camState.copy() ## frameInfo includes pixelSize, objective, centerPosition, scopePosition, imagePosition ss = self.dev.getScopeState() if ss['id'] != scopeState: #print "scope state changed" scopeState = ss['id'] ## regenerate frameInfo here ps = ss['pixelSize'] ## size of CCD pixel #pos = ss['centerPosition'] #pos2 = [pos[0] - size[0]*ps[0]*0.5 + region[0]*ps[0], pos[1] - size[1]*ps[1]*0.5 + region[1]*ps[1]] transform = pg.SRTTransform3D(ss['transform']) #transform.translate(region[0]*ps[0], region[1]*ps[1]) ## correct for ROI here frameInfo = { 'pixelSize': [ps[0] * binning[0], ps[1] * binning[1]], ## size of image pixel #'scopePosition': ss['scopePosition'], #'centerPosition': pos, 'objective': ss.get('objective', None), #'imagePosition': pos2 #'cameraTransform': ss['transform'], 'cameraTransform': transform, } ## Copy frame info to info array info.update(frameInfo) #for k in frameInfo: #info[k] = frameInfo[k] ## Process all waiting frames. If there is more than one frame waiting, guess the frame times. dt = (now - lastFrameTime) / len(frames) if dt > 0: info['fps'] = 1.0/dt else: info['fps'] = None for frame in frames: frameInfo = info.copy() data = frame['data'] #print data del frame['data'] frameInfo.update(frame) out = Frame(data, frameInfo) with self.connectMutex: conn = list(self.connections) for c in conn: c(out) #self.emit(QtCore.SIGNAL("newFrame"), out) self.sigNewFrame.emit(out) lastFrameTime = now lastFrameId = frames[-1]['id'] loopCount = 0 time.sleep(1e-3) ## check for stop request every 10ms if now - lastStopCheck > 10e-3: lastStopCheck = now ## If no frame has arrived yet, do NOT allow the camera to stop (this can hang the driver) << bug should be fixed in pvcam driver, not here. self.lock.lock() if self.stopThread: self.stopThread = False self.lock.unlock() break self.lock.unlock() diff = ptime.time()-lastFrameTime if diff > (10 + exposure): if mode == 'Normal': print "Camera acquisition thread has been waiting %02f sec but no new frames have arrived; shutting down." % diff break else: pass ## do not exit loop if there is a possibility we are waiting for a trigger #from debug import Profiler #prof = Profiler() with self.camLock: #self.cam.stop() self.dev.stopCamera() #prof.mark(' camera stop:') except: try: with self.camLock: #self.cam.stop() self.dev.stopCamera() except: pass printExc("Error starting camera acquisition:") #self.emit(QtCore.SIGNAL("showMessage"), "ERROR starting acquisition (see console output)") self.sigShowMessage.emit("ERROR starting acquisition (see console output)") finally: pass #print "Camera ACQ thread exited." def stop(self, block=False): #print "AcquireThread.stop: Requesting thread stop, acquiring lock first.." with self.lock: self.stopThread = True #print "AcquireThread.stop: got lock, requested stop." #print "AcquireThread.stop: Unlocked, waiting for thread exit (%s)" % block if block: if not self.wait(10000): raise Exception("Timed out waiting for thread exit!") #print "AcquireThread.stop: thread exited" def reset(self): if self.isRunning(): self.stop() if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!") #self.start(QtCore.QThread.HighPriority) self.start()
class SutterMP285Thread(QtCore.QThread): sigPositionChanged = QtCore.Signal(object) sigError = QtCore.Signal(object) def __init__(self, dev, driver, driverLock, scale, limits, maxSpd): QtCore.QThread.__init__(self) self.lock = Mutex(QtCore.QMutex.Recursive) self.scale = scale self.mp285 = driver self.driverLock = driverLock #self.monitor = True self.update = False self.resolution = 'fine' self.dev = dev #self.port = port #self.pos = [0, 0, 0] #self.baud = baud self.velocity = [0,0,0] self.limits = deepcopy(limits) self.limitChanged = False self.maxSpeed = maxSpd #self.posxyz = [ 0, 0, 0] def setResolution(self, res): with self.lock: self.resolution = res def setLimits(self, limits): with self.lock: self.limits = deepcopy(limits) self.limitChanged = True def setMaxSpeed(self, s): with self.lock: self.maxSpeed = s #def setMonitor(self, mon): #with self.lock: #self.monitor = mon def updatePos(self): with self.lock: self.update = True def setVelocity(self, v): with self.lock: self.velocity = v def run(self): self.stopThread = False #self.sp = serial.Serial(int(self.port), baudrate=self.baud, bytesize=serial.EIGHTBITS) #time.sleep(3) ## Wait a few seconds for the mouse to say hello ## clear buffer before starting #if self.sp.inWaiting() > 0: #print "Discarding %d bytes" % self.sp.inWaiting() #self.sp.read(self.sp.inWaiting()) #import wingdbstub print " Starting MP285 thread: 0x%x" % int(QtCore.QThread.currentThreadId()) #import sip #print " also known as 0x%x" % sip.unwrapinstance(self) velocity = np.array([0,0,0]) pos = [0,0,0] try: self.getImmediatePos() monitor = True except: debug.printExc("Sutter MP285: Cannot determine position:") monitor = False while True: try: ## Lock and copy state to local variables with self.lock: update = self.update self.update = False limits = deepcopy(self.limits) maxSpeed = self.maxSpeed newVelocity = np.array(self.velocity[:]) resolution = self.resolution limitChanged = self.limitChanged self.limitChanged = False ## if limits have changed, inform the device if monitor and limitChanged: ## monitor is only true if this is a customized device with limit checking self.sendLimits() ## If requested velocity is different from the current velocity, handle that. if np.any(newVelocity != velocity): speed = np.clip(np.sum(newVelocity**2)**0.5, 0., 1.) ## should always be 0.0-1.0 #print "new velocity:", newVelocity, "speed:", speed if speed == 0: nv = np.array([0,0,0]) else: nv = newVelocity/speed speed = np.clip(speed, 0, maxSpeed) #print "final speed:", speed ## stop current move, get position, start new move #print "stop.." self.stopMove() #print "stop done." #print "getpos..." pos1 = self.readPosition() if pos1 is not None: if speed > 0: #print " set new velocity" self.writeVelocity(speed, nv, limits=limits, pos=pos1, resolution=resolution) #print " done" ## report current position velocity = newVelocity #print "done" ## If velocity is 0, we can do some position checks if np.all(velocity == 0): newPos = None if update: newPos = self.readPosition() elif monitor: newPos = self.getImmediatePos() if newPos is not None: ## If position has changed, emit a signal. change = [newPos[i] - pos[i] for i in range(len(newPos))] pos = newPos if any(change): #self.emit(QtCore.SIGNAL('positionChanged'), {'rel': change, 'abs': self.pos}) self.sigPositionChanged.emit({'rel': change, 'abs': pos}) else: ## moving; make a guess about the current position pass except: pass debug.printExc("Error in MP285 thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.mp285.close() def stop(self, block=False): #print " stop: locking" with self.lock: #print " stop: requesting stop" self.stopThread = True if block: #print " stop: waiting" if not self.wait(10000): raise Exception("Timed out while waiting for thread exit!") #print " stop: done" def readPosition(self): try: with self.driverLock: pos = np.array(self.mp285.getPos()) return pos*self.scale except TimeoutError: self.sigError.emit("Read timeout--press reset button on MP285.") return None except: self.sigError.emit("Read error--see console.") debug.printExc("Error getting packet:") return None def writeVelocity(self, spd, v, limits, pos, resolution): if not self.checkLimits(pos, limits): return ## check all limits for closest stop point pt = self.vectorLimit(pos, v, limits) if pt is None: return ## set speed #print " SPD:", spd, "POS:", pt self._setSpd(spd, fineStep=(resolution=='fine')) self._setPos(pt, block=False) def vectorLimit(self, pos, v, limits): ## return position of closest limit intersection given starting point and vector pts = [] pos = np.array(pos) v = np.array(v) for i in [0,1,2]: if v[i] < 0: p = self.intersect(pos, v, limits[i][0], i) else: p = self.intersect(pos, v, limits[i][1], i) if p is not None: pts.append(p) if len(pts) == 0: return None lengths = [np.sum((p-pos)**2)**0.5 for p in pts] i = np.argmin(lengths) return pts[i] def intersect(self, pos, v, x, ax): ## return the point where vector pos->v intersects plane x along axis ax if v[ax] == 0: return None dx = x-pos[ax] return pos + dx * v/v[ax] def checkLimits(self, pos, limits): for i in [0,1,2]: #print 'pos, limits', pos[i] #print limits[i] #print limits[i][0] #print limits[i][1] if pos[i] < limits[i][0] or pos[i] > limits[i][1]: return False return True def sendLimits(self): limits = [] for ax in [0,1,2]: for lim in [1,0]: pos, en = self.limits[ax][lim] limits.append(pos/self.scale[ax] if en else None) with self.driverLock: self.mp285.setLimits(limits) def _setPos(self, pos, block=True): pos = np.array(pos) / self.scale with self.driverLock: self.mp285.setPos(pos, block=block) def stopMove(self): with self.driverLock: self.mp285.stop() def _setSpd(self, v, fineStep=True): ## This should be done with a calibrated speed table instead. v = int(v*1e6) with self.driverLock: self.mp285.setSpeed(v, fineStep) def getImmediatePos(self): with self.driverLock: pos = np.array(self.mp285.getImmediatePos()) return pos*self.scale