after trigo

This commit is contained in:
2025-06-24 15:33:02 +02:00
parent 8ade6cf615
commit d54109e6de
31 changed files with 1232 additions and 87 deletions
+559
View File
@@ -0,0 +1,559 @@
# likely never worked ...
# def _wait_for_tasks(scan, **kwargs):
# print("checking remaining tasks from previous scan ...")
# for task in scan.remaining_tasks:
# task.join()
# print("... done.")
class CounterStatusInitNamespaceToDAQ:
def __init__(self, namespace=None, daq=None):
self.namespace = namespace
self.daq = daq
def callback_start_scan(self,scan=None, append_status_info=True):
if not append_status_info:
return
namespace_status = namespace.get_status(base=None)
stat = {"status_run_start": namespace_status}
scan.status = stat
def callback_start_step(self, scan=None, append_status_info=True):
pass
def callback_end_step(self, scan=None, append_status_info=True):
pass
def callback_end_scan(self, scan=None, append_status_info=True):
pass
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)
+11
View File
@@ -87,6 +87,8 @@ class Daq(Assembly):
@pgroup.setter
def pgroup(self, value):
if isinstance(self._pgroup, Adjustable):
return self._pgroup.set_target_value().wait()
self._pgroup = value
def acquire(self, file_name=None, Npulses=100, acq_pars={}):
@@ -376,3 +378,12 @@ def validate_response(resp):
# }
# r = requests.post(f'{broker_address}/retrieve_from_buffers',json=parameters, timeout=TIMEOUT_DAQ).json()
#### >>> TODO implment >>>
#### <<< TODO implment <<<
+30 -5
View File
@@ -139,6 +139,7 @@ class Scan:
def doNextStep(self, step_info=None, verbose=True):
# for call in self.callbacks_start_step:
# call()
t_step_start = time()
if self.checker:
first_check = time()
checker_unhappy = False
@@ -159,10 +160,14 @@ class Scan:
)
self.checker.clear_and_start_counting()
if self.callbacks_start_step:
for caller in self.callbacks_start_step:
caller(self, **self.callbacks_kwargs)
dt_callbacks_step_start = time()-t_step_start
if not len(self.values_todo) > 0:
return False
values_step = self.values_todo[0]
@@ -171,16 +176,20 @@ class Scan:
"Starting scan step %d of %d"
% (self.nextStep + 1, len(self.values_todo) + len(self.values_done))
)
ms = []
fina = self.get_filename(self.nextStep)
t_adj_start = time()
ms = []
for adj, tv in zip(self.adjustables, values_step):
ms.append(adj.set_target_value(tv))
for tm in ms:
tm.wait()
dt_adj = time()-t_adj_start
# settling
sleep(self.settling_time)
t_ctr_start = time()
readbacks_step = []
adjs_name = []
adjs_offset = []
@@ -225,26 +234,42 @@ class Scan:
for ta in acs:
ta.wait()
filenames.extend(ta.file_names)
dt_ctr = time() - t_ctr_start
if verbose:
print("Done with acquisition")
### >>> Callback end
t_callbacks_step_end = time()
if self.checker:
if not self.checker.stop_and_analyze():
return True
if callable(step_info):
tstepinfo = step_info()
else:
tstepinfo = step_info
tstepinfo = {}
self.values_done.append(self.values_todo.pop(0))
self.pulses_done.append(self.pulses_per_step.pop(0))
self.readbacks.append(readbacks_step)
if self.callbacks_end_step:
for caller in self.callbacks_end_step:
caller(self, **self.callbacks_kwargs)
dt_callbacks_step_end = time() - t_callbacks_step_end
### <<<< Callback end
tstepinfo['times'] = {
"callbacks_step_start":dt_callbacks_step_start,
"adjustables" : dt_adj,
"counters" : dt_ctr,
"callbacks_step_end": dt_callbacks_step_end,
}
self.appendScanInfo(
values_step, readbacks_step, step_files=filenames, step_info=tstepinfo
)
self.writeScanInfo()
if self.callbacks_end_step:
for caller in self.callbacks_end_step:
caller(self, **self.callbacks_kwargs)
self.nextStep += 1
return True
+1 -1
View File
@@ -38,7 +38,7 @@ class Acquisition:
if self._thread.ident is None:
return "waiting"
else:
if self._thread.isAlive():
if self._thread.is_alive():
return "acquiring"
else:
return "done"
+89 -12
View File
@@ -1239,6 +1239,8 @@ namespace.append_obj(
# module_name="eco.acquisition.epics_data",
# )
## TODO pgroup non adjustable/dynamically changeable!
namespace.append_obj(
"Scans",
name="scans_epics",
@@ -1289,6 +1291,8 @@ namespace.append_obj(
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",
@@ -1870,6 +1874,7 @@ namespace.append_obj(
name="checker",
)
# TODO resove scans pgroup sensitivity! Clearly non dynamic.
namespace.append_obj(
"Scans",
data_base_dir="scan_data",
@@ -2191,25 +2196,51 @@ class Incoupling(Assembly):
def __init__(self, name=None):
super().__init__(name=name)
self._append(SmaractRecord, "SARES23-LIC:MOT_13", name="ry", is_setting=True)
self._append(SmaractRecord, "SARES23-LIC:MOT_16", name="rx", is_setting=True)
self._append(SmaractRecord, "SARES23-USR:MOT_4", name="rx", is_setting=True)
self._append(SmaractRecord, "SARES23-LIC: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_2", name="focus_pos",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 = {
"polarizer": {
"pvname": "SLAAR21-LMOT-ELL3",
},
"hwp": {
"pvname": "SLAAR21-LMOT-ELL5",
},
"block": {
"nir_block": {
"pvname": "SLAAR21-LMOT-ELL2",
},
"nd_filter": {
"eos_block": {
"pvname": "SLAAR21-LMOT-ELL4",
},
}
}
### thorlabs piezo motors ###
@@ -2222,6 +2253,18 @@ class Incoupling(Assembly):
)
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,
@@ -2394,6 +2437,39 @@ 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):
@@ -2775,6 +2851,7 @@ namespace.append_obj(
# 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
@@ -2906,7 +2983,7 @@ class IlluminatorsLasers(Assembly):
self._append(
MpodChannel,
pvbase="SARES21-PS7071",
channel_number=5,
channel_number=3,
name="illumination_inline",
)
self._append(
+1 -1
View File
@@ -1,4 +1,4 @@
from data_api import get_data, search
# from data_api import get_data, search
from ..epics.detector import DetectorPvDataStream
from fnmatch import translate
import datetime, dateutil
+2 -1
View File
@@ -324,7 +324,8 @@ class CameraBasler(Assembly):
camserver_alias=camserver_alias,
camserver_group=camserver_group,
name="config_cs",
is_display=False,
is_display="recursive",
is_setting=True
)
self.config_cs.set_alias()
+12 -4
View File
@@ -1283,12 +1283,15 @@ class MotorRecord(Assembly):
if not step_value:
step_value = pv.get()
print(f"Tweaking {self.name} at step size {step_value}", end="\r")
start_value = self.get_current_value()
# help = "q = exit; up = step*2; down = step/2, left = neg dir, right = pos dir\n"
# help = help + "g = go abs, s = set"
help = "q = exit; up = step*2; down = step/2, left = neg dir, right = pos dir\n"
help = help + "g = go abs, s = set"
help = help + "g = go abs, s = start value, r = reset current value to"
print(f"tweaking {self.name}")
print(help)
print(f"Starting at {self.get_current_value()}")
print(f"Starting at {start_value}")
step_value = float(step_value)
oldstep = 0
k = KeyPress()
@@ -1323,6 +1326,9 @@ class MotorRecord(Assembly):
pvf.put(1)
elif k.isl():
pvr.put(1)
elif k.iskey("s"):
self.set_target_value(start_value)
p.print(value=self.get_current_value())
elif k.iskey("g"):
print("enter absolute position (char to abort go to)")
sys.stdout.flush()
@@ -1333,7 +1339,7 @@ class MotorRecord(Assembly):
except:
print("value cannot be converted to float, exit go to mode ...")
sys.stdout.flush()
elif k.iskey("s"):
elif k.iskey("r"):
print("enter new set value (char to abort setting)")
sys.stdout.flush()
v = sys.stdin.readline()
@@ -1341,15 +1347,17 @@ class MotorRecord(Assembly):
v = float(v[0:-1])
self.reset_current_value_to(v)
except:
print("value cannot be converted to float, exit go to mode ...")
print("value cannot be converted to float, exit reset mode ...")
sys.stdout.flush()
elif k.isq():
break
else:
print(help)
self.clear_value_callback(index=ind_callback)
print("\r", end="")
print(f"final position: {self.get_current_value()}")
print(f"final tweak step: {pv.get()}")
# print('\033[K',"the info",sep='',flush=True)
def tweak(self, *args, **kwargs):
return self._tweak_ioc(*args, **kwargs)
+2
View File
@@ -49,6 +49,8 @@ class PowerBrickComm:
parstring.strip("\n")
setstring = parstring + "=" + str(value)
return self.pbsshcom.iawrite(setstring)
# def set_hmz
class PowerBrickChannelPars(Assembly):
@@ -40,6 +40,7 @@ pars_StatusMotorListCtrl = {
("EncLoss", "r"),
("HomeInProgress", "r"),
("HomeComplete", "r"),
("HomeOffset", "r"),
("TriggerMove", "r"),
("DacLimit", "r"),
("SoftLimit", "r"),
+4 -1
View File
@@ -118,7 +118,10 @@ class MpodChannel(Assembly):
self.pvbase+f':{self._module_string}_CH{self.channel_number}_RMP_DOWN_RATE_SP',
pvlowlimname = self.pvbase+f':{self._module_string}_CH{self.channel_number}_RMP_DOWN_RATE_SP.LOPR',
pvhighlimname = self.pvbase+f':{self._module_string}_CH{self.channel_number}_RMP_DOWN_RATE_SP.HOPR',
name='ramp_down', is_setting=True)
name='ramp_down', is_setting=True)
self._append(AdjustablePv,
self.pvbase+f':{self._module_string}_CH{self.channel_number}_MEAS_OUT_A',
name='current', is_setting=False, is_display=True)
self._append(MpodStatus,self.pvbase, self.channel_number, self._module_string, name='flags')
def get_current_value(self,*args,**kwargs):
+1
View File
@@ -8,6 +8,7 @@ class Changer:
self._changer = changer
self._stopper = stopper
self._thread = PropagatingThread(target=self._changer, args=(target,))
# self._thread = Thread(target=self._changer, args=(target,))
if not hold:
self._thread.start()
+28
View File
@@ -139,6 +139,34 @@ class AnalogOutput(Assembly):
is_setting=False,
is_display=False,
)
self._append(
AdjustablePv,
self.pvname + ".AOFF",
name="_adj_offset",
is_setting=True,
is_display=False,
)
self._append(
AdjustablePv,
self.pvname + ".ASLO",
name="_adj_slope",
is_setting=True,
is_display=False,
)
self._append(
AdjustablePv,
self.pvname + ".EOFF",
name="linear_calibration_offset",
is_setting=True,
is_display=False,
)
self._append(
AdjustablePv,
self.pvname + ".ESLO",
name="linear_calibration_slope",
is_setting=True,
is_display=False,
)
def get_current_value(self):
return self.value.get_current_value()
+46
View File
@@ -0,0 +1,46 @@
# single acquisition class
class Acquisition:
def __init__(
self,
parent=None,
acquire=lambda: None,
acquisition_kwargs={},
hold=True,
stopper=None,
get_result=lambda: None,
):
self.acquisition_kwargs = acquisition_kwargs
for key, val in acquisition_kwargs.items():
self.__dict__[key] = val
self._stopper = stopper
self._get_result = get_result
if acquire:
self.set_acquire_foo(acquire, hold=hold)
def set_acquire_foo(self, acquire, hold=True):
self._acquire = acquire
self._thread = PropagatingThread(target=self._acquire)
if not hold:
self._thread.start()
def wait(self):
self._thread.join()
return self._get_result()
def start(self):
self._thread.start()
def status(self):
if self._thread.ident is None:
return "waiting"
else:
if self._thread.is_alive():
return "acquiring"
else:
return "done"
def stop(self):
self._stopper()
+10 -6
View File
@@ -82,7 +82,7 @@ def spec_convenience(Adj):
try:
self._currentChange = self.set_target_value(value)
self._currentChange.wait()
except KeyboardInterrupt:
except (KeyboardInterrupt,SystemExit):
self._currentChange.stop()
return self._currentChange
@@ -101,9 +101,12 @@ def spec_convenience(Adj):
self._currentChange = self.set_target_value(
value + startvalue, *args, **kwargs
)
print("spec conven")
self._currentChange.wait()
except KeyboardInterrupt:
except (KeyboardInterrupt,SystemExit):
self._currentChange.stop()
return self._currentChange
Adj.mv = mv
@@ -292,7 +295,7 @@ def update_changes(Adj):
cb_id = self.add_value_callback(cbfoo)
self._currentChange = self.set_target_value(value)
self._currentChange.wait()
except KeyboardInterrupt:
except (KeyboardInterrupt, SystemExit):
self._currentChange.stop()
print(f"\nAborted change at (~) {self.get_current_value():1.5g}")
finally:
@@ -641,14 +644,15 @@ class AdjustableVirtual:
)
def check_target_value_within_limits(self, value):
in_lims = []
in_lims = [True]
values = self._foo_set_target_value_current_value(value)
if not hasattr(values, "__iter__"):
values = (values,)
for val, adj in zip(values, self._adjustables):
lim_low, lim_high = adj.get_limits()
in_lims.append((lim_low < val) and (val < lim_high))
if not val is None:
in_lims.append((lim_low < val) and (val < lim_high))
return all(in_lims)
def reset_current_value_to(self, value):
@@ -847,7 +851,7 @@ class Tweak:
self.thread = None
def print(self):
if self.thread and self.thread.isAlive():
if self.thread and self.thread.is_alive():
return
else:
self.thread = Thread(target=self.print_foo)
+2 -2
View File
@@ -7,8 +7,8 @@ from tabulate import tabulate
import sys, colorama
try:
from inspect import getargspec
except:
from inspect import getfullargspec
except: # for python 3.12
from inspect import getfullargspec as getargspec
import eco
from ansi2html import Ansi2HTMLConverter
+14
View File
@@ -28,3 +28,17 @@ class ValueUpdateMonitorable(Protocol):
class InitialisationWaitable(Protocol):
def _wait_for_initialisation(self):
...
@runtime_checkable
class Counter:
def acquire(self):
...
def start(self):
...
def stop(self):
...
# file_name=fina, Npulses=self.pulses_per_step[0], acq_pars=acq_pars):
)
+8 -8
View File
@@ -431,14 +431,6 @@ class GPS(Assembly):
append_diffractometer_modules(self, configuration)
if configuration.diffcalc():
self._append(
Crystals,
diffractometer_you=self,
name="diffcalc",
is_setting=False,
is_display=False,
)
for jf_id, jf_name in configuration.jfs():
self._append(
Jungfrau,
@@ -468,6 +460,14 @@ class GPS(Assembly):
is_setting=False,
is_display=True,
)
if configuration.diffcalc():
self._append(
Crystals,
diffractometer_you=self,
name="diffcalc",
is_setting=False,
is_display=False,
)
if recspace_conv is not None:
module_name, Conv_name = recspace_conv.split(":")
+23 -2
View File
@@ -145,6 +145,13 @@ class StaeubliTx200(Assembly):
try:
import bernina_urdf
self._urdf = bernina_urdf.models.Tx200_Ceiling(jf_id=robot_config.jf_id())
if robot_config.vis_gps():
try:
self._urdf.__dict__['gps'] = bernina_urdf.models.GPS()
from epics import PV
self._gps_motors = [PV(f'SARES22-GPS:MOT_{m}.RBV') for m in ['TX', 'TY', 'RX', 'NY_RY2TH', 'MY_RYTH', 'HEX_TX', 'HEX_RX']]
except Exception as e:
print(f"Adding visual model of GPS failed with {e}")
self._append(
AdjustableFS,
f"/sf/bernina/config/eco/reference_values/robot_auto_update_simulation.json",
@@ -603,10 +610,13 @@ class StaeubliTx200(Assembly):
def show(self):
self._urdf.sim.show()
if 'gps' in self._urdf.__dict__.keys():
self._urdf.sim.vis.add(self._urdf.__dict__['gps'])
######## Helper functions ##########
def _auto_updater_simulation(self): #
while True:
gpss = None
js = np.array(
[
self._cache["pos"][k]
@@ -616,11 +626,22 @@ class StaeubliTx200(Assembly):
if np.any([j is None for j in js]):
time.sleep(1)
continue
if "_gps_motors" in self.__dict__.keys():
try:
gpss = np.array([m.value if not 'MOT_RX.RBV' in m.pvname else np.arctan(m.value / 1146) * 180 / np.pi for m in self._gps_motors ])
except Exception as e:
print(e)
if self.auto_update_simulation():
change = False
if not np.all(js.round(3) == self._urdf.sim.pos.round(3)):
self._urdf.sim.pos = js
if self._urdf.sim._vis_running():
self._urdf.sim.vis.step(0)
change = True
if (gpss is not None)&('gps' in self._urdf.__dict__.keys()):
if not np.all(gpss.round(3) == self._urdf.gps.sim.pos.round(3)):
self._urdf.gps.sim.pos = gpss
change = True
if self._urdf.sim._vis_running() and change:
self._urdf.sim.vis.step(0)
time.sleep(0.05)
def _get_on_poll_info(self):
+81 -14
View File
@@ -2,6 +2,7 @@ from eco.xoptics.attenuator_safety_bernina import AttenuatorSafetyBernina
from scipy import constants
from eco.devices_general.powersockets import MpodChannel
from eco.devices_general.wago import AnalogOutput
from eco.devices_general.cameras_swissfel import CameraBasler
from eco.epics.detector import DetectorPvDataStream
import sys
@@ -88,18 +89,55 @@ class THzVirtualStages(Assembly):
self.offset_mirr_z.mv(self._mz())
self.offset_par_z.mv(self._pz())
class THz_cameras(Assembly):
def __init__(self, name=None, camera_config={}):
super().__init__(name=name)
for name, cfg in camera_config.items():
self._append(
CameraBasler,
cfg["pvname"],
camserver_alias = "THC_" + name,
name=name,
is_setting=True,
is_display="recursive",
)
self.__dict__[name].serial_no.mv(cfg["serial_number"])
class High_field_thz_chamber(Assembly):
def __init__(
self,
delay_offset_detector = None,
thc_x_adjustable=None,
name=None,
configuration=[],
illumination_mpod=None,
helium_control_valve=None,
):
super().__init__(name=name)
self.delay_offset_detector = delay_offset_detector
self._thc_x_adjustable = thc_x_adjustable
self.par_out_pos = [-20, -9.5]
self.camera_configuration = {
"inline": {
"pvname": "SARES20-CAMS142-M2",
"serial_number": 23067644,
},
"sideview_45": {
"pvname": "SARES20-CAMS142-M3",
"serial_number": 23075971,
},
}
self.motor_configuration = {
"inline_mirror": {
"id": "SARES23-USR:MOT_1",
"pv_descr": "THz Chamber Inline_Mirror",
"direction": 1,
"sensor": 1,
"speed": 250,
"offset": 2.150241,
"home_direction": "back",
"kwargs": {"accuracy": 0.01},
},
"rx": {
# "id": "SARES23-USR:MOT_13",
"id": "SARES23-USR:MOT_16",
@@ -194,6 +232,14 @@ class High_field_thz_chamber(Assembly):
"home_direction": "forward",
},
}
### Cameras ###
self._append(
THz_cameras,
name="camera",
camera_config=self.camera_configuration,
)
### lakeshore temperatures ####
self._append(
AdjustablePv,
@@ -219,20 +265,7 @@ class High_field_thz_chamber(Assembly):
name="temp_gishield",
is_setting=False,
)
### in vacuum smaract motors ###
# for name, config in self.motor_configuration.items():
# if "kwargs" in config.keys():
# tmp_kwargs = config["kwargs"]
# else:
# tmp_kwargs = {}
# self._append(
# SmaractStreamdevice,
# pvname=Id + config["id"],
# name=name,
# is_setting=True,
# **tmp_kwargs,
# )
### in vacuum smaract motors ###
for name, config in self.motor_configuration.items():
self._append(
SmaractRecord,
@@ -255,6 +288,40 @@ class High_field_thz_chamber(Assembly):
name="delay_x_center",
)
self._append(
MotorRecord,
pvname="SLAAR21-LMOT-M522:MOTOR_1",
name="delaystage_thz",
is_setting=True,
is_display=False,
is_status=True,
)
self._append(
DelayTime,
self.delaystage_thz,
name="delay_thz",
offset_detector=self.delay_offset_detector,
is_setting=False,
is_display=True,
is_status=True,
)
if self._thc_x_adjustable is not None:
def movexcomp(x):
delay = self.delay_thz.get_current_value()
dx = x - self._thc_x_adjustable.get_current_value()
new_delay = delay + dx / 1000 / constants.c
return x, new_delay
self._append(
AdjustableVirtual,
[self._thc_x_adjustable, self.delay_thz],
lambda x, delay_thz: x,
movexcomp,
name="thcx_delaycomp",
is_setting=False,
)
if "cube" in configuration:
for name, config in self.motor_configuration_cube.items():
self._append(
+1 -1
View File
@@ -139,7 +139,7 @@ class HexapodPI(Assembly):
),
reset_current_value_to=False,
change_simultaneously=False,
check_limits=False,
check_limits=True,
append_aliases=True,
unit="mm",
name="y",
+2 -2
View File
@@ -198,6 +198,7 @@ class AdjustablePv:
""" 0: moving 1: move done"""
change_done = 1
if self.accuracy is not None:
if (
np.abs(
self.get_current_value(readback=False)
@@ -221,9 +222,8 @@ class AdjustablePv:
)
self._pv.put(value)
time.sleep(0.1)
while self.get_change_done() == 0:
time.sleep(0.1)
time.sleep(0.01)
def set_target_value(self, value, hold=False):
"""Adjustable convention"""
+43 -2
View File
@@ -48,8 +48,12 @@ class DetectorPvData(Assembly):
else:
return self.readback.get_current_value()
# def get_current_value_callback(self):
# pass
def get_current_value_callback(self, foo='accumulate',collector = [], run_once=True, print_output=False):
if hasattr(self, "_pv"):
return CallbackEpics(self,self._pv,foo=foo,collector=collector, run_once=run_once, print_output=print_output)
# else:
# raise Exception('the object does not have a _pv')
def __call__(self):
return self.get_current_value()
@@ -249,3 +253,40 @@ class DetectorPvDataStream(Assembly):
def get_current_value(self, **kwargs):
return self._pv.get(**kwargs)
class CallbackEpics:
def __init__(self,pv,foo='accumulate',collector = [], run_once=True, print_output=False):
self.pv = pv
if collector is not None:
self.data = collector
if foo=='accumulate':
foo = self.accumulate_values
self.foo = foo
self.run_once = run_once
self.print = print_output
def start(self):
self.cb_index = self.pv.add_callback(self.foo,run_once=True,)
def stop(self):
self.pv.remove_callback(self.cb_index)
def __enter__(self):
self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop()
def accumulate_values(self, pvname=None, value=None, timestamp=None, **kwargs):
# if not self.data:
# self.data = []
ts_local = time()
self.data.append(
{"value": value, "timestamp_ioc": timestamp, "timestamp_local": ts_local}
)
if self.print:
print(
f"{pvname}: {value}; time_ioc: {timestamp}; time_local: {ts_local}; diff: {ts_local-timestamp}"
)
+14 -8
View File
@@ -1120,6 +1120,12 @@ class LaserBernina(Assembly):
name="delaystage_m2",
is_setting=True,
)
self._append(
MotorRecord,
"SLAAR21-LMOT-M533:MOT",
name="hwp_compressor",
is_setting=True,
)
self._append(
DelayTime,
self.delaystage_pump,
@@ -1301,14 +1307,14 @@ class PositionMonitors(Assembly):
# is_display="recursive",
# is_status=True,
# )
# self._append(
# CameraPositionMonitor,
# "SLAAR21-LCAM-CS841",
# # name="table2_position",
# name="timing_drift",
# is_display="recursive",
# is_status=True,
# )
self._append(
CameraPositionMonitor,
"SLAAR21-LCAM-CS841",
# name="table2_position",
name="timing_drift",
is_display="recursive",
is_status=True,
)
self._append(
CameraPositionMonitor,
"SLAAR21-LCAM-C561",
+36 -5
View File
@@ -248,12 +248,33 @@ class MasterEventSystem(Assembly):
def __repr__(self):
return self.status(printit=False)
class EvrSequencer(Assembly):
def __init__(self, pv_base, name=None):
super().__init__(name=name)
try:
self._append(AdjustablePvEnum, pv_base + ':SEQ_SOURCE', name='source', is_display=True, is_setting=True)
self._append(AdjustablePvEnum, pv_base + ':SEQ_SNUMPD', name='pulser_number', is_display=True, is_setting=True)
self._append(DetectorPvData, pv_base + ':SEQ_RUNNING', name='is_running', is_display=True)
self._append(AdjustablePvEnum, pv_base + ':Seq-Ena-Sel', name='enabled', is_display=True, is_setting=True)
self._append(DetectorPvData, pv_base + ':SEQ_SELECT_FREQ', name='frequency', is_display=True)
except:
print(f'The evr sequencer of {pv_base} is likely old type')
self._append(AdjustableMemory, None, name='frequency', is_display=True)
self._append(AdjustablePvEnum, pv_base + ':Seq-RunMode-Sel', name='mode', is_display=True, is_setting=True)
self._append(AdjustablePv, pv_base + ':SEQ_DELAY', name='delay', is_display=True, is_setting=True)
self._append(AdjustablePv, pv_base + ':SEQ_REPS', name='repetitions', is_display=True, is_setting=True)
self._append(AdjustablePv, pv_base + ':SEQ_MULTIPLIER', name='freq_multiplier', is_display=True, is_setting=True)
class EvrPulser(Assembly):
def __init__(self, pv_base, event_master, name=None):
def __init__(self, pv_base, event_master, parent_evr=None, name=None):
super().__init__(name=name)
self.pv_base = pv_base
self._event_master = event_master
self._parent_evr = parent_evr
self._append(
AdjustablePvString, pv_base + "-Name-I", name="description", is_display=True
@@ -329,10 +350,13 @@ class EvrPulser(Assembly):
@property
def _eventcode(self):
try:
return self._event_master.event_codes[self.eventcode.get_current_value()]
except KeyError:
return None
if self.eventcode.get_current_value() == 27:
return self._parent_evr.sequencer
else:
try:
return self._event_master.event_codes[self.eventcode.get_current_value()]
except KeyError:
return None
class DummyPulser(Assembly):
@@ -527,12 +551,17 @@ class EventReceiver(Assembly):
):
super().__init__(name=name)
self.pvname = pvname
self._append(EvrSequencer,self.pvname,name='sequencer', is_display=True, is_setting=True)
pulsers = []
for n in range(n_pulsers):
self._append(
EvrPulser,
f"{self.pvname}:Pul{n}",
event_master,
parent_evr=self,
name=f"pulser{n}",
is_setting=True,
is_display=False,
@@ -564,6 +593,8 @@ class EventReceiver(Assembly):
# to._pulsers = self.pulsers
self.outputs = outputs
self._append(
AdjustablePv,
self.pvname + ":SYSRESET",
+3 -2
View File
@@ -11,12 +11,13 @@ plt.ion()
class TtProcessor:
def __init__(self, Nbg=10, channel_proj = "SARES20-CAMS142-M5.roi_signal_x_profile"):
def __init__(self, Nbg=10, ref_code=25, channel_proj = "SARES20-CAMS142-M5.roi_signal_x_profile"):
self.channel_proj = channel_proj
self.bg = deque([], Nbg)
self.sig = deque([], 1)
self.pos = deque([], 1)
self.amp = deque([], 1)
self.ref_code=ref_code
self.spec_ppd = deque([], 1)
self.accumulator = Thread(target=self.run_continuously)
self.accumulator.start()
@@ -41,7 +42,7 @@ class TtProcessor:
codes = m.data.data["SAR-CVME-TIFALL5:EvtSet"].value
if codes is None:
continue
is_reference = codes[25] == 1
is_reference = codes[self.ref_code] == 1
try:
if (lastgoodix - ix) > 1:
print(f"missed {lastgoodix-ix-1} events!")
-2
View File
@@ -178,14 +178,12 @@ class TimetoolBerninaUSD(Assembly):
AdjustablePv,
pvsetname="SLAAR21-LFEEDBACK1:TARGET1",
name="feedback_setpoint",
accuracy=10,
is_setting=True,
)
self._append(
AdjustablePv,
pvsetname="SLAAR21-LFEEDBACK1:ENABLE",
name="feedback_enabled",
accuracy=10,
is_setting=True,
)
self._append(
+5
View File
@@ -540,6 +540,11 @@ class Namespace(Assembly):
print(f"Initialisation took {time()-starttime} seconds")
if (not silent) and print_times:
try:
from collections import Iterable
except:
import collections.abc
collections.Iterable = collections.abc.Iterable
from ascii_graph import Pyasciigraph
gr = Pyasciigraph()
+23 -6
View File
@@ -397,18 +397,35 @@ class DiffGeometryYou(Assembly):
### use robot motors if robot is in config
if cfg.robot():
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/move_robot",
name="move_robot",
default_value=True,
is_setting=False,
)
def rob_get(a): return a
def rob_set(a):
if self.move_robot():
return [a]
else:
return None
gam_rob = AdjustableVirtual([self.diffractometer.gamma_robot],rob_get, rob_set, name="gamma_robot", check_limits=True)
del_rob = AdjustableVirtual([self.diffractometer.delta_robot],rob_get, rob_set, name="delta_robot", check_limits=True)
get_lims = lambda a: a
gam_rob.get_limits = get_lims(self.diffractometer.gamma_robot.get_limits)
del_rob.get_limits = get_lims(self.diffractometer.delta_robot.get_limits)
adjustables_dict.update(
{
"gamma": self.diffractometer.gamma_robot,
"delta": self.diffractometer.delta_robot,
"gamma": gam_rob,
"delta": del_rob,
}
)
### add the phi constraint to thc as phi_wobble if thc is in config
if cfg.thc():
import eco.bernina as b
thc = b.__dict__["thc"]
thc.phi_wobble = self.constraints.phi
def phi_wobble_get(a): return a
def phi_wobble_set(a): return [a]
self.diffractometer.thc._append(AdjustableVirtual, [self.constraints.phi], phi_wobble_get, phi_wobble_set, name='phi_wobble')
if cfg.kappa():
adjs = ["gamma", "mu", "delta", "eta_kap", "kappa", "phi_kap"]
+3 -2
View File
@@ -1,4 +1,4 @@
from time import sleep
from time import sleep, time
import sys, select
from threading import Thread
@@ -32,9 +32,9 @@ class TimeoutPath:
def __str__(self) -> str:
return str(self._path)
class PropagatingThread(Thread):
def run(self):
run_start = time()
self.exc = None
try:
if hasattr(self, "_Thread__target"):
@@ -44,6 +44,7 @@ class PropagatingThread(Thread):
)
else:
self.ret = self._target(*self._args, **self._kwargs)
except BaseException as e:
self.exc = e
+177
View File
@@ -0,0 +1,177 @@
from time import sleep
import sys, select
from threading import Thread
from concurrent.futures import ThreadPoolExecutor, TimeoutError
from pathlib import Path
from typing import Any
import numpy as np
import matplotlib.pyplot as plt
from numbers import Number
class TimeoutPath:
executor = ThreadPoolExecutor(max_workers=1)
def __init__(self, *args, timeout: float = 1, **kwargs):
self._path = Path(*args, **kwargs)
self.timeout = timeout
def exists(self) -> bool:
future = TimeoutPath.executor.submit(self._path.exists)
try:
return future.result(self.timeout)
except TimeoutError:
return False
def get_path(self) -> Path:
return self._path
def __getattr__(self, name: str) -> Any:
return getattr(self._path, name)
def __str__(self) -> str:
return str(self._path)
class PropagatingThread(Thread):
def run(self):
self.exc = None
try:
if hasattr(self, "_Thread__target"):
# Thread uses name mangling prior to Python 3.
self.ret = self._Thread__target(
*self._Thread__args, **self._Thread__kwargs
)
else:
self.ret = self._target(*self._args, **self._kwargs)
except BaseException as e:
self.exc = e
def join(self):
super(PropagatingThread, self).join()
if self.exc:
raise self.exc
return self.ret
def isiter(a):
try:
iter(a)
return True
except TypeError:
return False
def roundto(v,interval):
return np.rint(v/interval)*interval
def linlog_intervals(*args, verbose=True, plot=False):
"""Get linearly and logarithmically spaced arrays from providing limits and intervals or number of intervals.
Example usages:
linlog_intervals(-1e-12,('lin',.1e-12),2e-12,('log',2e-12),1e-6,('lin',4),5e-6)
Args:
*args : limits and specifications of intervals,
limits are numbers,
specifications tuples of strings 'lin' or 'log'
and the definition if interval.
A integer is interpretet as number of intervals within the limits,
a float is interpreted asinterval size,
where in log definition, the size of the first (smallest) interval is matched.
verbose (bool, optional): _description_. Defaults to True.
plot (bool, optional): _description_. Defaults to False.
Raises:
Exception: _description_
Exception: _description_
Exception: _description_
Returns:
numpy ndarray: 1D array of intervals.
"""
limits = []
idefs = []
last_lim = False
for arg in args:
if not last_lim and isinstance(arg,Number):
limits.append(arg)
last_lim = True
elif last_lim and isiter(arg):
idefs.append(arg)
last_lim = False
else:
raise Exception('Limits need to follow interval description and vice versa')
if verbose:
print(limits,idefs)
if not len(limits)==len(idefs)+1:
raise Exception('need exactly one more limit than interval definitions!')
a = []
for i,idef in enumerate(idefs):
tlims = limits[i:i+2]
if np.diff(tlims)<=0:
raise Exception('number limits should increasing!')
if isinstance(a,np.ndarray):
if np.isclose(a[-1],tlims[0]):
a = a[:-1]
a = [a]
if idef[0] == 'lin':
if type(idef[1]) is int:
a.append(np.linspace(*tlims,idef[1]+1))
if type(idef[1]) is float:
a.append(np.arange(tlims[0],tlims[1]+idef[1],idef[1]))
if verbose:
print(f'From {tlims[0]:5g} to {a[-1][-1]:5g}: {len(a[-1])-1} linear intervals of {np.mean(np.diff(a[-1])):5g} size')
if plot:
plt.plot(np.arange(len(np.hstack(a))-len(a[-1]), len(np.hstack(a))),a[-1],'db', mfc='none')
if idef[0] == 'log':
tlims = np.asarray(tlims)
if type(idef[1]) is int:
a.append(np.logspace(*np.log10(tlims),idef[1]+1))
if type(idef[1]) is float:
intlog = np.log10(tlims[0]+idef[1])-np.log10(tlims[0])
a.append(10**np.arange(np.log10(tlims[0]),np.log10(tlims[1]),intlog))
if verbose:
intervals = np.diff(np.log10(a[-1]))
print(f'From {tlims[0]:5g} to {a[-1][-1]:5g}: {len(a[-1])-1} logarithmic intervals between {np.min(intervals):5g} and {np.max(intervals):5g} in sizes.')
if plot:
plt.plot(np.arange(len(np.hstack(a))-len(a[-1]), len(np.hstack(a))),a[-1],'or',mfc='none')
if verbose:
if not np.isclose(a[-1][-1],tlims[1]):
print(f' NB: given limit {tlims[1]:5g} computes off by {tlims[1]-a[-1][-1]:5g} from last element!')
a = np.hstack(a)
if plot:
plt.plot(np.arange(len(a)),a,'.k')
if verbose:
print(f'{len(a)} elements in total.')
return a
# TODO
# _wait_strs = '\|/-\|/-'
# class WaitInput:
# def __init__(self,text,wait_time=5,update_interval=1):
# self.text = text
# self.wait_time=wait_time
# def start(self):
# resttime = self.wait_time
# while resttime>0:
# print(f"You have {resttime} seconds to answer!")
# i, o, e = select.select( [sys.stdin], [], [], 2 )
# if (i):
# print("You said", sys.stdin.readline().strip())
# else:
# print("You said nothing!")