diff --git a/eco/acquisition/daq_client.py b/eco/acquisition/daq_client.py index 03ed9f1..ca59965 100644 --- a/eco/acquisition/daq_client.py +++ b/eco/acquisition/daq_client.py @@ -4,12 +4,15 @@ import shutil from threading import Thread import time import traceback +import colorama +import numpy as np import requests from pathlib import Path from time import sleep from eco.utilities import NumpyEncoder from eco.elements.protocols import Adjustable +from eco.utilities.utilities import foo_get_kwargs from ..epics.detector import DetectorPvDataStream from ..epics.utilities_epics import Monitor from epics import PV @@ -18,7 +21,7 @@ from ..elements.assembly import Assembly from ..utilities.path_alias import PathAlias import inputimeout from IPython import get_ipython - +from os.path import relpath class Daq(Assembly): def __init__( @@ -39,6 +42,9 @@ class Daq(Assembly): rate_multiplicator=None, name=None, namespace=None, + checker=None, + run_table=None, + pulse_picker=None, elog = None, ): super().__init__(name=name) @@ -74,15 +80,38 @@ class Daq(Assembly): self._detectors_event_code = detectors_event_code self.name = name self.namespace = namespace + self.checker = checker + self.run_table=run_table + self.pulse_picker = pulse_picker self._default_file_path = None if not rate_multiplicator == "auto": print( "warning: rate multiplicator automatically determined from event_master!" ) - self.callbacks_start_scan = [self.check_counters_for_scan, self.append_start_status_to_scan, self.count_run_number_up_and_attach_to_scan, self.scan_message_to_elog, self.append_scan_monitors] - self.callbacks_start_step = [self.copy_aliases_to_scan] - self.callbacks_end_step = [self.copy_scan_info_to_raw] - self.callbacks_end_scan = [self.append_status_to_scan_and_store, self.copy_scan_info_to_raw, self.end_scan_monitors] + self.callbacks_start_scan = [ + self.check_counters_for_scan, + self.init_namespace, + self.append_start_status_to_scan, + self.count_run_number_up_and_attach_to_scan, + self.scan_message_to_elog, + self._create_runtable_metadata_append_status_to_runtable, + self.append_scan_monitors] + self.callbacks_start_step = [ + self.copy_aliases_to_scan, + self.check_checker_before_step, + self.pulse_picker_action_start_step, + ] + self.callbacks_step_counting = [] + self.callbacks_end_step = [ + self.pulse_picker_action_end_step, + self.copy_scan_info_to_raw, + self.check_checker_after_step + ] + self.callbacks_end_scan = [ + self.append_status_to_scan_and_store, + self.copy_scan_info_to_raw, + self.end_scan_monitors, + ] self.elog = elog @property @@ -112,14 +141,14 @@ class Daq(Assembly): if scan: acq_pars = { "scan_info": { - "scan_name": scan.description, + "scan_name": scan.description(), "scan_values": scan.values_current_step, "scan_readbacks": scan.readbacks_current_step, "scan_step_info": { "step_number": scan.next_step + 1, }, "name": [adj.name for adj in scan.adjustables], - "expected_total_number_of_steps": scan.number_of_steps, + "expected_total_number_of_steps": scan.number_of_steps(), }, "run_number": scan.daq_run_number, "user_tag": "usertag", @@ -165,7 +194,28 @@ class Daq(Assembly): stop_id=self.running[ix]["start_id"] + Npulses - 1, acq_ix=ix, wait=wait ) - def start(self, label=None, **kwargs): + def start(self, label=None, scan=None, **kwargs): + if scan: + acq_pars = { + "scan_info": { + "scan_name": scan.description(), + "scan_values": scan.values_current_step, + "scan_readbacks": scan.readbacks_current_step, + "scan_step_info": { + "step_number": scan.next_step + 1, + }, + "name": [adj.name for adj in scan.adjustables], + "expected_total_number_of_steps": scan.number_of_steps(), + }, + "run_number": scan.daq_run_number, + "user_tag": "usertag", + } + kwargs.update(acq_pars) + kwargs['channels_JF'] = kwargs.get('channels_JF',self.channels["channels_JF"].get_current_value()) + kwargs['channels_BS'] = kwargs.get('channels_BS',self.channels["channels_BS"].get_current_value()) + kwargs['channels_BSCAM'] = kwargs.get('channels_BSCAM',self.channels["channels_BSCAM"].get_current_value()) + kwargs['channels_CA'] = kwargs.get('channels_CA',self.channels["channels_CA"].get_current_value()) + starttime_local = time.time() while self.pulse_id._pv.get_timevars() is None: time.sleep(0.02) @@ -178,6 +228,8 @@ class Daq(Assembly): } acq_pars.update(kwargs) self.running.append(acq_pars) + if scan: + scan.daq_current_acquisition_index = self.running.index(acq_pars) return self.running.index(acq_pars) def stop( @@ -187,19 +239,68 @@ class Daq(Assembly): label=None, wait=True, wait_cycle_sleep=0.01, + scan=None, ): if not stop_id: stop_id = int(self.pulse_id.get_current_value()) + + if scan: + acq_ix = scan.daq_current_acquisition_index if not acq_ix: acq_ix = -1 acq_pars = self.running.pop(acq_ix) acq_pars["stop_id"] = stop_id label = acq_pars.pop("label") + + # if scan: + # tmp = scan.info() + # tmp['daq_pars'] = acq_pars + # scan.info() if wait: while int(self.pulse_id.get_current_value()) < stop_id: sleep(wait_cycle_sleep) - return self.retrieve(**acq_pars) + + response = self.retrieve(**acq_pars) + # print(response) + + if scan and not scan.daq_run_number==int(response["run_number"]): + raise Exception( + f"Run number mismatch: scan {scan.daq_run_number} != response {int(response['run_number'])}" + ) + + # correct file names to relative paths + if scan: + run_directory = list( + Path(f"/sf/bernina/data/{self.pgroup}/raw").glob(f"run{scan.daq_run_number:04d}*") + )[0].as_posix() + + response['files'] = [relpath(file, run_directory) for file in response['files']] + + return response + + # if scan: + # response = self.acquire_pulses( + # Npulses, + # # directory_relative=Path(file_name).parents[0], + # wait=True, + # channels_JF=self.channels["channels_JF"].get_current_value(), + # channels_BS=self.channels["channels_BS"].get_current_value(), + # channels_BSCAM=self.channels["channels_BSCAM"].get_current_value(), + # channels_CA=self.channels["channels_CA"].get_current_value(), + # **acq_pars, + # ) + # acquisition.acquisition_kwargs.update({"file_names": response["files"]}) + # if scan and not scan.daq_run_number==int(response["run_number"]): + # raise Exception( + # f"Run number mismatch: scan {scan.daq_run_number} != response {int(response['run_number'])}" + # ) + + # for key, val in acquisition.acquisition_kwargs.items(): + # acquisition.__dict__[key] = val + + + def retrieve( self, @@ -356,8 +457,37 @@ class Daq(Assembly): json={"pgroup": pgroup, "run_number": run_number, "files": file_names}, ) + + def pulse_picker_action_start_step( + self, + scan, + do_pulse_picker_action=True, + **kwargs, + ): + + if not self.pulse_picker: + return + if not do_pulse_picker_action: + return + + self.pulse_picker.open(verbose=False) + + def pulse_picker_action_end_step( + self, + scan, + do_pulse_picker_action=True, + **kwargs, + ): + if not self.pulse_picker: + return + if not do_pulse_picker_action: + return + + self.pulse_picker.close(verbose=False) + + def check_counters_for_scan( - self, scan, channels_to_check=["channels_BSCAM", "channels_JF"], timeout=3 + self, scan, channels_to_check=["channels_BSCAM", "channels_JF"], channels_check_timeout=3, **kwargs ): if not set(self.channels.keys()).intersection(set(channels_to_check)): return @@ -367,8 +497,8 @@ class Daq(Assembly): print(f"{nam} : {chs.get_current_value()}") try: o = inputimeout.inputimeout( - prompt=f"Press Ctrl-c to abort, Return to continue, or wait {timeout} seconds", - timeout=timeout, + prompt=f"Press Ctrl-c to abort, Return to continue, or wait {channels_check_timeout} seconds", + timeout=channels_check_timeout, ) except inputimeout.TimeoutOccurred: print("... timed out, continuing with selection.") @@ -409,6 +539,10 @@ class Daq(Assembly): # except Exception as e: # print(f"Error to set dap configuration {e}") + def init_namespace(self,scan=None, init_required_namespace_components_only=True, append_status_info=True): + if append_status_info: + self.namespace.init_all(silent=False, required_only=init_required_namespace_components_only) + def append_start_status_to_scan(self,scan=None, append_status_info=True): if not append_status_info: return @@ -416,6 +550,53 @@ class Daq(Assembly): stat = {"status_run_start": namespace_status} scan.namespace_status = stat + def _create_runtable_metadata_append_status_to_runtable( + self, + scan, + append_status_info=True): + + print("run_table appending run") + runno = scan.daq_run_number + metadata = { + "type": "scan", + "name": scan.description.get_current_value(), + "scan_info_file": '', + } + for n, adj in enumerate(scan.adjustables): + nname = None + adj_pvname = None + if hasattr(adj, "Id"): + adj_pvname = adj.Id + if hasattr(adj, "name"): + nname = adj.name + + metadata.update( + { + f"scan_dim_{n}": nname, + f"from_dim_{n}": scan.values_todo.get_current_value()[0][n], + f"to_dim_{n}": scan.values_todo.get_current_value()[-1][n], + f"pvname_dim_{n}": adj_pvname, + } + ) + if np.mean(np.diff(scan.pulses_per_step)) < 1: + pulses_per_step = scan.pulses_per_step[0] + else: + pulses_per_step = scan.pulses_per_step + metadata.update( + { + "steps": len(scan.values_todo.get_current_value()), + "pulses_per_step": pulses_per_step, + "counters": scan.counters_names.get_current_value(), + "scan_command": scan.scan_command.get_current_value(), + } + ) + t_start_rt = time.time() + try: + self.run_table.append_run(runno, metadata=metadata, d=scan.namespace_status["status_run_start"]["status"]) + except: + print("WARNING: issue adding data to run table") + print(f"Runtable appending took: {time.time()-t_start_rt:.3f} s") + def copy_scan_info_to_raw(self,scan, **kwargs): t_start = time.time() @@ -426,22 +607,12 @@ class Daq(Assembly): runno = self.get_last_run_number() # get data that should come later from api or similar. - run_directory = list( - Path(f"/sf/bernina/data/{self.pgroup}/raw").glob(f"run{runno:04d}*") - )[0].as_posix() + # run_directory = list( + # Path(f"/sf/bernina/data/{self.pgroup}/raw").glob(f"run{runno:04d}*") + # )[0].as_posix() # Get scan info from scan si = scan.scan_info - - # correct some data in there (relative paths for now) - from os.path import relpath - - newfiles = [] - for files in si["scan_files"]: - newfiles.append([relpath(file, run_directory) for file in files]) - - si["scan_files"] = newfiles - # save temprary file and send then to raw pgroup = self.pgroup @@ -483,14 +654,13 @@ class Daq(Assembly): # ) - def append_status_to_scan_and_store(self, scan, append_status_info=True, **kwargs ): if not append_status_info: return - if not len(scan.values_done)>0: + if not len(scan.values_done())>0: return namespace_status = self.namespace.get_status(base=None) @@ -530,9 +700,40 @@ class Daq(Assembly): # print(response.json()) # print("###############################") scan.scan_info["scan_parameters"]["status"] = "aux/status.json" + + + def check_checker_before_step(self,scan, **kwargs): + # self. + if self.checker: + first_check = time.time() + checker_unhappy = False + print('') + while not self.checker.check_now(): + print( + colorama.Fore.RED + + f"Condition checker is not happy, waiting for OK conditions since {time.time()-first_check:5.1f} seconds." + + colorama.Fore.RESET, + # end="\r", + ) + sleep(1) + + checker_unhappy = True + if checker_unhappy: + print( + colorama.Fore.RED + + f"Condition checker was not happy and waiting for {time.time()-first_check:5.1f} seconds." + + colorama.Fore.RESET + ) + self.checker.clear_and_start_counting() + + def check_checker_after_step(self,scan, **kwargs): + if self.checker: + if not self.checker.stop_and_analyze(): + scan._current_step_ok = False + - def copy_aliases_to_scan(self, scan, force=False, **kwargs): - if force or (len(scan.values_done) == 1): + def copy_aliases_to_scan(self, scan, send_aliases_now=False, **kwargs): + if send_aliases_now or (len(scan.values_done()) == 1): namespace_aliases = self.namespace.alias.get_all() if hasattr(scan, "daq_run_number"): runno = scan.daq_run_number @@ -583,30 +784,28 @@ class Daq(Assembly): # print("################################") scan.scan_info["scan_parameters"]["aliases"] = "aux/aliases.json" - def scan_message_to_elog(self, scan=None): + def scan_message_to_elog(self, scan=None, **kwargs): # def _create_metadata_structure_start_scan( # scan, run_table=run_table, elog=elog, append_status_info=True, **kwargs # ): runno = scan.daq_run_number message_string = f"#### DAQ run {runno}" - if scan.description: - message_string += f': {scan.description}\n' + if scan.description(): + message_string += f': {scan.description()}\n' else: message_string += f'\n' try: - scan_command = get_ipython().user_ns["In"][-1] - message_string += "`" + scan_command + "`\n" - except: - print("Count not retrieve ipython scan command!") + elog_ids = scan.status_to_elog(message_string,auto_title=False) + scan._elog_id = elog_ids[1] # message_string += "`" + metadata["scan_info_file"] + "`\n" - try: - elog_ids = self.elog.post( - message_string, - Title=f'Run {runno}: {scan.description}', - text_encoding="markdown", - ) - scan._elog_id = elog_ids[1] + # try: + # elog_ids = self.elog.post( + # message_string, + # Title=f'Run {runno}: {scan.description()}', + # text_encoding="markdown", + # ) + # metadata.update({"elog_message_id": scan._elog_id}) # metadata.update( # {"elog_post_link": scan._elog.elogs[1]._log._url + str(scan._elog_id)} @@ -632,12 +831,12 @@ class Daq(Assembly): scan.daq_monitors[tname] = Monitor(adj.pvname) except Exception: print(f"Could not add CA monitor for {tname}") - traceback.print_exc() + # traceback.print_exc() try: rname = adj.readback.alias.get_full_name() except Exception: print("no readback configured") - traceback.print_exc() + # traceback.print_exc() try: scan.daq_monitors[rname] = Monitor(adj.readback.pvname) except Exception: @@ -690,6 +889,37 @@ class Daq(Assembly): ) print(f"Status: {response.json()['status']} Message: {response.json()['message']}") + + def get_callback_keywords(self): + kws_all = set([]) + for cb in self.callbacks_start_scan: + kws = foo_get_kwargs(cb) + + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_start_step: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_step_counting: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_end_step: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_end_scan: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + return kws_all + # scan.monitors = None # def run_table_stuff(self): diff --git a/eco/acquisition/scan.py b/eco/acquisition/scan.py index ccf960a..cc55788 100755 --- a/eco/acquisition/scan.py +++ b/eco/acquisition/scan.py @@ -1,3 +1,5 @@ +from datetime import datetime +from itertools import product from numbers import Number import os import json @@ -7,8 +9,10 @@ import traceback from pathlib import Path import colorama +from eco.elements.detector import DetectorGet, DetectorMemory +from eco.elements.protocols import Adjustable from eco.utilities.utilities import NumpyEncoder, foo_get_kwargs -from ..elements.adjustable import DummyAdjustable +from ..elements.adjustable import AdjustableMemory, DummyAdjustable from IPython import get_ipython from .daq_client import Daq from eco.elements.assembly import Assembly @@ -32,7 +36,7 @@ class RunList(Assembly): def get_run_list(self): ... -class StepScan: +class StepScan(Assembly): def __init__( self, adjustables, @@ -41,82 +45,111 @@ class StepScan: description='', Npulses=100, basepath="", - # scan_in1fo_dir="", settling_time=0, - # checker=None, - # scan_directories=False, callbacks_start_scan=[], callbacks_start_step=[], + callbacks_step_counting=[], callbacks_end_step=[], callbacks_end_scan=[], - # checker_sleep_time=2, - return_at_end="question", - # run_number=None, + return_at_end="timeout", + gridspecs = None, elog=None, + name='current_scan', **kwargs_callbacks, ): # if np.any([char in fina for char in inval_chars]): # raise ScanNameError - self.number_of_steps = len(values) + super().__init__(name=name) + self._append(DetectorMemory, datetime.now().strftime('%Y-%M-%d %H:%M:%S'), name='start_time') + self._description = description + self._append(DetectorGet, lambda : self._description, name='description') + self.adjustables = adjustables + self._append(DetectorGet, lambda : self._get_names(self.adjustables), name='adjustables_names') + self.counters = counters + self._append(DetectorGet, lambda : self._get_names(self.counters), name='counters_names') + + self._append(DetectorMemory, len(values), name='number_of_steps') + try: + scan_command = get_ipython().user_ns["In"][-1] + except: + scan_command = "unknown" + self._append(DetectorMemory, scan_command, name='scan_command') + if not isinstance(Npulses, Number): if not len(Npulses) == len(values): raise ValueError("steps for Number of pulses and values must match!") self.pulses_per_step = Npulses else: self.pulses_per_step = [Npulses] * len(values) - self.adjustables = adjustables - self.values_todo = values - self.values_done = [] + + self._values_todo = values + self._append(DetectorGet, lambda : self._values_todo, name='values_todo', is_display=False) + self._values_done = [] + self._append(DetectorGet, lambda : self._values_done, name='values_done', is_display=False) + self._append(DetectorMemory, gridspecs, name='grid_specs', is_display=False) + self.pulses_done = [] + self.readbacks = [] - self.counters = counters - self.settling_time = settling_time + + self._settling_time = settling_time + self._append(DetectorGet, lambda : self._settling_time, name='settling_time', is_display=False) self.next_step = 0 - self.description = description - # self.scan_info_dir = scan_info_dir - - anames = [] - for ta in adjustables: - try: - anames.append(ta.alias.get_full_name()) - except: - anames.append(ta.name) - + + + self.scan_info = { "scan_parameters": { - "name": anames, + "name": self.adjustables_names.get_current_value(), + "grid_specs": self.grid_specs.get_current_value(), # "Id": [ta.Id if hasattr(ta, "Id") else "noId" for ta in adjustables], }, - "scan_description": self.description, + "scan_description": self._description, "scan_values_all": values, "scan_values": [], "scan_readbacks": [], "scan_files": [], "scan_step_info": [], } - # self.scan_info_filename = os.path.join(self.scan_info_dir, fina) - # self._scan_directories = scan_directories - # self.checker = checker - self.initial_values = [] + + self._append(DetectorGet, lambda : self.scan_info, name='info', is_display=False) + + + + initial_values = [] + for adj in self.adjustables: + tv = adj.get_current_value() + initial_values.append(adj.get_current_value()) + print("Initial value of %s : %g" % (adj.name, tv)) + + self._append(DetectorMemory, initial_values, name='initial_values', is_display=False) + self.return_at_end = return_at_end - # self._checker_sleep_time = checker_sleep_time self._elog = elog - # self.run_number = run_number self.remaining_tasks = [] self.callbacks_start_scan = callbacks_start_scan self.callbacks_start_step = callbacks_start_step + self.callbacks_step_counting = callbacks_step_counting self.callbacks_end_step = callbacks_end_step self.callbacks_end_scan = callbacks_end_scan self.callbacks_kwargs = kwargs_callbacks - # print(f"Scan info in file {self.scan_info_filename}.") - for adj in self.adjustables: - tv = adj.get_current_value() - self.initial_values.append(adj.get_current_value()) - print("Initial value of %s : %g" % (adj.name, tv)) + + - self.run_callbacks_start_scan() + self._have_run_callbacks_start_scan = False + def _get_names(self, elements): + """Get the names of the elements.""" + names = [] + for el in elements: + if hasattr(el, "alias"): + names.append(el.alias.get_full_name()) + elif hasattr(el, "name"): + names.append(el.name) + else: + names.append("unknown") + return names def run_callbacks_start_scan(self): if self.callbacks_start_scan: for caller in self.callbacks_start_scan: @@ -133,6 +166,21 @@ class StepScan: if hasattr(ctr, "callbacks_start_step") and ctr.callbacks_start_step: for tcb in ctr.callbacks_start_step: tcb(self, **self.callbacks_kwargs) + def run_callbacks_step_counting(self): + if self.callbacks_step_counting: + for caller in self.callbacks_step_counting: + caller(self, **self.callbacks_kwargs) + for ctr in self.counters: + if hasattr(ctr, "callbacks_step_counting") and ctr.callbacks_step_counting: + for tcb in ctr.callbacks_step_counting: + tcb(self, **self.callbacks_kwargs) + def has_callbacks_step_counting(self): + if self.callbacks_step_counting: + return True + for ctr in self.counters: + if hasattr(ctr, "callbacks_step_counting") and ctr.callbacks_step_counting: + return True + return False def run_callbacks_end_step(self): if self.callbacks_end_step: for caller in self.callbacks_end_step: @@ -160,16 +208,16 @@ class StepScan: # return fina def do_next_step(self, step_info=None, verbose=True): - + self._current_step_ok = True t_step_start = time() self.run_callbacks_start_step() dt_callbacks_step_start = time()-t_step_start - if not len(self.values_todo) > 0: + if not len(self._values_todo) > 0: return False - self.values_current_step = self.values_todo[0] - statstr = "Step %d of %d" % (self.next_step + 1, len(self.values_todo) + len(self.values_done)) + self.values_current_step = self._values_todo[0] + statstr = "Step %d of %d" % (self.next_step + 1, len(self._values_todo) + len(self._values_done)) # fina = self.get_filename(self.nextStep) @@ -182,7 +230,7 @@ class StepScan: dt_adj = time()-t_adj_start # settling - sleep(self.settling_time) + sleep(self._settling_time) # counters t_ctr_start = time() @@ -206,44 +254,40 @@ class StepScan: statstr += ' ; Ctrs ' - acs = [] - for ctr in self.counters: - # if isinstance(ctr, Daq): - # acq_pars = { - # "scan_info": { - # "scan_name": self.description, - # "scan_values": values_step, - # "scan_readbacks": readbacks_step, - # "scan_step_info": { - # "step_number": self.nextStep + 1, - # }, - # "name": [adj.name for adj in self.adjustables], - # "expected_total_number_of_steps": len(self.values_todo) - # + len(self.values_done), - # }, - # "run_number": self.run_number, - # "user_tag": self.fina, - # } - # acq = ctr.acquire( - # file_name=fina, Npulses=self.pulses_per_step[0], acq_pars=acq_pars - # ) - # else: - acq = ctr.acquire(scan=self, Npulses=self.pulses_per_step[0]) - acs.append(acq) - try: - if hasattr(ctr, "name"): - statstr += f"{ctr.name}, " - except: - pass - filenames = [] - for ta in acs: - ta.wait() - filenames.extend(ta.file_names) + if not self.has_callbacks_step_counting(): + acs = [] + for ctr in self.counters: + acq = ctr.acquire(scan=self, Npulses=self.pulses_per_step[0]) + acs.append(acq) + try: + if hasattr(ctr, "name"): + statstr += f"{ctr.name}, " + except: + pass + filenames = [] + for ta in acs: + ta.wait() + filenames.extend(ta.file_names) + else: + acs = [] + for ctr in self.counters: + ctr.start(scan=self) + try: + if hasattr(ctr, "name"): + statstr += f"{ctr.name}, " + except: + pass + self.run_callbacks_step_counting() + + filenames = [] + for ctr in self.counters: + resp = ctr.stop(scan=self) + filenames.extend(resp["files"]) statstr = statstr[:-2] + ' done.' print(statstr, end='\n') dt_ctr = time() - t_ctr_start - sleep(.003) + sleep(.003) # from display debugging, maybe unnecessary. @@ -253,10 +297,10 @@ class StepScan: # if not self.checker.stop_and_analyze(): # return True if callable(step_info): - tstepinfo = step_info() + tstepinfo = step_info.get_current_value() else: tstepinfo = {} - self.values_done.append(self.values_todo.pop(0)) + self._values_done.append(self._values_todo.pop(0)) self.pulses_done.append(self.pulses_per_step.pop(0)) self.readbacks.append(self.readbacks_current_step) @@ -271,14 +315,20 @@ class StepScan: "callbacks_step_end": dt_callbacks_step_end, } + gridspecs = self.grid_specs.get_current_value() + if gridspecs: + tstepinfo['grid_index'] = gridspecs['index_plan'][self.next_step] + self.appendScanInfo( self.values_current_step, self.readbacks_current_step, step_files=filenames, step_info=tstepinfo ) # self.writeScanInfo() - self.next_step += 1 - - return True + if self._current_step_ok: + self.next_step += 1 + return True + else: + return False def appendScanInfo( self, values_step, readbacks_step, step_files=None, step_info=None @@ -301,6 +351,11 @@ class StepScan: if kws: kws_all.update(set(kws)) print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_step_counting: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) for cb in self.callbacks_end_step: kws = foo_get_kwargs(cb) if kws: @@ -324,6 +379,12 @@ class StepScan: if kws: kws_all.update(set(kws)) print(cb.__name__, "has keywords:", kws) + if hasattr(ctr, "callbacks_step_counting"): + for cb in ctr.callbacks_step_counting: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) if hasattr(ctr, "callbacks_end_step"): for cb in ctr.callbacks_end_step: kws = foo_get_kwargs(cb) @@ -365,8 +426,11 @@ class StepScan: f.truncate() def scan_all(self, step_info=None): + if not self._have_run_callbacks_start_scan: + self.run_callbacks_start_scan() + self._have_run_callbacks_start_scan = True done = False - steps_remaining = len(self.values_todo) + steps_remaining = len(self._values_todo) with Progress() as self._progress: pr_task = self._progress.add_task( "[green]Scanning...", total=steps_remaining @@ -391,6 +455,7 @@ class StepScan: print("Changing back to value(s) before scan.") for ch in chs: ch.wait() + elif self.return_at_end == "timeout": timeout = 10 try: @@ -424,12 +489,15 @@ class StepScan: def changeToInitialValues(self): c = [] - for adj, iv in zip(self.adjustables, self.initial_values): + for adj, iv in zip(self.adjustables, self.initial_values()): c.append(adj.set_target_value(iv)) return c -class Scans: + + + +class Scans(Assembly): """Convenience class to initialte typical scans with some default parameters the base StepScan and others.""" def __init__( self, @@ -440,14 +508,19 @@ class Scans: # scan_directories=False, callbacks_start_scan=[], callbacks_start_step=[], + callbacks_step_counting=[], callbacks_end_step=[], callbacks_end_scan=[], # run_table=None, elog=None, + name='scans', ): + super().__init__(name=name) # self._run_table = run_table self.callbacks_start_scan = callbacks_start_scan self.callbacks_start_step = callbacks_start_step + self.callbacks_step_counting = callbacks_step_counting + self.callbacks_end_step = callbacks_end_step self.callbacks_end_scan = callbacks_end_scan # self.data_base_dir = data_base_dir @@ -476,9 +549,79 @@ class Scans: # self.scan_info_dir = scan_info_dir # self.filename_generator = RunFilenameGenerator(self.scan_info_dir) self._default_counters = default_counters + self._append(DetectorGet, self._get_counter_names, name='default_counters_names') + self._append(DetectorMemory, 'none since session start', name='acquiring_scan') # self.checker = checker # self._scan_directories = scan_directories self._elog = elog + + + def _get_counter_names(self): + """Get the names of the default counters.""" + return [tc.name for tc in self._default_counters] + + def get_callback_keywords(self): + kws_all = set([]) + for cb in self.callbacks_start_scan: + kws = foo_get_kwargs(cb) + + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_start_step: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_step_counting: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_end_step: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for cb in self.callbacks_end_scan: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + for ctr in self._default_counters: + if hasattr(ctr, "callbacks_start_scan"): + for cb in ctr.callbacks_start_scan: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + if hasattr(ctr, "callbacks_start_step"): + for cb in ctr.callbacks_start_step: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + if hasattr(ctr, "callbacks_step_counting"): + for cb in ctr.callbacks_step_counting: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + if hasattr(ctr, "callbacks_end_step"): + for cb in ctr.callbacks_end_step: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + if hasattr(ctr, "callbacks_end_scan"): + for cb in ctr.callbacks_end_scan: + kws = foo_get_kwargs(cb) + if kws: + kws_all.update(set(kws)) + print(cb.__name__, "has keywords:", kws) + + return kws_all + def acquire( self, @@ -488,13 +631,11 @@ class Scans: counters=[], start_immediately=True, settling_time=0, - step_info=None, return_at_end=True, - # checker="default", + step_info=None, **kwargs_callbacks, ): adjustable = DummyAdjustable() - positions = list(range(N_repetitions)) values = [[tp] for tp in positions] # file_name = self.filename_generator.get_nextrun_filename(file_name) @@ -516,11 +657,13 @@ class Scans: callbacks_end_scan=self.callbacks_end_scan, elog=self._elog, return_at_end=return_at_end, + name='acquiring_scan', **kwargs_callbacks, ) + self._append(s,name='acquiring_scan', overwrite=True) if start_immediately: s.scan_all(step_info=step_info) - return s + # return s def ascan( self, @@ -529,70 +672,25 @@ class Scans: end_pos, N_intervals, N_pulses, - file_name="", - counters=[], - checker="default", - start_immediately=True, - step_info=None, - return_at_end="question", - settling_time=0, - **kwargs_callbacks, - ): - positions = np.linspace(start_pos, end_pos, N_intervals + 1) - values = [[tp] for tp in positions] - file_name = self.filename_generator.get_nextrun_filename(file_name) - run_number = self.filename_generator.get_nextrun_number() - if not counters: - counters = self._default_counters - if checker == "default": - checker = self.checker - s = StepScan( - [adjustable], - values, - counters, - file_name, - Npulses=N_pulses, - basepath=self.data_base_dir, - scan_info_dir=self.scan_info_dir, - checker=self.checker, - settling_time=settling_time, - scan_directories=self._scan_directories, - return_at_end=return_at_end, - callbacks_start_scan=self.callbacks_start_scan, - callbacks_start_step=self.callbacks_start_step, - callbacks_end_step=self.callbacks_end_step, - callbacks_end_scan=self.callbacks_end_scan, - run_table=self._run_table, - elog=self._elog, - run_number=run_number, - **kwargs_callbacks, - ) - if start_immediately: - s.scan_all(step_info=step_info) - return s - - def ascan_position_list( - self, - adjustable, - position_list, - N_pulses, description="", counters=[], - # checker="default", start_immediately=True, + return_at_end="timeout", settling_time=0, step_info=None, - return_at_end="question", **kwargs_callbacks, ): - positions = position_list + + if type(N_intervals) is float: + print('Interval size defined as float, interpreting as interval size.') + positions = np.arange(start_pos, N_intervals, end_pos) + elif type(N_intervals) is int: + print('Interval size defined as int, interpreting as number of intervals.') + positions = np.linspace(start_pos, end_pos, N_intervals + 1) + values = [[tp] for tp in positions] - # description = self.filename_generator.get_nextrun_filename(description) - # run_number = self.filename_generator.get_nextrun_number() if not counters: counters = self._default_counters - # if checker == "default": - # checker = self.checker s = StepScan( [adjustable], values, @@ -606,11 +704,55 @@ class Scans: callbacks_end_step=self.callbacks_end_step, callbacks_end_scan=self.callbacks_end_scan, elog=self._elog, + name='acquiring_scan', **kwargs_callbacks, ) + self._append(s,name='acquiring_scan', overwrite=True) if start_immediately: s.scan_all(step_info=step_info) - return s + # return s + + def ascan_position_list( + self, + adjustable, + position_list, + N_pulses, + description="", + counters=[], + # checker="default", + start_immediately=True, + settling_time=0, + step_info=None, + return_at_end="timeout", + name='acquiring_scan', + **kwargs_callbacks, + ): + positions = position_list + values = [[tp] for tp in positions] + + if not counters: + counters = self._default_counters + + s = StepScan( + [adjustable], + values, + counters=counters, + description=description, + Npulses=N_pulses, + settling_time=settling_time, + return_at_end=return_at_end, + callbacks_start_scan=self.callbacks_start_scan, + callbacks_start_step=self.callbacks_start_step, + callbacks_end_step=self.callbacks_end_step, + callbacks_end_scan=self.callbacks_end_scan, + elog=self._elog, + name='acquiring_scan', + **kwargs_callbacks, + ) + self._append(s,name='acquiring_scan', overwrite=True) + if start_immediately: + s.scan_all(step_info=step_info) + # return s def dscan( @@ -620,49 +762,112 @@ class Scans: end_pos, N_intervals, N_pulses, - file_name="", + description="", counters=[], - checker="default", start_immediately=True, settling_time=0, step_info=None, - return_at_end="question", + return_at_end="timeout", **kwargs_callbacks, ): - positions = np.linspace(start_pos, end_pos, N_intervals + 1) + """Differential scan, i.e. the adjustable is moved to the start position and then moved in steps of the interval size.""" + + if type(N_intervals) is float: + print('Interval size defined as float, interpreting as interval size.') + positions = np.arange(start_pos, N_intervals, end_pos) + elif type(N_intervals) is int: + print('Interval size defined as int, interpreting as number of intervals.') + positions = np.linspace(start_pos, end_pos, N_intervals + 1) current = adjustable.get_current_value() values = [[tp + current] for tp in positions] - file_name = self.filename_generator.get_nextrun_filename(file_name) - run_number = self.filename_generator.get_nextrun_number() + + if not counters: counters = self._default_counters - if checker == "default": - checker = self.checker + s = StepScan( [adjustable], values, counters, - file_name, Npulses=N_pulses, - basepath=self.data_base_dir, - scan_info_dir=self.scan_info_dir, - checker=self.checker, - scan_directories=self._scan_directories, + description=description, return_at_end=return_at_end, settling_time=settling_time, callbacks_start_scan=self.callbacks_start_scan, callbacks_start_step=self.callbacks_start_step, callbacks_end_step=self.callbacks_end_step, callbacks_end_scan=self.callbacks_end_scan, - run_table=self._run_table, elog=self._elog, - run_number=run_number, + name='acquiring_scan', **kwargs_callbacks, ) + self._append(s,name='acquiring_scan', overwrite=True, status=True) if start_immediately: s.scan_all(step_info=step_info) - return s + # return s + + def snakescan( + self, + adjustable_slow, + step_interval, + Nrows, + adjustable_fast, + interval, + description="", + counters=[], + start_immediately=True, + settling_time=0, + step_info=None, + return_at_end="timeout", + **kwargs_callbacks, + ): + + + adj_slow_start = adjustable_slow.get_current_value() + adj_fast_start = adjustable_fast.get_current_value() + print('Snakescan is relative, starting from here: %s, %s' % (adj_slow_start, adj_fast_start)) + + start_positions = [ + [adj_slow_start + step_interval * i, adj_fast_start + (i%2)*interval ] + for i in range(Nrows)] + + def counting_function(scan): + cv = adjustable_fast.get_current_value() + print(cv) + if abs(cv - adj_fast_start) < abs(cv - adj_fast_start - interval): + print('moving to interval') + adjustable_fast.set_target_value(adj_fast_start +interval).wait() + else: + print('moving back') + adjustable_fast.set_target_value(adj_fast_start).wait() + + + if not counters: + counters = self._default_counters + + s = StepScan( + [adjustable_slow, adjustable_fast], + start_positions, + counters, + Npulses=1, + description=description, + return_at_end=return_at_end, + settling_time=settling_time, + callbacks_start_scan=self.callbacks_start_scan, + callbacks_start_step=self.callbacks_start_step, + callbacks_step_counting=[counting_function], + callbacks_end_step=self.callbacks_end_step, + callbacks_end_scan=self.callbacks_end_scan, + elog=self._elog, + name='acquiring_scan', + **kwargs_callbacks, + ) + self._append(s,name='acquiring_scan', overwrite=True) + if start_immediately: + s.scan_all(step_info=step_info) + # return s + def a2scan( self, adjustable0, @@ -685,7 +890,7 @@ class Scans: positions1 = np.linspace(start1_pos, end1_pos, N_intervals + 1) values = [[tp0, tp1] for tp0, tp1 in zip(positions0, positions1)] if not counters: - counters = self._default_counters + counters = self.default_counters.get_current_value() if checker == "default": checker = self.checker s = StepScan( @@ -705,78 +910,86 @@ class Scans: run_table=self._run_table, elog=self._elog, return_at_end=return_at_end, + name='acquiring_scan', **kwargs_callbacks, ) + self._append(s,name='acquiring_scan', overwrite=True) if start_immediately: s.scan_all(step_info=step_info) - return s + # return s + + def meshscan( + self, + *adj_specs, + scanning_order='last_fastest', + N_pulses=None, + description="", + counters=[], + start_immediately=True, + return_at_end="timeout", + settling_time=0, + step_info=None, + **kwargs_callbacks, + ): + """ + Mesh scan, i.e. a scan in multiple dimensions, where the last adjustable is moved first. + The scanning order can be changed by setting the `scanning_order` parameter. + """ + adjustables = [] + positions = [] + for adj_spec in adj_specs: + adj = adj_spec[0] + spec = adj_spec[1:] + if isinstance(adj, Adjustable): + adjustables.append(adj) + positions.append(interpret_step_specification(spec)) + + shape = [len(tp) for tp in positions] + + if scanning_order=='last_fastest': + index_plan = list(product(*[range(n) for n in shape])) + elif scanning_order=='fist_fastst': + index_plan = [tc[::-1] for tc in product(*[range(n) for n in shape][::-1])] + + values = [] + for ixs in index_plan: + values.append([tp[ti] for ti,tp in zip(ixs,positions)]) + + gridspecs = { + 'shape' : shape, + 'positions':positions, + 'index_plan':index_plan, + } + + if not counters: + counters = self._default_counters + + s = StepScan( + adjustables, + values, + counters=counters, + Npulses=N_pulses, + description=description, + return_at_end=return_at_end, + settling_time=settling_time, + callbacks_start_scan=self.callbacks_start_scan, + callbacks_start_step=self.callbacks_start_step, + callbacks_end_step=self.callbacks_end_step, + callbacks_end_scan=self.callbacks_end_scan, + elog=self._elog, + gridspecs=gridspecs, + name='acquiring_scan', + **kwargs_callbacks, + ) + + self._append(s,name='acquiring_scan', overwrite=True) + if start_immediately: + s.scan_all(step_info=step_info) + + - - - - - # def rscan(self, *args, **kwargs): - # print( - # "Warning: This is not implemented, should be reflectivity scan. \n for relative/differential scan please use dscan ." - # ) - # # return self.rscan(*args, **kwargs) - - - - # def a2scanList( - # self, - # adjustable0, - # start0_pos, - # end0_pos, - # adjustable1, - # start1_pos, - # end1_pos, - # N_intervals, - # N_pulses, - # file_name=None, - # counters=[], - # checker="default", - # start_immediately=True, - # step_info=None, - # return_at_end="question", - # **kwargs_callbacks, - # ): - # positions0 = np.linspace(start0_pos, end0_pos, N_intervals + 1) - # positions1 = np.linspace(start1_pos, end1_pos, N_intervals + 1) - # # self.prefix - # # + f"{runno:{self.Ndigits}0d}" - # # + self.separator - # # + "*." - # # + self.suffix - # values = [[tp0, tp1] for tp0, tp1 in zip(positions0, positions1)] - # if not counters: - # counters = self._default_counters - # if checker == "default": - # checker = self.checker - # s = Scan( - # [adjustable0, adjustable1], - # values, - # self.counters, - # file_name, - # Npulses=N_pulses, - # basepath=self.data_base_dir, - # scan_info_dir=self.scan_info_dir, - # checker=self.checker, - # scan_directories=self._scan_directories, - # return_at_end=return_at_end, - # callbacks_start_scan=self.callbacks_start_scan, - # callbacks_start_step=self.callbacks_start_step, - # callbacks_end_step=self.callbacks_end_step, - # callbacks_end_scan=self.callbacks_end_scan, - # run_table=self._run_table, - # elog=self._elog, - # **kwargs_callbacks, - # ) - # if start_immediately: - # s.scan_all(step_info=step_info) - # return s class RunFilenameGenerator: @@ -833,3 +1046,26 @@ class RunFilenameGenerator: + "." + self.suffix ) + + + +def interpret_step_specification(spec): + # normal linear scan + if len(spec) == 3 and all(isinstance(ta,Number) for ta in spec): + start_pos, end_pos, N_intervals = spec + if type(N_intervals) is float: + print('Interval size defined as float, interpreting as interval size.') + positions = np.arange(start_pos, N_intervals, end_pos) + elif type(N_intervals) is int: + print('Interval size defined as int, interpreting as number of intervals.') + positions = np.linspace(start_pos, end_pos, N_intervals + 1) + return positions + elif len(spec) == 1 and np.iterable(spec[0]): + if type(spec[0]) is str: + raise Exception("Step position specification is a string, interpreting as position list!") + positions = spec[0] + return positions + else: + raise Exception( + "Step position specification is not understood, should be 3 numbers or a list of positions." + ) \ No newline at end of file diff --git a/eco/bernina/bernina.py b/eco/bernina/bernina.py index e722beb..f8e8b8a 100644 --- a/eco/bernina/bernina.py +++ b/eco/bernina/bernina.py @@ -1,6 +1,8 @@ import json from pathlib import Path from threading import Thread + +import zmq import eco from eco.acquisition.scan import NumpyEncoder from eco.devices_general.digitizers import DigitizerIoxosBoxcarChannel @@ -8,6 +10,7 @@ from eco.devices_general.powersockets import MpodModule from eco.devices_general.wago import AnalogOutput from eco.elements.adjustable import AdjustableFS from eco.elements.adjustable import AdjustableVirtual +from eco.elements.detector import DetectorGet from eco.loptics.bernina_experiment import DelayCompensation from eco.devices_general.cameras_swissfel import CameraBasler from epics import PV @@ -34,7 +37,7 @@ path_aliases = PathAlias() sys.path.append("/sf/bernina/config/src/python/bernina_analysis") namespace = Namespace( - name="bernina", root_module=__name__, alias_namespace=NamespaceCollection().bernina + name="bernina", root_module=__name__, alias_namespace=NamespaceCollection().bernina, required_names_directory="/sf/bernina/config/eco/required_bernina_names.json" ) namespace.alias_namespace.data = [] @@ -43,10 +46,15 @@ _config_bernina_dict = AdjustableFS( "/sf/bernina/config/eco/configuration/bernina_config.json", name="_config_bernina_dict", ) -from eco.elements.adj_obj import AdjustableObject +from eco.elements.adj_obj import AdjustableObject, DetectorObject namespace.append_obj(AdjustableObject, _config_bernina_dict, name="config_bernina") + + + + + namespace.append_obj( "RunData", config_bernina.pgroup, @@ -95,13 +103,6 @@ namespace.append_obj( module_name="eco.utilities.elog", lazy=True, ) -# namespace.append_obj( -# "Elog", -# "https://elog-gfa.psi.ch/Bernina", -# screenshot_directory="/tmp", -# name="elog", -# module_name="eco.utilities.elog", -# eco.defaults.ELOG = elog namespace.append_obj( @@ -171,15 +172,8 @@ for tk in components: namespace.append_obj_from_config(tk, lazy=True) -# Adding stuff the "new" way +# Adding all beamline components the "new" way -# namespace.append_obj( -# "EventReceiver", -# "", -# lazy=True, -# name="cam_north", -# module_name="eco.devices_general.cameras_ptz", -# ) namespace.append_obj( "BerninaVacuum", @@ -285,7 +279,6 @@ namespace.append_obj( lazy=True, ) - namespace.append_obj( "Bernina_XEYE", zoomstage_pv=config_bernina.xeye.zoomstage_pv._value, @@ -297,7 +290,6 @@ namespace.append_obj( module_name="eco.xdiagnostics.profile_monitors", ) - ## beamline components ## namespace.append_obj( @@ -314,35 +306,6 @@ namespace.append_obj( lazy=True, ) -# namespace.append_obj( -# "Pprm", -# "SARFE10-PPRM064", -# "SARFE10-PPRM064", -# name= "prof_fe", -# # "z_und": 64, -# # "desc": "Profile monitor after Front End", -# module_name="eco.xdiagnostics.profile_monitors", -# ) -# namespace.append_obj( -# "Pprm", -# "SAROP11-PPRM066", -# "SAROP11-PPRM066", -# name= "prof_mirr_alv1", -# # "z_und": 66, -# # "desc": "Profile monitor after Alvra Mirror 1", -# module_name="eco.xdiagnostics.profile_monitors", -# ) -# namespace.append_obj( -# "Pprm", -# "SAROP21-PPRM094", -# "SAROP21-PPRM094", -# name= "prof_mirr1", -# # "z_und": 94, -# # "desc": "Profile monitor after Mirror 1", -# module_name="eco.xdiagnostics.profile_monitors", -# ) - - namespace.append_obj( "OffsetMirrorsBernina", name="offset", @@ -357,50 +320,17 @@ namespace.append_obj( module_name="eco.xoptics.slits", lazy=True, ) -# namespace.append_obj( -# "SolidTargetDetectorPBPS", -# "SAROP21-PBPS103", -# diode_channels_raw={ -# "up": "SAROP21-CVME-PBPS1:Lnk9Ch3-DATA-SUM", -# "down": "SAROP21-CVME-PBPS1:Lnk9Ch4-DATA-SUM", -# "left": "SAROP21-CVME-PBPS1:Lnk9Ch2-DATA-SUM", -# "right": "SAROP21-CVME-PBPS1:Lnk9Ch1-DATA-SUM", -# }, -# fe_digi_channels={ -# "left": "SAROP21-CVME-PBPS1:Lnk9Ch2", -# "right": "SAROP21-CVME-PBPS1:Lnk9Ch1", -# "up": "SAROP21-CVME-PBPS1:Lnk9Ch3", -# "down": "SAROP21-CVME-PBPS1:Lnk9Ch4", -# }, -# name="mon_mono_old", -# module_name="eco.xdiagnostics.intensity_monitors", -# lazy=True, -# ) namespace.append_obj( "SolidTargetDetectorPBPS", "SAROP21-PBPS103", use_calibration=False, - # channel_xpos="SLAAR21-LTIM01-EVR0:CALCX", - # channel_ypos="SLAAR21-LTIM01-EVR0:CALCY", - # channel_intensity="SLAAR21-LTIM01-EVR0:CALCI", - # diode_channels_raw={ - # "up": "SLAAR21-LSCP1-FNS:CH6:VAL_GET", - # "down": "SLAAR21-LSCP1-FNS:CH7:VAL_GET", - # "left": "SLAAR21-LSCP1-FNS:CH4:VAL_GET", - # "right": "SLAAR21-LSCP1-FNS:CH5:VAL_GET", - # }, diode_channels_raw={ "up": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD1", "down": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD2", "left": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD0", "right": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD3", }, - # calibration_records={ - # "intensity": "SLAAR21-LTIM01-EVR0:CALCI", - # "xpos": "SLAAR21-LTIM01-EVR0:CALCX", - # "ypos": "SLAAR21-LTIM01-EVR0:CALCY", - # }, name="mon_mono", module_name="eco.xdiagnostics.intensity_monitors", pipeline_computation="SAROP21-PBPS103_proc", @@ -511,53 +441,23 @@ namespace.append_obj( ) -# namespace.append_obj( -# "SolidTargetDetectorPBPS", -# "SAROP21-PBPS133", -# channel_xpos="SLAAR21-LTIM01-EVR0:CALCX", -# channel_ypos="SLAAR21-LTIM01-EVR0:CALCY", -# channel_intensity="SLAAR21-LTIM01-EVR0:CALCI", -# diode_channels_raw={ -# "up": "SLAAR21-LSCP1-FNS:CH6:VAL_GET", -# "down": "SLAAR21-LSCP1-FNS:CH7:VAL_GET", -# "left": "SLAAR21-LSCP1-FNS:CH4:VAL_GET", -# "right": "SLAAR21-LSCP1-FNS:CH5:VAL_GET", -# }, -# calibration_records={ -# "intensity": "SLAAR21-LTIM01-EVR0:CALCI", -# "xpos": "SLAAR21-LTIM01-EVR0:CALCX", -# "ypos": "SLAAR21-LTIM01-EVR0:CALCY", -# }, -# name="mon_opt_old", -# module_name="eco.xdiagnostics.intensity_monitors", -# lazy=True, -# ) - namespace.append_obj( "SolidTargetDetectorPBPS", "SAROP21-PBPS133", use_calibration=False, - # channel_xpos="SLAAR21-LTIM01-EVR0:CALCX", - # channel_ypos="SLAAR21-LTIM01-EVR0:CALCY", - # channel_intensity="SLAAR21-LTIM01-EVR0:CALCI", diode_channels_raw={ "up": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD1", "down": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD2", "left": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD0", "right": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD3", }, - # calibration_records={ - # "intensity": "SLAAR21-LTIM01-EVR0:CALCI", - # "xpos": "SLAAR21-LTIM01-EVR0:CALCX", - # "ypos": "SLAAR21-LTIM01-EVR0:CALCY", - # }, + name="mon_opt", module_name="eco.xdiagnostics.intensity_monitors", pipeline_computation="SAROP21-PBPS133_proc", lazy=True, ) - namespace.append_obj( "Pprm", "SARFE10-PPRM064", @@ -688,11 +588,6 @@ namespace.append_obj( # ) -# namespace.append_obj('Daq', instrument= "bernina",pgroup= config_berninamesp["pgroup"], channels_JF=channels_JF, channels_BS=channels_BS,channels_BSCAM=channels_BSCAM,channels_CA=channels_CA,pulse_id_adj="SLAAR21-LTIM01-EVR0:RX-PULSEID",event_master=event_system.event_master,detectors_event_code=50,name='daq',module_name='eco.acquisition.daq_client') - -# namespace.append_obj('Scans',data_base_dir="scan_data",scan_info_dir=f"/sf/bernina/data/{config_berninamesp['pgroup']}/res/scan_info", -# default_counters=[daq],checker=checker,scan_directories=True,run_table=run_table,elog=elog, -# module_name = "eco.acquisition.scan",name="scans") namespace.append_obj( "ProfKbBernina", module_name="eco.xdiagnostics.profile_monitors", @@ -708,6 +603,7 @@ namespace.append_obj( name="tt_kb", lazy=True, ) + # namespace.append_obj( # "TimetoolSpatial", # module_name="eco.timing.timing_diag", @@ -755,7 +651,6 @@ namespace.append_obj( name="evr_hutch_laser", module_name="eco.timing.event_timing_new_new", lazy=True, - # lazy=False, ) namespace.append_obj( "EventReceiver", @@ -767,7 +662,6 @@ namespace.append_obj( name="evr_camserver72", module_name="eco.timing.event_timing_new_new", lazy=True, - # lazy=False, ) namespace.append_obj( "EventReceiver", @@ -779,7 +673,6 @@ namespace.append_obj( name="evr_camserver73", module_name="eco.timing.event_timing_new_new", lazy=True, - # lazy=False, ) namespace.append_obj( "EventReceiver", @@ -791,7 +684,6 @@ namespace.append_obj( name="evr_camserver74", module_name="eco.timing.event_timing_new_new", lazy=True, - # lazy=False, ) namespace.append_obj( "EventReceiver", @@ -803,7 +695,6 @@ namespace.append_obj( name="evr_camserver83", module_name="eco.timing.event_timing_new_new", lazy=True, - # lazy=False, ) namespace.append_obj( "EventReceiver", @@ -815,7 +706,6 @@ namespace.append_obj( name="evr_camserver84", module_name="eco.timing.event_timing_new_new", lazy=True, - # lazy=False, ) namespace.append_obj( "EventReceiver", @@ -827,7 +717,6 @@ namespace.append_obj( name="evr_camserver85", module_name="eco.timing.event_timing_new_new", lazy=True, - # lazy=False, ) namespace.append_obj( "DigitizerKeysight", @@ -900,14 +789,6 @@ namespace.append_obj( module_name="eco.devices_general.wago", ) -# namespace.append_obj( -# "AnalogInput", -# "SARES20-CWAG-GPS01:ADC08", -# lazy=True, -# name="oxygen_sensor", -# module_name="eco.devices_general.wago", -# ) - namespace.append_obj( "GudeStrip", "SARES20-CPPS-01", @@ -1018,6 +899,15 @@ namespace.append_obj( lazy=True, ) +namespace.append_obj( + "Att_usd", + name="att_usd", + module_name="eco.xoptics.att_usd", + xp=xp, + lazy=True, +) + + ### channelsfor daq ### namespace.append_obj( "AdjustableFS", @@ -1055,64 +945,6 @@ namespace.append_obj( name="channels_CA_epicsdaq", ) -namespace.append_obj( - "Att_usd", - name="att_usd", - module_name="eco.xoptics.att_usd", - xp=xp, - lazy=True, -) - -# namespace.append_obj( -# "Jungfrau", -# "JF13T01V01", -# name="det_invac", -# pgroup_adj=config_bernina.pgroup, -# module_name="eco.detector.jungfrau", -# config_adj=config_JFs, -# lazy=True,) - -# namespace.append_obj( -# "Jungfrau", -# "JF04T01V01", -# name="det_rowland", -# pgroup_adj=config_bernina.pgroup, -# module_name="eco.detector.jungfrau", -# config_adj=config_JFs, -# lazy=True, -# ) - -namespace.append_obj( - "Jungfrau", - "JF03T01V02", - name="det_i0", - pgroup_adj=config_bernina.pgroup, - module_name="eco.detector.jungfrau", - config_adj=config_JFs, - lazy=True, -) - -# namespace.append_obj( -# "Jungfrau", -# "JF14T01V01", -# name="det_diff", -# pgroup_adj=config_bernina.pgroup, -# module_name="eco.detector.jungfrau", -# config_adj=config_JFs, -# lazy=True, -# ) - -# namespace.append_obj( -# "DetectorRobot", -# JF_detector_id="JF07T32V02", -# JF_detector_name="det_diff", -# pgroup_adj=config_bernina.pgroup, -# config_adj=config_JFs, -# module_name="eco.endstations.bernina_robot", -# lazy=True, -# name="robot", -# ) - namespace.append_obj( "MpodModule", "SARES21-PS7071", @@ -1135,662 +967,6 @@ namespace.append_obj( module_name="eco.devices_general.powersockets", ) -# -# namespace.append_obj( -# "MpodModule", -# "SARES21-CPCL-PS7071", -# [1,2,3,4], -# ['ch1','ch2','ch3','ch4'], -# module_string='HV_EHS_3', -# name="power_HV_patch1", -# module_name="eco.devices_general.powersockets", -# ) -# -# namespace.append_obj( -# "MpodModule", -# "SARES21-CPCL-PS7071", -# [5,6,7,8], -# ['ch1','ch2','ch3','ch4'], -# module_string='HV_EHS_3', -# name="power_HV_patch2", -# module_name="eco.devices_general.powersockets", -# ) - -### draft new epics daq ### -namespace.append_obj( - "EpicsDaq", - channel_list=channels_CA_epicsdaq, - name="daq_epics_local", - module_name="eco.acquisition.epics_data", - lazy=True, -) -### old epics daq ### -# namespace.append_obj( -# "ChannelList", -# name="epics_channel_list", -# file_name="/sf/bernina/config/channel_lists/default_channel_list_epics", -# module_name="eco.utilities.config", -# ) - -# namespace.append_obj( -# "Epicstools", -# name="epics_daq", -# channel_list=epics_channel_list, -# default_file_path=f"/sf/bernina/data/{config_berninamesp['pgroup']}/res/epics_daq/", -# module_name="eco.acquisition.epics_data", -# ) - - -## TODO pgroup non adjustable/dynamically changeable! -namespace.append_obj( - "Scans", - name="scans_epics", - module_name="eco.acquisition.scan", - data_base_dir=f"{config_bernina.pgroup()}/scan_data", - scan_info_dir=f"{daq_epics_local.default_file_path()}/{config_bernina.pgroup()}/scan_info", - default_counters=[daq_epics_local], - checker=None, - scan_directories=True, - run_table=None, - lazy=True, -) -# -# -##### standard DAQ ####### -namespace.append_obj( - "Daq", - instrument="bernina", - pgroup=config_bernina.pgroup, - channels_JF=channels_JF, - channels_BS=channels_BS, - channels_BSCAM=channels_BSCAM, - channels_CA=channels_CA, - config_JFs=config_JFs, - pulse_id_adj="SLAAR21-LTIM01-EVR0:RX-PULSEID", - event_master=event_master, - detectors_event_code=50, - rate_multiplicator="auto", - name="daq", - module_name="eco.acquisition.daq_client", - lazy=True, -) -namespace.append_obj( - "Daq", - instrument="bernina", - broker_address="http://sf-daq-1:10002", - pgroup=config_bernina.pgroup, - channels_JF=channels_JF, - channels_BS=channels_BS, - channels_BSCAM=channels_BSCAM, - channels_CA=channels_CA, - config_JFs=config_JFs, - pulse_id_adj="SLAAR21-LTIM01-EVR0:RX-PULSEID", - event_master=event_master, - detectors_event_code=50, - name="daq_dev", - module_name="eco.acquisition.daq_client", - lazy=True, -) - - -#TODO: need to check if the value property actually works here for the pgroup in the run table to make is dynamic! -namespace.append_obj( - "Run_Table2", - name="run_table", - module_name="eco.utilities.runtable", - exp_id=config_bernina.pgroup._value, - exp_path=f"/sf/bernina/data/{config_bernina.pgroup._value}/res/run_table/", - devices="eco.bernina", - keydf_fname="/sf/bernina/config/src/python/gspread/gspread_keys.pkl", - cred_fname="/sf/bernina/config/src/python/gspread/pandas_push", - gsheet_key_path="/sf/bernina/config/eco/reference_values/run_table_gsheet_keys", - lazy=True, - parse=True, # <-- set this to False to avoid parsing and only add the status information to the runtable -) - - -def _wait_for_tasks(scan, **kwargs): - print("checking remaining tasks from previous scan ...") - for task in scan.remaining_tasks: - task.join() - print("... done.") - - -def _append_namesace_status_to_scan( - scan, daq=daq, namespace=namespace, append_status_info=True, **kwargs -): - if not append_status_info: - return - namespace_status = namespace.get_status(base=None) - stat = {"status_run_start": namespace_status} - scan.status = stat - - -def _write_namespace_status_to_scan( - scan, daq=daq, namespace=namespace, append_status_info=True, end_scan=True, **kwargs -): - if not append_status_info: - return - if end_scan: - namespace_status = namespace.get_status(base=None) - scan.status["status_run_end"] = namespace_status - if (not end_scan) and not (len(scan.values_done) == 1): - return - if hasattr(scan, "daq_run_number"): - runno = scan.daq_run_number - else: - runno = daq.get_last_run_number() - pgroup = daq.pgroup - tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/stat_run{runno:04d}") - tmpdir.mkdir(exist_ok=True, parents=True) - try: - tmpdir.chmod(0o775) - except: - pass - - statusfile = tmpdir / Path("status.json") - if not statusfile.exists(): - with open(statusfile, "w") as f: - json.dump(scan.status, f, sort_keys=True, cls=NumpyEncoder, indent=4) - else: - with open(statusfile, "r+") as f: - f.seek(0) - json.dump(scan.status, f, sort_keys=True, cls=NumpyEncoder, indent=4) - f.truncate() - print("Wrote status with seek truncate!") - if not statusfile.group() == statusfile.parent.group(): - shutil.chown(statusfile, group=statusfile.parent.group()) - - response = daq.append_aux( - statusfile.resolve().as_posix(), - pgroup=pgroup, - run_number=runno, - ) - print("####### transfer status #######") - print(response.json()) - print("###############################") - scan.scan_info["scan_parameters"]["status"] = "aux/status.json" - - -def _write_namespace_aliases_to_scan(scan, daq=daq, force=False, **kwargs): - if force or (len(scan.values_done) == 1): - namespace_aliases = namespace.alias.get_all() - if hasattr(scan, "daq_run_number"): - runno = scan.daq_run_number - else: - runno = daq.get_last_run_number() - pgroup = daq.pgroup - tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/aliases_run{runno:04d}") - tmpdir.mkdir(exist_ok=True, parents=True) - try: - tmpdir.chmod(0o775) - except: - pass - aliasfile = tmpdir / Path("aliases.json") - if not Path(aliasfile).exists(): - with open(aliasfile, "w") as f: - json.dump( - namespace_aliases, f, sort_keys=True, cls=NumpyEncoder, indent=4 - ) - else: - with open(aliasfile, "r+") as f: - f.seek(0) - json.dump( - namespace_aliases, f, sort_keys=True, cls=NumpyEncoder, indent=4 - ) - f.truncate() - if not aliasfile.group() == aliasfile.parent.group(): - shutil.chown(aliasfile, group=aliasfile.parent.group()) - - scan.remaining_tasks.append( - Thread( - target=daq.append_aux, - args=[aliasfile.resolve().as_posix()], - kwargs=dict(pgroup=pgroup, run_number=runno), - ) - ) - # DEBUG - print( - f"Sending scan_info_rel.json in {Path(aliasfile).parent.stem} to run number {runno}." - ) - scan.remaining_tasks[-1].start() - # response = daq.append_aux( - # aliasfile.resolve().as_posix(), - # pgroup=pgroup, - # run_number=runno, - # ) - print("####### transfer aliases started #######") - # print(response.json()) - # print("################################") - scan.scan_info["scan_parameters"]["aliases"] = "aux/aliases.json" - - -def _message_end_scan(scan, **kwargs): - print(f"Finished run {scan.run_number}.") - if hasattr(scan, "daq_run_number"): - runno_daq_saved = scan.daq_run_number - print(f"daq_run_number is run {runno_daq_saved}.") - - try: - runno = daq.get_last_run_number() - print(f"daq last run number is run {runno}.") - except: - pass - - try: - e = pyttsx3.init() - e.say(f"Finished run {scan.run_number}.") - e.runAndWait() - e.stop() - except: - print("Audio output failed.") - - -# def _copy_scan_info_to_raw(scan, daq=daq): -# run_number = daq.get_last_run_number() -# pgroup = daq.pgroup -# print(f"Copying info file to run {run_number} to the raw directory of {pgroup}.") -# response = daq.append_aux( -# scan.scan_info_filename, pgroup=pgroup, run_number=run_number -# ) -# print(f"Status: {response.json()['status']} Message: {response.json()['message']}") - - -def _create_general_run_info(scan, daq=daq, **kwargs): - with open(scan.scan_info_filename, "r") as f: - si = json.load(f) - - info = {} - # general info, potentially automatically filled - info["general"] = {} - # individual data filled by daq/writers/user through api - info["start"] = {} - info["end"] = {} - info["steps"] = [] - - -def _copy_scan_info_to_raw(scan, daq=daq, **kwargs): - t_start = time.time() - - scan.writeScanInfo() - - # get data that should come later from api or similar. - run_directory = list( - Path(f"/sf/bernina/data/{daq.pgroup}/raw").glob(f"run{scan.run_number:04d}*") - )[0].as_posix() - with open(scan.scan_info_filename, "r") as f: - si = json.load(f) - - # correct some data in there (relative paths for now) - from os.path import relpath - - newfiles = [] - for files in si["scan_files"]: - newfiles.append([relpath(file, run_directory) for file in files]) - - si["scan_files"] = newfiles - - # save temprary file and send then to raw - if hasattr(scan, "daq_run_number"): - runno = scan.daq_run_number - else: - runno = daq.get_last_run_number() - pgroup = daq.pgroup - tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/info_run{runno:04d}") - tmpdir.mkdir(exist_ok=True, parents=True) - try: - tmpdir.chmod(0o775) - except: - pass - scaninfofile = tmpdir / Path("scan_info_rel.json") - if not Path(scaninfofile).exists(): - with open(scaninfofile, "w") as f: - json.dump(si, f, sort_keys=True, cls=NumpyEncoder, indent=4) - else: - with open(scaninfofile, "r+") as f: - f.seek(0) - json.dump(si, f, sort_keys=True, cls=NumpyEncoder, indent=4) - f.truncate() - if not scaninfofile.group() == scaninfofile.parent.group(): - shutil.chown(scaninfofile, group=scaninfofile.parent.group()) - # print(f"Copying info file to run {runno} to the raw directory of {pgroup}.") - - scan.remaining_tasks.append( - Thread( - target=daq.append_aux, - args=[scaninfofile.as_posix()], - kwargs=dict(pgroup=pgroup, run_number=runno), - ) - ) - # DEBUG - print( - f"Sending scan_info_rel.json in {Path(scaninfofile).parent.stem} to run number {runno}." - ) - scan.remaining_tasks[-1].start() - # response = daq.append_aux(scaninfofile.as_posix(), pgroup=pgroup, run_number=runno) - # print(f"Status: {response.json()['status']} Message: {response.json()['message']}") - # print( - # f"--> creating and copying file took{time.time()-t_start} s, presently adding to deadtime." - # ) - - -from eco.detector import Jungfrau - - -def _copy_selected_JF_pedestals_to_raw( - scan, daq=daq, copy_selected_JF_pedestals_to_raw=True, **kwargs -): - def copy_to_aux(daq, scan): - if hasattr(scan, "daq_run_number"): - runno = scan.daq_run_number - else: - runno = daq.get_last_run_number() - - pgroup = daq.pgroup - - for jf_id in daq.channels["channels_JF"](): - jf = Jungfrau(jf_id, name="noname", pgroup_adj=config_bernina.pgroup) - print( - f"Copying {jf_id} pedestal to run {runno} in the raw directory of {pgroup}." - ) - response = daq.append_aux( - jf.get_present_pedestal_filename_in_run(intempdir=True), - pgroup=pgroup, - run_number=runno, - ) - print( - f"Status: {response.json()['status']} Message: {response.json()['message']}" - ) - print( - f"Copying {jf_id} gainmap to run {runno} in the raw directory of {pgroup}." - ) - - response = daq.append_aux( - jf.get_present_gain_filename_in_run(intempdir=True), - pgroup=pgroup, - run_number=runno, - ) - print( - f"Status: {response.json()['status']} Message: {response.json()['message']}" - ) - - if copy_selected_JF_pedestals_to_raw: - scan.remaining_tasks.append(Thread(target=copy_to_aux, args=[daq, scan])) - scan.remaining_tasks[-1].start() - - -def _increment_daq_run_number(scan, daq=daq, **kwargs): - try: - daq_last_run_number = daq.get_last_run_number() - if int(scan.run_number) is int(daq_last_run_number) + 1: - print("############ incremented ##########") - daq_run_number = daq.get_next_run_number() - else: - daq_run_number = daq_last_run_number - if int(scan.run_number) is not int(daq_run_number): - print( - f"Difference in run number between eco {int(scan.run_number)} and daq {int(daq_run_number)}: using run number {int(scan.run_number)}" - ) - if int(scan.run_number) > int(daq_run_number): - n = int(scan.run_number) - int(daq_run_number) - print("Increasing daq run_number") - for i in range(n): - rn = daq.get_next_run_number() - print(rn) - scan.daq_run_number = rn - else: - scan.daq_run_number = daq_run_number - - except Exception as e: - print(e) - - -class Monitor: - def __init__(self, pvname, start_immediately=True): - self.data = {} - self.print = False - self.pv = PV(pvname) - self.cb_index = None - if start_immediately: - self.start_callback() - - def start_callback(self): - self.cb_index = self.pv.add_callback(self.append) - - def stop_callback(self): - self.pv.remove_callback(self.cb_index) - - def append(self, pvname=None, value=None, timestamp=None, **kwargs): - if not (pvname in self.data): - self.data[pvname] = [] - ts_local = time.time() - self.data[pvname].append( - {"value": value, "timestamp": timestamp, "timestamp_local": ts_local} - ) - if self.print: - print( - f"{pvname}: {value}; time: {timestamp}; time_local: {ts_local}; diff: {ts_local-timestamp}" - ) - - -import traceback - - -def append_scan_monitors( - scan, - daq=daq, - custom_monitors={}, - **kwargs, -): - scan.monitors = {} - for adj in scan.adjustables: - try: - tname = adj.alias.get_full_name() - except Exception: - tname = adj.name - traceback.print_exc() - try: - scan.monitors[tname] = Monitor(adj.pvname) - except Exception: - print(f"Could not add CA monitor for {tname}") - traceback.print_exc() - try: - rname = adj.readback.alias.get_full_name() - except Exception: - print("no readback configured") - traceback.print_exc() - try: - scan.monitors[rname] = Monitor(adj.readback.pvname) - except Exception: - print(f"Could not add CA readback monitor for {tname}") - traceback.print_exc() - - for tname, tobj in custom_monitors.items(): - try: - if type(tobj) is str: - tmonpv = tobj - scan.monitors[tname] = Monitor(tmonpv) - print(f"Added custom monitor for {tname}") - except Exception: - print(f"Could not add custom monitor for {tname}") - traceback.print_exc() - try: - tname = daq.pulse_id.alias.get_full_name() - scan.monitors[tname] = Monitor(daq.pulse_id.pvname) - except Exception: - print(f"Could not add daq.pulse_id monitor") - traceback.print_exc() - - -def end_scan_monitors(scan, daq=daq, **kwargs): - for tmon in scan.monitors: - scan.monitors[tmon].stop_callback() - - monitor_result = {tmon: scan.monitors[tmon].data for tmon in scan.monitors} - - ####### - # get data that should come later from api or similar. - run_directory = list( - Path(f"/sf/bernina/data/{daq.pgroup}/raw").glob(f"run{scan.run_number:04d}*") - )[0].as_posix() - - # correct some data in there (relative paths for now) - from os.path import relpath - - # save temprary file and send then to raw - if hasattr(scan, "daq_run_number"): - runno = scan.daq_run_number - else: - runno = daq.get_last_run_number() - pgroup = daq.pgroup - tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/info_run{runno:04d}") - tmpdir.mkdir(exist_ok=True, parents=True) - try: - tmpdir.chmod(0o775) - except: - pass - scanmonitorfile = tmpdir / Path("scan_monitor.pkl") - if not Path(scanmonitorfile).exists(): - with open(scanmonitorfile, "wb") as f: - pickle.dump(monitor_result, f) - - print(f"Copying monitor file to run {runno} to the raw directory of {pgroup}.") - response = daq.append_aux( - scanmonitorfile.as_posix(), pgroup=pgroup, run_number=runno - ) - print(f"Status: {response.json()['status']} Message: {response.json()['message']}") - - # scan.monitors = None - - -def _init_all(scan, append_status_info=True, **kwargs): - if not append_status_info: - return - namespace.init_all(silent=False) - - -callbacks_start_scan = [] -callbacks_start_scan.append(_init_all) -callbacks_start_scan.append(_wait_for_tasks) -callbacks_start_scan.append(_append_namesace_status_to_scan) -callbacks_start_scan.append(_increment_daq_run_number) -callbacks_start_scan.append(append_scan_monitors) -callbacks_end_step = [] -callbacks_end_step.append(_copy_scan_info_to_raw) -callbacks_end_step.append(_write_namespace_aliases_to_scan) -callbacks_end_step.append( - lambda scan, daq=daq, namespace=namespace, append_status_info=True, end_scan=True, **kwargs: _write_namespace_status_to_scan( - scan, - daq=daq, - namespace=namespace, - append_status_info=append_status_info, - end_scan=False, - **kwargs, - ) -) -callbacks_end_scan = [] -callbacks_end_scan.append(_write_namespace_status_to_scan) -callbacks_end_scan.append(_copy_scan_info_to_raw) -callbacks_end_scan.append( - lambda scan, daq=daq, force=True, **kwargs: _write_namespace_aliases_to_scan( - scan, daq=daq, force=force, **kwargs - ) -) -callbacks_end_scan.append(_copy_selected_JF_pedestals_to_raw) -callbacks_end_scan.append(end_scan_monitors) -callbacks_end_scan.append(_message_end_scan) - -# >>>> Extract for run_table and elog - - -# if self._run_table or self._elog: -def _create_metadata_structure_start_scan( - scan, run_table=run_table, elog=elog, append_status_info=True, **kwargs -): - runname = os.path.basename(scan.fina).split(".")[0] - runno = int(runname.split("run")[1].split("_")[0]) - metadata = { - "type": "scan", - "name": runname.split("_", 1)[1], - "scan_info_file": scan.scan_info_filename, - } - for n, adj in enumerate(scan.adjustables): - nname = None - nId = None - if hasattr(adj, "Id"): - nId = adj.Id - if hasattr(adj, "name"): - nname = adj.name - - metadata.update( - { - f"scan_motor_{n}": nname, - f"from_motor_{n}": scan.values_todo[0][n], - f"to_motor_{n}": scan.values_todo[-1][n], - f"id_motor_{n}": nId, - } - ) - if np.mean(np.diff(scan.pulses_per_step)) < 1: - pulses_per_step = scan.pulses_per_step[0] - else: - pulses_per_step = scan.pulses_per_step - metadata.update( - { - "steps": len(scan.values_todo), - "pulses_per_step": pulses_per_step, - "counters": [daq.name for daq in scan.counterCallers], - } - ) - - try: - try: - metadata.update({"scan_command": get_ipython().user_ns["In"][-1]}) - except: - print("Count not retrieve ipython scan command!") - - message_string = f"#### Run {runno}" - if metadata["name"]: - message_string += f': {metadata["name"]}\n' - else: - message_string += "\n" - - if "scan_command" in metadata.keys(): - message_string += "`" + metadata["scan_command"] + "`\n" - message_string += "`" + metadata["scan_info_file"] + "`\n" - elog_ids = elog.post( - message_string, - Title=f'Run {runno}: {metadata["name"]}', - text_encoding="markdown", - ) - scan._elog_id = elog_ids[1] - metadata.update({"elog_message_id": scan._elog_id}) - metadata.update( - {"elog_post_link": scan._elog.elogs[1]._log._url + str(scan._elog_id)} - ) - except: - print("Elog posting failed with:") - traceback.print_exc() - if not append_status_info: - return - d = {} - ## use values from status for run_table - try: - status = scan.status["status_run_start"] - d = status["settings"] - d.update(status["status"]) - except: - print("Tranferring values from status to run_table did not work") - t_start_rt = time.time() - try: - run_table.append_run(runno, metadata=metadata, d=d) - except: - print("WARNING: issue adding data to run table") - print(f"RT appending: {time.time()-t_start_rt:.3f} s") - - -# <<<< Extract for run table and elog -callbacks_start_scan.append(_create_metadata_structure_start_scan) - namespace.append_obj( "CheckerCA", module_name="eco.acquisition.checkers", @@ -1815,18 +991,83 @@ namespace.append_obj( name="checker", ) -# TODO resove scans pgroup sensitivity! Clearly non dynamic. + +### draft new epics daq ### +namespace.append_obj( + "EpicsDaq", + channel_list=channels_CA_epicsdaq, + name="daq_epics_local", + module_name="eco.acquisition.epics_data", + lazy=True, +) + namespace.append_obj( "Scans", - data_base_dir="scan_data", - scan_info_dir=f"/sf/bernina/data/{config_bernina.pgroup()}/res/scan_info", - default_counters=[daq], - checker=checker, + name="scans_epics", + module_name="eco.acquisition.scan", + data_base_dir=f"{config_bernina.pgroup()}/scan_data", + scan_info_dir=f"{daq_epics_local.default_file_path()}/{config_bernina.pgroup()}/scan_info", + default_counters=[daq_epics_local], + checker=None, scan_directories=True, - callbacks_start_scan=callbacks_start_scan, - callbacks_end_step=callbacks_end_step, - callbacks_end_scan=callbacks_end_scan, + run_table=None, + lazy=True, +) +# +# +##### standard DAQ ####### + + +#TODO: need to check if the value property actually works here for the pgroup in the run table to make is dynamic! +namespace.append_obj( + "Run_Table2", + name="run_table", + module_name="eco.utilities.runtable_stripped", + exp_id=config_bernina.pgroup._value, + # exp_path=f"/sf/bernina/data/{config_bernina.pgroup._value}/res/run_table/", + exp_path=f"/sf/bernina/data/{config_bernina.pgroup._value}/res/run_data/run_table/", + devices="eco.bernina", + keydf_fname="/sf/bernina/config/src/python/gspread/gspread_keys.pkl", + cred_fname="/sf/bernina/config/src/python/gspread/pandas_push", + gsheet_key_path="/sf/bernina/config/eco/reference_values/run_table_gsheet_keys", + parse=False, + lazy=True, +) + + +namespace.append_obj( + "Daq", + instrument="bernina", + pgroup=config_bernina.pgroup, + channels_JF=channels_JF, + channels_BS=channels_BS, + channels_BSCAM=channels_BSCAM, + channels_CA=channels_CA, + config_JFs=config_JFs, + pulse_id_adj="SLAAR21-LTIM01-EVR0:RX-PULSEID", + event_master=event_master, + detectors_event_code=50, + rate_multiplicator="auto", + name="daq", + namespace=namespace, + checker=checker, run_table=run_table, + pulse_picker=NamespaceComponent(namespace,'xp'), + elog=elog, + module_name="eco.acquisition.daq_client", + lazy=True, +) + + + +namespace.append_obj( + "Scans", + # data_base_dir="scan_data", + # scan_info_dir=f"/sf/bernina/data/{config_bernina.pgroup()}/res/scan_info", + default_counters=[daq], + callbacks_start_scan=[], + callbacks_end_step=[], + callbacks_end_scan=[], elog=elog, name="scans", module_name="eco.acquisition.scan", @@ -2440,7 +1681,7 @@ namespace.append_obj( # self._append(SmaractRecord, "SARES23-USR:MOT_13", name="focus_Rz", is_setting=True) # self._append(SmaractRecord, "SARES23-USR:MOT_15", name="focus_Ry", is_setting=True) # self._append(SmaractRecord, "SARES23-USR:MOT_11", name="focus_Rx", is_setting=True) -# self._append(SmaractRecord, "SARES23-LIC:MOT_18", name="thz_wp", is_setting=True) +# self._append(SmaractRecord, ":MOT_18", name="thz_wp", is_setting=True) # self._append( # SmaractRecord, "SARES23-LIC:MOT_16", name="delaystage_thz", is_setting=True # ) @@ -2743,6 +1984,94 @@ namespace.append_obj( ############## experiment specific ############# +class ConvergentBeamDiffraction(Assembly): + def __init__(self, name=None): + super().__init__(name=name) + self._append( + SmaractRecord, "SARES20-MCS3:MOT_1", preferred_home_direction='forward',name="sample_x", is_setting=True + ) + self._append( + SmaractRecord, "SARES20-MCS3:MOT_2", preferred_home_direction='forward', name="sample_y", is_setting=True + ) + self._append( + SmaractRecord, "SARES20-MCS3:MOT_3", preferred_home_direction='reverse', name="sample_z", is_setting=True + ) + # self._append(DetectorGet,self._get_zmq_dataset, name='positions', is_display=False) + # self._append(DetectorObject,self._positions, name='positions') + + + def _get_zmq_dataset(self): + # import zmq + # import json + # from pprint import pprint + + + ATTRS = [ + "SlitU - left (float64, mm)", + "SlitU - right (float64, mm)", + "SlitU - up (float64, mm)", + "SlitU - down (float64, mm)", + "SlitD - left (int64, pm)", + "SlitD - right (int64, pm)", + "SlitD - up (int64, pm)", + "SlitD - down (int64, pm)", + "MLL - UP - X (float64, nm)", + "MLL - UP - Y (float64, nm)", + "MLL - UP - Z (float64, nm)", + "MLL - UP - Pitch (float64, ndeg)", + "MLL - UP - Roll (float64, ndeg)", + "MLL - UP - Yaw (float64, ndeg)", + "MLL - DOWN - X (float64, nm)", + "MLL - DOWN - Y (float64, nm)", + "MLL - DOWN - Z (float64, nm)", + "MLL - DOWN - Pitch (float64, ndeg)", + "MLL - DOWN - Roll (float64, ndeg)", + "MLL - DOWN - Yaw (float64, ndeg)", + "OSA - X (int64, pm)", + "OSA - Y (int64, pm)", + "OSA - Z (int64, pm)", + "SAM - X (float64, mm)", + "SAM - Y (float64, mm)", + "SAM - Z (float64, mm)", + "SAM - pitch (int64, ndeg)", + "SAM - yaw (int64, ndeg)", + "CONE - X (float64, mm)", + "CONE - Y (float64, mm)", + "CONE - Z (float64, mm)", + "MIC - X (float64, mm)", + "MIC - Y (int64, nm)", + "MIC - Z (float64, mm)", + "BSU - X (float64, mm)", + "BSU - Y (float64, mm)", + "BSU - Z (float64, mm)", + "BSD - X (float64, mm)", + "BSD - Y (float64, mm)", + "BSD - Z (float64, mm)" + ] + + HOST = "129.129.243.102" # Replace with the IP address of our server in BL network + + socket = zmq.Context.instance().socket(zmq.SUB) + socket.setsockopt(zmq.RCVTIMEO, 100) + socket.setsockopt(zmq.LINGER, 0) + socket.connect(f"tcp://{HOST}:50002") + socket.setsockopt_string(zmq.SUBSCRIBE, "") + while not socket.poll(timeout=100): + pass + + positions = socket.recv() + positions = json.loads(positions.decode()).split(";") + + data = {ATTRS[i]: positions[i] for i in range(len(ATTRS))} + # pprint(data) + return data + + +namespace.append_obj( + ConvergentBeamDiffraction, + name="cbd", + lazy=True, +) class Pumpdelay(Assembly): def __init__( @@ -3239,6 +2568,40 @@ def name2pgroups(name, beamline="bernina"): ] return eq + ni +def change_pgroup(searchstring='', config=config_bernina): + """ + Change the pgroup of the bernina config. + """ + gs = name2pgroups(searchstring) + if len(gs) == 0: + print("No pgroup found.") + # elif len(gs) == 1: + # print(f"Found pgroup for {gs[0][0]} : {gs[0][1] }") + # print(f'(old pgroup: {config.pgroup})') + # if input('would you like to change? (y/n) ')=='y': + # config.pgroup = gs[0][1] + # print(f"Changed pgroup to {config.pgroup}") + else: + old_group = config.pgroup.get_current_value() + try: + print(f'Currently {pgroup2name(old_group)}: {old_group}') + except: + pass + + print(f"Found {len(gs)} pgroups:") + for i, g in enumerate(gs): + print(f"{i+1}: {g[0]} ({g[1]})") + try: + sel = int(input("Please select the pgroup to use: ")) - 1 + + if sel < 0 or sel >= len(gs): + raise ValueError("Invalid selection") + + config.pgroup.set_target_value(gs[sel][1]) + print(f"Changed pgroup from {old_group} to {config.pgroup.get_current_value()}") + except ValueError as e: + print(f"Invalid selection: {e}") + from eco.utilities import linlog_intervals, roundto diff --git a/eco/bernina/bernina_bak.py b/eco/bernina/bernina_bak.py new file mode 100644 index 0000000..70bc060 --- /dev/null +++ b/eco/bernina/bernina_bak.py @@ -0,0 +1,3400 @@ +import json +from pathlib import Path +from threading import Thread + +import zmq +import eco +from eco.acquisition.scan import NumpyEncoder +from eco.devices_general.digitizers import DigitizerIoxosBoxcarChannel +from eco.devices_general.powersockets import MpodModule +from eco.devices_general.wago import AnalogOutput +from eco.elements.adjustable import AdjustableFS +from eco.elements.adjustable import AdjustableVirtual +from eco.elements.detector import DetectorGet +from eco.loptics.bernina_experiment import DelayCompensation +from eco.devices_general.cameras_swissfel import CameraBasler +from epics import PV +import time +import pickle + +# from eco.endstations.bernina_sample_environments import Organic_crystal_breadboard_old +from eco.motion.smaract import SmaractController +from eco.timing.event_timing_new_new import EvrOutput +from .config import components + +# from .config import config as config_berninamesp +from ..utilities.config import Namespace, NamespaceComponent +from ..aliases import NamespaceCollection +import pyttsx3 + +from ..utilities.path_alias import PathAlias +import sys, os, shutil +import numpy as np +from IPython import get_ipython + + +path_aliases = PathAlias() +sys.path.append("/sf/bernina/config/src/python/bernina_analysis") + +namespace = Namespace( + name="bernina", root_module=__name__, alias_namespace=NamespaceCollection().bernina, required_names_directory="/sf/bernina/config/eco/required_bernina_names.json" +) +namespace.alias_namespace.data = [] + +# Adding stuff that might be relevant for stuff configured below (e.g. config) +_config_bernina_dict = AdjustableFS( + "/sf/bernina/config/eco/configuration/bernina_config.json", + name="_config_bernina_dict", +) +from eco.elements.adj_obj import AdjustableObject, DetectorObject + +namespace.append_obj(AdjustableObject, _config_bernina_dict, name="config_bernina") + +def change_pgroup(searchstring='', config=config_bernina): + """ + Change the pgroup of the bernina config. + """ + gs = name2pgroups(searchstring) + if len(gs) == 0: + print("No pgroup found.") + # elif len(gs) == 1: + # print(f"Found pgroup for {gs[0][0]} : {gs[0][1] }") + # print(f'(old pgroup: {config.pgroup})') + # if input('would you like to change? (y/n) ')=='y': + # config.pgroup = gs[0][1] + # print(f"Changed pgroup to {config.pgroup}") + else: + old_group = config.pgroup.get_current_value() + try: + print(f'Currently {pgroup2name(old_group)}: {old_group}') + except: + pass + + print(f"Found {len(gs)} pgroups:") + for i, g in enumerate(gs): + print(f"{i+1}: {g[0]} ({g[1]})") + try: + sel = int(input("Please select the pgroup to use: ")) - 1 + + if sel < 0 or sel >= len(gs): + raise ValueError("Invalid selection") + + config.pgroup.set_target_value(gs[sel][1]) + print(f"Changed pgroup from {old_group} to {config.pgroup.get_current_value()}") + except ValueError as e: + print(f"Invalid selection: {e}") + + + + +namespace.append_obj( + "RunData", + config_bernina.pgroup, + name="runs", + load_kwargs={ + # "checknstore_parsing_result": "/sf/bernina/data/{pgroup}/res", + "checknstore_parsing_result": "/sf/bernina/data/{pgroup}/scratch", + "load_dap_data": True, + "lazyEscArrays": True, + "exclude_from_files": ["PVDATA"], + }, + module_name="eco.acquisition.scan_data", +) + +namespace.append_obj( + "StatusData", + config_bernina.pgroup, + name="run_status", + load_kwargs={}, + module_name="eco.acquisition.scan_data", + lazy=False, +) + +namespace.append_obj( + "Elog", + "https://elog-gfa.psi.ch/Bernina", + screenshot_directory="/tmp", + name="elog_gfa", + module_name="eco.utilities.elog", + lazy=True, +) + +namespace.append_obj( + "Elog", + pgroup_adj=config_bernina.pgroup, + name="scilog", + module_name="eco.utilities.elog_scilog", + lazy=True, +) + +namespace.append_obj( + "ElogsMultiplexer", + scilog, + elog_gfa, + name="elog", + module_name="eco.utilities.elog", + lazy=True, +) +# namespace.append_obj( +# "Elog", +# "https://elog-gfa.psi.ch/Bernina", +# screenshot_directory="/tmp", +# name="elog", +# module_name="eco.utilities.elog", +# + +eco.defaults.ELOG = elog +namespace.append_obj( + "DummyAdjustable", + module_name="eco.elements.adjustable", + lazy=True, + name="dummy_adjustable", +) +namespace.append_obj( + "set_global_memory_dir", + "/sf/bernina/config/eco/memory", + module_name="eco.elements.memory", + name="path_memory", + lazy=False, +) + +namespace.append_obj( + "DataHub", + name="archiver", + module_name="eco.dbase.archiver", + pv_pulse_id="SARES20-CVME-01-EVR0:RX-PULSEID", + add_to_cnf=True, + lazy=True, +) +eco.defaults.ARCHIVER = archiver + +namespace.append_obj( + "get_strip_chart_function", + name="strip_chart", + module_name="eco.dbase.strip_chart", + lazy=True, +) + +namespace.append_obj( + "EventWorker", + name="bs_worker", + module_name="escape.stream", + lazy=True, +) + +namespace.append_obj( + "BerninaEnv", + name="env_log", + module_name="eco.fel.atmosphere", + lazy=True, +) +namespace.append_obj( + "BerninaEnvironment", + name="env", + module_name="eco.devices_general.env_sensors", + lazy=True, +) + +namespace.append_obj( + "AdjustableFS", + "/photonics/home/gac-bernina/eco/configuration/run_table_channels_CA", + name="_env_channels_ca", + module_name="eco.elements.adjustable", + lazy=True, +) + +# adding all stuff from the config components the "old" way of configuring. +# whatever is added, it is available by the configured name in this module +# afterwards, and can be used immediately, e.g. as input argument for the next thing. + +for tk in components: + namespace.append_obj_from_config(tk, lazy=True) + + +# Adding stuff the "new" way + +# namespace.append_obj( +# "EventReceiver", +# "", +# lazy=True, +# name="cam_north", +# module_name="eco.devices_general.cameras_ptz", +# ) + +namespace.append_obj( + "BerninaVacuum", + name="vacuum", + module_name="eco.endstations.bernina_vacuum", + lazy=True, +) + +## general components ## +namespace.append_obj( + "CtaSequencer", + "SAR-CCTA-ESB", + 0, + name="seq", + module_name="eco.timing.sequencer", + lazy=True, +) +namespace.append_obj( + "MasterEventSystem", + "SIN-TIMAST-TMA", + name="event_master", + module_name="eco.timing.event_timing_new_new", + # pv_eventset="SAR-CVME-TIFALL5:EvtSet", + # lazy=False, + lazy=True, +) +namespace.append_obj( + "TimingSystem", + pv_master="SIN-TIMAST-TMA", + pv_pulse_id="SARES20-CVME-01-EVR0:RX-PULSEID", + pv_eventset="SAR-CVME-TIFALL5:EvtSet", + name="event_system", + module_name="eco.timing.event_timing_new_new", + lazy=True, +) + + +## Old stuff that was still in config and might be needed +namespace.append_obj( + "Pulsepick", + Id="SAROP21-OPPI113", + evronoff="SGE-CPCW-72-EVR0:FrontUnivOut15-Ena-SP", + evrsrc="SGE-CPCW-72-EVR0:FrontUnivOut15-Src-SP", + name="xp_old", + module_name="eco.xoptics.pp", + lazy=True, +) +namespace.append_obj( + "XrayPulsePicker", + pvbase="SAROP21-OPPI113", + evronoff="SGE-CPCW-72-EVR0:FrontUnivOut15-Ena-SP", + evrsrc="SGE-CPCW-72-EVR0:FrontUnivOut15-Src-SP", + evr_output_base="SGE-CPCW-72-EVR0:FrontUnivOut15", + evr_pulser_base="SGE-CPCW-72-EVR0:Pul0", + event_master=NamespaceComponent(namespace, "event_master"), + name="xp", + module_name="eco.xoptics.pp", + lazy=True, +) + +namespace.append_obj( + "laser_shutter", + "SLAAR21-LTIM01-EVR0", + name="laser_shutter", + module_name="eco.loptics.laser_shutter", + lazy=True, +) +namespace.append_obj( + "PhotonShutter", + "SARFE10-OPSH044:REQUEST", + name="pshut_und", + module_name="eco.xoptics.shutters", + lazy=True, +) +namespace.append_obj( + "PhotonShutter", + "SARFE10-OPSH059:REQUEST", + name="pshut_fe", + module_name="eco.xoptics.shutters", + lazy=True, +) +namespace.append_obj( + "SafetyShutter", + "SGE01-EPKT822:BST1_oeffnen", + name="sshut_opt", + module_name="eco.xoptics.shutters", + lazy=True, +) +namespace.append_obj( + "SafetyShutter", + "SGE01-EPKT820:BST1_oeffnen", + name="sshut_fe", + module_name="eco.xoptics.shutters", + lazy=True, +) +namespace.append_obj( + "AttenuatorAramis", + "SARFE10-OATT053", + shutter=pshut_und, + set_limits=[], + module_name="eco.xoptics.attenuator_aramis", + name="att_fe", + lazy=True, +) + + +namespace.append_obj( + "Bernina_XEYE", + zoomstage_pv=config_bernina.xeye.zoomstage_pv._value, + camera_pv=config_bernina.xeye.camera_pv._value, + bshost=config_bernina.xeye.bshost._value, + bsport=config_bernina.xeye.bsport._value, + name="xeye", + lazy=True, + module_name="eco.xdiagnostics.profile_monitors", +) + + +## beamline components ## + +namespace.append_obj( + "JJSlitUnd", + name="slit_und", + module_name="eco.xoptics.slits", + lazy=True, +) +namespace.append_obj( + "SlitBlades", + "SAROP21-OAPU092", + name="slit_switch", + module_name="eco.xoptics.slits", + lazy=True, +) + +# namespace.append_obj( +# "Pprm", +# "SARFE10-PPRM064", +# "SARFE10-PPRM064", +# name= "prof_fe", +# # "z_und": 64, +# # "desc": "Profile monitor after Front End", +# module_name="eco.xdiagnostics.profile_monitors", +# ) +# namespace.append_obj( +# "Pprm", +# "SAROP11-PPRM066", +# "SAROP11-PPRM066", +# name= "prof_mirr_alv1", +# # "z_und": 66, +# # "desc": "Profile monitor after Alvra Mirror 1", +# module_name="eco.xdiagnostics.profile_monitors", +# ) +# namespace.append_obj( +# "Pprm", +# "SAROP21-PPRM094", +# "SAROP21-PPRM094", +# name= "prof_mirr1", +# # "z_und": 94, +# # "desc": "Profile monitor after Mirror 1", +# module_name="eco.xdiagnostics.profile_monitors", +# ) + + +namespace.append_obj( + "OffsetMirrorsBernina", + name="offset", + lazy=True, + module_name="eco.xoptics.offsetMirrors_new", +) + +namespace.append_obj( + "SlitBlades", + "SAROP21-OAPU102", + name="slit_mono", + module_name="eco.xoptics.slits", + lazy=True, +) +# namespace.append_obj( +# "SolidTargetDetectorPBPS", +# "SAROP21-PBPS103", +# diode_channels_raw={ +# "up": "SAROP21-CVME-PBPS1:Lnk9Ch3-DATA-SUM", +# "down": "SAROP21-CVME-PBPS1:Lnk9Ch4-DATA-SUM", +# "left": "SAROP21-CVME-PBPS1:Lnk9Ch2-DATA-SUM", +# "right": "SAROP21-CVME-PBPS1:Lnk9Ch1-DATA-SUM", +# }, +# fe_digi_channels={ +# "left": "SAROP21-CVME-PBPS1:Lnk9Ch2", +# "right": "SAROP21-CVME-PBPS1:Lnk9Ch1", +# "up": "SAROP21-CVME-PBPS1:Lnk9Ch3", +# "down": "SAROP21-CVME-PBPS1:Lnk9Ch4", +# }, +# name="mon_mono_old", +# module_name="eco.xdiagnostics.intensity_monitors", +# lazy=True, +# ) + +namespace.append_obj( + "SolidTargetDetectorPBPS", + "SAROP21-PBPS103", + use_calibration=False, + # channel_xpos="SLAAR21-LTIM01-EVR0:CALCX", + # channel_ypos="SLAAR21-LTIM01-EVR0:CALCY", + # channel_intensity="SLAAR21-LTIM01-EVR0:CALCI", + # diode_channels_raw={ + # "up": "SLAAR21-LSCP1-FNS:CH6:VAL_GET", + # "down": "SLAAR21-LSCP1-FNS:CH7:VAL_GET", + # "left": "SLAAR21-LSCP1-FNS:CH4:VAL_GET", + # "right": "SLAAR21-LSCP1-FNS:CH5:VAL_GET", + # }, + diode_channels_raw={ + "up": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD1", + "down": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD2", + "left": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD0", + "right": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD3", + }, + # calibration_records={ + # "intensity": "SLAAR21-LTIM01-EVR0:CALCI", + # "xpos": "SLAAR21-LTIM01-EVR0:CALCX", + # "ypos": "SLAAR21-LTIM01-EVR0:CALCY", + # }, + name="mon_mono", + module_name="eco.xdiagnostics.intensity_monitors", + pipeline_computation="SAROP21-PBPS103_proc", + lazy=True, +) + +from eco.devices_general.motors import SmaractStreamdevice, SmaractRecord + +namespace.append_obj( + "SlitBladesGeneral", + name="slit_kb", + def_blade_up={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_2"], + "kwargs": {}, + }, + def_blade_down={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_1"], + "kwargs": {}, + }, + def_blade_left={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_9"], + "kwargs": {}, + }, + def_blade_right={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_4"], + "kwargs": {}, + }, + module_name="eco.xoptics.slits", + lazy=True, +) + + +namespace.append_obj( + "SlitBladesGeneral", + name="slit_cleanup", + def_blade_up={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_6"], + "kwargs": {}, + }, + def_blade_down={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_5"], + "kwargs": {}, + }, + def_blade_left={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_8"], + "kwargs": {}, + }, + def_blade_right={ + "args": [SmaractRecord, "SARES20-MCS1:MOT_7"], + "kwargs": {}, + }, + module_name="eco.xoptics.slits", + lazy=True, +) + + +namespace.append_obj( + "GasDetector", + name="mon_und_gas", + module_name="eco.xdiagnostics.intensity_monitors", + lazy=True, +) +namespace.append_obj( + "SolidTargetDetectorPBPS", + "SARFE10-PBPS053", + # diode_channels_raw={ + # "up": "SARFE10-CVME-PHO6212:Lnk9Ch13-DATA-SUM", + # "down": "SARFE10-CVME-PHO6212:Lnk9Ch12-DATA-SUM", + # "left": "SARFE10-CVME-PHO6212:Lnk9Ch14-DATA-SUM", + # "right": "SARFE10-CVME-PHO6212:Lnk9Ch15-DATA-SUM", + # }, + name="mon_und", + use_calibration=False, + module_name="eco.xdiagnostics.intensity_monitors", + pipeline_computation="SAROP21-PBPS103_proc", + lazy=True, +) + + +namespace.append_obj( + "RefLaser_Aramis", + "SAROP21-OLAS134", + module_name="eco.xoptics.reflaser", + name="reflaser_beamline", + lazy=True, +) + +namespace.append_obj( + "RefLaser_BerninaUSD", + module_name="eco.xoptics.reflaser", + name="reflaser", + outpos_adjfs_path="/sf/bernina/config/eco/configuration/reflaser_usd_lastposition.json", + lazy=True, +) + +namespace.append_obj( + "SpectralEncoder", + "SAROP21-PSEN135", + module_name="eco.xdiagnostics.timetools", + name="tt_opt", + mirror_stages={ + "las_in_rx": "SLAAR21-LMOT-M538:MOT", + "las_in_ry": "SLAAR21-LMOT-M537:MOT", + "las_out_rx": "SLAAR21-LMOT-M536:MOT", + "las_out_ry": "SLAAR21-LMOT-M535:MOT", + }, + lazy=True, +) + + +# namespace.append_obj( +# "SolidTargetDetectorPBPS", +# "SAROP21-PBPS133", +# channel_xpos="SLAAR21-LTIM01-EVR0:CALCX", +# channel_ypos="SLAAR21-LTIM01-EVR0:CALCY", +# channel_intensity="SLAAR21-LTIM01-EVR0:CALCI", +# diode_channels_raw={ +# "up": "SLAAR21-LSCP1-FNS:CH6:VAL_GET", +# "down": "SLAAR21-LSCP1-FNS:CH7:VAL_GET", +# "left": "SLAAR21-LSCP1-FNS:CH4:VAL_GET", +# "right": "SLAAR21-LSCP1-FNS:CH5:VAL_GET", +# }, +# calibration_records={ +# "intensity": "SLAAR21-LTIM01-EVR0:CALCI", +# "xpos": "SLAAR21-LTIM01-EVR0:CALCX", +# "ypos": "SLAAR21-LTIM01-EVR0:CALCY", +# }, +# name="mon_opt_old", +# module_name="eco.xdiagnostics.intensity_monitors", +# lazy=True, +# ) + +namespace.append_obj( + "SolidTargetDetectorPBPS", + "SAROP21-PBPS133", + use_calibration=False, + # channel_xpos="SLAAR21-LTIM01-EVR0:CALCX", + # channel_ypos="SLAAR21-LTIM01-EVR0:CALCY", + # channel_intensity="SLAAR21-LTIM01-EVR0:CALCI", + diode_channels_raw={ + "up": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD1", + "down": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD2", + "left": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD0", + "right": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD3", + }, + # calibration_records={ + # "intensity": "SLAAR21-LTIM01-EVR0:CALCI", + # "xpos": "SLAAR21-LTIM01-EVR0:CALCX", + # "ypos": "SLAAR21-LTIM01-EVR0:CALCY", + # }, + name="mon_opt", + module_name="eco.xdiagnostics.intensity_monitors", + pipeline_computation="SAROP21-PBPS133_proc", + lazy=True, +) + + +namespace.append_obj( + "Pprm", + "SARFE10-PPRM064", + "SARFE10-PPRM064", + module_name="eco.xdiagnostics.profile_monitors", + name="prof_fe", + in_target=3, + lazy=True, +) + +namespace.append_obj( + "Pprm", + "SAROP11-PPRM066", + "SAROP11-PPRM066", + module_name="eco.xdiagnostics.profile_monitors", + name="prof_mirr_alv1", + in_target=3, + lazy=True, +) + +namespace.append_obj( + "Pprm", + "SAROP21-PPRM094", + "SAROP21-PPRM094", + module_name="eco.xdiagnostics.profile_monitors", + name="prof_mirr1", + in_target=3, + lazy=True, +) + +namespace.append_obj( + "Pprm", + "SAROP21-PPRM113", + "SAROP21-PPRM113", + bs_channels={ + "intensity": "SAROP21-PPRM113:intensity", + "xpos": "SAROP21-PPRM113:x_fit_mean", + "ypos": "SAROP21-PPRM113:y_fit_mean", + }, + module_name="eco.xdiagnostics.profile_monitors", + name="prof_mono", + in_target=3, + lazy=True, +) + + +namespace.append_obj( + "Pprm", + "SAROP21-PPRM133", + "SAROP21-PPRM133", + module_name="eco.xdiagnostics.profile_monitors", + name="prof_opt", + in_target=3, + lazy=True, +) + + +namespace.append_obj( + "Pprm", + "SAROP21-PPRM138", + "SAROP21-PPRM138", + bs_channels={ + "intensity": "SAROP21-PPRM138:intensity", + "xpos": "SAROP21-PPRM138:x_fit_mean", + "ypos": "SAROP21-PPRM138:y_fit_mean", + }, + module_name="eco.xdiagnostics.profile_monitors", + name="prof_att", + in_target=3, + lazy=True, +) +namespace.append_obj( + "AttenuatorAramis", + "SAROP21-OATT135", + shutter=xp, + set_limits=[], + module_name="eco.xoptics.attenuator_aramis", + name="att", + lazy=True, +) + + +namespace.append_obj( + "SolidTargetDetectorBerninaUSD", + "SARES20-MCS1:MOT_12", + channel_xpos="SARES21-PBPS141:XPOS", + channel_ypos="SARES21-PBPS141:YPOS", + channel_intensity="SARES21-PBPS141:INTENSITY", + diode_channels_raw={ + "up": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD1", + "down": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD2", + "left": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD0", + "right": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD3", + }, + module_name="eco.xdiagnostics.intensity_monitors", + pipeline_computation="SARES21-PBPS141_proc", + name="mon_kb", + lazy=True, +) + +namespace.append_obj( + "DownstreamDiagnostic", + name="dsd_table", + module_name="eco.xdiagnostics.dsd", + lazy=True, +) + +namespace.append_obj( + "Pprm_dsd", + pvname="SARES20-DSDPPRM", + pvname_camera="SARES20-PROF146-M1", + module_name="eco.xdiagnostics.profile_monitors", + name="prof_dsd", + lazy=True, +) +# namespace.append_obj( +# "SolidTargetDetectorPBPS", +# "SARES20-DSDPBPS", +# # diode_channels_raw={ +# # "up": "", +# # "down": "", +# # "left": "", +# # "right":"", +# # }, +# module_name="eco.xdiagnostics.intensity_monitors", +# name="mon_dsd", +# lazy=True, +# ) + + +# namespace.append_obj('Daq', instrument= "bernina",pgroup= config_berninamesp["pgroup"], channels_JF=channels_JF, channels_BS=channels_BS,channels_BSCAM=channels_BSCAM,channels_CA=channels_CA,pulse_id_adj="SLAAR21-LTIM01-EVR0:RX-PULSEID",event_master=event_system.event_master,detectors_event_code=50,name='daq',module_name='eco.acquisition.daq_client') + +# namespace.append_obj('Scans',data_base_dir="scan_data",scan_info_dir=f"/sf/bernina/data/{config_berninamesp['pgroup']}/res/scan_info", +# default_counters=[daq],checker=checker,scan_directories=True,run_table=run_table,elog=elog, +# module_name = "eco.acquisition.scan",name="scans") +namespace.append_obj( + "ProfKbBernina", + module_name="eco.xdiagnostics.profile_monitors", + name="prof_kb", + pvname_mirror="SARES20-MCS1:MOT_11", + lazy=True, +) +namespace.append_obj( + "TimetoolBerninaUSD", + module_name="eco.timing.timing_diag", + pvname_mirror="SARES20-MCS1:MOT_11", + andor_spectrometer="SLAAR11-LSPC-ALCOR1", + name="tt_kb", + lazy=True, +) +# namespace.append_obj( +# "TimetoolSpatial", +# module_name="eco.timing.timing_diag", +# name="tt_spatial_dev", +# lazy=True, +# ) + +namespace.append_obj( + "HexapodSymmetrie", + name="usd_table", + module_name="eco.endstations.hexapod", + offset=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + lazy=True, +) + +namespace.append_obj( + "EventReceiver", + "SARES20-CVME-01-EVR0", + event_master, + n_pulsers=24, + n_output_front=7, + n_output_rear=16, + name="evr", + module_name="eco.timing.event_timing_new_new", + lazy=True, +) +namespace.append_obj( + "EventReceiver", + "SLAAR-LTIM02-EVR0", + event_master, + n_pulsers=24, + n_output_front=7, + n_output_rear=16, + name="evr_laser", + module_name="eco.timing.event_timing_new_new", + lazy=True, +) +namespace.append_obj( + "EventReceiver", + "SLAAR21-LTIM01-EVR0", + event_master, + n_pulsers=24, + n_output_front=7, + n_output_rear=16, + name="evr_hutch_laser", + module_name="eco.timing.event_timing_new_new", + lazy=True, + # lazy=False, +) +namespace.append_obj( + "EventReceiver", + "SGE-CPCW-72-EVR0", + event_master, + n_pulsers=16, + n_output_front=16, + n_output_rear=0, + name="evr_camserver72", + module_name="eco.timing.event_timing_new_new", + lazy=True, + # lazy=False, +) +namespace.append_obj( + "EventReceiver", + "SGE-CPCW-73-EVR0", + event_master, + n_pulsers=16, + n_output_front=16, + n_output_rear=0, + name="evr_camserver73", + module_name="eco.timing.event_timing_new_new", + lazy=True, + # lazy=False, +) +namespace.append_obj( + "EventReceiver", + "SGE-CPCW-74-EVR0", + event_master, + n_pulsers=16, + n_output_front=16, + n_output_rear=0, + name="evr_camserver74", + module_name="eco.timing.event_timing_new_new", + lazy=True, + # lazy=False, +) +namespace.append_obj( + "EventReceiver", + "SGE-CPCW-83-EVR0", + event_master, + n_pulsers=16, + n_output_front=16, + n_output_rear=0, + name="evr_camserver83", + module_name="eco.timing.event_timing_new_new", + lazy=True, + # lazy=False, +) +namespace.append_obj( + "EventReceiver", + "SGE-CPCW-84-EVR0", + event_master, + n_pulsers=16, + n_output_front=16, + n_output_rear=0, + name="evr_camserver84", + module_name="eco.timing.event_timing_new_new", + lazy=True, + # lazy=False, +) +namespace.append_obj( + "EventReceiver", + "SGE-CPCW-85-EVR0", + event_master, + n_pulsers=16, + n_output_front=16, + n_output_rear=0, + name="evr_camserver85", + module_name="eco.timing.event_timing_new_new", + lazy=True, + # lazy=False, +) +namespace.append_obj( + "DigitizerKeysight", + "SARES21-GES1", + name="digitizer_keysight_user", + module_name="eco.devices_general.digitizers", + lazy=True, +) +namespace.append_obj( + "DigitizerIoxos", + "SARES20-LSCP9-FNS", + name="digitizer_ioxos_user", + module_name="eco.devices_general.digitizers", + lazy=True, +) +namespace.append_obj( + "DigitizerIoxos", + "SLAAR21-LSCP1-FNS", + name="digitizer_ioxos_laser", + module_name="eco.devices_general.digitizers", + lazy=True, +) + +namespace.append_obj( + "AxisPTZ", + "bernina-cam-n", + lazy=True, + name="cam_north", + module_name="eco.devices_general.cameras_ptz", +) +namespace.append_obj( + "AxisPTZ", + "bernina-cam-w", + lazy=True, + name="cam_west", + module_name="eco.devices_general.cameras_ptz", +) +namespace.append_obj( + "AxisPTZ", + "bernina-cam-s", + lazy=True, + name="cam_south", + module_name="eco.devices_general.cameras_ptz", +) +namespace.append_obj( + "Xspect", + name="xspect", + lazy=True, + module_name="eco.xdiagnostics.xspect", +) +namespace.append_obj( + "SlitPosWidth", + "SAROP21-OAPU138", + name="slit_att", + lazy=True, + module_name="eco.xoptics.slits", +) +namespace.append_obj( + "WagoAnalogInputs", + "SARES20-CWAG-GPS01", + lazy=True, + name="analog_inputs", + module_name="eco.devices_general.wago", +) +namespace.append_obj( + "WagoAnalogOutputs", + "SARES20-CWAG-GPS01", + lazy=True, + name="analog_outputs", + module_name="eco.devices_general.wago", +) + +# namespace.append_obj( +# "AnalogInput", +# "SARES20-CWAG-GPS01:ADC08", +# lazy=True, +# name="oxygen_sensor", +# module_name="eco.devices_general.wago", +# ) + +namespace.append_obj( + "GudeStrip", + "SARES20-CPPS-01", + lazy=True, + name="powerstrip_gps", + module_name="eco.devices_general.powersockets", +) +namespace.append_obj( + "GudeStrip", + "SARES20-CPPS-04", + lazy=True, + name="powerstrip_xrd", + module_name="eco.devices_general.powersockets", +) +namespace.append_obj( + "GudeStrip", + "SARES20-CPPS-02", + lazy=True, + name="powerstrip_patch2", + module_name="eco.devices_general.powersockets", +) + +## diffractometers +namespace.append_obj( + "AdjustableFS", + "/photonics/home/gac-bernina/eco/configuration/config_JFs", + module_name="eco.elements.adjustable", + lazy=True, + name="config_JFs", +) + +namespace.append_obj( + "GPS", + module_name="eco.endstations.bernina_diffractometers", + name="gps", + pvname="SARES22-GPS", + configuration=config_bernina.gps_config, + pgroup_adj=config_bernina.pgroup, + jf_config=config_JFs, + fina_hex_angle_offset="/sf/bernina/config/eco/reference_values/hex_pi_angle_offset.json", + xp = NamespaceComponent(namespace,"xp"), + helium_control_valve={ + "pvbase": "SARES21-PS7071", + "channel_number": 4, + "name": "helium_control_valve", + "pvname": "SARES20-CWAG-GPS01:DAC04", + }, + illumination_mpod=[ + { + "pvbase": "SARES21-PS7071", + "channel_number": 5, + "module_string": "LV_OMPV_1", + "name": "illumination", + } + ], + thc_config = NamespaceComponent(namespace,'config_bernina.thc_config',get_current_value=True), + lazy=True, +) + +namespace.append_obj( + "StaeubliTx200", + module_name="eco.endstations.bernina_robots", + name="rob", + # pshell_url="http://PC14742:8080/", + pshell_url="http://saresb-robot:8080/", + robot_config=config_bernina.robot_config, + pgroup_adj=config_bernina.pgroup, + jf_config=config_JFs, + lazy=True, +) + + +namespace.append_obj( + "SmarActOpenLoopRecord", + module_name="eco.devices_general.motors", + pvname="SARES23-USR:asyn", + channel=14, + name="openloop_horizontal", + lazy=True, +) + +namespace.append_obj( + "XRDYou", + module_name="eco.endstations.bernina_diffractometers", + Id="SARES21-XRD", + configuration=config_bernina.xrd_config, + pgroup_adj=config_bernina.pgroup, + jf_config=config_JFs, + invert_kappa_ellbow=config_bernina.invert_kappa_ellbow._value, + fina_hex_angle_offset="/sf/bernina/config/eco/reference_values/hex_pi_angle_offset.json", + name="xrd", + lazy=True, +) +namespace.append_obj( + "Crystals", + module_name="eco.utilities.recspace", + name="diffcalc", + lazy=True, +) +namespace.append_obj( + "KBMirrorBernina", + "SAROP21-OKBV139", + "SAROP21-OKBH140", + module_name="eco.xoptics.kb_bernina", + usd_table=usd_table, + name="kb", + diffractometer=xrd, + lazy=True, +) + +### channelsfor daq ### +namespace.append_obj( + "AdjustableFS", + "/photonics/home/gac-bernina/eco/configuration/channels_JF", + module_name="eco.elements.adjustable", + lazy=True, + name="channels_JF", +) +namespace.append_obj( + "AdjustableFS", + "/photonics/home/gac-bernina/eco/configuration/channTest of new scilog for Ovuka experimentels_BS", + module_name="eco.elements.adjustable", + lazy=True, + name="channels_BS", +) +namespace.append_obj( + "AdjustableFS", + "/photonics/home/gac-bernina/eco/configuration/channels_BSCAM", + module_name="eco.elements.adjustable", + lazy=True, + name="channels_BSCAM", +) +namespace.append_obj( + "AdjustableFS", + "/photonics/home/gac-bernina/eco/configuration/channels_CA", + module_name="eco.elements.adjustable", + lazy=True, + name="channels_CA", +) +namespace.append_obj( + "AdjustableFS", + "/photonics/home/gac-bernina/eco/configuration/channels_CA_epicsdaq", + module_name="eco.elements.adjustable", + lazy=True, + name="channels_CA_epicsdaq", +) + +namespace.append_obj( + "Att_usd", + name="att_usd", + module_name="eco.xoptics.att_usd", + xp=xp, + lazy=True, +) + +# namespace.append_obj( +# "Jungfrau", +# "JF13T01V01", +# name="det_invac", +# pgroup_adj=config_bernina.pgroup, +# module_name="eco.detector.jungfrau", +# config_adj=config_JFs, +# lazy=True,) + +# namespace.append_obj( +# "Jungfrau", +# "JF04T01V01", +# name="det_rowland", +# pgroup_adj=config_bernina.pgroup, +# module_name="eco.detector.jungfrau", +# config_adj=config_JFs, +# lazy=True, +# ) + +namespace.append_obj( + "Jungfrau", + "JF03T01V02", + name="det_i0", + pgroup_adj=config_bernina.pgroup, + module_name="eco.detector.jungfrau", + config_adj=config_JFs, + lazy=True, +) + +# namespace.append_obj( +# "Jungfrau", +# "JF14T01V01", +# name="det_diff", +# pgroup_adj=config_bernina.pgroup, +# module_name="eco.detector.jungfrau", +# config_adj=config_JFs, +# lazy=True, +# ) + +# namespace.append_obj( +# "DetectorRobot", +# JF_detector_id="JF07T32V02", +# JF_detector_name="det_diff", +# pgroup_adj=config_bernina.pgroup, +# config_adj=config_JFs, +# module_name="eco.endstations.bernina_robot", +# lazy=True, +# name="robot", +# ) + +namespace.append_obj( + "MpodModule", + "SARES21-PS7071", + [1, 2, 3, 4], + ["ch1", "ch2", "ch3", "ch4"], + module_string="LV_OMPV_1", + name="power_LV_patch1", + lazy=True, + module_name="eco.devices_general.powersockets", +) + +namespace.append_obj( + "MpodModule", + "SARES21-PS7071", + [5, 6, 7, 8], + ["ch1", "ch2", "ch3", "ch4"], + module_string="LV_OMPV_1", + name="power_LV_patch2", + lazy=True, + module_name="eco.devices_general.powersockets", +) + +# +# namespace.append_obj( +# "MpodModule", +# "SARES21-CPCL-PS7071", +# [1,2,3,4], +# ['ch1','ch2','ch3','ch4'], +# module_string='HV_EHS_3', +# name="power_HV_patch1", +# module_name="eco.devices_general.powersockets", +# ) +# +# namespace.append_obj( +# "MpodModule", +# "SARES21-CPCL-PS7071", +# [5,6,7,8], +# ['ch1','ch2','ch3','ch4'], +# module_string='HV_EHS_3', +# name="power_HV_patch2", +# module_name="eco.devices_general.powersockets", +# ) + +namespace.append_obj( + "CheckerCA", + module_name="eco.acquisition.checkers", + pvname="SLAAR21-LTIM01-EVR0:CALCI", + thresholds=[0.2, 10], + required_fraction=0.6, + filepath_thresholds="/photonics/home/gac-bernina/eco/configuration/checker_thresholds_default", + filepath_fraction="/photonics/home/gac-bernina/eco/configuration/checker_required_fraction_default", + lazy=True, + name="checker_mon_opt_ioxos", +) + +namespace.append_obj( + "CheckerBS", + module_name="eco.acquisition.checkers", + bs_channel="SAROP21-PBPS133:INTENSITY", + thresholds=[0.2, 10], + required_fraction=0.6, + filepath_thresholds="/photonics/home/gac-bernina/eco/configuration/checker_thresholds_default", + filepath_fraction="/photonics/home/gac-bernina/eco/configuration/checker_required_fraction_default", + lazy=True, + name="checker", +) + + +### draft new epics daq ### +namespace.append_obj( + "EpicsDaq", + channel_list=channels_CA_epicsdaq, + name="daq_epics_local", + module_name="eco.acquisition.epics_data", + lazy=True, +) +### old epics daq ### +# namespace.append_obj( +# "ChannelList", +# name="epics_channel_list", +# file_name="/sf/bernina/config/channel_lists/default_channel_list_epics", +# module_name="eco.utilities.config", +# ) + +# namespace.append_obj( +# "Epicstools", +# name="epics_daq", +# channel_list=epics_channel_list, +# default_file_path=f"/sf/bernina/data/{config_berninamesp['pgroup']}/res/epics_daq/", +# module_name="eco.acquisition.epics_data", +# ) + + +## TODO pgroup non adjustable/dynamically changeable! +namespace.append_obj( + "Scans", + name="scans_epics", + module_name="eco.acquisition.scan", + data_base_dir=f"{config_bernina.pgroup()}/scan_data", + scan_info_dir=f"{daq_epics_local.default_file_path()}/{config_bernina.pgroup()}/scan_info", + default_counters=[daq_epics_local], + checker=None, + scan_directories=True, + run_table=None, + lazy=True, +) +# +# +##### standard DAQ ####### + + + +namespace.append_obj( + "Daq", + instrument="bernina", + pgroup=config_bernina.pgroup, + channels_JF=channels_JF, + channels_BS=channels_BS, + channels_BSCAM=channels_BSCAM, + channels_CA=channels_CA, + config_JFs=config_JFs, + pulse_id_adj="SLAAR21-LTIM01-EVR0:RX-PULSEID", + event_master=event_master, + detectors_event_code=50, + rate_multiplicator="auto", + name="daq", + namespace=namespace, + checker=checker, + elog=elog, + module_name="eco.acquisition.daq_client", + lazy=True, +) + + +# namespace.append_obj( +# "Daq", +# instrument="bernina", +# broker_address="http://sf-daq-1:10002", +# pgroup=config_bernina.pgroup, +# channels_JF=channels_JF, +# channels_BS=channels_BS, +# channels_BSCAM=channels_BSCAM, +# channels_CA=channels_CA, +# config_JFs=config_JFs, +# pulse_id_adj="SLAAR21-LTIM01-EVR0:RX-PULSEID", +# event_master=event_master, +# detectors_event_code=50, +# name="daq_dev", +# module_name="eco.acquisition.daq_client", +# lazy=True, +# ) + + +#TODO: need to check if the value property actually works here for the pgroup in the run table to make is dynamic! +namespace.append_obj( + "Run_Table2", + name="run_table", + module_name="eco.utilities.runtable", + exp_id=config_bernina.pgroup._value, + exp_path=f"/sf/bernina/data/{config_bernina.pgroup._value}/res/run_table/", + devices="eco.bernina", + keydf_fname="/sf/bernina/config/src/python/gspread/gspread_keys.pkl", + cred_fname="/sf/bernina/config/src/python/gspread/pandas_push", + gsheet_key_path="/sf/bernina/config/eco/reference_values/run_table_gsheet_keys", + lazy=True, + parse=True, # <-- set this to False to avoid parsing and only add the status information to the runtable +) + + +def _wait_for_tasks(scan, **kwargs): + print("checking remaining tasks from previous scan ...") + for task in scan.remaining_tasks: + task.join() + print("... done.") + + +def _append_namesace_status_to_scan( + scan, daq=daq, namespace=namespace, append_status_info=True, **kwargs +): + if not append_status_info: + return + namespace_status = namespace.get_status(base=None) + stat = {"status_run_start": namespace_status} + scan.status = stat + + +def _write_namespace_status_to_scan( + scan, daq=daq, namespace=namespace, append_status_info=True, end_scan=True, **kwargs +): + if not append_status_info: + return + if end_scan: + namespace_status = namespace.get_status(base=None) + scan.status["status_run_end"] = namespace_status + if (not end_scan) and not (len(scan.values_done) == 1): + return + if hasattr(scan, "daq_run_number"): + runno = scan.daq_run_number + else: + runno = daq.get_last_run_number() + pgroup = daq.pgroup + tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/stat_run{runno:04d}") + tmpdir.mkdir(exist_ok=True, parents=True) + try: + tmpdir.chmod(0o775) + except: + pass + + statusfile = tmpdir / Path("status.json") + if not statusfile.exists(): + with open(statusfile, "w") as f: + json.dump(scan.status, f, sort_keys=True, cls=NumpyEncoder, indent=4) + else: + with open(statusfile, "r+") as f: + f.seek(0) + json.dump(scan.status, f, sort_keys=True, cls=NumpyEncoder, indent=4) + f.truncate() + print("Wrote status with seek truncate!") + if not statusfile.group() == statusfile.parent.group(): + shutil.chown(statusfile, group=statusfile.parent.group()) + + response = daq.append_aux( + statusfile.resolve().as_posix(), + pgroup=pgroup, + run_number=runno, + ) + print("####### transfer status #######") + print(response.json()) + print("###############################") + scan.scan_info["scan_parameters"]["status"] = "aux/status.json" + + +def _write_namespace_aliases_to_scan(scan, daq=daq, force=False, **kwargs): + if force or (len(scan.values_done) == 1): + namespace_aliases = namespace.alias.get_all() + if hasattr(scan, "daq_run_number"): + runno = scan.daq_run_number + else: + runno = daq.get_last_run_number() + pgroup = daq.pgroup + tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/aliases_run{runno:04d}") + tmpdir.mkdir(exist_ok=True, parents=True) + try: + tmpdir.chmod(0o775) + except: + pass + aliasfile = tmpdir / Path("aliases.json") + if not Path(aliasfile).exists(): + with open(aliasfile, "w") as f: + json.dump( + namespace_aliases, f, sort_keys=True, cls=NumpyEncoder, indent=4 + ) + else: + with open(aliasfile, "r+") as f: + f.seek(0) + json.dump( + namespace_aliases, f, sort_keys=True, cls=NumpyEncoder, indent=4 + ) + f.truncate() + if not aliasfile.group() == aliasfile.parent.group(): + shutil.chown(aliasfile, group=aliasfile.parent.group()) + + scan.remaining_tasks.append( + Thread( + target=daq.append_aux, + args=[aliasfile.resolve().as_posix()], + kwargs=dict(pgroup=pgroup, run_number=runno), + ) + ) + # DEBUG + print( + f"Sending scan_info_rel.json in {Path(aliasfile).parent.stem} to run number {runno}." + ) + scan.remaining_tasks[-1].start() + # response = daq.append_aux( + # aliasfile.resolve().as_posix(), + # pgroup=pgroup, + # run_number=runno, + # ) + print("####### transfer aliases started #######") + # print(response.json()) + # print("################################") + scan.scan_info["scan_parameters"]["aliases"] = "aux/aliases.json" + + +def _message_end_scan(scan, **kwargs): + print(f"Finished run {scan.run_number}.") + if hasattr(scan, "daq_run_number"): + runno_daq_saved = scan.daq_run_number + print(f"daq_run_number is run {runno_daq_saved}.") + + try: + runno = daq.get_last_run_number() + print(f"daq last run number is run {runno}.") + except: + pass + + try: + e = pyttsx3.init() + e.say(f"Finished run {scan.run_number}.") + e.runAndWait() + e.stop() + except: + print("Audio output failed.") + + +# def _copy_scan_info_to_raw(scan, daq=daq): +# run_number = daq.get_last_run_number() +# pgroup = daq.pgroup +# print(f"Copying info file to run {run_number} to the raw directory of {pgroup}.") +# response = daq.append_aux( +# scan.scan_info_filename, pgroup=pgroup, run_number=run_number +# ) +# print(f"Status: {response.json()['status']} Message: {response.json()['message']}") + + +def _create_general_run_info(scan, daq=daq, **kwargs): + with open(scan.scan_info_filename, "r") as f: + si = json.load(f) + + info = {} + # general info, potentially automatically filled + info["general"] = {} + # individual data filled by daq/writers/user through api + info["start"] = {} + info["end"] = {} + info["steps"] = [] + + +def _copy_scan_info_to_raw(scan, daq=daq, **kwargs): + t_start = time.time() + + scan.writeScanInfo() + + # get data that should come later from api or similar. + run_directory = list( + Path(f"/sf/bernina/data/{daq.pgroup}/raw").glob(f"run{scan.run_number:04d}*") + )[0].as_posix() + with open(scan.scan_info_filename, "r") as f: + si = json.load(f) + + # correct some data in there (relative paths for now) + from os.path import relpath + + newfiles = [] + for files in si["scan_files"]: + newfiles.append([relpath(file, run_directory) for file in files]) + + si["scan_files"] = newfiles + + # save temprary file and send then to raw + if hasattr(scan, "daq_run_number"): + runno = scan.daq_run_number + else: + runno = daq.get_last_run_number() + pgroup = daq.pgroup + tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/info_run{runno:04d}") + tmpdir.mkdir(exist_ok=True, parents=True) + try: + tmpdir.chmod(0o775) + except: + pass + scaninfofile = tmpdir / Path("scan_info_rel.json") + if not Path(scaninfofile).exists(): + with open(scaninfofile, "w") as f: + json.dump(si, f, sort_keys=True, cls=NumpyEncoder, indent=4) + else: + with open(scaninfofile, "r+") as f: + f.seek(0) + json.dump(si, f, sort_keys=True, cls=NumpyEncoder, indent=4) + f.truncate() + if not scaninfofile.group() == scaninfofile.parent.group(): + shutil.chown(scaninfofile, group=scaninfofile.parent.group()) + # print(f"Copying info file to run {runno} to the raw directory of {pgroup}.") + + scan.remaining_tasks.append( + Thread( + target=daq.append_aux, + args=[scaninfofile.as_posix()], + kwargs=dict(pgroup=pgroup, run_number=runno), + ) + ) + # DEBUG + print( + f"Sending scan_info_rel.json in {Path(scaninfofile).parent.stem} to run number {runno}." + ) + scan.remaining_tasks[-1].start() + # response = daq.append_aux(scaninfofile.as_posix(), pgroup=pgroup, run_number=runno) + # print(f"Status: {response.json()['status']} Message: {response.json()['message']}") + # print( + # f"--> creating and copying file took{time.time()-t_start} s, presently adding to deadtime." + # ) + + +from eco.detector import Jungfrau + + +def _copy_selected_JF_pedestals_to_raw( + scan, daq=daq, copy_selected_JF_pedestals_to_raw=True, **kwargs +): + def copy_to_aux(daq, scan): + if hasattr(scan, "daq_run_number"): + runno = scan.daq_run_number + else: + runno = daq.get_last_run_number() + + pgroup = daq.pgroup + + for jf_id in daq.channels["channels_JF"](): + jf = Jungfrau(jf_id, name="noname", pgroup_adj=config_bernina.pgroup) + print( + f"Copying {jf_id} pedestal to run {runno} in the raw directory of {pgroup}." + ) + response = daq.append_aux( + jf.get_present_pedestal_filename_in_run(intempdir=True), + pgroup=pgroup, + run_number=runno, + ) + print( + f"Status: {response.json()['status']} Message: {response.json()['message']}" + ) + print( + f"Copying {jf_id} gainmap to run {runno} in the raw directory of {pgroup}." + ) + + response = daq.append_aux( + jf.get_present_gain_filename_in_run(intempdir=True), + pgroup=pgroup, + run_number=runno, + ) + print( + f"Status: {response.json()['status']} Message: {response.json()['message']}" + ) + + if copy_selected_JF_pedestals_to_raw: + scan.remaining_tasks.append(Thread(target=copy_to_aux, args=[daq, scan])) + scan.remaining_tasks[-1].start() + + +def _increment_daq_run_number(scan, daq=daq, **kwargs): + try: + daq_last_run_number = daq.get_last_run_number() + if int(scan.run_number) is int(daq_last_run_number) + 1: + print("############ incremented ##########") + daq_run_number = daq.get_next_run_number() + else: + daq_run_number = daq_last_run_number + if int(scan.run_number) is not int(daq_run_number): + print( + f"Difference in run number between eco {int(scan.run_number)} and daq {int(daq_run_number)}: using run number {int(scan.run_number)}" + ) + if int(scan.run_number) > int(daq_run_number): + n = int(scan.run_number) - int(daq_run_number) + print("Increasing daq run_number") + for i in range(n): + rn = daq.get_next_run_number() + print(rn) + scan.daq_run_number = rn + else: + scan.daq_run_number = daq_run_number + + except Exception as e: + print(e) + + +class Monitor: + def __init__(self, pvname, start_immediately=True): + self.data = {} + self.print = False + self.pv = PV(pvname) + self.cb_index = None + if start_immediately: + self.start_callback() + + def start_callback(self): + self.cb_index = self.pv.add_callback(self.append) + + def stop_callback(self): + self.pv.remove_callback(self.cb_index) + + def append(self, pvname=None, value=None, timestamp=None, **kwargs): + if not (pvname in self.data): + self.data[pvname] = [] + ts_local = time.time() + self.data[pvname].append( + {"value": value, "timestamp": timestamp, "timestamp_local": ts_local} + ) + if self.print: + print( + f"{pvname}: {value}; time: {timestamp}; time_local: {ts_local}; diff: {ts_local-timestamp}" + ) + + +import traceback + + +def append_scan_monitors( + scan, + daq=daq, + custom_monitors={}, + **kwargs, +): + scan.monitors = {} + for adj in scan.adjustables: + try: + tname = adj.alias.get_full_name() + except Exception: + tname = adj.name + traceback.print_exc() + try: + scan.monitors[tname] = Monitor(adj.pvname) + except Exception: + print(f"Could not add CA monitor for {tname}") + traceback.print_exc() + try: + rname = adj.readback.alias.get_full_name() + except Exception: + print("no readback configured") + traceback.print_exc() + try: + scan.monitors[rname] = Monitor(adj.readback.pvname) + except Exception: + print(f"Could not add CA readback monitor for {tname}") + traceback.print_exc() + + for tname, tobj in custom_monitors.items(): + try: + if type(tobj) is str: + tmonpv = tobj + scan.monitors[tname] = Monitor(tmonpv) + print(f"Added custom monitor for {tname}") + except Exception: + print(f"Could not add custom monitor for {tname}") + traceback.print_exc() + try: + tname = daq.pulse_id.alias.get_full_name() + scan.monitors[tname] = Monitor(daq.pulse_id.pvname) + except Exception: + print(f"Could not add daq.pulse_id monitor") + traceback.print_exc() + + +def end_scan_monitors(scan, daq=daq, **kwargs): + for tmon in scan.monitors: + scan.monitors[tmon].stop_callback() + + monitor_result = {tmon: scan.monitors[tmon].data for tmon in scan.monitors} + + ####### + # get data that should come later from api or similar. + run_directory = list( + Path(f"/sf/bernina/data/{daq.pgroup}/raw").glob(f"run{scan.run_number:04d}*") + )[0].as_posix() + + # correct some data in there (relative paths for now) + from os.path import relpath + + # save temprary file and send then to raw + if hasattr(scan, "daq_run_number"): + runno = scan.daq_run_number + else: + runno = daq.get_last_run_number() + pgroup = daq.pgroup + tmpdir = Path(f"/sf/bernina/data/{pgroup}/res/tmp/info_run{runno:04d}") + tmpdir.mkdir(exist_ok=True, parents=True) + try: + tmpdir.chmod(0o775) + except: + pass + scanmonitorfile = tmpdir / Path("scan_monitor.pkl") + if not Path(scanmonitorfile).exists(): + with open(scanmonitorfile, "wb") as f: + pickle.dump(monitor_result, f) + + print(f"Copying monitor file to run {runno} to the raw directory of {pgroup}.") + response = daq.append_aux( + scanmonitorfile.as_posix(), pgroup=pgroup, run_number=runno + ) + print(f"Status: {response.json()['status']} Message: {response.json()['message']}") + + # scan.monitors = None + + +def _init_all(scan, append_status_info=True, **kwargs): + if not append_status_info: + return + namespace.init_all(silent=False) + + +callbacks_start_scan = [] +callbacks_start_scan.append(_init_all) +callbacks_start_scan.append(_wait_for_tasks) +callbacks_start_scan.append(_append_namesace_status_to_scan) +callbacks_start_scan.append(_increment_daq_run_number) +callbacks_start_scan.append(append_scan_monitors) +callbacks_end_step = [] +callbacks_end_step.append(_copy_scan_info_to_raw) +callbacks_end_step.append(_write_namespace_aliases_to_scan) +callbacks_end_step.append( + lambda scan, daq=daq, namespace=namespace, append_status_info=True, end_scan=True, **kwargs: _write_namespace_status_to_scan( + scan, + daq=daq, + namespace=namespace, + append_status_info=append_status_info, + end_scan=False, + **kwargs, + ) +) +callbacks_end_scan = [] +callbacks_end_scan.append(_write_namespace_status_to_scan) +callbacks_end_scan.append(_copy_scan_info_to_raw) +callbacks_end_scan.append( + lambda scan, daq=daq, force=True, **kwargs: _write_namespace_aliases_to_scan( + scan, daq=daq, force=force, **kwargs + ) +) +callbacks_end_scan.append(_copy_selected_JF_pedestals_to_raw) +callbacks_end_scan.append(end_scan_monitors) +callbacks_end_scan.append(_message_end_scan) + +# >>>> Extract for run_table and elog + + +# if self._run_table or self._elog: +def _create_metadata_structure_start_scan( + scan, run_table=run_table, elog=elog, append_status_info=True, **kwargs +): + runname = os.path.basename(scan.fina).split(".")[0] + runno = int(runname.split("run")[1].split("_")[0]) + metadata = { + "type": "scan", + "name": runname.split("_", 1)[1], + "scan_info_file": scan.scan_info_filename, + } + for n, adj in enumerate(scan.adjustables): + nname = None + nId = None + if hasattr(adj, "Id"): + nId = adj.Id + if hasattr(adj, "name"): + nname = adj.name + + metadata.update( + { + f"scan_motor_{n}": nname, + f"from_motor_{n}": scan.values_todo[0][n], + f"to_motor_{n}": scan.values_todo[-1][n], + f"id_motor_{n}": nId, + } + ) + if np.mean(np.diff(scan.pulses_per_step)) < 1: + pulses_per_step = scan.pulses_per_step[0] + else: + pulses_per_step = scan.pulses_per_step + metadata.update( + { + "steps": len(scan.values_todo), + "pulses_per_step": pulses_per_step, + "counters": [daq.name for daq in scan.counterCallers], + } + ) + + try: + try: + metadata.update({"scan_command": get_ipython().user_ns["In"][-1]}) + except: + print("Count not retrieve ipython scan command!") + + message_string = f"#### Run {runno}" + if metadata["name"]: + message_string += f': {metadata["name"]}\n' + else: + message_string += "\n" + + if "scan_command" in metadata.keys(): + message_string += "`" + metadata["scan_command"] + "`\n" + message_string += "`" + metadata["scan_info_file"] + "`\n" + elog_ids = elog.post( + message_string, + Title=f'Run {runno}: {metadata["name"]}', + text_encoding="markdown", + ) + scan._elog_id = elog_ids[1] + metadata.update({"elog_message_id": scan._elog_id}) + metadata.update( + {"elog_post_link": scan._elog.elogs[1]._log._url + str(scan._elog_id)} + ) + except: + print("Elog posting failed with:") + traceback.print_exc() + if not append_status_info: + return + d = {} + ## use values from status for run_table + try: + status = scan.status["status_run_start"] + d = status["settings"] + d.update(status["status"]) + except: + print("Tranferring values from status to run_table did not work") + t_start_rt = time.time() + try: + run_table.append_run(runno, metadata=metadata, d=d) + except: + print("WARNING: issue adding data to run table") + print(f"RT appending: {time.time()-t_start_rt:.3f} s") + + +# <<<< Extract for run table and elog + +# TODO resove scans pgroup sensitivity! Clearly non dynamic. +namespace.append_obj( + "Scans", + # data_base_dir="scan_data", + # scan_info_dir=f"/sf/bernina/data/{config_bernina.pgroup()}/res/scan_info", + default_counters=[daq], + callbacks_start_scan=[], + callbacks_end_step=[], + callbacks_end_scan=[], + elog=elog, + name="scans", + module_name="eco.acquisition.scan", + lazy=True, +) + + +##################################################################################################### +## more temporary devices will be outcoupled to temorary module. +# namespace.append_obj( +# "RIXS", +# lazy=True, +# name="rixs", +# module_name="eco.endstations.bernina_rixs", +# ) + +#### Beam pointing cameras for THz setups #### + + +# namespace.append_obj( +# "CameraBasler", +# pvname="SLAAR21-LCAM-C531", +# lazy=True, +# name="cam_NIR_position", +# camserver_group=["Laser", "Bernina"], +# module_name="eco.devices_general.cameras_swissfel", +# ) +# +# +# namespace.append_obj( +# "CameraBasler", +# pvname="SLAAR21-LCAM-C511", +# lazy=True, +# name="cam_NIR_angle", +# camserver_group=["Laser", "Bernina"], +# module_name="eco.devices_general.cameras_swissfel", +# ) + +namespace.append_obj( + "AxisPTZ", + "bernina-cam-mobile1", + lazy=True, + name="cam_mob1", + module_name="eco.devices_general.cameras_ptz", +) + +# this is the large inline camera +namespace.append_obj( + "BerninaInlineMicroscope", + # pvname_camera="SARES20-CAMS142-M3", #THC + pvname_camera="SARES20-CAMS142-M1", # GIC + lazy=True, + name="samplecam_inline", + module_name="eco.microscopes", +) + +# namespace.append_obj( +# "CameraBasler", +# "SARES20-CAMS142-C1", +# lazy=True, +# name="samplecam_front", +# module_name="eco.microscopes", +# ) + +# namespace.append_obj( +# "MicroscopeMotorRecord", +# pvname_camera="SARES20-CAMS142-C1", +# lazy=True, +# name="samplecam", +# module_name="eco.microscopes", +# pvname_zoom="SARES20-MF1:MOT_5", +# ) + + +# from eco.devices_general.cameras_swissfel import FeturaMicroscope +# from eco.elements.assembly import Assembly +# class SpatialTimetool(Assembly): +# def __init__(self, pvname_camera=None, pvname_base_zoom=None, pvname_target_stage = None, name=None): +# super().__init__(name=name) +# self._append(FeturaMicroscope, pvname_camera = pvname_camera, pvname_base_zoom=pvname_base_zoom, name = "camera", camserver_alias=name, is_display="recursive") +# if pvname_target_stage: +# self._append(MotorRecord, pvname = pvname_target_stage, name = "target_transl") +# self._append(MotorRecord,'SARES23-USR:MOT_2', name='delaystage', is_setting=True) + +# namespace.append_obj( +# SpatialTimetool, +# pvname_camera = "SARES20-CAMS142-M4", +# pvname_base_zoom="SARES20-FETURA", +# pvname_target_stage = "SARES20-MF1:MOT_8", +# name="tt_spatial", +# lazy=True, +# ) + +# namespace.append_obj( +# "MicroscopeFeturaPlus", +# "SARES20-PROF142-M1", +# lazy=True, +# name="samplecam_highres", +# module_name="eco.microscopes", +# ) + +# namespace.append_obj( +# "MicroscopeMotorRecord", +# "SARES20-CAMS142-C1", +# lazy=True, +# pvname_zoom="SARES20-MF1:MOT_7", +# name="samplecam_topview", +# module_name="eco.microscopes", +# ) + +# namespace.append_obj( +# "CameraBasler", +# "SARES20-CAMS142-M2", +# lazy=True, +# name="samplecam_sideview_45", +# module_name="eco.devices_general.cameras_swissfel", +# ) + +namespace.append_obj( + "CameraBasler", + # "SARES20-CAMS142-C1", # THC + "SARES20-CAMS142-M3", # GIC + lazy=True, + name="samplecam_sideview", + module_name="eco.devices_general.cameras_swissfel", +) + +namespace.append_obj( + "OxygenSensor", + "SARES20-CWAG-GPS01:ADC08", + lazy=True, + name="oxygen_sensor", + module_name="eco.devices_general.sensors_ai", +) + +# namespace.append_obj( +# "CameraBasler", +# "SARES20-CAMS142-C2", +# lazy=True, +# name="samplecam_back_racks", +# module_name="eco.devices_general.cameras_swissfel", +# ) + +# namespace.append_obj( +# "CameraBasler", +# "SARES20-CAMS142-C3", +# lazy=True, +# name="samplecam_back_door", +# module_name="eco.devices_general.cameras_swissfel", +# ) + + +namespace.append_obj( + "CameraBasler", + "SARES20-CAMS142-C2", + lazy=True, + name="samplecam_xrd", + module_name="eco.devices_general.cameras_swissfel", +) + +# namespace.append_obj( +# "PaseShifterAramis", +# "SLAAR02-TSPL-EPL", +# lazy=True, +# name="phase_shifter", +# module_name="eco.devices_general.timing", +# ) + + +# will be split in permanent and temporary +namespace.append_obj( + "LaserBernina", + lazy=True, + name="las", + module_name="eco.loptics.bernina_laser", + pvname="SLAAR21-LMOT", +) +namespace.append_obj( + "PositionMonitors", + lazy=True, + name="las_pointing_monitors", + module_name="eco.loptics.bernina_laser", +) + +# namespace.append_obj( +# "IncouplingCleanBernina", +# lazy=True, +# name="clic", +# module_name="eco.loptics.bernina_laser", +# ) +namespace.append_obj( + "MidIR", + lazy=True, + name="midir", + module_name="eco.loptics.bernina_laser", +) + +from ..elements.assembly import Assembly +from ..devices_general.motors import SmaractStreamdevice +from ..loptics.bernina_laser import DelayTime + + +# namespace.append_obj( +# "Organic_crystal_breadboard", +# lazy=True, +# name="ocb", +# module_name="eco.endstations.bernina_sample_environments", +# Id="SARES23", +# ) + +from ..epics.adjustable import AdjustablePv, AdjustablePvEnum + + +# class Double_Pulse_Pump(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) + +# ### dp smaract stages #### + +# self.motor_configuration = { +# "delaystage_both": { +# "id": "SARES23-USR:MOT_15", +# }, +# "delaystage_pulse2": { +# "id": "SARES23-USR:MOT_1", +# }, +# "wp_both": { +# "id": "SARES23-USR:MOT_3", +# }, +# "wp_pulse2": { +# "id": "SARES23-USR:MOT_2", +# }, +# } +# for name, config in self.motor_configuration.items(): +# self._append( +# SmaractRecord, +# pvname=config["id"], +# name=name, +# is_setting=True, +# ) +# self._append( +# DelayTime, self.delaystage_both, name="delay_both", is_setting=True +# ) +# self._append( +# DelayTime, self.delaystage_pulse2, name="delay_pulse2", is_setting=True +# ) + + +# namespace.append_obj( +# Double_Pulse_Pump, +# lazy=True, +# name="pump", +# ) + + +# ad hoc N2 jet readout +class N2jet(Assembly): + def __init__(self, name=None): + super().__init__(name=name) + + ### lakeshore temperatures #### + self._append( + AdjustablePv, + pvsetname="SARES20-CRYO:TEMP-C_RBV", + pvreadbackname="SARES20-CRYO:TEMP-C_RBV", + accuracy=0.1, + name="sample_temp", + is_setting=False, + ) + ### oxford jet readouts #### + self._append( + AdjustablePv, + pvsetname="SARES20-OXCS:GasSetPoint", + pvreadbackname="SARES20-OXCS:GasSetPoint", + accuracy=0.1, + name="gas_temp_setpoint", + is_setting=False, + ) + self._append( + AdjustablePv, + pvsetname="SARES20-OXCS:GasTemp", + pvreadbackname="SARES20-OXCS:GasTemp", + accuracy=0.1, + name="gas_temp", + is_setting=False, + ) + self._append( + AdjustablePv, + pvsetname="SARES20-OXCS:GasFlow", + pvreadbackname="SARES20-OXCS:GasFlow", + accuracy=0.1, + name="gas_flow", + is_setting=False, + ) + self._append( + AdjustablePv, + pvsetname="SARES20-OXCS:Remaining", + pvreadbackname="SARES20-OXCS:Remaining", + accuracy=0.1, + name="gas_remaining", + is_setting=False, + ) +from eco.devices_general.motors import ThorlabsPiezoRecord + + +# # ad hoc incoupling device +class Incoupling(Assembly): + def __init__(self, name=None): + super().__init__(name=name) + self._append(SmaractRecord, "SARES20-MCS1:MOT_13", name="ry", is_setting=True) + self._append(SmaractRecord, "SARES23-USR:MOT_4", name="rx", is_setting=True) + self._append(SmaractRecord, "SARES20-MCS1:MOT_15", name="y", is_setting=True) + self._append(MotorRecord, "SARES20-MF2:MOT_5", name="x",is_setting=True) + self._append(SmaractRecord, "SARES23-USR:MOT_3", name="eos_focus",is_setting=True) + + + self._append(AnalogOutput, 'SLAAR21-LDIO-LAS6991:DAC06_VOLTS',name='eos_fb_rx', is_setting=True) + self._append(AnalogOutput, 'SLAAR21-LDIO-LAS6991:DAC05_VOLTS',name='eos_fb_ry', is_setting=True) + + self._append(AnalogOutput, 'SLAAR21-LDIO-LAS6991:DAC09_VOLTS',name='nir_mirr1_ry', is_setting=True) + self._append(AnalogOutput, 'SLAAR21-LDIO-LAS6991:DAC10_VOLTS',name='nir_mirr1_rx', is_setting=True) + + self._append(AnalogOutput, 'SLAAR21-LDIO-LAS6991:DAC11_VOLTS',name='nir_mirr2_ry', is_setting=True) + self._append(AnalogOutput, 'SLAAR21-LDIO-LAS6991:DAC12_VOLTS',name='nir_mirr2_rx', is_setting=True) + + self._append( + AdjustablePv, + pvsetname="SLAAR21-LCAM-C561:FIT2_REQUIRED.PROC", + name="eos_fb_setpoint_rq", + accuracy=1, + is_setting=True, + ) + self._append( + AdjustablePv, + pvsetname="SLAAR21-LCAM-C561:FIT2_DEFAULT.PROC", + name="eos_fb_setpoint_df", + accuracy=1, + is_setting=True, + ) + self._append( + AdjustablePv, + pvsetname="SLAAR21-LTIM01-EVR0:CALCW.A", + name="eos_fd_enable", + accuracy=1, + is_setting=True, + ) + try: + self.motor_configuration_thorlabs = { + "nir_block": { + "pvname": "SLAAR21-LMOT-ELL2", + }, + "eos_block": { + "pvname": "SLAAR21-LMOT-ELL4", + } + + } + + ### thorlabs piezo motors ### + for name, config in self.motor_configuration_thorlabs.items(): + self._append( + ThorlabsPiezoRecord, + pvname=config["pvname"], + name=name, + is_setting=True, + ) + except Exception as e: + print(e) + # self._append(AdjustableVirtual, + # [self.crystal, self.hwp], + # self.thz_pol_get, + # self.thz_pol_set, + # name="thz_polarization", + # ) + + # def thz_pol_set(self, val): + # return 1.0 * val, 1.0 / 2 * val + + # def thz_pol_get(self, val, val2): + # return 1.0 * val2 + +namespace.append_obj( + Incoupling, + lazy=True, + name="las_inc", +) + + + + +# namespace.append_obj( +# "Organic_crystal_breadboard", +# lazy=True, +# name="ocb", +# delay_offset_detector=NamespaceComponent(namespace, "thc.delay_x_center"), +# thc_x_adjustable=NamespaceComponent(namespace, "thc.x"), +# module_name="eco.endstations.bernina_sample_environments", +# ) +# namespace.append_obj( +# "Organic_crystal_breadboard", +# lazy=True, +# name="ocb", +# delay_offset_detector=None, +# thc_x_adjustable=None, +# module_name="eco.endstations.bernina_sample_environments", +# ) + +# namespace.append_obj( +# "Electro_optic_sampling", +# lazy=True, +# name="eos", +# module_name="eco.endstations.bernina_sample_environments", +# ) +# namespace.append_obj( +# "Electro_optic_sampling_new", +# lazy=True, +# name="eos_new", +# module_name="eco.endstations.bernina_sample_environments", +# ) +# class Sample_stages(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) +# self._append(MotorRecord, "SARES20-MF1:MOT_11", name="x", is_setting=True) +# self._append(MotorRecord, "SARES20-MF1:MOT_9", name="y", is_setting=True) + +# namespace.append_obj( +# Sample_stages, +# lazy=True, +# name="sample", +# ) + +# class LaserSteering(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) +# self._append(SmaractRecord, "SARES23-USR:MOT_3", name="mirr1_pitch", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_4", name="mirr1_roll", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_14", name="mirr2_pitch", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_12", name="mirr2_roll", is_setting=True) + +# class THzGeneration(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) +# self._append(SmaractRecord, "SARES23-LIC:MOT_16", name="par_x", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_8", name="mirr_x", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_7", name="mirr_z", is_setting=True) +# self._append(SmaractRecord, "SARES23-LIC:MOT_18", name="mirr_ry", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_9", name="mirr_rz", is_setting=True) +# self._append(SmaractRecord, "SARES23-LIC:MOT_15", name="polarizer", is_setting=True) + + +# class THzVirtualStages(Assembly): +# def __init__(self, name=None, mx=None, mz=None, px=None, pz=None): +# super().__init__(name=name) +# self._mx = mx +# self._mz = mz +# self._px = px +# self._pz = pz +# self._append( +# AdjustableFS, +# "/photonics/home/gac-bernina/eco/configuration/p21145_mirr_x0", +# name="offset_mirr_x", +# default_value=0, +# is_setting=True, +# ) +# self._append( +# AdjustableFS, +# "/photonics/home/gac-bernina/eco/configuration/p21145_mirr_z0", +# name="offset_mirr_z", +# default_value=0, +# is_setting=True, +# ) +# self._append( +# AdjustableFS, +# "/photonics/home/gac-bernina/eco/configuration/p21145_par_x0", +# name="offset_par_x", +# default_value=0, +# is_setting=True, +# ) +# self._append( +# AdjustableFS, +# "/photonics/home/gac-bernina/eco/configuration/p21145_par_z0", +# name="offset_par_z", +# default_value=0, +# is_setting=True, +# ) + +# def get_divergence(mx, px): +# return px - self.offset_par_x() + +# def set_divergence(x): +# mx = self.offset_mirr_x() + x +# px = self.offset_par_x() + x +# return mx, px + +# def get_focus_z(mx, pz): +# return pz - self.offset_par_z() + +# def set_focus_z(z): +# mz = self.offset_mirr_z() + z +# pz = self.offset_par_z() + z +# return mz, pz + +# self._append( +# AdjustableVirtual, +# [mx, px], +# get_divergence, +# set_divergence, +# name="divergence_virtual", +# ) +# self._append( +# AdjustableVirtual, +# [mz, pz], +# get_focus_z, +# set_focus_z, +# name="focus_virtual", +# ) + +# def set_offsets_to_current_value(self): +# self.offset_mirr_x.mv(self._mx()) +# self.offset_mirr_z.mv(self._mz()) +# self.offset_par_x.mv(self._px()) +# self.offset_par_z.mv(self._pz()) + + +# class THz(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) +# self._append(SmaractRecord, "SARES23-USR:MOT_6", name="par_x", is_setting=True) +# self._append(MotorRecord, "SARES20-MF1:MOT_10", name="par_y", is_setting=True) +# self._append(SmaractRecord, "SARES23-LIC:MOT_13", name="par_z", is_setting=True) +# self._append(SmaractRecord, "SARES23-LIC:MOT_14", name="par_rx", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_15", name="par_ry", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_1", name="delaystage_thz", is_setting=True,) +# self._append(DelayTime, self.delaystage_thz, name="delay_thz", is_setting=False, is_display=True,) +# self._append(LaserSteering, name="ir_pointing", is_setting=False) +# self._append(THzGeneration, name="generation", is_setting=False) + +### Virtual stages ### +# self._append( +# THzVirtualStages, +# name="virtual_stages", +# mx=self.generation.mirr_x, +# mz=self.generation.mirr_z, +# px=self.generation.par_x, +# pz = self.par_z, +# is_setting=False) + + +# namespace.append_obj( +# THz, +# lazy=True, +# name="thz", +# self._append( +# AdjustableVirtual, +# [self.crystal_ROT, self.thz_wp], +# self.thz_pol_get, +# self.thz_pol_set, +# name="", +# ) +# # self.thz_polarization = AdjustableVirtual( +# # [self.crystal_ROT, self.thz_wp], +# # self.thz_pol_get, +# # self.thz_pol_set, +# # name="thz_polarization", +# # ) +# self._append( +# AdjustableVirtual, +# [self.delay_thz, self.delay_800_pump], +# self.delay_get, +# self.delay_set, +# name="combined_delay", +# ) + +# # self.combined_delay = AdjustableVirtual( +# # [self.delay_thz, self.delay_800_pump], +# # self.delay_get, +# # self.delay_set, +# # name="combined_delay", +# # ) + +# def thz_pol_set(self, val): +# return 1.0 * val, 1.0 / 2 * val + +# def thz_pol_get(self, val, val2): +# return 1.0 * val2 +# ) + +# class THz_in_air(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) + +# self._append(SmaractRecord, "SARES23-USR:MOT_5", name="crystal_ROT", is_setting=True) +# self._append(SmaractRecord, "SARES23-LIC:MOT_15", name="ir_1_z", is_setting=True) +# self._append(SmaractRecord, "SARES23-LIC:MOT_13", name="ir_1_Ry", is_setting=True) +# self._append(SmaractRecord, "SARES23-LIC:MOT_14", name="ir_1_Rx", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_10", name="ir_2_Rx", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_7", name="ir_2_Ry", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_9", name="para_2_x", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_3", name="thz_mir_x", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_1", name="thz_mir_z", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_8", name="thz_mir_Ry", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_2", name="thz_mir_Rz", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_6", name="focus_z", is_setting=True) +# self._append( +# MotorRecord, +# "SARES20-MF1:MOT_4", +# name="focus_y", +# is_setting=True, +# is_display=True, +# ) +# self._append(SmaractRecord, "SARES23-USR:MOT_14", name="focus_x", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_13", name="focus_Rz", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_15", name="focus_Ry", is_setting=True) +# self._append(SmaractRecord, "SARES23-USR:MOT_11", name="focus_Rx", is_setting=True) +# self._append(SmaractRecord, ":MOT_18", name="thz_wp", is_setting=True) +# self._append( +# SmaractRecord, "SARES23-LIC:MOT_16", name="delaystage_thz", is_setting=True +# ) +# self._append(DelayTime, self.delaystage_thz, name="delay_thz", is_setting=True) +# self._append( +# MotorRecord, +# "SLAAR21-LMOT-M521:MOTOR_1", +# name="delaystage_800_pump", +# is_setting=True, +# ) +# self._append( +# DelayTime, self.delaystage_800_pump, name="delay_800_pump", is_setting=True +# ) +# self._append( +# AdjustableFS, +# "/photonics/home/gac-bernina/eco/configuration/combined_delta", +# name="combined_delta", +# default_value=0, +# is_setting=True, +# ) +# self.delay_thz = DelayTime(self.delaystage_thz, name="delay_thz") + +# self._append( +# AdjustableVirtual, +# [self.crystal_ROT, self.thz_wp], +# self.thz_pol_get, +# self.thz_pol_set, +# name="", +# ) +# # self.thz_polarization = AdjustableVirtual( +# # [self.crystal_ROT, self.thz_wp], +# # self.thz_pol_get, +# # self.thz_pol_set, +# # name="thz_polarization", +# # ) +# self._append( +# AdjustableVirtual, +# [self.delay_thz, self.delay_800_pump], +# self.delay_get, +# self.delay_set, +# name="combined_delay", +# ) + +# # self.combined_delay = AdjustableVirtual( +# # [self.delay_thz, self.delay_800_pump], +# # self.delay_get, +# # self.delay_set, +# # name="combined_delay", +# # ) + +# def thz_pol_set(self, val): +# return 1.0 * val, 1.0 / 2 * val + +# def thz_pol_get(self, val, val2): +# return 1.0 * val2 + +# def delay_set(self, val): +# return 1.0 * val + self.combined_delta(), 1.0 * val + +# def delay_get(self, val, val2): +# return 1.0 * val2 + + +# namespace.append_obj( +# THz_in_air, +# lazy=True, +# name="thz", +# ) + + +namespace.append_obj( + "SmaractController", + "SARES20-MCS1:MOT_", + lazy=True, + name="smaract_usd", + module_name="eco.motion.smaract", +) +namespace.append_obj( + "SmaractController", + "SARES20-MCS2:MOT_", + lazy=True, + name="smaract_user1", + module_name="eco.motion.smaract", +) +namespace.append_obj( + "SmaractController", + "SARES20-MCS3:MOT_", + lazy=True, + name="smaract_user2", + module_name="eco.motion.smaract", +) + +from ..devices_general.motors import MotorRecord +from ..loptics.bernina_laser import DelayTime +from ..microscopes import MicroscopeMotorRecord + + +# class JohannAnalyzer(Assembly): +# def __init__(self, name=""): +# super().__init__(name=name) +# self._append( +# MotorRecord, +# "SARES20-MF1:MOT_3", +# name="pitch", +# is_setting=True, +# is_display=True, +# ) +# self._append( +# MotorRecord, +# "SARES20-MF1:MOT_4", +# name="roll", +# is_setting=True, +# is_display=True, +# ) + + +# namespace.append_obj(JohannAnalyzer, name="analyzer", lazy=True) + + +# class GratingHolder(Assembly): +# def __init__(self, name=""): +# super().__init__(name=name) +# self._append(y=True, +# MotorRecord, +# "SARES20-MF1:MOT_7", +# name="vertical", +# is_setting=True, +# is_display=True, +# ) +# self._append( +# SmaractRecord, +# "SARES23-USR:MOT_6", +# name="horizontal", +# is_setting=True, +# is_display=True, +# ) + + +# namespace.append_obj(GratingHolder, name="grating_holder") + + +# ad hoc 2 pulse setup +# class Laser2pulse(Assembly): +# def __init__(self, name=None):y=True, +# super().__init__(name=name) +# self._append( +# SmaractStreamdevice, +# "SARES23-ESB1", +# name="pump_exp_delaystage", +# is_setting=True, +# ) +# +# self._append( +# DelayTime, +# self.pump_exp_delaystage, +# name="pump_delay_exp", +# is_setting=False, +# is_display=True, +# reset_current_value_to=False, +# ) +# self._append(SmaractStreamdevice, "SARES23-ESB5", name="wp", is_setting=True) +# self._append( +# SmaractStreamdevice, +# "SARES23-ESB4", +# name="pump_2_delaystage", +# is_setting=True, +# ) +# self._append( +# DelayTime, +# self.pump_2_delaystage, +# name="pump_2_delay", +# is_setting=False, +# is_display=True, +# reset_current_value_to=False, +# ) +# self._append(SmaractStreamdevice, "SARES23-ESB6", name="ratio", is_setting=True) +# self._append( +# SmaractStreamdevice, "SARES23-ESB17", name="rx_pump", is_setting=True +# ) +# self._append( +# SmaractStreamdevice, "SARES23-ESB18", name="ry_pump", is_setting=True +# ) +# +# +# namespace.append_obj( +# Laser2pulse, +# lazy=True, +# name="laser2pulse", +# ) + + +# from eco.xoptics import dcm_pathlength_compensation as dpc + +# namespace._append( +# "MotorRecord", +# "SLAAR21-LMOT-M523:MOTOR_1", +# name="delaystage_glob", +# is_setting=True, +# module_name="eco.devices_general.motors", +# ) +# namespace.append( +# "DelayTime", +# delaystage_glob, +# name="delay_glob", +# is_setting=True, +# module_name="eco.loptics.bernina_laser", +# ) + + +namespace.append_obj( + "SwissFel", + name="fel", + lazy=True, + module_name="eco.fel.swissfel", +) +namespace.append_obj( + "DoubleCrystalMono", + pvname="SAROP21-ODCM098", + fel=fel, + las=las, + undulator_deadband_eV=2.0, + name="mono", + lazy=True, + module_name="eco.xoptics.dcm_new", +) + +# namespace.append_obj( +# "AramisDcmFeedback", +# mono=mono, +# xbpm=mon_opt, +# name="mono_feedback", +# lazy=True, +# module_name="eco.xoptics.xopt_feedback", +# ) + + +# namespace.append_obj( +# "MonoTimecompensation", +# las.delay_glob, +# mono.mono_und_energy, +# "/sf/bernina/config/eco/reference_values/dcm_reference_timing.json", +# "/sf/bernina/config/eco/reference_values/dcm_reference_invert_delay.json", +# lazy=True, +# name="mono_und_time_corrected", +# module_name="eco.xoptics.dcm_pathlength_compensation", +# ) + + +# ad hoc interferometric timetool +# class TTinterferometrid(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) +# self._append(MotorRecord, "SARES20-MF1:MOT_7", name="z_target", is_setting=True) +# self._append( +# MotorRecord, "SARES20-MF1:MOT_10", name="x_target", is_setting=True +# ) +# self._append( +# MotorRecord, +# "SLAAR21-LMOT-M521:MOTOR_1", +# name="delaystage", +# is_setting=True +# # MotorRecord,"SLAAR21-LMOT-M521",name = "" +# # starting following commandline silently: +# # caqtdm -macro "P=SLAAR21-LMOT-M521:,M=MOTOR_1" motorx_more.ui +# ) +# self._append( +# DelayTime, +# self.delaystage, +# name="delay", +# is_setting=True, +# is_display=True, +# ) +# self._append( +# SmaractStreamdevice, +# "SARES23-ESB18", +# name="rot_BC", +# accuracy=3e-3, +# is_setting=True, +# ) +# # self._append( +# # MotorRecord, "SARES20-MF1:MOT_15", name="zoom_microscope", is_setting=True +# # ) +# self._append( +# MicroscopeMotorRecord, +# pvname_camera="SARES20-CAMS142-M1", +# camserver_alias="tt_spatial", +# pvname_zoom="SARES20-MF1:MOT_15", +# is_setting=True, +# is_display="recursive", +# name="microscope", +# ) +# +# +# namespace.append_obj( +# TTinterferometrid, +# lazy=True, +# name="exp", +# ) + + +############## experiment specific ############# + +class ConvergentBeamDiffraction(Assembly): + def __init__(self, name=None): + super().__init__(name=name) + self._append( + SmaractRecord, "SARES20-MCS3:MOT_1", preferred_home_direction='forward',name="sample_x", is_setting=True + ) + self._append( + SmaractRecord, "SARES20-MCS3:MOT_2", preferred_home_direction='forward', name="sample_y", is_setting=True + ) + self._append( + SmaractRecord, "SARES20-MCS3:MOT_3", preferred_home_direction='reverse', name="sample_z", is_setting=True + ) + # self._append(DetectorGet,self._get_zmq_dataset, name='positions', is_display=False) + # self._append(DetectorObject,self._positions, name='positions') + + + def _get_zmq_dataset(self): + # import zmq + # import json + # from pprint import pprint + + + ATTRS = [ + "SlitU - left (float64, mm)", + "SlitU - right (float64, mm)", + "SlitU - up (float64, mm)", + "SlitU - down (float64, mm)", + "SlitD - left (int64, pm)", + "SlitD - right (int64, pm)", + "SlitD - up (int64, pm)", + "SlitD - down (int64, pm)", + "MLL - UP - X (float64, nm)", + "MLL - UP - Y (float64, nm)", + "MLL - UP - Z (float64, nm)", + "MLL - UP - Pitch (float64, ndeg)", + "MLL - UP - Roll (float64, ndeg)", + "MLL - UP - Yaw (float64, ndeg)", + "MLL - DOWN - X (float64, nm)", + "MLL - DOWN - Y (float64, nm)", + "MLL - DOWN - Z (float64, nm)", + "MLL - DOWN - Pitch (float64, ndeg)", + "MLL - DOWN - Roll (float64, ndeg)", + "MLL - DOWN - Yaw (float64, ndeg)", + "OSA - X (int64, pm)", + "OSA - Y (int64, pm)", + "OSA - Z (int64, pm)", + "SAM - X (float64, mm)", + "SAM - Y (float64, mm)", + "SAM - Z (float64, mm)", + "SAM - pitch (int64, ndeg)", + "SAM - yaw (int64, ndeg)", + "CONE - X (float64, mm)", + "CONE - Y (float64, mm)", + "CONE - Z (float64, mm)", + "MIC - X (float64, mm)", + "MIC - Y (int64, nm)", + "MIC - Z (float64, mm)", + "BSU - X (float64, mm)", + "BSU - Y (float64, mm)", + "BSU - Z (float64, mm)", + "BSD - X (float64, mm)", + "BSD - Y (float64, mm)", + "BSD - Z (float64, mm)" + ] + + HOST = "129.129.243.102" # Replace with the IP address of our server in BL network + + socket = zmq.Context.instance().socket(zmq.SUB) + socket.setsockopt(zmq.RCVTIMEO, 100) + socket.setsockopt(zmq.LINGER, 0) + socket.connect(f"tcp://{HOST}:50002") + socket.setsockopt_string(zmq.SUBSCRIBE, "") + while not socket.poll(timeout=100): + pass + + positions = socket.recv() + positions = json.loads(positions.decode()).split(";") + + data = {ATTRS[i]: positions[i] for i in range(len(ATTRS))} + # pprint(data) + return data + + +namespace.append_obj( + ConvergentBeamDiffraction, + name="cbd", + lazy=True, +) + +class Pumpdelay(Assembly): + def __init__( + self, + delaystage_PV="SARES23-USR:MOT_2", + name=None, + ): + super().__init__(name=name) + + self._append(SmaractRecord, delaystage_PV, name="delaystage", is_setting=True) + self._append(DelayTime, self.delaystage, name="pdelay", is_setting=True) + + +namespace.append_obj( + Pumpdelay, + name="pumpdelay", + lazy=True, +) + + +from eco.loptics.bernina_laser import Stage_LXT_Delay + + +# namespace.append_obj( +# "StageLxtDelay", +# ocb.delay_thz, +# las, +# lazy=True, +# name="lxt", +# direction=-1, +# module_name="eco.loptics.bernina_laser", +# ) + +namespace.append_obj( + "LxtCompStageDelay", + NamespaceComponent(namespace, "tt_kb.delay"), + NamespaceComponent(namespace, "las.xlt"), + feedback_enabled_adj=NamespaceComponent(namespace, "tt_kb.feedback_enabled"), + lazy=True, + name="lxt", + module_name="eco.loptics.bernina_laser", +) + +##combined delaystage with phase shifter motion## + + +# def thz_pol_set(self, val): +# return 1.0 * val, 1.0 / 2 * val + +# def thz_pol_get(self, val, val2): +# return 1.0 * val2 + + +# try to append pgroup folder to path !!!!! This caused eco to run in a timeout without error traceback !!!!! +# TODO pgroup non dynamic here! +try: + import sys + from ..utilities import TimeoutPath + + if TimeoutPath(f"/sf/bernina/data/{config_bernina.pgroup()}/res/").exists(): + pgroup_eco_path = TimeoutPath( + f"/sf/bernina/data/{config_bernina.pgroup()}/res/eco" + ) + pgroup_eco_path.mkdir(mode=0o775, exist_ok=True) + try: + pgroup_eco_path.chmod(mode=0o775) + except: + pass + + sys.path.append(pgroup_eco_path.as_posix()) + else: + print( + "Could not access experiment folder, could be due to more systematic file system failure!" + ) +except: + print("Did not succeed to append an eco folder in current prgoup") + + +class Xspect_EH55(Assembly): + def __init__(self, name="xspect_bernina"): + super().__init__(name=name) + self._append( + MotorRecord, "SARES20-MF1:MOT_15", name="x_crystal", is_setting=True + ) + self._append( + MotorRecord, "SARES20-MF1:MOT_16", name="y_crystal", is_setting=True + ) + self._append( + SmaractRecord, "SARES23-USR:MOT_17", name="theta_crystal", is_setting=True + ) + self._append( + CameraBasler, + "SARES20-CAMS142-M3", + name="camera_bsss", + is_display=False, + is_setting=False, + ) + + +namespace.append_obj(Xspect_EH55, name="xspect_bernina", lazy=True) + +############## BIG JJ SLIT ##################### +namespace.append_obj( + "SlitBladesGeneral", + name="slit_cleanup_sam", + def_blade_up={ + "args": [MotorRecord, "SARES20-MF1:MOT_2"], + "kwargs": {"is_psi_mforce": True}, + }, + def_blade_down={ + "args": [MotorRecord, "SARES20-MF1:MOT_3"], + "kwargs": {"is_psi_mforce": True}, + }, + def_blade_left={ + "args": [MotorRecord, "SARES20-MF1:MOT_5"], + "kwargs": {"is_psi_mforce": True}, + }, + def_blade_right={ + "args": [MotorRecord, "SARES20-MF1:MOT_4"], + "kwargs": {"is_psi_mforce": True}, + }, + module_name="eco.xoptics.slits", + lazy=True, +) + +############## SMALL JJ SLIT ##################### + +# namespace.append_obj( +# "SlitPosWidth", +# pvname="SARES20-MF1:", +# motornames={ +# "hpos": "MOT_2", +# "vpos": "MOT_5", +# "hgap": "MOT_3", +# "vgap": "MOT_4", +# }, +# name="slit_cleanup_air", +# lazy=True, +# module_name="eco.xoptics.slits", +# ) + + +## N2 sample heater setup + +from eco.devices_general.env_sensors import WagoSensor + + +class SampleHeaterJet(Assembly): + def __init__(self, name="sampleheaterjet"): + super().__init__(name=name) + self._append( + WagoSensor, pvbase="SARES20-CWAG-GPS01:TEMP-T9", name="sensor_sample" + ) + self._append( + WagoSensor, pvbase="SARES20-CWAG-GPS01:TEMP-T10", name="sensor_jet_mount" + ) + self._append( + WagoSensor, pvbase="SARES20-CWAG-GPS01:TEMP-T11", name="sensor_hexapod" + ) + self._append( + MpodChannel, + pvbase="SARES21-PS7071", + channel_number=5, + name="fan_hexapod_1", + ) + self._append( + MpodChannel, + pvbase="SARES21-PS7071", + channel_number=6, + name="fan_hexapod_2", + ) + + +# namespace.append_obj(SampleHeaterJet, name="heater_jet", lazy=True) + + +## sample illumination +from eco.devices_general.powersockets import MpodChannel + + +class IlluminatorsLasers(Assembly): + def __init__(self, name="sample_illumination"): + super().__init__(name=name) + self._append( + MpodChannel, + pvbase="SARES21-PS7071", + channel_number=3, + name="illumination_inline", + ) + self._append( + MpodChannel, + pvbase="SARES21-PS7071", + channel_number=2, + name="illumination_side", + ) + # self._append( + # MpodChannel, + # pvbase="SARES21-CPCL-PS7071", + # channel_number=6, + # name="illumination_top", + # ) + # self._append( + # MpodChannel, + # pvbase="SARES21-CPCL-PS7071", + # channel_number=4, + # name="flattening_laser", + # ) + + +namespace.append_obj(IlluminatorsLasers, name="sample_illumination", lazy=True) + +## LIQUID jet setup + +# from eco.devices_general.wago import AnalogOutput +# from eco.detector import Jungfrau +# from eco.timing.event_timing_new_new import EvrOutputsample +# from eco.devices_general.digitizers import DigitizerIoxosBoxcarChannel +# from eco.elements.adjustable import AdjustableVirtual +# import numpy as np + + +class LiquidJetSpectroscopy(Assembly): + def __init__(self, pgroup_adj=None, config_JF_adj=None, name=None): + super().__init__(name=name) + self._append( + MotorRecord, + "SARES20-MF1:MOT_12", + name="x", + backlash_definition=True, + is_setting=True, + ) + self._append( + MotorRecord, + "SARES20-MF1:MOT_10", + name="y", + backlash_definition=True, + is_setting=True, + ) + self._append( + MotorRecord, + "SARES20-MF1:MOT_11", + name="z", + backlash_definition=True, + is_setting=True, + ) + # self._append( + # MotorRecord,y=True, + # "SARES20-MF1:MOT_3", + # name="x_analyzer", + # backlash_definition=True, + # is_setting=True, + # ) + # self._append( + # MotorRecord, + # "SARES21-XRD:MOT_P_T", + # name="y_vhdet", + # is_setting=True, + # + self._append( + Jungfrau, + "JF04T01V01", + name="det_em", + pgroup_adj=pgroup_adj, + config_adj=config_JF_adj, + ) + + +# namespace.append_obj( +# LiquidJetSpectroscopy, +# pgroup_adj=config_bernina.pgroup, +# config_JF_adj=config_JFs, +# name="liquidjet", +# lazy=True, +# ) + + +class Tapedrive(Assembly): + def __init__(self, name=None): + super().__init__(name=name) + self._append( + AdjustablePv, "KERNVARIABLES:DELAYBETWEENXFELANDLASER", name="delay" + ) + self._append(SmaractRecord, "SARES23-USR:MOT_12", name="freespace_ver") + self._append(SmaractRecord, "SARES23-USR:MOT_13", name="freespace_hor") + + self._append(MotorRecord, "SARES20-MF1:MOT_13", name="x_target_totem") + self._append(MotorRecord, "SARES20-MF1:MOT_14", name="y_target_totem") + + self._append(AnalogOutput, "SARES20-CWAG-GPS01:DAC01", name="shutter1") + self._append(AnalogOutput, "SARES20-CWAG-GPS01:DAC02", name="shutter2") + self._append(AnalogOutput, "SARES20-CWAG-GPS01:DAC03", name="shutter3") + self._append(AnalogOutput, "SARES20-CWAG-GPS01:DAC04", name="shutter4") + + self._append( + EvrOutput, + f"SARES20-CVME-01-EVR0:RearUniv0", + pulsers=evr.pulsers, + name=f"trigger_patch1_bnc16", + is_setting=True, + # is_display="recursive", + ) + self._append( + EvrOutput, + f"SARES20-CVME-01-EVR0:RearUniv1", + pulsers=evr.pulsers, + name=f"trigger_patch2_bnc16", + is_setting=True, + # is_display="recursive", + ) + + self._append( + Jungfrau, + "JF07T32V01", + config_adj=daq.config_JFs, + pgroup_adj=config_bernina.pgroup, + name="det_diff", + is_setting=True, + is_status=True, + # is_display="recursive", + ) + self._append( + Jungfrau, + "JF05T01V01", + config_adj=daq.config_JFs, + pgroup_adj=config_bernina.pgroup, + name="det_spect", + is_setting=True, + is_status=True, + # is_display="recursive", + ) + self._append( + Jungfrau, + "JF03T01V01", + config_adj=daq.config_JFs, + pgroup_adj=config_bernina.pgroup, + name="det_imon", + is_setting=True, + is_status=True, + # is_display="recursive", + ) + + self._append( + DigitizerIoxosBoxcarChannel, "SARES20-LSCP9-FNS:CH1", name="diode_1" + ) + self._append( + DigitizerIoxosBoxcarChannel, "SARES20-LSCP9-FNS:CH2", name="diode_2" + ) + + self._append( + AdjustableFS, + "/photonics/home/gac-bernina/eco/configuration/p20231_mono_und_offset", + name="mono_und_calib", + default_value=[[6500, 0], [7100, 0]], + is_setting=True, + ) + + def en_set(en): + ofs = np.array(self.mono_und_calib()).T + fel_ofs = ofs[1][np.argmin(abs(ofs[0] - en))] + return en, en / 1000 - fel_ofs + + def en_get(monoen, felen): + return monoen + + self._append( + AdjustableVirtual, + [mono, fel.aramis_photon_energy_undulators], + en_get, + en_set, + name="mono_und_energy", + ) + + def add_mono_und_calibration(self): + mono_energy = mono.get_current_value() + fel_offset = ( + mono.get_current_value() / 1000 + - fel.aramis_photon_energy_undulators.get_current_value() + ) + self.mono_und_calib.mvr([[mono_energy, fel_offset]]) + + +# namespace.append_obj(Tapedrive, name="tapedrive", lazy=True) + + +#### pgroup specific appending, might be temporary at this location #### + +# namespace.append_obj("Xom", module_name="xom", name="xom", lazy=True) + + +# namespace.init_all() + +############## maybe to be recycled ################### + +# { +# "args": [], +# "name": "ocb", +# "z_und": 142, +# "desc": "LiNbO3 crystal breadboard", +# "type": "eco.endstations.bernina_sample_environments:LiNbO3_crystal_breadboard", +# "kwargs": {"Id": "SARES23"}, +# },class LiquidJetSpectroscopy(Assembly): +# def __init__(self, name=None): +# super().__init__(name=name) +# self._append( +# MotorRecord, +# "SARES20-MF1:MOT_2", +# name="x_jet", +# backlash_definition=True, +# is_setting=True, +# ) +# self._append( +# MotorRecord, +# "SARES20-MF1:MOT_4", +# name="y_jet", +# backlash_definition=True, +# is_setting=True, +# ) +# self._append( +# MotorRecord, +# "SARES20-MF1:MOT_6", +# name="z_jet", +# backlash_definition=True, +# is_setting=True, +# ) +# self._append( +# MotorRecord, +# "SARES20-MF1:MOT_3", +# name="x_analyzer", +# backlash_definition=True, +# is_setting=True, +# ) +# self._append( +# MotorRecord, +# "SARES21-XRD:MOT_P_T", +# name="y_vhdet", +# is_setting=True, +# ) +# self._append( +# Jungfrau, "JF03T01V02", name="det_i0", pgroup_adj=config_bernina.pgroup +# ) +# self._append( +# Jungfrau, "JF04T01V01", name="det_em", pgroup_adj=config_bernina.pgroup +# ) +# self._append( +# Jungfrau, "JF14T01V01", name="det_vhamos", pgroup_adj=config_bernina.pgroup +# ) +# self._append(CameraBasler, "SARES20-CAMS142-M2", name="prof_pump") + +# { +# "args": [], +# "name": "vonHamos", +# "z_und": 142, +# "desc": "Kern experiment, von Hamos vertical and horizontal stages ", +# "type": "eco.devices_general.micos_stage:stage", +# "kwargs": { +# "vonHamos_horiz_pv": config["Kern"]["vonHamos_horiz"], +# "vonHamos_vert_pv": config["Kern"]["vonHamos_vert"], +# }, +# }, + +# { +# "name": "mono_old", +# "args": ["SAROP21-ODCM098"], +# "kwargs": { +# "energy_sp": "SAROP21-ARAMIS:ENERGY_SP", +# "energy_rb": "SAROP21-ARAMIS:ENERGY", +# }, +# "z_und": 98, +# "desc": "DCM Monochromator", +# "type": "eco.xoptics.dcm:Double_Crystal_Mono", +# }, + + +def pgroup2name(pgroup): + tp = "/sf/bernina/exp/" + d = Path(tp) + dirs = [i for i in d.glob("*") if i.is_symlink()] + names = [i.name for i in dirs] + targets = [i.resolve().name for i in dirs] + return names[targets.index(pgroup)] + + +def name2pgroups(name, beamline="bernina"): + tp = f"/sf/{beamline}/exp/" + d = Path(tp) + dirs = [i for i in d.glob("*") if i.is_symlink()] + names = [i.name for i in dirs] + targets = [i.resolve().name for i in dirs] + eq = [[i_n, i_p] for i_n, i_p in zip(names, targets) if name == i_n] + ni = [ + [i_n, i_p] + for i_n, i_p in zip(names, targets) + if (not name == i_n) and (name in i_n) + ] + return eq + ni + + +from eco.utilities import linlog_intervals, roundto + + +def timetool_data_monitor(warning_threshold=1000, loopsleep=5): + dir(bs_worker) + tt_kb.spectrum_signal.stream.accumulate(do_accumulate=True) + print("Monitoring timetool data ...") + + while True: + + eid_diff = int( + event_system.pulse_id.get_current_value() + - tt_kb.spectrum_signal.stream.eventIds[-1][-1] + ) + if eid_diff > warning_threshold: + message = f"Last timetool data {eid_diff} pulses ago!" + print(message) + try: + e = pyttsx3.init() + e.say(message) + e.runAndWait() + e.stop() + except: + pass + time.sleep(loopsleep) diff --git a/eco/devices_general/motors.py b/eco/devices_general/motors.py index 842e457..d1c1fdc 100755 --- a/eco/devices_general/motors.py +++ b/eco/devices_general/motors.py @@ -1573,6 +1573,8 @@ class SmaractRecord(Assembly): alias_fields={}, backlash_definition=False, expect_bad_limits=True, + preferred_home_direction='forward', + **kwargs, ): super().__init__(name=name) # self.settings.append(self) @@ -1580,6 +1582,7 @@ class SmaractRecord(Assembly): self.pvname = pvname self._motor = _Motor(pvname) + self._append(AdjustableMemory,preferred_home_direction, name=preferred_home_direction) self._elog = elog for an, af in alias_fields.items(): self.alias.append( @@ -1709,7 +1712,10 @@ class SmaractRecord(Assembly): self.set_limits(-abs_set_value, abs_set_value) def home(self): - self.home_forward(1) + if self.preferred_home_direction.get_current_value() == "forward" or self.preferred_home_direction.get_current_value() is None or self.preferred_home_direction.get_current_value(): + self.home_forward(1) + elif self.preferred_home_direction.get_current_value() == "reverse" or self.preferred_home_direction.get_current_value() is False: + self.home_reverse(1) time.sleep(0.1) while not self.flags.is_homed.get_current_value(): time.sleep(0.1) diff --git a/eco/elements/adjustable.py b/eco/elements/adjustable.py index 18f038b..291d027 100644 --- a/eco/elements/adjustable.py +++ b/eco/elements/adjustable.py @@ -15,6 +15,7 @@ from eco.aliases import Alias from eco.devices_general.utilities import Changer +from eco.elements import memory from eco.utilities.keypress import KeyPress # from .assembly import Assembly @@ -422,13 +423,19 @@ def _keywordChecker(kw_key_list_tups): @tweak_option @value_property class AdjustableMemory: - def __init__(self, value=0, name="adjustable_memory"): + def __init__(self, value=0, name="adjustable_memory", return_deep_copy=True): self.name = name self.alias = Alias(name) self.current_value = value + self._return_deep_copy = return_deep_copy + if memory.global_memory_dir: + self.memory = memory.Memory(self) def get_current_value(self): - return deepcopy(self.current_value) + if self._return_deep_copy: + return deepcopy(self.current_value) + else: + return self.current_value def set_target_value(self, value, hold=False): def changer(value): @@ -485,6 +492,8 @@ class AdjustableFS: self._write_value(default_value) self.alias = Alias(name) self.max_read_period = max_read_period + # if memory.global_memory_dir: + # self.memory = memory.Memory(self) self.name = name def get_current_value(self): diff --git a/eco/elements/assembly.py b/eco/elements/assembly.py index fe43d4f..70d8700 100644 --- a/eco/elements/assembly.py +++ b/eco/elements/assembly.py @@ -39,6 +39,17 @@ class Collection: def get_list(self): return self._list + + # esired new way, in order to old containers and allow them to be replaced "on top" of a structure. + # causes other issues from recoursion, bigger issue... + # def get_list(self): + # ls = [] + # for item in self._list: + # if hasattr(item, f"{self.name}") and isinstance(item.__dict__[self.name],Collection): + # ls.append(item.__dict__[self.name].get_list()) + # else: + # ls.append(ls) + # return ls def append(self, obj, recursive=True, force=False): if force: @@ -67,6 +78,13 @@ class Collection: def pop_item(self, item): return self.pop(self.index(item)) + + def pop_obj_children(self, obj): + o = [] + for it in obj.__dict__[self.name].get_list(): + if (it in self._list): + o.append(self.pop_item(it)) + return o def __call__(self): return self.get_list() @@ -118,9 +136,28 @@ class Assembly: is_alias=True, view_toplevel_only=True, call_obj=True, - append_property_with_name=False, + # append_property_with_name=False, + overwrite=False, **kwargs, ): + if overwrite: + + if name in self.__dict__: + old = self.__dict__[name] + for collection in [ + self.settings_collection, + self.status_collection, + ]: + if isinstance(old,Assembly): + collection.pop_obj_children(old) + else: + if old in collection.get_list(): + collection.pop_item(old) + self.display_collection.pop_item(old) + self.alias.pop_object(old.alias) + # del self.__dict__[name] + + if isinstance(foo_obj_init, Adjustable) and not isclass(foo_obj_init): # adj_copy = copy.copy(foo_obj_init) adj_copy = foo_obj_init @@ -138,23 +175,23 @@ class Assembly: # except: # print(f'object {name} / {foo_obj_init} not initialized with name/parent') # self.__dict__[name] = foo_obj_init(*args, **kwargs) - if append_property_with_name: - if isinstance(self.__dict__[name], Adjustable): - self.__class__.__dict__[append_property_with_name] = property( - self.__dict__[name].get_current_value, - lambda val: self.__dict__[name].set_target_value(val).wait(), - ) - elif isinstance(self.__dict__[name], Detector): - self.__class__.__dict__[append_property_with_name] = property( - self.__dict__[name].get_current_value, - ) + # if append_property_with_name: + # if isinstance(self.__dict__[name], Adjustable): + # self.__class__.__dict__[append_property_with_name] = property( + # self.__dict__[name].get_current_value, + # lambda val: self.__dict__[name].set_target_value(val).wait(), + # ) + # elif isinstance(self.__dict__[name], Detector): + # self.__class__.__dict__[append_property_with_name] = property( + # self.__dict__[name].get_current_value, + # ) - if is_setting == "auto": - is_setting = isinstance(self.__dict__[name], Adjustable) + # if is_setting == "auto": + # is_setting = isinstance(self.__dict__[name], Adjustable) if is_setting: self.settings_collection.append(self.__dict__[name], recursive=True) - if is_status == "auto": - is_status = isinstance(self.__dict__[name], Detector) + # if is_status == "auto": + # is_status = isinstance(self.__dict__[name], Detector) if is_status: self.status_collection.append(self.__dict__[name], recursive=True) if is_display: @@ -358,7 +395,7 @@ class Assembly: return s def get_display_str( - self, tablefmt="simple", maxcolwidths=[None, 50, None, None, None] + self, tablefmt="simple", with_base_name=False, maxcolwidths=[None, 50, None, None, None] ): main_name = self.name stats = self.display_collection() @@ -366,7 +403,7 @@ class Assembly: tab = [] for to in stats: name = to.alias.get_full_name(base=self) - + is_adjustable = isinstance(to, Adjustable) is_detector = isinstance(to, Detector) typechar = "" @@ -396,9 +433,14 @@ class Assembly: if value is None: value = "" - tab.append( - [".".join([main_name, name]), value, unit, typechar, description] - ) + if with_base_name: + tab.append( + [".".join([main_name, name]), value, unit, typechar, description] + ) + else: + tab.append( + [ name, value, unit, typechar, description] + ) if tab: s = tabulate(tab, tablefmt=tablefmt, maxcolwidths=maxcolwidths) else: @@ -437,7 +479,7 @@ class Assembly: json.dump(stat, f, indent=4, cls=NumpyEncoder) files.append(filepath) - elog.post( + return elog.post( message, *files, text_encoding="html", @@ -445,7 +487,7 @@ class Assembly: # tags=[], def __repr__(self): - label = self.alias.get_full_name() + " status\n" + label = self.alias.get_full_name() + " display\n" return label + self.get_display_str() # def _wait_for_initialisation(self, timeout=2): diff --git a/eco/elements/detector.py b/eco/elements/detector.py index d24cd15..8f627fb 100644 --- a/eco/elements/detector.py +++ b/eco/elements/detector.py @@ -1,4 +1,5 @@ -from eco.elements.adjustable import AdjustableMemory, default_representation +from copy import deepcopy +from eco.elements.adjustable import AdjustableMemory, default_representation, spec_convenience from eco.elements.assembly import Assembly from eco.aliases import Alias import time @@ -91,3 +92,25 @@ class DetectorGet: if self._cache_get_seconds: self._get_cache = (ts, value) return value + +@call_convenience +@value_property +class DetectorMemory: + def __init__(self, value=0, name="detector_memory", return_deep_copy=True): + self.name = name + self.alias = Alias(name) + self.current_value = value + self._return_deep_copy = return_deep_copy + + def get_current_value(self): + if self._return_deep_copy: + return deepcopy(self.current_value) + else: + return self.current_value + + + def __repr__(self): + name = self.name + cv = self.get_current_value() + s = f"{name} at value: {cv}" + "\n" + return s diff --git a/eco/utilities/config.py b/eco/utilities/config.py index bc60d47..1ca77ed 100644 --- a/eco/utilities/config.py +++ b/eco/utilities/config.py @@ -1,6 +1,7 @@ import json import importlib from pathlib import Path +from eco.elements.adjustable import AdjustableFS, AdjustableMemory from eco.elements.protocols import InitialisationWaitable import sys from time import sleep, time @@ -21,6 +22,7 @@ from threading import Thread from tqdm import tqdm from rich import progress from inspect import signature +from simple_term_menu import TerminalMenu import traceback @@ -322,7 +324,7 @@ class IsInitialisingError(Exception): class Namespace(Assembly): - def __init__(self, name=None, root_module=None, alias_namespace=None): + def __init__(self, name=None, root_module=None, alias_namespace=None, required_names=[], required_names_directory=None): super().__init__(name) # self.name = name self.lazy_items = {} @@ -338,6 +340,11 @@ class Namespace(Assembly): self._initializing = [] self.root_module = root_module self.alias_namespace = alias_namespace + if required_names_directory: + self._append(AdjustableFS,required_names_directory,name='required_names', is_setting=True, is_display=True) + else: + self._append(AdjustableMemory,[], name='required_names', is_setting=True, is_display=True) + @property def initialisation_times_sorted(self): @@ -383,6 +390,18 @@ class Namespace(Assembly): except ValueError: pass + def select_required_names(self): + terminal_menu = TerminalMenu( + self.all_names, + multi_select=True, + show_multi_select_hint=True, + preselected_entries=list(self.required_names()), + title="Select required names for namespace %s" % self.name, + ) + selected_indices = terminal_menu.show() + selected_names = terminal_menu.chosen_menu_entries + if selected_names: + self.required_names(list(selected_names)) @@ -425,6 +444,7 @@ class Namespace(Assembly): def init_all( self, + required_only=True, verbose=False, raise_errors=False, print_summary=True, @@ -441,6 +461,20 @@ class Namespace(Assembly): print( f"WARNING - previously hard failed items are NOT initialized:\n{self.failed_names} " ) + if required_only: + if not self.required_names(): + print( + f"WARNING - No required names defined in namespace {self.name}, initializing all items!" + ) + else: + names_to_init = ( + self.all_names - self.initialized_names - set(exclude_names) + ) & set(self.required_names()) + else: + names_to_init = self.all_names - self.initialized_names - set(exclude_names) + + + if silent: self.silently_initializing = True print( @@ -453,7 +487,7 @@ class Namespace(Assembly): self.exc_init.submit( self.init_name, name, verbose=verbose, raise_errors=raise_errors ) - for name in (self.all_names - set(exclude_names)) + for name in names_to_init ] self.exc_init.shutdown(wait=True) self.exc_init = ThreadPoolExecutor(max_workers=1) @@ -462,22 +496,22 @@ class Namespace(Assembly): self.init_name, name, verbose=verbose, raise_errors=raise_errors ) for name in ( - self.all_names - self.initialized_names - set(exclude_names) + names_to_init ) ] self.exc_init.shutdown(wait=True) self.silently_initializing = False if giveup_failed: - failed_names = self.lazy_names + failed_names = names_to_init.intersection(self.lazy_names) for k in failed_names: self.failed_items[k] = self.lazy_items.pop(k) if print_summary: print( - f"Initialized {len(self.initialized_names)} of {len(self.all_names)}." + f"Initialized {len(self.initialized_names & names_to_init)} of {len(names_to_init)}." ) print( "Failed objects: " - + ", ".join(self.lazy_names.union(self.failed_names)) + + ", ".join(self.failed_names & names_to_init) ) print(f"Initialisation took {time()-starttime} seconds") @@ -492,13 +526,11 @@ class Namespace(Assembly): lambda name: self.init_name( name, verbose=verbose, raise_errors=raise_errors ), - self.all_names - - self.initialized_names - - set(exclude_names), + names_to_init, ), description="Initializing ...", total=len( - self.all_names - self.initialized_names - set(exclude_names) + names_to_init ), transient=True, ) @@ -512,13 +544,11 @@ class Namespace(Assembly): lambda name: self.init_name( name, verbose=verbose, raise_errors=raise_errors ), - self.all_names - - self.initialized_names - - set(exclude_names), + names_to_init, ), description="Initializing ...", total=len( - self.all_names - self.initialized_names - set(exclude_names) + names_to_init ), transient=True, ) @@ -526,18 +556,18 @@ class Namespace(Assembly): # ) # # ) if giveup_failed: - failed_names = self.lazy_names - for k in failed_names: - self.failed_items[k] = self.lazy_items.pop(k) + failed_names = names_to_init.intersection(self.lazy_names) + for k in failed_names: + self.failed_items[k] = self.lazy_items.pop(k) if print_summary: - print( - f"Initialized {len(self.initialized_names)} of {len(self.all_names)}." - ) - print( - "Failed objects: " - + ", ".join(self.lazy_names.union(self.failed_names)) - ) - print(f"Initialisation took {time()-starttime} seconds") + print( + f"Initialized {len(self.initialized_names & names_to_init)} of {len(names_to_init)}." + ) + print( + "Failed objects: " + + ", ".join(self.failed_names & names_to_init) + ) + print(f"Initialisation took {time()-starttime} seconds") if (not silent) and print_times: try: diff --git a/eco/utilities/runtable_gsheet.py b/eco/utilities/runtable_gsheet.py index 65ee152..c2f5e43 100644 --- a/eco/utilities/runtable_gsheet.py +++ b/eco/utilities/runtable_gsheet.py @@ -21,7 +21,7 @@ class RuntableGsheet: def require_worksheets(self): tls = [tmp.title for tmp in self._spreadsheet.worksheets()] - for title in [self._wstitle_available_keys, self._wstitle_run_table]: + for title in [self._wstitle_run_table,self._wstitle_available_keys]: if not title in tls: self._spreadsheet.add_worksheet(title,1,1) @@ -37,11 +37,15 @@ class RuntableGsheet: ncolstot = 0 for i,k in enumerate(keys): ti = np.unravel_index(i, shape, order='C') + if self._remove_leading: + tk = k.split(self._remove_leading)[1] + else: + tk = k cells.append( gspread.Cell( ti[1]+rng['startRowIndex']+1, ti[0]+rng['startColumnIndex']+1, - k.split(self._remove_leading)[1])) + tk)) nrowstot = int(max(nrowstot,ti[1]+rng['startRowIndex']+1)) ncolstot = int(max(ncolstot,ti[0]+rng['startColumnIndex']+1)) @@ -61,9 +65,13 @@ class RuntableGsheet: set_cells= [] nrowstot = 0 ncolstot = 0 - test_keys = [tk.split(self._remove_leading)[1] for tk in table.keys()] + # test_keys = [tk.split(self._remove_leading)[1] for tk in table.keys()] + test_keys = [tk for tk in table.keys()] + for cell in cell_list: tstr = cell.value + if not tstr: + continue if not isinstance(tstr,str): continue tstr = tstr.split(self._name_delimiter)[0].strip() @@ -71,6 +79,8 @@ class RuntableGsheet: if tstr in test_keys: set_vals = table[tstr] for n,set_val in enumerate(set_vals): + if np.isnan(set_val): + set_val='nan' set_cells.append(gspread.Cell(cell.row + n + 1, cell.col, set_val)) nrowstot = int(max(nrowstot,cell.row + n + 1)) ncolstot = int(max(ncolstot,cell.col)) diff --git a/eco/utilities/runtable_stripped.py b/eco/utilities/runtable_stripped.py new file mode 100644 index 0000000..a990457 --- /dev/null +++ b/eco/utilities/runtable_stripped.py @@ -0,0 +1,946 @@ +from oauth2client.service_account import ServiceAccountCredentials +from pandas import DataFrame +import pandas as pd +import warnings + +from eco.utilities.runtable_gsheet import RuntableGsheet +from ..elements.adjustable import AdjustableFS +from ..elements.memory import Memory +from subprocess import call +from eco.utilities.config import Proxy +from eco.bernina import namespace + +warnings.simplefilter(action="ignore", category=pd.errors.PerformanceWarning) +warnings.simplefilter(action="ignore", category=UserWarning) +import timeit +import os +from pathlib import Path +from epics import PV +import numpy as np +import gspread +import gspread_dataframe as gd +import gspread_formatting as gf +import gspread_formatting.dataframe as gf_dataframe +from datetime import datetime +import xlwt +import openpyxl +from ..devices_general.pv_adjustable import PvRecord +from epics import caget +import eco +import threading + +pd.options.display.max_rows = 100 +pd.options.display.max_columns = 50 +pd.options.display.max_colwidth = 50 +pd.options.display.width = None +pd.set_option("display.float_format", lambda x: "%.5g" % x) + + +class Gsheet_API: + def __init__( + self, + keydf_fname, + cred_fname, + exp_id, + exp_path, + gsheet_key_path, + ): + ### credentials and settings for uploading to gspread ### + self._scope = [ + "https://spreadsheets.google.com/feeds", + "https://www.googleapis.com/auth/drive", + ] + self._credentials = ServiceAccountCredentials.from_json_keyfile_name( + "/sf/bernina/config/src/python/gspread/pandas_push", self._scope + ) + self.gc = gspread.authorize(self._credentials) + self._keydf_fname = keydf_fname + self.keys = ( + "metadata gps jet energy las_inc delay lxt pulse_id att_self att_fe_self" + ) + self._key_df = DataFrame() + self.gsheet_keys = AdjustableFS( + gsheet_key_path, + name="gsheet_keys", + default_value="metadata thc gps xrd att att_usd kb", + ) + self.init_runtable(exp_id) + + def create_rt_spreadsheet(self, exp_id): + self.gc = gspread.authorize(self._credentials) + spreadsheet = self.gc.create( + title=f"run_table_{exp_id}", folder_id="1F7DgF0HW1O71nETpfrTvQ35lRZCs5GvH" + ) + spreadsheet.add_worksheet("runtable", 10, 10) + spreadsheet.add_worksheet("positions", 10, 10) + ws = spreadsheet.get_worksheet(0) + spreadsheet.del_worksheet(ws) + return spreadsheet + + def _append_to_gspread_key_df(self, gspread_key_df): + if os.path.exists(self._keydf_fname): + self._key_df = pd.read_pickle(self._keydf_fname) + # deprecated: self._key_df = self._key_df.append(gspread_key_df) + self._key_df = pd.concat([self._key_df, gspread_key_df]) + self._key_df.to_pickle(self._keydf_fname) + else: + self._key_df.to_pickle(self._keydf_fname) + + def init_runtable(self, exp_id): + if os.path.exists(self._keydf_fname): + self._key_df = pd.read_pickle(self._keydf_fname) + if self._key_df is not None and str(exp_id) in self._key_df.index: + spreadsheet_key = self._key_df["keys"][f"{exp_id}"] + else: + f_create = str( + input( + f"No google spreadsheet id found for experiment {exp_id}. Create new run_table spreadsheet? (y/n) " + ) + ) + if f_create == "y": + print("creating") + spreadsheet = self.create_rt_spreadsheet(exp_id=exp_id) + print("created") + gspread_key_df = DataFrame( + {"keys": [spreadsheet.id]}, + index=[f"{exp_id}"], + ) + print(gspread_key_df) + spreadsheet_key = spreadsheet.id + self._append_to_gspread_key_df(gspread_key_df) + self._spreadsheet_key = spreadsheet_key + + def upload_rt(self, worksheet="runtable", keys=None, df=None): + """ + This function uploads all entries of which "type" contains "scan" to the worksheet positions. + keys takes a string of keys separated by a space, e.g. 'gps xrd las'. All columns, which contain + any of these strings are uploaded. keys = None defaults to self.keys. keys = '' returns all columns + """ + self.gc = gspread.authorize(self._credentials) + ws = self.gc.open_by_key(self._spreadsheet_key).worksheet(worksheet) + upload_df = df[df["metadata.type"].str.contains("scan", na=False)] + gd.set_with_dataframe(ws, upload_df, include_index=True, col=2) + gf_dataframe.format_with_dataframe( + ws, upload_df, include_index=True, include_column_header=True, col=2 + ) + + def upload_pos(self, worksheet="positions", keys=None, df=None): + """ + This function uploads all entries with "type == pos" to the worksheet positions. + keys takes a list of strin All columns, which contain any of these strings are uploaded. + keys = None defaults to self.keys. keys = [] returns all columns + """ + self.gc = gspread.authorize(self._credentials) + ws = self.gc.open_by_key(self._spreadsheet_key).worksheet(worksheet) + upload_df = df[df["metadata.type"].str.contains("pos", na=False)] + gd.set_with_dataframe(ws, upload_df, include_index=True, col=2) + gf_dataframe.format_with_dataframe( + ws, upload_df, include_index=True, include_column_header=True, col=2 + ) + + def _upload_all(self, df): + try: + self.upload_rt(df=df) + self.upload_pos(df=df) + except: + print( + f"Uploading of runtable to gsheet https://docs.google.com/spreadsheets/d/{self._spreadsheet_key}/ failed. Run run_table.upload_rt() for error traceback" + ) + + def upload_all(self, df): + rt = threading.Thread(target=self._upload_all, kwargs={"df": df}) + rt.start() + + +class Container: + def __init__(self, df, name=""): + self._cols = df.columns + self._top_level_name = name + self._df = df + self.__dir__() + + def _slice_df(self): + self._df.load() + next_level_names = self._get_next_level_names() + try: + if len(next_level_names) == 0: + columns_to_keep = [self._top_level_name[:-1]] + else: + columns_to_keep = [ + f"{self._top_level_name}{n}" + for n in next_level_names + if f"{self._top_level_name}{n}" in self._cols + ] + sdf = self._df[columns_to_keep] + except: + sdf = pd.DataFrame(columns=next_level_names) + return sdf + + def _get_next_level_names(self): + if len(self._top_level_name) == 0: + next_level_names = np.unique( + np.array([n.split(".")[0] for n in self._cols]) + ) + else: + next_level_names = np.unique( + np.array( + [ + n.split(self._top_level_name)[1].split(".")[0] + for n in self._cols + if n[: len(self._top_level_name)] == self._top_level_name + ] + ) + ) + return next_level_names + + def _create_lazy_container(self, n): + def cr(): + return Container(df=self._df, name=self._top_level_name + n + ".") + + return cr + + def _create_first_level_container(self, names, lazy=True): + for n in names: + if lazy: + self.__dict__[n] = Proxy(self._create_lazy_container(n)) + else: + self.__dict__[n] = Container( + self._df, name=self._top_level_name + n + "." + ) + + def to_dataframe(self, full_name=True, next_level=False): + df = self._slice_df() + if not full_name: + coln = pd.Index([k.split(self._top_level_name)[1] for k in df.columns]) + df.columns = coln + return df + + def recall(self, key, next_level=True, get_status=True): + sr = self[key] + if next_level: + srs = [self.__dict__[k][key] for k in self._get_next_level_names()] + srs.insert(0, sr) + sr = self._concatenate_srs(srs) + idxn = pd.Index([k.split(self._top_level_name)[1] for k in sr.index]) + sr.index = idxn + dev = name2obj(self._df.devices, self._top_level_name) + memory = Memory(obj=dev, memory_dir="") + if get_status: + try: + # get setting keys from obj + mem = { + tk: {ak: sr[ak] for ak in tv.keys() if ak in sr.index} + for tk, tv in dev.get_status().items() + } + except: + mem = {"settings": {k: v for k, v in sr.items() if not "readback" in k}} + else: + mem = {"settings": {k: v for k, v in sr.items() if not "readback" in k}} + memory.recall(input_obj=mem) + + def _concatenate_dfs(self, dfs): + dfc = dfs[0] + for df in dfs[1:]: + dfc = dfc.join(df) + return dfc + + def _concatenate_srs(self, srs): + src = srs[0] + for sr in srs[1:]: + if type(sr) == pd.core.series.Series: + src = src.append(sr) + return src + + def __dir__(self): + next_level_names = self._get_next_level_names() + to_create = np.array( + [n for n in next_level_names if not n in self.__dict__.keys()] + ) + directory = list(next_level_names) + directory.extend(["to_dataframe", "recall"]) + self._create_first_level_container(to_create) + return directory + + def __repr__(self): + # pd.options.display.width = os.get_terminal_size().columns + return self._slice_df().T.__repr__() + + def _repr_html_(self): + sdf = self._slice_df() + if hasattr(sdf, "_repr_html_"): + return sdf.T._repr_html_() + else: + return None + + def __getitem__(self, key): + if type(key) is tuple: + key = list(key) + df = self._slice_df().loc[key] + if hasattr(df, "T"): + df = df.T + return df + + +class Run_Table2: + def __init__( + self, + data=None, + exp_id="no_exp_id", + exp_path="run_data/runtable/", + keydf_fname=None, + cred_fname=None, + devices=None, + name=None, + gsheet_key_path=None, + parse=True, + ): + self._data = Run_Table_DataFrame( + data=data, + exp_id=exp_id, + exp_path=exp_path, + devices=devices, + name=name, + parse=parse, + ) + + if np.all([k is not None for k in [keydf_fname, cred_fname, gsheet_key_path]]): + self._google_sheet_api = Gsheet_API( + keydf_fname, + cred_fname, + exp_id, + exp_path, + gsheet_key_path, + ) + self.channels_gsheet = self._google_sheet_api.gsheet_keys + self._rt_gsheet = RuntableGsheet(self._google_sheet_api.gc.open_by_key(self._google_sheet_api._spreadsheet_key)) + # self._rt_gsheet.require_worksheets() + else: + self._google_sheet_api = None + + + self.__dir__() # why necessary to run here? + + def update(self): + self._rt_gsheet.set_available_keys(self._data.df.keys()) + self._rt_gsheet.fill_run_table_data(self._data.df) + + def append_run( + self, + runno, + metadata, + d={}, + wait=False, + ): + ar = threading.Thread(target=self._append_run, args=(runno, metadata,), kwargs={"d": d}) + ar.start() + if wait: + ar.join() + + def _append_run( + self, + runno, + metadata, + d={}, + ): + self._data.append_run(runno, metadata, d=d) + if self._google_sheet_api is not None: + df = self._reduce_df() + self._google_sheet_api._upload_all(df=df) + # self._rt_gsheet.set_available_keys(self._data.df.keys()) + self._rt_gsheet.fill_run_table_data(self._data.df) + + + def append_pos( + self, + name, + wait=False, + ): + ar = threading.Thread(target=self._append_pos, args=(name,)) + ar.start() + if wait: + ar.join() + + def _append_pos( + self, + name, + ): + self._data.append_pos(name) + if self._google_sheet_api is not None: + df = self._reduce_df() + self._google_sheet_api._upload_all(df=df) + + def to_dataframe(self): + return DataFrame(self._data) + + ###### diagnostic and convencience functions ###### + + def from_pgroup(self, pgroup): + """ + returns a run_table instance of the specified pgroup + note: this does neither replace the current run_table nor switch the automatic appending of data to a new run_table or pgroup + + usage: run_table_pxxx = run_table.run_table_from_other_pgroup('pxxx') + """ + return Run_Table2( + data=f"/sf/bernina/data/{pgroup}/res/run_table/{pgroup}_runtable.pkl" + ) + + def check_timeouts(self, include_bad_adjustables=True, plot=True, repeats=1): + return self._data.check_timeouts( + include_bad_adjustables=include_bad_adjustables, plot=plot, repeats=repeats + ) + + def _reduce_df( + self, + keys=None, + ): + if keys is None: + keys = self._google_sheet_api.gsheet_keys() + dfs = [] + for key in keys.split(" "): + d = self + add = True + for k in key.split("."): + if k in d.__dict__.keys(): + d = d.__dict__[k] + else: + add = False + if all([hasattr(d, "to_dataframe"), add]): + dfs.append(d.to_dataframe()) + + dfc = self._concatenate_dfs(dfs) + return dfc + + def _concatenate_dfs(self, dfs): + dfc = dfs[0] + for df in dfs[1:]: + dfc = dfc.join(df) + return dfc + + def _create_lazy_container(self, dev): + def cr(): + return Container(df=self._data, name=dev + ".") + + return cr + + def __dir__(self): + lazy = True + devs = np.unique(np.array([n.split(".")[0] for n in self._data.columns])) + for dev in devs: + if dev not in self.__dict__.keys(): + if lazy: + self.__dict__[dev] = Proxy(self._create_lazy_container(dev)) + else: + self.__dict__[dev] = Container(df=self._data, name=dev + ".") + directory = self.__dict__.keys() + return directory + + def __str__(self): + devs = np.unique(np.array([n.split(".")[0] for n in self._data.columns])) + devs = np.array([dev for dev in devs if 0 0: + for adj_id in self.ids_parsed[id(device)]["ids"]: + adj_name = self.ids_parsed[adj_id]["name"] + # if parent_name == name: + # k = adj_name + # else: + k = ".".join([name, adj_name]) + self.ids_parsed[adj_id]["names_parent"].append(name) + # self.ids_parsed[adj_id]["names"].append(".".join([parent_name, k])) + self.ids_parsed[adj_id]["names"].append(k) + return + for key in device.__dict__.keys(): + if ~np.any([s in key for s in exclude_keys]): + value = device.__dict__[key] + if np.all( + [ + ~np.any( + [ + s in str(type(value)) + for s in adjustable_exclude_class_types + ] + ), + hasattr(value, foo_get_current_value), + ] + ): + ## create device entry only if it has adjustables + if id(device) in self.ids_parsed.keys(): + if not "ids" in self.ids_parsed[id(device)].keys(): + self.ids_parsed[id(device)]["ids"] = [] + else: + self.ids_parsed.update({id(device): {"ids": []}}) + k = ".".join([name, key]) + if id(value) in self.ids_parsed.keys(): + if "value" in self.ids_parsed[id(value)]: + self.ids_parsed[id(device)]["ids"].append(id(value)) + self.ids_parsed[id(value)]["names_parent"].append(name) + self.ids_parsed[id(value)]["names"].append(k) + continue + self.ids_parsed[id(device)]["ids"].append(id(value)) + self.ids_parsed[id(value)] = {} + self.ids_parsed[id(value)]["names_parent"] = [name] + self.ids_parsed[id(value)]["name"] = key + self.ids_parsed[id(value)]["names"] = [k] + self.ids_parsed[id(value)]["value"] = value + + #if parent_name == name: + ## only a fix to record get_current_values() of top level devices + #if hasattr(device, foo_get_current_value): + # ## create device entry only if it has adjustables + # if id(device) in self.ids_parsed.keys(): + # if not "ids" in self.ids_parsed[id(device)].keys(): + # self.ids_parsed[id(device)]["ids"] = [] + # else: + # self.ids_parsed.update({id(device): {"ids": []}}) + # self.ids_parsed[id(device)]["ids"].append(id(device)) + # self.ids_parsed[id(device)]["names_parent"] = [name] + # self.ids_parsed[id(device)]["name"] = "value" + # self.ids_parsed[id(device)]["names"] = [".".join([name, "value"])] + # self.ids_parsed[id(device)]["value"] = device + + def _parse_child_instances( + self, parent_class, adj_prefix=None, parent_name=None, verbose=False, exclude_keys=[], parse_exclude_class_types=[], adjustable_exclude_class_types=[], is_eco=True, foo_get_current_value="get_current_value" + ): + # check if the parent_class was already parsed in its parents + if adj_prefix is not None: + if parent_class.name in adj_prefix: + return [] + self._get_all_adjustables( + parent_class, adj_prefix, parent_name, verbose=verbose, exclude_keys=exclude_keys, adjustable_exclude_class_types=adjustable_exclude_class_types, foo_get_current_value=foo_get_current_value + ) + if adj_prefix is not None: + adj_prefix = ".".join([adj_prefix, parent_class.name]) + else: + adj_prefix = parent_class.name + + sub_classes = [] + for key in parent_class.__dict__.keys(): + if ~np.any([s in key for s in exclude_keys]): + s_class = parent_class.__dict__[key] + reqs = [] + if is_eco: + reqs = [ + "eco" in str(s_class.__class__), + ] + if np.all( + reqs + + [ + hasattr(s_class, "__dict__"), + s_class.__hash__ is not None, + ~np.any( + [ + s in str(s_class.__class__) + for s in parse_exclude_class_types + ] + ), + ] + ): + if adj_prefix is None or ~np.any( + [ + key == s + for s in ".".join([parent_name, adj_prefix]).split(".") + ] + ): + s_class_name = None + if hasattr(s_class, "name"): + s_class_name = s_class.name + if s_class_name == None: + s_class.name = key + sub_classes.append(s_class) + return set(sub_classes).union( + [ + s + for c in sub_classes + for s in self._parse_child_instances(c, adj_prefix, parent_name, exclude_keys=exclude_keys, parse_exclude_class_types=parse_exclude_class_types, adjustable_exclude_class_types=adjustable_exclude_class_types, is_eco=is_eco, foo_get_current_value=foo_get_current_value) + ] + ) + + def _parse_parent(self, parent=None, verbose=False, exclude_keys=[], parse_exclude_class_types=[], adjustable_exclude_class_types=[], is_eco=True, foo_get_current_value="get_current_value"): + if len(exclude_keys) == 0: + exclude_keys = self._parse_exclude_keys + if len(parse_exclude_class_types) == 0: + parse_exclude_class_types = self._parse_exclude_class_types + if len(adjustable_exclude_class_types) == 0: + adjustable_exclude_class_types = self._adj_exclude_class_types + self.ids_parsed = {} + if parent == None: + parent = self.devices + self.ids_parsed[id(parent)] = {"ids": []} + for key in parent.__dict__.keys(): + try: + if ~np.any([s in key for s in exclude_keys]): + s_class = parent.__dict__[key] + reqs = [] + if is_eco: + reqs = [ + "eco" in str(s_class.__class__), + ] + if np.all( + reqs + + [ + hasattr(s_class, "__dict__"), + s_class.__hash__ is not None, + ~np.any( + [ + s in str(s_class.__class__) + for s in parse_exclude_class_types + ] + ), + ] + ): + s_class_name = None + if hasattr(s_class, "name"): + s_class_name = s_class.name + if s_class_name == None: + s_class.name = key + self._parse_child_instances( + s_class, parent_name=key, verbose=verbose, exclude_keys=exclude_keys, parse_exclude_class_types=parse_exclude_class_types, adjustable_exclude_class_types=adjustable_exclude_class_types, is_eco=is_eco, foo_get_current_value=foo_get_current_value + ) + except Exception as e: + print(e) + print(key) + self._check_adjustables() + + def _check_adjustables(self, check_for_current_none_values=True, by_id=True): + self.ids_bad = [] + for aid, adict in self.ids_parsed.items(): + if "value" in adict.keys(): + try: + v = adict["value"].get_current_value() + except Exception as e: + self.ids_bad.append(aid) + print( + f"get_current_value() method of {adict['names']} failed with {e}" + ) + continue + if check_for_current_none_values and v is None: + self.ids_bad.append(aid) + + def _orderlist(self, mylist, key_order, orderlist=None): + key_order = key_order.split(" ") + if orderlist == None: + index = np.concatenate( + [np.where(np.array(mylist) == k)[0] for k in key_order if k in mylist] + ) + else: + index = np.concatenate( + [ + np.where(np.array(orderlist) == k)[0] + for k in key_order + if k in orderlist + ] + ) + curidx = np.arange(len(mylist)) + newidx = np.append(index, np.delete(curidx, index)) + return [mylist[n] for n in newidx] + + def order_df(self, key_order=None): + """ + This function orders the columns of the stored dataframe by the given key_order. + key_order is a string with consecutive keys such as 'name type pulse_id. It defaults to self.key_order' + """ + if key_order is None: + key_order = self.key_order + devs = [item[0] for item in list(self.columns)] + self.df = self[self._orderlist(list(self.columns), key_order, orderlist=devs)] + + #### diagnostic and convenience functions #### + def check_timeouts( + self, include_bad_adjustables=True, repeats=1, plot=True, verbose=True + ): + if np.all([len(self.ids_parsed) == 0, self.parse]): + self._parse_parent(verbose=verbose) + ts = [] + devs = [] + + def get_dev_adjs(dev): + for k, adj in dev.items(): + val = adj.get_current_value() + + for k, dev in self.good_adjustables.items(): + + def func(dev=dev): + return get_dev_adjs(dev) + + t = timeit.timeit(func, number=repeats) + ts.append(float(t)) + devs.append(k) + print(k, t) + idx = np.argsort(ts) + self.times = [np.array(devs)[idx], np.array(ts)[idx]] + print("recorded adjustable results stored in run_table._data.times") + if include_bad_adjustables: + for k, dev in self.bad_adjustables.items(): + + def func(dev=dev): + return get_dev_adjs(dev) + + t = timeit.timeit(func, number=repeats) + ts.append(float(t)) + devs.append(k) + print(k, t) + idx = np.argsort(ts) + print( + "rejected timed out adjustable results stored in run_table._data.times_rejected" + ) + self.times_rejected = [np.array(devs)[idx], np.array(ts)[idx]] + + if plot: + import pylab as plt + + fig, ax = plt.subplots(1) + if include_bad_adjustables: + plt.barh( + self.times_rejected[0], + self.times_rejected[1], + color="red", + label="rejected adjustables", + ) + plt.barh( + self.times[0], + self.times[1], + label="recorded adjustables", + color="seagreen", + ) + plt.xlabel("time (s)") + plt.legend() + + +def name2obj(obj_parent, name, delimiter="."): + if type(name) is str: + name = name.split(delimiter) + obj = obj_parent + for tn in name: + if not tn or tn == "self": + obj = obj + else: + obj = obj.__dict__[tn] + + return obj diff --git a/eco/xoptics/kb_mirrors.py b/eco/xoptics/kb_mirrors.py index 1c8d1db..0c7d025 100644 --- a/eco/xoptics/kb_mirrors.py +++ b/eco/xoptics/kb_mirrors.py @@ -48,7 +48,7 @@ class KbVer(Assembly): self._append( AdjustableVirtual, [self.bend1, self.bend2], - lambda b1, b2: float(np.diff([b1, b2])), + lambda b1, b2: np.diff([b1, b2])[0], lambda mn: self._get_benders_set_diff(mn), name="bender_diff", unit="mm", diff --git a/eco/xoptics/offsetMirrors_new.py b/eco/xoptics/offsetMirrors_new.py index 6a8e828..2d4cf62 100644 --- a/eco/xoptics/offsetMirrors_new.py +++ b/eco/xoptics/offsetMirrors_new.py @@ -67,7 +67,7 @@ class OffsetMirrorsBernina(Assembly): self._append( AdjustableVirtual, [self.mirr1.rz, self.mirr2.rz], - lambda b1, b2: float(np.diff([b1, b2])), + lambda b1, b2: np.diff([b1, b2])[0], lambda mn: self._set_diff_2adj(mn, self.mirr1.rz, self.mirr2.rz), name="rz_diff", unit="mrad", diff --git a/eco/xoptics/pp.py b/eco/xoptics/pp.py index 0d892f1..1fcc074 100755 --- a/eco/xoptics/pp.py +++ b/eco/xoptics/pp.py @@ -107,15 +107,17 @@ class XrayPulsePicker(Assembly): else: return "unknown" - def open(self): + def open(self,verbose=True): self.evr_output_enable.set_target_value(1).wait() #self._evrsrc.put(62) - print("Opened Pulse Picker") + if verbose: + print("Opened Pulse Picker") - def close(self): + def close(self,verbose=True): self.evr_output_enable.set_target_value(0).wait() #self._evrsrc.put(62) - print("Closed Pulse Picker") + if verbose: + print("Closed Pulse Picker") def set_target_value(self,value,hold=False): if value: