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) 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") self.detectors=None _log.info(f'JungFrauJoch connected') except NameError as e: self.jfj = None _log.critical(f'JungfrauJoch not connected: {e}') is_scan_step=False else: self.jfj = None 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,]*wait_pulses+[1] 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 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 # x-ray_shutter cta.seq[219]=[1,]*cta_multiplier #trigger detector 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 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 = run['prefix'] #daq_sample['sample_name'] #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']} 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] # pair =f'"{key}":{value},' # header_appendix+=pair header_appendix+='}' #some keys are different in JFJoch 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='42',#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()