"""Dectris Pilatus X-ray camera class
Python class for Pilatus X-ray cameras using EPICS area detector
IOC.
:platform: Unix
:synopsis: Python class for Pilatus X-ray cameras
.. moduleauthor:: Henrique Dante de Almeida <henrique.almeida@lnls.br>
"""
from threading import Event
from py4syn.epics.StandardDevice import StandardDevice
from py4syn.epics.ICountable import ICountable
from py4syn.utils.timer import Timer
from epics import PV, ca, caput
# Pilatus ReadOut time
READOUTTIME = 1
[docs]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)
[docs] 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)
[docs] 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)
[docs] def getFilePath(self):
"""
Returns the path where image file should be saved.
"""
return self.pvFilePath.get(as_string=True)
[docs] 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)
[docs] 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)
[docs] def statusChange(self, value, **kw):
"""
Helper callback used to wait for the end of the acquisition.
"""
self.acquiring = value
self.acquireChanged.set()
[docs] def close(self):
"""
Stops an ongoing acquisition, if any, and puts the EPICS IOC in idle state.
"""
self.pvAcquire.put(0, wait=True)
[docs] 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
[docs] 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()
[docs] 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()
[docs] def setPresetValue(self, channel, val):
"""
Dummy method to set initial counter value.
"""
pass
[docs] 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()
[docs] def stopCount(self):
"""
Stops acquiring the image. This method simply calls :meth:`close`.
See: :meth:`close`
"""
self.close()
[docs] def canMonitor(self):
"""
Returns false indicating that Pilatus cannot be used as a counter monitor.
"""
return False
[docs] def canStopCount(self):
"""
Returns true indicating that Pilatus has a stop command.
"""
return True
[docs] def isCounting(self):
"""
Returns true if the camera is acquiring an image, or false otherwise.
Returns
-------
`bool`
"""
return self.acquiring
[docs] 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()
[docs] 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)