Project

General

Profile

Feature #2019 » acquisition.py

acquisition.py for 2.1 - Anchi Cheng, 10/17/2012 05:14 PM

 
#
# COPYRIGHT:
# The Leginon software is Copyright 2003
# The Scripps Research Institute, La Jolla, CA
# For terms of the license agreement
# see http://ami.scripps.edu/software/leginon-license
#
'''
Acquisition node is a TargetWatcher, so it receives either an ImageTargetData
or an ImageTargetListData. The method processTargetData is called on each
ImageTargetData.
'''
import targetwatcher
import time
import leginondata
import event
import calibrationclient
import presets
import copy
import threading
import node
import instrument
import gui.wx.Acquisition
import gui.wx.Presets
import navigator
import numpy
import math
from pyami import arraystats, imagefun, ordereddict
import smtplib
import emailnotification
import leginonconfig
import gridlabeler

debug = False

class NoMoveCalibration(targetwatcher.PauseRepeatException):
pass

class InvalidPresetsSequence(targetwatcher.PauseRepeatException):
pass

class BadImageStatsPause(targetwatcher.PauseRepeatException):
pass

class BadImageStatsAbort(Exception):
pass

class InvalidStagePosition(Exception):
pass

def setImageFilename(imagedata):
if imagedata['filename'] is not None:
return
listlabel = ''
numberstr = '%05d' % (imagedata['target']['number'],)
if imagedata['target']['list'] is not None:
listlabel = imagedata['target']['list']['label']
if imagedata['preset'] is None:
presetstr = ''
else:
presetstr = imagedata['preset']['name']
mystr = numberstr + presetstr
rootname = getRootName(imagedata, listlabel)
parts = []
parts.append(rootname)
if listlabel:
parts.append(listlabel)
parts.append(mystr)
if imagedata['version']:
vstr = 'v%02d' % (imagedata['version'],)
parts.append(vstr)

filename = '_'.join(parts)
imagedata['filename'] = filename

def getRootName(imagedata, listlabel=False):
'''
get the root name of an image from its parent
'''
parent_target = imagedata['target']
usegridlabel = not listlabel
if parent_target is None:
## there is no parent target
## create my own root name
return newRootName(imagedata, usegridlabel)

parent_image = parent_target['image']
if parent_image is None:
## there is no parent image
return newRootName(imagedata, usegridlabel)

## use root name from parent image
parent_root = parent_image['filename']
if parent_root:
return parent_root
else:
return newRootName(imagedata, usegridlabel)

def newRootName(imagedata, usegridlabel):
parts = []
sessionstr = imagedata['session']['name']
parts.append(sessionstr)
if usegridlabel:
if 'grid' in imagedata and imagedata['grid'] is not None:
gridlabel = gridlabeler.getGridLabel(imagedata['grid'])
parts.append(gridlabel)
sep = '_'
name = sep.join(parts)
return name


class Acquisition(targetwatcher.TargetWatcher):
panelclass = gui.wx.Acquisition.Panel
settingsclass = leginondata.AcquisitionSettingsData
# maybe not a class attribute
defaultsettings = {
'pause time': 2.5,
'move type': 'image shift',
'preset order': [],
'correct image': True,
'display image': True,
'save image': True,
'wait for process': False,
'wait for rejects': False,
'wait for reference': False,
#'duplicate targets': False,
#'duplicate target type': 'focus',
'iterations': 1,
'wait time': 0,
'adjust for transform': 'no',
'drift between': False,
'mover': 'presets manager',
'move precision': 0.0,
'accept precision': 1e-3,
'process target type': 'acquisition',
'save integer': False,
'background': False,
'use parent tilt': False,
'adjust time by tilt': False,
'reset tilt': False,
'high mean': 2**16,
'low mean': 50,
'bad stats response': 'Continue',
'emission off': False,
'target offset row': 0,
'target offset col': 0,
'correct image shift coma': False,
}
eventinputs = targetwatcher.TargetWatcher.eventinputs \
+ [event.DriftMonitorResultEvent,
event.MakeTargetListEvent,
event.ImageProcessDoneEvent,
event.AcquisitionImagePublishEvent] \
+ presets.PresetsClient.eventinputs \
+ navigator.NavigatorClient.eventinputs
eventoutputs = targetwatcher.TargetWatcher.eventoutputs \
+ [event.LockEvent,
event.UnlockEvent,
event.AcquisitionImagePublishEvent,
event.ChangePresetEvent, event.PresetLockEvent, event.PresetUnlockEvent,
event.DriftMonitorRequestEvent,
event.FixBeamEvent,
event.FixAlignmentEvent,
event.FixConditionEvent,
event.ImageListPublishEvent, event.ReferenceTargetPublishEvent] \
+ navigator.NavigatorClient.eventoutputs

def __init__(self, id, session, managerlocation, **kwargs):

targetwatcher.TargetWatcher.__init__(self, id, session, managerlocation, **kwargs)

self.addEventInput(event.AcquisitionImagePublishEvent, self.handleDriftImage)
self.addEventInput(event.DriftMonitorResultEvent, self.handleDriftResult)
self.addEventInput(event.ImageProcessDoneEvent, self.handleImageProcessDone)
self.addEventInput(event.MakeTargetListEvent, self.setGrid)
self.driftdone = threading.Event()
self.driftimagedone = threading.Event()
self.instrument = instrument.Proxy(self.objectservice, self.session)

self.calclients = ordereddict.OrderedDict()
self.calclients['image shift'] = calibrationclient.ImageShiftCalibrationClient(self)
self.calclients['stage position'] = calibrationclient.StageCalibrationClient(self)
self.calclients['modeled stage position'] = calibrationclient.ModeledStageCalibrationClient(self)
self.calclients['image beam shift'] = calibrationclient.ImageBeamShiftCalibrationClient(self)
self.calclients['beam shift'] = calibrationclient.BeamShiftCalibrationClient(self)
self.calclients['beam tilt'] = calibrationclient.BeamTiltCalibrationClient(self)

self.presetsclient = presets.PresetsClient(self)
self.navclient = navigator.NavigatorClient(self)
self.doneevents = {}
self.onTarget = False
self.imagelistdata = None
self.simloopstop = threading.Event()
self.received_image_drift = threading.Event()
self.requested_drift = None
self.grid = None

self.duplicatetypes = ['acquisition', 'focus']
self.presetlocktypes = ['acquisition', 'target', 'target list']

if debug:
self.timedebug = {}

self.start()

def checkSettings(self, settings):
problems = []
return problems
presetnames = settings['preset order']
for presetname in presetnames:
try:
self.presetsclient.getPresetFromDB(presetname)
except:
problems.append(('error','Preset "%s" does not exist. Create it, or import from a previous session' % (presetname,)))
continue
for type in ('dark', 'norm'):
for channel in (0,1):
exists = self.presetsclient.correctorImageExists(presetname, type, channel)
if not exists:
problems.append(('error','Preset "%s" Missing %s CCD image, channel %s' % (presetname, type, channel)))

return problems

def onPresetPublished(self, evt):
evt = gui.wx.Presets.NewPresetEvent()
self.panel.GetEventHandler().AddPendingEvent(evt)

def handleDriftResult(self, ev):
driftresult = ev['data']
status = driftresult['status']
final = driftresult['final']
drift = numpy.hypot(final['rowmeters'],final['colmeters']) / final['interval']
self.reportStatus('acquisition', 'Received drift result status "%s", final drift: %.3e' % (status, drift))
self.driftresult = driftresult
self.driftdone.set()

def handleDriftImage(self, ev):
driftimage = ev['data']
self.reportStatus('acquisition', 'Received drift image')
self.driftimage = driftimage
self.driftimagedone.set()

def handleImageProcessDone(self, ev):
imageid = ev['imageid']
status = ev['status']
if imageid in self.doneevents:
self.doneevents[imageid]['status'] = status
self.doneevents[imageid]['received'].set()

def processData(self, newdata):
if isinstance(newdata, leginondata.QueueData):
self.processTargetListQueue(newdata)
return
self.logger.info('Acquisition.processData')
self.imagelistdata = leginondata.ImageListData(session=self.session,
targets=newdata)
self.publish(self.imagelistdata, database=True)
targetwatcher.TargetWatcher.processData(self, newdata)
self.publish(self.imagelistdata, pubevent=True)
self.presetsclient.unlock()
self.logger.info('Acquisition.processData done')

def validateStagePosition(self, stageposition):
## check for out of stage range target
stagelimits = {
'x': (-9.9e-4, 9.9e-4),
'y': (-9.9e-4, 9.9e-4),
}
for axis, limits in stagelimits.items():
if stageposition[axis] < limits[0] or stageposition[axis] > limits[1]:
pstr = '%s: %g' % (axis, stageposition[axis])
messagestr = 'Aborting target: stage position %s out of range' % pstr
self.logger.info(messagestr)
raise InvalidStagePosition(messagestr)

def validatePresets(self):
presetorder = self.settings['preset order']
if not presetorder:
raise InvalidPresetsSequence('no presets configured')
availablepresets = self.getPresetNames()
for presetname in presetorder:
if presetname not in availablepresets:
raise InvalidPresetsSequence('bad preset %s in presets order' % (presetname,))

def makeTransformTarget(self, target, offset):
newtarget = leginondata.AcquisitionImageTargetData(initializer=target)
# Fix here about version
newtarget['delta row'] = target['delta row'] + offset['y']
newtarget['delta column'] = target['delta column'] + offset['x']
newtarget['fromtarget'] = target
newtarget.insert(force=True)
self.logger.info('target adjusted by (%.1f,%.1f) (column, row)' % (offset['x'],offset['y']))
return newtarget
def adjustTargetForTransform(self, targetdata):
## look up most recent version of this target
targetlist = targetdata['list']
targetnumber = targetdata['number']
newtargetdata = self.researchTargets(session=self.session, number=targetnumber, list=targetlist)
newtargetdata = newtargetdata[0]
## look up all transforms declared for this session
decq = leginondata.TransformDeclaredData(session=self.session)
transformsdeclared = decq.query()

## if no transforms declared, return recent target
if not transformsdeclared:
return newtargetdata

## if recent target after transforms declared, return recent target
if newtargetdata['image'] is None:
# simulated target
newtargettime = newtargetdata.timestamp
else:
# need to compare when the parent image is acquired not when target is
# selected
try:
# newtargetdata['image'] does not always has timestamp attribute.
# Do direct query
parentid = newtargetdata['image'].dbid
q = leginondata.AcquisitionImageData
parentdata = q.direct_query(parentid, readimages=False)
newtargettime = parentdata.timestamp
except:
self.logger.warning("Could not find parent image (ID: %d) timestamp,use targetinstead" % parentdata.dbid)
newtargettime = newtargetdata.timestamp
declaredtime = transformsdeclared[0].timestamp
if newtargettime > declaredtime:
return newtargetdata

## if transform declared after most recent target, need new transformed target
newtargetdata = self.requestTransformTarget(newtargetdata)
## make sure we move to new target
self.onTarget = False
return newtargetdata

def processTargetData(self, targetdata, attempt=None):
'''
This is called by TargetWatcher.processData when targets available
If called with targetdata=None, this simulates what occurs at
a target (going to presets, acquiring images, etc.)
'''

try:
self.validatePresets()
except InvalidPresetsSequence, e:
if targetdata is None or targetdata['type'] == 'simulated':
## don't want to repeat in this case
self.logger.error(str(e))
return 'aborted'
else:
raise

presetnames = self.settings['preset order']
ret = 'ok'
self.onTarget = False
for newpresetname in presetnames:
if self.alreadyAcquired(targetdata, newpresetname):
continue

if targetdata is not None and targetdata['type'] != 'simulated' and self.settings['adjust for transform'] != 'no':
if self.settings['drift between'] and self.goodnumber > 0:
self.declareDrift('between targets')
targetonimage = targetdata['delta column'],targetdata['delta row']
targetdata = self.adjustTargetForTransform(targetdata)
self.logger.info('target adjusted by (%.1f,%.1f) (column, row)' % (targetdata['delta column']-targetonimage[0],targetdata['delta row']-targetonimage[1]))
offset = {'x':self.settings['target offset col'],'y':self.settings['target offset row']}
if offset['x'] or offset['y']:
targetdata = self.makeTransformTarget(targetdata,offset)
### determine how to move to target
try:
emtarget = self.targetToEMTargetData(targetdata)
except InvalidStagePosition:
return 'invalid'

presetdata = self.presetsclient.getPresetByName(newpresetname)

### acquire film or CCD
self.startTimer('acquire')
ret = self.acquire(presetdata, emtarget, attempt=attempt, target=targetdata)
self.stopTimer('acquire')
# in these cases, return immediately
if ret in ('aborted', 'repeat'):
self.reportStatus('acquisition', 'Acquisition state is "%s"' % ret)
break
if ret == 'repeat':
return repeat

self.reportStatus('processing', 'Processing complete')

return ret

def alreadyAcquired(self, targetdata, presetname):
'''
determines if image already acquired using targetdata and presetname
'''
## if image exists with targetdata and presetdata, no acquire
## we expect target to be exact, however, presetdata may have
## changed so we only query on preset name

# seems to have trouple with using original targetdata as
# a query, so use a copy with only some of the fields
presetquery = leginondata.PresetData(name=presetname)
targetquery = leginondata.AcquisitionImageTargetData(initializer=targetdata)
## don't care if drift correction was done on target after image was
## acquired, so ignore version, delta row/col, parentimage, and fromtarget
targetquery['version'] = None
targetquery['delta row'] = None
targetquery['delta column'] = None
targetquery['image'] = None
targetquery['fromtarget'] = None
imagequery = leginondata.AcquisitionImageData(target=targetquery, preset=presetquery)
## other things to fill in
imagequery['scope'] = leginondata.ScopeEMData()
imagequery['camera'] = leginondata.CameraEMData()
imagequery['session'] = leginondata.SessionData()
datalist = self.research(datainstance=imagequery)
if datalist:
## no need to acquire again, but need to republish
self.reportStatus('output', 'Image was acquired previously, republishing')
imagedata = datalist[0]
self.publishDisplayWait(imagedata)
return True
else:
return False

def targetToEMTargetData(self, targetdata):
'''
convert an ImageTargetData to an EMTargetData object
using chosen move type.
The result is a valid scope state that will center
the target on the camera, but not necessarily at the
desired preset. It is shifted from the preset of the
original targetdata.

Certain fields are reset to None becuase they are not
necessary, and cause problems if used between different
magnification modes (LM, M, SA).
'''
emtargetdata = leginondata.EMTargetData()
if targetdata is not None:
# get relevant info from target data
targetdeltarow = targetdata['delta row']
targetdeltacolumn = targetdata['delta column']
origscope = targetdata['scope']
targetscope = leginondata.ScopeEMData(initializer=origscope)
## copy these because they are dictionaries that could
## otherwise be shared (although transform() should be
## smart enough to create copies as well)
targetscope['stage position'] = dict(origscope['stage position'])
targetscope['image shift'] = dict(origscope['image shift'])
targetscope['beam shift'] = dict(origscope['beam shift'])

movetype = self.settings['move type']
oldpreset = targetdata['preset']

zdiff = 0.0
### simulated target does not require transform
if targetdata['type'] == 'simulated':
newscope = origscope
else:
targetcamera = targetdata['camera']
## to shift targeted point to center...
deltarow = -targetdeltarow
deltacol = -targetdeltacolumn
pixelshift = {'row':deltarow, 'col':deltacol}
## figure out scope state that gets to the target
calclient = self.calclients[movetype]
try:
newscope = calclient.transform(pixelshift, targetscope, targetcamera)
except calibrationclient.NoMatrixCalibrationError, e:
raise NoMoveCalibration(e)

## if stage is tilted and moving by image shift,
## calculate z offset between center of image and target
if movetype in ('image shift','image beam shift','beam shift') and abs(targetscope['stage position']['a']) > 0.02:
calclient = self.calclients['stage position']
try:
tmpscope = calclient.transform(pixelshift, targetscope, targetcamera)
except calibrationclient.NoMatrixCalibrationError,e:
raise NoMoveCalibration(e)
ydiff = tmpscope['stage position']['y'] - targetscope['stage position']['y']
zdiff = ydiff * numpy.sin(targetscope['stage position']['a'])
### check if stage position is valid
if newscope['stage position']:
self.validateStagePosition(newscope['stage position'])

emtargetdata['preset'] = oldpreset
emtargetdata['movetype'] = movetype
emtargetdata['image shift'] = dict(newscope['image shift'])
emtargetdata['beam shift'] = dict(newscope['beam shift'])
emtargetdata['stage position'] = dict(newscope['stage position'])
emtargetdata['delta z'] = zdiff

emtargetdata['target'] = targetdata

## publish in DB because it will likely be needed later
## when returning to the same target,
## even after it is removed from memory
self.publish(emtargetdata, database=True)
return emtargetdata

def lastFilmAcquisition(self):
filmdata = self.research(datainstance=leginondata.FilmData(), results=1)
if filmdata:
filmdata = filmdata[0]
else:
filmdata = None
return filmdata

def setImageFilename(self, imagedata):
setImageFilename(imagedata)

def acquireFilm(self, presetdata, emtarget=None):
self.logger.info('acquiring film')
## get current film parameters
stock = self.instrument.tem.FilmStock
if stock < 1:
self.logger.error('Film stock = %s. Film exposure failed' % (stock,))
return 'no stock'
## create FilmData(AcquisitionImageData) which
## will be used to store info about this exposure
targetdata = emtarget['target']
filmdata = leginondata.FilmData(session=self.session, preset=presetdata, label=self.name, target=targetdata, emtarget=emtarget)

## first three of user name
self.instrument.tem.FilmUserCode = self.session['user']['username'][:3]
## use next dbid for film text
last_film = self.lastFilmAcquisition()
if last_film is None:
last_dbid = 0
else:
last_dbid = last_film.dbid
next_dbid = last_dbid + 1
existing_filmtext = self.instrument.tem.FilmText
lines = existing_filmtext.split('\n')
leg_label = 'Leginon ID: %s' % (next_dbid,)
if lines[-1].startswith('Leginon') or len(lines) >= 4:
lines[-1] = leg_label
else:
lines.append(leg_label)
new_filmtext = '\n'.join(lines)
self.instrument.tem.FilmText = new_filmtext
self.instrument.tem.FilmDateType = 'YY.MM.DD'

## get scope for database
scopebefore = self.instrument.getData(leginondata.ScopeEMData)
camerabefore = self.instrument.getData(leginondata.CameraEMData)
filmdata['camera'] = camerabefore
filmdata['scope'] = scopebefore
## insert film
self.instrument.tem.preFilmExposure(True)
# wait for shaking to stop
pause_time = self.instrument.tem.ExpWaitTime
self.logger.info('pausing for %s seconds' % (pause_time,))
time.sleep(pause_time)
# expose film
self.instrument.ccdcamera.getImage()
## take out film
self.instrument.tem.postFilmExposure(True)
filmleft = self.instrument.tem.FilmStock
self.logger.info('Films left: %d' % (filmleft,))
if filmleft < 1:
self.logger.error('No more film left, please change film box and click continue')
self.player.pause()
self.declareDrift('film changed')
return filmdata

def exposeSpecimen(self, seconds):
## I want to expose the specimen, but not the camera.
## I would rather use some kind of manual shutter where above specimen
## shutter opens and below specimen shutter remains closed.
## Using the screen down was easier and serves the same purpose, but
## with more error on the actual time exposed.
self.logger.info('Screen down for %ss to expose specimen...' % (seconds,))
self.instrument.tem.MainScreenPosition = 'down'
time.sleep(seconds)
self.instrument.tem.MainScreenPosition = 'up'
self.logger.info('Screen up.')

def getImageShiftOffset(self):
pimageshift = self.presetsclient.currentpreset['image shift']
simageshift = self.instrument.tem.ImageShift
offsetx = simageshift['x'] - pimageshift['x']
offsety = simageshift['y'] - pimageshift['y']
return {'x': offsetx, 'y': offsety}

def setImageShiftOffset(self, imageshift):
simageshift = self.instrument.tem.ImageShift
x = simageshift['x'] + imageshift['x']
y = simageshift['y'] + imageshift['y']
self.instrument.tem.ImageShift = {'x':x, 'y':y}

def moveAndPreset(self, presetdata, emtarget):
status = 'ok'
presetname = presetdata['name']
targetdata = emtarget['target']
#### move and change preset
movetype = self.settings['move type']
movefunction = self.settings['mover']
keep_shift = False
if movetype == 'image shift' and movefunction == 'navigator':
self.logger.warning('Navigator cannot be used for image shift, using Presets Manager instead')
movefunction = 'presets manager'
self.setStatus('waiting')
if movefunction == 'navigator':
emtarget = None
if not self.onTarget and targetdata['type'] != 'simulated':
precision = self.settings['move precision']
accept_precision = self.settings['accept precision']
final_imageshift = self.settings['final image shift']
if final_imageshift:
keep_shift = True
status = self.navclient.moveToTarget(targetdata, movetype, precision, accept_precision, final_imageshift=final_imageshift)
if status == 'error':
return status
self.presetsclient.toScope(presetname, emtarget, keep_shift=keep_shift)
stageposition = self.instrument.tem.getStagePosition()
stagea = stageposition['a']
if self.settings['correct image shift coma']:
## beam tilt correction induced by image shift
beamtiltclient = self.calclients['beam tilt']
tem = self.instrument.getTEMData()
cam = self.instrument.getCCDCameraData()
ht = self.instrument.tem.HighTension
mag = self.instrument.tem.Magnification
imageshift = self.instrument.tem.getImageShift()
self.beamtilt0 = self.instrument.tem.getBeamTilt()
try:
beamtilt = beamtiltclient.transformImageShiftToBeamTilt(imageshift, tem, cam, ht, self.beamtilt0, mag)
self.instrument.tem.BeamTilt = beamtilt
except Exception, e:
raise NoMoveCalibration(e)
if self.settings['adjust time by tilt'] and abs(stagea) > 10 * 3.14159 / 180:
camdata = leginondata.CameraEMData()
camdata.friendly_update(presetdata)
old_time = camdata['exposure time']
new_time = old_time / math.cos(stagea)
if new_time > 5000 or new_time <= 1:
self.logger.warning('Ignore unreasonable exposure time at %d ms' % new_time)
new_time = old_time
camdata['exposure time'] = new_time
self.logger.info('scale exposure time from %d to %d by cos(%d)' % (old_time,new_time,int(stagea*180/3.14159)))
self.instrument.setData(camdata)
self.onTarget = True
self.setStatus('processing')
return status

def acquireCCD(self, presetdata, emtarget=None,channel=None):
targetdata = emtarget['target']
## set correction channel
## in the future, may want this based on preset or something
if channel is None:
channel = 0
else:
channel = channel
## acquire image
self.reportStatus('acquisition', 'acquiring image...')
self.startTimer('acquire getData')
correctimage = self.settings['correct image']
if correctimage:
imagedata = self.acquireCorrectedCameraImageData(channel=channel)
else:
imagedata = self.acquireCameraImageData()
self.reportStatus('acquisition', 'image acquired')
self.stopTimer('acquire getData')
if imagedata is None:
return 'fail'

if self.settings['bad stats response'] != 'Continue':
self.evaluateStats(imagedata['image'])

## convert float to uint16
if self.settings['save integer']:
imagedata['image'] = numpy.clip(imagedata['image'], 0, 2**16-1)
imagedata['image'] = numpy.asarray(imagedata['image'], numpy.uint16)

## convert CameraImageData to AcquisitionImageData
dim = imagedata['camera']['dimension']
pixels = dim['x'] * dim['y']
pixeltype = str(imagedata['image'].dtype)
imagedata = leginondata.AcquisitionImageData(initializer=imagedata, preset=presetdata, label=self.name, target=targetdata, list=self.imagelistdata, emtarget=emtarget, pixels=pixels, pixeltype=pixeltype)
imagedata['version'] = 0
## store EMData to DB to prevent referencing errors
self.publish(imagedata['scope'], database=True)
self.publish(imagedata['camera'], database=True)
return imagedata

def acquire(self, presetdata, emtarget=None, attempt=None, target=None, channel=None):
reduce_pause = self.onTarget

if debug:
try:
tnum = emtarget['target']['number']
tkey = emtarget.dbid
except:
tnum = None
tkey = None
print tnum, 'MOVEANDPRESETPAUSE START'
t0 = time.time()
self.timedebug[tkey] = t0
if 'consecutive' in self.timedebug:
print tnum, '************************************* CONSECUTIVE', t0 - self.timedebug['consecutive']
self.timedebug['consecutive'] = t0

status = self.moveAndPreset(presetdata, emtarget)
if status == 'error':
self.logger.warning('Move failed. skipping acquisition at this target')
return status

pausetime = self.settings['pause time']
if reduce_pause:
pausetime = min(pausetime, 2.5)

self.logger.info('pausing for %s s' % (pausetime,))

self.startTimer('pause')
time.sleep(pausetime)
self.stopTimer('pause')

if debug:
print tnum, 'MOVEANDPRESETPAUSE DONE', time.time() - t0

## pre-exposure
pretime = presetdata['pre exposure']
if pretime:
self.exposeSpecimen(pretime)
args = (presetdata, emtarget, channel)
if self.settings['background']:
self.clearCameraEvents()
t = threading.Thread(target=self.acquirePublishDisplayWait, args=args)
t.start()
self.waitExposureDone()
else:
self.acquirePublishDisplayWait(*args)
return status

def acquirePublishDisplayWait(self, presetdata, emtarget, channel):
if debug:
try:
tnum = emtarget['target']['number']
tkey = emtarget.dbid
except:
tnum = None
tkey = None
print tnum, 'APDW START'
t0 = time.time()

if presetdata['film']:
imagedata = self.acquireFilm(presetdata, emtarget)
else:
imagedata = self.acquireCCD(presetdata, emtarget, channel=channel)

self.imagedata = imagedata
if debug and self.settings['correct image shift coma']:
print "beam tilt for image acquired",self.instrument.tem.BeamTilt
self.instrument.tem.BeamTilt = self.beamtilt0
print "resetted beam tilt",self.instrument.tem.BeamTilt
targetdata = emtarget['target']
if targetdata is not None and 'grid' in targetdata and targetdata['grid'] is not None:
imagedata['grid'] = targetdata['grid']
else:
if self.grid:
imagedata['grid'] = self.grid
self.publishDisplayWait(imagedata)

if debug:
print tnum, 'APDW DONE', time.time() - t0
ttt = time.time() - self.timedebug[tkey]
del self.timedebug[tkey]
print tnum, '************* TOTAL ***', ttt

def publishDisplayWait(self, imagedata):
'''
publish image data, display it, then wait for something to
process it
'''
## set the 'filename' value
self.setImageFilename(imagedata)

## set pixel size so mrc file will have it in header
if imagedata.__class__ is leginondata.AcquisitionImageData:
imagedata.attachPixelSize()

self.reportStatus('output', 'Publishing image...')
self.startTimer('publish image')
if self.settings['save image']:
imagedata.insert(force=True)
self.publish(imagedata, pubevent=True)

## set up to handle done events
dataid = imagedata.dbid
doneevent = {}
doneevent['received'] = threading.Event()
doneevent['status'] = 'waiting'
self.doneevents[dataid] = doneevent

self.stopTimer('publish image')
self.reportStatus('output', 'Image published')
self.reportStatus('output', 'Publishing stats...')
self.startTimer('publish stats')
self.publishStats(imagedata)
self.stopTimer('publish stats')
self.reportStatus('output', 'Stats published...')

image_array = imagedata['image']
if self.settings['display image'] and isinstance(image_array, numpy.ndarray):
self.reportStatus('output', 'Displaying image...')
self.startTimer('display')
self.setImage(numpy.asarray(image_array, numpy.float32), 'Image')
self.stopTimer('display')
self.reportStatus('output', 'Image displayed')

if self.settings['wait for process']:
self.setStatus('waiting')
self.startTimer('waitForImageProcess')
self.waitForImageProcessDone()
self.stopTimer('waitForImageProcess')
if not self.settings['background']:
self.setStatus('processing')
else:
self.setStatus('idle')
return 'ok'

def publishStats(self, imagedata):
im = imagedata['image']
if im is None:
return
allstats = arraystats.all(im)
statsdata = leginondata.AcquisitionImageStatsData()
statsdata['session'] = self.session
statsdata['min'] = allstats['min']
statsdata['max'] = allstats['max']
statsdata['mean'] = allstats['mean']
statsdata['stdev'] = allstats['std']
statsdata['image'] = imagedata
self.publish(statsdata, database=True)

def setEmailPassword(self, password):
self.emailpassword = password

def emailBadImageStats(self, stats):
s = smtplib.SMTP()
s.connect(leginonconfig.emailhost)
s.login(leginonconfig.emailuser, self.emailpassword)

subject = 'LEGINON: bad image stats'
responsetext = self.settings['bad stats response'].replace('Abort','aborted')
responsetext = responsetext.replace('Pause','paused at current')
text = 'Your Leginon session has '+ responsetext + ' target list(s) at \n\n'+time.ctime() + '\n\n due to bad image mean value of %.2f' %stats
mes = emailnotification.makeMessage(leginonconfig.emailfrom, leginonconfig.emailto, subject, text)
s.sendmail(leginonconfig.emailfrom, leginonconfig.emailto, mes.as_string())

def respondBadImageStats(self, badstate=''):
if self.settings['bad stats response'] == 'Abort all':
self.player.stopqueue()
self.logger.info('Stopping all targets in queue')
raise BadImageStatsAbort('image mean too '+badstate)
elif self.settings['bad stats response'] == 'Abort one':
self.player.stop()
self.logger.info('Skiping targets in this target list')
raise BadImageStatsAbort('image mean too '+badstate)
elif self.settings['bad stats response'] == 'Pause':
raise BadImageStatsPause('image mean too '+badstate)

def evaluateStats(self, imagearray):
mean = arraystats.mean(imagearray)
if mean > self.settings['high mean']:
try:
self.emailBadImageStats(mean)
except:
self.logger.info('could not email')
self.respondBadImageStats('high')
if mean < self.settings['low mean']:
try:
self.emailBadImageStats(mean)
except:
self.logger.info('could not email')
self.respondBadImageStats('low')

def publishImage(self, imdata):
self.publish(imdata, pubevent=True)

def waitForImageProcessDone(self):
imageids = self.doneevents.keys()
imageidstrs = map(str, imageids)
# wait for image processing nodes to complete
for id, eventinfo in self.doneevents.items():
self.reportStatus('processing', 'Waiting for %s to be processed' % (id,))
eventinfo['received'].wait()
idstr = str(id)
imageidstrs.remove(idstr)
self.reportStatus('processing', 'Done waiting for %s to be processed' % (id,))
self.doneevents.clear()
self.reportStatus('processing', 'Done waiting for images to be processed')

def stopWaitingForImage(self):
imageidstr = self.waitingforimages.getSelectedValue()
try:
imageid = eval(imageidstr)
except TypeError:
return
if imageid in self.doneevents:
self.doneevents[imageid]['received'].set()
self.doneevents[imageid]['status'] = 'forced'

def getPresetNames(self):
presetnames = self.presetsclient.getPresetNames()
return presetnames

def checkDrift(self, presetname, emtarget, threshold):
'''
request DriftManager to monitor drift
'''
driftdata = leginondata.DriftMonitorRequestData(session=self.session, presetname=presetname, emtarget=emtarget, threshold=threshold)
self.driftdone.clear()
self.driftimagedone.clear()
self.publish(driftdata, pubevent=True, database=True, dbforce=True)
self.reportStatus('acquisition', 'Waiting for DriftManager to check drift...')
self.driftimagedone.wait()
self.driftdone.wait()
return self.driftresult

def reportStatus(self, type, message):
self.logger.info('%s: %s' % (type, message))

def simulateTarget(self):
self.setStatus('processing')
currentpreset = self.presetsclient.getCurrentPreset()
if currentpreset is None:
try:
self.validatePresets()
except InvalidPresetsSequence:
self.logger.error('Configure at least one preset in the settings for this node.')
return
presetnames = self.settings['preset order']
currentpreset = self.presetsclient.getPresetByName(presetnames[0])
targetdata = self.newSimulatedTarget(preset=currentpreset,grid=self.grid)
self.publish(targetdata, database=True)
## change to 'processing' just like targetwatcher does
proctargetdata = self.reportTargetStatus(targetdata, 'processing')
try:
ret = self.processTargetData(targetdata=proctargetdata, attempt=1)
except BadImageStatsPause, e:
''' FIX ME!!! need to pause and allow repeat? '''
self.logger.error('processing target failed: %s' %e)
ret = 'aborted'
except BadImageStatsAbort, e:
self.logger.error('processing target failed: %s' %e)
ret = 'aborted'
self.reportTargetStatus(proctargetdata, 'done')
self.logger.info('Done with simulated target, status: %s (repeat will not be honored)' % (ret,))
self.setStatus('idle')

def simulateTargetLoop(self):
self.setStatus('processing')
iterations = self.settings['iterations']
self.logger.info('begin simulated target loop of %s iterations' % (iterations,))
self.simloopstop.clear()
for i in range(iterations):
self.logger.info('iteration %s of %s' % (i+1, iterations,))
self.simulateTarget()
self.setStatus('processing')
if self.simloopstop.isSet():
self.logger.info('User stopped loop')
break
waittime = self.settings['wait time']
self.setStatus('waiting')
time.sleep(waittime)
self.setStatus('processing')
self.logger.info('Simulated Target Loop Done')
self.setStatus('idle')

def simulateTargetLoopStop(self):
self.logger.info('Simulated Target Loop will stop after next iteration')
self.simloopstop.set()

def loadImage(self, imagedata):
try:
num = imagedata['image'].read()
except AttributeError:
pass
else:
imagedata.__setitem__('image', num, force=True)
self.setImage(numpy.asarray(imagedata['image'], numpy.float32), 'Image')

def processReferenceTarget(self):
refq = leginondata.ReferenceTargetData(session=self.session)
results = refq.query(results=1, readimages=False)
if not results:
return
# This really does not need data but beamfixer and other presetadjusters
# currently are subclass of targetwatcher and only watch for PublishEvent
# This can all be changed once alignment manager can handle different
# target types differently.
request_data = leginondata.FixBeamData()
request_data['session'] = self.session
try:
original_position = self.instrument.tem.getStagePosition()
self.publish(request_data, database=True, pubevent=True, wait=True)
self.instrument.tem.setStagePosition({'z':original_position['z']})
except node.ConfirmationNoBinding, e:
self.logger.debug(e)
except Exception, e:
self.logger.error(e)

def fixAlignment(self):
evt = event.FixAlignmentEvent()
try:
original_position = self.instrument.tem.getStagePosition()
status = self.outputEvent(evt, wait=True)
self.instrument.tem.setStagePosition({'z':original_position['z']})
except node.ConfirmationNoBinding, e:
self.logger.debug(e)
except Exception, e:
self.logger.error(e)

def fixCondition(self):
evt = event.FixConditionEvent()
try:
status = self.outputEvent(evt, wait=True)
except node.ConfirmationNoBinding, e:
self.logger.debug(e)
except Exception, e:
self.logger.error(e)

def getMoveTypes(self):
movetypes = []
for key, value in self.calclients.items():
if value.mover:
movetypes.append(key)
return movetypes
def setGrid(self,evt):
if evt['grid'] is None:
self.grid = None
self.logger.info('Remove filename grid prefix')
return
grid = evt['grid']
self.grid = grid
label = gridlabeler.getGridLabel(grid)
self.logger.info('Add grid prefix as '+label)
(4-4/7)