Files
SwissMX/psi_device.py

354 lines
15 KiB
Python

import logging,sys,os,socket
from math import ceil
_log=logging.getLogger(__name__)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG,format='%(name)s:%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
logging.getLogger('matplotlib').setLevel(logging.INFO)
if socket.gethostname()=='ganymede':
base=os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.abspath(os.path.join(base,'../PBSwissMX/python')))
sys.path.insert(0, os.path.abspath(os.path.join(base,'../../PBTools')))
else:
base=os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(base,'PBTools'))
sys.path.insert(0, os.path.join(base,'PBSwissMX/python'))
elif socket.gethostname()!='ganymede':
sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/slic/slic-package'))
sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/mx/swissmx_tools/jfj'))
from PyQt5.QtWidgets import (QApplication,)
from app_config import AppCfg #settings, option, toggle_option
import epics
from pbtools.misc.pp_comm import PPComm
from pbtools.misc.gather import Gather
import shapepath
try:
from slic.core.acquisition import SFAcquisition
from slic.core.acquisition.broker.tools import get_endstation
from slic.devices.timing.events import CTASequencer
from ctadaq import CTAAcquisition
from jfjoch_device import JFJ
except ImportError as e:
_log.warning(e)
class Shutter:
def __init__(self,mode=1):
self._mode=mode
app=QApplication.instance() #apologies! Wasn't sure how best to do this, could maybe feed sync_flag as a variable to open and close from swissmx.py
cfg=app._cfg
dt_misc = cfg.value(AppCfg.DT_MISC)
self.sync_flag=dt_misc['sync_flag']
def open(self):
mode=self._mode
if mode==0:
_log.info('open simulated shutter')
elif mode==1:
# open laser shutter
#epics.caput("SLAAR31-LPSYS-ESC:LHX1_SHUT_OPEN", 1) '''think this shutter is no longer used'''
if epics.caget("SLAAR31-LPSYS-ESCIP23:ONOFF.RVAL") == 1:
epics.caput("SLAAR31-LPSYS-ESCOP0:ONOFF", 1)
_log.info('laser shutter opened')
if self.sync_flag==0: #if using cta, sets pulse_picker output to follow pulser 0
# open fast shutter
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SOURCE", 0)
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SOURCE2", 0)
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SNUMPD", 0)
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SNUMPD2", 0)
epics.caput("SARES30-LTIM01-EVR0:RearUniv0-Ena-SP", 1)
_log.info('fast shutter opened')
def close(self):
mode=self._mode
if mode==0:
_log.info('close simulated shutter')
elif mode==1:
# close laser shutter
if epics.caget("SLAAR31-LPSYS-ESCIP23:ONOFF.RVAL") == 1:
epics.caput("SLAAR31-LPSYS-ESCOP2:ONOFF", 1)
_log.info('laser shutter closed')
#epics.caput("SLAAR31-LPSYS-ESC:LHX1_SHUT_CLOSE", 1) '''think this shutter is no longer used'''
# close fast shutter
epics.caput("SARES30-LTIM01-EVR0:RearUniv0-Ena-SP", 0)
if self.sync_flag==0: #if using cta, sets pulse_picker output back to high low, could do this every time?
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SOURCE", 3)
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SOURCE2", 4)
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SNUMPD", 4)
epics.caput("SARES30-LTIM01-EVR0:RearUniv0_SNUMPD2", 4)
_log.info('fast shutter closed')
class Deltatau:
def __init__(self,sim=False):
app=QApplication.instance()
cfg=app._cfg
host=cfg.value(AppCfg.DT_HOST)
# sim=False;host='localhost:10001:10002' # this only moves motors during acquisition
if sim:
self._comm=comm=None
self._gather=gather=None
else:
hpp=host.split(':')
param={'host':hpp[0]}
if len(hpp)>1:
param['port']=int(hpp[1])
if len(hpp)>2:
param['fast_gather_port']=int(hpp[2])
_log.info(' -> ssh-tunneling PPComm({host}:{port} {host}:{fast_gather_port})'.format(**param))
try:
self._comm=comm=PPComm(**param,timeout=2.0)
self._gather=gather=Gather(comm)
except (socket.timeout,socket.gaierror) as e:
_log.critical(f'can not connect to deltatau:"{host}" -> {e}')
self._shapepath=sp=shapepath.ShapePath(comm, gather, verbose=0xff, sync_mode=1, sync_flag=3)
class Jungfrau:
def __init__(self,sim=False):
# setup slic parameters
if sim:
self._sim=True
return
try:
self._pv_pulse_id=epics.PV('SAR-EXPMX-EVR0:RX-PULSEID')
self._pv_pulse_id.connect()
except NameError as e:
_log.critical(f'Jungfrau not connected: {e}')
self.n_pulses_run = None
self.bsdata_scalar = 1
def config(self,**kwargs):
if getattr(self,'_sim',False):
_log.info(f'simulated')
return
app=QApplication.instance() #temproary fix, couldnt access these in function, maybe the bt above needs to be self.detectors ... etc
cfg=app._cfg
det = cfg.value(AppCfg.DAQ_DET)
print("det", repr(det))
detectors = [det] if det.get("name") else None
bs_channels = cfg.value(AppCfg.DAQ_BS_CH)
pv_channels = cfg.value(AppCfg.DAQ_PV_CH)
loc=cfg.value(AppCfg.DAQ_LOC)
run=cfg.value(AppCfg.DAQ_RUN)
dt_misc = cfg.value(AppCfg.DT_MISC)
code_gen=kwargs.get('code_gen',0)
sync_mode=dt_misc['sync_mode']
sync_flag=dt_misc['sync_flag']
self.n_pulses_run = None
if loc['jungfraujoch']:
try:
#self.jfj = JFJ("http://sf-daq-2:5232") old port
self.jfj = JFJ("http://sf-daq-2:8080") #new port
self.detectors=None
_log.info(f'JungFrauJoch connected')
print('JungFrauJoch connected')
except NameError as e:
self.jfj = None
_log.critical(f'JungfrauJoch not connected: {e}')
is_scan_step=False
else:
self.jfj = None
_log.info(f'JungFrauJoch is not in use')
print('JungFrauJoch not in use')
if sync_flag==0:
grid_cnt=kwargs['grid']['count']
repetitions=grid_cnt[0] #'x' or number of columns
cta_multiplier=grid_cnt[1] #'y' or number of appertures in a column/number of rows
number_of_appertures = repetitions*cta_multiplier
cta=CTASequencer("SAR-CCTA-ESC")
cta.cfg.repetitions = repetitions
self.images=repetitions*cta_multiplier
if code_gen==3:
if kwargs['tmove']==0:
wait_pulses=0
else:
wait_pulses=kwargs['twait']//kwargs['tmove']
#xray_seq=[0,]*wait_pulses+[1] # multiplier is proportional to wait_time i.e. 10 ms = 1, 20 ms =2, 30 ms =3.
#xray_sequence=[0,]*wait_pulses+[1]
#cta.seq[200]=xray_sequence*cta_multiplier # x-ray_shutter
#cta.seq[214] = [1,] + [0,] * (len(xray_sequence * cta_multiplier) -1)
#trigger_sequence = [1,] + [0,] * wait_pulses + [0,] * wait_pulses + [0]
#cta.seq[215]=trigger_sequence*(cta_multiplier//2) # laser_shutter
#image_label_sequence = [0,] * wait_pulses + [1] + [0,] * wait_pulses + [0]
#cta.seq[216]=image_label_sequence*(cta_multiplier//2)
#cta.seq[219] = xray_sequence * cta_multiplier
on_off_ratio = 2 #1 on to 1 off which means 2 in the whole sequence
xray_sequence = [0,] * (wait_pulses - 1) + [1,] + [0,]
cta.seq[200] = xray_sequence * cta_multiplier # x-ray_shutter
cta.seq[214] = [1,] + [0,] * (len(xray_sequence * cta_multiplier) -1) #start motion
if run["triggered"]:
trigger_on = [0,] * wait_pulses + [1,]
trigger_off = [0,] + [0,] * wait_pulses
trigger_sequence = trigger_on * (on_off_ratio-1) + trigger_off
image_on = [0,] * (wait_pulses - 1) + [1,] + [0,]
image_label_sequence = [0,] +[0,] * wait_pulses + image_on * (on_off_ratio-1)
cta.seq[215] = trigger_sequence * (cta_multiplier//on_off_ratio) # trigger (laser_shutter or droplet ejector)
cta.seq[216] = image_label_sequence * (cta_multiplier//on_off_ratio) # image label (on or off)
else:
no_trigger_sequence = [0,] * (wait_pulses - 1) + [0,] + [0,]
cta.seq[215] = no_trigger_sequence * cta_multiplier # x-ray_shutter # trigger (laser_shutter or droplet ejector)
cta.seq[216] = no_trigger_sequence * cta_multiplier # x-ray_shutter # image label (on or off)1
cta.seq[219] = xray_sequence * cta_multiplier # detector trigger
else:
print('not code gen 3')
# no extra rows 1:1
cta.seq[214]=[1,]+[0,]*(cta_multiplier-1) #start motion
cta.seq[200]=[1,]*cta_multiplier #uncomment me for normal operation
#cta.seq[200]=[1,0,]*(cta_multiplier//2) # x-ray_shutter
cta.seq[219]=[1,]*cta_multiplier #trigger detector
if run["triggered"]:
cta.seq[215]=[1,0,]*(cta_multiplier//2) # Trigger 1:1
cta.seq[216]=[1,0,]*(cta_multiplier//2) # Label image light dark 1:1 #change back to 1,0 for normal on off measurements
self.n_pulses_run = len(cta.seq[200])*repetitions
self.bsdata_scalar = self.n_pulses_run/number_of_appertures
_log.info(f'The multiplier of the block size is {self.bsdata_scalar}')
cta.seq.upload()
self._daq=CTAAcquisition(cta, loc['end_station'], loc['p_group'], default_detectors=detectors,
default_channels=bs_channels,
default_pvs=pv_channels, rate_multiplicator=1, append_user_tag_to_data_dir=True, timeout=30)
else:
self._daq=SFAcquisition(
loc['end_station'], loc['p_group'],
default_detectors=detectors, default_channels=bs_channels, default_pvs=pv_channels,
rate_multiplicator=1, append_user_tag_to_data_dir=True)
def acquire(self, n_pulses, wait=False):
if getattr(self,'_sim',False):
_log.info(f'simulated')
return
try:
daq=self._daq
except AttributeError:
_log.info(f'simulated')
return
app=QApplication.instance()
cfg=app._cfg
run=cfg.value(AppCfg.DAQ_RUN)
loc=cfg.value(AppCfg.DAQ_LOC)
data_proc=cfg.value(AppCfg.DATA_PROC)
daq_sample=cfg.value(AppCfg.DAQ_SAMPLE)
daq_uc=cfg.value(AppCfg.DAQ_UC)
try:
self._pulse_id_start=int(self._pv_pulse_id.value)
except TypeError as e:
_log.warning(f'failed to get _pulse_id_start: {e}')
if self.n_pulses_run:
n_pulses_run=self.n_pulses_run
_log.info(f'self.n_pulses_run {self.n_pulses_run} is equal to n_pulses_run {n_pulses_run}')
_log.info(f'number of images to JFJ is {self.images}')
else:
n_pulses_run = n_pulses + run['padding']
_log.info(f'self.n_pulses_run {self.n_pulses_run} is not equal to n_pulses_run {n_pulses_run}')
#n_pulses_run*=2 # comment me out please when not using 10 ms wait (for stop and go)
images_per_file = run['block_size']
sample_name = daq_sample['protein'] #protein_name
prefix = run['prefix']
user_tag = f'{sample_name}_{prefix}'
if self.jfj:
#daq_uc={"a":70,"b":70,"c":70,"alpha":90,"beta":90,"gamma":90}
unit_cell = {"a":daq_uc['a'],"b":daq_uc['b'], "c":daq_uc['c'],"alpha":daq_uc['alpha'], "beta":daq_uc['beta'], "gamma":daq_uc['gamma']}
geometry_file_location = loc['geometry_file_location']
trigger_event = loc['event_trigger']
trigger_flag = run['triggered']
trigger_delay = run['laser_delay']
laser_energy = run['laser_pulse_energy']
transmission_ATT53 = epics.caget("SARFE10-OATT053:UsrRec.TD")
transmission_ATT150 = epics.caget("SAROP31-OATA150:UsrRec.TD")
transmission = transmission_ATT53*transmission_ATT150
detector_distance_mm = round(epics.caget("SAR-EXPMX:MOT_DET_Z.RBV"),2)
run_number = self._daq.client.next_run()
is_scan_step = True
pgroup=loc['p_group']
try:
endstation = get_endstation()
_log.info(f'got endstation through slic:{endstation}')
except:
endstation = loc['end_station'] #'cristallina' #need cdoe form slic to direct this
_log.info(f'got endstation through gui:{endstation}')
jfj_file_prefix = f'sf/{endstation}/data/{pgroup}/raw/run{run_number:04}-{user_tag}/data/acq'
header_appendix = {}
for key in data_proc.keys():
value = data_proc[key]
header_appendix[f'{key}']=value
header_appendix['geometry_file_location'] = geometry_file_location
header_appendix['trigger_flag'] = trigger_flag
header_appendix['trigger_delay'] = trigger_delay
header_appendix['trigger_event'] = trigger_event
header_appendix['laser_pulse_energy'] = trigger_delay
header_appendix['file_prefix'] = f'run{run_number:04}-{user_tag}'
self.jfj.acquire(beam_x_pxl = loc['beam_x'], beam_y_pxl = loc['beam_y'], detector_distance_mm = detector_distance_mm, incident_energy_keV = loc['incident_energy_kev'], transmission = transmission,
sample_name = sample_name, run_number = run_number, file_prefix = jfj_file_prefix, experiment_group=pgroup, ntrigger = self.images, images_per_file = images_per_file,
unit_cell=unit_cell, space_group_number=daq_uc['space_group_number'],
header_appendix=header_appendix
)
_log.info('JFJ primed')
else:
is_scan_step=False
block_size = int(run['block_size']*self.bsdata_scalar) #scales block_size for bsdata acquisition only
_log.info(f'The multiplier of the block size is {self.bsdata_scalar}, new block size is {block_size}')
if type(self._daq) is CTAAcquisition:
self._daq.acquire(user_tag, n_pulses=max(n_pulses_run, block_size), n_block_size=block_size, wait=False, cell_name=run['cell_name'], is_scan_step=is_scan_step)
else:
self._daq.acquire(user_tag, n_pulses=max(n_pulses_run, block_size), n_repeat=ceil(n_pulses_run/block_size), wait=False, cell_name=run['cell_name'], is_scan_step=is_scan_step)
cfg.setValue(AppCfg.DAQ_RUN,run)
is_scan_step=False
def gather_upload(self):
if getattr(self,'_sim',False):
_log.info(f'simulated')
return
try:
daq=self._daq
except AttributeError:
_log.info(f'simulated')
return
try:
self._pulse_id_end=int(self._pv_pulse_id.value)
except TypeError as e:
_log.warning(f'failed to get _pulse_id_start: {e}')
else:
_log.debug(f'pulse_id: {self._pulse_id_start}..{self._pulse_id_end}')
if __name__ == "__main__":
import os
import argparse
if hostname=='ganymede':
# use EPICS locally
# os.environ['EPICS_CA_ADDR_LIST']='localhost'
# use EPICS if connected to ESC network
os.environ['EPICS_CA_ADDR_LIST']='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066'
parser = argparse.ArgumentParser()
parser.add_argument("--mode", "-m", help="qt test", type=lambda x:int(x, 0), default=0)
args = parser.parse_args()
_log.info('Arguments:{}'.format(args.__dict__))
app=QApplication(sys.argv)
app._cfg=cfg=AppCfg()
if args.mode &0x01:
dt=Deltatau()
if args.mode&0x02:
jf=Jungfrau()
jf.acquire(n_pulses=100, wait=True)
jf.gather_upload()