def stateRequest(self, request, timeout=TIMEOUT): """ Helper method used to get or set camera state. Parameters ---------- request : `string` Command to be passed to the camera timeout : `float` Time to wait for camera answer Returns ------- `int` """ self.socket.send(request) timer = Timer(timeout) r = b'' while b'\n' not in r and timer.wait(self.socket): r += self.socket.recv(1) if timer.expired(): raise RuntimeError('Camera is not answering') r = r.strip(b'\x00\n') return int(r, 0)
def waitWhileOrUntil(self, condition, timeout=TIMEOUT, until=False): """ Helper method that implements :meth:`waitWhile` and :meth:`waitUntil` """ state = self.getState(timeout) timer = Timer(timeout) while until ^ bool(state & condition) and timer.check(): state = self.getState(timeout) if state & self.STATE_MASK_ERROR: raise RuntimeError('Camera returned error: %x' % state) if timer.expired(): raise RuntimeError('Camera got stuck condition: %x, state: %x' % (condition, state))
class Pilatus(StandardDevice, ICountable): """ Class to control Pilatus cameras via EPICS. Examples -------- >>> from shutil import move >>> from py4syn.epics.PilatusClass import Pilatus >>> from py4syn.epics.ShutterClass import SimpleShutter >>> >>> def getImage(pv, fileName='image.tif', shutter=''): ... shutter = SimpleShutter(shutter, shutter) ... camera = Pilatus('pilatus', pv) ... camera.setImageName('/remote/' + fileName) ... camera.setCountTime(10) ... camera.startCount() ... shutter.open() ... camera.wait() ... camera.stopCount() ... shutter.close() ... move('/remote/' + fileName, '/home/user/' + fileName) ... camera.close() ... """ RESPONSE_TIMEOUT = 35 def __init__(self, mnemonic, pv): """ **Constructor** See :class:`py4syn.epics.StandardDevice` Parameters ---------- mnemonic : `string` A mnemonic for the camera pv : `string` Base name of the EPICS process variable """ super().__init__(mnemonic) self.acquireChanged = Event() self.acquiring = False self.pvAcquire = PV(pv + ':Acquire') self.pvAcquire.add_callback(self.statusChange) caput(pv + ':FileTemplate', '%s%s\0') caput(pv + ':FilePath', '\0') self.pvAcquireTime = PV(pv + ':AcquireTime') self.pvAcquirePeriod = PV(pv + ':AcquirePeriod') self.pvFilePath = PV(pv + ':FilePath') self.pvFileName = PV(pv + ':FileName') self.pvFileTemplate = PV(pv + ':FileTemplate') self.pvThreshold = PV(pv + ':ThresholdEnergy') self.pvBeamX = PV(pv + ':BeamX') self.pvBeamY = PV(pv + ':BeamY') self.pvWavelength = PV(pv + ':Wavelength') self.pvStartAngle = PV(pv + ':StartAngle') self.pvAngleIncr = PV(pv + ':AngleIncr') self.pvDetDist = PV(pv + ':DetDist') self.pvNumImages = PV(pv + ':NumImages') self.pvDelayTime = PV(pv + ':DelayTime') self.pvTriggerMode = PV(pv + ':TriggerMode') self.pvDet2Theta = PV(pv + ':Det2theta') self.pvCamserverConnectStatus = PV(pv + ':CamserverAsyn.CNCT') self.pvLastFileName = PV(pv + ":FullFileName_RBV") self.timer = Timer(self.RESPONSE_TIMEOUT) def setImageName(self, name): """ Sets the output image file name. The image will be saved with this name after the acquisition. Parameters ---------- name : `string` The full pathname of the image. """ self.pvFileName.put(name + "\0", wait=True) def setFilePath(self, path): """ Sets the output image file path. The image will be saved in this location after the acquisition. Parameters ---------- name : `string` The path of location to save the image. """ self.pvFilePath.put(path + "\0", wait=True) def getFilePath(self): """ Returns the path where image file should be saved. """ return self.pvFilePath.get(as_string=True) def setFileName(self, name): """ Sets the output image file name. The image will be saved with this name after the acquisition. Parameters ---------- name : `string` The name of image to save. """ self.pvFileName.put(name + "\0", wait=True) def getFileName(self): """ Returns the name of the image to be saved. """ return self.pvFileName.get(as_string=True) def setFileTemplate(self, template="%s%s"): self.pvFileTemplate.put(template + "\0", wait=True) def getFileTemplate(self): return self.pvFileTemplate.get(as_string=True) def statusChange(self, value, **kw): """ Helper callback used to wait for the end of the acquisition. """ self.acquiring = value self.acquireChanged.set() def close(self): """ Stops an ongoing acquisition, if any, and puts the EPICS IOC in idle state. """ self.pvAcquire.put(0, wait=True) def getValue(self, **kwargs): """ This is a dummy method that always returns zero, which is part of the :class:`py4syn.epics.ICountable` interface. Pilatus does not return a value while scanning. Instead, it stores a file with the resulting image. """ return 0 def setCountTime(self, t): """ Sets the image acquisition time. Parameters ---------- t : `float` Acquisition time """ self.pvAcquireTime.put(t, wait=True) self.pvAcquirePeriod.put(t + READOUTTIME, wait=True) self.timer = Timer(t + self.RESPONSE_TIMEOUT) def getAcquireTime(self): return self.pvAcquireTime.get() def setAcquirePeriod(self, period): """ Sets the acquire period. Parameters ---------- t : `float` Acquisition period """ self.pvAcquirePeriod.put(period, wait=True) def getAcquirePeriod(self): return self.pvAcquirePeriod.get() def setPresetValue(self, channel, val): """ Dummy method to set initial counter value. """ pass def startCount(self): """ Starts acquiring an image. It will acquire for the duration set with :meth:`setCountTime`. The resulting file will be stored in the file set with :meth:`setImageName`. See: :meth:`setCountTime`, :meth:`setImageName` Examples -------- >>> def acquire(pilatus, time, filename): ... pilatus.setCountTime(time) ... pilatus.setImageName(filename) ... pilatus.startCount() ... pilatus.wait() ... pilatus.stopCount() ... """ if self.acquiring: raise RuntimeError('Already counting') self.acquiring = True self.pvAcquire.put(1) self.timer.mark() def stopCount(self): """ Stops acquiring the image. This method simply calls :meth:`close`. See: :meth:`close` """ self.close() def canMonitor(self): """ Returns false indicating that Pilatus cannot be used as a counter monitor. """ return False def canStopCount(self): """ Returns true indicating that Pilatus has a stop command. """ return True def isCounting(self): """ Returns true if the camera is acquiring an image, or false otherwise. Returns ------- `bool` """ return self.acquiring def wait(self): """ Blocks until the acquisition completes. """ if not self.acquiring: return self.acquireChanged.clear() while self.acquiring and self.timer.check(): self.acquireChanged.wait(5) self.acquireChanged.clear() if self.timer.expired(): raise RuntimeError('Camera is not answering') def setThreshold(self, threshold, wait=True): self.pvThreshold.put(threshold, wait=wait) def getThreshold(self): return self.pvThreshold.get() def setBeamPosition(self, position=[0, 0]): self.pvBeamX.put(position[0], wait=True) self.pvBeamY.put(position[1], wait=True) def getBeamPosition(self): return [self.pvBeamX.get(), self.pvBeamY.get()] def setWavelength(self, wavelength): self.pvWavelength.put(wavelength, wait=True) def getWavelength(self): return self.pvWavelength.get() def setStartAngle(self, start): self.pvStartAngle.put(start, wait=True) def getStartAngle(self): return self.pvStartAngle.get() def setAngleIncr(self, incr): self.pvAngleIncr.put(incr, wait=True) def getAngleIncr(self): return self.pvAngleIncr.get() def setDetDist(self, distance): self.pvDetDist.put(distance, wait=True) def getDetDist(self): return self.pvDetDist.get() def setNumImages(self, num): self.pvNumImages.put(num, wait=True) def getNumImages(self): return self.pvNumImages.get() def setDelayTime(self, delay): self.pvDelayTime.put(delay, wait=True) def getDelayTime(self): return self.pvDelayTime.get() def setTriggerMode(self, mode): """ Trigger mode Parameters ---------- mode : `int` 0 : Internal 1 : Ext. Enable 2 : Ext. Trigger 3 : Mult. Trigger 4 : Alignment """ self.pvTriggerMode.put(mode, wait=True) def getTriggerMode(self): return self.pvTriggerMode.get() def setDet2Theta(self, det2theta): self.pvDet2Theta.put(det2theta, wait=True) def getDet2Theta(self): return self.pvDet2Theta.get() def isCamserverConnected(self): return (self.pvCamserverConnectStatus.get() == 1)
class OceanOpticsSpectrometer(ImageHDF): # CONSTRUCTOR OF Ocean CLASS def __init__(self, mnemonic, pv=None, responseTimeout=15, output="./out", numPoints=1044, mcas=False): """Constructor responseTimeout : how much time to wait qe65000 answer numPoints : how many points are collected each time """ super().__init__(mnemonic, numPoints, output, 'ocean') self.acquireChanged = Event() self.acquiring = False try: # determines the start of counting self.pvStart = PV(pv + ":Acquire") # determines mode of Acquisition (Single,Continous, Dark Spectrum) self.pvAcquireMode = PV(pv + ":AcquisitionMode") # use darkcorrection self.pvDarkCorrection = PV(pv + ":ElectricalDark") # spectrum self.pvSpectrum = PV(pv + ":Spectra") self.pvSpectrumCorrected = PV(pv + ":DarkCorrectedSpectra") # set Acquire Time self.pvAcquireTime = PV(pv + ":SetIntegration") # integration Time self.pvTime = PV(pv + ":IntegrationTime:Value") # control the end of acquire process self.pvAcquire = PV(pv + ":Acquiring") self.pvAcquire.add_callback(self.statusChange) # acquisition mode self.pvAcMode = PV(pv + ":AcquisitionMode") # set to single mode self.pvAcMode.put("Single") # axis Spectra pvAxis = PV(pv + ":SpectraAxis") self.axis = pvAxis.get(as_numpy=True)[:self.numPoints] # regions of interest self.ROIS = [] self.mcas = mcas self.responseTimeout = responseTimeout self.timer = Timer(self.responseTimeout) except TypeError: raise RuntimeError('PV not found') except ValueError: raise RuntimeError('Device is offline') def statusChange(self, value, **kw): """ Helper callback used to wait for the end of the acquisition. """ if value == 0: self.acquiring = False else: self.acquiring = True # threads waiting are awakened self.acquireChanged.set() def setCountTime(self, time): """ Method to set the count time of a scaler device. Parameters ---------- time : `float` Count time to set to scaler device . Returns ------- out : None """ self.pvTime.put(time, wait=True) self.timer = Timer(time + self.responseTimeout) def getCountTime(self): return self.pvTime.get() def setCountStop(self): # TODO: test # Work only when in continuos mode pass def close(self): self.setCountStop() def saveUniquePoint(self, data, fmt, suffixName=""): self.mcaFile = super().nameFile(self.output, self.prefix + suffixName, "mca") np.savetxt(self.mcaFile, data, fmt=fmt) def saveSpectrum(self, **kwargs): ''' save the spectrum intensity in a mca file or an hdf file ''' dark = self.pvDarkCorrection.get() # the spectra come from different pv if use darkcorrection if dark == 1: allSpectrum =\ self.pvSpectrumCorrected.get(as_numpy=True)[:self.numPoints] else: allSpectrum = self.pvSpectrum.get(as_numpy=True)[:self.numPoints] self.spectrum = allSpectrum suffix = "" if self.image: super().saveSpectrum() if not self.image or self.mcas: self.saveUniquePoint( np.array([self.axis, self.spectrum]).T, "%f\t%f") # there are ROIS to save / works only for points if len(self.ROIS) > 0 and not self.image: i = 1 for mini, maxi in self.ROIS: # get the spectrum positions start = bisect(self.axis, mini) end = bisect(self.axis, maxi) roi = allSpectrum[start:end] self.spectrum = roi data = np.array([self.axis[start:end], self.spectrum]).T self.saveUniquePoint(data, "%f\t%f", suffixName="_ROI" + str(i)) i += 1 def isCountRunning(self): return (self.acquiring) def wait(self): """ Blocks until the acquisition completes. """ if self.acquiring is False: return self.acquireChanged.clear() # while acquiring and not time out waits # TODO: find a better way to do this while self.acquiring and self.timer.check(): self.acquireChanged.wait(0.001) self.acquireChanged.clear() if self.timer.expired(): raise RuntimeError('Ocean is not answering') def canMonitor(self): """ Returns false indicating cannot be use as a counter monitor""" return False def canStopCount(self): """ Returns true indicating that Dxp has a stop command. """ return False def getValue(self, **kwargs): """Return intensity It's a dummy method, always return 1.0. """ self.saveSpectrum() return 1.0 def isCounting(self): return self.acquiring def startCount(self): """ Starts acquiring an spectrum It's necessary to call setCounTime before""" if self.acquiring: raise RuntimeError('Already counting') self.acquiring = True self.pvStart.put("Stop") # resets initial time value self.timer.mark() def stopCount(self): self.setCountStop() def setPresetValue(self, channel, val): """Dummy method""" pass def startCollectImage(self, rows=0, cols=0): super().startCollectImage('float32', rows, cols) def addRoi(self, roi): """ Insert a new roi roi: a tuple with begin and end: (begin,end)""" self.ROIS.append(roi)
class Dxp(StandardDevice, ICountable): # CONSTRUCTOR OF DXP CLASS def __init__(self, mnemonic, output, numberOfChannels=4, numberOfRois=32, pv=None, dxpType="mca", responseTimeout=15): """ Constructor responseTimeout : how much time to wait dxp answer """ super().__init__(mnemonic) self.acquireChanged = Event() self.acquiring = False self.fileName = output # determines the start of counting self.pvDxpEraseStart = PV(pv + ":EraseStart.VAL") # determines mode of counting (Live Time, Real Time, ...) self.pvDxpPresetMode = PV(pv + ":PresetMode.VAL") self.pvDxpStop = PV(pv + ":StopAll.VAL") # store all channels self.pvDxpChannels = [] # store ROIs self.pvDxpRois = [] # store Acquire Time for each channel self.pvDxpAcquireTime = [] for c in range(0, numberOfChannels): # store each channel self.pvDxpChannels.append(PV(pv + ":" + dxpType + str(c + 1))) # for each channel store the PV for AcquireTime self.pvDxpAcquireTime.append( PV(pv + ":" + dxpType + "%d.PLTM" % (c + 1))) self.pvDxpRois.append([]) # storeing each ROI in your channel for r in range(0, numberOfRois): self.pvDxpRois[c].append( PV(pv + ":" + dxpType + str(c + 1) + '.R' + str(r))) self.pvDxpAcquire = PV(pv + ":Acquiring") self.pvDxpAcquire.add_callback(self.statusChange) self.channels = numberOfChannels self.dxpType = dxpType self.rois = numberOfRois self.responseTimeout = responseTimeout self.timer = Timer(self.responseTimeout) def statusChange(self, value, **kw): """ Helper callback used to wait for the end of the acquisition. """ self.acquiring = value # threads waiting are awakened self.acquireChanged.set() def setCountTime(self, time): """ Method to set the count time of a scaler device. Parameters ---------- time : `float` Count time to set to scaler device . Returns ------- out : None """ for i in range(0, self.channels): self.pvDxpAcquireTime[i].put(time, wait=True) self.timer = Timer(time + self.responseTimeout) def getCountTime(self): return self.pvDxpTime.get() def setCountStop(self): self.pvDxpStop.put(1, wait=True) def getValueChannel(self, **kwargs): """Return intensity channel is on format mcaC.Rr, where C is the channel and r is the ROI""" channel = kwargs['channel'] c = int(channel[3]) - 1 if (len(channel) > 4): r = int(channel[5]) return self.pvDxpRois[c][r] else: self.saveSpectrum(c, **kwargs) return 1.0 # save the spectrum intensity in a mca file def saveSpectrum(self, ch, **kwargs): fileName = self.fileName idx = 0 if (fileName): spectrum = self.pvDxpChannels[ch].get(as_numpy=True) prefix = fileName.split('.')[0] while os.path.exists('%s_%s%d_%04d.mca' % (prefix, self.dxpType, ch, idx)): idx += 1 fileName = '%s_%s%d_%04d.mca' % \ (prefix, self.dxpType, ch, idx) np.savetxt(fileName, spectrum, fmt='%f') def isCountRunning(self): return (self.pvDxpAcquire.get()) def wait(self): """ Blocks until the acquisition completes. """ if self.acquiring is False: return self.acquireChanged.clear() # while acquiring and not time out waits # TODO: find a better way to do this while self.acquiring and self.timer.check(): self.acquireChanged.wait(0.001) self.acquireChanged.clear() if self.timer.expired(): raise RuntimeError('DXP is not answering') def canMonitor(self): """ Returns false indcating Dxp cannot be use as a counter monitor""" return False def canStopCount(self): """ Returns true indicating that Dxp has a stop command. """ return True def getValue(self, **kwargs): """ This is a dummy method that always returns zero, which is part of the :class:`py4syn.epics.ICountable` interface. Dxp does not return a value while scanning. Instead, it stores a mca file with result . """ if (kwargs): return self.getValueChannel(**kwargs) return self.getValueChannel() def isCounting(self): return self.acquiring def startCount(self): """ Starts acquiring an spectrum It's necessary to call setCounTime before""" if self.acquiring: raise RuntimeError('Already counting') self.acquiring = True self.pvDxpEraseStart.put(1) # resets initial time value self.timer.mark() def stopCount(self): self.setCountStop() def setPresetValue(self, channel, val): """Dummy method""" pass def close(self): """Stops an ongoing acquisition, if any, and puts the EPICS IOC in idle state.""" self.pvDxpStop.put(1, wait=True)
class Dxp(ImageHDF): # CONSTRUCTOR OF DXP CLASS def __init__(self, mnemonic, numberOfChannels=4, numberOfRois=32, pv=None, dxpType="mca", responseTimeout=15, output="./out", numPoints=2048): """ Constructor responseTimeout : how much time to wait dxp answer imageDeep : how many points are collected each time """ super().__init__(mnemonic, numPoints, output, dxpType) self.dxpType = dxpType self.acquireChanged = Event() self.acquiring = False # determines the start of counting self.pvDxpEraseStart = PV(pv + ":EraseStart.VAL") # determines mode of counting (Live Time, Real Time, ...) self.pvDxpPresetMode = PV(pv + ":PresetMode.VAL") self.pvDxpStop = PV(pv + ":StopAll.VAL") # store all channels self.pvDxpChannels = [] # store ROIs self.pvDxpRois = [] # store Acquire Time for each channel self.pvDxpAcquireTime = [] self.pvDxpRealTime = [] for c in range(0, numberOfChannels): # store each channel self.pvDxpChannels.append(PV(pv + ":" + dxpType + str(c + 1))) # for each channel store the PV for AcquireTime self.pvDxpAcquireTime.append( PV(pv + ":" + dxpType + "%d.PLTM" % (c + 1))) # real time self.pvDxpRealTime.append( PV(pv + ":" + dxpType + "%d.ERTM" % (c + 1))) self.pvDxpRois.append([]) # storeing each ROI in your channel for r in range(0, numberOfRois): self.pvDxpRois[c].append( PV(pv + ":" + dxpType + str(c + 1) + '.R' + str(r))) self.pvDxpAcquire = PV(pv + ":Acquiring") self.pvDxpAcquire.add_callback(self.statusChange) self.channels = numberOfChannels self.rois = numberOfRois self.responseTimeout = responseTimeout self.timer = Timer(self.responseTimeout) def statusChange(self, value, **kw): """ Helper callback used to wait for the end of the acquisition. """ self.acquiring = value # threads waiting are awakened self.acquireChanged.set() def setCountTime(self, time): """ Method to set the count time of a scaler device. Parameters ---------- time : `float` Count time to set to scaler device . Returns ------- out : None """ for i in range(0, self.channels): self.pvDxpAcquireTime[i].put(time, wait=True) # This make long exposure time works if (self.responseTimeout < time * 0.4): self.responseTimeout = time * 0.4 self.timer = Timer(time + self.responseTimeout) def getCountTime(self): # AcquireTimes are the same return self.pvDxpAcquireTime[0].get() def getRealTime(self): """Return the Real Time""" if self.channels == 1: return self.pvDxpRealTime[0].get() else: times = [] for i in range(0, self.channels): times.append(self.pvDxpRealTime[i].get()) return times def setCountStop(self): self.pvDxpStop.put(1, wait=True) def getValueChannel(self, **kwargs): """Return intensity channel is on format mcaC.Rr, where C is the channel and r is the ROI""" channel = kwargs['channel'] c = int(channel[CHANNELPOSITION]) - 1 if (len(channel) > ROIPOSITION): r = int(channel[ROIPOSITION]) return self.pvDxpRois[c][r].get() else: self.saveSpectrum(c, **kwargs) return 1.0 def saveSpectrum(self, ch, **kwargs): '''save the spectrum intensity in a mca file or an hdf file This method load spectrum from a PV and then save it to HDF file''' self.pvDxpPresetMode.put("Live time") self.ch = ch self.spectrum = self.pvDxpChannels[self.ch].get(as_numpy=True) if self.image is None: # if is a point, prefix is different self.prefix = self.dxpType + str(self.ch) super().saveSpectrum() def isCountRunning(self): return (self.pvDxpAcquire.get()) def wait(self): """ Blocks until the acquisition completes. """ if self.acquiring is False: return self.acquireChanged.clear() # while acquiring and not time out waits # TODO: find a better way to do this while self.acquiring and self.timer.check(): self.acquireChanged.wait(0.001) self.acquireChanged.clear() if self.timer.expired(): raise RuntimeError('DXP is not answering') def canMonitor(self): """ Returns false indcating Dxp cannot be use as a counter monitor""" return False def canStopCount(self): """ Returns true indicating that Dxp has a stop command. """ return True def getValue(self, **kwargs): """ This is a dummy method that always returns zero, which is part of the :class:`py4syn.epics.ICountable` interface. Dxp does not return a value while scanning. Instead, it stores a mca file with result . """ if (kwargs): return self.getValueChannel(**kwargs) return self.getValueChannel() def isCounting(self): return self.acquiring def startCount(self): """ Starts acquiring an spectrum It's necessary to call setCounTime before""" if self.acquiring: raise RuntimeError('Already counting') self.acquiring = True self.pvDxpEraseStart.put(1) # resets initial time value self.timer.mark() def stopCount(self): self.setCountStop() def setPresetValue(self, channel, val): """Dummy method""" pass def close(self): """Stops an ongoing acquisition, if any, and puts the EPICS IOC in idle state.""" self.pvDxpStop.put(1, wait=True) def startCollectImage(self, rows=0, cols=0): """Start to collect an image When collect an image, the points will be saved on a hdf file""" super().startCollectImage("int32", rows, cols) def setNormValue(self, value): """Applies normalization""" if self.image is None: self.prefix = self.dxpType + str(self.ch) super().setNormValue(value)