def __init__( self, name, pgm_grat_pitch, pgm_mirr_pitch, pgmpvroot): # motors, maybe also detector to set the delay time self.logger = LoggerFactory.getLogger( "ContinuousPgmGratingEnergyMoveController:%s" % name) self.verbose = False self.name = name self._pgm_grat_pitch = pgm_grat_pitch self._pgm_mirr_pitch = pgm_mirr_pitch self._start_event = threading.Event() self._pgm_grat_pitch_speed_orig = None self._movelog_time = datetime.now() self._pgm_runupdown_time = None self.pvs = PvManager( { 'grating_density': 'NLINES', 'cff': 'CFF', 'grating_offset': 'GRTOFFSET', 'plane_mirror_offset': 'MIROFFSET', 'pgm_energy': 'ENERGY', 'grating_pitch': 'GRT:PITCH', 'mirror_pitch': 'MIR:PITCH', 'energy_calibration_gradient': 'MX', 'energy_calibration_reference': 'REFERENCE' }, pgmpvroot) if installation.isLive(): self.pvs.configure()
def __init__(self, name, pvroot, filepath=None, stdNotArr=True, reconnect=True, verbose=False, acquireToCagetDelay_s=0.1): # BL07I-DI-PHDGN-06:CAM:DATA self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.verbose = verbose self.acquireToCagetDelay_s = acquireToCagetDelay_s stdArr = "STD" if stdNotArr else "ARR" pvs = { "DATA": stdArr + ":ArrayData", "START": "CAM:Acquire", "WIDTH": "CAM:ArraySizeX_RBV", "HEIGHT": "CAM:ArraySizeY_RBV", "COLLECTTIME": "CAM:AcquireTime" } if reconnect: pvs["RECONNECT"] = "CAM:RESET.PROC" self.pvs = PvManager(pvs, pvroot) self.pvs.configure() self.filepath = filepath self.ds = None self.disableStop = False
class CryojetController(): def __init__(self, pvroot): if isinstance(pvroot, str): from gdascripts.scannable.epics.PvManager import PvManager self.pvs = PvManager({'setpoint': 'TTEMP:SET', 'sensor': 'STEMP'}, pvroot) self.pvs.configure() else: self.pvs = pvroot self.pvs.pvroot = "" def __repr__(self): return "CryojetController(pvroot=%r)" % ( self.pvs.pvroot) def __str__(self): return "setpoint=%f, sensor=%f" % ( self.getTempSetPoint(), self.getSensorTemp()) def setTempSetPoint(self, setpoint): self.pvs['setpoint'].caput(setpoint) def getTempSetPoint(self): return float(self.pvs['setpoint'].caget()) def getTempSensor(self): return float(self.pvs['sensor'].caget())
def __init__(self, name, detsystem, type, meta, unit=None, pv=None): super(DetectorMetaWithPv, self).__init__(name, detsystem, type, meta, unit) self.pv = None if pv is not None: self.pv = PvManager(pv[1:], pv[0]) self.pv.configure() self.pv_conversion = 1
def __init__(self, pvroot): if isinstance(pvroot, str): from gdascripts.scannable.epics.PvManager import PvManager self.pvs = PvManager( { 'setpoint': 'SET:SETPOINTFIELD', 'setpointRbv': 'RBV:SETPOINTFIELD', 'sweeprate': 'SET:FIELDSWEEPRATE', 'sweeprateRbv': 'RBV:FIELDSWEEPRATE', 'demand_field': 'RBV:DEMANDFIELD' }, pvroot) self.pvs.configure() else: self.pvs = pvroot self.pvs.pvroot = ""
def __init__(self, name, pvroot, filepath=None, determine_data_pv_based_on_zoom = False, numtracker_extension='tmp', filename_template='fire%d_%05d.png'): # BL07I-DI-PHDGN-06:CAM:DATA self.determine_data_pv_based_on_zoom = determine_data_pv_based_on_zoom self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.pvs = PvManager(['SET_SHUTTR', 'WIDTH', 'HEIGHT', 'ZOOM'], pvroot) self.pv_data = [] self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA1')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA2')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA3')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA4')) self.pvs.configure() self.filepath = filepath self.ds = None self.last_image_path = None self.last_filename = None self.last_image_number = 0 self.numtracker_extension = numtracker_extension self.filename_template = filename_template
class DetectorMetaWithPv(DetectorMeta, object): def __init__(self, name, detsystem, type, meta, unit=None, pv=None): super(DetectorMetaWithPv, self).__init__(name, detsystem, type, meta, unit) self.pv = None if pv is not None: self.pv = PvManager(pv[1:], pv[0]) self.pv.configure() self.pv_conversion = 1 def rawAsynchronousMoveTo(self, p): if p is not None: p = float(p) if p <= 0: p = None if self.pv: self.pv.caput(self.pv_conversion * p) super(DetectorMetaWithPv, self).rawAsynchronousMoveTo(p)
class IntelligentPowerSupply(): def __init__(self, pvroot): if isinstance(pvroot, str): from gdascripts.scannable.epics.PvManager import PvManager self.pvs = PvManager( { 'setpoint': 'SET:SETPOINTFIELD', 'setpointRbv': 'RBV:SETPOINTFIELD', 'sweeprate': 'SET:FIELDSWEEPRATE', 'sweeprateRbv': 'RBV:FIELDSWEEPRATE', 'demand_field': 'RBV:DEMANDFIELD' }, pvroot) self.pvs.configure() else: self.pvs = pvroot self.pvs.pvroot = "" def __repr__(self): return "IntelligentPowerSupplyScannable(%r)" % (self.pvs.pvroot) def __str__(self): return "setpoint=%f, demand_field=%f" % (self.getFieldSetPoint(), self.getFieldDemand()) def getFieldSetPoint(self): return float(self.pvs['setpoint'].caget()) def setFieldSetPoint(self, setpoint): self.pvs['setpoint'].caput(setpoint) def getFieldSetPointRBV(self): return float(self.pvs['setpointRbv'].caget()) def getSweepRate(self): return float(self.pvs['sweeprate'].caget()) def setSweepRate(self, setpoint): self.pvs['sweeprate'].caput(setpoint) def getSweepRateRBV(self): return float(self.pvs['sweeprateRbv'].caget()) def getFieldDemand(self): return float(self.pvs['demand_field'].caget())
def __init__(self, pvroot): if isinstance(pvroot, str): from gdascripts.scannable.epics.PvManager import PvManager self.pvs = PvManager({'setpoint': 'TTEMP:SET', 'sensor': 'STEMP'}, pvroot) self.pvs.configure() else: self.pvs = pvroot self.pvs.pvroot = ""
def __init__(self, name, pvroot, filepath, filename, fileformat): self.name = name self.inputNames = ['ExposureTime'] self.extraNames = ['FileNum'] self.outputFormat = ['%.2f', '%.0f'] self.level = 9 self.filepath = None self.filename = None self.fileformat = None self.filenum = None self.cached_exptime = None self.filenum = None self.pvs = PvManager([ 'Acquire', 'NImages', 'Abort', 'ExposureTime', 'FilePath', 'Filename', 'FileNumber', 'FileFormat', 'ThresholdEnergy' ], pvroot) self.configure(filepath, filename, fileformat)
def __init__(self, name, pgm_grat_pitch, pgm_mirr_pitch, pgmpvroot, id_energy, idpvroot, move_pgm=True, move_id=True): # motors, maybe also detector to set the delay time self.logger = LoggerFactory.getLogger("ContinuousPgmGratingIDGapEnergyMoveController:%s" % name) self.verbose = False self.name = name self._start_event = threading.Event() self._movelog_time = datetime.now() #PGM self._pgm_grat_pitch = pgm_grat_pitch self._pgm_mirr_pitch = pgm_mirr_pitch self._pgm_grat_pitch_speed_orig = None self._pgm_runupdown_time = None self._move_pgm = move_pgm self._move_id = move_id self.pvs = PvManager({'grating_density': 'NLINES', 'cff': 'CFF', 'grating_offset': 'GRTOFFSET', 'plane_mirror_offset': 'MIROFFSET', 'pgm_energy': 'ENERGY', 'grating_pitch': 'GRT:PITCH', 'mirror_pitch': 'MIR:PITCH', 'energy_calibration_gradient': 'MX', 'energy_calibration_reference': 'REFERENCE'}, pgmpvroot) if installation.isLive(): self.pvs.configure() #ID self._id_energy=id_energy self._id_gap = self._id_energy.id_gap self._id_gap_speed_orig=None self._id_runupdown_time = None self.idpvs = PvManager({'vel':'BLGSETVEL', 'acc':'IDGSETACC'}, idpvroot) if installation.isLive(): self.idpvs.configure() self.grating_pitch_positions=[] self.mirror_pitch_positions=[] self.pgm_energy_positions=[] self._start_time = None self.k=1.0 self.pgmspeedfactor=1.0
def __init__( self, name, energy, idgap, idpvroot, move_mono=True, move_id=True): # motors, maybe also detector to set the delay time self.logger = LoggerFactory.getLogger( "ContinuousEnergyMoveController:%s" % name) self.verbose = False self.setName(name) self._start_event = threading.Event() self._movelog_time = datetime.now() self._energy = energy #PGM self._mono_energy = energy.mono_energy self._mono_energy_speed_orig = None self._mono_runupdown_time = None #ID self._id_gap = idgap self._id_gap_speed_orig = None self._id_runupdown_time = None self.idpvs = PvManager({ 'vel': 'BLGSETVEL', 'acc': 'IDGSETACC' }, idpvroot) if installation.isLive(): self.idpvs.configure() self.mono_energy_positions = [] self.id_gap_positions = [] self._start_time = None #behaviour properties self._move_mono = move_mono self._move_id = move_id self.idspeedfactor = 1.0 self.monospeedfactor = 1.0 self.idstartdelaytime = 0.0 self.continuousMovingStarted = False
def __init__(self, name, pvroot, filepath=None, determine_data_pv_based_on_zoom=False, numtracker_extension='tmp', filename_template='fire%d_%05d.png'): # BL07I-DI-PHDGN-06:CAM:DATA self.determine_data_pv_based_on_zoom = determine_data_pv_based_on_zoom self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.pvs = PvManager(['SET_SHUTTR', 'WIDTH', 'HEIGHT', 'ZOOM'], pvroot) self.pv_data = [] self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA1')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA2')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA3')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA4')) self.pvs.configure() self.filepath = filepath self.ds = None self.last_image_path = None self.last_filename = None self.last_image_number = 0 self.numtracker_extension = numtracker_extension self.filename_template = filename_template
def __init__(self, name, rootPV, continuousMoveController): self.name = name self.setInputNames([name]) self.setExtraNames(self._getExtraNames()) self.setOutputFormat(self._getOutputFormat()) self.setLevel(100) self.pvs = PvManager(pvroot=rootPV) self.continuousMoveController = continuousMoveController #from gda.device.zebra.controller.impl import ZebraImpl #self.zebra = ZebraImpl() self.verbose = False self.negative_offset = 0.05 self.setDelayAfterRise(-0.01) self.setDelayAfterFall(-0.01) self.setAcquireTimePerPulse(0.02)
def __init__(self, name, pvroot, filepath, filename, fileformat): self.name = name self.inputNames = ['ExposureTime'] self.extraNames = ['FileNum'] self.outputFormat = ['%.2f', '%.0f'] self.level = 9 self.filepath = None self.filename = None self.fileformat = None self.filenum = None self.cached_exptime = None self.filenum = None self.pvs = PvManager(['Acquire','NImages','Abort','ExposureTime','FilePath','Filename','FileNumber','FileFormat','ThresholdEnergy'], pvroot) self.configure(filepath, filename, fileformat)
def __init__(self, name, pvroot, filepath=None, stdNotArr=True, reconnect=True, verbose=False, acquireToCagetDelay_s = 0.1): # BL07I-DI-PHDGN-06:CAM:DATA self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.verbose = verbose self.acquireToCagetDelay_s = acquireToCagetDelay_s stdArr = "STD" if stdNotArr else "ARR" pvs = { "DATA":stdArr+":ArrayData", "START":"CAM:Acquire", "WIDTH":"CAM:ArraySizeX_RBV", "HEIGHT":"CAM:ArraySizeY_RBV", "COLLECTTIME":"CAM:AcquireTime" } if reconnect: pvs["RECONNECT"]="CAM:RESET.PROC" self.pvs = PvManager(pvs, pvroot) self.pvs.configure() self.filepath = filepath self.ds = None self.disableStop = False
class ContinuousPgmGratingEnergyMoveController(ConstantVelocityMoveController, DeviceBase): def __init__( self, name, pgm_grat_pitch, pgm_mirr_pitch, pgmpvroot): # motors, maybe also detector to set the delay time self.logger = LoggerFactory.getLogger( "ContinuousPgmGratingEnergyMoveController:%s" % name) self.verbose = False self.name = name self._pgm_grat_pitch = pgm_grat_pitch self._pgm_mirr_pitch = pgm_mirr_pitch self._start_event = threading.Event() self._pgm_grat_pitch_speed_orig = None self._movelog_time = datetime.now() self._pgm_runupdown_time = None self.pvs = PvManager( { 'grating_density': 'NLINES', 'cff': 'CFF', 'grating_offset': 'GRTOFFSET', 'plane_mirror_offset': 'MIROFFSET', 'pgm_energy': 'ENERGY', 'grating_pitch': 'GRT:PITCH', 'mirror_pitch': 'MIR:PITCH', 'energy_calibration_gradient': 'MX', 'energy_calibration_reference': 'REFERENCE' }, pgmpvroot) if installation.isLive(): self.pvs.configure() # Implement: public interface ConstantVelocityMoveController extends ContinuousMoveController def setStart(self, start): # double self._move_start = start if self.verbose: self.logger.info('setStart(%r)' % start) def setEnd(self, end): # double self._move_end = end if self.verbose: self.logger.info('setEnd(%r)' % end) def setStep(self, step): # double self._move_step = step if self.verbose: self.logger.info('setStep(%r)' % step) # Implement: public interface ContinuousMoveController extends HardwareTriggerProvider def getPgmEnergyParameters(self): grating_densityFromEnum = {'0': 400., '1': 400., '2': 1200.} return (grating_densityFromEnum[self.pvs['grating_density'].caget()], float(self.pvs['cff'].caget()), float(self.pvs['grating_offset'].caget()), float(self.pvs['plane_mirror_offset'].caget()), float(self.pvs['energy_calibration_gradient'].caget()), float(self.pvs['energy_calibration_reference'].caget())) def getPgmEnergyParametersFixed(self): # Hard code these values until we can work out a nice way of getting at the PVs # TODO: Get the values from their Epics PVs """ caget BL10I-OP-PGM-01:NLINES BL10I-OP-PGM-01:CFF BL10I-OP-PGM-01:GRTOFFSET BL10I-OP-PGM-01:MIROFFSET BL10I-OP-PGM-01:MX BL10I-OP-PGM-01:REFERENCE """ grating_density = 400. # caget BL10I-OP-PGM-01:NLINES cff = 2.25 # caget BL10I-OP-PGM-01:CFF grating_offset = -0.053747 # caget BL10I-OP-PGM-01:GRTOFFSET plane_mirror_offset = 0.002514 # caget BL10I-OP-PGM-01:MIROFFSET #pgm_energy = 712.300 # caget BL10I-OP-PGM-01:ENERGY #grating_pitch = 88.0151063265128 # caget -g15 BL10I-OP-PGM-01:GRT:PITCH #mirror_pitch = 88.2753263680692 # caget -g15 BL10I-OP-PGM-01:MIR:PITCH energy_calibration_gradient = 1.0178 # caget BL10I-OP-PGM-01:MX energy_calibration_reference = 392.555 # caget BL10I-OP-PGM-01:REFERENCE return grating_density, cff, grating_offset, plane_mirror_offset, energy_calibration_gradient, energy_calibration_reference def prepareForMove(self): if self.verbose: self.logger.info('prepareForMove()...') self._pgm_grat_pitch_speed_orig = self._pgm_grat_pitch.speed # Calculate the energy midpoint energy_midpoint = (self._move_end + self._move_start) / 2. if self.verbose: self.logger.info('prepareForMove:energy_midpoint=%r ' % (energy_midpoint)) if installation.isLive(): (self.grating_density, self.cff, self.grating_offset, self.plane_mirror_offset, self.energy_calibration_gradient, self.energy_calibration_reference ) = self.getPgmEnergyParameters() else: (self.grating_density, self.cff, self.grating_offset, self.plane_mirror_offset, self.energy_calibration_gradient, self.energy_calibration_reference ) = self.getPgmEnergyParametersFixed() # Calculate plane mirror angle for given grating density, energy, cff and offsets self.mirr_pitch_midpoint = enecff2mirror( gd=self.grating_density, energy=energy_midpoint, cff=self.cff, groff=self.grating_offset, pmoff=self.plane_mirror_offset, ecg=self.energy_calibration_gradient, ecr=self.energy_calibration_reference) # Calculate grating angles for given grating density, energy, mirror angle and offsets self._grat_pitch_start = enemirror2grating( gd=self.grating_density, energy=self._move_start, pmang=self.mirr_pitch_midpoint, groff=self.grating_offset, pmoff=self.plane_mirror_offset, ecg=self.energy_calibration_gradient, ecr=self.energy_calibration_reference) self._grat_pitch_end = enemirror2grating( gd=self.grating_density, energy=self._move_end, pmang=self.mirr_pitch_midpoint, groff=self.grating_offset, pmoff=self.plane_mirror_offset, ecg=self.energy_calibration_gradient, ecr=self.energy_calibration_reference) ### Calculate main cruise moves & speeds from start/end/step self._pgm_grat_pitch_speed = ( abs(self._grat_pitch_end - self._grat_pitch_start) / self.getTotalTime()) ### Calculate ramp distance from required speed and ramp times # Set the speed before we read out ramp times in case it is dependent self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed # Should really be / | | | | | \ not /| | | | |\ self._pgm_runupdown_time = self._pgm_grat_pitch.timeToVelocity self._pgm_runupdown = self._pgm_grat_pitch_speed / 2 * self._pgm_runupdown_time ### Move motor at full speed to start position if self.verbose: self.logger.info( 'prepareForMove:_pgm_mirr_pitch.asynchronousMoveTo(%r) @ %r ' % (self.mirr_pitch_midpoint * 1000., self._pgm_mirr_pitch.speed)) self._pgm_mirr_pitch.asynchronousMoveTo(self.mirr_pitch_midpoint * 1000.) self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed_orig if self.getGratingMoveDirectionPositive(): if self.verbose: self.logger.info( 'prepareForMove:asynchronousMoveTo(%r) @ %r (+ve)' % ((self._grat_pitch_start - self._pgm_runupdown) * 1000., self._pgm_grat_pitch_speed_orig)) self._pgm_grat_pitch.asynchronousMoveTo( (self._grat_pitch_start - self._pgm_runupdown) * 1000.) else: if self.verbose: self.logger.info( 'prepareForMove:asynchronousMoveTo(%r) @ %r (-ve)' % ((self._grat_pitch_start + self._pgm_runupdown) * 1000., self._pgm_grat_pitch_speed_orig)) self._pgm_grat_pitch.asynchronousMoveTo( (self._grat_pitch_start + self._pgm_runupdown) * 1000.) self.waitWhileMoving() ### Calculate trigger delays if self.verbose: self.logger.info('...prepareForMove') def startMove(self): if self.verbose: self.logger.info('startMove()...') # Notify all position callables to start waiting for their time self._start_time = datetime.now() self._start_event.set() # Start threads to start ID & PGM and at the correct times self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed if self.getGratingMoveDirectionPositive(): if self.verbose: self.logger.info( 'startMove:asynchronousMoveTo(%r) @ %r (+ve)' % ((self._grat_pitch_end + self._pgm_runupdown) * 1000., self._pgm_grat_pitch_speed)) self._pgm_grat_pitch.asynchronousMoveTo( (self._grat_pitch_end + self._pgm_runupdown) * 1000.) else: if self.verbose: self.logger.info( 'startMove:asynchronousMoveTo(%r) @ %r (-ve)' % ((self._grat_pitch_end - self._pgm_runupdown) * 1000., self._pgm_grat_pitch_speed)) self._pgm_grat_pitch.asynchronousMoveTo( (self._grat_pitch_end - self._pgm_runupdown) * 1000.) # How do we trigger the detectors, since they are 'HardwareTriggerable'? if self.verbose: self.logger.info('...startMove') def isMoving(self): if self.verbose and (datetime.now() - self._movelog_time) > timedelta(seconds=1): self.logger.info( 'isMoving() _pgm_grat_pitch=%r @ %r, _pgm_mirr_pitch=%r @ %r' % (self._pgm_grat_pitch.isBusy(), self._pgm_grat_pitch(), self._pgm_mirr_pitch.isBusy(), self._pgm_mirr_pitch())) self._movelog_time = datetime.now() return self._pgm_grat_pitch.isBusy() or self._pgm_mirr_pitch.isBusy() def waitWhileMoving(self): if self.verbose: self.logger.info('waitWhileMoving()...') while self.isMoving(): time.sleep(1) if self.verbose: self.logger.info('...waitWhileMoving()') def stopAndReset(self): self._start_time = None self._start_event.clear() if self.verbose: self.logger.info('stopAndReset()') self._pgm_grat_pitch.stop() self._restore_orig_speed() # Implement: public interface HardwareTriggerProvider extends Device def setTriggerPeriod(self, seconds): # double self._triggerPeriod = seconds if self.verbose: self.logger.info('setTriggerPeriod(%r)' % seconds) def getNumberTriggers(self): triggers = self.getTotalMove() / self._move_step if self.verbose: self.logger.info('getNumberTriggers()=%r (%r)' % (int(triggers), triggers)) return int(triggers) def getTotalTime(self): totalTime = self.getNumberTriggers() * self._triggerPeriod if self.verbose: self.logger.info('getTotalTime()=%r' % totalTime) return totalTime def getTimeToVelocity(self): return self._pgm_runupdown_time # Override: public abstract class DeviceBase extends ConfigurableBase implements Device # None needed # Other functions def getTotalMove(self): totalMove = abs(self._move_end - self._move_start) if self.verbose: self.logger.info('getTotalMove()=%r' % totalMove) return totalMove def getGratingMoveDirectionPositive(self): return (self._grat_pitch_end - self._grat_pitch_start) > 0 class DelayableCallable(Callable): def __init__(self, controller, demand_position): #self.start_event = threading.Event() self.start_event = controller._start_event self._controller, self._demand_position = controller, demand_position self.logger = LoggerFactory.getLogger( "ContinuousPgmGratingEnergyMoveController:%s:DelayableCallable[%r]" % (controller.name, demand_position)) if self._controller.verbose: self.logger.info('__init__(%r, %r)...' % (controller.name, demand_position)) def call(self): if self._controller.verbose: self.logger.info('call...') # Wait for controller to start all motors moving and set start time if self._controller._start_time: if self._controller.verbose: self.logger.info('start_time=%r' % (self._controller._start_time)) else: if self._controller.verbose: self.logger.info('wait()...') timeout = 60 self.start_event.wait(timeout) if not self.start_event.isSet(): raise Exception( "%rs timeout waiting for startMove() to be called on %s at position %r." % (timeout, self._controller.name, self._demand_position)) if self._controller.verbose: self.logger.info('...wait()') # Wait for delay before actually move start and then a time given by # how far through the scan this point is grat_pitch = enemirror2grating( gd=self._controller.grating_density, energy=self._demand_position, pmang=self._controller.mirr_pitch_midpoint, groff=self._controller.grating_offset, pmoff=self._controller.plane_mirror_offset, ecg=self._controller.energy_calibration_gradient, ecr=self._controller.energy_calibration_reference) complete = abs((grat_pitch - self._controller._grat_pitch_start) / (self._controller._grat_pitch_end - self._controller._grat_pitch_start)) sleeptime_s = (self._controller._pgm_runupdown_time + (complete * self._controller.getTotalTime())) delta = datetime.now() - self._controller._start_time delta_s = delta.seconds + delta.microseconds / 1000000. if delta_s > sleeptime_s: self.logger.warn( 'Sleep time already past!!! sleeptime_s=%r, delta_s=%r, sleeptime_s-delta_s=%r' % (sleeptime_s, delta_s, sleeptime_s - delta_s)) else: if self._controller.verbose: self.logger.info( 'sleeping... sleeptime_s=%r, delta_s=%r, sleeptime_s-delta_s=%r' % (sleeptime_s, delta_s, sleeptime_s - delta_s)) time.sleep(sleeptime_s - delta_s) energy = angles2energy( gd=self._controller.grating_density, grang=self._controller._pgm_grat_pitch() / 1000., pmang=self._controller._pgm_mirr_pitch() / 1000., groff=self._controller.grating_offset, pmoff=self._controller.plane_mirror_offset, ecg=self._controller.energy_calibration_gradient, ecr=self._controller.energy_calibration_reference) if self._controller.verbose: self.logger.info('...DelayableCallable:call returning %r, %r' % (self._demand_position, energy)) return self._demand_position, energy def getPositionCallableExtraNames(self): return ['readback'] def getPositionCallableFormat(self): return ['%f', '%f'] # public Callable<T> getPositionCallable() throws DeviceException; def getPositionCallableFor(self, position): if self.verbose: self.logger.info('getPositionCallableFor(%r)...' % position) # TODO: return actual positions back calculated from energy positions return self.DelayableCallable(self, position) def _restore_orig_speed(self): if self._pgm_grat_pitch_speed_orig: if self.verbose: self.logger.info('Restoring original speed %r, was %r' % (self._pgm_grat_pitch_speed_orig, self._pgm_grat_pitch.speed)) self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed_orig self._pgm_grat_pitch_speed_orig = None def atScanEnd(self): if self.verbose: self.logger.info('atScanEnd()...') self._restore_orig_speed()
class ContinuousPgmGratingIDGapEnergyMoveController(ConstantVelocityMoveController, DeviceBase): '''Controller for constant velocity scan moving both PGM Grating Pitch and ID Gap at same time at constant speed respectively. It works for both Live and Dummy mode. ''' def __init__(self, name, pgm_grat_pitch, pgm_mirr_pitch, pgmpvroot, id_energy, idpvroot, move_pgm=True, move_id=True): # motors, maybe also detector to set the delay time self.logger = LoggerFactory.getLogger("ContinuousPgmGratingIDGapEnergyMoveController:%s" % name) self.verbose = False self.name = name self._start_event = threading.Event() self._movelog_time = datetime.now() #PGM self._pgm_grat_pitch = pgm_grat_pitch self._pgm_mirr_pitch = pgm_mirr_pitch self._pgm_grat_pitch_speed_orig = None self._pgm_runupdown_time = None self._move_pgm = move_pgm self._move_id = move_id self.pvs = PvManager({'grating_density': 'NLINES', 'cff': 'CFF', 'grating_offset': 'GRTOFFSET', 'plane_mirror_offset': 'MIROFFSET', 'pgm_energy': 'ENERGY', 'grating_pitch': 'GRT:PITCH', 'mirror_pitch': 'MIR:PITCH', 'energy_calibration_gradient': 'MX', 'energy_calibration_reference': 'REFERENCE'}, pgmpvroot) if installation.isLive(): self.pvs.configure() #ID self._id_energy=id_energy self._id_gap = self._id_energy.id_gap self._id_gap_speed_orig=None self._id_runupdown_time = None self.idpvs = PvManager({'vel':'BLGSETVEL', 'acc':'IDGSETACC'}, idpvroot) if installation.isLive(): self.idpvs.configure() self.grating_pitch_positions=[] self.mirror_pitch_positions=[] self.pgm_energy_positions=[] self._start_time = None self.k=1.0 self.pgmspeedfactor=1.0 def setPGMSpeedFactor(self,val): self.pgmspeedfactor=val def getPGMSpeedFactor(self): return self.pgmspeedfactor #https://jira.diamond.ac.uk/browse/I10-301 def enableIDMove(self): self._move_id=True def disableIDMove(self): self._move_id=False def isIDMoveEnabled(self): return self._move_id def enablePGMMove(self): self._move_pgm=True def disablePGMMove(self): self._move_pgm=False def isPGMMoveEnabled(self): return self._move_pgm # Implement: public interface ConstantVelocityMoveController extends ContinuousMoveController def setStart(self, start): # double self._move_start = start if self.verbose: self.logger.info('setStart(%r)' % start) def setEnd(self, end): # double self._move_end = end if self.verbose: self.logger.info('setEnd(%r)' % end) def setStep(self, step): # double self._move_step = step if self.verbose: self.logger.info('setStep(%r)' % step) # Implement: public interface ContinuousMoveController extends HardwareTriggerProvider def getPgmEnergyParameters(self): grating_densityFromEnum = {'0' : 400., '1':400., '2':1200.} return (grating_densityFromEnum[self.pvs['grating_density'].caget()], float(self.pvs['cff'].caget()), float(self.pvs['grating_offset'].caget()), float(self.pvs['plane_mirror_offset'].caget()), float(self.pvs['energy_calibration_gradient'].caget()), float(self.pvs['energy_calibration_reference'].caget())) def getPgmEnergyParametersFixed(self): # Hard code these values until we can work out a nice way of getting at the PVs # TODO: Get the values from their Epics PVs """ caget BL10I-OP-PGM-01:NLINES BL10I-OP-PGM-01:CFF BL10I-OP-PGM-01:GRTOFFSET BL10I-OP-PGM-01:MIROFFSET BL10I-OP-PGM-01:MX BL10I-OP-PGM-01:REFERENCE """ grating_density = 400. # caget BL10I-OP-PGM-01:NLINES cff = 2.25 # caget BL10I-OP-PGM-01:CFF grating_offset = -0.053747 # caget BL10I-OP-PGM-01:GRTOFFSET plane_mirror_offset = 0.002514 # caget BL10I-OP-PGM-01:MIROFFSET #pgm_energy = 712.300 # caget BL10I-OP-PGM-01:ENERGY #grating_pitch = 88.0151063265128 # caget -g15 BL10I-OP-PGM-01:GRT:PITCH #mirror_pitch = 88.2753263680692 # caget -g15 BL10I-OP-PGM-01:MIR:PITCH energy_calibration_gradient = 1.0178 # caget BL10I-OP-PGM-01:MX energy_calibration_reference = 392.555 # caget BL10I-OP-PGM-01:REFERENCE return grating_density, cff, grating_offset, plane_mirror_offset, energy_calibration_gradient, energy_calibration_reference def PreparePGMForMove(self): self._pgm_grat_pitch_speed_orig = 0.018 self._pgm_grat_pitch.speed=self._pgm_grat_pitch_speed_orig # if self._pgm_grat_pitch_speed_orig != 0.018: # raise Exception("PGM Grit Pitch motor speed %f is not at maximum 0.018!" % (self._pgm_grat_pitch_speed_orig)) # Calculate the energy midpoint energy_midpoint = (self._move_end + self._move_start) / 2. if self.verbose: self.logger.info('preparePGMForMove:energy_midpoint=%r ' % (energy_midpoint)) if installation.isLive(): self.grating_density, self.cff, self.grating_offset, self.plane_mirror_offset, self.energy_calibration_gradient, self.energy_calibration_reference = self.getPgmEnergyParameters() else: self.grating_density, self.cff, self.grating_offset, self.plane_mirror_offset, self.energy_calibration_gradient, self.energy_calibration_reference = self.getPgmEnergyParametersFixed() # Calculate plane mirror angle for given grating density, energy, cff and offsets self.mirr_pitch_midpoint = enecff2mirror(gd=self.grating_density, energy=energy_midpoint, cff=self.cff, groff=self.grating_offset, pmoff=self.plane_mirror_offset, ecg=self.energy_calibration_gradient, ecr=self.energy_calibration_reference) # Calculate grating angles for given grating density, energy, mirror angle and offsets self._grat_pitch_start = enemirror2grating(gd=self.grating_density, energy=self._move_start, pmang=self.mirr_pitch_midpoint, groff=self.grating_offset, pmoff=self.plane_mirror_offset, ecg=self.energy_calibration_gradient, ecr=self.energy_calibration_reference) self._grat_pitch_end = enemirror2grating(gd=self.grating_density, energy=self._move_end, pmang=self.mirr_pitch_midpoint, groff=self.grating_offset, pmoff=self.plane_mirror_offset, ecg=self.energy_calibration_gradient, ecr=self.energy_calibration_reference) if not self.isIDMoveEnabled(): self.k=1.0 print self.k ### Calculate main cruise moves & speeds from start/end/step self._pgm_grat_pitch_speed = abs(self._grat_pitch_end - self._grat_pitch_start) / self.getTotalTime() print self._pgm_grat_pitch_speed self._pgm_grat_pitch_speed=self._pgm_grat_pitch_speed*self.k * self.pgmspeedfactor print self._pgm_grat_pitch_speed ### Calculate ramp distance from required speed and ramp times #check speed within limits if self._pgm_grat_pitch_speed<=0.000 or self._pgm_grat_pitch_speed>0.018: raise Exception("Calculated PGM Grit Pitch motor speed %f is outside limits [%f, %f]!" % (self._pgm_grat_pitch_speed, 0.000, 0.018)) # Set the speed before we read out ramp times in case it is dependent self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed # Should really be / | | | | | \ not /| | | | |\ self._pgm_runupdown_time = self._pgm_grat_pitch.timeToVelocity self._pgm_runupdown = self._pgm_grat_pitch_speed / 2 * self._pgm_runupdown_time ### Move motor at full speed to start position if self.verbose: self.logger.info('preparePGMForMove:_pgm_mirr_pitch.asynchronousMoveTo(%r) @ %r ' % (self.mirr_pitch_midpoint * 1000., self._pgm_mirr_pitch.speed)) self._pgm_mirr_pitch.asynchronousMoveTo(self.mirr_pitch_midpoint * 1000.) self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed_orig if self.getGratingMoveDirectionPositive(): if self.verbose: self.logger.info('preparePGMForMove:asynchronousMoveTo(%r) @ %r (+ve)' % ((self._grat_pitch_start - self._pgm_runupdown) * 1000., self._pgm_grat_pitch_speed_orig)) self._pgm_grat_pitch.asynchronousMoveTo((self._grat_pitch_start - self._pgm_runupdown) * 1000.) else: if self.verbose: self.logger.info('preparePGMForMove:asynchronousMoveTo(%r) @ %r (-ve)' % ((self._grat_pitch_start + self._pgm_runupdown) * 1000., self._pgm_grat_pitch_speed_orig)) self._pgm_grat_pitch.asynchronousMoveTo((self._grat_pitch_start + self._pgm_runupdown) * 1000.) def PrepareIDForMove(self): if installation.isLive(): self._id_gap_speed_orig = float(self.idpvs['vel'].caget()) else: self._id_gap_speed_orig = self._id_gap.speed # Calculate the energy midpoint energy_midpoint = (self._move_end + self._move_start) / 2. if self.verbose: self.logger.info('prepareIDForMove:energy_midpoint=%r ' % (energy_midpoint)) # Calculate rowphase motor positions for given polarisation at energy midpoint self.id_rowphase1_midpoint = self._id_energy.rowphase1_from_energy(energy_midpoint) self.id_rowphase2_midpoint = self._id_energy.rowphase2_from_energy(energy_midpoint) self.id_rowphase3_midpoint = self._id_energy.rowphase3_from_energy(energy_midpoint) self.id_rowphase4_midpoint = self._id_energy.rowphase4_from_energy(energy_midpoint) # Calculate grating angles for given grating density, energy, mirror angle and offsets self._id_gap_start = self._id_energy.gap_from_energy(self._move_start) self._id_gap_end = self._id_energy.gap_from_energy(self._move_end) ### Calculate main cruise moves & speeds from start/end/step self._id_gap_speed = abs(self._id_gap_end - self._id_gap_start) / self.getTotalTime() print self._id_gap_speed self._id_gap_speed_rounded = Math.round(self._id_gap_speed*500.0)/500.0 print self._id_gap_speed_rounded self.k=self._id_gap_speed_rounded/self._id_gap_speed ### Calculate ramp distance from required speed and ramp times #check speed within limits if self._id_gap_speed_rounded<0.004 or self._id_gap_speed_rounded>1.0: raise Exception("Calculated ID gap speed %f is outside limits [%f, %f]!" % (self._id_gap_speed_rounded, 0.004, 1.0)) if installation.isLive(): #self._id_gap.speed = self._id_gap_speed #Cannot set id_gap.speed through soft motor which in EPICS is read-only self.idpvs['vel'].caput(self._id_gap_speed_rounded) else: self._id_gap.speed = self._id_gap_speed_rounded #acceleration time per axis self._id_axis_speed=self._id_gap_speed_rounded/2 self._id_runupdown_time_per_axis=self._id_axis_speed*4 # Should really be / | | | | | \ not /| | | | |\ self._id_runupdown_per_axis = self._id_axis_speed / 2 * self._id_runupdown_time_per_axis self._id_gap_runupdown = self._id_runupdown_per_axis * 2 ### Move motor at full speed to start position if self.verbose: self.logger.info('prepareIDForMove:%s.asynchronousMoveTo(%r) ' % (self._id_energy.id_rowphase1.getName(),self.id_rowphase1_midpoint)) self.logger.info('prepareIDForMove:%s.asynchronousMoveTo(%r) ' % (self._id_energy.id_rowphase2.getName(),self.id_rowphase2_midpoint)) self.logger.info('prepareIDForMove:%s.asynchronousMoveTo(%r) ' % (self._id_energy.id_rowphase3.getName(),self.id_rowphase3_midpoint)) self.logger.info('prepareIDForMove:%s.asynchronousMoveTo(%r) ' % (self._id_energy.id_rowphase4.getName(),self.id_rowphase4_midpoint)) self._id_energy.id_rowphase1.asynchronousMoveTo(self.id_rowphase1_midpoint) self._id_energy.id_rowphase2.asynchronousMoveTo(self.id_rowphase2_midpoint) self._id_energy.id_rowphase3.asynchronousMoveTo(self.id_rowphase3_midpoint) self._id_energy.id_rowphase4.asynchronousMoveTo(self.id_rowphase4_midpoint) if installation.isLive(): self.idpvs['vel'].caput(self._id_gap_speed_orig) else: self._id_gap.speed = self._id_gap_speed_orig if self.getIDGapMoveDirectionPositive(): if self.verbose: self.logger.info('prepareIDForMove:asynchronousMoveTo(%r) @ %r (+ve)' % ((self._id_gap_start - self._id_gap_runupdown), self._id_gap_speed_orig)) self._id_energy.id_gap.asynchronousMoveTo((self._id_gap_start - self._id_gap_runupdown)) else: if self.verbose: self.logger.info('prepareIDForMove:asynchronousMoveTo(%r) @ %r (-ve)' % ((self._id_gap_start + self._id_gap_runupdown), self._id_gap_speed_orig)) self._id_energy.id_gap.asynchronousMoveTo((self._id_gap_start + self._id_gap_runupdown)) def prepareForMove(self): if self.verbose: self.logger.info('prepareForMove()...') if self.isIDMoveEnabled(): self.PrepareIDForMove() else: if self.verbose: self.logger.info('ID move is disabled in %r' % (self.getName())) if self.isPGMMoveEnabled(): self.PreparePGMForMove() else: if self.verbose: self.logger.info('PGM move is disabled in %r' % (self.getName())) if not self.isPGMMoveEnabled() and not self.isIDMoveEnabled(): raise Exception("Both PGM and ID moves are disabled so no scan will occur!") self.waitWhileMoving() ### Calculate trigger delays if self.verbose: self.logger.info('...prepareForMove') def startMove(self): if self.verbose: self.logger.info('startMove()...') # Notify all position callables to start waiting for their time self._start_time = datetime.now() self._start_event.set() # Start threads to start ID & PGM and at the correct times if self.isPGMMoveEnabled(): self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed if self.isIDMoveEnabled(): if installation.isLive(): self.idpvs['vel'].caput(self._id_gap_speed_rounded) else: self._id_gap.speed = self._id_gap_speed_rounded if self.isPGMMoveEnabled(): if self.getGratingMoveDirectionPositive(): if self.verbose: self.logger.info('startMove PGM Grating Pitch: asynchronousMoveTo(%r) @ %r (+ve)' % ( (self._grat_pitch_end + self._pgm_runupdown)*1000., self._pgm_grat_pitch_speed)) self._pgm_grat_pitch.asynchronousMoveTo((self._grat_pitch_end + self._pgm_runupdown)*1000.) else: if self.verbose: self.logger.info('startMove PGM Grating Pitch: asynchronousMoveTo(%r) @ %r (-ve)' % ( (self._grat_pitch_end - self._pgm_runupdown)*1000., self._pgm_grat_pitch_speed)) self._pgm_grat_pitch.asynchronousMoveTo((self._grat_pitch_end - self._pgm_runupdown)*1000.) if self.isIDMoveEnabled(): if self.getIDGapMoveDirectionPositive(): if self.verbose: self.logger.info('startMove ID Gap: asynchronousMoveTo(%r) @ %r (+ve)' % ( (self._id_gap_end + 0.05 + self._id_gap_runupdown), self._id_gap_speed)) self._id_energy.id_gap.asynchronousMoveTo((self._id_gap_end + 0.05 + self._id_gap_runupdown)) else: if self.verbose: self.logger.info('startMove ID Gap: asynchronousMoveTo(%r) @ %r (-ve)' % ( (self._id_gap_end - 0.05 - self._id_gap_runupdown), self._id_gap_speed)) self._id_energy.id_gap.asynchronousMoveTo((self._id_gap_end - 0.05 - self._id_gap_runupdown)) # How do we trigger the detectors, since they are 'HardwareTriggerable'? if self.verbose: self.logger.info('...startMove') def isMoving(self): if self.verbose and (datetime.now() - self._movelog_time) > timedelta(seconds=1): self.logger.info('isMoving() _pgm_grat_pitch=%r @ %r, _pgm_mirr_pitch=%r @ %r, _id_gap=%r @ %r' % ( self._pgm_grat_pitch.isBusy(), self._pgm_grat_pitch(), self._pgm_mirr_pitch.isBusy(), self._pgm_mirr_pitch(), self._id_energy.id_gap.isBusy(), self._id_energy.id_gap())) self._movelog_time = datetime.now() return self._pgm_grat_pitch.isBusy() or self._pgm_mirr_pitch.isBusy() or self._id_energy.id_gap.isBusy() def waitWhileMoving(self): if self.verbose: self.logger.info('waitWhileMoving()...') while self.isMoving(): time.sleep(1) if self.verbose: self.logger.info('...waitWhileMoving()') def stopAndReset(self): self._start_time = None self._start_event.clear() if self.verbose: self.logger.info('stopAndReset()') if self.isPGMMoveEnabled(): self._pgm_grat_pitch.stop() if self.isIDMoveEnabled(): self._id_energy.id_gap.stop() self._restore_orig_speed() # Implement: public interface HardwareTriggerProvider extends Device def setTriggerPeriod(self, seconds): # double self._triggerPeriod = seconds if self.verbose: self.logger.info('setTriggerPeriod(%r)' % seconds) def getNumberTriggers(self): triggers = self.getTotalMove() / abs(self._move_step) if self.verbose: self.logger.info('getNumberTriggers()=%r (%r)' % (int(triggers), triggers)) return int(triggers) def getTotalTime(self): totalTime = self.getNumberTriggers() * self._triggerPeriod if self.verbose: self.logger.info('getTotalTime()=%r' % totalTime) return totalTime def getTimeToVelocity(self): return self._pgm_runupdown_time # Override: public abstract class DeviceBase extends ConfigurableBase implements Device # None needed # Other functions def getTotalMove(self): totalMove = abs(self._move_end - self._move_start) if self.verbose: self.logger.info('getTotalMove()=%r' % totalMove) return totalMove def getGratingMoveDirectionPositive(self): return (self._grat_pitch_end - self._grat_pitch_start) > 0 def getIDGapMoveDirectionPositive(self): return (self._id_gap_end - self._id_gap_start) > 0 class DelayableCallable(Callable): def __init__(self, controller, demand_position): #self.start_event = threading.Event() self.start_event = controller._start_event self._controller, self._demand_position = controller, demand_position self.logger = LoggerFactory.getLogger("ContinuousPgmGratingIDGapEnergyMoveController:%s:DelayableCallable[%r]" % (controller.name, demand_position)) if self._controller.verbose: self.logger.info('__init__(%r, %r)...' % (controller.name, demand_position)) def call(self): if self._controller.verbose: self.logger.info('call...') # Wait for controller to start all motors moving and set start time if self._controller._start_time: if self._controller.verbose: self.logger.info('start_time=%r' % (self._controller._start_time)) else: if self._controller.verbose: self.logger.info('wait()...') timeout=60 self.start_event.wait(timeout) if not self.start_event.isSet(): raise Exception("%rs timeout waiting for startMove() to be called on %s at position %r." % (timeout, self._controller.name, self._demand_position)) if self._controller.verbose: self.logger.info('...wait()') # Wait for delay before actually move start and then a time given by # how far through the scan this point is grat_pitch = enemirror2grating(gd = self._controller.grating_density, energy = self._demand_position, pmang = self._controller.mirr_pitch_midpoint, groff = self._controller.grating_offset, pmoff = self._controller.plane_mirror_offset, ecg = self._controller.energy_calibration_gradient, ecr = self._controller.energy_calibration_reference) complete = abs( (grat_pitch - self._controller._grat_pitch_start) / (self._controller._grat_pitch_end - self._controller._grat_pitch_start) ) sleeptime_s = (self._controller._pgm_runupdown_time + (complete * self._controller.getTotalTime())) delta = datetime.now() - self._controller._start_time delta_s = delta.seconds + delta.microseconds/1000000. if delta_s > sleeptime_s: self.logger.warn('Sleep time already past!!! sleeptime_s=%r, delta_s=%r, sleeptime_s-delta_s=%r' % (sleeptime_s, delta_s, sleeptime_s-delta_s)) else: if self._controller.verbose: self.logger.info('sleeping... sleeptime_s=%r, delta_s=%r, sleeptime_s-delta_s=%r' % (sleeptime_s, delta_s, sleeptime_s-delta_s)) time.sleep(sleeptime_s-delta_s) energy = angles2energy(gd = self._controller.grating_density, grang = self._controller._pgm_grat_pitch()/1000., pmang = self._controller._pgm_mirr_pitch()/1000., groff = self._controller.grating_offset, pmoff = self._controller.plane_mirror_offset, ecg = self._controller.energy_calibration_gradient, ecr = self._controller.energy_calibration_reference) if self._controller.verbose: self.logger.info('...DelayableCallable:call returning %r, %r' % (self._demand_position, energy)) return self._demand_position, energy def getPositionCallableExtraNames(self): return ['readback'] def getPositionCallableFormat(self): return ['%f', '%f'] # public Callable<T> getPositionCallable() throws DeviceException; def getPositionCallableFor(self, position): if self.verbose: self.logger.info('getPositionCallableFor(%r)...' % position) # TODO: return actual positions back calculated from energy positions return self.DelayableCallable(self, position) def _restore_orig_speed(self): if self.isPGMMoveEnabled(): if self._pgm_grat_pitch_speed_orig: if self.verbose: self.logger.info('Restoring original PGM Grating Pitch speed %r, was %r' % (self._pgm_grat_pitch_speed_orig, self._pgm_grat_pitch.speed)) self._pgm_grat_pitch.speed = self._pgm_grat_pitch_speed_orig self._pgm_grat_pitch_speed_orig = None if self.isIDMoveEnabled(): if self._id_gap_speed_orig: if installation.isLive(): if self.verbose: self.logger.info('Restoring original ID gap speed %r, was %r' % (self._id_gap_speed_orig, self.idpvs['vel'].caget())) self.idpvs['vel'].caput(self._id_gap_speed_orig) else: if self.verbose: self.logger.info('Restoring original ID gap speed %r, was %r' % (self._id_gap_speed_orig, self._id_gap.speed)) self._id_gap.speed = self._id_gap_speed_orig self._id_gap_speed_orig = None def atScanEnd(self): if self.verbose: self.logger.info('atScanEnd()...') self._restore_orig_speed()
class EpicsPilatus(PseudoDetector): '''Pilatus PD obj=PilatusClass(name,pvroot,filepath,filename) e.g. pilatus=PilatusClass('P100k','BL16I-EA-PILAT-01:','/dls/i16/data/Pilatus/','p') pilatus.getThesholdEnergy to print threshhold Make sure camserver is runnning ssh -X det@i16-pilatus1 (password Pilatus2) ps aux | grep cam to see if camserver running to start camserver... cd p2_1mod camonly self.display_data=0 removes data plotting and data summary for faster aquisition self.setFileName(name) sets data file name e.g. 'p' self.setFilePath(name) sets data file path e.g. '/dls/i16/data/Pilatus/' self.defaultFileName()/ self.defaultFilePath()sets data file name/path back to the name given when the device was created ''' def __init__(self, name, pvroot, filepath, filename, fileformat): self.name = name self.inputNames = ['ExposureTime'] self.extraNames = ['FileNum'] self.outputFormat = ['%.2f', '%.0f'] self.level = 9 self.filepath = None self.filename = None self.fileformat = None self.filenum = None self.cached_exptime = None self.filenum = None self.pvs = PvManager(['Acquire','NImages','Abort','ExposureTime','FilePath','Filename','FileNumber','FileFormat','ThresholdEnergy'], pvroot) self.configure(filepath, filename, fileformat) def configure(self, filepath = None, filename=None, fileformat = None): self.pvs.configure() if filepath: self.setFilepath(filepath) if filename: self.setFilename(filename) if fileformat: self.setFileformat(fileformat) self.setNumberImages(1) self.updateFilenumber() self.cached_exptime = 0 sleep(1) # DETECTOR INTERFACE def createsOwnFiles(self): return True def setCollectionTime(self, t): if t != self.cached_exptime: self.setCollectionTimeCommand(t) self.cached_exptime = t def getCollectionTime(self): return self.cached_exptime def collectData(self): self.acquireCommand() def getStatus(self): if self.isBusyCommand(): return BUSY else: return IDLE def readout(self): self.updateFilenumber() return self.fileformat % (self.filepath, self.filename, self.filenum) # SCANNABLE INTERFACE (currently used by pos but not scan ??) def stop(self): self.stopCommand() def getPosition(self): self.updateFilenumber() self.readout() return [self.cached_exptime, self.filenum] def asynchronousMoveTo(self, t): self.setCollectionTime(t) self.collectData() def getFilepath(self): return self.filepath # PILATUS COMMANDS # Note: Command appended where name already taken def setFilename(self, filename): self.pvs['Filename'].caput(filename) self.filename = filename def setFilepath(self, filepath): self.filepath=filepath self.pvs['FilePath'].caput(filepath) def setFileformat(self, fileformat): self.fileformat = fileformat self.pvs['FileFormat'].caput(fileformat) def updateFilenumber(self): self.filenum = float(self.pvs['FileNumber'].caget())-1 def setNumberImages(self, n): self.pvs['NImages'].caput(n) def setCollectionTimeCommand(self, t): self.pvs['ExposureTime'].caput(t) sleep(1) def isBusyCommand(self): return float(self.pvs['Acquire'].caget()) def stopCommand(self): self.pvs['Abort'].caput(1) def acquireCommand(self): self.pvs['Acquire'].caput(1) def getThresholdEnergy(self): print "WARNING: this method returns before the underlying hardware has been updated" return float(self.pvs['ThresholdEnergy'].caget())
class EpicsFirewireCamera(PseudoDetector): #TODO: Known bug: only works when epics zoom turned off (read the zoom value) def __init__(self, name, pvroot, filepath=None, determine_data_pv_based_on_zoom = False, numtracker_extension='tmp', filename_template='fire%d_%05d.png'): # BL07I-DI-PHDGN-06:CAM:DATA self.determine_data_pv_based_on_zoom = determine_data_pv_based_on_zoom self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.pvs = PvManager(['SET_SHUTTR', 'WIDTH', 'HEIGHT', 'ZOOM'], pvroot) self.pv_data = [] self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA1')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA2')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA3')) self.pv_data.append(gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA4')) self.pvs.configure() self.filepath = filepath self.ds = None self.last_image_path = None self.last_filename = None self.last_image_number = 0 self.numtracker_extension = numtracker_extension self.filename_template = filename_template def createsOwnFiles(self): return False def setCollectionTime(self, t): self.pvs['SET_SHUTTR'].caput(int(t)) def getCollectionTime(self): return float(self.pvs['SET_SHUTTR'].caget()) def prepareForCollection(self): self.last_image_number = -1 def collectData(self): #rawdata = self.pvs['DATA'].cagetArray() if self.determine_data_pv_based_on_zoom: zoom_ordinal = int(float(self.pvs['ZOOM'].caget())) rawdata = self.pv_data[zoom_ordinal].get() else: rawdata = self.pv_data[0].get() data = map(unsign2, rawdata ) self.ds = DatasetFactory.zeros(int(float(self.pvs['HEIGHT'].caget())), int(float(self.pvs['WIDTH'].caget())), data) self.last_image_number += 1 self.last_filename = self._generateCurrentFilename() if self.filepath is not None: self.saveImage(self.last_filename); def _getCurrentScanNumer(self): return NumTracker(self.numtracker_extension).getCurrentFileNumber(); def _generateCurrentFilename(self): if isScanRunning(): return self.filename_template % (self._getCurrentScanNumer(), self.last_image_number) else: return time.strftime("%Y%m%d%H%M%S", time.localtime()) + '%03d' % ((time.time()%1)*1000) + '.png' def getStatus(self): return IDLE def readout(self): if self.ds is None: raise Exception("Epics Firewire Camera %s has not acquired an image" % self.name) else: return self.ds def setFilepath(self, filepath): self.filepath=filepath def getFilepath(self): return self.filepath def getLastImagePath(self): return self.last_image_path def saveImage(self, filename): path = self.filepath + filename if self.ds is None: raise Exception("Epics Firewire Camera %s has not acquired an image" % self.name) else: sfh = ScanFileHolder() sfh.addDataSet("Image",self.ds) sfh.save(PNGSaver(path)) self.last_image_path = path
class EpicsGigECamera(PseudoDetector): #TODO: Known bug: only works when epics zoom turned off (read the zoom value) def __init__(self, name, pvroot, filepath=None, stdNotArr=True, reconnect=True, verbose=False, acquireToCagetDelay_s=0.1): # BL07I-DI-PHDGN-06:CAM:DATA self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.verbose = verbose self.acquireToCagetDelay_s = acquireToCagetDelay_s stdArr = "STD" if stdNotArr else "ARR" pvs = { "DATA": stdArr + ":ArrayData", "START": "CAM:Acquire", "WIDTH": "CAM:ArraySizeX_RBV", "HEIGHT": "CAM:ArraySizeY_RBV", "COLLECTTIME": "CAM:AcquireTime" } if reconnect: pvs["RECONNECT"] = "CAM:RESET.PROC" self.pvs = PvManager(pvs, pvroot) self.pvs.configure() self.filepath = filepath self.ds = None self.disableStop = False def createsOwnFiles(self): return False def setCollectionTime(self, t): #self.pvs["COLLECTTIME"].caput(int(t)) self.pvs["COLLECTTIME"].caput(float(t)) def getCollectionTime(self): return float(self.pvs['COLLECTTIME'].caget()) def setDisableStop(self, disable): self.disableStop = disable def stop(self): if self.verbose: print "%s.stop()" % self.name if not self.disableStop: self.pvs["START"].caput(0) def collectData(self): """ >>>pos cam1 1.5 cam1det.collectData() acquiring... cam1det.collectData() (1.604000s) ended cam1det.readout() getting... cam1det.readout() (0.340000s) sign correction... cam1det.readout() (6.910000s) creating DataSet... cam1det.readout() (0.352000s) saving... cam1det.readout() (0.581000s) ended Move completed: cam1 : t: 1.500000 path: 122201.0 """ if self.verbose: print "%s.collectData() acquiring..." % self.name t = time.time() self.ds = None self.pvs["START"].caput(1) sleep(self.getCollectionTime() + self.acquireToCagetDelay_s) if self.verbose: dt, t = time.time() - t, time.time() print "%s.collectData() (%fs) ended" % (self.name, dt) def getStatus(self): return IDLE def readout(self): if self.ds is None: if self.verbose: print "%s.readout() getting..." % self.name t = time.time() rawdata = self.pvs['DATA'].cagetArray() if self.verbose: dt, t = time.time() - t, time.time() print "%s.readout() (%fs) sign correction..." % (self.name, dt) data = map(unsign2, rawdata) if self.verbose: dt, t = time.time() - t, time.time() print "%s.readout() (%fs) creating DataSet..." % (self.name, dt) self.ds = DatasetFactory.createFromObject(data, [ int(float(self.pvs['HEIGHT'].caget())), int(float(self.pvs['WIDTH'].caget())) ]) if self.verbose: dt, t = time.time() - t, time.time() print "%s.readout() (%fs) saving..." % (self.name, dt) if self.filepath is not None: self.saveImage( time.strftime("%Y%m%d%H%M%S.png", time.localtime())) if self.verbose: dt, t = time.time() - t, time.time() print "%s.readout() (%fs) ended" % (self.name, dt) return self.ds def setFilepath(self, filepath): self.filepath = filepath def getFilepath(self): return self.filepath def saveImage(self, filename): path = self.filepath + filename if self.ds is None: raise Exception("Epics GigE Camera %s has not acquired an image" % self.name) else: sfh = ScanFileHolder() sfh.addDataSet("Image", self.ds) sfh.save(PNGSaver(path))
class EpicsGigECamera(PseudoDetector): #TODO: Known bug: only works when epics zoom turned off (read the zoom value) def __init__(self, name, pvroot, filepath=None, stdNotArr=True, reconnect=True, verbose=False, acquireToCagetDelay_s = 0.1): # BL07I-DI-PHDGN-06:CAM:DATA self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.verbose = verbose self.acquireToCagetDelay_s = acquireToCagetDelay_s stdArr = "STD" if stdNotArr else "ARR" pvs = { "DATA":stdArr+":ArrayData", "START":"CAM:Acquire", "WIDTH":"CAM:ArraySizeX_RBV", "HEIGHT":"CAM:ArraySizeY_RBV", "COLLECTTIME":"CAM:AcquireTime" } if reconnect: pvs["RECONNECT"]="CAM:RESET.PROC" self.pvs = PvManager(pvs, pvroot) self.pvs.configure() self.filepath = filepath self.ds = None self.disableStop = False def createsOwnFiles(self): return False def setCollectionTime(self, t): #self.pvs["COLLECTTIME"].caput(int(t)) self.pvs["COLLECTTIME"].caput(float(t)) def getCollectionTime(self): return float(self.pvs['COLLECTTIME'].caget()) def setDisableStop(self, disable): self.disableStop = disable def stop(self): if self.verbose: print "%s.stop()" % self.name if not self.disableStop: self.pvs["START"].caput(0) def collectData(self): """ >>>pos cam1 1.5 cam1det.collectData() acquiring... cam1det.collectData() (1.604000s) ended cam1det.readout() getting... cam1det.readout() (0.340000s) sign correction... cam1det.readout() (6.910000s) creating DataSet... cam1det.readout() (0.352000s) saving... cam1det.readout() (0.581000s) ended Move completed: cam1 : t: 1.500000 path: 122201.0 """ if self.verbose: print "%s.collectData() acquiring..." % self.name t = time.time() self.ds = None self.pvs["START"].caput(1) sleep(self.getCollectionTime()+self.acquireToCagetDelay_s) if self.verbose: dt, t = time.time()-t, time.time() print "%s.collectData() (%fs) ended" % (self.name, dt) def getStatus(self): return IDLE def readout(self): if self.ds is None: if self.verbose: print "%s.readout() getting..." % self.name t = time.time() rawdata = self.pvs['DATA'].cagetArray() if self.verbose: dt, t = time.time()-t, time.time() print "%s.readout() (%fs) sign correction..." % (self.name, dt) data = map(unsign2, rawdata ) if self.verbose: dt, t = time.time()-t, time.time() print "%s.readout() (%fs) creating DataSet..." % (self.name, dt) self.ds = DatasetFactory.createFromObject(data, [int(float(self.pvs['HEIGHT'].caget())), int(float(self.pvs['WIDTH'].caget()))]) if self.verbose: dt, t = time.time()-t, time.time() print "%s.readout() (%fs) saving..." % (self.name, dt) if self.filepath is not None: self.saveImage(time.strftime("%Y%m%d%H%M%S.png", time.localtime())) if self.verbose: dt, t = time.time()-t, time.time() print "%s.readout() (%fs) ended" % (self.name, dt) return self.ds def setFilepath(self, filepath): self.filepath=filepath def getFilepath(self): return self.filepath def saveImage(self, filename): path = self.filepath + filename if self.ds is None: raise Exception("Epics GigE Camera %s has not acquired an image" % self.name) else: sfh = ScanFileHolder() sfh.addDataSet("Image",self.ds) sfh.save(PNGSaver(path))
class EpicsPilatus(PseudoDetector): '''Pilatus PD obj=PilatusClass(name,pvroot,filepath,filename) e.g. pilatus=PilatusClass('P100k','BL16I-EA-PILAT-01:','/dls/i16/data/Pilatus/','p') pilatus.getThesholdEnergy to print threshhold Make sure camserver is runnning ssh -X det@i16-pilatus1 (password Pilatus2) ps aux | grep cam to see if camserver running to start camserver... cd p2_1mod camonly self.display_data=0 removes data plotting and data summary for faster aquisition self.setFileName(name) sets data file name e.g. 'p' self.setFilePath(name) sets data file path e.g. '/dls/i16/data/Pilatus/' self.defaultFileName()/ self.defaultFilePath()sets data file name/path back to the name given when the device was created ''' def __init__(self, name, pvroot, filepath, filename, fileformat): self.name = name self.inputNames = ['ExposureTime'] self.extraNames = ['FileNum'] self.outputFormat = ['%.2f', '%.0f'] self.level = 9 self.filepath = None self.filename = None self.fileformat = None self.filenum = None self.cached_exptime = None self.filenum = None self.pvs = PvManager([ 'Acquire', 'NImages', 'Abort', 'ExposureTime', 'FilePath', 'Filename', 'FileNumber', 'FileFormat', 'ThresholdEnergy' ], pvroot) self.configure(filepath, filename, fileformat) def configure(self, filepath=None, filename=None, fileformat=None): self.pvs.configure() if filepath: self.setFilepath(filepath) if filename: self.setFilename(filename) if fileformat: self.setFileformat(fileformat) self.setNumberImages(1) self.updateFilenumber() self.cached_exptime = 0 sleep(1) # DETECTOR INTERFACE def createsOwnFiles(self): return True def setCollectionTime(self, t): if t != self.cached_exptime: self.setCollectionTimeCommand(t) self.cached_exptime = t def getCollectionTime(self): return self.cached_exptime def collectData(self): self.acquireCommand() def getStatus(self): if self.isBusyCommand(): return BUSY else: return IDLE def readout(self): self.updateFilenumber() return self.fileformat % (self.filepath, self.filename, self.filenum) # SCANNABLE INTERFACE (currently used by pos but not scan ??) def stop(self): self.stopCommand() def getPosition(self): self.updateFilenumber() self.readout() return [self.cached_exptime, self.filenum] def asynchronousMoveTo(self, t): self.setCollectionTime(t) self.collectData() def getFilepath(self): return self.filepath # PILATUS COMMANDS # Note: Command appended where name already taken def setFilename(self, filename): self.pvs['Filename'].caput(filename) self.filename = filename def setFilepath(self, filepath): self.filepath = filepath self.pvs['FilePath'].caput(filepath) def setFileformat(self, fileformat): self.fileformat = fileformat self.pvs['FileFormat'].caput(fileformat) def updateFilenumber(self): self.filenum = float(self.pvs['FileNumber'].caget()) - 1 def setNumberImages(self, n): self.pvs['NImages'].caput(n) def setCollectionTimeCommand(self, t): self.pvs['ExposureTime'].caput(t) sleep(1) def isBusyCommand(self): return float(self.pvs['Acquire'].caget()) def stopCommand(self): self.pvs['Abort'].caput(1) def acquireCommand(self): self.pvs['Acquire'].caput(1) def getThresholdEnergy(self): print "WARNING: this method returns before the underlying hardware has been updated" return float(self.pvs['ThresholdEnergy'].caget())
class ContinuousEnergyMoveController(ConstantVelocityMoveController, DeviceBase): '''Controller for constant velocity scan moving both PGM Energy and JID Gap, or DCM energy and IID gap at same time at constant speed respectively. It works for both Live and Dummy mode. ''' def __init__( self, name, energy, idgap, idpvroot, move_mono=True, move_id=True): # motors, maybe also detector to set the delay time self.logger = LoggerFactory.getLogger( "ContinuousEnergyMoveController:%s" % name) self.verbose = False self.setName(name) self._start_event = threading.Event() self._movelog_time = datetime.now() self._energy = energy #PGM self._mono_energy = energy.mono_energy self._mono_energy_speed_orig = None self._mono_runupdown_time = None #ID self._id_gap = idgap self._id_gap_speed_orig = None self._id_runupdown_time = None self.idpvs = PvManager({ 'vel': 'BLGSETVEL', 'acc': 'IDGSETACC' }, idpvroot) if installation.isLive(): self.idpvs.configure() self.mono_energy_positions = [] self.id_gap_positions = [] self._start_time = None #behaviour properties self._move_mono = move_mono self._move_id = move_id self.idspeedfactor = 1.0 self.monospeedfactor = 1.0 self.idstartdelaytime = 0.0 self.continuousMovingStarted = False # Implement: public interface ConstantVelocityMoveController extends ContinuousMoveController def setStart(self, start): # double self._move_start = start if self.verbose: self.logger.info('setStart(%r)' % start) def setEnd(self, end): # double self._move_end = end if self.verbose: self.logger.info('setEnd(%r)' % end) def setStep(self, step): # double self._move_step = step if self.verbose: self.logger.info('setStep(%r)' % step) # Implement: public interface ContinuousMoveController extends HardwareTriggerProvider def PrepareMonoForMove(self): if self._mono_energy.getName() == "pgmenergy": self.energy_scale = 1000.0 #convert energy from keV to eV else: self.energy_scale = 1.0 self._mono_energy_speed_orig = self._mono_energy.speed #current speed in EPICS ### Calculate main cruise moves & speeds from start/end/step in eV self._mono_energy_speed = ( abs(self._move_end - self._move_start) * self.energy_scale / self.getTotalTime()) * self.getMonoSpeedFactor() #check speed within limits if self._mono_energy_speed <= 0.000 or self._mono_energy_speed > 10.0: #eV/sec raise DeviceException( "Calculated %s speed %f is outside limits [%f, %f]!" % (self._mono_energy.getName(), self._mono_energy_speed, 0.000, 10.0)) ### Calculate ramp distance from required speed and ramp times # Set the speed before we read out ramp times in case it is dependent self._mono_energy.speed = self._mono_energy_speed # Should really be / | | | | | \ not /| | | | |\ self._mono_runupdown_time = self._mono_energy.timeToVelocity self._mono_runupdown = self._mono_energy_speed / 2 * self._mono_runupdown_time ### Move motor at full speed to start position self._mono_energy.speed = self._mono_energy_speed_orig if self.isEnergyMoveDirectionPositive(): if self.verbose: self.logger.info( 'prepareMonoForMove: %s.asynchronousMoveTo(%r) @ %r (+ve)' % (self._mono_energy.getName(), (self._move_start * self.energy_scale - self._mono_runupdown), self._mono_energy_speed_orig)) self._mono_energy.asynchronousMoveTo( (self._move_start * self.energy_scale - self._mono_runupdown)) else: if self.verbose: self.logger.info( 'prepareMonoForMove: %s.asynchronousMoveTo(%r) @ %r (-ve)' % (self._mono_energy.getName(), (self._energy_start * self.energy_scale + self._mono_runupdown), self._mono_energy_speed_orig)) self._mono_energy.asynchronousMoveTo( (self._move_start * self.energy_scale + self._mono_runupdown)) def id_speed_limits(self): if self._id_gap.getName() == 'jgap': return JID_GAP_SPEED_LOWER_LIMIT, JID_GAP_SPEED_UPPER_LIMIT else: return IID_GAP_SPEED_LOWER_LIMIT, IID_GAP_SPEED_UPPER_LIMIT def PrepareIDForMove(self): GAP_SPEED_LOWER_LIMIT, GAP_SPEED_UPPER_LIMIT = self.id_speed_limits() if installation.isLive(): #get ID gap speed from EPICS PV self._id_gap_speed_orig = float(self.idpvs['vel'].caget()) else: self._id_gap_speed_orig = self._id_gap.speed #assume no row phase motors need to move during continuous energy move self._id_gap_start = self._energy.idgap( self._move_start) #idgap calculation using energy in keV self._id_gap_end = self._energy.idgap(self._move_end) ### Calculate main cruise moves & speeds from start/end/step self._id_gap_speed = abs(self._id_gap_end - self._id_gap_start ) / self.getTotalTime() * self.idspeedfactor #check speed within limits if self._id_gap_speed < GAP_SPEED_LOWER_LIMIT or self._id_gap_speed > GAP_SPEED_UPPER_LIMIT: raise DeviceException( "Calculated ID gap speed %f is outside limits [%f, %f]!" % (self._id_gap_speed, GAP_SPEED_LOWER_LIMIT, GAP_SPEED_UPPER_LIMIT)) if installation.isLive(): #Cannot set id_gap.speed through soft motor which in EPICS is read-only self.idpvs['vel'].caput(self._id_gap_speed) else: self._id_gap.speed = self._id_gap_speed ### Calculate ramp distance from required speed and ramp times #acceleration time per axis self._id_axis_speed = self._id_gap_speed / 2 self._id_runupdown_time_per_axis = self._id_axis_speed * 4 # Should really be / | | | | | \ not /| | | | |\ self._id_runupdown_per_axis = self._id_axis_speed / 2 * self._id_runupdown_time_per_axis self._id_gap_runupdown = self._id_runupdown_per_axis * 2 if installation.isLive(): self.idpvs['vel'].caput(self._id_gap_speed_orig) else: self._id_gap.speed = self._id_gap_speed_orig if self.isIDGapMoveDirectionPositive(): if self.verbose: self.logger.info( 'prepareIDForMove: _id_gap.asynchronousMoveTo(%r) @ %r (+ve)' % ((self._id_gap_start - self._id_gap_runupdown), self._id_gap_speed_orig)) self._id_gap.asynchronousMoveTo(self._id_gap_start - self._id_gap_runupdown) else: if self.verbose: self.logger.info( 'prepareIDForMove: _id_gap.asynchronousMoveTo(%r) @ %r (-ve)' % ((self._id_gap_start + self._id_gap_runupdown), self._id_gap_speed_orig)) self._id_gap.asynchronousMoveTo(self._id_gap_start + self._id_gap_runupdown) def prepareForMove(self): if self.verbose: self.logger.info('prepareForMove()...') self.continuousMovingStarted = False if self.isMonoMoveEnabled(): self.PrepareMonoForMove() else: if self.verbose: self.logger.info('%s move is disabled in %r' % (self._mono_energy.getName(), self.getName())) if self.isIDMoveEnabled(): self.PrepareIDForMove() else: if self.verbose: self.logger.info('ID %s move is disabled in %r' % (self._id_gap.getName(), self.getName())) if not self.isMonoMoveEnabled() and not self.isIDMoveEnabled(): print("Both PGM and ID moves are disabled so no scan will occur!") raise DeviceException( "Both PGM and ID moves are disabled so no scan will occur!") self.waitWhileMoving() if self.verbose: self.logger.info('...prepareForMove') def startMove(self): if self.verbose: self.logger.info('startMove()...') # Notify all position callables to start waiting for their time self._start_time = datetime.now() self._start_event.set() # Start threads to start ID & PGM and at the correct times if self.isMonoMoveEnabled(): self._mono_energy.speed = self._mono_energy_speed if self.isIDMoveEnabled(): if installation.isLive(): self.idpvs['vel'].caput(self._id_gap_speed) else: self._id_gap.speed = self._id_gap_speed if self.isMonoMoveEnabled(): if self.isEnergyMoveDirectionPositive(): if self.verbose: self.logger.info( 'startMove Mono Energy: _mono_energy.asynchronousMoveTo(%r) @ %r (+ve)' % ((self._move_end * self.energy_scale + self._mono_runupdown), self._mono_energy_speed)) self._mono_energy.asynchronousMoveTo( (self._move_end * self.energy_scale + self._mono_runupdown)) else: if self.verbose: self.logger.info( 'startMove Mono Energy: _mono_energy.asynchronousMoveTo(%r) @ %r (-ve)' % ((self._move_end * self.energy_scale - self._mono_runupdown), self._mono_energy_speed)) self._mono_energy.asynchronousMoveTo( (self._move_end * self.energy_scale - self._mono_runupdown)) if self.isIDMoveEnabled(): sleep(self.getIDStartDelayTime()) if self.isIDGapMoveDirectionPositive(): if self.verbose: self.logger.info( 'startMove ID Gap: _id_gap.asynchronousMoveTo(%r) @ %r (+ve)' % ((self._id_gap_end + ID_GAP_END_OFFSET + self._id_gap_runupdown), self._id_gap_speed)) self._id_gap.asynchronousMoveTo( (self._id_gap_end + ID_GAP_END_OFFSET + self._id_gap_runupdown)) else: if self.verbose: self.logger.info( 'startMove ID Gap: _id_gap.asynchronousMoveTo(%r) @ %r (-ve)' % ((self._id_gap_end - ID_GAP_END_OFFSET - self._id_gap_runupdown), self._id_gap_speed)) self._id_gap.asynchronousMoveTo( (self._id_gap_end - ID_GAP_END_OFFSET - self._id_gap_runupdown)) self.continuousMovingStarted = True if self.verbose: self.logger.info('...startMove') def isMoving(self): if self.isIDMoveEnabled(): if self.verbose and (datetime.now() - self._movelog_time) > timedelta(seconds=1): self.logger.info( 'isMoving() _mono_energy=%r @ %r, _id_gap=%r @ %r' % (self._mono_energy.isBusy(), self._mono_energy(), self._id_gap.isBusy(), self._id_gap())) self._movelog_time = datetime.now() return self._mono_energy.isBusy() or self._id_gap.isBusy() else: if self.verbose and (datetime.now() - self._movelog_time) > timedelta(seconds=1): self.logger.info( 'isMoving() _mono_energy=%r @ %r' % (self._mono_energy.isBusy(), self._mono_energy())) self._movelog_time = datetime.now() return self._mono_energy.isBusy() def waitWhileMoving(self): if self.verbose: self.logger.info('waitWhileMoving()...') while self.isMoving(): time.sleep(1) if self.verbose: self.logger.info('...waitWhileMoving()') def stopAndReset(self): if self.verbose: self.logger.info('stopAndReset()') self._start_time = None self._start_event.clear() if self.isMonoMoveEnabled(): self._mono_energy.stop() if self.isIDMoveEnabled() and installation.isDummy(): self._id_gap.stop() self._restore_orig_speed() # Implement: public interface HardwareTriggerProvider extends Device def setTriggerPeriod(self, seconds): # double self._triggerPeriod = seconds if self.verbose: self.logger.info('setTriggerPeriod(%r)' % seconds) def getNumberTriggers(self): triggers = self.getTotalMove() / abs(self._move_step) if self.verbose: self.logger.info('getNumberTriggers()=%r (%r)' % (int(triggers), triggers)) return int(triggers) def getTotalTime(self): total_time = self.getNumberTriggers() * self._triggerPeriod if self.verbose: self.logger.info('getTotalTime()=%r' % total_time) return total_time def getTimeToVelocity(self): return self._mono_runupdown_time # Override: public abstract class DeviceBase implements Device, ConditionallyConfigurable, Localizable # None needed # Other functions def getTotalMove(self): total_move = abs(self._move_end - self._move_start) if self.verbose: self.logger.info('getTotalMove()=%r' % total_move) return total_move def isEnergyMoveDirectionPositive(self): return (self._move_end - self._move_start) > 0 def isIDGapMoveDirectionPositive(self): return (self._id_gap_end - self._id_gap_start) > 0 class DelayableCallable(Callable): def __init__(self, controller, demand_position): #self.start_event = threading.Event() self.start_event = controller._start_event self._controller, self._demand_position = controller, demand_position self.logger = LoggerFactory.getLogger( "ContinuousPgmEnergyIDGapMoveController:%s:DelayableCallable[%r]" % (controller.name, demand_position)) if self._controller.verbose: self.logger.info('__init__(%r, %r)...' % (controller.name, demand_position)) def call(self): if self._controller.verbose: self.logger.info('call...') # Wait for controller to start all motors moving and set start time if self._controller._start_time: if self._controller.verbose: self.logger.info('start_time=%r' % (self._controller._start_time)) else: if self._controller.verbose: self.logger.info('wait()...') timeout = 60 self.start_event.wait(timeout) if not self.start_event.isSet(): raise RuntimeError( "%rs timeout waiting for startMove() to be called on %s at position %r." % (timeout, self._controller.name, self._demand_position)) if self._controller.verbose: self.logger.info('...wait()') # Wait for delay before actually move start and then a time given by # how far through the scan this point is complete = abs( (self._demand_position - self._controller._move_start) / (self._controller._move_end - self._controller._move_start)) sleeptime_s = (self._controller._mono_runupdown_time + (complete * self._controller.getTotalTime())) delta = datetime.now() - self._controller._start_time delta_s = delta.seconds + delta.microseconds / 1000000. if delta_s > sleeptime_s: self.logger.warn( 'Sleep time already past!!! sleeptime_s=%r, delta_s=%r, sleeptime_s-delta_s=%r' % (sleeptime_s, delta_s, sleeptime_s - delta_s)) else: if self._controller.verbose: self.logger.info( 'sleeping... sleeptime_s=%r, delta_s=%r, sleeptime_s-delta_s=%r' % (sleeptime_s, delta_s, sleeptime_s - delta_s)) time.sleep(sleeptime_s - delta_s) energy = self._controller._mono_energy() if self._controller.verbose: self.logger.info('...DelayableCallable:call returning %r, %r' % (self._demand_position, energy)) return self._demand_position, energy def getPositionCallableExtraNames(self): return ['readback'] def getPositionCallableFormat(self): return ['%f', '%f'] # public Callable<T> getPositionCallable() throws DeviceException; def getPositionCallableFor(self, position): if self.verbose: self.logger.info('getPositionCallableFor(%r)...' % position) return self.DelayableCallable(self, position) def _restore_orig_speed(self): if self.isMonoMoveEnabled() and self._mono_energy_speed_orig: if self.verbose: self.logger.info( 'Restoring original PGM Energy speed %r, was %r' % (self._mono_energy_speed_orig, self._mono_energy.speed)) self._mono_energy.speed = self._mono_energy_speed_orig self._mono_energy_speed_orig = None if self.isIDMoveEnabled() and self._id_gap_speed_orig: if installation.isLive(): if self.verbose: self.logger.info( 'Restoring original ID gap speed %r, was %r' % (self._id_gap_speed_orig, self.idpvs['vel'].caget())) self.idpvs['vel'].caput(self._id_gap_speed_orig) else: if self.verbose: self.logger.info( 'Restoring original ID gap speed %r, was %r' % (self._id_gap_speed_orig, self._id_gap.speed)) self._id_gap.speed = self._id_gap_speed_orig self._id_gap_speed_orig = None def atScanEnd(self): if self.verbose: self.logger.info('atScanEnd()...') self._restore_orig_speed() # Should we also move the pgm_energy to a known value too, such as the midpoint? def setIDStartDelayTime(self, t): self.idstartdelaytime = t def getIDStartDelayTime(self): return self.idstartdelaytime def setIDSpeedFactor(self, val): self.idspeedfactor = val def getIDSpeedFactor(self): return self.idspeedfactor def setMonoSpeedFactor(self, val): self.monospeedfactor = val def getMonoSpeedFactor(self): return self.monospeedfactor #https://jira.diamond.ac.uk/browse/I10-301 def enableIDMove(self): self._move_id = True def disableIDMove(self): self._move_id = False def isIDMoveEnabled(self): return self._move_id def enableMonoMove(self): self._move_mono = True def disableMonoMove(self): self._move_mono = False def isMonoMoveEnabled(self): return self._move_mono
class EpicsFirewireCamera(PseudoDetector): #TODO: Known bug: only works when epics zoom turned off (read the zoom value) def __init__(self, name, pvroot, filepath=None, determine_data_pv_based_on_zoom=False, numtracker_extension='tmp', filename_template='fire%d_%05d.png'): # BL07I-DI-PHDGN-06:CAM:DATA self.determine_data_pv_based_on_zoom = determine_data_pv_based_on_zoom self.name = name self.extraNames = [] self.outputFormat = [] self.level = 9 self.pvs = PvManager(['SET_SHUTTR', 'WIDTH', 'HEIGHT', 'ZOOM'], pvroot) self.pv_data = [] self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA1')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA2')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA3')) self.pv_data.append( gda.epics.LazyPVFactory.newReadOnlyIntegerArrayPV(pvroot + 'DATA4')) self.pvs.configure() self.filepath = filepath self.ds = None self.last_image_path = None self.last_filename = None self.last_image_number = 0 self.numtracker_extension = numtracker_extension self.filename_template = filename_template def createsOwnFiles(self): return False def setCollectionTime(self, t): self.pvs['SET_SHUTTR'].caput(int(t)) def getCollectionTime(self): return float(self.pvs['SET_SHUTTR'].caget()) def prepareForCollection(self): self.last_image_number = -1 def collectData(self): #rawdata = self.pvs['DATA'].cagetArray() if self.determine_data_pv_based_on_zoom: zoom_ordinal = int(float(self.pvs['ZOOM'].caget())) rawdata = self.pv_data[zoom_ordinal].get() else: rawdata = self.pv_data[0].get() data = map(unsign2, rawdata) self.ds = DatasetFactory.zeros(int(float(self.pvs['HEIGHT'].caget())), int(float(self.pvs['WIDTH'].caget())), data) self.last_image_number += 1 self.last_filename = self._generateCurrentFilename() if self.filepath is not None: self.saveImage(self.last_filename) def _getCurrentScanNumer(self): return NumTracker(self.numtracker_extension).getCurrentFileNumber() def _generateCurrentFilename(self): if isScanRunning(): return self.filename_template % (self._getCurrentScanNumer(), self.last_image_number) else: return time.strftime("%Y%m%d%H%M%S", time.localtime()) + '%03d' % ( (time.time() % 1) * 1000) + '.png' def getStatus(self): return IDLE def readout(self): if self.ds is None: raise Exception( "Epics Firewire Camera %s has not acquired an image" % self.name) else: return self.ds def setFilepath(self, filepath): self.filepath = filepath def getFilepath(self): return self.filepath def getLastImagePath(self): return self.last_image_path def saveImage(self, filename): path = self.filepath + filename if self.ds is None: raise Exception( "Epics Firewire Camera %s has not acquired an image" % self.name) else: sfh = ScanFileHolder() sfh.addDataSet("Image", self.ds) sfh.save(PNGSaver(path)) self.last_image_path = path