#TODO: # currently 2 settings/configs are used # QSettings -> cat ~/.config/Paul\ Scherrer\ Institut/SwissMX.conf # yaml -> swissmx.yaml # QSettings are changed by program # #yaml is fixed and not altened by program import logging _log = logging.getLogger(__name__) from PyQt5.QtCore import QSettings from PyQt5.QtWidgets import QApplication, QLineEdit import os, json, yaml import GenericDialog class AppCfg(QSettings): GEO_OPT_CTR='geometry/opt_ctr' GEO_PIX2POS='geometry/pix2pos' GEO_BEAM_SZ="geometry/beam_size" GEO_BEAM_POS="geometry/beam_pos" WINDOW_GEOMETRY="window/geometry" WINDOW_SPLITTER="window/splitter" WINDOW_STATE= "window/state" PST_X_UP ="post_sample_tube/x_up" PST_Y_UP ="post_sample_tube/y_up" PST_X_DOWN="post_sample_tube/x_down" PST_Y_DOWN="post_sample_tube/y_down" PST_DX ="post_sample_tube/dx" PST_DY ="post_sample_tube/dy" PST_TZ_IN ="post_sample_tube/z_in" PST_TZ_OUT="post_sample_tube/z_out" COL_DX ="collimator/dx" COL_DY ="collimator/dy" COL_X_IN ="collimator/x_in" COL_Y_IN ="collimator/y_in" BKLGT_IN ="backlight/in" BKLGT_OUT ="backlight/out" DT_HOST="deltatau/host" DT_SHOW_PLOTS="deltatau/show_plots" DT_VEL_SCL="deltatau/velocity_scale" # ---------- OBSOLETE ??? ---------- #ZOOM_BUTTONS="sample_viewing/zoom_buttons" SKIP_ESCAPE_TRANSITIONS_IF_SAFE="escape/skip_transitions_if_safe" CRYOJET_MOTION_ENABLED="cryojet/motion_enabled" CRYOJET_NOZZLE_OUT="cryojet/nozzle_out" CRYOJET_NOZZLE_IN="cryojet/nozzle_in" #DELTATAU_HOST="deltatau/host" #DELTATAU_SHOW_PLOTS="deltatau/show_plots" DELTATAU_OMEGACOS="deltatau/omegacos" DELTATAU_SORT_POINTS="deltatau/sort_points" #DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale" CAMERA_TRANSFORMATIONS="camera/transformations" CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm" EXPERIMENT_PGROUP="experiment/pgroup" EXPERIMENT_UID="experiment/uid" ACTIVATE_PULSE_PICKER="scanning/activate_pulse_picker" def __init__(self): super(AppCfg, self).__init__("PSI", "SwissMX") keys = self.allKeys() # Dump config to debug #for k in keys: # print(k, self.value(k)) #set default keys if not existing if AppCfg.GEO_BEAM_SZ not in keys: _log.warning(f'{AppCfg.GEO_BEAM_SZ} not defined. set default') self.setValue(AppCfg.GEO_BEAM_SZ, [30.2, 25.6]) #([40, 20) -> tuples are not supported natively if AppCfg.GEO_BEAM_POS not in keys: _log.warning(f'{AppCfg.GEO_BEAM_POS} not defined. set default') self.setValue(AppCfg.GEO_BEAM_POS, [23.4, -54.2]) # beam position relativ to optical center in mm if AppCfg.GEO_PIX2POS not in keys: _log.warning(f'{AppCfg.GEO_OPT_CTR} not defined. use default') import numpy as np lut_pix2pos=(np.array([1., 200., 400., 600., 800., 1000.]), np.array([[[ 2.42827273e-03, -9.22117396e-05], [-1.10489804e-04, -2.42592492e-03]], [[ 1.64346103e-03, -7.52341417e-05], [-6.60711165e-05, -1.64190224e-03]], [[ 9.91307639e-04, -4.02008751e-05], [-4.23878232e-05, -9.91563507e-04]], [[ 5.98443038e-04, -2.54046255e-05], [-2.76831563e-05, -6.02738142e-04]], [[ 3.64418977e-04, -1.41389267e-05], [-1.55708176e-05, -3.66233567e-04]], [[ 2.16526433e-04, -8.23070130e-06], [-9.29894004e-06, -2.16842976e-04]]])) self.setValue(AppCfg.GEO_PIX2POS, lut_pix2pos) if AppCfg.GEO_OPT_CTR not in keys: _log.warning(f'{AppCfg.GEO_OPT_CTR} not defined. use default') import numpy as np opt_ctr=np.array([603.28688025, 520.01112846]) self.setValue(AppCfg.GEO_OPT_CTR, opt_ctr) #if AppCfg.ACTIVATE_PULSE_PICKER not in keys: # self.setValue(AppCfg.ACTIVATE_PULSE_PICKER, False) #if AppCfg.SKIP_ESCAPE_TRANSITIONS_IF_SAFE not in keys: # self.setValue(AppCfg.SKIP_ESCAPE_TRANSITIONS_IF_SAFE, False) def sync(self): super(AppCfg, self).sync() #import numpy as np #a=np.array(((1,2,3),(4,5,6))) #self._yamlFn=fn=os.path.expanduser('~/.config/PSI/SwissMX.yaml') #_yaml = [{'name': 'John Doe', 'occupation': 'gardener'}, # {'name': 'Lucy Black', 'occupation': 'teacher'}] #_yaml = {'name': 'John Doe', 'occupation': 'gardener','A':(1,2,3),'B':{1,2,3},'C': {1:(1,2,3),2:'df',3:'dddd'},4:a } #with open(self._yamlFn, 'w') as f: # data = yaml.dump(_yaml, f) # print(data) def setValue(self, key: str, val): #overload to debug return super(AppCfg, self).setValue(key,val) def value(self,key,*vargs,**kwargs): #overload to debug val=super(AppCfg, self).value(key,*vargs,**kwargs) return val #@property #def value(self): # return super(AppCfg, self).value def option(self,key: str) -> bool: try: return self.value(key, type=bool) except: _log.error(f"option {key} not known") return False def toggle_option(self,key: str): v = self.value(key, type=bool) self.setValue(key, not v) self.sync() #inst_folder = Path(__file__).absolute().parent #config_file = inst_folder / "swissmx.yaml" #configs = yaml.load(config_file.read_text(),Loader=yaml.FullLoader) #endstation = configs["configure_for"] #appsconf = configs[endstation] #simulated = appsconf.get("simulate", False) #logger.info(f"configuring for endstation: {endstation.upper()}") #if simulated: # logger.warning("SIMULATION is ACTIVE") #css_file = inst_folder / "swissmx.css" #def font(name: str) -> str: # p = Path(__file__).absolute().parent / "fonts" / name # return str(p) #def logo(size: int = 0) -> str: # p = Path(__file__).absolute().parent / "logos" / "logo.png" # if size: # p = Path(__file__).absolute().parent / "logos" / f"tell_logo_{size}x{size}.png" # return str(p) def dlg_geometry(self,obj): SPN=GenericDialog.Spinner app=QApplication.instance() cfg=app._cfg w, h = map(float,cfg.value(AppCfg.GEO_BEAM_SZ)) d = GenericDialog.GenericDialog( title="geometry", message="Enter the size of the beam in microns", inputs={ "bw": ("beam width um" ,w,SPN(w, min=1, max=200, suffix=" \u00B5m"),), "bh": ("beam height um",h,SPN(h, min=1, max=200, suffix=" \u00B5m"),), }, ) if d.exec(): results = d.results _log.info("Updating beamsize to {}".format(results)) bm_sz= (results["bw"], results["bh"]) _log.debug("types {}".format(type(w))) cfg.setValue(AppCfg.GEO_BEAM_SZ, bm_sz) cfg.sync() bm=obj._goBeamMarker bm.setSize(bm_sz) #self._beammark.set_beam_size((w, h)) def dlg_posttube_references(self): SPN=GenericDialog.Spinner app=QApplication.instance() cfg=app._cfg x_up = cfg.value(AppCfg.PST_X_UP , 0.0,type=float) y_up = cfg.value(AppCfg.PST_Y_UP , 0.0,type=float) x_down = cfg.value(AppCfg.PST_X_DOWN, 0.0,type=float) y_down = cfg.value(AppCfg.PST_Y_DOWN, 0.0,type=float) dx = cfg.value(AppCfg.PST_DX , 0.0,type=float) dy = cfg.value(AppCfg.PST_DY , 0.0,type=float) tz_in = cfg.value(AppCfg.PST_TZ_IN , 0.0,type=float) tz_out = cfg.value(AppCfg.PST_TZ_OUT, 0.0,type=float) d = GenericDialog.GenericDialog( title="Post Sample Tube Configuration", message="Enter the relative displacements for X and Y to move the post sample tube either in or out.", inputs={ AppCfg.PST_X_UP : ("Up X" , x_up , SPN(x_up , decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), AppCfg.PST_Y_UP : ("Up Y" , y_up , SPN(y_up , decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), AppCfg.PST_X_DOWN: ("Down X" , x_down, SPN(x_down, decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), AppCfg.PST_Y_DOWN: ("Down Y" , y_down, SPN(y_down, decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), AppCfg.PST_DX : ("out delta X" , dx , SPN(dx , decimals=3, min=-32.0, max=32.0, suffix=" mm"), ), AppCfg.PST_DY : ("out delta Y" , dy , SPN(dy , decimals=3, min=-32.0, max=32.0, suffix=" mm"), ), AppCfg.PST_TZ_IN : ("tube Z in position" , tz_in , SPN(tz_in , decimals=3, min=-8.0 , max=1.0 , suffix=" mm"), ), AppCfg.PST_TZ_OUT: ("tube Z OUT position", tz_out, SPN(tz_out, decimals=3, min=-8.0 , max=1.0 , suffix=" mm"), ), }, ) if d.exec(): results = d.results _log.info("setting post-sample-tube displacements {}".format(results)) for k, v in results.items(): cfg.setValue(k, v) cfg.sync() def dlg_collimator_reference_positions(self): SPN=GenericDialog.Spinner app=QApplication.instance() cfg=app._cfg x_out = cfg.value(AppCfg.COL_DX , 0.0,type=float) y_out = cfg.value(AppCfg.COL_DY , 0.0,type=float) x_in = cfg.value(AppCfg.COL_X_IN, 0.0,type=float) y_in = cfg.value(AppCfg.COL_Y_IN, 0.0,type=float) d = GenericDialog.GenericDialog( title="Collimator configuration", message="Enter reference positions for the collimator", inputs={ AppCfg.COL_DX: ("Collimator out deltaX", x_out, SPN(x_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), AppCfg.COL_DY: ("Collimator out deltaY", y_out, SPN(y_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), AppCfg.COL_X_IN: ("Collimator in X", x_in, SPN(x_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), AppCfg.COL_Y_IN: ("Collimator in Y", y_in, SPN(y_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), }, ) if d.exec(): results = d.results _log.info("setting collimator reference positions {}".format(results)) for k, v in results.items(): cfg.setValue(k, v) cfg.sync() def dlg_backlight_positions(self): SPN=GenericDialog.Spinner app=QApplication.instance() cfg=app._cfg p_in = cfg.value(AppCfg.BKLGT_IN,0,type=int) p_out = cfg.value(AppCfg.BKLGT_OUT,0,type=int) d = GenericDialog.GenericDialog( title="Back Light configuration", message="Enter reference positions for the backlight", inputs={ AppCfg.BKLGT_IN: ("In position" , p_in , SPN(p_in, min=-30000, max=10), ), AppCfg.BKLGT_OUT: ("Out position", p_out, SPN(p_out, min=-1000, max=10), ), }, ) if d.exec(): results = d.results _log.info("setting back light reference positions {}".format(results)) for k, v in results.items(): cfg.setValue(k, v) cfg.sync() def dlg_cryojet_positions(self): p_in = settings.value(CRYOJET_NOZZLE_IN, type=float) p_out = settings.value(CRYOJET_NOZZLE_OUT, type=float) motion_enabled = option(CRYOJET_MOTION_ENABLED) d = GenericDialog( title="Cryojet Nozzle Configuration", message="Enter reference positions for the cryojet nozzle position", inputs={ CRYOJET_NOZZLE_IN: ("In position", p_in, Spinner(p_in, min=3, max=15)), CRYOJET_NOZZLE_OUT: ("Out position",p_out,Spinner(p_out, min=-1000, max=10),), CRYOJET_MOTION_ENABLED: ("Move Cryojet in Transitions",motion_enabled,Checkbox(motion_enabled, "move cryojet"),), }, ) if d.exec(): results = d.results _log.info("setting cryojet reference positions {}".format(results)) for k, v in results.items(): settings.setValue(k, v) settings.sync() def dlg_deltatau_parameters(self): SPN=GenericDialog.Spinner CB=GenericDialog.Checkbox app=QApplication.instance() cfg=app._cfg #dt1 = cfg.value(AppCfg.DT_HOST,'SAR-CPPM-EXPMX1') dt1 = cfg.value(AppCfg.DT_HOST,'localhost:10001:10002') dt2 = cfg.value(AppCfg.DT_VEL_SCL, 1, type=float) dt3 = cfg.option(AppCfg.DT_SHOW_PLOTS) d = GenericDialog.GenericDialog( title="Delta Tau Parameters", message="These parameters affect the data collection.", inputs={ AppCfg.DT_HOST:("host name (host[:port:port_gather])", dt1, QLineEdit(),), AppCfg.DT_VEL_SCL: ("Velocity Scale (1=optimal, 0=zero vel at target)", dt2,SPN(dt2, min=0, max=1, suffix=""),), AppCfg.DT_SHOW_PLOTS: ("show plots after collection", dt3,CB(dt3, "active"),), #DELTATAU_SORT_POINTS: ("Sort pointshoot/prelocated coords", b,CB(b, "sort points"),), }, ) if d.exec(): results = d.results _log.info("setting delta tau parameters {}".format(results)) for k, v in results.items(): cfg.setValue(k, v) cfg.sync() def dlg_tell_mount_positions(self): AUTODRY_ENABLED = "tell/autodry_enabled" AUTODRY_MAXMOUNTS = "tell/autodry_max_number_of_mounts" AUTODRY_MAXTIME = "tell/autodry_max_time" SECS_HOURS = 60 * 60 enabled = option(AUTODRY_ENABLED) maxtime = settings.value(AUTODRY_MAXTIME, type=float) / SECS_HOURS maxmounts = settings.value(AUTODRY_MAXMOUNTS, type=int) d = GenericDialog.GenericDialog( title="TELL Settings", message="These control some features of the TELL sample changer", inputs={ AUTODRY_ENABLED: ("Auto dry", enabled, Checkbox(enabled, "enabled")), AUTODRY_MAXMOUNTS: ("Max. num. mounts between dry",maxmounts, Spinner(maxmounts, decimals=0, min=1, max=100, suffix=" mounts"),), AUTODRY_MAXTIME: ("Max. time between dry",maxtime, Spinner(maxtime, decimals=1, min=0.5, max=5, suffix=" hours"),), }, ) if d.exec(): results = d.results _log.info("setting tell parameters {}".format(results)) for k, v in results.items(): if k == AUTODRY_MAXTIME: v = v * SECS_HOURS settings.setValue(k, v) settings.sync()