Merge branch 'master' into slab

Merge master into SLAB to add new functionalities
This commit is contained in:
“Roman
2023-10-10 16:49:51 +02:00
70 changed files with 8257 additions and 4254 deletions
+5 -1
View File
@@ -1,5 +1,9 @@
{
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.pythonPath": "/sf/bernina/anaconda/bernina_envs/bernina37/bin/python"
"python.pythonPath": "/sf/bernina/applications/bm/envs/bernina38/bin/python",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "none"
}
+2
View File
@@ -4,3 +4,5 @@ except:
print("cannot import Prototypic protocol classes")
from eco.elements.assembly import Assembly
ELOG = None
+48 -1
View File
@@ -3,11 +3,13 @@ import numpy as np
from ..elements.adjustable import AdjustableFS
from ..epics.adjustable import AdjustablePv
from ..epics.detector import DetectorPvDataStream
from ..detector.detectors_psi import DetectorBsStream
from ..elements.assembly import Assembly
class CheckerCA(Assembly):
def __init__(self, pvname, thresholds, required_fraction, name=None):
def __init__(self, pvname=None, thresholds=None, required_fraction=None, name=None):
super().__init__(name=name)
self._append(DetectorPvDataStream, pvname, name="monitor")
self._append(
@@ -51,6 +53,51 @@ class CheckerCA(Assembly):
print(f" given limit was {self.required_fraction()*100}%.")
return fraction >= self.required_fraction()
class CheckerBS(Assembly):
def __init__(self, bs_channel=None, thresholds=None, required_fraction=None, name=None):
super().__init__(name=name)
self._append(DetectorBsStream, bs_channel, name="monitor")
self._append(
AdjustableFS,
"/photonics/home/gac-bernina/eco/configuration/checker_thresholds",
default_value=sorted(thresholds),
name="thresholds",
)
self._append(
AdjustableFS,
"/photonics/home/gac-bernina/eco/configuration/checker_required_fraction",
default_value=required_fraction,
name="required_fraction",
)
def check_now(self):
cv = self.monitor.get_current_value()
thresholds = self.thresholds()
if cv > thresholds[0] and cv < thresholds[1]:
return True
else:
return False
# def append_to_data(self, **kwargs):
# self.data.append(kwargs["value"])
def clear_and_start_counting(self):
self.monitor.accumulate_start()
# def stopcounting(self):
# self.PV.clear_callbacks()
def stop_and_analyze(self):
data = np.asarray(self.monitor.accumulate_stop())
thresholds = self.thresholds()
good = np.logical_and(data > thresholds[0], data < thresholds[1])
fraction = good.sum() / len(good)
isgood = fraction >= self.required_fraction()
if not isgood:
print(f"Checker: {fraction*100}% inside limits {self.thresholds()},")
print(f" given limit was {self.required_fraction()*100}%.")
return fraction >= self.required_fraction()
# checker_obj = Checker_obj(checkerPV)
+46 -18
View File
@@ -1,7 +1,10 @@
import requests
from pathlib import Path
from time import sleep
from eco.elements.protocols import Adjustable
from ..epics.detector import DetectorPvDataStream
from epics import PV
from ..acquisition.utilities import Acquisition
from ..elements.assembly import Assembly
from ..utilities.path_alias import PathAlias
@@ -23,6 +26,7 @@ class Daq(Assembly):
channels_BSCAM=None,
channels_CA=None,
config_JFs=None,
rate_multiplicator=None,
name=None,
):
super().__init__(name=name)
@@ -43,9 +47,14 @@ class Daq(Assembly):
self.broker_address = broker_address
self.broker_address_aux = broker_address_aux
self.timeout = timeout
self.pgroup = pgroup
self._pgroup = pgroup
if type(pulse_id_adj) is str:
self.pulse_id = DetectorPvDataStream(pulse_id_adj, name="pulse_id")
self._pid_wo_automonitor = PV(
"SGE-CPCW-85-EVR0:RX-PULSEID",
connection_timeout=0.05,
auto_monitor=False,
)
else:
self.pulse_id = pulse_id_adj
self.running = []
@@ -54,7 +63,24 @@ class Daq(Assembly):
self.name = name
self._default_file_path = None
self.rate_multiplicator = rate_multiplicator
@property
def pgroup(self):
if isinstance(self._pgroup, Adjustable):
return self._pgroup.get_current_value()
else:
return self._pgroup
@pgroup.setter
def pgroup(self, value):
if isinstance(self._pgroup, Adjustable):
self._pgroup.set_target_value(value).wait()
else:
self._pgroup = value
def acquire(self, file_name=None, Npulses=100, acq_pars={}):
print(acq_pars)
print(file_name, Npulses)
acquisition = Acquisition(
acquire=None,
@@ -164,6 +190,7 @@ class Daq(Assembly):
parameters["directory_name"] = directory_relative.as_posix()
parameters["pgroup"] = pgroup
parameters["rate_multiplicator"] = self.rate_multiplicator
# print("----- debug info ----->\n", parameters, "\n<----- debug info -----")
response = validate_response(
requests.post(
@@ -172,10 +199,10 @@ class Daq(Assembly):
timeout=self.timeout,
).json()
)
runno = response['run_number']
filenames = response['files']
runno = response["run_number"]
filenames = response["files"]
# filenames = [
# (directory_base / Path(filename_format.format(runno)))
@@ -186,27 +213,27 @@ class Daq(Assembly):
return runno, filenames
def get_next_run_number(self,pgroup=None):
def get_next_run_number(self, pgroup=None):
if pgroup is None:
pgroup = self.pgroup
res = requests.get(
f"{self.broker_address}/get_next_run_number",
json={'pgroup':pgroup},
json={"pgroup": pgroup},
timeout=self.timeout,
)
assert res.ok, f'Getting last run number failed {res.raise_for_status()}'
return int(res.json()['message'])
)
assert res.ok, f"Getting last run number failed {res.raise_for_status()}"
return int(res.json()["message"])
def get_last_run_number(self,pgroup=None):
def get_last_run_number(self, pgroup=None):
if pgroup is None:
pgroup = self.pgroup
res = requests.get(
f"{self.broker_address}/get_last_run_number",
json={'pgroup':pgroup},
json={"pgroup": pgroup},
timeout=self.timeout,
)
assert res.ok, f'Getting last run number failed {res.raise_for_status()}'
return int(res.json()['message'])
)
assert res.ok, f"Getting last run number failed {res.raise_for_status()}"
return int(res.json()["message"])
def get_detector_frequency(self):
return self._event_master.event_codes[
@@ -243,16 +270,17 @@ class Daq(Assembly):
f"{self.broker_address}/take_pedestal", json=parameters
).json()
def append_aux(self,*file_names,run_number=None,pgroup=None):
def append_aux(self, *file_names, run_number=None, pgroup=None):
if pgroup is None:
pgroup = self.pgroup
if run_number is None:
run_number = self.get_last_run_number()
return requests.post(
self.broker_address_aux+'/copy_user_files',
json={'pgroup': pgroup, 'run_number': run_number, 'files': file_names}
)
self.broker_address_aux + "/copy_user_files",
json={"pgroup": pgroup, "run_number": run_number, "files": file_names},
)
def validate_response(resp):
if resp.get("status") == "ok":
+79 -10
View File
@@ -1,3 +1,4 @@
from numbers import Number
import os
import json
import numpy as np
@@ -51,34 +52,50 @@ class Scan:
Npulses=100,
basepath="",
scan_info_dir="",
settling_time=0,
checker=None,
scan_directories=False,
callbackStartStep=None,
callbacks_start_scan=[],
callbacks_start_step=[],
callbacks_end_step=[],
callbacks_end_scan=[],
checker_sleep_time=2,
return_at_end="question",
run_table=None,
run_number=None,
elog=None,
**kwargs_callbacks,
):
if np.any([char in fina for char in inval_chars]):
raise ScanNameError
self.Nsteps = len(values)
self._run_table = run_table
self.pulses_per_step = Npulses
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.pulses_done = []
self.readbacks = []
self.counterCallers = counterCallers
self.settling_time = settling_time
self.fina = fina
self.nextStep = 0
self.basepath = basepath
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": [ta.name for ta in adjustables],
"name": anames,
"Id": [ta.Id if hasattr(ta, "Id") else "noId" for ta in adjustables],
},
"scan_values_all": values,
@@ -95,7 +112,11 @@ class Scan:
self._checker_sleep_time = checker_sleep_time
self._elog = elog
self.run_number = run_number
self.remaining_tasks = []
self.callbacks_start_step = callbacks_start_step
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()
@@ -104,7 +125,7 @@ class Scan:
if callbacks_start_scan:
for caller in callbacks_start_scan:
caller(self)
caller(self, **self.callbacks_kwargs)
def get_filename(self, stepNo, Ndigits=4):
fina = os.path.join(self.basepath, Path(self.fina).stem)
@@ -136,6 +157,10 @@ 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)
if not len(self.values_todo) > 0:
return False
values_step = self.values_todo[0]
@@ -150,6 +175,10 @@ class Scan:
ms.append(adj.set_target_value(tv))
for tm in ms:
tm.wait()
# settling
sleep(self.settling_time)
readbacks_step = []
adjs_name = []
adjs_offset = []
@@ -185,12 +214,12 @@ class Scan:
"user_tag": self.fina,
}
acq = ctr.acquire(
file_name=fina, Npulses=self.pulses_per_step, acq_pars=acq_pars
file_name=fina, Npulses=self.pulses_per_step[0], acq_pars=acq_pars
)
elif isinstance(ctr, Slab_Ioxos_Daq):
acq = ctr.acquire(file_name=fina, N_pulses=self.pulses_per_step, adjs_rb=readbacks_step, adjs_name=[adj.name for adj in self.adjustables])
else:
acq = ctr.acquire(file_name=fina, Npulses=self.pulses_per_step)
acq = ctr.acquire(file_name=fina, Npulses=self.pulses_per_step[0])
acs.append(acq)
filenames = []
for ta in acs:
@@ -207,12 +236,17 @@ class Scan:
else:
tstepinfo = step_info
self.values_done.append(self.values_todo.pop(0))
self.pulses_done.append(self.pulses_per_step.pop(0))
self.readbacks.append(readbacks_step)
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
def appendScanInfo(
@@ -254,7 +288,7 @@ class Scan:
if self.callbacks_end_scan:
for caller in self.callbacks_end_scan:
caller(self)
caller(self, **self.callbacks_kwargs)
if self.return_at_end == "question":
if input("Change back to initial values? (y/n)")[0] == "y":
chs = self.changeToInitialValues()
@@ -285,12 +319,16 @@ class Scans:
checker=None,
scan_directories=False,
callbacks_start_scan=[],
callbacks_start_step=[],
callbacks_end_step=[],
callbacks_end_scan=[],
run_table=None,
elog=None,
):
self._run_table = run_table
self.callbacks_start_scan = callbacks_start_scan
self.callbacks_start_step = callbacks_start_step
self.callbacks_end_step = callbacks_end_step
self.callbacks_end_scan = callbacks_end_scan
self.data_base_dir = data_base_dir
scan_info_dir = Path(scan_info_dir)
@@ -337,6 +375,7 @@ class Scans:
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)
@@ -354,10 +393,13 @@ class Scans:
checker=self.checker,
scan_directories=self._scan_directories,
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,
return_at_end=return_at_end,
**kwargs_callbacks,
)
if start_immediately:
s.scanAll(step_info=step_info)
@@ -370,10 +412,11 @@ class Scans:
file_name="",
counters=[],
start_immediately=True,
settling_time=0,
step_info=None,
return_at_end=True,
**kwargs_callbacks,
):
adjustable = DummyAdjustable()
positions = list(range(N_repetitions))
@@ -390,14 +433,18 @@ class Scans:
Npulses=N_pulses,
basepath=self.data_base_dir,
scan_info_dir=self.scan_info_dir,
settling_time=settling_time,
checker=self.checker,
scan_directories=self._scan_directories,
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,
return_at_end=return_at_end,
**kwargs_callbacks,
)
if start_immediately:
s.scanAll(step_info=step_info)
@@ -415,6 +462,8 @@ class Scans:
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]
@@ -431,13 +480,17 @@ class Scans:
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.scanAll(step_info=step_info)
@@ -453,8 +506,10 @@ class Scans:
file_name="",
counters=[],
start_immediately=True,
settling_time=0,
step_info=None,
return_at_end="question",
**kwargs_callbacks,
):
positions = np.linspace(start_pos, end_pos, N_intervals + 1)
current = adjustable.get_current_value()
@@ -474,11 +529,15 @@ class Scans:
checker=self.checker,
scan_directories=self._scan_directories,
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,
**kwargs_callbacks,
)
if start_immediately:
s.scanAll(step_info=step_info)
@@ -498,8 +557,10 @@ class Scans:
file_name=None,
counters=[],
start_immediately=True,
settling_time=0,
step_info=None,
return_at_end="question",
**kwargs_callbacks,
):
positions = posList
values = [[tp] for tp in positions]
@@ -517,12 +578,16 @@ class Scans:
scan_info_dir=self.scan_info_dir,
checker=self.checker,
scan_directories=self._scan_directories,
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,
run_table=self._run_table,
elog=self._elog,
run_number=run_number,
**kwargs_callbacks,
)
if start_immediately:
s.scanAll(step_info=step_info)
@@ -543,6 +608,7 @@ class Scans:
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)
@@ -566,9 +632,12 @@ class Scans:
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.scanAll(step_info=step_info)
+12 -10
View File
@@ -30,10 +30,10 @@ class Alias:
def pop_object(self, obj):
i = self.children.index(obj)
o = self.children[i]
o = self.children.pop(i)
o.parent = None
def get_all(self, joiner="."):
def get_all(self, joiner=".", channeltypes=None):
aa = []
if self.channel:
ta = {}
@@ -41,18 +41,20 @@ class Alias:
ta["channel"] = self.channel
if self.channeltype:
ta["channeltype"] = self.channeltype
aa.append(ta)
if (not channeltypes) or (ta["channeltype"] in channeltypes):
aa.append(ta)
if self.children:
for tc in self.children:
taa = tc.get_all()
for ta in taa:
aa.append(
{
"alias": joiner.join([self.alias, ta["alias"]]),
"channel": ta["channel"],
"channeltype": ta["channeltype"],
}
)
if (not channeltypes) or (ta["channeltype"] in channeltypes):
aa.append(
{
"alias": joiner.join([self.alias, ta["alias"]]),
"channel": ta["channel"],
"channeltype": ta["channeltype"],
}
)
return aa
def get_full_name(self, base=None, joiner="."):
+1820 -199
View File
File diff suppressed because it is too large Load Diff
+87
View File
@@ -0,0 +1,87 @@
# new beamline startup
from eco import Assembly
from eco.xoptics.attenuator_aramis import AttenuatorAramis
namespace.append_obj(
"Att_usd",
name="att_usd",
module_name="eco.xoptics.att_usd",
xp=xp,
lazy=True,
)
namespace.append_obj(
"SlitPosWidth",
"SAROP21-OAPU138",
name="slit_att",
lazy=True,
module_name="eco.xoptics.slits",
),
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(
"SlitBlades",
"SAROP21-OAPU102",
name="slit_mono",
module_name="eco.xoptics.slits",
lazy=True,
)
{
"name": "pshut_und",
"type": "eco.xoptics.shutters:PhotonShutter",
"args": ["SARFE10-OPSH044:REQUEST"],
"kwargs": {},
"z_und": 44,
"desc": "First shutter after Undulators",
},
{
"name": "xp",
"args": [],
"kwargs": {
"Id": "SAROP21-OPPI113",
"evronoff": "SGE-CPCW-72-EVR0:FrontUnivOut15-Ena-SP",
"evrsrc": "SGE-CPCW-72-EVR0:FrontUnivOut15-Src-SP",
},
"z_und": 103,
"desc": "X-ray pulse picker",
"type": "eco.xoptics.pp:Pulsepick",
},
{
"name": "att_fe",
"type": "eco.xoptics.attenuator_aramis:AttenuatorAramis",
"args": ["SARFE10-OATT053"],
"kwargs": {"shutter": Component("pshut_und")},
"z_und": 53,
"desc": "Attenuator in Front End",
},
class AttenuationFELBernina(Assembly):
def __init__(self, name=None):
super().__init__(name=name)
self._append(
AttenuatorAramis, "SAROP21-OATT135", set_limits=[], name="opt", shutter=None
)
self._append(
AttenuatorAramis, "SARFE10-OATT053", set_limits=[], name="fe", shutter=None
)
+103 -225
View File
@@ -32,27 +32,27 @@ components = [
"kwargs": {},
"lazy": True,
},
{
"name": "elog",
"type": "eco.utilities.elog:Elog",
"args": ["https://elog-gfa.psi.ch/Bernina"],
"kwargs": {
"screenshot_directory": "/tmp",
},
},
{
"name": "screenshot",
"type": "eco.utilities.elog:Screenshot",
"args": [],
"kwargs": {"screenshot_directory": "/sf/bernina/config/screenshots"},
},
{
"name": "fel",
"type": "eco.fel.swissfel:SwissFel",
"args": [],
"kwargs": {},
"desc": "Fel related control and feedback",
},
# {
# "name": "screenshot",
# "type": "eco.utilities.elog:Screenshot",
# "args": [],
# "kwargs": {"screenshot_directory": "/sf/bernina/config/screenshots"},
# },
# {
# "name": "fel",
# "type": "eco.fel.swissfel:SwissFel",
# "args": [],
# "kwargs": {},
# "desc": "Fel related control and feedback",
# },
# {
# "name": "mono",
# "args": ["SAROP21-ODCM098"],
# "kwargs": {},
# "z_und": 98,
# "desc": "DCM Monochromator",
# "type": "eco.xoptics.dcm_new:DoubleCrystalMono",
# },
# {
# "name": "slit_und",
# "type": "eco.xoptics.slits:SlitFourBlades_old",
@@ -75,28 +75,28 @@ components = [
# "type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS",
# "kwargs": {"VME_crate": "SAROP21-CVME-PBPS2", "link": 9},
# },
{
"name": "mon_und",
"z_und": 53,
"desc": "Intensity/position monitor after Undulator",
"type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS_new",
"args": ["SARFE10-PBPS053"],
"kwargs": {
"VME_crate": "SAROP21-CVME-PBPS1",
"link": 9,
"channels": {
"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",
},
"calc": {
"itot": "SLAAR21-LTIM01-EVR0:CALCI",
"xpos": "SLAAR21-LTIM01-EVR0:CALCX",
"ypos": "SLAAR21-LTIM01-EVR0:CALCY",
},
},
},
# {
# "name": "mon_und",
# "z_und": 53,
# "desc": "Intensity/position monitor after Undulator",
# "type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS_new",
# "args": ["SARFE10-PBPS053"],
# "kwargs": {
# "VME_crate": "SAROP21-CVME-PBPS1",
# "link": 9,
# "channels": {
# "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",
# },
# "calc": {
# "itot": "SLAAR21-LTIM01-EVR0:CALCI",
# "xpos": "SLAAR21-LTIM01-EVR0:CALCX",
# "ypos": "SLAAR21-LTIM01-EVR0:CALCY",
# },
# },
# },
{
"name": "pshut_und",
"type": "eco.xoptics.shutters:PhotonShutter",
@@ -145,14 +145,14 @@ components = [
# "args": ["SARFE10-PBPS053"],
# "kwargs": {},
# },
{
"name": "xspect",
"z_und": 53,
"desc": "X-ray single shot spectrometer",
"type": "eco.xdiagnostics.xspect:Xspect",
"args": [],
"kwargs": {},
},
# {
# "name": "xspect",
# "z_und": 53,
# "desc": "X-ray single shot spectrometer",
# "type": "eco.xdiagnostics.xspect:Xspect",
# "args": [],
# "kwargs": {},
# },
{
"name": "prof_fe",
"args": ["SARFE10-PPRM064"] * 2,
@@ -186,25 +186,17 @@ components = [
"type": "eco.xoptics.offsetMirrors_new:OffsetMirrorsBernina",
"kwargs": {},
},
{
"name": "mono",
"args": ["SAROP21-ODCM098"],
"kwargs": {},
"z_und": 98,
"desc": "DCM Monochromator",
"type": "eco.xoptics.dcm_new:DoubleCrystalMono",
},
{
"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",
},
# {
# "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",
# },
{
"name": "prof_mono",
"args": ["SAROP21-PPRM113"] * 2,
@@ -233,28 +225,6 @@ components = [
# "args": ["SAROP21-PBPS133"],
# "kwargs": {"VME_crate": "SAROP21-CVME-PBPS1", "link": 9},
# },
{
"name": "mon_opt",
"z_und": 133,
"desc": "Intensity/position monitor after Optics hutch",
"type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS_new",
"args": ["SAROP21-PBPS133"],
"kwargs": {
"VME_crate": "SAROP21-CVME-PBPS1",
"link": 9,
"channels": {
"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",
},
"calc": {
"itot": "SLAAR21-LTIM01-EVR0:CALCI",
"xpos": "SLAAR21-LTIM01-EVR0:CALCX",
"ypos": "SLAAR21-LTIM01-EVR0:CALCY",
},
},
},
{
"name": "prof_opt",
"args": ["SAROP21-PPRM133"] * 2,
@@ -279,22 +249,22 @@ components = [
"desc": "Attenuator Bernina",
"type": "eco.xoptics.attenuator_aramis:AttenuatorAramis",
},
{
"name": "slit_att",
"args": ["SAROP21-OAPU136"],
"kwargs": {},
"z_und": 136,
"desc": "Slits behind attenuator",
"type": "eco.xoptics.slits:SlitPosWidth",
},
{
"name": "det_dio",
"args": ["SAROP21-PDIO138"],
"z_und": 138,
"desc": "Diode digitizer for exp data",
"type": "eco.devices_general.detectors:DiodeDigitizer",
"kwargs": {"VME_crate": "SAROP21-CVME-PBPS2", "link": 9},
},
# {
# "name": "slit_att",
# "args": ["SAROP21-OAPU136"],
# "kwargs": {},
# "z_und": 136,
# "desc": "Slits behind attenuator",
# "type": "eco.xoptics.slits:SlitPosWidth",
# },
# {
# "name": "det_dio",
# "args": ["SAROP21-PDIO138"],
# "z_und": 138,
# "desc": "Diode digitizer for exp data",
# "type": "eco.devices_general.detectors:DiodeDigitizer",
# "kwargs": {"VME_crate": "SAROP21-CVME-PBPS2", "link": 9},
# },
{
"name": "prof_att",
"args": ["SAROP21-PPRM138"] * 2,
@@ -354,17 +324,6 @@ components = [
# "diff_detector": {"jf_id": "JF01T03V01"},
# },
# },
{
"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"],
},
},
# {
# "args": [],
# "name": "gasjet",
@@ -386,22 +345,6 @@ components = [
"bsport": 11151,
},
},
{
"args": ["SARES20-CAMS142-M1"],
"name": "cam_sample_sideview",
"z_und": 142,
"desc": "",
"type": "eco.devices_general.cameras_swissfel:CameraBasler",
"kwargs": {},
},
{
"args": ["SARES20-CAMS142-M3"],
"name": "cam_sample_inline",
"z_und": 142,
"desc": "",
"type": "eco.devices_general.cameras_swissfel:CameraBasler",
"kwargs": {},
},
# {
# "args": ["SARES20-CAMS142-C3"],
# "name": "cam_sample_xrd",
@@ -423,18 +366,6 @@ components = [
# "camera_pv": config["cams_qioptiq"]["camera_pv"],
# },
# },
{
"args": [],
"name": "cams_sigma",
"z_und": 142,
"desc": "Sigma objective",
"type": "eco.endstations.bernina_cameras:Sigma",
"kwargs": {
"bshost": "sf-daqsync-01.psi.ch",
"bsport": 11149,
"camera_pv": config["cams_sigma"]["camera_pv"],
},
},
# {
# "args": ["SLAAR02-TSPL-EPL"],
# "name": "phase_shifter",
@@ -451,28 +382,6 @@ components = [
"type": "eco.loptics.laser_shutter:laser_shutter",
"kwargs": {},
},
{
"args": [],
"name": "epics_channel_list",
"desc": "epics channel list",
"type": "eco.utilities.config:ChannelList",
"kwargs": {
"file_name": "/sf/bernina/config/channel_lists/default_channel_list_epics"
},
"lazy": True,
},
{
"args": [],
"name": "epics_daq",
"z_und": 142,
"desc": "epics data acquisition",
"type": "eco.acquisition.epics_data:Epicstools",
"kwargs": {
"channel_list": Component("epics_channel_list"),
"default_file_path": f"/sf/bernina/data/{config['pgroup']}/res/epics_daq/",
},
"lazy": True,
},
# {
# "args": [],
# "name": "daq_dia_old",
@@ -489,17 +398,17 @@ components = [
# "default_file_path": None,
# },
# },
{
"args": [
config["checker_PV"],
config["checker_thresholds"],
config["checker_fractionInThreshold"],
], #'SARFE10-PBPG050:HAMP-INTENSITY-CAL',[60,700],.7],
"name": "checker",
"desc": "checker functions for data acquisition",
"type": "eco.acquisition.checkers:CheckerCA",
"kwargs": {},
},
# {
# "args": [
# config["checker_PV"],
# config["checker_thresholds"],
# config["checker_fractionInThreshold"],
# ], #'SARFE10-PBPG050:HAMP-INTENSITY-CAL',[60,700],.7],
# "name": "checker",
# "desc": "checker functions for data acquisition",
# "type": "eco.acquisition.checkers:CheckerCA",
# "kwargs": {},
# },
# {
# "args": [
# "SARES20-LSCP9-FNS:CH1:VAL_GET",
@@ -513,21 +422,6 @@ components = [
# },
# {
# "args": [],
# "name": "scans_epics",
# "desc": "epics non beam synchronous based acquisition",
# "type": "eco.acquisition.scan:Scans",
# "kwargs": {
# "data_base_dir": "scan_data",
# "scan_info_dir": f"/sf/bernina/data/{config['pgroup']}/res/scan_info",
# "default_counters": [Component("epics_daq")],
# "checker": Component("checker_epics"),
# "scan_directories": True,
# "run_table": Component("run_table"),
# },
# "lazy": True,
# },
# {
# "args": [],
# "name": "lxt",
# "desc": "laser timing with pockels cells and phase shifter",
# "type": "eco.timing.lasertiming:Lxt",
@@ -579,14 +473,6 @@ components = [
"default_file_path": f"/sf/bernina/data/{config['pgroup']}/res/%s",
},
},
{
"args": [],
"name": "usd_table",
"z_und": 141,
"desc": "Upstream diagnostics table",
"type": "eco.endstations.hexapod:HexapodSymmetrie",
"kwargs": {"offset": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]},
},
# {
# "args": ["SARES23-"],
# "name": "slit_kb",
@@ -603,20 +489,20 @@ components = [
# "type": "eco.xoptics.slit_USD:Upstream_diagnostic_slits",
# "kwargs": {"right": "LIC7", "left": "LIC8", "up": "LIC8", "down": "LIC5"},
# },
{
"args": [
[
Component("slit_und"),
Component("slit_switch"),
Component("slit_att"),
Component("slit_kb"),
]
],
"name": "slits",
"desc": "collection of all slits",
"type": "eco.utilities.beamline:Slits",
"kwargs": {},
},
# {
# "args": [
# [
# Component("slit_und"),
# Component("slit_switch"),
# Component("slit_att"),
# Component("slit_kb"),
# ]
# ],
# "name": "slits",
# "desc": "collection of all slits",
# "type": "eco.utilities.beamline:Slits",
# "kwargs": {},
# },
# {
# "args": [
# [Component("slit_switch"), Component("slit_att"), Component("slit_kb"),]
@@ -659,14 +545,6 @@ components = [
# },
# },
# },
{
"args": [],
"name": "dsd",
"z_und": 146,
"desc": "downstream diagnostics",
"type": "eco.xdiagnostics.dsd:DownstreamDiagnostic",
"kwargs": {},
},
]
try:
+19
View File
@@ -0,0 +1,19 @@
import subprocess, os
def get_strip_chart_function():
return strip_chart
def strip_chart(*args, **kwargs):
"""Usage: Arguments represent channels in the strip_chart config command line argument.
Alternatively arguments can be detectors or adjustables, from which _all_ channels are determined
"""
channels = list(args)
cmd = ["strip_chart"]
cmd += ['-config="' + str(channels) + '"']
cmd += ["-start"]
line = " ".join(cmd)
print(f"Starting following commandline silently:\n" + line)
with open(os.devnull, "w") as FNULL:
subprocess.Popen(line, shell=True, stdout=FNULL, stderr=subprocess.STDOUT)
+116
View File
@@ -5,6 +5,9 @@ from epics.pv import PV
from bsread.bsavail import pollStream
from bsread import dispatcher, source
from ..epics import get_from_archive
from escape import stream
from time import time, sleep
from eco.acquisition.utilities import Acquisition
@get_from_archive
@@ -20,8 +23,18 @@ class DetectorBsStream:
self._pv = PV(self.pvname)
self.alias = Alias(name, channel=bs_channel, channeltype="BS")
self.stream = stream.EscData(source=stream.EventSource(self.bs_channel, None))
def bs_avail(self):
return self.bs_channel in [
tmp["name"] for tmp in dispatcher.get_current_channels()
]
def get_current_value(self, force_bsstream=False):
if not force_bsstream:
if not hasattr(self, "_pv"):
return None
return self._pv.get()
else:
raise NotImplementedError(
@@ -37,6 +50,109 @@ class DetectorBsStream:
while not done:
done = foo(s.receive())
def collect(self, seconds=None, samples=None):
if (not seconds) and (not samples):
raise Exception(
"Either a time interval or number of samples need to be defined."
)
try:
self._pv.callbacks.pop(self._collection["ix_cb"])
except:
pass
self._collection = {"done": False}
self.data_collected = []
if seconds:
self._collection["start_time"] = time()
self._collection["seconds"] = seconds
stopcond = (
lambda: (time() - self._collection["start_time"])
> self._collection["seconds"]
)
def addData(**kw):
if not stopcond():
self.data_collected.append(kw["value"])
else:
self._pv.callbacks.pop(self._collection["ix_cb"])
self._collection["done"] = True
elif samples:
self._collection["samples"] = samples
stopcond = lambda: len(self.data_collected) >= self._collection["samples"]
def addData(**kw):
self.data_collected.append(kw["value"])
if stopcond():
self._pv.callbacks.pop(self._collection["ix_cb"])
self._collection["done"] = True
self._collection["ix_cb"] = self._pv.add_callback(addData)
time_wait_start = time()
while not self._collection["done"]:
sleep(0.005)
if seconds:
if (time() - time_wait_start) > seconds:
if len(self.data_collected) == 0:
print(
f"No {self.name}({self.Id}) data update in time interval, reporting last value"
)
self._pv.callbacks.pop(self._collection["ix_cb"])
self.data_collected.append(self.get_current_value())
break
return self.data_collected
def acquire(self, hold=False, seconds=None, samples=None, **kwargs):
return Acquisition(
acquire=lambda: self.collect(seconds=seconds, samples=samples, **kwargs),
hold=hold,
stopper=None,
get_result=lambda: self.data_collected,
)
def accumulate_ring_buffer(self, n_buffer):
if not hasattr(self, "_accumulate"):
self._accumulate = {"n_buffer": n_buffer, "ix": 0, "n_cb": -1}
else:
self._accumulate["n_buffer"] = n_buffer
self._accumulate["ix"] = 0
self._pv.callbacks.pop(self._accumulate["n_cb"], None)
self._data = np.squeeze(np.zeros([n_buffer * 2, self._pv.count])) * np.nan
def addData(**kw):
self._accumulate["ix"] = (self._accumulate["ix"] + 1) % self._accumulate[
"n_buffer"
]
self._data[self._accumulate["ix"] :: self._accumulate["n_buffer"]] = kw[
"value"
]
self._accumulate["n_cb"] = self._pv.add_callback(addData)
def accumulate_start(self):
if not hasattr(self, "_accumulate_inf"):
self._accumulate_inf = {"n_cb": -1}
self._pv.callbacks.pop(self._accumulate_inf["n_cb"], None)
self._data_inf = []
def addData(**kw):
self._data_inf.append(kw["value"])
self._accumulate_inf["n_cb"] = self._pv.add_callback(addData)
def accumulate_stop(self):
self._pv.callbacks.pop(self._accumulate_inf["n_cb"], None)
return self._data_inf
@property
def data(self):
return self._data[
self._accumulate["ix"]
+ 1 : self._accumulate["ix"]
+ 1
+ self._accumulate["n_buffer"]
]
@get_from_archive
class DetectorBsCam:
+336 -5
View File
@@ -1,11 +1,15 @@
import shutil
from tkinter import W
from ..elements.adjustable import AdjustableVirtual, AdjustableGetSet
from eco.base.adjustable import Adjustable
from ..elements.adjustable import AdjustableFS, AdjustableVirtual, AdjustableGetSet
from ..epics.adjustable import AdjustablePv
from ..elements.assembly import Assembly
from ..aliases import Alias
from pathlib import Path
from ..elements import memory
from datetime import datetime
import requests
class Jungfrau(Assembly):
@@ -15,12 +19,16 @@ class Jungfrau(Assembly):
pv_trigger="SAR-CVME-TIFALL5-EVG0:SoftEvt-EvtCode-SP",
trigger_on=254,
trigger_off=255,
broker_address="http://sf-daq:10002",
pgroup_adj=None,
config_adj=None,
name=None,
):
super().__init__(name=name)
self.alias = Alias(name, channel=jf_id, channeltype="JF")
self.pgroup = pgroup_adj
self.jf_id = jf_id
self.broker_address = broker_address
self._append(
AdjustablePv,
pv_trigger,
@@ -57,6 +65,34 @@ class Jungfrau(Assembly):
name="gain_file",
is_display=True,
)
self._append(
AdjustableGetSet,
self.get_present_pedestal_filename_in_run,
lambda value: NotImplementedError(
"Can not set the pedestal file manually yet."
),
name="pedestal_file_in_run",
is_display=True,
)
self._append(
AdjustableGetSet,
self.get_present_gain_filename_in_run,
lambda value: NotImplementedError(
"Can not set the pedestal file manually yet."
),
name="gain_file_in_run",
is_display=True,
)
if config_adj:
self._append(
JungfrauDaqConfig,
jf_id,
config_adj,
name="config_daq",
is_setting=True,
is_status=True,
is_display="recursive",
)
def _set_trigger_enable(self, value):
if value:
@@ -68,12 +104,307 @@ class Jungfrau(Assembly):
filepath = Path(f"/sf/jungfrau/config/gainMaps/{self.jf_id}/gains.h5")
if filepath.exists():
return filepath.resolve().as_posix()
return filepath.as_posix()
else:
raise Exception(f"File {filepath.resolve().as_posix()} seems not to exist!")
raise Exception(f"File {filepath.as_posix()} seems not to exist!")
def get_present_gain_filename_in_run(self, intempdir=False):
f = Path(self.get_present_gain_filename())
dest = Path(
f"/sf/bernina/data/{self.pgroup()}/res/tmp/gainmaps_{self.jf_id}.h5"
)
if not dest.exists():
shutil.copyfile(f, dest)
if intempdir:
return dest.as_posix()
else:
return f"aux/{dest.name}"
def get_present_pedestal_filename(self):
searchpath = Path(f"/sf/jungfrau/data/pedestal/{self.jf_id}")
filelist = list(searchpath.glob("*.h5"))
times = [datetime.strptime(f.stem, "%Y%m%d_%H%M%S") for f in filelist]
return filelist[times.index(max(times))].resolve().as_posix()
return filelist[times.index(max(times))].as_posix()
def get_present_pedestal_filename_in_run(self, intempdir=False):
f = Path(self.get_present_pedestal_filename())
dest = Path(
f"/sf/bernina/data/{self.pgroup()}/res/tmp/pedestal_{self.jf_id}_{f.stem}.h5"
)
if not dest.exists():
shutil.copyfile(f, dest)
if intempdir:
return dest.as_posix()
else:
return f"aux/{dest.name}"
def get_detector_frequency(self):
return self._event_master.event_codes[
self._detectors_event_code
].frequency.get_current_value()
def get_availability(self):
is_available = (
self.jf_id
in requests.get(f"{self.broker_address}/get_allowed_detectors_list").json()[
"detectors"
]
)
return is_available
def get_vis_url(self):
tmp = requests.get(f"{self.broker_address}/get_allowed_detectors_list").json()
ix = tmp["detectors"].index(self.jf_id)
return tmp["visualisation_address"][ix]
def get_isrunning(self):
is_running = (
self.jf_id
in requests.get(f"{self.broker_address}/get_running_detectors_list").json()[
"detectors"
]
)
return is_running
def power_on(self):
JF_channel = self.jf_id
par = {"detector_name": JF_channel}
return requests.post(
f"{self.broker_address}/power_on_detector", json=par
).json()
# def take_pedestal(self, JF_list=None, pgroup=None):
# if pgroup is None:
# pgroup = self.pgroup
# if not JF_list:
# JF_list = self.get_JFs_running()
# parameters = {
# "pgroup": pgroup,
# "rate_multiplicator": 1,
# "detectors": {tJF: {} for tJF in JF_list},
# }
# return requests.post(
# f"{self.broker_address}/take_pedestal", json=parameters
# ).json()
class JungfrauDaqConfig(Assembly):
def __init__(self, jf_id, jf_daq_cfg: Adjustable, name=None):
super().__init__(name=name)
self._jf_id = jf_id
self._jf_daq_cfg = jf_daq_cfg
cfg = self._jf_daq_cfg.get_current_value()
if self._jf_id not in cfg.keys():
cfg[self._jf_id] = {}
self._jf_daq_cfg.set_target_value(cfg).wait()
self._append(
AdjustableGetSet,
self._get_adc_to_energy,
self._set_adc_to_energy,
name="convert_adc_to_energy",
is_display=True,
is_setting=True,
)
self._append(
AdjustableGetSet,
self._get_geometry_corr,
self._set_geometry_corr,
name="apply_tile_geometry",
is_display=True,
is_setting=True,
)
self._append(
AdjustableGetSet,
self._get_compressed_bitshuffle,
self._set_compressed_bitshuffle,
name="compress_bitshuffle",
is_display=True,
is_setting=True,
)
self._append(
AdjustableGetSet,
self._get_keep_raw_data,
self._set_keep_raw_data,
name="keep_raw_data",
is_display=True,
is_setting=True,
)
self._append(
AdjustableGetSet,
self._get_large_pixel_processing,
self._set_large_pixel_processing,
name="large_pixel_processing",
is_display=True,
is_setting=True,
)
self._append(
AdjustableGetSet,
self._get_rounding_factor,
self._set_rounding_factor,
name="rounding_factor_keV",
is_display=True,
is_setting=True,
)
self._append(
AdjustableGetSet,
self._get_disabled_modules,
self._set_disabled_modules,
name="disabled_tiles",
is_display=True,
is_setting=True,
)
def _get_adc_to_energy(self, *args):
try:
return self._jf_daq_cfg.get_current_value()[self._jf_id]["adc_to_energy"]
except KeyError:
return False
def _set_adc_to_energy(self, value):
if value:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["adc_to_energy"] = True
self._jf_daq_cfg.set_target_value(cfg).wait()
else:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["adc_to_energy"] = False
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_geometry_corr(self, *args):
try:
return self._jf_daq_cfg.get_current_value()[self._jf_id]["geometry"]
except KeyError:
return "not sure what happens"
def _set_geometry_corr(self, value):
if value:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["geometry"] = True
self._jf_daq_cfg.set_target_value(cfg).wait()
else:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["adc_to_energy"] = False
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_compressed_bitshuffle(self, *args):
try:
return self._jf_daq_cfg.get_current_value()[self._jf_id]["compression"]
except KeyError:
return False
def _set_compressed_bitshuffle(self, value):
if value:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["compression"] = True
self._jf_daq_cfg.set_target_value(cfg).wait()
else:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["compression"] = False
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_keep_raw_data(self, *args):
try:
return not self._jf_daq_cfg.get_current_value()[self._jf_id][
"remove_raw_files"
]
except KeyError:
# raise Exception("unclear what the default for keeping raw files is!")
return None
def _set_keep_raw_data(self, value):
if value:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["remove_raw_files"] = False
self._jf_daq_cfg.set_target_value(cfg).wait()
else:
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["remove_raw_files"] = True
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_large_pixel_processing(self, *args):
try:
return self._jf_daq_cfg.get_current_value()[self._jf_id][
"double_pixels_action"
]
except KeyError:
# raise Exception("unclear what the default for double pixels is!")
return None
def _set_large_pixel_processing(self, value):
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["double_pixels_action"] = value
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_rounding_factor(self, *args):
try:
return self._jf_daq_cfg.get_current_value()[self._jf_id]["factor"]
except KeyError:
# raise Exception("unclear what the default for double pixels is!")
return None
def _set_rounding_factor(self, value):
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["factor"] = value
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_disabled_modules(self, *args):
try:
return self._jf_daq_cfg.get_current_value()[self._jf_id]["disabled_modules"]
except KeyError:
return []
def _set_disabled_modules(self, value):
cfg = self._jf_daq_cfg.get_current_value()
if value == []:
cfg[self._jf_id].pop("disabled_modules")
else:
cfg[self._jf_id]["disabled_modules"] = value
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_binning(self, *args):
try:
return self._jf_daq_cfg.get_current_value()[self._jf_id]["downsample"]
except KeyError:
return [1, 1]
def _set_binning(self, value):
cfg = self._jf_daq_cfg.get_current_value()
if value == [1, 1]:
cfg[self._jf_id].pop("downsample")
else:
cfg[self._jf_id]["downsample"] = value
self._jf_daq_cfg.set_target_value(cfg).wait()
def _get_keepraw(self, *args):
try:
remove_raw = self._jf_daq_cfg.get_current_value()[self._jf_id][
"remove_raw_files"
]
# if type(remove_raw) is bool:
return remove_raw
except KeyError:
return "not sure what happens"
def _set_keepraw(self, value):
cfg = self._jf_daq_cfg.get_current_value()
cfg[self._jf_id]["remove_raw_files"] = value
self._jf_daq_cfg.set_target_value(cfg).wait()
# {
# "adc_to_energy": true,
# "compression": true,
# "double_pixels_actions": "interpolate",
# "downsample": [
# 1,
# 1
# ],
# "factor": 0.25,x
# "geometry": true,
# "remove_raw_files": false
# "disabled_modules": [],
# },
+185 -6
View File
@@ -1,14 +1,18 @@
from cam_server import CamClient, PipelineClient
from matplotlib.backend_bases import MouseButton
from eco.devices_general.utilities import Changer
from ..aliases import Alias, append_object_to_object
from ..elements.adjustable import AdjustableVirtual, AdjustableGetSet, value_property
from eco.elements.detector import DetectorGet
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
from eco.elements.adj_obj import AdjustableObject, DetectorObject
from .pipelines_swissfel import Pipeline
from ..elements.assembly import Assembly
from .motors import MotorRecord
import sys
from pathlib import Path
import time
import matplotlib.pyplot as plt
sys.path.append("/sf/bernina/config/src/python/sf_databuffer/")
import bufferutils
@@ -30,6 +34,120 @@ def get_pipelineclient():
PIPELINE_CLIENT = PipelineClient()
return PIPELINE_CLIENT
@value_property
class CamserverConfig2(Assembly):
def __init__(self, cam_id, camserver_alias=None, name=None, camserver_group=None):
super().__init__(name=name)
self.cam_id = cam_id
self.camserver_alias = camserver_alias
self.camserver_group = camserver_group
self._cross = None
self._append(AdjustableGetSet,
self._get_config,
self._set_config,
cache_get_seconds =.05,
precision=0,
check_interval=None,
name='_config',
is_setting=False,
is_display=False)
self._append(AdjustableObject, self._config, name='config',is_setting=True, is_display='recursive')
self._append(DetectorGet, self._get_info, cache_get_seconds =.05, name='_info', is_setting=False, is_display=False)
self._append(DetectorObject, self._info, name='info', is_display='recursive', is_setting=False)
@property
def pc(self):
return get_pipelineclient()
@property
def cc(self):
return get_camclient()
def _get_config(self):
return self.cc.get_camera_config(self.cam_id)
def _set_config(self, value, hold=False):
return Changer(
target=value,
changer=lambda v: self.cc.set_camera_config(self.cam_id, v),
hold=hold,
)
def _get_info(self):
fields = {
"camera_geometry": self.cc.get_camera_geometry(self.cam_id),
"pipelines": self._get_pipelines(),
}
return fields
### convenience functions ###
def get_camera_image(self):
im = self.cc.get_camera_array(self.cam_id)
return im
def set_alias(self, alias=None):
"""creates an alias in the camera config on the server. If no alias is provided, it defaults to the camera name"""
if not alias:
alias = self.camserver_alias
self.set_config_fields({"alias": [alias.upper()]})
def set_group(self, group=None):
"""adds the camera to the given group"""
if not group:
group = self.camserver_group
self.config.group(group)
def _get_pipelines(self):
return [p for p in self.pc.get_pipelines() if self.cam_id in p]
def set_config_fields(self, fields):
"""fields is a dictionary containing the keys and values that should be updated, e.g. fields={'group': ['Laser', 'Bernina']}"""
config = self.cc.get_camera_config(self.cam_id)
config.update(fields)
self.cc.set_camera_config(self.cam_id, config)
def set_config_fields_multiple_cams(self, conditions, fields):
"""
conditions is a dictionary holding the conditions to select a subset of cameras, e.g. {"group": Bernina}
fields is a dictionary containing the keys and values that should be updated, e.g. fields={'alias': ['huhu', 'duda']}
"""
cams = {
cam: self.cc.get_camera_config(cam)
for cam in self.cc.get_cameras()
if not "jungfrau" in cam
}
cams_selected = {}
for cam, cfg in cams.items():
try:
if all([value in cfg[key] for key, value in conditions.items()]):
cfg.update(fields)
self.cc.set_camera_config(cam, cfg)
cams_selected[cam] = cfg
except Exception as e:
print(f"{type(e)} {e} in cam {cam}")
return cams_selected
def clear_all_bernina_aliases(self, verbose=True):
cams_selected = self.set_config_fields_multiple_cams(
conditions={"group": "Bernina"}, fields={"alias": []}
)
if verbose:
print(f"Reset alias of {len(cams_selected)} cameras")
print(cams_selected.keys())
def _run_cmd(self, line, silent=True):
if silent:
print(f"Starting following commandline silently:\n" + line)
with open(os.devnull, "w") as FNULL:
subprocess.Popen(
line, shell=True, stdout=FNULL, stderr=subprocess.STDOUT
)
else:
subprocess.Popen(line, shell=True)
def gui(self):
self._run_cmd(f'csm')
@value_property
class CamserverConfig(Assembly):
@@ -50,7 +168,7 @@ class CamserverConfig(Assembly):
def get_current_value(self):
return self.cc.get_camera_config(self.cam_id)
def set_target_calue(self, value, hold=False):
def set_target_value(self, value, hold=False):
return Changer(
target=value,
changer=lambda v: self.cc.set_camera_config(self.cam_id, v),
@@ -165,13 +283,14 @@ class CameraBasler(Assembly):
if not camserver_alias:
camserver_alias = self.alias.get_full_name() + f" ({pvname})"
self._append(
CamserverConfig,
CamserverConfig2,
self.pvname,
camserver_alias=camserver_alias,
camserver_group=camserver_group,
name="config_cs",
is_display=False,
)
self.config_cs.set_alias()
if camserver_group is not None:
self.config_cs.set_group()
@@ -233,7 +352,7 @@ class CameraBasler(Assembly):
)
self._append(
AdjustablePv,
self.pvname + ":BINY",
self.pvname + ":BINX",
name="_binx",
is_setting=True,
is_display=False,
@@ -338,11 +457,71 @@ class CameraBasler(Assembly):
)
def _set_params(self, *args):
self.running(0)
self.running(1)
for ob, val in args:
ob(val)
self._set_parameters(1)
self.running(1)
self.running(2)
def set_cross(self, x=None, y=None, x_um_per_px=None, y_um_per_px=None):
"""set x and y position of the refetence marker on a camera px/um calibration is conserved if no new value is given"""
def prompt(x,y,x_um_per_px,y_um_per_px):
x=int(x)
y=int(y)
answer = input(f"Set the new cross position [{x}, {y}] with calibration [{x_um_per_px:.3}, {y_um_per_px:.3}] ([y]/n)?") or "y"
if answer == "y":
calib.reference_marker([x - 1, y - 1, x + 1, y + 1])
calib.reference_marker_width(2 * x_um_per_px)
calib.reference_marker_height(2 * y_um_per_px)
print("\nNew calibration:")
print(calib)
else:
print("aborted")
calib = self.config_cs.config.camera_calibration
print("Current calibration:")
print(calib)
try:
w = calib.reference_marker_width()
h = calib.reference_marker_height()
rm = calib.reference_marker()
if not x_um_per_px:
x_um_per_px = w / abs(rm[2] - rm[0])
if not y_um_per_px:
y_um_per_px = h / abs(rm[3] - rm[1])
except:
rm=[0,0,0,0]
x_um_per_px = 1
y_um_per_px = 1
if x is None or y is None:
x = (rm[2] + rm[0])/2
y = (rm[3] + rm[1])/2
img = self.config_cs.get_camera_image()
run = True
def on_click(event):
if event.button is MouseButton.LEFT:
x = event.xdata
y = event.ydata
cross_plot.set_data(x,y)
plt.draw()
print(f'cross at x: {x:.4} and y: {y:.4}')
self.config_cs._cross = [x,y]
else:
plt.disconnect(bid)
plt.close(self.config_cs.cam_id)
fig = plt.figure(num=self.config_cs.cam_id)
plt.title(f"Set cross: left mouse click, Finish: right click")
plt.imshow(img)
cross_plot = plt.plot(x,y, '+r', markersize=10)[0]
bid = fig.canvas.mpl_connect('button_press_event', on_click)
plt.show(block=True)
x, y = self.config_cs._cross
print(x,y)
prompt(x,y,x_um_per_px,y_um_per_px)
def gui(self):
self._run_cmd(
+67
View File
@@ -0,0 +1,67 @@
from eco import Assembly
from eco.epics.adjustable import AdjustablePv, AdjustablePvEnum, AdjustablePvString
from eco.epics.detector import DetectorPvData, DetectorPvEnum
from epics import PV
class I2cChannel(Assembly):
def __init__(self, pvbase, channelnumber, name=None):
super().__init__(name=name)
self.pvbase = pvbase
self.channel_number = channelnumber
self._append(
AdjustablePvString,
f'{self.pvbase}_CH{self.channel_number}:PROCESS.DESC',
name="description",
is_setting=True,
)
self._append(DetectorPvData,f'{self.pvbase}_CH{self.channel_number}:TEMP', has_unit=True, name='temp')
self._append(DetectorPvData,f'{self.pvbase}_CH{self.channel_number}:HUMIREL', has_unit=True, name = 'humi')
self._append(DetectorPvData,f'{self.pvbase}_CH{self.channel_number}:PRES', has_unit=True, name='pres')
self._append(AdjustablePv,f'{self.pvbase}_CH{self.channel_number}:ONOFF', name='enabled', is_setting=True)
self._append(AdjustablePvEnum,f'{self.pvbase}_CH{self.channel_number}:SENSOR_TYPE', name='sensor_type', is_setting=True)
self._pv_init = PV(self.pvbase+':INIT.PROC')
def get_current_value(self,*args,**kwargs):
return f'{self.temp.get_current_value(*args,**kwargs):.2f}°C , {self.humi.get_current_value(*args,**kwargs):.2f}%relHum, {self.pres.get_current_value(*args,**kwargs):.2f} mB'
# def set_target_value(self,*args,**kwargs):
# return self.enabled.set_target_value(*args,**kwargs)
def initialize(self):
self._pv_init.put(1)
class I2cModule(Assembly):
def __init__(self,pvbase='SARES20-CI2C',N_channels=8, name=None):
super().__init__(name=name)
for n in range(1,N_channels+1):
self._append(I2cChannel,pvbase,channelnumber=n,name=f'ch{n}')
class BerninaEnvironment(Assembly):
def __init__(self,pvbases=['SLAAR21-LI2C01', 'SARES20-CI2C'],channels=[[1,2,3,4,5,6,7,8],[4,5,6,7,8]], channelnames = [['las_tab1_in', 'las_tab1_cen', 'las_tab1_out', 'las_tab2_in', 'las_tab2_cen', 'las_tab2_out', 'las_tab2_below', 'tt_spec'], ['tt_opt', 'tt_kb', 'exp1', 'exp2', 'exp3']], name=None):
super().__init__(name=name)
for pvbase,channelnumbers,tnames in zip(pvbases,channels,channelnames):
for n,tname in zip(channelnumbers,tnames):
self._append(I2cChannel,pvbase,channelnumber=n,name=tname)
class WagoSensor(Assembly):
def __init__(self,pvbase='SARES20-CWAG-GPS01:TEMP-T9', name=None):
super().__init__(name=name)
self.pvbase = pvbase
self._append(DetectorPvData,f'{self.pvbase}', unit='°C', name='temperature')
self._append(DetectorPvEnum,f'{self.pvbase}-SS', name='status')
self._append(AdjustablePv,f'{self.pvbase}-WLEN', name='cable_length', unit='m', is_setting=True)
self.unit = self.temperature.unit
def get_current_value(self):
return self.temperature.get_current_value()
+545 -42
View File
@@ -13,6 +13,7 @@ from ..elements.adjustable import (
update_changes,
value_property,
)
from ..devices_general.pv_adjustable import PvRecord
from ..elements.detector import DetectorGet
from ..epics import get_from_archive
from ..utilities.keypress import KeyPress
@@ -26,6 +27,7 @@ import numpy as np
from .motor_controller import MforceChannel
from .detectors import DetectorVirtual
from ..epics.detector import DetectorPvData
import json
if hasattr(global_config, "elog"):
elog = global_config.elog
@@ -433,8 +435,10 @@ class MotorRecord(Assembly):
# alias_fields={"readback": "RBV"},
alias_fields={},
backlash_definition=False,
is_psi_mforce=False,
schneider_config=None,
expect_bad_limits=True,
has_park_pv=False,
):
super().__init__(name=name)
# self.settings.append(self)
@@ -520,6 +524,14 @@ class MotorRecord(Assembly):
name="description",
is_setting=True,
)
if has_park_pv:
self._append(
AdjustablePv,
self.pvname + "_KILL",
name="parked",
is_setting=True,
)
if backlash_definition:
self._append(
AdjustablePv,
@@ -546,8 +558,38 @@ class MotorRecord(Assembly):
is_setting=True,
)
if expect_bad_limits:
self.check_bad_limits()
if is_psi_mforce:
controller_base, tmp = self.pvname.split(":")
channel = int(tmp.split("_")[1])
mforce_base = controller_base + ":" + str(channel)
self._append(
AdjustablePv,
mforce_base + "_RC",
name="run_current",
is_setting=True,
)
self._append(
AdjustablePv,
mforce_base + "_HC",
name="hold_current",
is_setting=True,
)
self._append(
AdjustablePv,
mforce_base + "_set",
name="m_code_set",
is_setting=False,
is_display=False,
)
self._append(
AdjustablePv,
mforce_base + "_get",
name="m_code_get",
is_setting=False,
is_display=False,
)
if schneider_config:
pv_base, port = schneider_config
self._append(
@@ -558,6 +600,8 @@ class MotorRecord(Assembly):
is_setting=True,
is_display=False,
)
if expect_bad_limits:
self.check_bad_limits()
def check_bad_limits(self, abs_set_value=2**53):
ll, hl = self.get_limits()
@@ -568,8 +612,19 @@ class MotorRecord(Assembly):
"""Adjustable convention"""
def changer(value):
statflag_start = self.status_flag.get_current_value()
if not statflag_start.value == 0:
raise AdjustableError(
f"Motor {self.alias.get_full_name()}({self.pvname}) cannot start moving with status flag {statflag_start.name} ."
)
self._status = self._motor.move(value, ignore_limits=(not check), wait=True)
self._status_message = _status_messages[self._status]
statflag_end = self.status_flag.get_current_value()
if not statflag_end.value == 0:
raise AdjustableError(
f"Motor {self.alias.get_full_name()}({self.pvname}) cannot finish move with status flag {statflag_end.name} ."
)
if self._status < 0:
raise AdjustableError(self._status_message)
elif self._status > 0:
@@ -856,6 +911,85 @@ class MForceSettings(Assembly):
self.set_controller_command(f"IS=1,{switch1},{polarity}")
self.set_controller_command(f"IS=2,{switch2},{polarity}")
class SmaractSettings(Assembly):
def __init__(self,
pvname,
name=None,
):
super().__init__(name=name)
self.pvname = pvname
self._append(
PvRecord,
pvsetname = self.pvname + "_PTYP",
pvreadbackname=self.pvname + "_PTYP_RB",
name="sensor_type_num",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + "_AUTOZERO",
name="autozero_on_homing",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + "_MCLF",
name="max_frequency",
is_setting=True,
)
self._append(
AdjustableFS,
file_path="/photonics/home/gac-bernina/eco/configuration/smaract/setting_table",
name="_setting_table",
is_setting=False,
is_display=False,
)
def recall(self, stage_alias_or_model=None):
setting_table = self._setting_table()
stages = np.array([(alias, settings["models"]) for alias, settings in setting_table.items()], dtype=object)
if stage_alias_or_model is not None:
if stage_alias_or_model in stages.T[0]:
alias = stage_alias_or_model
else:
idx = [stage_alias_or_model in a for a in stages.T[1]]
if np.sum(idx) == 1:
alias = stages.T[0][idx][0]
if np.sum(idx) > 1:
print("Multiple entries found for model {stage_alias_or_model}. Please check _settings_table")
return
if np.sum(idx ==0 ):
print("No entries found for model {stage_alias_or_model}. Please check _settings_table or model number / alias.")
return
else:
stages = [(alias, settings["models"]) for alias, settings in setting_table.items()]
input_message = "\nSelect the stage to load setting:\n q) quit\n"
input_message += f'{"Idx":>3} {"Alias":<30} {"Models"}\n'
for index, (alias, model) in enumerate(stages):
input_message += f'{index:>3}) {alias:<30} {model}\n'
input_message += 'Input: '
idx = ''
while idx not in range(len(stages)):
idx = input(input_message)
if idx == 'q':
return
else:
try:
idx = int(idx)
except:
continue
print(f'Selected stage: {stages[idx]}')
alias = stages[idx][0]
stage_settings = setting_table[alias]
if np.any([mcs in self.pvname for mcs in ["SARES23-USR", "SARES23-LIC"]]):
mcs_code = stage_settings["MCS"]
else:
mcs_code = stage_settings["MCS2"]
stage_settings["settings"]["sensor_type_num"] = mcs_code
self.memory.recall(input_obj=stage_settings)
@spec_convenience
@update_changes
@@ -870,6 +1004,7 @@ class SmaractRecord(Assembly):
# alias_fields={"readback": "RBV"},
alias_fields={},
backlash_definition=False,
expect_bad_limits=True,
):
super().__init__(name=name)
# self.settings.append(self)
@@ -883,6 +1018,16 @@ class SmaractRecord(Assembly):
Alias(an, channel=".".join([pvname, af]), channeltype="CA")
)
self._currentChange = None
self._append(
SmaractSettings, self.pvname, name="motor_parameters", is_setting=False
)
self._append(
AdjustablePv, self.pvname + ".LLM", name="limit_low", is_setting=True
)
self._append(
AdjustablePv, self.pvname + ".HLM", name="limit_high", is_setting=True
)
self._append(
AdjustablePvEnum,
self.pvname + ".STAT",
@@ -890,6 +1035,12 @@ class SmaractRecord(Assembly):
is_setting=False,
is_display=True,
)
self._append(
AdjustablePv,
self.pvname + ".ACCL",
name="acceleration_time",
is_setting=True,
)
self._append(
AdjustablePvEnum, self.pvname + ".DIR", name="direction", is_setting=True
)
@@ -897,17 +1048,13 @@ class SmaractRecord(Assembly):
self._append(
AdjustablePv, self.pvname + ".FOFF", name="force_offset", is_setting=True
)
self._append(
AdjustablePv,
self.pvname + "_AUTO_SET_EGU",
name="autoset_unit",
is_setting=True,
)
self._append(AdjustablePv, self.pvname + ".VELO", name="speed", is_setting=True)
self._append(
AdjustablePv,
self.pvname + ".HOMR",
name="home_forward",
is_setting=True,
is_setting=False,
is_status=False,
is_display=False,
)
@@ -915,7 +1062,7 @@ class SmaractRecord(Assembly):
AdjustablePv,
self.pvname + ".HOMR",
name="home_reverse",
is_setting=True,
is_setting=False,
is_status=False,
is_display=False,
)
@@ -927,21 +1074,7 @@ class SmaractRecord(Assembly):
is_setting=False,
is_display=True,
)
self._append(
AdjustablePv, self.pvname + ".VELO", name="speed", is_setting=False
)
self._append(
AdjustablePv,
self.pvname + ".ACCL",
name="acceleration_time",
is_setting=False,
)
self._append(
AdjustablePv, self.pvname + ".LLM", name="limit_low", is_setting=False
)
self._append(
AdjustablePv, self.pvname + ".HLM", name="limit_high", is_setting=False
)
self._append(
AdjustablePvEnum, self.pvname + ".SPMG", name="mode", is_setting=False
)
@@ -956,36 +1089,24 @@ class SmaractRecord(Assembly):
is_display="recursive",
is_status=True,
)
# self._append(
# AdjustablePvEnum,
# self.pvname + ".SPMG",
# name="motor_state",
# is_setting=False,
# )
self._append(
AdjustablePvString, self.pvname + ".EGU", name="unit", is_setting=False
AdjustablePvString, self.pvname + ".EGU", name="unit", is_setting=True
)
self._append(
AdjustablePvString,
self.pvname + ".DESC",
name="description",
is_setting=False,
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + "_CAL_CMD",
self.pvname + "_CAL",
name="_calibrate_sensor",
is_setting=False,
is_setting=True,
is_status=False,
is_display=False,
)
self._append(
AdjustablePvEnum,
self.pvname + "_POS_TYPE_RB",
pvname_set=self.pvname + "_POS_TYPE_SP",
name="sensor_type",
is_setting=True,
)
if backlash_definition:
self._append(
AdjustablePv,
@@ -1011,6 +1132,388 @@ class SmaractRecord(Assembly):
name="backlash_fraction",
is_setting=True,
)
if expect_bad_limits:
self.check_bad_limits()
def check_bad_limits(self, abs_set_value=2**53):
ll, hl = self.get_limits()
if ll == 0 and hl == 0:
self.set_limits(-abs_set_value, abs_set_value)
def home(self):
self.home_forward(1)
time.sleep(0.1)
while not self.flags.is_homed.get_current_value():
time.sleep(0.1)
def calibrate_sensor(self):
self._calibrate_sensor(1)
time.sleep(0.1)
while not self.flags.motion_complete.get_current_value():
time.sleep(0.1)
def set_target_value(self, value, hold=False, check=True):
"""Adjustable convention"""
def changer(value):
self._status = self._motor.move(value, ignore_limits=(not check), wait=True)
self._status_message = _status_messages[self._status]
if self._status < 0:
raise AdjustableError(self._status_message)
elif self._status > 0:
print("\n")
print(self._status_message)
# changer = lambda value: self._motor.move(\
# value, ignore_limits=(not check),
# wait=True)
return Changer(
target=value,
parent=self,
changer=changer,
hold=hold,
stopper=self._motor.stop,
)
def stop(self):
"""Adjustable convention"""
try:
self._currentChange.stop()
except:
self.mode.set_target_value(0)
pass
def get_current_value(self, posType="user", readback=True):
"""Adjustable convention"""
_keywordChecker([("posType", posType, _posTypes)])
if posType == "user":
return self._motor.get_position(readback=readback)
if posType == "dial":
return self._motor.get_position(readback=readback, dial=True)
if posType == "raw":
return self._motor.get_position(readback=readback, raw=True)
def reset_current_value_to(self, value, posType="user"):
"""Adjustable convention"""
_keywordChecker([("posType", posType, _posTypes)])
if posType == "user":
return self._motor.set_position(value)
if posType == "dial":
return self._motor.set_position(value, dial=True)
if posType == "raw":
return self._motor.set_position(value, raw=True)
def get_moveDone(self):
"""Adjustable convention"""
""" 0: moving 1: move done"""
return PV(str(self.Id + ".DMOV")).value
def set_limits(
self, low_limit, high_limit, posType="user", relative_to_present=False
):
"""
set limits. usage: set_limits(low_limit, high_limit)
"""
_keywordChecker([("posType", posType, _posTypes)])
ll_name, hl_name = "LLM", "HLM"
if posType == "dial":
ll_name, hl_name = "DLLM", "DHLM"
if relative_to_present:
v = self.get_current_value(posType=posType)
low_limit = v + low_limit
high_limit = v + high_limit
self._motor.put(ll_name, low_limit)
self._motor.put(hl_name, high_limit)
def add_value_callback(self, callback, index=None):
return self._motor.get_pv("RBV").add_callback(callback=callback, index=index)
def clear_value_callback(self, index=None):
if index:
self._motor.get_pv("RBV").remove_callback(index)
else:
self._motor.get_pv("RBV").clear_callbacks()
def get_limits(self, posType="user"):
"""Adjustable convention"""
_keywordChecker([("posType", posType, _posTypes)])
ll_name, hl_name = "LLM", "HLM"
if posType == "dial":
ll_name, hl_name = "DLLM", "DHLM"
return self._motor.get(ll_name), self._motor.get(hl_name)
def gui(self):
pv, m = tuple(self.pvname.split(":"))
self._run_cmd(f'caqtdm -macro "P={pv},M=:{m}, T=MCS" /sf/controls/config/qt/motorx_all.ui')
def gui_extra(self):
pv, m = tuple(self.pvname.split(":"))
self._run_cmd(f'caqtdm -macro "P={pv},M={m}" /ioc/modules/qt/MCS_extra.ui')
# return string with motor value as variable representation
def __str__(self):
# """ return short info for the current motor"""
s = f"{self.name}"
s += f"\t@ {colorama.Style.BRIGHT}{self.get_current_value():1.6g}{colorama.Style.RESET_ALL} (dial @ {self.get_current_value(posType='dial'):1.6g}; stat: {self.status_flag().name})"
# # s += "\tuser limits (low,high) : {:1.6g},{:1.6g}\n".format(*self.get_limits())
s += f"\n{colorama.Style.DIM}low limit {colorama.Style.RESET_ALL}"
s += ValueInRange(*self.get_limits()).get_str(self.get_current_value())
s += f" {colorama.Style.DIM}high limit{colorama.Style.RESET_ALL}"
# # s += "\tuser limits (low,high) : {:1.6g},{1.6g}".format(self.get_limits())
return s
def __repr__(self):
print(str(self))
return object.__repr__(self)
def __call__(self, value):
self._currentChange = self.set_target_value(value)
def _tweak_ioc(self, step_value=None):
pv = self._motor.get_pv("TWV")
pvf = self._motor.get_pv("TWF")
pvr = self._motor.get_pv("TWR")
if not step_value:
step_value = pv.get()
print(f"Tweaking {self.name} at step size {step_value}", end="\r")
help = "q = exit; up = step*2; down = step/2, left = neg dir, right = pos dir\n"
help = help + "g = go abs, s = set"
print(f"tweaking {self.name}")
print(help)
print(f"Starting at {self.get_current_value()}")
step_value = float(step_value)
oldstep = 0
k = KeyPress()
cll = colorama.ansi.clear_line()
class Printer:
def print(self, **kwargs):
print(
cll + f"stepsize: {self.stepsize}; current: {kwargs['value']}",
end="\r",
)
p = Printer()
print(" ")
p.stepsize = step_value
p.print(value=self.get_current_value())
ind_callback = self.add_value_callback(p.print)
pv.put(step_value)
while k.isq() is False:
if oldstep != step_value:
p.stepsize = step_value
p.print(value=self.get_current_value())
oldstep = step_value
k.waitkey()
if k.isu():
step_value = step_value * 2.0
pv.put(step_value)
elif k.isd():
step_value = step_value / 2.0
pv.put(step_value)
elif k.isr():
pvf.put(1)
elif k.isl():
pvr.put(1)
elif k.iskey("g"):
print("enter absolute position (char to abort go to)")
sys.stdout.flush()
v = sys.stdin.readline()
try:
v = float(v.strip())
self.set_target_value(v)
except:
print("value cannot be converted to float, exit go to mode ...")
sys.stdout.flush()
elif k.iskey("s"):
print("enter new set value (char to abort setting)")
sys.stdout.flush()
v = sys.stdin.readline()
try:
v = float(v[0:-1])
self.reset_current_value_to(v)
except:
print("value cannot be converted to float, exit go to mode ...")
sys.stdout.flush()
elif k.isq():
break
else:
print(help)
self.clear_value_callback(index=ind_callback)
print(f"final position: {self.get_current_value()}")
print(f"final tweak step: {pv.get()}")
def tweak(self, *args, **kwargs):
return self._tweak_ioc(*args, **kwargs)
@spec_convenience
@update_changes
@get_from_archive
@value_property
class SmaractRecord_old(Assembly):
#Note: this is the one that works with the old SmarAct IOCs before Thierry made changes in 09/2023
def __init__(
self,
pvname,
name=None,
elog=None,
# alias_fields={"readback": "RBV"},
alias_fields={},
backlash_definition=False,
expect_bad_limits=True,
):
super().__init__(name=name)
# self.settings.append(self)
self.settings_collection.append(self, force=True)
self.pvname = pvname
self._motor = _Motor(pvname)
self._elog = elog
for an, af in alias_fields.items():
self.alias.append(
Alias(an, channel=".".join([pvname, af]), channeltype="CA")
)
self._currentChange = None
self._append(
AdjustablePvEnum,
self.pvname + "_POS_TYPE_RB",
pvname_set=self.pvname + "_POS_TYPE_SP",
name="sensor_type",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + "_MAX_FREQ",
name="max_frequency",
is_setting=True,
)
self._append(AdjustablePv, self.pvname + ".VELO", name="speed", is_setting=True)
self._append(
AdjustablePv,
self.pvname + ".ACCL",
name="acceleration_time",
is_setting=True,
)
self._append(
AdjustablePv, self.pvname + ".LLM", name="limit_low", is_setting=True
)
self._append(
AdjustablePv, self.pvname + ".HLM", name="limit_high", is_setting=True
)
self._append(
AdjustablePvEnum,
self.pvname + ".STAT",
name="status_flag",
is_setting=False,
is_display=True,
)
self._append(
AdjustablePvEnum, self.pvname + ".DIR", name="direction", is_setting=True
)
self._append(AdjustablePv, self.pvname + ".OFF", name="offset", is_setting=True)
self._append(
AdjustablePv, self.pvname + ".FOFF", name="force_offset", is_setting=True
)
self._append(
AdjustablePv,
self.pvname + "_AUTO_SET_EGU",
name="autoset_unit",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + ".HOMR",
name="home_forward",
is_setting=False,
is_status=False,
is_display=False,
)
self._append(
AdjustablePv,
self.pvname + ".HOMR",
name="home_reverse",
is_setting=False,
is_status=False,
is_display=False,
)
self._append(
DetectorPvData,
self.pvname + ".RBV",
name="readback",
is_setting=False,
is_display=True,
)
self._append(
AdjustablePvEnum, self.pvname + ".SPMG", name="mode", is_setting=False
)
self._append(
DetectorPvData, self.pvname + ".MSTA", name="_flags", is_setting=False
)
self._append(
SmaractRecordFlags,
self.pvname,
self._flags,
name="flags",
is_display="recursive",
is_status=True,
)
self._append(
AdjustablePvString, self.pvname + ".EGU", name="unit", is_setting=True
)
self._append(
AdjustablePvString,
self.pvname + ".DESC",
name="description",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + "_CAL_CMD",
name="_calibrate_sensor",
is_setting=True,
is_status=False,
is_display=False,
)
if backlash_definition:
self._append(
AdjustablePv,
self.pvname + ".BVEL",
name="backlash_velocity",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + ".BACC",
name="backlash_acceleration",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + ".BDST",
name="backlash_distance",
is_setting=True,
)
self._append(
AdjustablePv,
self.pvname + ".FRAC",
name="backlash_fraction",
is_setting=True,
)
if expect_bad_limits:
self.check_bad_limits()
def check_bad_limits(self, abs_set_value=2**53):
ll, hl = self.get_limits()
if ll == 0 and hl == 0:
self.set_limits(-abs_set_value, abs_set_value)
def home(self):
self.home_forward(1)
+179
View File
@@ -0,0 +1,179 @@
from cam_server import CamClient, PipelineClient
from eco.devices_general.utilities import Changer
from eco.elements.adj_obj import AdjustableObject, DetectorObject
from eco.elements.detector import DetectorGet
from ..aliases import Alias, append_object_to_object
from ..elements.adjustable import AdjustableVirtual, AdjustableGetSet, value_property
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
from ..elements.assembly import Assembly
from .motors import MotorRecord
import sys
from pathlib import Path
import time
sys.path.append("/sf/bernina/config/src/python/sf_databuffer/")
import bufferutils
CAM_CLIENT = None
PIPELINE_CLIENT = None
def get_camclient():
global CAM_CLIENT
if not CAM_CLIENT:
CAM_CLIENT = CamClient()
return CAM_CLIENT
def get_pipelineclient():
global PIPELINE_CLIENT
if not PIPELINE_CLIENT:
PIPELINE_CLIENT = PipelineClient()
return PIPELINE_CLIENT
@value_property
class Pipeline(Assembly):
def __init__(self, pipeline_name, name=None, camserver_group=None):
super().__init__(name=name)
self.pipeline_name = pipeline_name
self.camserver_group = camserver_group
self._append(AdjustableGetSet,
self._get_config,
self._set_config,
cache_get_seconds =.05,
precision=0,
check_interval=None,
name='_config',
is_setting=False,
is_display=False)
self._append(AdjustableObject, self._config, name='config',is_setting=True, is_display='recursive')
self._append(DetectorGet, self._get_info, cache_get_seconds =.05, name='_info', is_setting=False, is_display=False)
self._append(DetectorObject, self._info, name='info', is_display='recursive', is_setting=False)
# @property
# def cc(self):
# return get_camclient()
@property
def pc(self):
return get_pipelineclient()
def _get_config(self):
return self.pc.get_pipeline_config(self.pipeline_name)
def _set_config(self, value, hold=False):
return Changer(
target=value,
changer=lambda v: self.pc.set_pipeline_config(self.pipeline_name, v),
hold=hold,
)
def _get_info(self, reject_kws = ['config']):
info = self.pc.get_instance_info(self.pipeline_name)
for rkw in reject_kws:
info.pop(rkw)
return info
def _get_stream(self):
return self.pc.get_instance_stream(self.pipeline_name)
# ### convenience functions ###
# def set_alias(self, alias=None):
# """creates an alias in the camera config on the server. If no alias is provided, it defaults to the camera name"""
# if not alias:
# alias = self.camserver_alias
# self.set_config_fields({"alias": [alias.upper()]})
# def set_group(self, group=None):
# """creates an alias in the camera config on the server. If no alias is provided, it defaults to the camera name"""
# if not group:
# group = self.camserver_group
# self.set_config_fields({"group": group})
def restart_pipeline(self):
base_directory = "/sf/bernina/config/src/python/sf_databuffer/"
label = self.pipeline_name
policies = bufferutils.read_files(base_directory / Path("policies"), "policies")
sources = bufferutils.read_files(base_directory / Path("sources"), "sources")
sources_new = sources.copy()
# Only for debugging purposes
labeled_sources = bufferutils.get_labeled_sources(sources_new, label)
for s in labeled_sources:
bufferutils.logging.info(f"Restarting {s['stream']}")
sources_new = bufferutils.remove_labeled_source(sources_new, label)
# Stopping the removed source(s)
bufferutils.update_sources_and_policies(sources_new, policies)
# Starting the source(s) again
bufferutils.update_sources_and_policies(sources, policies)
def stop(self):
self.pc.stop_instance(self.pipeline_name)
# def set_cross(self, x, y, x_um_per_px=None, y_um_per_px=None):
# """set x and y position of the refetence marker on a camera px/um calibration is conserved if no new value is given"""
# calib = self.get_current_value()["camera_calibration"]
# if calib:
# if not x_um_per_px:
# x_um_per_px = calib["reference_marker_width"] / abs(
# calib["reference_marker"][2] - calib["reference_marker"][0]
# )
# if not y_um_per_px:
# y_um_per_px = calib["reference_marker_height"] / abs(
# calib["reference_marker"][3] - calib["reference_marker"][1]
# )
# else:
# calib = {}
# x_um_per_px = 1
# y_um_per_px = 1
# calib["reference_marker"] = [x - 1, y - 1, x + 1, y + 1]
# calib["reference_marker_width"] = 2 * x_um_per_px
# calib["reference_marker_height"] = 2 * y_um_per_px
# self.set_config_fields(fields={"camera_calibration": calib})
# def set_config_fields_multiple_cams(self, conditions, fields):
# """
# conditions is a dictionary holding the conditions to select a subset of cameras, e.g. {"group": Bernina}
# fields is a dictionary containing the keys and values that should be updated, e.g. fields={'alias': ['huhu', 'duda']}
# """
# cams = {
# cam: self.cc.get_camera_config(cam)
# for cam in self.cc.get_cameras()
# if not "jungfrau" in cam
# }
# cams_selected = {}
# for cam, cfg in cams.items():
# try:
# if all([value in cfg[key] for key, value in conditions.items()]):
# cfg.update(fields)
# self.cc.set_camera_config(cam, cfg)
# cams_selected[cam] = cfg
# except Exception as e:
# print(f"{type(e)} {e} in cam {cam}")
# return cams_selected
# def clear_all_bernina_aliases(self, verbose=True):
# cams_selected = self.set_config_fields_multiple_cams(
# conditions={"group": "Bernina"}, fields={"alias": []}
# )
# if verbose:
# print(f"Reset alias of {len(cams_selected)} cameras")
# print(cams_selected.keys())
# def __repr__(self):
# s = f"**Camera Server Config {self.pipeline_name} with Alias {self.name}**\n"
# for key, item in self.get_current_value().items():
# s += f"{key:20} : {item}\n"
# return s
+68 -1
View File
@@ -1,6 +1,7 @@
from ..epics.adjustable import AdjustablePvEnum, AdjustablePvString
from ..epics.adjustable import AdjustablePvEnum, AdjustablePvString, AdjustablePv
from ..elements.assembly import Assembly
from ..epics.detector import DetectorPvEnum, DetectorPvData
from eco.elements.adjustable import spec_convenience
class PowerSocket(Assembly):
@@ -65,3 +66,69 @@ class GudeStrip(Assembly):
self._append(
DetectorPvData, pvbase + ":VOLTAGE", is_display=True, name="voltage"
)
class MpodStatus(Assembly):
def __init__(self,pvbase,channel_number, module_string='LV_OMPV_1', name=None):
super().__init__(name=name)
self.pvbase = pvbase
self._module_string = module_string
self.channel_number = channel_number
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_ON',name='is_on')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_INHIBIT',name='inhibited')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_FAILURE_MIN_SENS_VOLTAGE',name='voltage_readback_low')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_FAILURE_MAX_SENS_VOLTAGE',name='voltage_readback_high')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_FAILURE_MAX_TERM_VOLTAGE',name='terminal_voltage_readback_high')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_FAILURE_MAX_CURRENT',name='current_too_high')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_FAILURE_MAX_TEMP',name='temperature_high')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_FAILURE_MAX_POWER',name='output_power_high')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_TIMEOUT',name='communication_timeout')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_CURR_CTRL',name='constant_current_mode')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_RMP_UP',name='ramping_up')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_RMP_DOWN',name='ramping_down')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_KILL',name='kill_enabled')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_EMERGENCY_OFF',name='emergency_off')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_FINE_ADJUST',name='fine_adjustment')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_VOLTAGE_CTRL',name='constant_voltage_mode')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_LOW_CURR_MEAS',name='current_readback_range_low')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_OUT_CURR_OOB',name='current_readback_range_high')
self._append(DetectorPvEnum,self.pvbase + f':{self._module_string}_CH{self.channel_number}_OVERCURRENT',name='overcurrent')
@spec_convenience
class MpodChannel(Assembly):
def __init__(self,pvbase,channel_number, module_string='LV_OMPV_1', name=None):
super().__init__(name=name)
self.pvbase = pvbase
self._module_string = module_string
self.channel_number = channel_number
self._append(AdjustablePvEnum,self.pvbase+f':{self._module_string}_CH{self.channel_number}_SWITCH_SP', name='on', is_setting=True)
self._append(AdjustablePv,
self.pvbase+f':{self._module_string}_CH{self.channel_number}_OUTPUT_V_SP',
pvreadbackname = self.pvbase+f':{self._module_string}_CH{self.channel_number}_MEAS_SENS_V',
pvlowlimname = self.pvbase+f':{self._module_string}_CH{self.channel_number}_OUTPUT_V_SP.LOPR',
pvhighlimname = self.pvbase+f':{self._module_string}_CH{self.channel_number}_OUTPUT_V_SP.HOPR',
name='voltage', is_setting=True)
self._append(AdjustablePv,
self.pvbase+f':{self._module_string}_CH{self.channel_number}_RMP_UP_RATE_SP',
pvlowlimname = self.pvbase+f':{self._module_string}_CH{self.channel_number}_RMP_UP_RATE_SP.LOPR',
pvhighlimname = self.pvbase+f':{self._module_string}_CH{self.channel_number}_RMP_UP_RATE_SP.HOPR',
name='ramp_up', is_setting=True)
self._append(AdjustablePv,
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)
self._append(MpodStatus,self.pvbase, self.channel_number, self._module_string, name='flags')
def get_current_value(self,*args,**kwargs):
return self.on.get_current_value(*args,**kwargs)
def set_target_value(self,*args,**kwargs):
return self.on.set_target_value(*args,**kwargs)
class MpodModule(Assembly):
def __init__(self,pvbase,channelnumbers, channelnames, module_string='LV_OMPV_1', name=None):
super().__init__(name=name)
for channelnumber,channelname in zip(channelnumbers,channelnames):
self._append(MpodChannel,pvbase,channel_number=channelnumber, module_string=module_string,name=channelname)
+126 -3
View File
@@ -1,10 +1,18 @@
from eco.elements.assembly import Assembly
from eco.epics.detector import DetectorPvData
from eco.epics.adjustable import AdjustablePvString, AdjustablePv
from eco.epics.adjustable import (
AdjustablePvString,
AdjustablePv,
spec_convenience,
tweak_option,
)
class AnalogInput(Assembly):
def __init__(self, pvname, name=None):
"""Analog input, which is defined by a PV name. There are linear calibration
options (hidden adjustment and visible linear_calibration values):
value = raw*linear_calibration_slope + linear_calibration_offset"""
super().__init__(name=name)
self.pvname = pvname
self._append(
@@ -17,6 +25,106 @@ class AnalogInput(Assembly):
is_setting=True,
is_display=True,
)
self._append(
AdjustablePvString,
self.pvname + ".EGU",
name="unit",
is_setting=False,
is_display=True,
)
self.value.unit = self.unit
self._append(
DetectorPvData,
self.pvname + ".RVAL",
name="raw",
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=True,
)
self._append(
AdjustablePv,
self.pvname + ".ESLO",
name="linear_calibration_slope",
is_setting=True,
is_display=True,
)
def get_current_value(self):
return self.value.get_current_value()
def reset_offset_current_value_to(self,value=0):
self.linear_calibration_offset.set_target_value(
(-1)
* self.raw.get_current_value()
* self.linear_calibration_slope.get_current_value()
* self._adj_slope.get_current_value()
+ value
).wait()
def reset_slope_current_value_to(self,value=1):
oslo = self.linear_calibration_slope.get_current_value()
ooff = self.linear_calibration_offset.get_current_value()
ooff_raw = ooff / oslo / self._adj_slope.get_current_value()
# print(ooff_raw)
nslo = value / (self.raw.get_current_value()+ooff_raw)
self.linear_calibration_slope.set_target_value(nslo).wait()
self.linear_calibration_offset.set_target_value(
ooff_raw
* nslo
* self._adj_slope.get_current_value()
).wait()
class WagoAnalogInputs(Assembly):
def __init__(self, pvbase, name=None):
super().__init__(name=name)
self.pvbase = pvbase
for n in range(1, 9):
self._append(AnalogInput, pvbase + f":ADC{n:02d}", name=f"ch{n:d}")
@spec_convenience
@tweak_option
class AnalogOutput(Assembly):
def __init__(self, pvname, name=None):
super().__init__(name=name)
self.pvname = pvname
self._append(
AdjustablePv,
self.pvname,
name="value",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePvString,
self.pvname + ".DESC",
name="description",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePvString,
self.pvname + ".EGU",
@@ -35,10 +143,25 @@ class AnalogInput(Assembly):
def get_current_value(self):
return self.value.get_current_value()
def set_target_value(self, *args, **kwargs):
return self.value.set_target_value(*args, **kwargs)
class WagoAnalogInputs(Assembly):
def __call__(self, *args):
if args:
self.value.set_target_Value(*args).wait()
else:
return self.value.get_current_value()
class WagoAnalogOutputs(Assembly):
def __init__(self, pvbase, name=None):
super().__init__(name=name)
self.pvbase = pvbase
for n in range(1, 9):
self._append(AnalogInput, pvbase + f":ADC{n:02d}", name=f"ch{n:d}")
self._append(
AnalogOutput,
pvbase + f":DAC{n:02d}",
name=f"ch{n:d}",
is_setting=True,
is_display=True,
)
-82
View File
@@ -1,82 +0,0 @@
from .dummy import *
# from ..utilities.config import initFromConfigList
# from epics import PV
# from .. import ecocnf
# from ..aliases import NamespaceCollection
# import logging
# from .config import components, config
# import sys
# _namespace = globals()
# _mod = sys.modules[__name__]
# _scope_name = "bernina"
# alias_namespaces = NamespaceCollection()
# # from ..utilities.runtable import Run_Table
# # def init(pgroup, alias_namespaces, instances):
# # run_table = Run_Table(pgroup, alias_namespaces.bernina, instances)
# # return run_table
# def init(*args, lazy=None):
# if args:
# allnames = [tc["name"] for tc in components]
# comp_toinit = []
# for arg in args:
# if not arg in allnames:
# raise Exception(f"The component {arg} has no configuration defined!")
# else:
# comp_toinit.append(components[allnames.index(arg)])
# else:
# comp_toinit = components
# if lazy is None:
# lazy = ecocnf.startup_lazy
# op = {}
# for key, value in initFromConfigList(comp_toinit, components, lazy=lazy).items():
# # _namespace[key] = value
# _mod.__dict__[key] = value
# op[key] = value
# if not lazy:
# print("made here")
# if hasattr(value, "alias"):
# for ta in value.alias.get_all():
# try:
# alias_namespaces.bernina.update(
# ta["alias"], ta["channel"], ta["channeltype"]
# )
# except:
# print(f'could not init alias {ta["alias"]}')
# else:
# print(f"object {key} has no alias!")
# alias_namespaces.bernina.store()
# # try:
# # run_table = bernina.init(config['pgroup'], alias_namespaces,_mod)
# # _mod.__dict__['rt'] = run_table
# # op['rt'] = run_table
# # except:
# # print('Initializing of run_table failed')
# return op
# def parse_for_aliases():
# names = [tc["name"] for tc in components]
# for name in names:
# to = _mod.__dict__[name]
# if hasattr(to, "alias"):
# for ta in to.alias.get_all():
# try:
# globals()["alias_namespaces"].bernina.update(
# ta["alias"], ta["channel"], ta["channeltype"]
# )
# except:
# print(f'could not init alias {ta["alias"]}')
# else:
# print(f"object {name} has no alias!")
# globals()["alias_namespaces"].bernina.store()
-1157
View File
File diff suppressed because it is too large Load Diff
-676
View File
@@ -1,676 +0,0 @@
# # New configuration of components:
# components is an ordered list of
# - name in parent package
# - type, describing the python Class or factory function.
# - arguments of that type args
# - kwargs of that type
# # Conventions for the type
# the call of type will try to pass a kwarg 'name' with the
# name of the component, before only calling args and kwargs.
# if arg or kwarg is of type eco.utilities.Component (dummy class)
# this indicates that an earlier initialized object is used
# (e.g. from same configuration).
from ..utilities.config import (
Component,
init_device,
initFromConfigList,
Configuration,
)
_eco_lazy_init = False
config = Configuration(
"/sf/bernina/config/eco/bernina_config_eco.json", name="bernina_config"
)
components = [
{
"type": "eco.utilities.config:append_to_path",
"args": config["path_exp"],
"name": "path_exp",
"kwargs": {},
"lazy": True,
},
{
"name": "elog",
"type": "eco.utilities.elog:Elog",
"args": ["https://elog-gfa.psi.ch/Bernina"],
"kwargs": {
"screenshot_directory": "/tmp",
},
},
{
"name": "screenshot",
"type": "eco.utilities.elog:Screenshot",
"args": [],
"kwargs": {"screenshot_directory": "/sf/bernina/config/screenshots"},
},
{
"name": "fel",
"type": "eco.fel.swissfel:SwissFel",
"args": [],
"kwargs": {},
"desc": "Fel related control and feedback",
},
# {
# "name": "slit_und",
# "type": "eco.xoptics.slits:SlitFourBlades_old",
# "args": ["SARFE10-OAPU044"],
# "kwargs": {},
# "desc": "Slit after Undulator",
# },
# {
# "name": "slit_und_epics",
# "type": "eco.xoptics.slits:SlitFourBlades_old",
# "args": ["SARFE10-OAPU044"],
# "kwargs": {},
# "desc": "Slit after Undulator",
# },
# {
# "name": "mon_und",
# "args": ["SARFE10-PBPS053"],
# "z_und": 53,
# "desc": "Intensity/Position monitor after Undolator",
# "type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS",
# "kwargs": {"VME_crate": "SAROP21-CVME-PBPS2", "link": 9},
# },
{
"name": "mon_und",
"z_und": 53,
"desc": "Intensity/position monitor after Undulator",
"type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS_new",
"args": ["SARFE10-PBPS053"],
"kwargs": {
"VME_crate": "SAROP21-CVME-PBPS1",
"link": 9,
"channels": {
"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",
},
"calc": {
"itot": "SLAAR21-LTIM01-EVR0:CALCI",
"xpos": "SLAAR21-LTIM01-EVR0:CALCX",
"ypos": "SLAAR21-LTIM01-EVR0:CALCY",
},
},
},
{
"name": "pshut_und",
"type": "eco.xoptics.shutters:PhotonShutter",
"args": ["SARFE10-OPSH044:REQUEST"],
"kwargs": {},
"z_und": 44,
"desc": "First shutter after Undulators",
},
{
"name": "pshut_fe",
"type": "eco.xoptics.shutters:PhotonShutter",
"args": ["SARFE10-OPSH059:REQUEST"],
"kwargs": {},
"z_und": 59,
"desc": "Photon shutter end of front end",
},
{
"name": "sshut_opt",
"type": "eco.xoptics.shutters:SafetyShutter",
"args": ["SGE01-EPKT822:BST1_oeffnen"],
"kwargs": {},
"z_und": 115,
"desc": "Bernina safety shutter",
},
{
"name": "sshut_fe",
"type": "eco.xoptics.shutters:SafetyShutter",
"args": ["SGE01-EPKT820:BST1_oeffnen"],
"kwargs": {},
"z_und": 115,
"desc": "Bernina safety shutter",
},
{
"name": "att_fe",
"type": "eco.xoptics.attenuator_aramis:AttenuatorAramis",
"args": ["SARFE10-OATT053"],
"kwargs": {"shutter": Component("pshut_und")},
"z_und": 53,
"desc": "Attenuator in Front End",
},
# {
# "name": "mon_und",
# "z_und": 53,
# "desc": "Intensity/position monitor after Optics hutch",
# "type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS",
# "args": ["SARFE10-PBPS053"],
# "kwargs": {},
# },
{
"name": "xspect",
"z_und": 53,
"desc": "X-ray single shot spectrometer",
"type": "eco.xdiagnostics.xspect:Xspect",
"args": [],
"kwargs": {},
},
{
"name": "prof_fe",
"args": ["SARFE10-PPRM064"] * 2,
"kwargs": {},
"z_und": 64,
"desc": "Profile monitor after Front End",
"type": "eco.xdiagnostics.profile_monitors:Pprm",
},
{
"name": "prof_mirr_alv1",
"args": ["SAROP11-PPRM066"] * 2,
"kwargs": {},
"z_und": 66,
"desc": "Profile monitor after Alvra Mirror 1",
"type": "eco.xdiagnostics.profile_monitors:Pprm",
},
{
"name": "prof_mirr1",
"args": ["SAROP21-PPRM094"] * 2,
"kwargs": {},
"z_und": 94,
"desc": "Profile monitor after Mirror 1",
"type": "eco.xdiagnostics.profile_monitors:Pprm",
},
{
"name": "offset",
"args": [],
"kwargs": {},
"z_und": 96,
"desc": "offset mirrors in pink mode",
"type": "eco.xoptics.offsetMirrors_new:OffsetMirrorsBernina",
"kwargs": {},
},
{
"name": "mono",
"args": ["SAROP21-ODCM098"],
"kwargs": {},
"z_und": 98,
"desc": "DCM Monochromator",
"type": "eco.xoptics.dcm_new:DoubleCrystalMono",
},
{
"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",
},
{
"name": "prof_mono",
"args": ["SAROP21-PPRM113"] * 2,
"kwargs": {},
"z_und": 102,
"desc": "Profile monitor after Monochromator",
"type": "eco.xdiagnostics.profile_monitors:Pprm",
},
{
"name": "xp",
"args": [],
"kwargs": {
"Id": "SAROP21-OPPI113",
"evronoff": "SGE-CPCW-72-EVR0:FrontUnivOut15-Ena-SP",
"evrsrc": "SGE-CPCW-72-EVR0:FrontUnivOut15-Src-SP",
},
"z_und": 103,
"desc": "X-ray pulse picker",
"type": "eco.xoptics.pp:Pulsepick",
},
# {
# "name": "mon_opt_old",
# "z_und": 133,
# "desc": "Intensity/position monitor after Optics hutch",
# "type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS",
# "args": ["SAROP21-PBPS133"],
# "kwargs": {"VME_crate": "SAROP21-CVME-PBPS1", "link": 9},
# },
{
"name": "mon_opt",
"z_und": 133,
"desc": "Intensity/position monitor after Optics hutch",
"type": "eco.xdiagnostics.intensity_monitors:SolidTargetDetectorPBPS_new",
"args": ["SAROP21-PBPS133"],
"kwargs": {
"VME_crate": "SAROP21-CVME-PBPS1",
"link": 9,
"channels": {
"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",
},
"calc": {
"itot": "SLAAR21-LTIM01-EVR0:CALCI",
"xpos": "SLAAR21-LTIM01-EVR0:CALCX",
"ypos": "SLAAR21-LTIM01-EVR0:CALCY",
},
},
},
{
"name": "prof_opt",
"args": ["SAROP21-PPRM133"] * 2,
"kwargs": {},
"z_und": 133,
"desc": "Profile monitor after Optics hutch",
"type": "eco.xdiagnostics.profile_monitors:Pprm",
},
{
"name": "spect_tt",
"args": ["SAROP21-PSEN135"],
"kwargs": {"reduction_client_address": "http://sf-daqsync-01:8889/"},
"z_und": 135,
"desc": "Spectral encoding timing diagnostics before Attenuator.",
"type": "eco.xdiagnostics.timetools:SpectralEncoder",
},
{
"name": "att",
"args": ["SAROP21-OATT135"],
"kwargs": {"shutter": Component("xp"), "set_limits": []},
"z_und": 135,
"desc": "Attenuator Bernina",
"type": "eco.xoptics.attenuator_aramis:AttenuatorAramis",
},
{
"name": "slit_att",
"args": ["SAROP21-OAPU136"],
"kwargs": {},
"z_und": 136,
"desc": "Slits behind attenuator",
"type": "eco.xoptics.slits:SlitPosWidth",
},
{
"name": "det_dio",
"args": ["SAROP21-PDIO138"],
"z_und": 138,
"desc": "Diode digitizer for exp data",
"type": "eco.devices_general.detectors:DiodeDigitizer",
"kwargs": {"VME_crate": "SAROP21-CVME-PBPS2", "link": 9},
},
{
"name": "prof_att",
"args": ["SAROP21-PPRM138"] * 2,
"kwargs": {},
"z_und": 138,
"desc": "Profile monitor after Attenuator",
"type": "eco.xdiagnostics.profile_monitors:Pprm",
},
# {
# "name": "spatial_tt",
# "args": [],
# "kwargs": {"reduction_client_address": "http://sf-daqsync-02:12003/"},
# "z_und": 141,
# "desc": "spatial encoding timing diagnostics before sample.",
# "type": "eco.xdiagnostics.timetools:SpatialEncoder",
# "lazy": True,
# },
# {
# "name": "slit_kb",
# "args": [],
# "kwargs": {"pvname": "SARES20-MF1"},
# "z_und": 141,
# "desc": "Slits behind Kb",
# "type": "eco.xoptics.slits:SlitBlades_JJ",
# # "type": "eco.xoptics.slits:SlitBladesJJ_old",
# },
# {
# "args": [],
# "name": "gps_old",
# "z_und": 142,
# "desc": "General purpose station",
# "type": "eco.endstations.bernina_diffractometers:GPS_old",
# "kwargs": {
# "Id": "SARES22-GPS",
# "configuration": config["gps_config"],
# "fina_hex_angle_offset": "/sf/bernina/config/eco/reference_values/hex_pi_angle_offset.json",
# },
# "lazy": True,
# },
# {
# "args": [],
# "name": "xrd_old",
# "z_und": 142,
# "desc": "Xray diffractometer",
# "type": "eco.endstations.bernina_diffractometers:XRD_old",
# "kwargs": {"Id": "SARES21-XRD", "configuration": config["xrd_config"]},
# },
# {
# "args": [],
# "name": "xrd",
# "z_und": 142,
# "desc": "Xray diffractometer",
# "type": "eco.endstations.bernina_diffractometers:XRD",
# "kwargs": {
# "Id": "SARES21-XRD",
# "configuration": config["xrd_config"],
# "diff_detector": {"jf_id": "JF01T03V01"},
# },
# },
{
"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"],
},
},
# {
# "args": [],
# "name": "gasjet",
# "z_und": 142,
# "desc": "ToF comm. gasjet",
# "type": "tof:jet",
# "kwargs": {},
# },
{
"args": [],
"name": "xeye",
"z_und": 142,
"desc": "Mobile X-ray eye in Bernina hutch",
"type": "eco.xdiagnostics.profile_monitors:Bernina_XEYE",
"kwargs": {
"zoomstage_pv": config["xeye"]["zoomstage_pv"],
"camera_pv": config["xeye"]["camera_pv"],
"bshost": "sf-daqsync-01.psi.ch",
"bsport": 11151,
},
},
{
"args": ["SARES20-CAMS142-M1"],
"name": "cam_sample_sideview",
"z_und": 142,
"desc": "",
"type": "eco.devices_general.cameras_swissfel:CameraBasler",
"kwargs": {},
},
{
"args": ["SARES20-CAMS142-M3"],
"name": "cam_sample_inline",
"z_und": 142,
"desc": "",
"type": "eco.devices_general.cameras_swissfel:CameraBasler",
"kwargs": {},
},
# {
# "args": ["SARES20-CAMS142-C3"],
# "name": "cam_sample_xrd",
# "z_und": 142,
# "desc": "",
# "type": "eco.devices_general.cameras_swissfel:CameraBasler",
# "kwargs": {},
# },
# {
# "args": [],
# "name": "cams_qioptiq",
# "z_und": 142,
# "desc": "Qioptic sample viewer in Bernina hutch",
# "type": "eco.endstations.bernina_cameras:Qioptiq",
# "kwargs": {
# "bshost": "sf-daqsync-01.psi.ch",
# "bsport": 11149,
# "zoomstage_pv": config["cams_qioptiq"]["zoomstage_pv"],
# "camera_pv": config["cams_qioptiq"]["camera_pv"],
# },
# },
{
"args": [],
"name": "cams_sigma",
"z_und": 142,
"desc": "Sigma objective",
"type": "eco.endstations.bernina_cameras:Sigma",
"kwargs": {
"bshost": "sf-daqsync-01.psi.ch",
"bsport": 11149,
"camera_pv": config["cams_sigma"]["camera_pv"],
},
},
# {
# "args": ["SLAAR02-TSPL-EPL"],
# "name": "phase_shifter",
# "z_und": 142,
# "desc": "Experiment laser phase shifter",
# "type": "eco.devices_general.timing:PhaseShifterAramis",
# "kwargs": {},
# },
{
"args": ["SLAAR21-LTIM01-EVR0"],
"name": "laser_shutter",
"z_und": 142,
"desc": "Laser Shutter",
"type": "eco.loptics.laser_shutter:laser_shutter",
"kwargs": {},
},
{
"args": [],
"name": "epics_channel_list",
"desc": "epics channel list",
"type": "eco.utilities.config:ChannelList",
"kwargs": {
"file_name": "/sf/bernina/config/channel_lists/default_channel_list_epics"
},
"lazy": True,
},
{
"args": [],
"name": "epics_daq",
"z_und": 142,
"desc": "epics data acquisition",
"type": "eco.acquisition.epics_data:Epicstools",
"kwargs": {
"channel_list": Component("epics_channel_list"),
"default_file_path": f"/sf/bernina/data/{config['pgroup']}/res/epics_daq/",
},
"lazy": True,
},
# {
# "args": [],
# "name": "daq_dia_old",
# "desc": "server based acquisition",
# "type": "eco.acquisition.dia:DIAClient",
# "kwargs": {
# "instrument": "bernina",
# "api_address": config["daq_address"],
# "pgroup": config["pgroup"],
# "pedestal_directory": config["jf_pedestal_directory"],
# "gain_path": config["jf_gain_path"],
# "config_default": config["daq_dia_config"],
# "jf_channels": config["jf_channels"],
# "default_file_path": None,
# },
# },
{
"args": [
config["checker_PV"],
config["checker_thresholds"],
config["checker_fractionInThreshold"],
], #'SARFE10-PBPG050:HAMP-INTENSITY-CAL',[60,700],.7],
"name": "checker",
"desc": "checker functions for data acquisition",
"type": "eco.acquisition.checkers:CheckerCA",
"kwargs": {},
},
# {
# "args": [
# "SARES20-LSCP9-FNS:CH1:VAL_GET",
# [-100000, 100000],
# config["checker_fractionInThreshold"],
# ], #'SARFE10-PBPG050:HAMP-INTENSITY-CAL',[60,700],.7],
# "name": "checker_epics",
# "desc": "checker functions for data acquisition",
# "type": "eco.acquisition.checkers:CheckerCA",
# "kwargs": {},
# },
# {
# "args": [],
# "name": "scans_epics",
# "desc": "epics non beam synchronous based acquisition",
# "type": "eco.acquisition.scan:Scans",
# "kwargs": {
# "data_base_dir": "scan_data",
# "scan_info_dir": f"/sf/bernina/data/{config['pgroup']}/res/scan_info",
# "default_counters": [Component("epics_daq")],
# "checker": Component("checker_epics"),
# "scan_directories": True,
# "run_table": Component("run_table"),
# },
# "lazy": True,
# },
# {
# "args": [],
# "name": "lxt",
# "desc": "laser timing with pockels cells and phase shifter",
# "type": "eco.timing.lasertiming:Lxt",
# "kwargs": {},
# },
# {
# "args": ["SARES20-CVME-01-EVR0"],
# "name": "evr_bernina",
# "desc": "Bernina event receiver",
# "type": "eco.timing.event_timing:EventReceiver",
# "kwargs": {},
# },
{
"args": [],
"name": "default_channel_list",
"desc": "Bernina default channels, used in daq",
"type": "eco.utilities.config:ChannelList",
"kwargs": {
"file_name": "/sf/bernina/config/channel_lists/default_channel_list"
},
},
{
"args": [],
"name": "default_channel_list_bs",
"desc": "Bernina default bs channels, used by bs_daq",
"type": "eco.utilities.config:ChannelList",
"kwargs": {
"file_name": "/sf/bernina/config/channel_lists/default_channel_list_bs"
},
},
{
"args": [],
"name": "channels_spectrometer_projection",
"desc": "",
"type": "eco.utilities.config:ChannelList",
"kwargs": {
"file_name": "/sf/bernina/config/channel_lists/channel_list_PSSS_projection"
},
},
{
"args": [],
"name": "bs_daq",
"desc": "bs daq writer (locally!)",
"type": "eco.acquisition.bs_data:BStools",
"kwargs": {
"default_channel_list": {
"bernina_default_channels_bs": Component("default_channel_list_bs")
},
"default_file_path": f"/sf/bernina/data/{config['pgroup']}/res/%s",
},
},
{
"args": [],
"name": "usd_table",
"z_und": 141,
"desc": "Upstream diagnostics table",
"type": "eco.endstations.hexapod:HexapodSymmetrie",
"kwargs": {"offset": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]},
},
# {
# "args": ["SARES23-"],
# "name": "slit_kb",
# "z_und": 141,
# "desc": "Upstream diagnostics slits",
# "type": "eco.xoptics.slit_USD:Upstream_diagnostic_slits",
# "kwargs": {"right": "LIC4", "left": "LIC3", "up": "LIC2", "down": "LIC1"},
# },
# {
# "args": ["SARES23-"],
# "name": "slit_cleanup",
# "z_und": 141,
# "desc": "Upstream diagnostics slits",
# "type": "eco.xoptics.slit_USD:Upstream_diagnostic_slits",
# "kwargs": {"right": "LIC7", "left": "LIC8", "up": "LIC8", "down": "LIC5"},
# },
{
"args": [
[
Component("slit_und"),
Component("slit_switch"),
Component("slit_att"),
Component("slit_kb"),
]
],
"name": "slits",
"desc": "collection of all slits",
"type": "eco.utilities.beamline:Slits",
"kwargs": {},
},
# {
# "args": [
# [Component("slit_switch"), Component("slit_att"), Component("slit_kb"),]
# ],
# "name": "slits",
# "desc": "collection of all slits",
# "type": "eco.utilities.beamline:Slits",
# "kwargs": {},
# "lazy": False,
# },
# {
# "args": [],
# "name": "thc",
# "z_und": 142,
# "desc": "High field THz Chamber",
# "type": "eco.endstations.bernina_sample_environments:High_field_thz_chamber",
# "kwargs": {"Id": "SARES23", "configuration": ["ottifant"]},
# },
# {
# "args": [],
# "name": "ocb",
# "z_und": 142,
# "desc": "Organic Crystal Breadboard",
# "type": "eco.endstations.bernina_sample_environments:Organic_crystal_breadboard",
# "kwargs": {"Id": "SARES23"},
# },
# {
# "args": [],
# "name": "eos",
# "z_und": 142,
# "desc": "electro optic sampling stages",
# "type": "eco.endstations.bernina_sample_environments:Electro_optic_sampling",
# "kwargs": {
# "Id": "SARES23",
# "pgroup": config["pgroup"],
# "diode_channels": {
# "d1": "SARES20-LSCP9-FNS:CH1:VAL_GET",
# "d2": "SARES20-LSCP9-FNS:CH2:VAL_GET",
# "diff": "SARES20-LSCP9-FNS:CH3:VAL_GET",
# },
# },
# },
{
"args": [],
"name": "dsd",
"z_und": 146,
"desc": "downstream diagnostics",
"type": "eco.xdiagnostics.dsd:DownstreamDiagnostic",
"kwargs": {},
},
]
try:
components.extend(config["components"])
print("Did append additional components!")
except:
print("Could not append components from config.")
-32
View File
@@ -1,32 +0,0 @@
from eco.elements.adjustable import AdjustableFS
from eco.motion.smaract import SmaractController
# from .config import components
# from .config import config as config_berninamesp
from ..utilities.config import Namespace
# from ..aliases import NamespaceCollection
import pyttsx3
from ..utilities.path_alias import PathAlias
import sys, os
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
# )
# 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
# namespace.append_obj(AdjustableObject, _config_bernina_dict, name="config_bernina")
+51 -4
View File
@@ -1,3 +1,4 @@
from eco.elements.detector import DetectorGet
from .assembly import Assembly
from .adjustable import AdjustableGetSet
@@ -5,10 +6,10 @@ from functools import partial
class AdjustableObject(Assembly):
def __init__(self, adjustable_dict, name=None):
def __init__(self, adjustable_dict, is_setting_children = True, name=None):
super().__init__(name=name)
self._base_dict = adjustable_dict
self.init_object()
self.init_object(is_setting_children=is_setting_children)
def set_field(self, fieldname, value):
d = self._base_dict.get_current_value()
@@ -23,7 +24,13 @@ class AdjustableObject(Assembly):
raise Exception(f"{fieldname} is not in dictionary")
return d[fieldname]
def init_object(self):
def update_base_dict(self, updatedict):
tmp = self._base_dict.get_current_value()
tmp.update(updatedict)
self._base_dict.set_target_value(tmp)
self.__init__(self._base_dict, name=self.name)
def init_object(self, is_setting_children=True):
# super().__init__(name=self.name)
for k, v in self._base_dict.get_current_value().items():
tadj = AdjustableGetSet(
@@ -38,11 +45,51 @@ class AdjustableObject(Assembly):
self._append(
AdjustableObject(tadj, name=k),
call_obj=False,
is_setting=is_setting_children,
name=ln,
is_display="recursive",
)
else:
self._append(
tadj, call_obj=False, is_setting=is_setting_children, is_display=True, name=ln
)
class DetectorObject(Assembly):
def __init__(self, detector_dict, name=None):
super().__init__(name=name)
self._base_dict = detector_dict
self.init_object()
def get_field(self, fieldname):
d = self._base_dict.get_current_value()
if fieldname not in d.keys():
raise Exception(f"{fieldname} is not in dictionary")
return d[fieldname]
def init_object(self):
# super().__init__(name=self.name)
for k, v in self._base_dict.get_current_value().items():
tdet = DetectorGet(
partial(self.get_field, k), name=k
)
if k in self.__dict__.keys():
ln = f"{k}_"
else:
ln = f"{k}"
if type(v) is dict:
self._append(
DetectorObject(tdet, name=k),
call_obj=False,
is_setting=False,
name=ln,
is_display="recursive",
)
else:
self._append(
tadj, call_obj=False, is_setting=False, is_display=True, name=ln
tdet, call_obj=False, is_setting=False, is_display=True, name=ln
)
+172 -28
View File
@@ -4,9 +4,11 @@ import time
from json import load, dump
from pathlib import Path
from threading import Thread
import itertools
import colorama
import numpy as np
import eco
from eco.aliases import Alias
from eco.devices_general.utilities import Changer
@@ -104,6 +106,60 @@ def spec_convenience(Adj):
Adj.mv = mv
Adj.mvr = mvr
def wm_elog(self, premessage=None, tags=[]):
elog = self._get_elog()
tname = self.alias.get_full_name()
value = self.get_current_value()
if premessage:
messages = [
premessage,
f"{tname} is at {value}.",
]
else:
messages = [f"{tname} is at {value}."]
self.mvr(value)
elog.post(*messages, tags=tags)
def mv_elog(self, value, premessage=None, tags=[]):
elog = self._get_elog()
tname = self.alias.get_full_name()
start = self.get_current_value()
end = value
rel_change = end - start
if premessage:
messages = [
premessage,
f"Changing {tname} from {start} by {rel_change} to {end}.",
]
else:
messages = [f"Changing {tname} from {start} by {rel_change} to {end}."]
self.mv(value)
elog.post(*messages, tags=tags)
def mvr_elog(self, value, premessage=None, tags=[]):
elog = self._get_elog()
tname = self.alias.get_full_name()
start = self.get_current_value()
end = start + value
rel_change = value
if premessage:
messages = [
premessage,
f"Changing {tname} from {start} by {rel_change} to {end}.",
]
else:
messages = [f"Changing {tname} from {start} by {rel_change} to {end}."]
self.mvr(value)
elog.post(*messages, tags=tags)
if hasattr(Adj, "wm"):
Adj.wm_elog = wm_elog
if hasattr(Adj, "mv"):
Adj.mv_elog = mv_elog
if hasattr(Adj, "mvr"):
Adj.mvr_elog = mvr_elog
def call(self, value=None):
if not value is None:
return self.mv(value)
@@ -112,6 +168,18 @@ def spec_convenience(Adj):
Adj.__call__ = call
def _get_elog(self):
if hasattr(self, "_elog") and self._elog:
return self._elog
elif hasattr(self, "__elog") and self.__elog:
return self.__elog
elif eco.ELOG:
return eco.ELOG
else:
return None
Adj._get_elog = _get_elog
return Adj
@@ -167,18 +235,31 @@ class ValueInRange:
def update_changes(Adj):
def get_position_str(start, end, value):
start = float(start)
value = float(value)
end = float(end)
s = ValueInRange(start, end, bar_width=30, unit="", fmt="1.5g").get_str(value)
return (
colorama.Style.BRIGHT
+ f"{value:1.5}".rjust(10)
+ colorama.Style.RESET_ALL
+ " "
+ s
+ 2 * "\t"
)
vals = [v if hasattr(v, "__iter__") else [v] for v in [start, end, value]]
# bars = []
bars = ""
for s, v, e in zip(*vals):
s = float(s)
v = float(v)
e = float(e)
s = ValueInRange(s, e, bar_width=30, unit="", fmt="1.5g").get_str(v)
bars = bars + (
colorama.Style.BRIGHT
+ f"{v:1.5}".rjust(10)
+ colorama.Style.RESET_ALL
+ " "
+ s
+ 2 * "\t"
)
# bars.append((
# colorama.Style.BRIGHT
# + f"{v:1.5}".rjust(10)
# + colorama.Style.RESET_ALL
# + " "
# + s
# + 2 * "\t"
# ))
return bars
def update_change(self, value, elog=None):
start = self.get_current_value()
@@ -188,14 +269,16 @@ def update_changes(Adj):
)
except TypeError:
print(f"Changing {self.name} from {start} to {value}")
# for pos in get_position_str(start, value, start):
# print(pos, end="\r")
print(get_position_str(start, value, start), end="\r")
try:
if hasattr(self, "add_value_callback"):
def cbfoo(**kwargs):
present_value = self.get_current_value()
# print(get_position_str(start, value, kwargs["value"]), end="\r")
# for pos in get_position_str(start, value, present_value):
# print(pos, end="\r")
print(get_position_str(start, value, present_value), end="\r")
cb_id = self.add_value_callback(cbfoo)
@@ -246,19 +329,37 @@ def update_changes(Adj):
def value_property(Adj, wait_for_change=True, value_name="_value"):
if wait_for_change:
def tmp(Adj, value):
Adj.set_target_value(value, hold=False).wait()
def set_target_value_wait(self, value):
try:
self.set_target_value(value, hold=False).wait()
except:
self.set_target_value(value).wait()
def get_current_value(self):
o = self.get_current_value()
if hasattr(o, "__setitem__"):
# print("overwriting output class")
class TempObj(o.__class__):
def __setitem__(oself, *args):
o.__class__.__setitem__(oself, *args)
self._set_target_value_wait(oself)
return TempObj(o)
else:
return o
Adj._set_target_value_wait = set_target_value_wait
Adj._get_current_value = get_current_value
setattr(
Adj,
value_name,
property(
Adj.get_current_value,
tmp,
Adj._get_current_value,
Adj._set_target_value_wait,
),
)
Adj.value = property(lambda self: self._value)
return Adj
@@ -270,11 +371,12 @@ def value_property(Adj, wait_for_change=True, value_name="_value"):
@tweak_option
@value_property
class DummyAdjustable:
def __init__(self, name="no_adjustable"):
def __init__(self, name="no_adjustable", limits=[-100, 100]):
self.name = name
self.alias = Alias(name)
self.current_value = 0
self.limits = tuple(limits)
def get_current_value(self):
return self.current_value
@@ -287,6 +389,12 @@ class DummyAdjustable:
target=value, parent=self, changer=changer, hold=hold, stopper=None
)
def get_limits(self):
return self.limits
def set_limits(self, lowlim, highlim):
self.limits = (lowlim, highlim)
def __repr__(self):
name = self.name
cv = self.get_current_value()
@@ -429,6 +537,7 @@ class AdjustableVirtual:
append_aliases=False,
name=None,
unit=None,
check_limits=False,
):
self.name = name
self.alias = Alias(name)
@@ -444,6 +553,7 @@ class AdjustableVirtual:
self._foo_get_current_value = foo_get_current_value
self._reset_current_value_to = reset_current_value_to
self._change_simultaneously = change_simultaneously
self._check_limits = check_limits
if reset_current_value_to:
for adj in self._adjustables:
if not hasattr(adj, "reset_current_value_to"):
@@ -457,18 +567,25 @@ class AdjustableVirtual:
vals = (vals,)
def changer(value):
if self._check_limits:
if not self.check_target_value_within_limits(value):
raise Exception(
f"Target value of virtual adjustable {self.name} is higher than limit values of {[adj.name for adj in self._adjustables]}!"
)
if self._change_simultaneously:
self._active_changers = [
adj.set_target_value(val, hold=False)
for val, adj in zip(vals, self._adjustables)
if val is not None
]
for tc in self._active_changers:
tc.wait()
else:
for val, adj in zip(vals, self._adjustables):
self._active_changers = [adj.set_target_value(val, hold=False)]
self._active_changers[0].wait()
if val is not None:
self._active_changers = [adj.set_target_value(val, hold=False)]
self._active_changers[0].wait()
def stopper():
for tc in self._active_changers:
@@ -484,6 +601,14 @@ class AdjustableVirtual:
*[adj.get_current_value() for adj in self._adjustables]
)
def check_target_value_within_limits(self, value):
in_lims = []
values = self._foo_set_target_value_current_value(value)
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))
return all(in_lims)
def reset_current_value_to(self, value):
if not self._reset_current_value_to:
raise NotImplementedError(
@@ -500,7 +625,15 @@ class AdjustableVirtual:
@tweak_option
@value_property
class AdjustableGetSet:
def __init__(self, foo_get, foo_set, precision=0, check_interval=None, name=None):
def __init__(
self,
foo_get,
foo_set,
precision=0,
check_interval=None,
cache_get_seconds=None,
name=None,
):
"""assumes a waiting setterin function, in case no check_interval parameter is supplied"""
self.alias = Alias(name)
self.name = name
@@ -508,6 +641,7 @@ class AdjustableGetSet:
self._get = foo_get
self._check_interval = check_interval
self.precision = precision
self._cache_get_seconds = cache_get_seconds
def set_and_wait(self, value):
if self._check_interval:
@@ -517,7 +651,7 @@ class AdjustableGetSet:
else:
self._set(value)
def set_target_value(self, value):
def set_target_value(self, value, hold=False):
return Changer(
target=value,
parent=self,
@@ -527,7 +661,17 @@ class AdjustableGetSet:
)
def get_current_value(self):
return self._get()
ts = time.time()
if self._cache_get_seconds and hasattr(self, "_get_cache"):
if ts - self._get_cache[0] < self._cache_get_seconds:
value = self._get_cache[1]
else:
value = self._get()
else:
value = self._get()
if self._cache_get_seconds:
self._get_cache = (ts, value)
return value
@spec_convenience
+253 -20
View File
@@ -1,8 +1,15 @@
from datetime import datetime
from inspect import isclass
import json
from pathlib import Path
from tkinter import W
from markdown import markdown
from numpy import isin
from eco.elements.protocols import Detector
# from eco.acquisition.scan import NumpyEncoder
from eco.elements.protocols import Detector, InitialisationWaitable
from ..aliases import Alias
from tabulate import tabulate
import colorama
@@ -13,6 +20,11 @@ import subprocess
from rich.progress import track
from eco import Adjustable, Detector
import eco
_initializing_assemblies = []
class Collection:
def __init__(self, name=None):
@@ -60,7 +72,7 @@ class Collection:
class Assembly:
def __init__(self, name=None, parent=None, is_alias=True):
def __init__(self, name=None, parent=None, is_alias=True, elog=None):
self.name = name
self.alias = Alias(name, parent=parent)
# self.settings = []
@@ -71,6 +83,10 @@ class Assembly:
self.view_toplevel_only = []
if memory.global_memory_dir:
self.memory = memory.Memory(self)
if elog:
self.__elog = elog
else:
self.__class__.__elog = property(lambda dum: ELOG)
def _append(
self,
@@ -83,9 +99,16 @@ class Assembly:
is_alias=True,
view_toplevel_only=True,
call_obj=True,
append_property_with_name=False,
**kwargs,
):
if call_obj and callable(foo_obj_init):
if isinstance(foo_obj_init, Adjustable) and not isclass(foo_obj_init):
self.__dict__[name] = foo_obj_init
elif isinstance(foo_obj_init, Detector) and not isclass(foo_obj_init):
self.__dict__[name] = foo_obj_init
elif isinstance(foo_obj_init, Assembly) and not isclass(foo_obj_init):
self.__dict__[name] = foo_obj_init
elif call_obj and callable(foo_obj_init):
self.__dict__[name] = foo_obj_init(*args, **kwargs, name=name)
else:
self.__dict__[name] = foo_obj_init
@@ -93,6 +116,17 @@ 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 is_setting == "auto":
is_setting = isinstance(self.__dict__[name], Adjustable)
if is_setting:
@@ -110,11 +144,13 @@ class Assembly:
if view_toplevel_only:
self.view_toplevel_only.append(self.__dict__[name])
def get_status(self, base="self", verbose=True):
def get_status(self, base="self", verbose=True, channeltypes=None):
if base == "self":
base = self
settings = {}
settings_channels = {}
status = {}
status_channels = {}
nodet = []
geterror = []
for ts in track(
@@ -129,11 +165,71 @@ class Assembly:
# else:
if hasattr(ts, "get_current_value"):
try:
settings[ts.alias.get_full_name(base=base)] = ts.get_current_value()
if (not channeltypes) or (ts.alias.channeltype in channeltypes):
settings[
ts.alias.get_full_name(base=base)
] = ts.get_current_value()
try:
settings_channels[
ts.alias.get_full_name(base=base)
] = ts.alias.channel
except:
pass
except:
geterror.append(ts.alias.get_full_name(base=base))
else:
nodet.append(ts.alias.get_full_name(base=base))
# with ThreadPoolExecutor(max_workers=max_workers) as exc:
# list(
# progress.track(
# exc.map(
# lambda name: self.init_name(
# name, verbose=verbose, raise_errors=raise_errors
# ),
# self.all_names
# - self.initialized_names
# - set(exclude_names),
# ),
# description="Initializing ...",
# total=len(
# self.all_names - self.initialized_names - set(exclude_names)
# ),
# transient=True,
# )
# )
def get_stat_one_assembly(ts):
if hasattr(ts, "get_current_value"):
try:
if (not channeltypes) or (ts.alias.channeltype in channeltypes):
status[
ts.alias.get_full_name(base=base)
] = ts.get_current_value()
try:
status_channels[
ts.alias.get_full_name(base=base)
] = ts.alias.channel
except:
pass
except:
geterror.append(ts.alias.get_full_name(base=base))
else:
nodet.append(ts.alias.get_full_name(base=base))
# with ThreadPoolExecutor(max_workers=max_workers) as exc:
# list(
# progress.track(
# exc.map(
# get_stat_one_assembly,
# self.status_collection.get_list(),
# ),
# description="Getting status...",
# total=len(self.status_collection.get_list()),
# transient=True,
# )
# )
for ts in track(
self.status_collection.get_list(),
transient=True,
@@ -146,7 +242,16 @@ class Assembly:
# else:
if hasattr(ts, "get_current_value"):
try:
status[ts.alias.get_full_name(base=base)] = ts.get_current_value()
if (not channeltypes) or (ts.alias.channeltype in channeltypes):
status[
ts.alias.get_full_name(base=base)
] = ts.get_current_value()
try:
status_channels[
ts.alias.get_full_name(base=base)
] = ts.alias.channel
except:
pass
except:
geterror.append(ts.alias.get_full_name(base=base))
else:
@@ -159,7 +264,12 @@ class Assembly:
"Retrieved error while running get_current_value from: "
+ ", ".join(geterror)
)
return {"settings": settings, "status": status}
return {
"settings": settings,
"status": status,
"settings_channels": settings_channels,
"status_channels": status_channels,
}
def status(self, get_string=False):
stat = self.get_status()
@@ -194,38 +304,161 @@ class Assembly:
s = tabulate([[name, value] for name, value in stat_filt[stat_field].items()])
return s
def get_display_str(self):
def get_display_str(self, tablefmt="simple"):
main_name = self.name
stats = self.display_collection()
# stats_dict = {}
tab = []
for to in stats:
name = to.alias.get_full_name(base=self)
value = to.get_current_value()
is_adjustable = isinstance(to, Adjustable)
if is_adjustable:
typechar = "✏️"
else:
typechar = "👁️"
is_detector = isinstance(to, Detector)
typechar = ""
if is_adjustable:
typechar += "✏️"
elif is_detector:
typechar += "👁️"
if hasattr(to, "settings_collection"):
typechar += ""
try:
value = to.get_current_value()
except AttributeError:
if hasattr(to, "settings_collection"):
value = "\x1b[3mhas lower level items\x1b[0m"
if isinstance(value, Enum):
value = f"{value.value} ({value.name})"
try:
unit = to.unit()
unit = to.unit.get_current_value()
except:
unit = None
tab.append([".".join([main_name, name]), value, unit, typechar])
s = tabulate(tab)
try:
description = to.description.get_current_value()
except:
description = None
tab.append(
[".".join([main_name, name]), value, unit, typechar, description]
)
s = tabulate(tab, tablefmt=tablefmt)
return s
def status_to_elog(
self,
text="",
text_encoding="markdown",
auto_title=True,
attach_display=True,
attach_status_file=True,
):
elog = self._get_elog()
message = ""
files = []
if auto_title:
message += markdown(f"#### Status {self.alias.get_full_name()}")
if text:
if text_encoding == "markdown":
message += markdown(text)
if attach_display:
message += self.get_display_str(tablefmt="html")
if attach_status_file:
stat = self.get_status()
tmppath = Path("/tmp")
filepath = tmppath / Path(
f"status_{self.alias.get_full_name}_{datetime.now().isoformat()}.json"
)
with open(filepath, "w") as f:
# json.dump(stat, f, cls=NumpyEncoder, indent=4)
json.dump(stat, f, indent=4)
files.append(filepath)
elog.post(
message,
*files,
text_encoding="html",
)
# tags=[],
def __repr__(self):
label = self.alias.get_full_name() + " status\n"
return label + self.get_display_str()
def _run_cmd(self, line):
print(f"Starting following commandline silently:\n" + line)
with open(os.devnull, "w") as FNULL:
subprocess.Popen(line, shell=True, stdout=FNULL, stderr=subprocess.STDOUT)
# def _wait_for_initialisation(self, timeout=2):
# for ton, to in self.__dict__.items():
# try:
# iswaitable = isinstance(to, InitialisationWaitable)
# if iswaitable:
# to._wait_for_initialisation()
# except:
# pass
def _wait_for_initialisation(self):
for item in self.status_collection.get_list():
if isinstance(item, Assembly) and (item in _initializing_assemblies):
continue
if isinstance(item, InitialisationWaitable):
if isinstance(item, Assembly):
_initializing_assemblies.append(item)
item._wait_for_initialisation()
def _run_cmd(self, line, silent=True):
if silent:
print(f"Starting following commandline silently:\n" + line)
with open(os.devnull, "w") as FNULL:
subprocess.Popen(
line, shell=True, stdout=FNULL, stderr=subprocess.STDOUT
)
else:
subprocess.Popen(line, shell=True)
def _get_elog(self):
if hasattr(self, "_elog") and self._elog:
return self._elog
elif hasattr(self, "__elog") and self.__elog:
return self.__elog
elif eco.ELOG:
return eco.ELOG
else:
return None
import epics.pv
import time
class Monitor:
def __init__(self, assembly):
self.assembly = assembly
self.data = {}
self.callbacks = {}
self.pvs = {}
def start_monitoring(self):
o = self.assembly.get_status(channeltypes=["CA"])
# self.data = {k: [v] for k, v in o["status"].items()}
self.channelkeys = {v: k for k, v in o["status_channels"].items()}
self.pvs = {k: epics.pv.PV(v) for k, v in o["status_channels"].items()}
# for cik, civ in epics.pv._PVcache_.items():
# if cik[0] in o["status_channels"].keys():
# tname = self.channelkeys[cik[0]]
# tpv = civ
for tname, tpv in self.pvs.items():
self.callbacks[tname] = tpv.add_callback(self.append)
def stop_monitoring(self):
for tname in self.pvs:
self.pvs[tname].remove_callback(index=self.callbacks[tname])
def append(self, pvname=None, value=None, timestamp=None, **kwargs):
if not (self.channelkeys[pvname] in self.data):
self.data[self.channelkeys[pvname]] = []
ts_local = time.time()
self.data[self.channelkeys[pvname]].append(
{"value": value, "timestamp": timestamp, "timestamp_local": ts_local}
)
class Assembly_old:
+16 -2
View File
@@ -1,5 +1,7 @@
from eco.elements.adjustable import AdjustableMemory
from eco.elements.assembly import Assembly
from eco.aliases import Alias
import time
def value_property(Det, value_name="_value"):
@@ -65,11 +67,23 @@ class DetectorVirtual(Assembly):
@call_convenience
@value_property
class DetectorGet:
def __init__(self, foo_get, name=None):
def __init__(self, foo_get, cache_get_seconds=None, name=None):
""" """
self.alias = Alias(name)
self.name = name
self._get = foo_get
self._cache_get_seconds = cache_get_seconds
def get_current_value(self):
return self._get()
ts = time.time()
if self._cache_get_seconds and hasattr(self,'_get_cache'):
if ts - self._get_cache[0] < self._cache_get_seconds:
value = self._get_cache[1]
else:
value = self._get()
else:
value = self._get()
if self._cache_get_seconds:
self._get_cache= (ts,value)
return value
+84 -16
View File
@@ -5,6 +5,10 @@ from ..utilities.keypress import KeyPress
from tabulate import tabulate
import sys, colorama
from inspect import getargspec
import eco
from ansi2html import Ansi2HTMLConverter
conv = Ansi2HTMLConverter()
global_memory_dir = None
@@ -60,8 +64,23 @@ class Memory:
# print(self.get_memory_difference_str(index))
self.recall(memory_index=index)
def _get_elog(self):
if hasattr(self, "_elog") and self._elog:
return self._elog
elif hasattr(self, "__elog") and self.__elog:
return self.__elog
elif eco.ELOG:
return eco.ELOG
else:
return None
def memorize(
self, message=None, attributes={}, force_message=True, preset_varname=None
self,
message=None,
attributes={},
force_message=True,
preset_varname=None,
to_elog=True,
):
self.setup_path()
stat_now = self.obj_parent.get_status(base=self.obj_parent)
@@ -84,6 +103,15 @@ class Memory:
tmp = AdjustableFS(self.dir / Path(key + ".json"))
tmp(stat_now)
self._memories(mem)
print(f"Saved memory for {self.obj_parent.alias.get_full_name()}: {message}")
print(f"memory file: {tmp.file_path.as_posix()}")
if to_elog:
elog = self._get_elog()
elog.post(
f"Saved memory for {self.obj_parent.alias.get_full_name()}: {message}",
tmp.file_path,
text_encoding="markdown",
)
def get_memory(self, input_obj=None, index=None, key=None, filter_existing=True):
if not input_obj is None:
@@ -116,6 +144,15 @@ class Memory:
else:
return mem_full
def clear_memory(self, index=None, key=None):
if not (index is None):
key = list(self._memories().keys())[index]
if key is None:
raise Exception("memory key or index to be deleted needs to be specified!")
mem = self._memories.get_current_value()
mem.pop(key)
self._memories.set_target_value(mem).wait()
def recall(
self,
memory_index=None,
@@ -125,8 +162,26 @@ class Memory:
show_changes_only=True,
set_changes_only=True,
check_limits=True,
change_serially=False,
force=False,
):
"""Recall a memory_index, from an index in the default meory list, from a
dictionary containing the memory information, or from a path to a file containing the memory.
Args:
memory_index (integer, optional): index in memory list. Defaults to None.
input_obj (dictionary or string, optional): direct passing memory as dict or s filepath (string) to the memory file. Defaults to None.
key (string, optional): key of memory in memory list (if not defined by the index). Defaults to None.
wait (bool, optional): Wait for the memory recall changes to complete. Defaults to True.
show_changes_only (bool, optional): in rpreview show only changes that are different to present setting. Defaults to True.
set_changes_only (bool, optional): setting only the changes that changed. Defaults to True.
check_limits (bool, optional): check limits before changing. Defaults to True.
change_serially (bool, optional): change and wait each change after each other, not simultaneously. Defaults to False.
force (bool, optional): force the change without previous preview. Defaults to False.
Returns:
_type_: _description_
"""
# if input_obj:
mem = self.get_memory(
index=memory_index,
@@ -160,6 +215,8 @@ class Memory:
changes.append(to.set_target_value(val, check=check_limits))
else:
changes.append(to.set_target_value(val))
if change_serially:
changes[-1].wait()
if wait:
for change in changes:
change.wait()
@@ -171,7 +228,12 @@ class Memory:
...
def get_memory_difference_str(
self, memory, select=None, ask_select=True, show_changes_only=False
self,
memory,
select=None,
ask_select=True,
show_changes_only=False,
tablefmt="plain",
):
# mem = self.get_memory(index=memory_index)
mem = memory
@@ -187,12 +249,15 @@ class Memory:
tselstr = " "
if present_value == recall_value:
changed = False
comp_indicator = (
colorama.Fore.GREEN
+ colorama.Style.BRIGHT
+ "=="
+ colorama.Style.RESET_ALL
)
if tablefmt == "html":
comp_indicator = "=="
else:
comp_indicator = (
colorama.Fore.GREEN
+ colorama.Style.BRIGHT
+ "=="
+ colorama.Style.RESET_ALL
)
else:
changed = True
if not tsel:
@@ -202,14 +267,18 @@ class Memory:
tdiff = f"{recall_value - present_value:+g}"
except TypeError:
tdiff = "special"
comp_indicator = (
colorama.Fore.RED
+ colorama.Style.BRIGHT
+ f"{tdiff:s}"
+ colorama.Style.RESET_ALL
)
if tablefmt == "html":
comp_indicator = f"{tdiff:s}"
else:
comp_indicator = (
colorama.Fore.RED
+ colorama.Style.BRIGHT
+ f"{tdiff:s}"
+ colorama.Style.RESET_ALL
)
if show_changes_only and (not changed):
continue
table.append([n, tselstr, key, present_value, comp_indicator, recall_value])
if len(table) == 0:
@@ -225,12 +294,12 @@ class Memory:
"memory",
],
colalign=("decimal", "center", "left", "decimal", "center", "decimal"),
tablefmt=tablefmt,
)
def select_from_memory(
self, input_obj=None, key=None, memory_index=None, show_changes_only=True
):
mem = self.get_memory(input_obj=input_obj, key=key, index=memory_index)
rec = mem["settings"]
k = KeyPress()
@@ -385,7 +454,6 @@ class Preset:
return s
def __repr__(self):
return self.__str__()
+12
View File
@@ -16,3 +16,15 @@ class Adjustable(Protocol):
class Detector(Protocol):
def get_current_value(self):
...
@runtime_checkable
class ValueUpdateMonitorable(Protocol):
def get_current_value_callback(self):
...
@runtime_checkable
class InitialisationWaitable(Protocol):
def _wait_for_initialisation(self):
...
+111 -58
View File
@@ -14,6 +14,7 @@ from ..elements.assembly import Assembly
from ..detector.jungfrau import Jungfrau
from .kappa_conversion import kappa2you, you2kappa
import numpy as np
from ..utilities.recspace import Crystals, DiffGeometryYou
def addMotorRecordToSelf(self, name=None, Id=None):
@@ -32,6 +33,7 @@ class GPS(Assembly):
configuration=["base"],
alias_namespace=None,
fina_hex_angle_offset=None,
diffcalc=False,
):
super().__init__(name=name)
self.pvname = pvname
@@ -136,7 +138,6 @@ class GPS(Assembly):
)
if "phi_hex" in self.configuration:
### motors PI hexapod ###
if fina_hex_angle_offset:
fina_hex_angle_offset = Path(fina_hex_angle_offset).expanduser()
@@ -274,6 +275,15 @@ class GPS(Assembly):
unit="deg",
)
if diffcalc:
self._append(
Crystals,
diffractometer_you=self,
name="diffcalc",
is_setting=False,
is_display=False,
)
def gui(self, guiType="xdm"):
"""Adjustable convention"""
cmd = ["caqtdm", "-macro"]
@@ -400,8 +410,12 @@ class XRDYou(Assembly):
name=None,
Id=None,
configuration=["base"],
diff_detector=None,
detectors=None,
invert_kappa_ellbow=True,
pgroup_adj=None,
configsjf_adj=None,
fina_hex_angle_offset=None,
diffcalc=True,
):
"""X-ray diffractometer platform in AiwssFEL Bernina.\
<configuration> : list of elements mounted on
@@ -566,7 +580,7 @@ class XRDYou(Assembly):
try:
self._append(
MotorRecord_new,
Id + ":MOT_TBL_RY",
Id + ":MOT_TBL_RZ",
name="rzhl",
is_setting=True,
is_display=True,
@@ -580,62 +594,75 @@ class XRDYou(Assembly):
self._append(
MotorRecord_new,
Id + ":MOT_HEX_TX",
name="tphi",
name="transl_eta",
is_setting=True,
is_display=True,
)
self._append(
MotorRecord_new,
Id + ":MOT_HEX_RX",
name="phi",
name="eta",
is_setting=True,
is_display=True,
)
if "phi_hex" in self.configuration:
### motors PI hexapod ###
append_object_to_object(
self,
AdjustablePv,
"SARES20-HEX_PI:SET-POSI-X",
pvreadbackname="SARES20-HEX_PI:POSI-X",
name="xhex",
)
append_object_to_object(
self,
AdjustablePv,
"SARES20-HEX_PI:SET-POSI-Y",
pvreadbackname="SARES20-HEX_PI:POSI-Y",
name="yhex",
)
append_object_to_object(
self,
AdjustablePv,
"SARES20-HEX_PI:SET-POSI-Z",
pvreadbackname="SARES20-HEX_PI:POSI-Z",
name="zhex",
)
append_object_to_object(
self,
AdjustablePv,
"SARES20-HEX_PI:SET-POSI-U",
pvreadbackname="SARES20-HEX_PI:POSI-U",
name="uhex",
)
append_object_to_object(
self,
AdjustablePv,
"SARES20-HEX_PI:SET-POSI-V",
pvreadbackname="SARES20-HEX_PI:POSI-V",
name="vhex",
)
append_object_to_object(
self,
AdjustablePv,
"SARES20-HEX_PI:SET-POSI-W",
pvreadbackname="SARES20-HEX_PI:POSI-W",
name="whex",
if fina_hex_angle_offset:
fina_hex_angle_offset = Path(fina_hex_angle_offset).expanduser()
self._append(
HexapodPI,
"SARES20-HEX_PI",
name="hex",
fina_angle_offset=fina_hex_angle_offset,
is_setting=True,
is_display="recursive",
)
# if "phi_hex" in self.configuration:
# ### motors PI hexapod ###
# append_object_to_object(
# self,
# AdjustablePv,
# "SARES20-HEX_PI:SET-POSI-X",
# pvreadbackname="SARES20-HEX_PI:POSI-X",
# name="xhex",
# )
# append_object_to_object(
# self,
# AdjustablePv,
# "SARES20-HEX_PI:SET-POSI-Y",
# pvreadbackname="SARES20-HEX_PI:POSI-Y",
# name="yhex",
# )
# append_object_to_object(
# self,
# AdjustablePv,
# "SARES20-HEX_PI:SET-POSI-Z",
# pvreadbackname="SARES20-HEX_PI:POSI-Z",
# name="zhex",
# )
# append_object_to_object(
# self,
# AdjustablePv,
# "SARES20-HEX_PI:SET-POSI-U",
# pvreadbackname="SARES20-HEX_PI:POSI-U",
# name="uhex",
# )
# append_object_to_object(
# self,
# AdjustablePv,
# "SARES20-HEX_PI:SET-POSI-V",
# pvreadbackname="SARES20-HEX_PI:POSI-V",
# name="vhex",
# )
# append_object_to_object(
# self,
# AdjustablePv,
# "SARES20-HEX_PI:SET-POSI-W",
# pvreadbackname="SARES20-HEX_PI:POSI-W",
# name="whex",
# )
if "kappa" in self.configuration:
self._append(
@@ -757,14 +784,28 @@ class XRDYou(Assembly):
unit="deg",
)
if diff_detector:
if detectors:
for tdet in detectors:
tname = tdet["name"]
tid = tdet["jf_id"]
self._append(
Jungfrau,
tid,
name=tname,
is_setting=False,
is_display=False,
pgroup_adj=pgroup_adj,
config_adj=configsjf_adj,
view_toplevel_only=True,
)
if diffcalc:
self._append(
Jungfrau,
diff_detector["jf_id"],
name="det_diff",
Crystals,
diffractometer_you=self,
name="diffcalc",
is_setting=False,
is_display=False,
view_toplevel_only=True,
)
def get_adjustable_positions_str(self):
@@ -804,10 +845,12 @@ class XRDYou(Assembly):
kappa_angle=60,
degrees=True,
bernina_kappa=True,
invert_elbow=False,
invert_elbow=None,
):
"""tool to convert from you definition angles to kappa angles, in
particular the bernina kappa where the"""
if invert_elbow is None:
invert_elbow = self.invert_kappa_ellbow
if bernina_kappa:
eta = -eta
phi = -phi
@@ -828,16 +871,16 @@ class XRDYou(Assembly):
if bernina_kappa:
eta_k = eta_k - np.pi / 2
kappa = -kappa
if False:
if True:
def flip_ang(ang):
if 1 < abs(ang // np.pi):
if 2 <= abs(ang // np.pi):
return ang - np.sign(ang) * np.pi * 2
else:
return ang
# phi_k = flip_ang(phi_k)
phi_k = phi_k + np.pi * 2
phi_k = flip_ang(phi_k)
# phi_k = phi_k + np.pi * 2
eta_k = flip_ang(eta_k)
kappa = flip_ang(kappa)
if degrees:
@@ -852,8 +895,10 @@ class XRDYou(Assembly):
kappa_angle=60,
degrees=True,
bernina_kappa=True,
invert_elbow=False,
invert_elbow=None,
):
if invert_elbow is None:
invert_elbow = self.invert_kappa_ellbow
if degrees:
eta_k, kappa, phi_k, kappa_angle = np.deg2rad(
[eta_k, kappa, phi_k, kappa_angle]
@@ -927,7 +972,14 @@ class XRDYou(Assembly):
class XRD(Assembly):
def __init__(self, name=None, Id=None, configuration=["base"], diff_detector=None):
def __init__(
self,
name=None,
Id=None,
configuration=["base"],
diff_detector=None,
pgroup_adj=None,
):
"""X-ray diffractometer platform in AiwssFEL Bernina.\
<configuration> : list of elements mounted on
the plaform, options are kappa, nutable, hlgonio, polana"""
@@ -1152,6 +1204,7 @@ class XRD(Assembly):
name="det_diff",
is_setting=False,
is_display=True,
pgroup_adj=pgroup_adj,
view_toplevel_only=True,
)
+21
View File
@@ -0,0 +1,21 @@
from eco import Assembly
from eco.detector.jungfrau import Jungfrau
class DetectorRobot(Assembly):
def __init__(
self,
JF_detector_id=None,
JF_detector_name="det_diff",
name="robot",
pgroup_adj=None,
config_adj=None,
):
super().__init__(name=name)
self._append(
Jungfrau,
JF_detector_id,
pgroup_adj=pgroup_adj,
config_adj=config_adj,
name=JF_detector_name,
)
+99 -55
View File
@@ -1,15 +1,14 @@
from eco.devices_general.powersockets import MpodChannel
import sys
sys.path.append("..")
from ..devices_general.motors import MotorRecord, SmaractStreamdevice
from ..devices_general.smaract import SmarActRecord
from ..devices_general.motors import MotorRecord, SmaractRecord
from ..epics.adjustable import AdjustablePv
import numpy as np
from epics import PV
from ..aliases import Alias, append_object_to_object
from time import sleep
import escape.parse.swissfel as sf
from ..bernina import config
import pylab as plt
import escape
from pathlib import Path
@@ -33,20 +32,19 @@ def addMotorRecordToSelf(self, name=None, Id=None):
def addSmarActRecordToSelf(self, Id=None, name=None, **kwargs):
self.__dict__[name] = SmaractStreamdevice(Id, name=name, **kwargs)
self.__dict__[name] = SmaractRecord(Id, name=name, **kwargs)
self.alias.append(self.__dict__[name].alias)
class High_field_thz_chamber(Assembly):
def __init__(self, name=None, Id=None, alias_namespace=None, configuration=[]):
def __init__(self, name=None, alias_namespace=None, configuration=[], illumination_mpod = None, helium_control_valve=None):
super().__init__(name=name)
self.Id = Id
self.name = name
self.alias = Alias(name)
self.par_out_pos = [35, -9.5]
self.motor_configuration = {
"rx": {
"id": "-ESB13",
# "id": "SARES23-USR:MOT_13",
"id": "SARES23-USR:MOT_6",
"pv_descr": "Motor7:1 THz Chamber Rx",
"type": 2,
"sensor": 1,
@@ -55,7 +53,8 @@ class High_field_thz_chamber(Assembly):
"kwargs": {"accuracy": 0.01},
},
"x": {
"id": "-ESB14",
# "id": "SARES23-USR:MOT_14",
"id": "SARES23-USR:MOT_15",
"pv_descr": "Motor7:2 THz Chamber x ",
"type": 1,
"sensor": 0,
@@ -63,7 +62,8 @@ class High_field_thz_chamber(Assembly):
"home_direction": "back",
},
"z": {
"id": "-ESB10",
# "id": "SARES23-USR:MOT_10",
"id": "SARES23-LIC:MOT_16",
"pv_descr": "Motor6:1 THz Chamber z ",
"type": 1,
"sensor": 0,
@@ -71,7 +71,8 @@ class High_field_thz_chamber(Assembly):
"home_direction": "back",
},
"ry": {
"id": "-ESB11",
# "id": "SARES23-USR:MOT_11",
"id": "SARES23-LIC:MOT_15",
"pv_descr": "Motor6:2 THz Chamber Ry",
"type": 2,
"sensor": 1,
@@ -79,7 +80,8 @@ class High_field_thz_chamber(Assembly):
"home_direction": "back",
},
"rz": {
"id": "-ESB12",
# "id": "SARES23-USR:MOT_12",
"id": "SARES23-USR:MOT_4",
"pv_descr": "Motor6:3 THz Chamber Rz",
"type": 2,
"sensor": 1,
@@ -91,33 +93,41 @@ class High_field_thz_chamber(Assembly):
### lakeshore temperatures ####
self._append(
AdjustablePv,
pvsetname="SARES20-CRYO:TEMP.VAL",
pvreadbackname="SARES20-CRYO:TEMP_RBV",
pvsetname="SARES20-LS336:LOOP1_SP",
pvreadbackname="SARES20-LS336:A_RBV",
accuracy=0.1,
name="temp_sample",
is_setting=False,
)
self._append(
AdjustablePv,
pvsetname="SARES20-CRYO:TEMP-B",
pvreadbackname="SARES20-CRYO:TEMP-B_RBV",
pvsetname="SARES20-LS336:LOOP2_SP",
pvreadbackname="SARES20-LS336:B_RBV",
accuracy=0.1,
name="temp_coldfinger",
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():
if "kwargs" in config.keys():
tmp_kwargs = config["kwargs"]
else:
tmp_kwargs = {}
self._append(
SmaractStreamdevice,
pvname=Id + config["id"],
SmaractRecord,
pvname=config["id"],
name=name,
is_setting=True,
**tmp_kwargs,
)
self._append(
AdjustableFS,
@@ -160,6 +170,29 @@ class High_field_thz_chamber(Assembly):
is_display=True,
is_setting=True,
)
if illumination_mpod:
for illu in illumination_mpod:
self._append(MpodChannel,illu['pvbase'], illu['channel_number'], module_string=illu['module_string'], name=illu['name'])
if helium_control_valve:
self._append(MpodChannel,helium_control_valve['pvbase'], helium_control_valve['channel_number'], module_string=helium_control_valve['module_string'], name="_helium_valve_mpod_ch", is_display=True, is_setting=True)
def get_valve(voltage):
if voltage < 2.9:
val=0
elif voltage > 5.5:
val=100
else:
val = (voltage-2.9)/(5.5-2.9)*100
return val
def set_valve(val):
if val <1:
voltage = .5
else:
voltage = val*(5.5-2.9)/100+2.9
return voltage
self._append(AdjustableVirtual, [self._helium_valve_mpod_ch.voltage], get_valve, set_valve, name=helium_control_valve["name"], is_display=True, is_setting=False)
def moveout(self):
change_in_pos = str(
@@ -187,12 +220,12 @@ class High_field_thz_chamber(Assembly):
def set_stage_config(self):
for name, config in self.motor_configuration.items():
mot = self.__dict__[name]
mot.caqtdm_name(config["pv_descr"])
mot.stage_type(config["type"])
mot.description(config["pv_descr"])
#mot.stage_type(config["type"])
mot.sensor_type(config["sensor"])
mot.speed(config["speed"])
mot.max_frequency(config["speed"])
sleep(0.5)
mot.calibrate_sensor(1)
mot.calibrate_sensor()
def home_smaract_stages(self, stages=None):
if stages == None:
@@ -209,25 +242,27 @@ class High_field_thz_chamber(Assembly):
)
sleep(1)
if config["home_direction"] == "back":
mot.home_backward(1)
while mot.status_channel().value == 7:
mot.home_reverse(1)
sleep(.5)
while not mot.flags.motion_complete():
sleep(1)
if mot.is_homed() == 0:
if not mot.flags.is_homed():
print(
"Homing failed, try homing {} in forward direction".format(name)
)
mot.home_forward(1)
elif config["home_direction"] == "forward":
mot.home_forward(1)
while mot.status_channel().value == 7:
sleep(.5)
while not mot.flags.motion_complete():
sleep(1)
if mot.is_homed() == 0:
if not mot.flags.is_homed():
print(
"Homing failed, try homing {} in backward direction".format(
name
)
)
mot.home_backward(1)
mot.home_reverse(1)
def calc_otti(
self, otti_nu=None, otti_del=None, otti_det=None, plotit=True, **kwargs
@@ -255,40 +290,45 @@ class Organic_crystal_breadboard(Assembly):
self.alias = Alias(name)
self.motor_configuration = {
"mirr2_x": {
"id": "-LIC17",
"mir_x": {
# "id": "-LIC17",
"id": "-USR:MOT_8",
"pv_descr": "Motor8:2 THz mirror x ",
"type": 1,
"sensor": 13,
"speed": 250,
"home_direction": "back",
},
"mirr2_rz": {
"id": "-LIC18",
"mir_rz": {
# "id": "-LIC18",
"id": "-USR:MOT_9",
"pv_descr": "Motor8:3 THz mirror rz ",
"type": 1,
"sensor": 13,
"speed": 250,
"home_direction": "back",
},
"mirr2_ry": {
"id": "-ESB1",
"mir_ry": {
# "id": "-ESB1",
"id": "-LIC:MOT_18",
"pv_descr": "Motor3:1 THz mirror ry ",
"type": 2,
"sensor": 1,
"speed": 250,
"home_direction": "forward",
},
"mirr2_z": {
"id": "-LIC16",
"mir_z": {
# "id": "-LIC16",
"id": "-USR:MOT_7",
"pv_descr": "Motor8:1 THz mirror z",
"type": 1,
"sensor": 13,
"speed": 250,
"home_direction": "back",
},
"par2_x": {
"id": "-ESB3",
"par_x": {
# "id": "-ESB3",
"id": "-LIC:MOT_17",
"pv_descr": "Motor3:3 THz parabola2 x",
"type": 1,
"sensor": 0,
@@ -296,39 +336,43 @@ class Organic_crystal_breadboard(Assembly):
"home_direction": "back",
},
"delaystage_thz": {
"id": "-ESB18",
# "id": "-ESB18",
"id": "-USR:MOT_1",
"pv_descr": "Motor8:3 NIR delay stage",
"type": 1,
"sensor": 0,
"speed": 100,
"home_direction": "back",
},
"nir_mirr1_ry": {
"id": "-ESB17",
"nir_m1_ry": {
# "id": "-ESB17",
"id": "-USR:MOT_3",
"pv_descr": "Motor8:2 near IR mirror 1 ry",
"type": 2,
"sensor": 1,
"speed": 250,
"home_direction": "back",
},
"nir_mirr1_rx": {
"id": "-ESB16",
"nir_m1_rx": {
"id": "-USR:MOT_16",
"pv_descr": "Motor8:1 near IR mirror 1 rx",
"type": 2,
"sensor": 1,
"speed": 250,
"home_direction": "back",
},
"nir_mirr2_ry": {
"id": "-ESB9",
"nir_m2_ry": {
# "id": "-ESB9",
"id": "-USR:MOT_14",
"pv_descr": "Motor5:3 near IR mirror 2 ry",
"type": 2,
"sensor": 1,
"speed": 250,
"home_direction": "back",
},
"nir_mirr2_rx": {
"id": "-ESB4",
"nir_m2_rx": {
# "id": "-USR:MOT_4",
"id": "-USR:MOT_12",
"pv_descr": "Motor4:1 near IR mirror 2 rx",
"type": 1,
"sensor": 13,
@@ -336,7 +380,7 @@ class Organic_crystal_breadboard(Assembly):
"home_direction": "back",
},
"crystal": {
"id": "-ESB2",
"id": "-USR:MOT_2",
"pv_descr": "Motor3:2 crystal rotation",
"type": 2,
"sensor": 1,
@@ -344,7 +388,7 @@ class Organic_crystal_breadboard(Assembly):
"home_direction": "back",
},
"wp": {
"id": "-ESB7",
"id": "-USR:MOT_7",
"pv_descr": "Motor5:1 waveplate rotation",
"type": 2,
"sensor": 1,
@@ -359,7 +403,7 @@ class Organic_crystal_breadboard(Assembly):
### smaract motors ###
for name, config in self.motor_configuration.items():
self._append(
SmaractStreamdevice,
SmaractRecord,
pvname=Id + config["id"],
name=name,
is_setting=True,
+67 -11
View File
@@ -1,6 +1,8 @@
import time
from epics import PV
from ..elements.adjustable import AdjustableFS, AdjustableVirtual
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
from ..epics.detector import DetectorPvData
from time import sleep
from ..aliases import append_object_to_object, Alias
from scipy.spatial.transform import Rotation
@@ -24,80 +26,119 @@ class Hexapod_PI:
for i in "RST"
]
class AdjustablePiHex(AdjustablePv):
def __init__(self, pvname=None, pvreadbackname=None, accuracy=None, unit=None, name=None):
super().__init__(pvname, pvreadbackname=pvreadbackname, accuracy=accuracy, unit=unit, name=name)
self.limit_high = AdjustableFS(f'/sf/bernina/config/eco/reference_values/hex_pi_{name}_limit_high.json', default_value=0)
self.limit_low = AdjustableFS(f'/sf/bernina/config/eco/reference_values/hex_pi_{name}_limit_low.json', default_value=0)
def change(self, value):
if self.limit_low:
if value < self.limit_low():
raise Exception(
f"Target value of {self.name} is smaller than limit value!"
)
if self.limit_high:
if self.limit_high() < value:
raise Exception(
f"Target value of {self.name} is higher than limit value!"
)
self._pv.put(value)
time.sleep(0.1)
while self.get_change_done() == 0:
time.sleep(0.1)
def get_limits(self):
return (self.limit_low(), self.limit_high())
def set_limits(self, limit_low, limit_high):
self.limit_low(limit_low)
self.limit_high(limit_high)
class HexapodPI(Assembly):
def __init__(self, pvname, name=None, fina_angle_offset=None):
super().__init__(name=name)
self.pvname = pvname
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-POSI-X",
pvreadbackname=self.pvname + ":POSI-X",
accuracy=0.001,
unit="mm",
name="x_raw",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-POSI-Y",
pvreadbackname=self.pvname + ":POSI-Y",
accuracy=0.001,
unit="mm",
name="y_raw",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-POSI-Z",
pvreadbackname=self.pvname + ":POSI-Z",
accuracy=0.001,
unit="mm",
name="z_raw",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-POSI-U",
pvreadbackname=self.pvname + ":POSI-U",
accuracy=0.001,
unit="deg",
name="rx_raw",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-POSI-V",
pvreadbackname=self.pvname + ":POSI-V",
accuracy=0.001,
unit="deg",
name="ry_raw",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-POSI-W",
pvreadbackname=self.pvname + ":POSI-W",
accuracy=0.001,
unit="deg",
name="rz_raw",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-PIVOT-R",
pvreadbackname=self.pvname + ":PIVOT-R",
accuracy=0.001,
unit="mm",
name="pivot_x",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-PIVOT-S",
pvreadbackname=self.pvname + ":PIVOT-S",
accuracy=0.001,
unit="mm",
name="pivot_y",
is_setting=True,
)
self._append(
AdjustablePv,
AdjustablePiHex,
self.pvname + ":SET-PIVOT-T",
pvreadbackname=self.pvname + ":PIVOT-T",
accuracy=0.001,
unit="mm",
name="pivot_z",
is_setting=True,
)
@@ -115,6 +156,8 @@ class HexapodPI(Assembly):
reset_current_value_to=False,
append_aliases=False,
change_simultaneously=False,
check_limits=True,
unit="mm",
name="x",
is_setting=False,
)
@@ -127,7 +170,9 @@ class HexapodPI(Assembly):
),
reset_current_value_to=False,
change_simultaneously=False,
check_limits=True,
append_aliases=False,
unit="mm",
name="y",
is_setting=False,
)
@@ -141,6 +186,8 @@ class HexapodPI(Assembly):
reset_current_value_to=False,
append_aliases=False,
change_simultaneously=False,
check_limits=True,
unit="mm",
name="z",
is_setting=False,
)
@@ -364,16 +411,17 @@ class HexapodPI_old:
return self.__str__()
class HexapodSymmetrie:
class HexapodSymmetrie(Assembly):
def __init__(
self, pv_master="SARES20-HEXSYM", name="hex_usd", offset=[0, 0, 0, 0, 0, 0]
):
self.name = name
super().__init__(name=name)
self.offset = offset
self.pvname = pv_master
self.coordinate_switch = AdjustablePvEnum(
f"{self.pvname}:MOVE#PARAM:CM", name="hex_usd_coordinate_switch"
)
self.pvs_setpos = {
"x": PV(f"{self.pvname}:MOVE#PARAM:X.VAL"),
"y": PV(f"{self.pvname}:MOVE#PARAM:Y.VAL"),
@@ -390,6 +438,14 @@ class HexapodSymmetrie:
"ry": PV(f"{self.pvname}:POSMACH:RY"),
"rz": PV(f"{self.pvname}:POSMACH:RZ"),
}
self._append(DetectorPvData, f"{self.pvname}:POSMACH:X", name="x")
self._append(DetectorPvData, f"{self.pvname}:POSMACH:Y", name="y")
self._append(DetectorPvData, f"{self.pvname}:POSMACH:Z", name="z")
self._append(DetectorPvData, f"{self.pvname}:POSMACH:RX", name="rx")
self._append(DetectorPvData, f"{self.pvname}:POSMACH:RY", name="ry")
self._append(DetectorPvData, f"{self.pvname}:POSMACH:RZ", name="rz")
self._ctrl_pv = PV(f"{self.pvname}:STATE#PANEL:SET.VAL")
def set_coordinates(self, x, y, z, rx, ry, rz, relative_to_eco_offset=True):
+167 -15
View File
@@ -5,51 +5,74 @@ import numpy as np
from epics import PV
from eco.aliases import Alias
from eco.elements.adjustable import tweak_option, spec_convenience, value_property
from eco.elements.adjustable import (
AdjustableMemory,
tweak_option,
spec_convenience,
value_property,
)
from . import get_from_archive
from eco.devices_general.utilities import Changer
from ..elements.assembly import Assembly
# Work in progress! TODO
@spec_convenience
@get_from_archive
@tweak_option
@value_property
class AdjustablePv:
class AdjustableAtomicPv:
def __init__(
self,
pvsetname,
pvreadbackname=None,
accuracy=None,
name=None,
elog=None,
element_count=None,
):
# alias_fields={"setpv": pvsetname, "readback": pvreadbackname},
# ):
self.Id = pvsetname
self.pvname = pvsetname
self.name = name
# for an, af in alias_fields.items():
# self.alias.append(
# Alias(an, channel=".".join([pvname, af]), channeltype="CA")
# )
self._pv = PV(self.Id, connection_timeout=0.05, count=element_count)
self._pv = PV(self.pvname, connection_timeout=0.05, count=element_count)
self._currentChange = None
self.accuracy = accuracy
if pvreadbackname is None:
self._pvreadback = PV(self.Id, count=element_count, connection_timeout=0.05)
pvreadbackname = self.Id
self.pvname = self.Id
self._pvreadback = PV(self.pvname, count=element_count, connection_timeout=0.05)
pvreadbackname = self.pvname
self.pvname = self.pvname
else:
self._pvreadback = PV(
pvreadbackname, count=element_count, connection_timeout=0.05
)
self.pvname = pvreadbackname
if pvlowlimname:
self._pvlowlim = PV(
pvlowlimname, count=element_count, connection_timeout=0.05
)
else:
self._pvlowlim = None
if pvhighlimname:
self._pvhighlim = PV(
pvhighlimname, count=element_count, connection_timeout=0.05
)
else:
self._pvhighlim = None
self.alias = Alias(name, channel=pvreadbackname, channeltype="CA")
def _wait_for_initialisation(self):
self._pv.wait_for_connection()
if hasattr(self, "_pv_readback") and self._pv_readback:
self._pv_readback.wait_for_connection()
if hasattr(self, "_pv_lowliself.accuracy = accuracym") and self._pv_lowlim:
self._pv_lowlim.wait_for_connection()
if hasattr(self, "_pv_highlim") and self._pv_highlim:
self._pv_highlim.wait_for_connection()
def get_current_value(self, readback=True):
if readback:
currval = self._pvreadback.get()
@@ -73,6 +96,130 @@ class AdjustablePv:
return change_done
def change(self, value):
if self._pvlowlim:
if value < self._pvlowlim.get():
raise Exception(
f"Target value of {self.name} is smaller than limit value!"
)
if self._pvhighlim:
if self._pvhighlim.get() < value:
raise Exception(
f"Target value of {self.name} is higher than limit value!"
)
self._pv.put(value)
time.sleep(0.1)
while self.get_change_done() == 0:
time.sleep(0.1)
def set_target_value(self, value, hold=False):
"""Adjustable convention"""
changer = lambda value: self.change(value)
return Changer(
target=value, parent=self, changer=changer, hold=hold, stopper=None
)
@spec_convenience
@get_from_archive
@tweak_option
@value_property
class AdjustablePv:
def __init__(
self,
pvsetname,
pvreadbackname=None,
pvlowlimname=None,
pvhighlimname=None,
accuracy=None,
name=None,
elog=None,
element_count=None,
unit=None,
):
# alias_fields={"setpv": pvsetname, "readback": pvreadbackname},
# ):
self.Id = pvsetname
self.name = name
# for an, af in alias_fields.items():
# self.alias.append(
# Alias(an, channel=".".join([pvname, af]), channeltype="CA")
# )
self._pv = PV(self.Id, connection_timeout=0.05, count=element_count)
self._currentChange = None
self.accuracy = accuracy
if unit:
self.unit = AdjustableMemory(unit, name="unit")
if pvreadbackname is None:
self._pvreadback = PV(self.Id, count=element_count, connection_timeout=0.05)
pvreadbackname = self.Id
self.pvname = self.Id
else:
self._pvreadback = PV(
pvreadbackname, count=element_count, connection_timeout=0.05
)
self.pvname = pvreadbackname
if pvlowlimname:
self._pvlowlim = PV(
pvlowlimname, count=element_count, connection_timeout=0.05
)
else:
self._pvlowlim = None
if pvhighlimname:
self._pvhighlim = PV(
pvhighlimname, count=element_count, connection_timeout=0.05
)
else:
self._pvhighlim = None
self.alias = Alias(name, channel=pvreadbackname, channeltype="CA")
def _wait_for_initialisation(self):
self._pv.wait_for_connection()
if hasattr(self, "_pv_readback") and self._pv_readback:
self._pv_readback.wait_for_connection()
if hasattr(self, "_pv_lowlim") and self._pv_lowlim:
self._pv_lowlim.wait_for_connection()
if hasattr(self, "_pv_highlim") and self._pv_highlim:
self._pv_highlim.wait_for_connection()
def get_current_value(self, readback=True):
if readback:
currval = self._pvreadback.get()
if not readback:
currval = self._pv.get()
return currval
def get_change_done(self):
"""Adjustable convention"""
""" 0: moving 1: move done"""
change_done = 1
if self.accuracy is not None:
if (
np.abs(
self.get_current_value(readback=False)
- self.get_current_value(readback=True)
)
> self.accuracy
):
change_done = 0
return change_done
def change(self, value):
if self._pvlowlim:
if value < self._pvlowlim.get():
raise Exception(
f"Target value of {self.name} is smaller than limit value!"
)
if self._pvhighlim:
if self._pvhighlim.get() < value:
raise Exception(
f"Target value of {self.name} is higher than limit value!"
)
self._pv.put(value)
time.sleep(0.1)
while self.get_change_done() == 0:
@@ -122,8 +269,8 @@ class AdjustablePvEnum:
if pvname_set:
self._pv_set = PV(pvname_set, connection_timeout=0.05)
tstrs = self._pv_set.enum_strs
if not (tstrs == self.enum_strs):
raise Exception("pv enum setter strings do not match the values!")
if not all([tstr in self.enum_strs for tstr in tstrs]):
raise Exception("pv enum setter strings are not all a readback option!")
else:
self._pv_set = None
@@ -137,6 +284,11 @@ class AdjustablePvEnum:
)
self.alias = Alias(name, channel=self.Id, channeltype="CA")
def _wait_for_initialisation(self):
self._pv.wait_for_connection()
if hasattr(self, "_pv_set") and self._pv_set:
self._pv_set.wait_for_connection()
def validate(self, value):
if type(value) is str:
return self.PvEnum.__members__[value]
+23 -14
View File
@@ -6,6 +6,7 @@ from ..epics.detector import DetectorPvData, DetectorPvEnum
from ..aliases import Alias
from datetime import datetime
from time import sleep
import numpy as np
from ..detector.detectors_psi import DetectorBsStream
@@ -67,7 +68,8 @@ class SwissFel(Assembly):
# SAR-EVPO-010:DEACTIVATE "FALSE"
self._append(
AdjustablePvEnum,
"SAROP-ARAMIS:BEAMLINE_SP",
"SAROP-ARAMIS:BEAMLINE",
pvname_set = "SAROP-ARAMIS:BEAMLINE_SP",
name="aramis_beamline_switch",
is_display=True,
is_setting=True,
@@ -248,6 +250,7 @@ class MessageBoard(Assembly):
self._append(Message, "SF-OP:ESF-MSG", name="furka_message", is_setting=True)
@spec_convenience
class Message:
def __init__(self, pvstem, name=None):
self.pvname = pvstem
@@ -258,36 +261,39 @@ class Message:
AdjustablePvString(self.pvname + f":OP-DATE{n + 1}") for n in range(5)
]
self.alias = Alias(name, channel=self.pvname + ":OP-MSG1", channeltype="CA")
self.pv_tmp = AdjustablePvString(self.pvname + ":OP-MSG-tmp")
def set_new_message(self, message):
for i in range(3, -1, -1):
self.pvs_msg[i + 1].set_target_value(
self.pvs_msg[i].get_current_value()
).wait()
self.pvs_date[i + 1].set_target_value(
self.pvs_date[i].get_current_value()
).wait()
timestr = datetime.now().strftime("%a %d-%b-%Y %H:%M:%S")
self.pvs_msg[0].set_target_value(message).wait()
self.pvs_date[0].set_target_value(timestr).wait()
# for i in range(3, -1, -1):
# self.pvs_msg[i + 1].set_target_value(
# self.pvs_msg[i].get_current_value()
# ).wait()
# self.pvs_date[i + 1].set_target_value(
# self.pvs_date[i].get_current_value()
# ).wait()
self.pv_tmp.set_target_value(message).wait()
# timestr = datetime.now().strftime("%a %d-%b-%Y %H:%M:%S")
# self.pvs_msg[0].set_target_value(message).wait()
# self.pvs_date[0].set_target_value(timestr).wait()
def get_current_value(self):
return self.pvs_msg[0].get_current_value()
def set_target_value(self, value):
def set_target_value(self, value, hold=False):
return Changer(
target=value,
parent=self,
changer=self.set_new_message,
hold=True,
hold=hold,
stopper=None,
)
@spec_convenience
class UndulatorK(Assembly):
def __init__(self, name=None):
def __init__(self, maximum_energy_change_keV=1.0, name=None):
super().__init__(name=name)
self.maximum_energy_change_keV = maximum_energy_change_keV
self._append(
DetectorPvData,
"SARUN:FELPHOTENE",
@@ -332,6 +338,9 @@ class UndulatorK(Assembly):
return self.aramis_undulator_photon_energy.get_current_value()
def change_energy(self, energy):
if np.abs(energy - self.get_current_value()) > self.maximum_energy_change_keV:
raise Exception("Likely too large undulator energy change requested!!!")
vals = self.calc_new_Ksets(energy)
for kset, val in zip(self.ksets, vals):
kset.set_target_value(val)
+4
View File
@@ -1,3 +1,5 @@
from eco.elements.assembly import Assembly
from eco.loptics.position_monitors import CameraPositionMonitor
from ..aliases import Alias
from ..devices_general.motors import MotorRecord, SmaractStreamdevice
from ..devices_general.smaract import SmarActRecord
@@ -364,3 +366,5 @@ class Laser_Exp:
def __repr__(self):
return self.get_adjustable_positions_str()
+354 -20
View File
@@ -1,11 +1,17 @@
from eco.loptics.position_monitors import CameraPositionMonitor
from ..elements.assembly import Assembly
from ..devices_general.motors import SmaractStreamdevice, MotorRecord
from functools import partial
from ..devices_general.motors import SmaractStreamdevice, MotorRecord, SmaractRecord
from ..elements.adjustable import AdjustableMemory, AdjustableVirtual, AdjustableFS
from ..timing.lasertiming_edwin import XltEpics
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
from ..epics.detector import DetectorPvData
from ..devices_general.detectors import DetectorVirtual
from ..timing.lasertiming_edwin import XltEpics, LaserRateControl
import colorama
import datetime
from pint import UnitRegistry
import numpy as np
import time
# from time import sleep
@@ -15,12 +21,263 @@ ureg = UnitRegistry()
class IncouplingCleanBernina(Assembly):
def __init__(self, name=None):
super().__init__(name=name)
self._append(SmaractStreamdevice, "SARES23-ESB13", name="tilt")
self._append(SmaractStreamdevice, "SARES23-ESB14", name="rotation")
self._append(SmaractStreamdevice, "SARES23-LIC15", name="transl_vertical")
self._append(SmaractRecord, "SARES23-LIC:MOT_17", name="tilt")
self._append(SmaractRecord, "SARES23-LIC:MOT_18", name="rotation")
self._append(SmaractRecord, "SARES23-LIC:MOT_16", name="transl_vertical")
self._append(MotorRecord, "SARES20-MF2:MOT_5", name="transl_horizontal")
class Spectrometer(Assembly):
def __init__(self, pvname, name=None):
super().__init__(name=name)
self.pvname = pvname
self._append(
AdjustablePvEnum,
pvname + ":TRIGGER",
name="trigger_mode",
is_setting=True,
)
self._append(AdjustablePvEnum, pvname + ":INIT", name="state", is_setting=True)
self._append(
AdjustablePv,
pvname + ":EXPOSURE",
name="exposure_time",
is_setting=True,
)
self._append(DetectorPvData, pvname + ":CENTRE", name="center")
self._append(DetectorPvData, pvname + ":FWHM", name="fwhm")
self._append(DetectorPvData, pvname + ":AMPLITUDE", name="amplitude")
self._append(DetectorPvData, pvname + ":INTEGRAL", name="integral")
self._append(DetectorPvData, pvname + ":BASER_HEIGHT", name="base_value")
self._append(
AdjustablePv, pvname + ":XVAL1", name="spectrum_min", is_setting=True
)
self._append(
AdjustablePv, pvname + ":XVAL2", name="spectrum_max", is_setting=True
)
# SLAAR02-LSPC-OSC:SERIALNR
flag_names_filter_wheel = [
"error",
"proc_tongle",
"connected",
"moving",
"homed",
"remote_operation",
]
class FilterWheelFlags(Assembly):
def __init__(self, flags, name="flags"):
super().__init__(name=name)
self._flags = flags
for flag_name in flag_names_filter_wheel:
self._append(
DetectorVirtual,
[self._flags],
partial(self._get_flag_name_value, flag_name=flag_name),
name=flag_name,
is_status=True,
is_display=True,
)
def _get_flag_name_value(self, value, flag_name=None):
index = flag_names_filter_wheel.index(flag_name)
return int("{0:015b}".format(int(value))[-1 * (index + 1)]) == 1
class FilterWheel(Assembly):
def __init__(self, pvname, name=None):
super().__init__(name=name)
self.pvname = pvname
self._append(AdjustablePvEnum, f"{pvname}.VAL", name="_val", is_setting=True)
self._append(AdjustablePvEnum, f"{pvname}.RBV", name="_rb", is_setting=True)
self._append(AdjustablePv, f"{pvname}.CMD", name="_cmd", is_setting=False)
self.set_remote_operation()
self._append(
DetectorPvData,
self.pvname + ".STA",
name="_flags",
is_setting=False,
is_display=False,
)
self._append(
FilterWheelFlags,
self._flags,
name="flags",
is_display="recursive",
is_setting=False,
is_status=True,
)
def set_remote_operation(self):
self._val(7)
def set_manual_operation(self):
self._val(8)
def home(self):
self.set_remote_operation()
self._val(6)
def is_moving(self):
pass
class FilterWheelAttenuator(Assembly):
def __init__(self, pvname, name=None):
super().__init__(name=name)
self._append(FilterWheel, pvname=pvname + "IFW_A", name="wheel_1")
self._append(FilterWheel, pvname=pvname + "IFW_B", name="wheel_2")
self.targets_1 = {
"t": 10 ** -np.array([0.2, 0.3, 0.5, 0.6, 1.0]),
"pos": np.array([1, 2, 3, 4, 5]),
}
self.targets_2 = {
"t": 10 ** -np.array([0.2, 0.3, 0.4, 0.5, 0.6]),
"pos": np.array([1, 2, 3, 4, 5]),
}
self._calc_transmission()
def _calc_transmission(self):
t1 = self.targets_1["t"]
t2 = self.targets_2["t"]
t_comb = (
(np.expand_dims(t1, axis=0)).T * (np.expand_dims(t2, axis=0))
).flatten()
pos_comb = np.array(
[[p1, p2] for p1 in self.targets_1["pos"] for p2 in self.targets_2["pos"]]
)
self.transmissions = {"t": t_comb, "pos": pos_comb}
def home(self):
self.wheel_1.home()
self.wheel_2.home()
class StageLxtDelay(Assembly):
def __init__(self, fine_delay_adj, coarse_delay_adj, direction=1, name=None):
super().__init__(name=name)
self._append(fine_delay_adj, name="_fine_delay_adj", is_setting=True)
self._append(coarse_delay_adj, name="_coarse_delay_adj", is_setting=True)
self._append(AdjustableMemory, direction, name="_direction", is_setting=True)
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/{name}_combined_delay_phase_shifter_threshold",
name="switch_threshold",
default_value=50e-12,
is_setting=True,
)
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/{name}_conbined_fine_adj_offset",
name="offset_fine_adj",
default_value=0.0,
is_setting=True,
)
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/{name}_combined_coarse_adj_offset",
name="offset_coarse_adj",
default_value=0.0,
is_setting=True,
)
self._append(
AdjustableVirtual,
[self._fine_delay_adj, self._coarse_delay_adj],
self._get_comb_delay,
self._set_comb_delay,
name="delay",
unit="s",
)
def _get_comb_delay(self, pd, ps):
ps_rel = ps - self.offset_coarse_adj()
pd_rel = pd - self.offset_fine_adj()
return (ps_rel + pd_rel) * self._direction.get_current_value()
def _set_comb_delay(self, delay):
if delay < abs(self.switch_threshold.get_current_value()):
### check to prevent slow phaseshifter corrections <50fs
if (
np.abs(
self._coarse_delay_adj.get_current_value()
- self.offset_coarse_adj.get_current_value()
)
> 50e-15
):
ps_pos = self.offset_coarse_adj.get_current_value()
else:
ps_pos = None
pd_pos = self.offset_fine_adj.get_current_value() + delay
else:
ps_pos = self.offset_coarse_adj.get_current_value() + delay
pd_pos = self.offset_fine_adj.get_current_value()
if pd_pos is None:
outfine = None
else:
outfine = self._direction.get_current_value() * pd_pos,
if ps_pos is None:
outcoarse = None
else:
outcoarse = self._direction.get_current_value() * ps_pos,
return (outfine,outcoarse)
class Stage_LXT_Delay(AdjustableVirtual):
def __init__(self, fine_delay_adj, coarse_delay_adj, direction=1, name=None):
self._fine_delay_adj = fine_delay_adj
self._coarse_delay_adj = coarse_delay_adj
self._direction = direction
self.switch_threshold = AdjustableFS(
f"/photonics/home/gac-bernina/eco/configuration/{name}_combined_delay_phase_shifter_threshold",
name="switch_threshold",
default_value=50e-12,
)
self.offset_fine_adj = AdjustableFS(
f"/photonics/home/gac-bernina/eco/configuration/{name}_conbined_fine_adj_offset",
name="offset_fine_adj",
default_value=0.0,
)
self.offset_coarse_adj = AdjustableFS(
f"/photonics/home/gac-bernina/eco/configuration/{name}_combined_coarse_adj_offset",
name="offset_coarse_adj",
default_value=0.0,
)
AdjustableVirtual.__init__(
self,
[self._fine_delay_adj, self._coarse_delay_adj],
self._get_comb_delay,
self._set_comb_delay,
name=name,
unit="s",
)
def _get_comb_delay(self, pd, ps):
ps_rel = ps - self.offset_coarse_adj()
pd_rel = pd - self.offset_fine_adj()
return (ps_rel + pd_rel) * self._direction
def _set_comb_delay(self, delay):
if delay < abs(self.switch_threshold()):
### check to prevent slow phaseshifter corrections <50fs
if np.abs(self._coarse_delay_adj() - self.offset_coarse_adj()) > 50e-15:
ps_pos = self.offset_coarse_adj()
else:
ps_pos = None
pd_pos = self.offset_fine_adj() + delay
else:
ps_pos = self.offset_coarse_adj() + delay
pd_pos = self.offset_fine_adj()
return self._direction * pd_pos, self._direction * ps_pos
class LaserBernina(Assembly):
def __init__(self, pvname, name=None):
super().__init__(name=name)
@@ -44,15 +301,31 @@ class LaserBernina(Assembly):
self._append(
MotorRecord, self.pvname + "-M533:MOT", name="wp_pol", is_setting=True
)
self._append(
MotorRecord, self.pvname + "-M534:MOT", name="wp_att", is_setting=True
)
self._append(
AdjustableFS,
"/photonics/home/gac-bernina/eco/configuration/wp_att_calibration",
name="wp_att_calibration",
is_display=False,
)
tmptime = time.time()
while (time.time() - tmptime) < 10:
try:
self._append(
Spectrometer,
"SLAAR02-LSPC-OSC",
name="oscillator_spectrum",
is_setting=False,
is_display=True,
)
print("SUCCESS: oscillator spectrometer configured!")
break
except:
pass
def uJ2wp(uJ):
direction = 1
@@ -63,36 +336,55 @@ class LaserBernina(Assembly):
)
def wp2uJ(wp):
return np.interp(wp, *np.asarray(self.wp_att_calibration()).T)
try:
return np.interp(wp, *np.asarray(self.wp_att_calibration()).T)
except:
return np.nan
self._append(
AdjustableVirtual, [self.wp_att], wp2uJ, uJ2wp, name="pulse_energy_pump"
)
# self._append(
# MotorRecord,
# self.pvname + "-M522:MOTOR_1",
# name="delaystage_pump",
# is_setting=True,
# )wp_att
# self._append(
# DelayTime, self.delaystage_pump, name="delay_pump", is_setting=True
# )
self._append(
LaserRateControl, name="rate", is_setting=True, is_display="recursive"
)
self._append(XltEpics, name="xlt", is_setting=True, is_display="recursive")
# Upstairs, Laser 1 LAM
# self._append(
# MotorRecord,
# "SLAAR21-LMOT-M521:MOTOR_1",
# name="delaystage_pump",
# is_setting=True,
# )
self._append(
MotorRecord,
self.pvname + "-M522:MOTOR_1",
"SLAAR21-LMOT-M521:MOTOR_1",
name="delaystage_pump",
is_setting=True,
)
self._append(
DelayTime, self.delaystage_pump, name="delay_pump", is_setting=True
)
self._append(XltEpics, name="xlt", is_setting=True, is_display="recursive")
# Upstairs, Laser 1 LAM
self._append(
MotorRecord,
"SLAAR21-LMOT-M521:MOTOR_1",
name="delaystage_eos",
is_setting=True,
)
self._append(
DelayTime,
self.delaystage_eos,
name="delay_eos",
self.delaystage_pump,
name="delay_pump",
is_setting=True,
)
# self._append(
# Stage_LXT_Delay,
# self.delay_glob,
# self.xlt,
# direction=1,
# name="delay",
# )
# self._append(
# SmaractStreamdevice,
# pvname="SARES23-ESB18",
# name="delaystage_thz",
@@ -181,3 +473,45 @@ class DelayCompensation(AdjustableVirtual):
s += f"{(self.get_current_value()*ureg.second).to_compact():P~6.3f}"
s += f"{colorama.Style.RESET_ALL}"
return s
class PositionMonitors(Assembly):
def __init__(self, name=None):
super().__init__(name=name)
self._append(
CameraPositionMonitor,
"SLAAR21-LCAM-CS844",
name="table1_angle",
is_display="recursive",
is_status=True,
)
self._append(
CameraPositionMonitor,
"SLAAR21-LCAM-CS843",
name="table1_position",
is_display="recursive",
is_status=True,
)
self._append(
CameraPositionMonitor,
"SLAAR21-LCAM-CS842",
name="table2_angle",
is_display="recursive",
is_status=True,
)
self._append(
CameraPositionMonitor,
"SLAAR21-LCAM-CS841",
name="table2_position",
is_display="recursive",
is_status=True,
)
# self._append(
# CameraPositionMonitor,
# "SLAAR21-LCAM-C511",
# name="opaout_focus",
# is_display="recursive",
# is_status=True,
# )
# self._append(CameraPositionMonitor, 'SLAAR21-LCAM-C541', name='cam541')
# self._append(CameraPositionMonitor, 'SLAAR21-LCAM-C542', name='cam542')
+25
View File
@@ -0,0 +1,25 @@
from eco import Assembly
from eco.epics.adjustable import AdjustablePv, AdjustablePvEnum
from eco.epics.detector import DetectorPvData, DetectorPvDataStream
class LabMaxEnergyMonitor(Assembly):
def __init__(self,pvname, name=None):
super().__init__(name=name)
self.pvname = pvname
self._append(DetectorPvDataStream, pvname+':READ_SC',name='pulse_energy')
self._append(DetectorPvDataStream, pvname+':ENERGY_AVE100',name='pulse_energy_avg100')
self._append(AdjustablePvEnum, pvname+':READ.SCAN',name='read_mode')
self._append(AdjustablePv, pvname+':SET_FSD',name='output_signal_voltage', is_setting=True)
self._append(AdjustablePvEnum, pvname+':WL_CORR_MODE',name='correct_wavelenght', is_setting=True)
self._append(AdjustablePvEnum, pvname+':TRIG_SOURCE',name='trigger_source', is_setting=True)
self._append(AdjustablePv, pvname+':SET_TRIG_LEVEL',name='output_signal_voltage', is_setting=True)
self._append(DetectorPvData, pvname+':GET_RANGE_SC',name='range', is_setting=True)
self._append(AdjustablePv, pvname+':SELECT_RANGE',name='set_range', is_setting=True)
+25
View File
@@ -0,0 +1,25 @@
from eco.epics.detector import DetectorPvDataStream
from eco.epics.adjustable import AdjustablePv
from eco import Assembly
class CameraPositionMonitor(Assembly):
def __init__(self,pvname,name=None):
super().__init__(name=name)
self.pvname = pvname
self._append(DetectorPvDataStream,pvname+':FIT-XPOS',name='xpos_raw')
self._append(DetectorPvDataStream,pvname+':FIT-YPOS',name='ypos_raw')
self._append(DetectorPvDataStream,pvname+':FIT-XWID',name='xwidth_raw')
self._append(DetectorPvDataStream,pvname+':FIT-YWID',name='ywidth_raw')
self._append(DetectorPvDataStream,pvname+':FIT-XCOM',name='xcom_raw')
self._append(DetectorPvDataStream,pvname+':FIT-YCOM',name='ycom_raw')
self._append(DetectorPvDataStream,pvname+':FIT-XPOS_EGU',name='xpos')
self._append(DetectorPvDataStream,pvname+':FIT-YPOS_EGU',name='ypos')
self._append(DetectorPvDataStream,pvname+':FIT-XWID_EGU',name='xwidth')
self._append(DetectorPvDataStream,pvname+':FIT-YWID_EGU',name='ywidth')
self._append(AdjustablePv,pvname+':XCALIB',name='xcalib_gradient', is_setting=True)
self._append(AdjustablePv,pvname+':YCALIB',name='ycalib_gradient', is_setting=True)
self._append(AdjustablePv,pvname+':XCALIB-OFFS',name='xcalib_offset', is_setting=True)
self._append(AdjustablePv,pvname+':YCALIB-OFFS',name='ycalib_offset', is_setting=True)
+38 -3
View File
@@ -11,8 +11,6 @@ class MicroscopeMotorRecord(Assembly):
def __init__(
self,
pvname_camera=None,
camserver_alias=None,
# camserver_alias=None,
pvname_zoom=None,
pvname_focus=None,
name=None,
@@ -22,7 +20,7 @@ class MicroscopeMotorRecord(Assembly):
self._append(
CameraBasler,
pvname_camera,
camserver_alias=camserver_alias,
camserver_alias=name,
name="camera",
is_setting=True,
is_display="recursive",
@@ -110,3 +108,40 @@ class OptoSigmaZoom(Assembly):
def set_target_value(self, value, **kwargs):
return self.zoom.set_target_value(value, **kwargs)
@spec_convenience
class FeturaPlusZoom(Assembly):
def __init__(
self,
pv_get_position="SARES20-FETURA:POS_RB",
pv_set_position="SARES20-FETURA:POS_SP",
name=None,
):
super().__init__(name=name)
self.settings_collection.append(self)
self._append(
AdjustablePv,
pv_set_position,
pv_get_position,
accuracy=1,
name="zoom_raw",
is_setting=False,
)
self._append(
AdjustableVirtual,
[self.zoom_raw],
lambda x: abs(round(x / 1000 * 100) - 100),
lambda x: round(abs(x - 100) / 100 * 1000),
name="zoom",
is_setting=False,
)
def get_current_value(self):
return self.zoom.get_current_value()
def set_target_value(self, value, **kwargs):
return self.zoom.set_target_value(value, **kwargs)
+50 -28
View File
@@ -2,6 +2,7 @@ from epics import caget_many
from ..elements.adjustable import AdjustableMemory, AdjustableVirtual
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum, AdjustablePvString
from ..epics.detector import DetectorPvData, DetectorPvDataStream
from ..detector.detectors_psi import DetectorBsStream
from eco.epics.utilities_epics import EpicsString
import logging
from ..elements.assembly import Assembly
@@ -13,12 +14,17 @@ logging.getLogger("cta_lib").setLevel(logging.WARNING)
class TimingSystem(Assembly):
"""This is a wrapper object for the global timing system at SwissFEL"""
def __init__(self, pv_master=None, pv_pulse_id=None, name=None):
def __init__(self, pv_master=None, pv_pulse_id=None, pv_eventset=None, name=None):
super().__init__(name=name)
self._append(MasterEventSystem, pv_master, name="event_master", is_display=True)
# self._append(DetectorPvDataStream, pv_pulse_id, name="pulse_id")
self._append(
MasterEventSystem, pv_master, name="event_master", is_display="recursive"
DetectorBsStream, "pulse_id", cachannel=pv_pulse_id, name="pulse_id"
)
self._append(DetectorPvDataStream, pv_pulse_id, name="pulse_id")
self._append(DetectorBsStream, "lab_time", cachannel=None, name="lab_time")
if pv_eventset:
self._append(DetectorBsStream, pv_eventset, cachannel=None, name="eventset")
# EVR output mapping
@@ -212,7 +218,7 @@ class MasterEventSystem(Assembly):
for s, c in zip(slots, codes):
if not c == None:
if c in codes_out:
print(f"Code {c} exists multiple times!")
# print(f"Code {c} exists multiple times!")
continue
slots_out.append(s)
codes_out.append(c)
@@ -295,31 +301,36 @@ class EvrPulser(Assembly):
is_setting=True,
)
self.description = EpicsString(pv_base + "-Name-I")
self._append(
AdjustableVirtual,
[self._eventcode.frequency],
lambda x: x,
lambda x: x,
name="frequency",
)
self._append(
AdjustableVirtual,
[self._eventcode.delay],
lambda x: x,
lambda x: x,
name="delay_eventcode",
)
self._append(
AdjustableVirtual,
[self.delay_pulser],
lambda tp: self.delay_eventcode.get_current_value() + tp,
lambda x: x - self.delay_eventcode.get_current_value(),
name="delay",
)
if True: #self._eventcode is not None:
self._append(
AdjustableVirtual,
[self._eventcode.frequency],
lambda x: x,
lambda x: x,
name="frequency",
)
self._append(
AdjustableVirtual,
[self._eventcode.delay],
lambda x: x,
lambda x: x,
name="delay_eventcode",
)
self._append(
AdjustableVirtual,
[self.delay_pulser],
lambda tp: self.delay_eventcode.get_current_value() + tp,
lambda x: x - self.delay_eventcode.get_current_value(),
name="delay",
)
@property
def _eventcode(self):
return self._event_master.event_codes[self.eventcode.get_current_value()]
try:
return self._event_master.event_codes[self.eventcode.get_current_value()]
except KeyError:
return None
class DummyPulser(Assembly):
@@ -491,14 +502,14 @@ class EvrOutput(Assembly):
def pulserA(self):
try:
return self._pulsers[self.pulserA_number.get_current_value()]
except IndexError:
except (IndexError, TypeError):
return DummyPulser()
@property
def pulserB(self):
try:
return self._pulsers[self.pulserA_number.get_current_value()]
except IndexError:
except (IndexError, TypeError):
return DummyPulser()
@@ -551,6 +562,17 @@ class EventReceiver(Assembly):
# to._pulsers = self.pulsers
self.outputs = outputs
self._append(
AdjustablePv,
self.pvname + ":SYSRESET",
is_status=False,
is_setting=False,
name="restart_ioc_pv",
)
def restart_ioc(self):
self.restart_ioc_pv.set_target_value(1)
def gui(self):
dev = self.pvname.split("-")[-1]
sys = self.pvname[: -(len(dev) + 1)]
+312 -98
View File
@@ -2,60 +2,281 @@ from epics import PV
import os
import numpy as np
import time
from eco.elements.detector import DetectorVirtual
from ..devices_general.utilities import Changer
from ..elements.adjustable import (
spec_convenience,
AdjustableFS,
AdjustableVirtual,
tweak_option,
value_property,
)
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
from ..epics.detector import DetectorPvData
from ..aliases import append_object_to_object, Alias
from ..elements.assembly import Assembly
# @spec_convenience
# class XltEpics:
# def __init__(self, pvname="SLAAR02-LTIM-PDLY", name="lxt_epics"):
# self.pvname = pvname
# self.alias = Alias(name)
# append_object_to_object(
# self,
# AdjustablePvEnum,
# self.pvname + ":SHOTDELAY",
# name="oscialltor_pulse_offset",
# )
# append_object_to_object(
# self,
# AdjustablePvEnum,
# self.pvname + ":SHOTMOFFS_ENA",
# name="modulo_offset_mode",@spec_convenience
# class XltEpics:
# def __init__(self, pvname="SLAAR02-LTIM-PDLY", name="lxt_epics"):
# self.pvname = pvname
# self.alias = Alias(name)
# append_object_to_object(
# self,
# AdjustablePvEnum,
# self.pvname + ":SHOTDELAY",
# name="oscialltor_pulse_offset",
# )
# append_object_to_object(
# self,
# AdjustablePvEnum,
# self.pvname + ":SHOTMOFFS_ENA",
# name="modulo_offset_mode",
# )
# append_object_to_object(
# self, AdjustablePv, self.pvname + ":DELAY_Z_OFFS", name="_offset"
# )
# self.offset = AdjustableVirtual(
# [self._offset],
# lambda offset: offset * 1e-12,
# lambda offset: offset / 1e-12,
# name="offset",
# )
# append_object_to_object(
# self, AdjustablePv, self.pvname + ":DELAY", name="_set_user_delay_value"
# )
# self._delay_dial_rb = PV("SLAAR-LGEN:DLY_OFFS2")
# self.alias.append(
# Alias("delay_dial_rb", "SLAAR-LGEN:DLY_OFFS2", channeltype="CA")
# )
# self.waiting_for_change = PV(self.pvname + ":WAITING")
# def get_current_dial_value(self):
# return self._delay_dial_rb.get() * 1e-6
# def get_current_value(self):
# return self.get_current_dial_value() - self.offset.get_current_value()
# def change_user_and_wait(self, value, check_interval=0.03):
# if np.abs(value) > 0.1:
# raise Exception("Very large value! This value is counted in seconds!")
# if not self.waiting_for_change.get():
# raise Exception("lxt is still moving!")
# self.is_moving = False
# self.is_stopped = False
# def set_is_stopped(**kwargs):
# old_status = self.is_moving
# new_status = not bool(kwargs["value"])
# if (not new_status) and old_status:
# self.is_stopped = True
# self.is_moving = new_status
# self.waiting_for_change.add_callback(callback=set_is_stopped)
# self._set_user_delay_value.set_target_value(value / 1e-12)
# while not self.is_stopped:
# time.sleep(check_interval)
# self.waiting_for_change.clear_callbacks()
# def set_target_value(self, value, hold=False):
# return Changer(
# target=value,
# parent=self,
# changer=self.change_user_and_wait,
# hold=hold,
# stopper=None,
# )
# def reset_current_value_to(self, value):
# self.offset.set_target_value((self.get_current_dial_value() - value)).wait()
# )
# append_object_to_object(
# self, AdjustablePv, self.pvname + ":DELAY_Z_OFFS", name="_offset"
# )
# self.offset = AdjustableVirtual(
# [self._offset],
# lambda offset: offset * 1e-12,
# lambda offset: offset / 1e-12,
# name="offset",
# )
# append_object_to_object(
# self, AdjustablePv, self.pvname + ":DELAY", name="_set_user_delay_value"
# )
# self._delay_dial_rb = PV("SLAAR-LGEN:DLY_OFFS2")
# self.alias.append(
# Alias("delay_dial_rb", "SLAAR-LGEN:DLY_OFFS2", channeltype="CA")
# )
# self.waiting_for_change = PV(self.pvname + ":WAITING")
# def get_current_dial_value(self):
# return self._delay_dial_rb.get() * 1e-6
# def get_current_value(self):
# return self.get_current_dial_value() - self.offset.get_current_value()
# def change_user_and_wait(self, value, check_interval=0.03):
# if np.abs(value) > 0.1:
# raise Exception("Very large value! This value is counted in seconds!")
# if not self.waiting_for_change.get():
# raise Exception("lxt is still moving!")
# self.is_moving = False
# self.is_stopped = False
# def set_is_stopped(**kwargs):
# old_status = self.is_moving
# new_status = not bool(kwargs["value"])
# if (not new_status) and old_status:
# self.is_stopped = True
# self.is_moving = new_status
# self.waiting_for_change.add_callback(callback=set_is_stopped)
# self._set_user_delay_value.set_target_value(value / 1e-12)
# while not self.is_stopped:
# time.sleep(check_interval)
# self.waiting_for_change.clear_callbacks()
# def set_target_value(self, value, hold=False):
# return Changer(
# target=value,
# parent=self,
# changer=self.change_user_and_wait,
# hold=hold,
# stopper=None,
# )
# def reset_current_value_to(self, value):
# self.offset.set_target_value((self.get_current_dial_value() - value)).wait()
@spec_convenience
class XltEpics:
@tweak_option
@value_property
class XltEpics(Assembly):
def __init__(self, pvname="SLAAR02-LTIM-PDLY", name="lxt_epics"):
super().__init__(name=name)
self.settings_collection.append(self, force=True)
self.pvname = pvname
self.alias = Alias(name)
append_object_to_object(
self,
AdjustablePvEnum,
self.pvname + ":SHOTDELAY",
name="oscialltor_pulse_offset",
# self.settings_collection.append(self, force=True)
# self.status_collection.append(self, force=True)
# self.display_collection.append(self, force=True)
self._append(
AdjustablePv,
self.pvname + ":DELAY_Z_OFFS",
name="_offset",
is_setting=True,
is_display=False,
) # in picoseconds
self._append(
DetectorPvData,
"SLAAR-LGEN:DLY_OFFS2",
unit="ps",
name="delay_dial_rb",
is_setting=False,
is_display=False,
)
append_object_to_object(
self,
AdjustablePvEnum,
self.pvname + ":SHOTMOFFS_ENA",
name="modulo_offset_mode",
)
append_object_to_object(
self, AdjustablePv, self.pvname + ":DELAY_Z_OFFS", name="_offset"
)
self.offset = AdjustableVirtual(
# SLAAR-LGEN:DLY_OFFS2
self._append(
AdjustableVirtual,
[self._offset],
lambda offset: offset * 1e-12,
lambda offset: offset / 1e-12,
name="offset",
unit="s",
is_setting=False,
is_display=False,
)
append_object_to_object(
self, AdjustablePv, self.pvname + ":DELAY", name="_set_user_delay_value"
self._append(
DetectorVirtual,
[self.delay_dial_rb, self.offset],
lambda dialrb,offset: dialrb * 1e-6 - offset,
unit="s",
name="readback",
)
self._delay_dial_rb = PV("SLAAR-LGEN:DLY_OFFS2")
self.alias.append(
Alias("delay_dial_rb", "SLAAR-LGEN:DLY_OFFS2", channeltype="CA")
self._append(
AdjustablePv,
self.pvname + ":WINDOW_REQ",
name="phase_shifter_window_start",
is_setting=True,
is_display=True,
unit="ps",
)
self._append(
AdjustablePv,
self.pvname + ":LONG_DELAY_THRESH",
name="_long_delay_threshold",
is_setting=True,
is_display=False,
unit="ps",
)
self._append(
AdjustableVirtual,
[self._long_delay_threshold],
lambda offset: offset * 1e-12,
lambda offset: offset / 1e-12,
name="long_delay_threshold",
unit="s",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvname + ":DELAY",
name="_set_user_delay_value",
is_setting=False,
is_display=False,
)
# self._append(
# DetectorPvData,
# "SLAAR-LGEN:DLY_OFFS2",
# name="delay_dial",
# is_setting=False,
# is_display=True,
# )
# self._delay_dial_rb = PV("SLAAR-LGEN:DLY_OFFS2")
# self.alias.append(
# Alias("delay_dial_rb", "SLAAR-LGEN:DLY_OFFS2", channeltype="CA")
# )
self.waiting_for_change = PV(self.pvname + ":WAITING")
def get_current_dial_value(self):
return self._delay_dial_rb.get() * 1e-6
# def get_current_dial_value(self):
# return self.delay_dial_rb.get_current_value() * 1e-6
def get_current_value(self):
return self.get_current_dial_value() - self.offset.get_current_value()
return self.readback.get_current_value()
def change_user_and_wait(self, value, check_interval=0.03):
# def get_current_dial_value(self):
# return self.delay_dial_rb.get_current_value() * 1e-6
# def get_current_user_value(self):
# return (
# self.delay_dial_rb.get_current_value() * 1e-6
# - self.offset.get_current_value()
# )
def change_user_and_wait(self, value, check_interval=0.03, evr_wait_time=0.01):
if np.abs(value) > 0.1:
raise Exception("Very large value! This value is counted in seconds!")
if not self.waiting_for_change.get():
@@ -70,8 +291,18 @@ class XltEpics:
self.is_stopped = True
self.is_moving = new_status
self.waiting_for_change.add_callback(callback=set_is_stopped)
self._set_user_delay_value.set_target_value(value / 1e-12)
is_phasshift = not (
self.long_delay_threshold.get_current_value() < np.abs(value)
)
if is_phasshift:
self.waiting_for_change.add_callback(callback=set_is_stopped)
self._set_user_delay_value.set_target_value(
(value) / 1e-12
)
if not is_phasshift:
time.sleep(evr_wait_time)
self.is_stopped = True
while not self.is_stopped:
time.sleep(check_interval)
@@ -87,17 +318,41 @@ class XltEpics:
)
def reset_current_value_to(self, value):
self.offset.set_target_value((self.get_current_dial_value() - value)).wait()
self.offset.set_target_value(self.offset.get_current_value() + self.get_current_value() - value).wait()
# caqtdm -noMsg -macro S=SLAAR02-LTIM-PDLY /sf/laser/config/qt/SLAAR02-L-SET_DELAY.ui
def gui(self):
self._run_cmd(
f"caqtdm -noMsg -macro S=SLAAR02-LTIM-PDLY /sf/laser/config/qt/SLAAR02-L-SET_DELAY.ui"
)
@spec_convenience
@tweak_option
class XltEpics(Assembly):
def __init__(self, pvname="SLAAR02-LTIM-PDLY", name="lxt_epics"):
@value_property
class LaserRateControl(Assembly):
def __init__(self, pvname="SLAAR02-LTIM-PDLY", name=None):
super().__init__(name=name)
self.pvname = pvname
self.settings_collection.append(self, force=True)
self.status_collection.append(self, force=True)
self.pvname = pvname
# self.settings_collection.append(self, force=True)
# self.status_collection.append(self, force=True)
# self.display_collection.append(self, force=True)
self._append(
AdjustablePvEnum,
self.pvname + ":ONEINN_MODE",
name="reference_mode",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
"SIN-TIMAST-TMA:Evt-22-Freq-SP",
name="laser_frequency",
unit="Hz",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePvEnum,
self.pvname + ":SHOTDELAY",
@@ -113,84 +368,43 @@ class XltEpics(Assembly):
is_display=True,
)
self._append(
AdjustablePv,
self.pvname + ":DELAY_Z_OFFS",
name="_offset",
AdjustablePvEnum,
self.pvname + ":USE_EXT_EVT",
name="use_ext_reference_event",
is_setting=True,
is_display=False,
)
self._append(
AdjustableVirtual,
[self._offset],
lambda offset: offset * 1e-12,
lambda offset: offset / 1e-12,
name="offset",
is_setting=False,
is_display=True,
)
self._append(
AdjustablePv,
self.pvname + ":DELAY",
name="_set_user_delay_value",
is_setting=False,
is_display=False,
self.pvname + ":ALT_EXT_EVT",
name="ext_reference_event",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvname + ":P_RATIO",
name="rep_len",
name="ref_pattern_len",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
"SIN-TIMAST-TMA:Evt-22-Freq-SP",
name="laser_frequency",
is_setting=True,
is_display=True,
)
self._delay_dial_rb = PV("SLAAR-LGEN:DLY_OFFS2")
self.alias.append(
Alias("delay_dial_rb", "SLAAR-LGEN:DLY_OFFS2", channeltype="CA")
)
self.waiting_for_change = PV(self.pvname + ":WAITING")
def get_current_dial_value(self):
return self._delay_dial_rb.get() * 1e-6
def get_current_value(self):
return self.get_current_dial_value() - self.offset.get_current_value()
# self._append(
# DetectorPvData,
# "SLAAR-LGEN:DLY_OFFS2",
# name="delay_dial",
# is_setting=False,
# is_display=True,
# )
def change_user_and_wait(self, value, check_interval=0.03):
if np.abs(value) > 0.1:
raise Exception("Very large value! This value is counted in seconds!")
if not self.waiting_for_change.get():
raise Exception("lxt is still moving!")
self.is_moving = False
self.is_stopped = False
# self._delay_dial_rb = PV("SLAAR-LGEN:DLY_OFFS2")
# self.alias.append(
# Alias("delay_dial_rb", "SLAAR-LGEN:DLY_OFFS2", channeltype="CA")
# )
def set_is_stopped(**kwargs):
old_status = self.is_moving
new_status = not bool(kwargs["value"])
if (not new_status) and old_status:
self.is_stopped = True
self.is_moving = new_status
self.waiting_for_change.add_callback(callback=set_is_stopped)
self._set_user_delay_value.set_target_value(value / 1e-12)
while not self.is_stopped:
time.sleep(check_interval)
self.waiting_for_change.clear_callbacks()
def set_target_value(self, value, hold=False):
return Changer(
target=value,
parent=self,
changer=self.change_user_and_wait,
hold=hold,
stopper=None,
)
def reset_current_value_to(self, value):
self.offset.set_target_value((self.get_current_dial_value() - value)).wait()
def gui(self):
self._run_cmd(
f"caqtdm -noMsg -macro S=SLAAR02-LTIM-PDLY /sf/laser/config/qt/SLAAR02-L-SET_DELAY.ui"
)
-24
View File
@@ -1,24 +0,0 @@
from ..devices_general.motors import MotorRecord
from ..devices_general.smaract import SmarActRecord
from epics import PV
from ..devices_general.delay_stage import DelayStage
class Palm:
def __init__(self, Id):
self.Id = Id
self._delayStg = MotorRecord(self.Id + "-M552:MOT")
self.delay = DelayStage(self._delayStg)
def get_adjustable_positions_str(self):
ostr = "*****Palm motor positions******\n"
for tkey, item in self.__dict__.items():
if hasattr(item, "get_current_value"):
pos = item.get_current_value()
ostr += " " + tkey.ljust(10) + " : % 14g\n" % pos
return ostr
def __repr__(self):
return self.get_adjustable_positions_str()
-24
View File
@@ -1,24 +0,0 @@
from ..devices_general.motors import MotorRecord
from ..devices_general.smaract import SmarActRecord
from epics import PV
from ..devices_general.delay_stage import DelayStage
class Psen:
def __init__(self, Id):
self.Id = Id
self._delayStg = MotorRecord(self.Id + "-M561:MOT")
self.delay = DelayStage(self._delayStg)
def get_adjustable_positions_str(self):
ostr = "*****PSEN motor positions******\n"
for tkey, item in self.__dict__.items():
if hasattr(item, "get_current_value"):
pos = item.get_current_value()
ostr += " " + tkey.ljust(10) + " : % 14g\n" % pos
return ostr
def __repr__(self):
return self.get_adjustable_positions_str()
+23 -5
View File
@@ -97,29 +97,47 @@ class CtaSequencer(Assembly):
return arrays
def append_sequence_step(self, code, step_delay):
def append_sequence_step(self, code, step_delay, send_immediately=True):
if code not in self.event_code_sequences.keys():
raise Exception(
f"Eventcode {code} is not within the allowed or configured eventcodes for the sequencer"
)
oldlength = self.length.get_current_value()
newlength = oldlength + step_delay
changes = []
self.new_sequences = {}
for i, ec in self.event_code_sequences.items():
if oldlength == 0:
o = []
else:
o = list(ec.get_current_value())
if i == code:
n = o + [0] * (newlength - oldlength - 1) + [1]
ind = newlength - oldlength - 1
if ind < 0:
o[newlength - 1] = 1
n = o
else:
n = o + [0] * (newlength - oldlength - 1) + [1]
else:
n = o + [0] * (newlength - oldlength)
self.new_sequences[i] = n
# print(o, n)
changes.append(ec.set_target_value(n))
if send_immediately:
self.send_new_sequences()
def send_new_sequences(self):
for i, n in self.new_sequences.items():
changes = []
changes.append(self.event_code_sequences[i].set_target_value(n))
for change in changes:
change.wait()
# self.event_code_sequences[code]._value[newlength - 1] = 1
lengths = [len(n) for i, n in self.new_sequences.items()]
newlength = lengths[0]
print(f"newlength is {newlength}, lengths are {lengths}")
self.length.set_target_value(newlength).wait()
@@ -142,7 +160,7 @@ class CtaSequencer(Assembly):
tsteps = is_present_array.nonzero()[0]
for step in tsteps:
if not step in seq_red.keys():
seq_red[step] = []
seq_red[int(step)] = []
seq_red[step].append(code)
return seq_red
+48 -3
View File
@@ -1,3 +1,5 @@
from eco.detector.detectors_psi import DetectorBsStream
from eco.devices_general.pipelines_swissfel import Pipeline
from ..elements.assembly import Assembly
from ..devices_general.motors import SmaractStreamdevice, MotorRecord, SmaractRecord
from ..elements.adjustable import AdjustableMemory, AdjustableVirtual
@@ -22,12 +24,13 @@ class TimetoolBerninaUSD(Assembly):
self,
name=None,
processing_pipeline="SARES20-CAMS142-M5_psen_db",
processing_instance="SARES20-CAMS142-M5_psen_db1",
edge_finding_pipeline="SAROP21-ATT01_proc",
processing_instance="SARES20-CAMS142-M5_psen_db",
spectrometer_camera_channel="SARES20-CAMS142-M5:FPICTURE",
spectrometer_pvname="SARES20-CAMS142-M5",
microscope_pvname="SARES20-PROF141-M1",
delaystage_PV="SLAAR21-LMOT-M524:MOTOR_1",
pvname_mirror="SARES23:LIC9",
pvname_mirror="SARES23-LIC:MOT_9",
pvname_zoom="SARES20-MF1:MOT_8",
mirror_in=15,
mirror_out=-5,
@@ -43,7 +46,10 @@ class TimetoolBerninaUSD(Assembly):
self.proc_client = PipelineClient()
self.proc_pipeline = processing_pipeline
self._append(Pipeline,self.proc_pipeline, name='pipeline_projection', is_setting=True)
self.proc_instance = processing_instance
self.proc_pipeline_edge = edge_finding_pipeline
self._append(Pipeline,self.proc_pipeline_edge, name='pipeline_edgefinding', is_setting=True)
self.spectrometer_camera_channel = spectrometer_camera_channel
self._append(
Target_xyz,
@@ -80,7 +86,7 @@ class TimetoolBerninaUSD(Assembly):
CameraBasler,
pvname=microscope_pvname,
name="camera_microscope",
camserver_alias=f"{name} ({microscope_pvname})",
camserver_alias="PROF_KB (SARES20-PROF141-M1)",
is_setting=True,
is_display=False,
)
@@ -128,6 +134,45 @@ class TimetoolBerninaUSD(Assembly):
is_setting=True,
)
# SARES20-CAMS142-M5.bsen_signal_x_profile
# SARES20-CAMS142-M5.processing_parameters
# SARES20-CAMS142-M5.psen_signal_x_profile
#
#
self._append(
DetectorBsStream,
"SARES20-CAMS142-M5.roi_signal_x_profile",
cachannel=None,
name="spectrum_signal",
is_setting=False,
is_display=True,
)
self._append(
DetectorBsStream,
"SARES20-CAMS142-M5.roi_background_x_prof",
cachannel=None,
name="spectrum_background",
is_setting=False,
is_display=True,
)
self._append(
DetectorBsStream,
"SARES20-CAMS142-M5.bsen_signal_x_profilef",
cachannel=None,
name="spectrum_bsen",
is_setting=False,
is_display=True,
)
self._append(
DetectorBsStream,
"SAROP21-ATT01:arrival_time",
cachannel=None,
name="edge_position",
is_setting=False,
is_display=True,
)
def get_online_data(self):
self.online_monitor = TtProcessor()
+86 -16
View File
@@ -1,7 +1,9 @@
import json
import importlib
from pathlib import Path
from eco.elements.protocols import InitialisationWaitable
import sys
from time import time
from colorama import Fore as _color
from functools import partial
@@ -265,10 +267,16 @@ class Namespace(Assembly):
# self.name = name
self.lazy_items = {}
self.initialized_items = {}
self.failed_items = {}
self.initialisation_times = {}
self.names_without_alias = []
self.root_module = root_module
self.alias_namespace = alias_namespace
@property
def initialisation_times_sorted(self):
return dict(sorted(self.initialisation_times.items(), key=lambda w: w[1]))
@property
def initialized_names(self):
return set(self.initialized_items.keys())
@@ -277,9 +285,13 @@ class Namespace(Assembly):
def lazy_names(self):
return set(self.lazy_items.keys())
@property
def failed_names(self):
return set(self.failed_items.keys())
@property
def all_names(self):
return self.initialized_names | self.lazy_names
return self.initialized_names | self.lazy_names | self.failed_names
def init_name(self, name, verbose=True, raise_errors=False):
# for name in self.all_names:
@@ -289,17 +301,28 @@ class Namespace(Assembly):
# if verbose:
# print(("(%s)" % (name)).ljust(25), end="")
# sys.stdout.flush()
starttime = time()
try:
dir(self.get_obj(name))
titem = self.get_obj(name)
if isinstance(titem, InitialisationWaitable):
titem._wait_for_initialisation()
else:
dir(titem)
if verbose:
print((_color.GREEN + "OK" + _color.RESET).rjust(5))
print(
f"{time()-starttime} s "
+ (_color.GREEN + "OK" + _color.RESET).rjust(5)
)
sys.stdout.flush()
except Exception as expt:
# tb = traceback.format_exc()
if verbose:
print((_color.RED + "FAILED" + _color.RESET).rjust(5))
print(
f"{time()-starttime} s "
+ (_color.RED + "FAILED" + _color.RESET).rjust(5)
)
# print(sys.exc_info())
if raise_errors:
raise expt
@@ -312,11 +335,19 @@ class Namespace(Assembly):
max_workers=5,
N_cycles=4,
silent=True,
giveup_failed=True,
exclude_names=[],
):
starttime = time()
if self.failed_names:
print(
f"WARNING - previously hard failed items are NOT initialized:\n{self.failed_names} "
)
if silent:
self.silently_initializing = True
print(
f"Initializeing all items in namespace {self.name} silently in background.\n Be aware of unrelated output!"
f"Initializing all items in namespace {self.name} silently in background.\n Be aware of unrelated output!"
)
def init():
@@ -325,7 +356,7 @@ class Namespace(Assembly):
self.exc_init.submit(
self.init_name, name, verbose=verbose, raise_errors=raise_errors
)
for name in self.all_names
for name in (self.all_names - set(exclude_names))
]
self.exc_init.shutdown(wait=True)
self.exc_init = ThreadPoolExecutor(max_workers=1)
@@ -333,15 +364,25 @@ class Namespace(Assembly):
self.exc_init.submit(
self.init_name, name, verbose=verbose, raise_errors=raise_errors
)
for name in (self.all_names - self.initialized_names)
for name in (
self.all_names - self.initialized_names - set(exclude_names)
)
]
self.exc_init.shutdown(wait=True)
self.silently_initializing = False
if giveup_failed:
failed_names = 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))
print(
"Failed objects: "
+ ", ".join(self.lazy_names.union(self.failed_names))
)
print(f"Initialisation took {time()-starttime} seconds")
Thread(target=init).start()
else:
@@ -354,10 +395,14 @@ class Namespace(Assembly):
lambda name: self.init_name(
name, verbose=verbose, raise_errors=raise_errors
),
self.all_names,
self.all_names
- self.initialized_names
- set(exclude_names),
),
description="Initializing ...",
total=len(self.all_names),
total=len(
self.all_names - self.initialized_names - set(exclude_names)
),
transient=True,
)
)
@@ -369,21 +414,32 @@ class Namespace(Assembly):
lambda name: self.init_name(
name, verbose=verbose, raise_errors=raise_errors
),
self.all_names,
self.all_names
- self.initialized_names
- set(exclude_names),
),
description="Initializing ...",
total=len(self.all_names),
total=len(
self.all_names - self.initialized_names - set(exclude_names)
),
transient=True,
)
)
# )
# # )
if giveup_failed:
failed_names = 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))
print(
"Failed objects: "
+ ", ".join(self.lazy_names.union(self.failed_names))
)
print(f"Initialisation took {time()-starttime} seconds")
# if verbose:
# print(("Configuring %s " % (name)).ljust(25), end="")
@@ -406,7 +462,7 @@ class Namespace(Assembly):
# if raise_errors:
# raise expt
def get_initialized_aliases(self):
def get_initialized_aliases(self, channeltypes=[]):
aliases = []
has_no_aliases = []
for tn, tv in self.initialized_items.items():
@@ -414,6 +470,13 @@ class Namespace(Assembly):
aliases += tv.alias.get_all()
except:
has_no_aliases.append(tn)
aliases_out = []
for channeltype in channeltypes:
for alias in aliases:
if alias["channeltype"] == channeltype:
aliases_out.append(alias)
if not channeltypes:
aliases_out = aliases
return aliases, has_no_aliases
def append_obj(
@@ -422,6 +485,7 @@ class Namespace(Assembly):
if lazy:
def init_local():
starttime = time()
if module_name:
obj_maker = getattr(import_module(module_name), obj_factory)
else:
@@ -432,7 +496,11 @@ class Namespace(Assembly):
else:
obj_initialized = obj_maker(*args, **kwargs)
self.initialized_items[name] = self.lazy_items.pop(name)
try:
self.initialized_items[name] = self.lazy_items.pop(name)
except KeyError:
self.initialized_items[name] = self.failed_items.pop(name)
self.initialisation_times[name] = time() - starttime
if hasattr(obj_initialized, "alias"):
self._append(
obj_initialized,
@@ -462,6 +530,7 @@ class Namespace(Assembly):
return obj_lazy
else:
starttime = time()
if module_name:
obj_maker = getattr(import_module(module_name), obj_factory)
else:
@@ -471,6 +540,7 @@ class Namespace(Assembly):
except TypeError:
obj = obj_maker(*args, **kwargs)
self.initialized_items[name] = obj
self.initialisation_times[name] = time() - starttime
if self.root_module:
sys.modules[self.root_module].__dict__[name] = obj
if hasattr(obj, "alias"):
+83 -7
View File
@@ -1,7 +1,27 @@
from pathlib import Path
import elog as _elog_ha
from getpass import getuser as _getuser
from getpass import getpass as _getpass
import os, datetime, subprocess
from markdown import markdown
import urllib3
urllib3.disable_warnings()
######################
class ElogsMultiplexer:
def __init__(self, *args):
self.elogs = args
def post(self, *args, **kwargs):
mids = []
for elog in self.elogs:
mids.append(elog.post(*args, **kwargs))
return mids
##########################
def getDefaultElogInstance(url, **kwargs):
@@ -29,17 +49,74 @@ class Elog:
self._screenshot = Screenshot(screenshot_directory)
self.read = self._log.read
def post(self, *args, Title=None, Author=None, Encoding="html", **kwargs):
def post(
self,
*args,
text_encoding="markdown",
markdown_extensions=["fenced_code"],
tags=[],
Title=None,
Author=None,
**kwargs,
):
""" """
if Encoding == "html":
args = list(args)
args[0] = args[0].replace("\n", "<br />\n")
message = ""
file_paths = []
for targ in args:
if not (isinstance(targ, str) or isinstance(targ, Path)):
raise Exception(
"Log messages should be of type string or pathlib.Path!"
)
if isinstance(targ, Path):
if Path(targ).expanduser().exists():
print("file exists")
file_paths.append(targ.as_posix())
if targ.suffix[1:] in ["jpg", "png"]:
if text_encoding in ["markdown", "html"]:
message += f'<p><img alt="" src="temporarypath-attachment_{len(file_paths)-1}" /></p>'
else:
targ = str(targ)
if text_encoding == "markdown":
message += markdown(targ, extensions=markdown_extensions)
Encoding = "html"
elif text_encoding == "html":
Encoding = "html"
else:
message += targ + "\n"
Encoding = "plain"
if not Author:
Author = self.user
return self._log.post(
*args, Title=Title, Author=Author, Encoding=Encoding, **kwargs
if file_paths:
attachments = file_paths
else:
attachments = None
mid = self._log.post(
message,
attachments=attachments,
Title=Title,
Author=Author,
Encoding=Encoding,
**kwargs,
)
if file_paths:
pm, patt, pa = self._log.read(mid)
for ntpa, tpa in enumerate(pa):
filename = "".join(Path(tpa).parts[-1].split("_")[2:])
print(filename)
Nocc = pm.count(f"temporarypath-attachment_{ntpa}")
print(Nocc)
if Nocc:
pm = pm.replace(f"temporarypath-attachment_{ntpa}", tpa)
self._log.post(pm, msg_id=mid)
return mid
def screenshot(self, message="", window=False, desktop=False, delay=3, **kwargs):
filepath = self._screenshot.shoot()[0]
kwargs.update({"attachments": [filepath]})
@@ -55,7 +132,6 @@ class Screenshot:
self.user = kwargs["user"]
def show_directory(self):
p = subprocess.Popen(
["nautilus", self._screenshot_directory],
stdout=subprocess.PIPE,
+144
View File
@@ -0,0 +1,144 @@
from functools import lru_cache
from markdown import markdown
from scilog import SciLog, LogbookMessage
from getpass import getuser as _getuser
from getpass import getpass as _getpass
import os, datetime, subprocess
import urllib3
from pathlib import Path
from eco.elements.assembly import Assembly
urllib3.disable_warnings()
def getDefaultElogInstance(
url="https://scilog.psi.ch/api/v1",
user="swissfelaramis-bernina@psi.ch",
pgroup=None,
**kwargs,
):
home = str(Path.home())
if not user:
user = _getuser()
if not ("password" in kwargs.keys()):
try:
with open(os.path.join(home, ".scilog_psi"), "r") as f:
_pw = f.read().strip()
except:
print(f"Enter scilog password for user: {user}")
_pw = _getpass()
kwargs.update(dict(password=_pw))
log = SciLog(url, options := {"username": user, "password": kwargs["password"]})
if pgroup:
lbs = log.get_logbooks(ownerGroup=pgroup)
if len(lbs) > 1:
raise Exception(f"Found more than one elog for user group {pgroup}")
log.select_logbook(lbs[0])
return log, user
class Elog(Assembly):
def __init__(
self,
url="https://scilog.psi.ch/api/v1",
pgroup_adj=None,
screenshot_directory="",
name="scilog",
**kwargs,
):
super().__init__(name=name)
self.scilog_url = url
self._append(pgroup_adj, name="pgroup")
dummy, self.user = getDefaultElogInstance(
url, pgroup=pgroup_adj.get_current_value(), **kwargs
)
self.__class__._log = property(
lambda dum: self._get_scilog_dynamically(
self.scilog_url, self.pgroup.get_current_value()
)
)
self._screenshot = Screenshot(screenshot_directory)
# self.read = self._log.read
@lru_cache
def _get_scilog_dynamically(self, url, pgroup):
log, user = getDefaultElogInstance(url, pgroup=pgroup)
self.user = user
return log
def post(
self,
*args,
tags=[],
text_encoding="markdown",
markdown_extensions=["fenced_code"],
**kwargs,
):
"""args can be text or pathlibPath instances (for files to be uploaded)"""
msg = LogbookMessage()
for targ in args:
if not (isinstance(targ, str) or isinstance(targ, Path)):
raise Exception("Log messages should be of type string!")
if isinstance(targ, Path):
if Path(targ).expanduser().exists():
print("file exists")
msg.add_file(targ.as_posix())
else:
targ = str(targ)
if text_encoding == "markdown":
msg.add_text(markdown(targ, extensions=markdown_extensions))
elif text_encoding == "html":
msg.add_text(targ)
else:
msg.add_text(targ)
for tag in tags:
msg.add_tag(tag)
return self._log.send_logbook_message(msg)
def screenshot(self, message="", window=False, desktop=False, delay=3, **kwargs):
filepath = self._screenshot.shoot()[0]
kwargs.update({"attachments": [filepath]})
self.post(message, **kwargs)
class Screenshot:
def __init__(self, screenshot_directory="", **kwargs):
self._screenshot_directory = screenshot_directory
if not ("user" in kwargs.keys()):
self.user = _getuser()
else:
self.user = kwargs["user"]
def show_directory(self):
p = subprocess.Popen(
["nautilus", self._screenshot_directory],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def shoot(self, message="", window=False, desktop=False, delay=3, **kwargs):
cmd = ["gnome-screenshot"]
if window:
cmd.append("-w")
cmd.append("--delay=%d" % delay)
elif desktop:
cmd.append("--delay=%d" % delay)
else:
cmd.append("-a")
tim = datetime.datetime.now()
fina = "%s-%s-%s_%s-%s-%s" % tim.timetuple()[:6]
if "Author" in kwargs.keys():
fina += "_%s" % user
else:
fina += "_%s" % self.user
fina += ".png"
filepath = os.path.join(self._screenshot_directory, fina)
cmd.append("--file")
cmd.append(filepath)
p = subprocess.call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return filepath, p
+49
View File
@@ -0,0 +1,49 @@
from simple_pid import PID
from ..epics.detector import DetectorPvDataStream
from ..elements.assembly import Assembly
from threading import Thread
from time import sleep
from ..elements.adjustable import AdjustableFS
class Feedback_Timetool(Assembly):
def __init__(self, name=None, pvname=None, control_adj = None, pid=[1, 0.01, 0], output_limits=(-100,100), setpoint=1060, calib_s_per_px=3e-15):
super().__init__(name=name)
self._append(DetectorPvDataStream, pvname, name="monitor")
self.pid = PID(
*pid,
setpoint=0,
output_limits=(output_limits[0]*abs(calib_s_per_px), output_limits[1]*abs(calib_s_per_px)),
sample_time=10,
)
self.control_adj = control_adj
self._append(
AdjustableFS,
"/photonics/home/gac-bernina/eco/configuration/tt_feedback_setpoint",
default_value=setpoint,
name="setpoint",
)
self._append(
AdjustableFS,
"/photonics/home/gac-bernina/eco/configuration/tt_feedback_calib_s_per_px",
default_value=calib_s_per_px,
name="calib_s_per_px",
)
self._running=False
def stop(self):
self._running = False
def run_continuously(self):
while(self._running):
rb_val = self.monitor.get_current_value()
set_val = self.pid(rb_val-self.setpoint())*self.calib_s_per_px()
print(f"moving phase control adjustable by {set_val}")
self.control_adj.mvr(set_val)
sleep(60)
def start_feedback(self):
self._running=True
self.feedback = Thread(target = self.run_continuously)
+676 -26
View File
@@ -2,12 +2,28 @@ from diffcalc.hkl.calc import HklCalculation
from diffcalc.hkl.constraints import Constraints
from diffcalc.hkl.geometry import Position
from diffcalc.ub import calc as dccalc
import pandas as pd
import numpy as np
from datetime import datetime
import os
from PIL import Image
# from diffcalc.ub import calc calc import UBCalculation, Crystal
from eco.elements.assembly import Assembly
from eco.elements.adjustable import AdjustableMemory
from eco.elements.adjustable import AdjustableMemory, AdjustableFS, AdjustableVirtual, DummyAdjustable
from typing import Tuple, Optional
from eco.elements.adj_obj import AdjustableObject
from epics import PV
class Diffractometer_Dummy(Assembly):
def __init__(self, *args, name=None, **kwargs):
Assembly.__init__(self, name=name)
self.configuration = ["base", "arm"]
adjs = ["nu", "mu", "delta", "eta", "chi", "phi"]
for adj in adjs:
self._append(DummyAdjustable, name=adj, is_setting=True, is_display=True, limits=[-180,180])
diffractometer_dummy = Diffractometer_Dummy(name = "dummy")
class CrystalNew(Assembly):
def __init__(self, *args, name=None, **kwargs):
@@ -32,26 +48,374 @@ class CrystalNew(Assembly):
)
class Crystals(Assembly):
def __init__(self, diffractometer_you=diffractometer_dummy, name=None):
super().__init__(name=name)
self.diffractometer = diffractometer_you
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_list",
name="crystal_list",
default_value={},
is_setting=True,
)
for key, meta in self.crystal_list().items():
if self.diffractometer.name in meta:
self._append(
DiffGeometryYou, diffractometer_you=self.diffractometer, name=key
)
def create_crystal(self, name=None):
if name == None:
name = input(
"Please choose a name for your crystal (no spaces or other special characters):"
)
specials = np.array([".", " ", "/", "(", ")", "[", "]"]+list(self.crystal_list().keys()))
in_name = np.array([s in name for s in specials])
if np.any(in_name):
raise Exception(
f"Special character(s) {specials[in_name]} in name not allowed or name already exists"
)
self._append(
DiffGeometryYou,
diffractometer_you=self.diffractometer,
name=name,
is_setting=True,
is_display=False,
)
crystals = self.crystal_list()
crystals[name] = [str(datetime.now()), self.diffractometer.name]
self.crystal_list.mv(crystals)
self.__dict__[name].new_ub()
def delete_crystal(self, name=None):
"""
Delete crystal with a given name, deletes also the files.
"""
if name==None:
crystal_names = list(self.crystal_list().keys())
input_message = "Select the crystal to delete:\nq) quit\n"
for index, crystal in enumerate(crystal_names):
input_message += f'{index:2}) {crystal:15}\n'
input_message += 'Your choice: '
idx = ''
while idx not in range(len(crystal_names)):
if idx == 'q':
break
idx = int(input(input_message))
print(f'Selected crystal: {crystal_names[idx]}')
name = crystal_names[idx]
sure = "n"
sure = input(
f"are you sure you want to permanently remove the crystal {name} and its UB matrix and memories (y/n)? "
)
if sure == "y":
crystals = self.crystal_list()
meta = crystals[name]
if self.diffractometer.name in meta:
self.deactivate_crystal(name=name)
removed = crystals.pop(name)
del removed
self.crystal_list.mv(crystals)
attrs = [
"unit_cell",
"u_matrix",
"ub_matrix",
"orientations",
"reflections",
"constraints",
]
for a in attrs:
if os.path.exists(
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_{a}"
):
os.remove(
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_{a}"
)
def activate_crystal(self, name=None):
crystals = self.crystal_list()
if name==None:
inactive_crystals = [k for k in crystals.keys() if not self.diffractometer.name in crystals[k]]
idx = ''
input_message = "Select the crystal to activate:\nq) quit\n"
for index, crystal in enumerate(inactive_crystals):
input_message += f'{index:2}) {crystal:15}\n'
input_message += 'Your choice: '
while idx not in range(len(inactive_crystals)):
if idx == 'q':
break
idx = int(input(input_message))
print(f'Activated crystal: {inactive_crystals[idx]}')
name = inactive_crystals[idx]
self._append(
DiffGeometryYou,
diffractometer_you=self.diffractometer,
name=name,
is_setting=True,
is_display=False,
)
meta = crystals[name]
if not self.diffractometer.name in meta:
meta = meta + [self.diffractometer.name]
crystals[name] = meta
self.crystal_list.mv(crystals)
def deactivate_crystal(self, name=None):
crystals = self.crystal_list()
if name==None:
active_crystals = [k for k in crystals.keys() if self.diffractometer.name in crystals[k]]
idx = ''
input_message = "Select the crystal to activate:\nq) quit\n"
for index, crystal in enumerate(active_crystals):
input_message += f'{index:2}) {crystal:15}\n'
input_message += 'Your choice: '
while idx not in range(len(active_crystals)):
if idx == "q":
break
idx = int(input(input_message))
print(f'Selected crystal: {active_crystals[idx]}')
name = active_crystals[idx]
meta = crystals[name]
if self.diffractometer.name in meta:
i = meta.index(self.diffractometer.name)
meta.pop(i)
crystals[name] = meta
self.crystal_list.mv(crystals)
removed = self.__dict__.pop(name)
self.alias.pop_object(removed.alias)
del removed
class DiffGeometryYou(Assembly):
def __init__(self, diffractometer_you=None, name=None):
super().__init__(name=name)
# self._append(diffractometer_you,call_obj=False, name='diffractometer')
self._append(AdjustableMemory, {}, name="contraints")
self._append(AdjustableMemory, {}, name="unit_cell")
self._append(AdjustableMemory, {}, name="U_matrix")
self._append(AdjustableMemory, {}, name="UB_matrix")
self._append(AdjustableMemory, [], name="orientations")
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_unit_cell",
name="unit_cell",
default_value={
"name": "",
"a": 1,
"b": 1,
"c": 1,
"alpha": 90,
"beta": 90,
"gamma": 90,
},
is_setting=True,
)
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_u_matrix",
name="u_matrix",
default_value=[],
is_setting=True,
is_display=False,
)
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_ub_matrix",
name="ub_matrix",
default_value=[],
is_setting=True,
is_display=False,
)
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_orientations",
name="orientations",
default_value=[],
is_setting=True,
)
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_reflections",
name="reflections",
default_value=[],
is_setting=True,
)
self.diffractometer = diffractometer_you
cons = {
"mu": None,
"eta": None,
"chi": None,
"phi": None,
"delta": None,
"nu": None,
"a_eq_b": None,
"bin_eq_bout": None,
"betain": None,
"betaout": None,
"qaz": None,
"naz": None,
"alpha": None,
"beta": None,
"bisect": None,
"psi": None,
"omega": None,
}
self._append(
AdjustableFS,
f"/photonics/home/gac-bernina/eco/configuration/crystals/{name}_constraints",
name="_constraints",
default_value=cons,
is_setting=True,
is_display=False,
)
self._append(
AdjustableObject,
self._constraints,
name="constraints",
is_setting=False,
is_display="recursive",
)
def get_position_angles(self):
nu = self.diffractometer.nu.get_current_value()
mu = self.diffractometer.mu.get_current_value()
delta = self.diffractometer.delta.get_current_value()
eta = self.diffractometer.eta.get_current_value()
chi = self.diffractometer.chi.get_current_value()
phi = self.diffractometer.phi.get_current_value()
adjs = ["nu", "mu", "delta", "eta", "chi", "phi"]
cfg = []
if hasattr(self.diffractometer, "configuration"):
cfg = self.diffractometer.configuration
if "kappa" in cfg:
adjs = ["nu", "mu", "delta", "eta_kap", "kappa", "phi_kap"]
self._diff_adjs = {
adj: self.diffractometer.__dict__[adj] if adj in self.diffractometer.__dict__.keys() else DummyAdjustable(name = adj+"dummy")
for adj in adjs
}
def get_h(*args, **kwargs):
return self.calc_hkl()[0]
def set_h(val):
return self._calc_angles_unique_diffractometer([val, None, None])
def get_k(*args, **kwargs):
return self.calc_hkl()[1]
def set_k(val):
return self._calc_angles_unique_diffractometer([None, val, None])
def get_l(*args, **kwargs):
return self.calc_hkl()[2]
def set_l(val):
return self._calc_angles_unique_diffractometer([None, None, val])
def get_hkl(*args, **kwargs):
return self.calc_hkl()
def set_hkl(val):
return self._calc_angles_unique_diffractometer(val)
self._append(
AdjustableVirtual, list(self._diff_adjs.values()), get_h, set_h, name="h"
)
self._append(
AdjustableVirtual, list(self._diff_adjs.values()), get_k, set_k, name="k"
)
self._append(
AdjustableVirtual, list(self._diff_adjs.values()), get_l, set_l, name="l"
)
self._append(
AdjustableVirtual,
list(self._diff_adjs.values()),
get_hkl,
self._calc_angles_unique_diffractometer,
name="hkl",
)
self.recalculate()
def convert_from_you(self, **kwargs):
cfg = self.diffractometer.configuration
if "kappa" in cfg:
eta_kap, kappa, phi_kap = self.diffractometer.calc_you2kappa(
kwargs["eta"], kwargs["chi"], kwargs["phi"]
)
kwargs.update({"eta_kap": eta_kap, "kappa": kappa, "phi_kap": phi_kap})
return {key:kwargs[key] for key in self._diff_adjs.keys()}
def convert_to_you(
self,
nu=None,
mu=None,
delta=None,
eta=None,
chi=None,
phi=None,
eta_kap=None,
kappa=None,
phi_kap=None,
):
cfg = self.diffractometer.configuration
if "kappa" in cfg:
eta, chi, phi = self.diffractometer.calc_kappa2you(eta_kap, kappa, phi_kap)
return nu, mu, delta, eta, chi, phi
def get_diffractometer_angles(self):
### assume that all angles exist in diffractometer at least as manual adjustable ###
nu, mu, delta, eta, chi, phi = self.convert_to_you(
**{key: adj() for key, adj in self._diff_adjs.items()}
)
return mu, delta, nu, eta, chi, phi
def _calc_angles_unique_diffractometer(self, hkl):
angles = self.calc_angles_unique(*hkl)
angles_diff_dict = self.convert_from_you(**angles)
return [angles_diff_dict[tk] for tk in self._diff_adjs.keys()]
def check_target_value_within_limits(self, **kwargs):
### virtual adjustables got a new function check_target_value_within_limits(values)
in_lims = []
target_dict = self.convert_from_you(**kwargs)
for axname, target_value in target_dict.items():
adj = self._diff_adjs[axname]
# for val, adj in zip(target_dict, self._diff_adjs.values()):
if hasattr(adj, "get_limits"):
lim_low, lim_high = adj.get_limits()
in_lims.append((lim_low < target_value) and (target_value < lim_high))
# print(axname,lim_low,target_value,lim_high)
else:
raise Exception(f"Failed to get limits of adjustable {adj.name}")
return all(in_lims)
def new_ub(self):
### missing: clear ub ###
### missing: check ub ###
crystal_name = input(f"Name of the crystal: ({self.name})" or str(self.name))
a = float(input(f"Lattice constant a (1): ") or 1)
b = float(input(f"Lattice constant b ({a}): ") or a)
c = float(input(f"Lattice constant c ({a}): ") or a)
alpha = float(input("Angle alpha (90): ") or 90)
beta = float(input(f"Angle beta ({alpha}): ") or alpha)
gamma = float(input(f"Angle gamma ({alpha}): ") or alpha)
im = Image.open(
"/photonics/home/gac-bernina/eco/configuration/crystals/you_diffractometer.png"
)
normal = [
float(val)
for val in input(
"(h,k,l) surface normal (along YOU z-axis), e.g. 0,0,1: "
).split(",")
or [0, 0, 1]
]
inplane = [
float(val)
for val in input(
"(h,k,l) in-plane orientation along beam (YOU y-axis), e.g. 1,0,0: "
).split(",")
or [1, 0, 0]
]
self.set_unit_cell(crystal_name, a, b, c, alpha, beta, gamma)
self.add_orientation(normal, (0, 0, 1), tag="surface normal")
self.add_orientation(
inplane, (0, 1, 0), tag="in-plane along x-ray beam direction"
)
self.calc_ub()
print(
"UB was calculated - next please set the constraints (.constraints) and the limits of the diffractometer motors"
)
def set_unit_cell(
self, name_crystal, a=None, b=None, c=None, alpha=None, beta=None, gamma=None
):
@@ -70,16 +434,28 @@ class DiffGeometryYou(Assembly):
def recalculate(self):
self.ubcalc = dccalc.UBCalculation("you")
# self.ubcalc.n_phi = [0,0,1]
uc = self.unit_cell()
self.ubcalc.set_lattice(uc.pop("name"), **uc)
for ori in self.orientations():
self.ubcalc.add_orientation(ori.pop("hkl"), ori.pop("xyz"), **ori)
for refl in self.reflections():
position = Position(*refl.pop("position"))
self.ubcalc.add_reflection(
refl.pop("hkl"), position, refl.pop("energy") * 1e-3, **refl
)
self._u_ub_to_dc()
def add_reflection(
self,
hkl,
position,
energy,
mu=None,
delta=None,
nu=None,
eta=None,
chi=None,
phi=None,
energy=None,
tag=None,
):
"""Add a reference reflection.
@@ -91,20 +467,43 @@ class DiffGeometryYou(Assembly):
----------
hkl : Tuple[float, float, float]
hkl index of the reflection
position: Position
list of diffractometer angles in internal representation in degrees
mu, delta, nu, eta, chi, phi: float
diffractometer angles in degrees, if not given, the current diffractometer angles are used
energy : float
energy of the x-ray beam
energy of the x-ray beam, if not given, the mono or machine energy are used depending on the beamline mode
tag : Optional[str], default = None
identifying tag for the reflection
"""
setvals = [mu, delta, nu, eta, chi, phi]
curvals = self.get_diffractometer_angles()
angs = [
curval if setval == None else setval
for setval, curval in zip(setvals, curvals)
]
if energy is None:
energy = self.get_energy()
position = Position(*angs)
self.ubcalc.add_reflection(hkl, position, energy, tag=tag)
self.reflections.set_target_value(
self.reflections()
+ [{"hkl": hkl, "position": position, "energy": energy, "tag": tag}]
+ [{"hkl": hkl, "position": angs, "energy": energy, "tag": tag}]
)
def del_reflection(self, idx):
"""Delete a reference reflection.
Parameters
----------
idx : int
index of the deleted reflection
"""
self.ubcalc.del_reflection(idx)
refls = self.reflections()
removed = refls.pop(idx)
self.reflections.set_target_value(refls)
print(f"Removed reflection {removed}")
self.recalculate()
def add_orientation(self, hkl, xyz, position=None, tag=None):
"""Add a reference orientation.
@@ -122,19 +521,270 @@ class DiffGeometryYou(Assembly):
tag : str
identifying tag for the reflection
"""
self.recalculate()
self.ubcalc.add_orientation(hkl, xyz, position=None, tag=None)
self.orientations.set_target_value(
self.orientations()
+ [{"hkl": hkl, "xyz": xyz, "position": position, "tag": tag}]
)
self.recalculate()
def calc_ub(self, *args, **kwargs):
self.ubcalc.calc_ub(*args, **kwargs)
def del_orientation(self, idx):
"""Delete a reference reflection.
def fit_ub(self, *args, **kwargs):
self.ubcalc.fit_ub(*args, **kwargs)
Parameters
----------
idx : int
index of the deleted reflection
"""
refls = self.orientations()
removed = refls.pop(idx)
self.orientations.set_target_value(refls)
print(f"Removed reflection {removed}")
self.recalculate()
def calc_ub(self, idx1=0, idx2=1):
"""Calculate UB matrix.
Calculate UB matrix using two reference reflections and/or
reference orientations.
By default use the first two reference reflections when provided.
If one or both reflections are not available use one or two reference
orientations to complement mission reflection data.
Parameters
----------
idx1: int or str, optional
The index or the tag of the first reflection or orientation.
idx2: int or str, optional
The index or the tag of the second reflection or orientation.
"""
self.recalculate()
self.ubcalc.calc_ub(idx1, idx2)
self._u_ub_from_dc()
def show_you_geometry(self):
im = Image.open(
"/photonics/home/gac-bernina/eco/configuration/crystals/you_diffractometer.png"
)
im.show()
def refine_ub(
self,
hkl,
mu=None,
delta=None,
nu=None,
eta=None,
chi=None,
phi=None,
energy=None,
refine_lattice=False,
refine_umatrix=False,
):
"""
Refine UB matrix to using single reflection.
Refine UB matrix to match diffractometer position for the specified
reflection. Refined U matrix will be accurate up to an azimuthal rotation
around the specified scattering vector.
Parameters
----------
hkl: Tuple[float, float, float] Miller indices of the reflection.
pos: Position Diffractometer position object.
wavelength: float Radiation wavelength.
refine_lattice: Optional[bool], default = False
Apply refined lattice parameters to the current UB calculation object.
refine_umatrix: Optional[bool], default = False
Apply refined U matrix to the current UB calculation object.
Returns
-------
Tuple[np.ndarray, Tuple[str, float, float, float, float, float, float]]
Refined U matrix as NumPy array and refined crystal lattice parameters.
"""
if refine_lattice:
print("fitting the lattice is not yet implemented")
return
setvals = [mu, delta, nu, eta, chi, phi]
curvals = self.get_diffractometer_angles()
angs = [
curval if setval == None else setval
for setval, curval in zip(setvals, curvals)
]
if energy is None:
energy = self.get_energy()
wl = self.en2lam(energy)
position = Position(*angs)
self.recalculate()
self.ubcalc.refine_ub(
hkl,
position=position,
wavelength=wl,
refine_lattice=refine_lattice,
refine_umatrix=refine_umatrix,
)
self._u_ub_from_dc()
def fit_ub(self, indices=None, refine_lattice=False, refine_umatrix=True):
"""Refine UB matrix using reference reflections.
Parameters
----------
indices: Sequence[Union[str, int]]
List of reference reflection indices or tags.
refine_lattice: Optional[bool], default = False
Apply refined lattice parameters to the current UB calculation object.
refine_umatrix: Optional[bool], default = False
Apply refined U matrix to the current UB calculation object.
Returns
-------
Tuple[np.ndarray, Tuple[str, float, float, float, float, float, float]]
Refined U matrix as NumPy array and refined crystal lattice parameters.
"""
self.recalculate()
if indices is None:
indices = list(range(len(self.reflections())))
ub, lat = self.ubcalc.fit_ub(
indices, refine_lattice=refine_lattice, refine_umatrix=refine_umatrix
)
if refine_umatrix:
print("\nFitted UB matrix applied")
else:
print(
"\nFitted UB matrix not applied. To apply it, set refine_umatrix=True"
)
print(ub)
if refine_lattice:
print("\nFitted lattice applied")
else:
print("\nFitted lattice not applied. To apply it, set refine_lattice=True")
for k, val in {
"name": lat[0],
"a": lat[1],
"b": lat[2],
"c": lat[3],
"alpha": lat[4],
"beta": lat[5],
"gamma": lat[6],
}.items():
print(f"{k:8}: {val}")
self._u_ub_from_dc()
if refine_lattice:
self._lat_from_dc()
def calc_angles(self, h=None, k=None, l=None, energy=None):
"""calculate diffractometer angles for a given h,k,l and energy in eV.
If any of the h, k, l are not given, their current value is used instead.
energy: float energy of the x-ray beam, if not given, the mono or machine energy are used depending on the beamline mode
Shows all solutions neglecting diffractometer limits"""
setvals = [h, k, l]
curvals = [self.h, self.k, self.l]
h, k, l = [
curval() if setval == None else setval
for setval, curval in zip(setvals, curvals)
]
self.recalculate()
cons = Constraints(self._constraints())
hklcalc = HklCalculation(self.ubcalc, cons)
if energy is None:
energy = self.get_energy()
lam = self.en2lam(energy)
result = hklcalc.get_position(h, k, l, lam)
result = pd.concat(
[
pd.DataFrame.from_dict(
{**tres[0].asdict, **tres[1]}, orient="index", columns=[f"sol. {n}"]
)
for n, tres in enumerate(result)
],
axis=1,
)
return result.T
def calc_angles_unique(self, h=None, k=None, l=None, energy=None):
"""calculate unique solution of diffractometer angles for a given h,k,l and energy in eV.
If any of the h, k, l are not given, their current value is used instead.
If the energy is not given, the monochromator energy is used."""
df = self.calc_angles(h, k, l, energy)
in_lims = np.array(
[
self.check_target_value_within_limits(**df.loc[idx].to_dict())
for idx in df.index
]
)
idx_in = df.index[in_lims]
idx_out = df.index[~in_lims]
if len(idx_in) > 1:
print(
f"There is not a unique angular configuration to reach ({h},{k},{l}), please change the diffractometer motor soft limits to allow only one of the solutions shown below:"
)
print(df.loc[idx_in])
raise Exception("No unique solution")
elif len(idx_in) == 0:
print(
"There is no angular configuration, which is allowed for the current diffractometer motor soft limits. please check the diffractometer limits."
)
print("Solutions")
print(df)
raise Exception("No unique solution")
solution_unique = df.loc[idx_in[0]]
return solution_unique.to_dict()
def calc_hkl(
self, mu=None, delta=None, nu=None, eta=None, chi=None, phi=None, energy=None
):
"""calculate (h,k,l) for given diffractometer angles and energy in eV.
If any of the diffractometer angles are not given, their current value is used instead.
If the energy is not given, the monochromator energy is used"""
setvals = [mu, delta, nu, eta, chi, phi]
curvals = self.get_diffractometer_angles()
angs = [
curval if setval == None else setval
for setval, curval in zip(setvals, curvals)
]
pos = Position(*angs)
self.recalculate()
if energy is None:
energy = self.get_energy()
lam = self.en2lam(energy)
cons = Constraints(self._constraints())
hklcalc = HklCalculation(self.ubcalc, cons)
try:
hkl = hklcalc.get_hkl(pos=pos, wavelength=lam)
except Exception as e:
print(str(e))
return
return hkl
def get_energy(self):
energy = PV("SAROP21-ARAMIS:ENERGY").value
return energy
def _u_ub_from_dc(self):
self.ub_matrix(self.ubcalc.UB.tolist())
self.u_matrix(self.ubcalc.U.tolist())
def _u_ub_to_dc(self):
if len(self.ub_matrix()) > 0:
self.ubcalc.set_ub(self.ub_matrix())
self.ubcalc.set_u(self.u_matrix())
def _lat_from_dc(self):
self.set_unit_cell(*self.ubcalc.crystal.get_lattice())
def en2lam(self, en):
"""input: energy in eV, returns wavelength in A"""
return 12398.419843320025 / en
def lam2en(self, lam):
"""input: wavelength in A, returns energy in eV"""
return 12398.419843320025 / lam
pass
# def __init__(sel):
+112 -694
View File
@@ -8,7 +8,7 @@ from subprocess import call
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
@@ -32,675 +32,6 @@ pd.options.display.width = None
pd.set_option("display.float_format", lambda x: "%.5g" % x)
class Run_Table:
def __init__(
self,
pgroup=None,
devices=None,
channels_ca={"pulse_id": "SLAAR11-LTIM01-EVR0:RX-PULSEID"},
name=None,
):
### Load device and alias_namespace after init of other devices ###
devices = eco.__dict__[devices]
self.devices = devices
self.name = name
self.adj_df = DataFrame()
self.unit_df = DataFrame()
self.gspread_key_df = None
self.gspread_key_file_name = (
f"/sf/bernina/config/src/python/gspread/gspread_keys"
)
self._channels_ca = channels_ca
### 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.keys = "metadata midir xrd energy transmission delay lxt pulse_id att_self att_fe_self"
self.key_order = "metadata xrd midir env_thc temperature1_rbk temperature2_rbk time name gps gps_hex thc ocb eos las lxt phase_shifter mono att att_fe slit_und slit_switch slit_att slit_kb slit_cleanup pulse_id mono_energy_rbk att_transmission att_fe_transmission"
spreadsheet_key = (None,)
self.init_runtable(pgroup)
### dicts holding adjustables and bad (not connected) adjustables ###
self.adjustables = {}
self.bad_adjustables = {}
self.units = {}
###parsing options
self._parse_exclude_keys = "status_indicators settings_collection status_indicators_collection presets memory _elog _currentChange _flags __ alias namespace daq scan evr _motor Alias".split(
" "
)
self._parse_exclude_class_types = (
"__ alias namespace daq scan evr _motor Alias AdjustablePv AxisPTZ".split(
" "
)
)
self._adj_exclude_class_types = (
"__ alias namespace daq scan evr _motor Alias".split(" ")
)
pd.options.display.max_rows = 100
pd.options.display.max_columns = 50
pd.set_option("display.float_format", lambda x: "%.5g" % x)
def create_rt_spreadsheet(self, pgroup):
self.gc = gspread.authorize(self._credentials)
spreadsheet = self.gc.create(
title=f"run_table_{pgroup}", 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.gspread_key_file_name + ".pkl"):
self.gspread_key_df = pd.read_pickle(self.gspread_key_file_name + ".pkl")
self.gspread_key_df = self.gspread_key_df.append(gspread_key_df)
self.gspread_key_df.to_pickle(self.gspread_key_file_name + ".pkl")
else:
self.gspread_key_df.to_pickle(self.gspread_key_file_name + ".pkl")
def init_runtable(self, pgroup):
if os.path.exists(self.gspread_key_file_name + ".pkl"):
self.gspread_key_df = pd.read_pickle(self.gspread_key_file_name + ".pkl")
if self.gspread_key_df is not None and str(pgroup) in self.gspread_key_df.index:
spreadsheet_key = self.gspread_key_df["keys"][f"{pgroup}"]
else:
f_create = str(
input(
f"No google spreadsheet id found for pgroup {pgroup}. Create new run_table spreadsheet? (y/n) "
)
)
if f_create == "y":
print("creating")
spreadsheet = self.create_rt_spreadsheet(pgroup=pgroup)
print("created")
gspread_key_df = DataFrame(
{"keys": [spreadsheet.id]}, index=[f"{pgroup}"]
)
spreadsheet_key = spreadsheet.id
else:
f_entermanually = input(
f"Do you want to enter a spreadsheet key for the pgroup {pgroup}? (y/n)"
)
if f_entermanually != "y":
print("Runtable not initialized")
return
spreadsheet_key = str(
input(
f"Please enter the google spreadsheet key, e.g. 1gK--KePLpYCs7U3QfNSPo69XipndbINe1Iz8to9bY1U: "
)
)
gspread_key_df = DataFrame(
{"keys": [spreadsheet_key]}, index=[f"{pgroup}"]
)
self._append_to_gspread_key_df(gspread_key_df)
self._spreadsheet_key = spreadsheet_key
# self.alias_file_name = (
# f"/sf/bernina/data/{pgroup}/res/runtables/{pgroup}_alias_runtable"
# )
self.adj_file_name = (
f"/sf/bernina/data/{pgroup}/res/runtables/{pgroup}_adjustable_runtable"
)
self.unit_file_name = (
f"/sf/bernina/data/{pgroup}/res/runtables/{pgroup}_unit_runtable"
)
self.load()
return
def _query_by_keys(self, keys="", df=None):
if df is None:
df = self.adj_df
keys = keys.split(" ")
if len(df.columns[0]) > 1:
query_df = df[
df.columns[
np.array(
[
np.any([np.any([x in i for x in keys]) for i in col])
for col in df.columns
]
)
]
]
else:
query_df = df[
df.columns[
np.array([np.any([x in col for x in keys]) for col in df.columns])
]
]
return query_df
def query(self, keys="", index=None, values=None, df=None):
"""
function to show saved data. keys is a string with keys separated by a space.
All columns, which contain any of these strings are returned. self.prefix
+ f"{runno:{self.Ndigits}0d}"
+ self.separator
+ "*."
+ self.suffix
Index can be a list od indices.
example: query(keys='xrd delay name', index = [0,5])
will return all columns containing either xrd or delay and show the data for runs 0 and 5
example 2: query(keys = 'xrd delay name', index = ['p1', 'p2'])
will return the same columns for the saved positions 1 and 2
"""
self.load()
# if len(keys) > 0:
# keys += " name"
query_df = self._query_by_keys(keys, df)
if not values is None:
query_df = query_df.query(values)
query_df = query_df.T
if not index is None:
query_df = query_df[index]
return query_df
def _get_values(self):
is_connected = np.array([pv.connected for pv in self._pvs.values()])
filtered_dict = {key: pv.value for key, pv in self._pvs.items() if pv.connected}
return filtered_dict
def _remove_duplicates(self):
self.adj_df = self.adj_df[~self.adj_df.index.duplicated(keep="last")]
# self.alias_df = self.alias_df[~self.alias_df.index.duplicated(keep="last")]
self.unit_df = self.unit_df[~self.unit_df.index.duplicated(keep="last")]
def save(self):
data_dir = Path(os.path.dirname(self.adj_file_name + ".pkl"))
if not data_dir.exists():
print(
f"Path {data_dir.absolute().as_posix()} does not exist, will try to create it..."
)
data_dir.mkdir(parents=True)
print(f"Tried to create {data_dir.absolute().as_posix()}")
data_dir.chmod(0o775)
print(f"Tried to change permissions to 775")
# self.alias_df.to_pickle(self.alias_file_name + ".pkl")
self.adj_df.to_pickle(self.adj_file_name + ".pkl")
self.unit_df.to_pickle(self.unit_file_name + ".pkl")
def load(self):
# if os.path.exists(self.alias_file_name + ".pkl"):
# self.alias_df = pd.read_pickle(self.alias_file_name + ".pkl")
if os.path.exists(self.adj_file_name + ".pkl"):
self.adj_df = pd.read_pickle(self.adj_file_name + ".pkl")
if os.path.exists(self.unit_file_name + ".pkl"):
self.unit_df = pd.read_pickle(self.unit_file_name + ".pkl")
def append_run(
self,
runno,
metadata={
"type": "ascan",
"name": "phi scan (001)",
"scan_motor": "phi",
"from": 1,
"to": 2,
"steps": 51,
},
):
self.load()
if len(self.adjustables) == 0:
self._parse_parent_fewerparents()
# dat = self._get_values()
# dat.update(metadata)
# dat["time"] = datetime.now()
# run_df = DataFrame([dat.values()], columns=dat.keys(), index=[runno])
# self.alias_df = self.alias_df.append(run_df)
dat = self._get_adjustable_values()
dat["metadata"] = metadata
dat["metadata"]["time"] = datetime.now()
names = ["device", "adjustable"]
multiindex = pd.MultiIndex.from_tuples(
[(dev, adj) for dev in dat.keys() for adj in dat[dev].keys()], names=names
)
values = np.array([val for adjs in dat.values() for val in adjs.values()])
run_df = DataFrame([values], columns=multiindex, index=[runno])
self.adj_df = self.adj_df.append(run_df)
multiindex_u = pd.MultiIndex.from_tuples(
[(dev, adj) for dev in self.units.keys() for adj in self.units[dev].keys()],
names=names,
)
values_u = np.array(
[val for adjs in self.units.values() for val in adjs.values()]
)
self.unit_df = DataFrame([values_u], columns=multiindex_u, index=["units"])
self._remove_duplicates()
self.save()
self.upload_all()
def append_pos(self, name=""):
self.load()
if len(self.adjustables) == 0:
self._parse_parent_fewerparents()
try:
posno = int(self.adj_df.query('type == "pos"').index[-1].split("p")[1]) + 1
except:
posno = 0
# dat = self._get_values()
# dat.update([("name", name), ("type", "pos")])
# dat["time"] = datetime.now()
# pos_df = DataFrame([dat.values()], columns=dat.keys(), index=[f"p{posno}"])
# self.alias_df = self.alias_df.append(pos_df)
dat = self._get_adjustable_values()
dat["metadata"] = {"time": datetime.now(), "name": name, "type": "pos"}
names = ["device", "adjustable"]
multiindex = pd.MultiIndex.from_tuples(
[(dev, adj) for dev in dat.keys() for adj in dat[dev].keys()], names=names
)
values = np.array([val for adjs in dat.values() for val in adjs.values()])
pos_df = DataFrame([values], columns=multiindex, index=[f"p{posno}"])
self.adj_df = self.adj_df.append(pos_df)
multiindex_u = pd.MultiIndex.from_tuples(
[(dev, adj) for dev in self.units.keys() for adj in self.units[dev].keys()],
names=names,
)
values_u = np.array(
[val for adjs in self.units.values() for val in adjs.values()]
)
self.unit_df = DataFrame([values_u], columns=multiindex_u, index=["units"])
self.save()
self.upload_all()
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.load()
self.gc = gspread.authorize(self._credentials)
self.order_df()
if keys is None:
keys = self.keys
self.ws = self.gc.open_by_key(self._spreadsheet_key).worksheet(worksheet)
if len(keys) > 0:
keys = keys + " type"
upload_df = self._query_by_keys(keys=keys, df=df)
else:
upload_df = df
if df is None:
upload_df = self.adj_df
upload_df = upload_df[
upload_df["metadata"]["type"].str.contains("scan", na=False)
]
gd.set_with_dataframe(self.ws, upload_df, include_index=True, col=2)
gf_dataframe.format_with_dataframe(
self.ws, upload_df, include_index=True, include_column_header=True, col=2
)
def upload_pos(self, worksheet="positions", keys=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.load()
self.gc = gspread.authorize(self._credentials)
self.order_df()
if keys is None:
keys = self.keys
self.ws = self.gc.open_by_key(self._spreadsheet_key).worksheet(worksheet)
if len(keys) > 0:
keys = keys + " metadata"
upload_df = self._query_by_keys(keys=keys)
else:
upload_df = self.adj_df
upload_df = upload_df[
upload_df["metadata"]["type"].str.contains("pos", na=False)
]
gd.set_with_dataframe(self.ws, upload_df, include_index=True, col=2)
gf_dataframe.format_with_dataframe(
self.ws, upload_df, include_index=True, include_column_header=True, col=2
)
def _upload_all(self):
try:
self.upload_rt()
self.upload_pos()
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):
rt = threading.Thread(target=self._upload_all)
rt.start()
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]
)
# index = np.array([mylist.index(k) 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
# self.alias_df = self.alias_df[
# self._orderlist(list(self.alias_df.columns), key_order)
# ]
devs = [item[0] for item in list(self.adj_df.columns)]
self.adj_df = self.adj_df[
self._orderlist(list(self.adj_df.columns), key_order, orderlist=devs)
]
def _get_adjustable_values(self, silent=True):
"""
This function gets the values of all adjustables in good adjustables and raises an error, when an adjustable is not connected anymore
"""
if silent:
dat = {}
for devname, dev in self.good_adjustables.items():
dat[devname] = {}
for adjname, adj in dev.items():
bad_adjs = []
try:
dat[devname][adjname] = adj.get_current_value()
except:
print(
f"run_table: getting value of {devname}.{adjname} failed, removing it from list of good adjustables"
)
bad_adjs.append(adjname)
for ba in bad_adjs:
if not devname in self.bad_adjustables.keys():
self.bad_adjustables[devname] = {}
self.bad_adjustables[devname][adjname] = self.good_adjustables[
devname
].pop(adjname)
else:
dat = {
devname: {
adjname: adj.get_current_value() for adjname, adj in dev.items()
}
for devname, dev in self.good_adjustables.items()
}
return dat
def subtract_df(self, devs, ind1, ind2):
"""
This function is used to subtract one dataframe from another to show changes between entries.
devs='thc tht' would show the devices thc and tht and ind1=0, ind2='p0' the difference between
run 0 and saved position 0.
"""
df1 = self.query(devs, [ind1])
df1 = df1[[type(val) is not str for val in df1]]
df2 = self.query(devs, [ind2])
df2 = df2[[type(val) is not str for val in df2]]
df2.columns = df1.columns
return df1.subtract(df2)
def _get_all_adjustables(self, device, pp_name=None):
if pp_name is not None:
name = ".".join([pp_name, device.name])
else:
name = device.name
self.adjustables[name] = {}
for key in device.__dict__.keys():
if ~np.any([s in key for s in self._parse_exclude_keys]):
value = device.__dict__[key]
if np.all(
[
~np.any(
[
s in str(type(value))
for s in self._adj_exclude_class_types
]
),
hasattr(value, "get_current_value"),
]
):
self.adjustables[name][key] = value
if hasattr(device, "get_current_value"):
self.adjustables[name][".".join([name, "self"])] = device
def _get_all_adjustables_fewerparents(
self, device, adj_prefix=None, parent_name=None
):
if adj_prefix is not None:
name = ".".join([adj_prefix, device.name])
else:
name = device.name
for key in device.__dict__.keys():
if ~np.any([s in key for s in self._parse_exclude_keys]):
value = device.__dict__[key]
if np.all(
[
~np.any(
[
s in str(type(value))
for s in self._adj_exclude_class_types
]
),
hasattr(value, "get_current_value"),
]
):
if parent_name == device.name:
self.adjustables[parent_name][key] = value
else:
self.adjustables[parent_name][".".join([name, key])] = value
if parent_name == device.name:
if hasattr(device, "get_current_value"):
self.adjustables[parent_name]["self"] = device
def _parse_child_instances_fewerparents(
self, parent_class, adj_prefix=None, parent_name=None
):
if parent_name is None:
parent_name = parent_class.name
self._get_all_adjustables_fewerparents(parent_class, adj_prefix, parent_name)
if parent_name is not parent_class.name:
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 self._parse_exclude_keys]):
s_class = parent_class.__dict__[key]
if np.all(
[
hasattr(s_class, "__dict__"),
hasattr(s_class, "name"),
s_class.__hash__ is not None,
"eco" in str(type(s_class)),
~np.any(
[
s in str(type(s_class))
for s in self._parse_exclude_class_types
]
),
]
):
sub_classes.append(s_class)
return set(sub_classes).union(
[
s
for c in sub_classes
for s in self._parse_child_instances_fewerparents(
c, adj_prefix, parent_name
)
]
)
def _parse_parent_fewerparents(self, parent=None):
if parent == None:
parent = self.devices
for key in parent.__dict__.keys():
try:
if ~np.any([s in key for s in self._parse_exclude_keys]):
s_class = parent.__dict__[key]
if np.all(
[
hasattr(s_class, "__dict__"),
hasattr(s_class, "name"),
s_class.__hash__ is not None,
"eco" in str(type(s_class)),
~np.any(
[
s in str(type(s_class))
for s in self._parse_exclude_class_types
]
),
]
):
self.adjustables[s_class.name] = {}
self._parse_child_instances_fewerparents(s_class)
except Exception as e:
print(e)
print(key)
# print(f"failed to parse {key} in runtable")
for name, value in self._channels_ca.get_current_value().items():
self.adjustables[f"env_{name}"] = {
key: PvRecord(pvsetname=ch) for key, ch in value.items()
}
self._check_adjustables()
def _parse_child_instances(self, parent_class, pp_name=None):
# try:
self._get_all_adjustables(parent_class, pp_name)
# except:
# print(f'Getting adjustables from {parent_class.name} failed')
# pass
if pp_name is not None:
pp_name = ".".join([pp_name, parent_class.name])
else:
pp_name = parent_class.name
sub_classes = []
for key in parent_class.__dict__.keys():
if ~np.any([s in key for s in self._parse_exclude_keys]):
s_class = parent_class.__dict__[key]
if np.all(
[
hasattr(s_class, "__dict__"),
hasattr(s_class, "name"),
s_class.__hash__ is not None,
"eco" in str(type(s_class)),
~np.any(
[
s in str(type(s_class))
for s in self._parse_exclude_class_types
]
),
]
):
sub_classes.append(s_class)
return set(sub_classes).union(
[s for c in sub_classes for s in self._parse_child_instances(c, pp_name)]
)
def _parse_parent(self, parent=None):
if parent == None:
parent = self.devices
for key in parent.__dict__.keys():
try:
if ~np.any([s in key for s in self._parse_exclude_keys]):
s_class = parent.__dict__[key]
if np.all(
[
hasattr(s_class, "__dict__"),
hasattr(s_class, "name"),
s_class.__hash__ is not None,
"eco" in str(type(s_class)),
~np.any(
[
s in str(type(s_class))
for s in self._parse_exclude_class_types
]
),
]
):
self._parse_child_instances(parent.__dict__[key])
except Exception as e:
print(e)
print(key)
# print(f"failed to parse {key} in runtable")
for name, value in self._channels_ca.get_current_value().items():
self.adjustables[f"env_{name}"] = {
key: PvRecord(pvsetname=ch) for key, ch in value.items()
}
self._check_adjustables()
def _check_adjustables(self, check_for_current_none_values=False):
good_adj = {}
bad_adj = {}
for device, adjs in self.adjustables.items():
good_dev_adj = {}
bad_dev_adj = {}
for name, adj in adjs.items():
if check_for_current_none_values and (adj.get_current_value() is None):
bad_dev_adj[name] = adj
else:
good_dev_adj[name] = adj
if len(good_dev_adj) > 0:
good_adj[device] = good_dev_adj
if len(bad_dev_adj) > 0:
bad_adj[device] = bad_dev_adj
self.good_adjustables = good_adj
self.bad_adjustables = bad_adj
def set_alias_namespace(self, alias_namespace):
aliases = [s.replace(".", "_") for s in alias_namespace.aliases]
self._alias_namespace = alias_namespace
self._pvs = dict(
zip(
aliases,
np.array(
[
PV(ch, connection_timeout=0.05, auto_monitor=True)
for ch in alias_namespace.channels
]
),
)
)
def get_alias_namespace(self):
return self._alias_namespace
alias_namespace = property(get_alias_namespace, set_alias_namespace)
def __repr__(self):
self.order_df()
return_df = self._query_by_keys(self.keys)
return return_df.T.__repr__()
class Gsheet_API:
def __init__(
self,
@@ -749,7 +80,8 @@ class Gsheet_API:
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)
self._key_df = self._key_df.append(gspread_key_df)
#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)
@@ -775,6 +107,7 @@ class Gsheet_API:
{"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
@@ -977,8 +310,9 @@ class Run_Table2:
self,
runno,
metadata,
d={},
):
self._data.append_run(runno, metadata)
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)
@@ -994,6 +328,19 @@ class Run_Table2:
def to_dataframe(self):
return DataFrame(self._data)
###### diagnostic and convencience functions ######
def run_table_from_other_pgroup(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,
@@ -1001,11 +348,18 @@ class Run_Table2:
):
if keys is None:
keys = self._google_sheet_api.gsheet_keys()
dfs = [
self.__dict__[key].to_dataframe()
for key in keys.split(" ")
if key in self.__dir__()
]
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
@@ -1042,6 +396,9 @@ class Run_Table2:
def __repr__(self):
return self.__str__()
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)
class Run_Table_DataFrame(DataFrame):
def __init__(
@@ -1052,6 +409,8 @@ class Run_Table_DataFrame(DataFrame):
devices=None,
name=None,
):
if type(data) is str:
data = pd.read_pickle(data)
super().__init__(data=data)
### Load devices to parse for adjustables ###
@@ -1070,7 +429,7 @@ class Run_Table_DataFrame(DataFrame):
self._parse_exclude_keys = "status_indicators settings_collection status_indicators_collection presets memory _elog _currentChange _flags __ alias namespace daq scan MasterEventSystem _motor Alias".split(
" "
)
self._parse_exclude_class_types = "__ alias namespace daq scan MasterEventSystem _motor Alias AdjustablePv".split(
self._parse_exclude_class_types = "__ alias namespace daq scan MasterEventSystem _motor Alias AdjustablePv Collection".split(
" "
)
self._adj_exclude_class_types = (
@@ -1131,24 +490,27 @@ class Run_Table_DataFrame(DataFrame):
"to": 2,
"steps": 51,
},
d={},
):
self.load()
if len(self.adjustables) == 0:
self._parse_parent_fewerparents()
dat = self._get_adjustable_values()
dat = self._get_adjustable_values(d=d)
dat["metadata"] = metadata
dat["metadata"]["time"] = datetime.now()
names = ["device", "adjustable"]
multiindex = pd.MultiIndex.from_tuples(
[(dev, adj) for dev in dat.keys() for adj in dat[dev].keys()], names=names
)
values = np.array([val for adjs in dat.values() for val in adjs.values()])
values = np.array([val for adjs in dat.values() for val in adjs.values()], dtype=object)
index = np.array(
[f"{dev}.{adj}" for dev, adjs in dat.items() for adj in adjs.keys()]
)
# run_df = DataFrame([values], columns=multiindex, index=[runno])
run_df = DataFrame([values], columns=index, index=[runno])
self.df = self.append(run_df)
#deprecated: self.df = self.append(run_df)
self.df = pd.concat([self.df, run_df])
self._remove_duplicates()
# self.order_df()
self.save()
@@ -1169,19 +531,20 @@ class Run_Table_DataFrame(DataFrame):
multiindex = pd.MultiIndex.from_tuples(
[(dev, adj) for dev in dat.keys() for adj in dat[dev].keys()], names=names
)
values = np.array([val for adjs in dat.values() for val in adjs.values()])
values = np.array([val for adjs in dat.values() for val in adjs.values()], dtype=object)
index = np.array(
[f"{dev}.{adj}" for dev, adjs in dat.items() for adj in adjs.keys()]
)
# pos_df = DataFrame([values], columns=multiindex, index=[f"p{posno}"])
pos_df = DataFrame([values], columns=index, index=[f"p{posno}"])
self.df = self.append(pos_df)
#deprecated: self.df = self.append(pos_df)
self.df = pd.concat([self.df,pos_df])
self._remove_duplicates()
# self.order_df()
self.save()
def _get_adjustable_values(self, silent=True):
def _get_adjustable_values(self, silent=True, d={}):
"""
This function gets the values of all adjustables in good adjustables and raises an error, when an adjustable is not connected anymore
"""
@@ -1191,6 +554,9 @@ class Run_Table_DataFrame(DataFrame):
dat[devname] = {}
bad_adjs = []
for adjname, adj in dev.items():
if f'{devname}.{adjname}' in d.keys():
dat[devname][adjname] = d[f'{devname}.{adjname}']
continue
try:
dat[devname][adjname] = adj.get_current_value()
except:
@@ -1207,7 +573,7 @@ class Run_Table_DataFrame(DataFrame):
else:
dat = {
devname: {
adjname: adj.get_current_value() for adjname, adj in dev.items()
adjname: d[f'{devname}.{adjname}'] if f'{devname}.{adjname}' in d.keys() else adj.get_current_value() for adjname, adj in dev.items()
}
for devname, dev in self.good_adjustables.items()
}
@@ -1239,7 +605,7 @@ class Run_Table_DataFrame(DataFrame):
self.adjustables[name][".".join([name, "self"])] = device
def _get_all_adjustables_fewerparents(
self, device, adj_prefix=None, parent_name=None
self, device, adj_prefix=None, parent_name=None, verbose=False
):
if adj_prefix is not None:
name = ".".join([adj_prefix, device.name])
@@ -1262,6 +628,8 @@ class Run_Table_DataFrame(DataFrame):
if parent_name == device.name:
self.adjustables[parent_name][key] = value
else:
#print("GET ADJ", parent_name, name, key)
self.adjustables[parent_name][".".join([name, key])] = value
if parent_name == device.name:
@@ -1269,17 +637,18 @@ class Run_Table_DataFrame(DataFrame):
self.adjustables[parent_name]["self"] = device
def _parse_child_instances_fewerparents(
self, parent_class, adj_prefix=None, parent_name=None
self, parent_class, adj_prefix=None, parent_name=None, verbose=False
):
if parent_name is None:
parent_name = own_name
self._get_all_adjustables_fewerparents(parent_class, adj_prefix, parent_name)
self._get_all_adjustables_fewerparents(parent_class, adj_prefix, parent_name, verbose=verbose)
if parent_name is not parent_class.name:
if adj_prefix is not None:
adj_prefix = ".".join([adj_prefix, parent_class.name])
else:
adj_prefix = parent_class.name
sub_classes = []
sub_classnames = []
for key in parent_class.__dict__.keys():
@@ -1298,11 +667,13 @@ class Run_Table_DataFrame(DataFrame):
for s in self._parse_exclude_class_types
]
),
]
):
if s_class.name == None:
s_class.name = key
sub_classes.append(s_class)
if adj_prefix is None or ~np.any([key == s for s in ".".join([parent_name,adj_prefix]).split(".")]):
if s_class.name == None:
s_class.name = key
sub_classes.append(s_class)
return set(sub_classes).union(
[
s
@@ -1313,7 +684,7 @@ class Run_Table_DataFrame(DataFrame):
]
)
def _parse_parent_fewerparents(self, parent=None):
def _parse_parent_fewerparents(self, parent=None, verbose=False):
if parent == None:
parent = self.devices
for key in parent.__dict__.keys():
@@ -1336,7 +707,7 @@ class Run_Table_DataFrame(DataFrame):
):
self.adjustables[key] = {}
self._parse_child_instances_fewerparents(
s_class, parent_name=key
s_class, parent_name=key, verbose=verbose
)
except Exception as e:
print(e)
@@ -1407,13 +778,18 @@ class Run_Table_DataFrame(DataFrame):
self._check_adjustables()
def _check_adjustables(self, check_for_current_none_values=False):
def _check_adjustables(self, check_for_current_none_values=True):
good_adj = {}
bad_adj = {}
for device, adjs in self.adjustables.items():
good_dev_adj = {}
bad_dev_adj = {}
for name, adj in adjs.items():
try:
adj.get_current_value()
except Exception as e:
print(f"get_current_value() method of {name} failed with {e}")
continue
if check_for_current_none_values and (adj.get_current_value() is None):
bad_dev_adj[name] = adj
else:
@@ -1454,6 +830,48 @@ class Run_Table_DataFrame(DataFrame):
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):
if len(self.adjustables) == 0:
self._parse_parent_fewerparents()
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)
+12
View File
@@ -0,0 +1,12 @@
from pathlib import Path
class DataFiles:
def __init__(self,pgroup=None, data_path='/sf/bernina/data/'):
self.pgroup = pgroup
self.data_path = data_path
self.raw = Path(self.data_path) / Path(pgroup) / Path('raw')
def get_available_runs(parse_numbers=True):
self.raw
+7 -1
View File
@@ -10,6 +10,7 @@ from ..elements.assembly import Assembly
from .profile_monitors import Pprm_dsd
from .intensity_monitors import SolidTargetDetectorPBPS_new_assembly
import numpy as np
from epics import PV
class DownstreamDiagnostic(Assembly):
@@ -24,9 +25,14 @@ class DownstreamDiagnostic(Assembly):
self._append(
MotorRecord, "SARES20-DSD:MOTOR_DSDY", name="ybase", is_setting=True
)
def get_xyposition_for_kb_angles_in_rad(self, theta_kbver, theta_kbhor):
y = np.tan(2 * theta_kbver) * 7075
x = np.tan(2 * theta_kbhor) / np.cos(2 * theta_kbver) * 6325
return x, y
def home(self,**kwargs):
self._run_cmd("python /ioc/qt/ESB_DSD_home.py SARES20-DSD", **kwargs)
def gui(self):
self._run_cmd("caqtdm -noMsg -macro P=SARES20-DSD /ioc/qt/ESB_DSD_motors.ui")
+637 -104
View File
@@ -1,4 +1,5 @@
from ..devices_general.motors import MotorRecord
from eco.devices_general.pipelines_swissfel import Pipeline
from ..devices_general.motors import MotorRecord, SmaractRecord
from ..devices_general.detectors import FeDigitizer
from ..epics.detector import DetectorPvDataStream
from ..detector.detectors_psi import DetectorBsStream
@@ -50,6 +51,388 @@ class FeDigitiza(Assembly):
# ]
class CalibrationRecord(Assembly):
def __init__(self, pvbase, name=None):
self.pvbase = pvbase
super().__init__(name)
self._append(
AdjustablePv,
self.pvbase + ".INPA",
name="input_A",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".INPB",
name="input_B",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".INPC",
name="input_C",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".INPD",
name="input_D",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".INPJ",
name="input_J",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".E",
name="const_E",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".F",
name="const_F",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".G",
name="const_G",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".H",
name="const_H",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".I",
name="const_I",
is_setting=True,
is_display=True,
)
self._append(
AdjustablePv,
self.pvbase + ".CALC",
name="function",
is_setting=True,
is_display=True,
)
# class SolidTargetDetectorPBPSMonOpt(SolidTargetDetectorPBPS):
# def __init__(self, *args, **kwargs):
# ...
class SolidTargetDetectorPBPS(Assembly):
def __init__(
self,
pvname,
# VME_crate=None,
# pipeline=None,
# link=None,
channel_xpos=None,
channel_ypos=None,
channel_intensity=None,
diode_channels_raw={},
# ch_up=12,
# ch_down=13,
# ch_left=15,
# ch_right=14,
# elog=None,
use_calibration=True,
calibration_records=None,
name=None,
fe_digi_channels={},
# calc=None,
# calc_calib={},
pipeline_computation=None,
):
super().__init__(name=name)
self.pvname = pvname
self._append(
MotorRecord, pvname + ":MOTOR_X1", name="x_diodes", is_setting=True
)
self._append(
MotorRecord, pvname + ":MOTOR_Y1", name="y_diodes", is_setting=True
)
self._append(
MotorRecord, pvname + ":MOTOR_PROBE", name="target_y", is_setting=True
)
self._append(
AdjustablePvEnum, pvname + ":PROBE_SP", name="target", is_setting=True
)
if channel_intensity:
self._append(
DetectorBsStream,
channel_intensity,
name="intensity",
is_setting=False,
)
else:
self._append(
DetectorBsStream,
pvname + ":INTENSITY",
name="intensity",
is_setting=False,
)
if channel_xpos:
self._append(DetectorBsStream, channel_xpos, name="xpos", is_setting=False)
else:
self._append(
DetectorBsStream, pvname + ":XPOS", name="xpos", is_setting=False
)
if channel_ypos:
self._append(DetectorBsStream, channel_ypos, name="ypos", is_setting=False)
else:
self._append(
DetectorBsStream, pvname + ":YPOS", name="ypos", is_setting=False
)
if diode_channels_raw:
self._append(
DetectorBsStream,
diode_channels_raw["up"],
name="signal_up_raw",
is_setting=False,
)
self._append(
DetectorBsStream,
diode_channels_raw["down"],
name="signal_down_raw",
is_setting=False,
)
self._append(
DetectorBsStream,
diode_channels_raw["left"],
name="signal_left_raw",
is_setting=False,
)
self._append(
DetectorBsStream,
diode_channels_raw["right"],
name="signal_right_raw",
is_setting=False,
)
if fe_digi_channels:
self._append(
FeDigitiza,
fe_digi_channels["left"],
name="settings_diode_left",
is_setting=True,
)
self._append(
FeDigitiza,
fe_digi_channels["right"],
name="settings_diode_right",
is_setting=True,
)
self._append(
FeDigitiza,
fe_digi_channels["up"],
name="settings_diode_up",
is_setting=True,
)
self._append(
FeDigitiza,
fe_digi_channels["down"],
name="settings_diode_down",
is_setting=True,
)
if use_calibration:
# Calibration calculation record
# Calibration
if calibration_records:
self._append(
CalibrationRecord,
calibration_records["intensity"],
name="calib_intensity",
is_setting=True,
is_display=False,
)
self._append(
CalibrationRecord,
calibration_records["xpos"],
name="calib_xpos",
is_setting=True,
is_display=False,
)
self._append(
CalibrationRecord,
calibration_records["ypos"],
name="calib_ypos",
is_setting=True,
is_display=False,
)
else:
self._append(
CalibrationRecord,
pvname + ":INTENSITY",
name="calib_intensity",
is_setting=True,
is_display=False,
)
self._append(
CalibrationRecord,
pvname + ":XPOS",
name="calib_xpos",
is_setting=True,
is_display=False,
)
self._append(
CalibrationRecord,
pvname + ":YPOS",
name="calib_ypos",
is_setting=True,
is_display=False,
)
if pipeline_computation:
self._append(Pipeline, pipeline_computation,name='pipeline_comp', is_setting=True, is_display=False)
def get_calibration_values(self, seconds=5, return_data=False):
self.x_diodes.set_target_value(0).wait()
self.y_diodes.set_target_value(0).wait()
ds = [
self.signal_up_raw,
self.signal_down_raw,
self.signal_left_raw,
self.signal_right_raw,
]
aqs = [d.acquire(seconds=seconds) for d in ds]
data = [aq.wait() for aq in aqs]
mean = [np.mean(td) for td in data]
std = [np.std(td) for td in data]
nsamples = [len(td) for td in data]
print(f"Got {nsamples} samples in {seconds} s.")
norm_diodes = [1 / tm / 4 for tm in mean]
if return_data:
return data,norm_diodes
return norm_diodes
def set_calibration_values(self, norm_diodes):
self.calib_intensity.const_E.set_target_value(norm_diodes[0])
self.calib_ypos.const_E.set_target_value(norm_diodes[0])
self.calib_intensity.const_F.set_target_value(norm_diodes[1])
self.calib_ypos.const_F.set_target_value(norm_diodes[1])
self.calib_intensity.const_G.set_target_value(norm_diodes[2])
self.calib_xpos.const_E.set_target_value(norm_diodes[2])
self.calib_intensity.const_H.set_target_value(norm_diodes[3])
self.calib_xpos.const_F.set_target_value(norm_diodes[3])
def get_calibration_values_position(
self, calib_intensities, seconds=5, motion_range=0.2
):
self.x_diodes.set_limits(-motion_range / 2 - 0.1, +motion_range / 2 + 0.1)
self.y_diodes.set_limits(-motion_range / 2 - 0.1, +motion_range / 2 + 0.1)
self.x_diodes.set_target_value(0).wait()
self.y_diodes.set_target_value(0).wait()
raw = []
for pos in [motion_range / 2, -motion_range / 2]:
print(pos)
self.x_diodes.set_target_value(pos).wait()
aqs = [
ts.acquire(seconds=seconds)
for ts in [self.signal_left_raw, self.signal_right_raw]
]
vals = [
np.mean(aq.wait()) * calib
for aq, calib in zip(aqs, calib_intensities[0:2])
]
raw.append((vals[0] - vals[1]) / (vals[0] + vals[1]))
xcalib = motion_range / np.diff(raw)[0]
self.x_diodes.set_target_value(0).wait()
raw = []
for pos in [motion_range / 2, -motion_range / 2]:
self.y_diodes.set_target_value(pos).wait()
aqs = [
ts.acquire(seconds=seconds)
for ts in [self.signal_up_raw, self.signal_down_raw]
]
vals = [
np.mean(aq.wait()) * calib
for aq, calib in zip(aqs, calib_intensities[2:4])
]
raw.append((vals[0] - vals[1]) / (vals[0] + vals[1]))
ycalib = motion_range / np.diff(raw)[0]
self.y_diodes.set_target_value(0).wait()
return xcalib, ycalib
def set_calibration_values_position(self, xcalib, ycalib):
self.calib_xpos.const_I.set_target_value(xcalib)
# self.calib_xpos.const_J.set_target_value(0)
self.calib_ypos.const_I.set_target_value(ycalib)
# self.calib_ypos.const_J.set_target_value(0)
def calibrate(self, seconds=5):
c = self.get_calibration_values(seconds=seconds)
self.set_calibration_values(c)
xc, yc = self.get_calibration_values_position(c, seconds=seconds)
self.set_calibration_values_position(xc, yc)
def set_gains(self, value):
try:
self.diode_up.gain.set(value)
self.diode_down.gain.set(value)
self.diode_left.gain.set(value)
self.diode_right.gain.set(value)
except:
print("No diodes configured, can not change any gain!")
def get_available_gains(self):
try:
nu = self.diode_up.gain.names
nd = self.diode_down.gain.names
nl = self.diode_left.gain.names
nr = self.diode_right.gain.names
assert (
nu == nd == nl == nr
), "NB: the gain options of the four diodes are not equal!!!"
return nu
except:
print("No diodes configured, can not change any gain!")
def get_gains(self):
try:
gains = dict()
gains["up"] = (self.diode_up.gain.get_name(), self.diode_up.gain.get())
gains["down"] = (
self.diode_down.gain.get_name(),
self.diode_down.gain.get(),
)
gains["left"] = (
self.diode_left.gain.get_name(),
self.diode_left.gain.get(),
)
gains["right"] = (
self.diode_right.gain.get_name(),
self.diode_right.gain.get(),
)
return gains
except:
print("No diodes configured, can not change any gain!")
class SolidTargetDetectorPBPS_assi(Assembly):
def __init__(
self,
@@ -340,108 +723,6 @@ class SolidTargetDetectorPBPS_new:
# SAROP21-CVME-PBPS:Lnk10Ch15-WD-gain
class SolidTargetDetectorPBPS:
def __init__(
self,
Id,
VME_crate=None,
link=None,
ch_up=12,
ch_down=13,
ch_left=15,
ch_right=14,
elog=None,
name=None,
):
self.Id = Id
self.name = name
self.diode_x = MotorRecord(Id + ":MOTOR_X1", name="diode_x")
self.diode_y = MotorRecord(Id + ":MOTOR_Y1", name="diode_y")
self.target_pos = MotorRecord(Id + ":MOTOR_PROBE", name="target_pos")
self.target = AdjustablePvEnum(Id + ":PROBE_SP", name="target")
if VME_crate:
self.diode_up = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_up))
self.diode_down = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_down))
self.diode_left = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_left))
self.diode_right = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_right))
if self.name:
self.alias = Alias(name)
self.alias.append(self.diode_x.alias)
self.alias.append(self.diode_y.alias)
self.alias.append(self.target_pos.alias)
self.alias.append(self.target.alias)
def __repr__(self):
s = f"**Intensity monitor {self.name}**\n\n"
s += f"Target in: {self.target.get_current_value().name}\n\n"
try:
sd = "**Biasd voltage**\n"
sd += " - Diode up: %.4f\n" % (sdelf.diode_up.get_biasd())
sd += " - Diode down: %.4f\n" % (sdelf.diode_down.get_biasd())
sd += " - Diode left: %.4f\n" % (sdelf.diode_left.get_biasd())
sd += " - Diode right: %.4f\n" % (sdelf.diode_right.get_biasd())
sd += "\n"
sd += "**Gain**\n"
sd += " - Diode up: %i\n" % (sdelf.diode_up.gain.get())
sd += " - Diode down: %i\n" % (sdelf.diode_down.gain.get())
sd += " - Diode left: %i\n" % (sdelf.diode_left.gain.get())
sd += " - Diode right: %i\n" % (sdelf.diode_right.gain.get())
s += sd
except:
pass
return s
def set_gains(self, value):
try:
self.diode_up.gain.set(value)
self.diode_down.gain.set(value)
self.diode_left.gain.set(value)
self.diode_right.gain.set(value)
except:
print("No diodes configured, can not change any gain!")
def get_available_gains(self):
try:
nu = self.diode_up.gain.names
nd = self.diode_down.gain.names
nl = self.diode_left.gain.names
nr = self.diode_right.gain.names
assert (
nu == nd == nl == nr
), "NB: the gain options of the four diodes are not equal!!!"
return nu
except:
print("No diodes configured, can not change any gain!")
def get_gains(self):
try:
gains = dict()
gains["up"] = (self.diode_up.gain.get_name(), self.diode_up.gain.get())
gains["down"] = (
self.diode_down.gain.get_name(),
self.diode_down.gain.get(),
)
gains["left"] = (
self.diode_left.gain.get_name(),
self.diode_left.gain.get(),
)
gains["right"] = (
self.diode_right.gain.get_name(),
self.diode_right.gain.get(),
)
return gains
except:
print("No diodes configured, can not change any gain!")
# SAROP21-CVME-PBPS:Lnk10Ch15-WD-gain
from ..elements.assembly import Assembly
class SolidTargetDetectorPBPS_new_assembly(Assembly):
def __init__(
self,
@@ -714,7 +995,7 @@ class SolidTargetDetectorPBPS_assembly(Assembly):
DetectorPvDataStream, calc["ypos"], name="ypos", is_setting=False
)
def get_calibration_values(self, seconds=5):
def get_calibration_values(self, seconds=5, return_data=False):
self.x_diodes.set_target_value(0).wait()
self.y_diodes.set_target_value(0).wait()
ds = [self.signal_up, self.signal_down, self.signal_left, self.signal_right]
@@ -723,6 +1004,8 @@ class SolidTargetDetectorPBPS_assembly(Assembly):
mean = [np.mean(td) for td in data]
std = [np.std(td) for td in data]
norm_diodes = [1 / tm / 4 for tm in mean]
if return_data:
return data,norm_diodes
return norm_diodes
def set_calibration_values(self, norm_diodes):
@@ -839,3 +1122,253 @@ class SolidTargetDetectorPBPS_assembly(Assembly):
return gains
except:
print("No diodes configured, can not change any gain!")
class SolidTargetDetectorBerninaUSD(Assembly):
def __init__(
self,
pv_targets,
# VME_crate=None,
# pipeline=None,
# link=None,
channel_xpos=None,
channel_ypos=None,
channel_intensity=None,
diode_channels_raw={},
# ch_up=12,
# ch_down=13,
# ch_left=15,
# ch_right=14,
# elog=None,
use_calibration=True,
calibration_records=None,
name=None,
# calc=None,
# calc_calib={},
):
super().__init__(name=name)
self._append(
SmaractRecord,
pv_targets,
name="target_x",
is_setting=True,
is_display=True,
)
if channel_intensity:
self._append(
DetectorBsStream,
channel_intensity,
name="intensity",
is_setting=False,
)
if channel_xpos:
self._append(DetectorBsStream, channel_xpos, name="xpos", is_setting=False)
# else:
# self._append(
# DetectorBsStream, pvname + ":XPOS", name="xpos", is_setting=False
# )
if channel_ypos:
self._append(DetectorBsStream, channel_ypos, name="ypos", is_setting=False)
# else:
# self._append(
# DetectorBsStream, pvname + ":YPOS", name="ypos", is_setting=False
# )
if diode_channels_raw:
self._append(
DetectorBsStream,
diode_channels_raw["up"],
name="signal_up_raw",
is_setting=False,
)
self._append(
DetectorBsStream,
diode_channels_raw["down"],
name="signal_down_raw",
is_setting=False,
)
self._append(
DetectorBsStream,
diode_channels_raw["left"],
name="signal_left_raw",
is_setting=False,
)
self._append(
DetectorBsStream,
diode_channels_raw["right"],
name="signal_right_raw",
is_setting=False,
)
if use_calibration:
# Calibration calculation record
# Calibration
if calibration_records:
self._append(
CalibrationRecord,
calibration_records["intensity"],
name="calib_intensity",
is_setting=True,
is_display=False,
)
self._append(
CalibrationRecord,
calibration_records["xpos"],
name="calib_xpos",
is_setting=True,
is_display=False,
)
self._append(
CalibrationRecord,
calibration_records["ypos"],
name="calib_ypos",
is_setting=True,
is_display=False,
)
# else:
# self._append(
# CalibrationRecord,
# pvname + ":INTENSITY",
# name="calib_intensity",
# is_setting=True,
# is_display=False,
# )
# self._append(
# CalibrationRecord,
# pvname + ":XPOS",
# name="calib_xpos",
# is_setting=True,
# is_display=False,
# )
# self._append(
# CalibrationRecord,
# pvname + ":YPOS",
# name="calib_ypos",
# is_setting=True,
# is_display=False,
# )
def get_calibration_values(self, seconds=5):
self.x_diodes.set_target_value(0).wait()
self.y_diodes.set_target_value(0).wait()
ds = [
self.signal_up_raw,
self.signal_down_raw,
self.signal_left_raw,
self.signal_right_raw,
]
aqs = [d.acquire(seconds=seconds) for d in ds]
data = [aq.wait() for aq in aqs]
mean = [np.mean(td) for td in data]
std = [np.std(td) for td in data]
nsamples = [len(td) for td in data]
print(f"Got {nsamples} samples in {seconds} s.")
norm_diodes = [1 / tm / 4 for tm in mean]
return norm_diodes
def set_calibration_values(self, norm_diodes):
self.calib_intensity.const_E.set_target_value(norm_diodes[0])
self.calib_ypos.const_E.set_target_value(norm_diodes[0])
self.calib_intensity.const_F.set_target_value(norm_diodes[1])
self.calib_ypos.const_F.set_target_value(norm_diodes[1])
self.calib_intensity.const_G.set_target_value(norm_diodes[2])
self.calib_xpos.const_E.set_target_value(norm_diodes[2])
self.calib_intensity.const_H.set_target_value(norm_diodes[3])
self.calib_xpos.const_F.set_target_value(norm_diodes[3])
def get_calibration_values_position(
self, calib_intensities, seconds=5, motion_range=0.2
):
self.x_diodes.set_limits(-motion_range / 2 - 0.1, +motion_range / 2 + 0.1)
self.y_diodes.set_limits(-motion_range / 2 - 0.1, +motion_range / 2 + 0.1)
self.x_diodes.set_target_value(0).wait()
self.y_diodes.set_target_value(0).wait()
raw = []
for pos in [motion_range / 2, -motion_range / 2]:
print(pos)
self.x_diodes.set_target_value(pos).wait()
aqs = [
ts.acquire(seconds=seconds)
for ts in [self.signal_left_raw, self.signal_right_raw]
]
vals = [
np.mean(aq.wait()) * calib
for aq, calib in zip(aqs, calib_intensities[0:2])
]
raw.append((vals[0] - vals[1]) / (vals[0] + vals[1]))
xcalib = motion_range / np.diff(raw)[0]
self.x_diodes.set_target_value(0).wait()
raw = []
for pos in [motion_range / 2, -motion_range / 2]:
self.y_diodes.set_target_value(pos).wait()
aqs = [
ts.acquire(seconds=seconds)
for ts in [self.signal_up_raw, self.signal_down_raw]
]
vals = [
np.mean(aq.wait()) * calib
for aq, calib in zip(aqs, calib_intensities[2:4])
]
raw.append((vals[0] - vals[1]) / (vals[0] + vals[1]))
ycalib = motion_range / np.diff(raw)[0]
self.y_diodes.set_target_value(0).wait()
return xcalib, ycalib
def set_calibration_values_position(self, xcalib, ycalib):
self.calib_xpos.const_I.set_target_value(xcalib)
self.calib_xpos.const_J.set_target_value(0)
self.calib_ypos.const_I.set_target_value(ycalib)
self.calib_ypos.const_J.set_target_value(0)
def calibrate(self, seconds=5):
c = self.get_calibration_values(seconds=seconds)
self.set_calibration_values(c)
xc, yc = self.get_calibration_values_position(c, seconds=seconds)
self.set_calibration_values_position(xc, yc)
def set_gains(self, value):
try:
self.diode_up.gain.set(value)
self.diode_down.gain.set(value)
self.diode_left.gain.set(value)
self.diode_right.gain.set(value)
except:
print("No diodes configured, can not change any gain!")
def get_available_gains(self):
try:
nu = self.diode_up.gain.names
nd = self.diode_down.gain.names
nl = self.diode_left.gain.names
nr = self.diode_right.gain.names
assert (
nu == nd == nl == nr
), "NB: the gain options of the four diodes are not equal!!!"
return nu
except:
print("No diodes configured, can not change any gain!")
def get_gains(self):
try:
gains = dict()
gains["up"] = (self.diode_up.gain.get_name(), self.diode_up.gain.get())
gains["down"] = (
self.diode_down.gain.get_name(),
self.diode_down.gain.get(),
)
gains["left"] = (
self.diode_left.gain.get_name(),
self.diode_left.gain.get(),
)
gains["right"] = (
self.diode_right.gain.get_name(),
self.diode_right.gain.get(),
)
return gains
except:
print("No diodes configured, can not change any gain!")
+32 -32
View File
@@ -1,3 +1,4 @@
from eco.detector.detectors_psi import DetectorBsStream
from ..devices_general.motors import MotorRecord, SmaractStreamdevice, SmaractRecord
from ..devices_general.detectors import CameraCA, CameraBS
from ..devices_general.cameras_swissfel import CameraBasler
@@ -15,9 +16,15 @@ def addMotorRecordToSelf(self, Id=None, name=None):
class Pprm(Assembly):
def __init__(self, pvname, pvname_camera, name=None):
def __init__(self, pvname, pvname_camera, name=None, in_target=1, bs_channels={}):
super().__init__(name=name)
self.pvname = pvname
self.in_target = in_target
self._append(
AdjustablePvEnum, self.pvname + ":PROBE_SP", name="target", is_setting=True
)
self._append(
MotorRecord,
pvname_camera + ":MOTOR_PROBE",
@@ -31,24 +38,32 @@ class Pprm(Assembly):
camserver_alias=f"{name} ({pvname_camera})",
name="camera",
is_setting=True,
is_display="recursive",
)
self._append(
AdjustablePvEnum, self.pvname + ":LED", name="led", is_setting=True
)
self._append(
AdjustablePvEnum, self.pvname + ":PROBE_SP", name="target", is_setting=True
)
for bscn,bscc in bs_channels.items():
self._append(
DetectorBsStream,
bscc,
name=bscn,
is_setting=False,
)
def movein(self, target=1):
def movein(self, target=None):
if target == None:
target = self.in_target
self.target.set_target_value(target)
def moveout(self, target=0):
self.target.set_target_value(target)
def __repr__(self):
s = f"**Profile Monitor {self.name}**\n"
s += f"Target in beam: {self.target.get_current_value().name}\n"
return s
# def __repr__(self):
# s = f"**Profile Monitor {self.name}**\n"
# s += f"Target in beam: {self.target.get_current_value().name}\n"
# return s
class Target_xyz(Assembly):
@@ -93,7 +108,7 @@ class ProfKbBernina(Assembly):
pvname_target_x="SARES20-MF2:MOT_1",
pvname_target_y="SARES20-MF2:MOT_2",
pvname_target_z="SARES20-MF2:MOT_3",
pvname_mirror="SARES23:LIC11",
pvname_mirror="SARES23-LIC:MOT_11",
mirror_in=15,
mirror_out=-5,
pvname_zoom="SARES20-MF2:MOT_4",
@@ -110,27 +125,10 @@ class ProfKbBernina(Assembly):
pvname_z="SARES20-MF2:MOT_3",
name="target_stages",
is_display="recursive",
is_setting=True,
)
self.target = self.target_stages.presets
self._append(
MotorRecord,
pvname_target_x,
name="x_target",
is_setting=True,
)
self._append(
MotorRecord,
pvname_target_y,
name="y_target",
is_setting=True,
)
self._append(
MotorRecord,
pvname_target_z,
name="z_target",
is_setting=True,
)
self._append(
SmaractRecord,
pvname_mirror,
@@ -159,6 +157,8 @@ class ProfKbBernina(Assembly):
self._append(
MotorRecord, pvname_zoom, name="zoom", is_setting=True, is_display=True
)
ix = self.settings_collection._list.index(self.zoom.offset)
self.settings_collection._list.pop(ix)
def movein_keep_target(self, wait=False):
ch = self.mirror_in.set_target_value(1)
@@ -173,18 +173,18 @@ class ProfKbBernina(Assembly):
def movein(self, wait=False):
ch = self.mirror_in.set_target_value(1)
try:
self.target_stages.presets.movein()
self.presets.movein()
except:
print("No movein preset found for target stages.")
print("No movein preset found for prof_kb.")
if wait:
ch.wait()
def moveout(self, wait=False):
ch = self.mirror_in.set_target_value(0)
try:
self.target_stages.presets.moveout()
self.presets.moveout()
except:
print("No moveout preset found for target stages.")
print("No moveout preset found for prof_kb.")
if wait:
ch.wait()
+31 -30
View File
@@ -8,9 +8,17 @@ from ..devices_general.delay_stage import DelayStage
from ..aliases import Alias, append_object_to_object
from ..loptics.bernina_experiment import DelayTime
from cam_server import PipelineClient
from eco import Assembly
class SpectralEncoder:
class TargetStages(Assembly):
def __init__(self,*args,name='target'):
super().__init__(name=name)
for df in args:
self._append(MotorRecord,df[1],name=df[0], is_display=True, is_setting=True)
class SpectralEncoder(Assembly):
def __init__(
self,
pvname,
@@ -20,36 +28,38 @@ class SpectralEncoder:
"spect_tt": "SLAAR21-LMOT-M553:MOT",
"retroreflector": "SLAAR21-LMOT-M561:MOT",
},
mirror_stages=None,
):
super().__init__(name=name)
self.pvname = pvname
self.name = name
self.alias = Alias(name)
append_object_to_object(
self, MotorRecord, pvname + ":MOTOR_X1", name="x_target"
)
append_object_to_object(
self, MotorRecord, pvname + ":MOTOR_Y1", name="y_target"
)
self._append(
TargetStages,
('x',pvname + ":MOTOR_X1"),
('y',pvname + ":MOTOR_Y1"),
name='target_stages',
is_display='recursive',
is_setting=True,
)
if delay_stages:
for key, pv in delay_stages.items():
tname = "delay_" + key + "_stg"
append_object_to_object(self, MotorRecord, pv, name=tname)
append_object_to_object(
self, DelayTime, self.__dict__[tname], name="delay_" + key
)
# self.delay = MotorRecord(self.Id + "-M424:MOT")
# self.delayTime = DelayStage(self.delay)
# self.data_reduction_client = PsenProcessingClient(
# address=reduction_client_address
# )
self._append(MotorRecord, pv, name=tname, is_setting=True)
self._append(
DelayTime, self.__dict__[tname], name="delay_" + key
)
if mirror_stages is not None:
for key, pv in mirror_stages.items():
self._append(MotorRecord, pv, name=key, is_setting=True)
# @property
# def roi(self):
# return self.data_reduction_client.get_roi_signal()
# @roi.setter
# def roi(self, values):
# def roi(self, values):tt_opt
# self.data_reduction_client.set_roi_signal(values)
# @property
@@ -60,15 +70,6 @@ class SpectralEncoder:
# def roi_background(self, values):
# self.data_reduction_client.set_roi_background(values)
def __repr__(self):
s = [f"Status {self.name}"]
s.append(str(self.x_target))
s.append(str(self.y_target))
# s.append(f"Data reduction is on")
# s.append(f" roi {self.roi}")
# s.append(f" roi_background {self.roi_background}")
return "\n".join(s)
class SpatialEncoder:
def __init__(
@@ -76,7 +77,7 @@ class SpatialEncoder:
name=None,
reduction_client_address="http://sf-daqsync-02:12003/",
delay_stages={"spatial_tt": "SLAAR21-LMOT-M522:MOTOR_1"},
pipeline_id="SARES20-CAMS142-M4_psen_db1",
pipeline_id="SARES20-CAMS142-M4_psen_db",
):
self.name = name
self.alias = Alias(name)
+23 -52
View File
@@ -1,59 +1,30 @@
from eco.elements.assembly import Assembly
from ..aliases import Alias, append_object_to_object
from ..devices_general.motors import MotorRecord
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
class Xspect:
class Xspect(Assembly):
def __init__(self, name=None):
self.alias = Alias(name)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS055:MOTOR_X1", name="x_grating"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS055:MOTOR_Y1", name="y_grating"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS055:MOTOR_ROT_X1", name="rx_grating"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS055:MOTOR_PROBE", name="transl_probe"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS059:MOTOR_X2", name="x_girder"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS059:MOTOR_X3", name="x_crystal"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS059:MOTOR_Y3", name="y_crystal"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS059:MOTOR_ROT_X3", name="rx_crystal"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS059:MOTOR_Y4", name="y_alignment"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS059:MOTOR_ROT_X4", name="rx_camera"
)
append_object_to_object(
self, MotorRecord, "SARFE10-PSSS059:MOTOR_X5", name="x_camera"
)
append_object_to_object(
self, AdjustablePvEnum, "SARFE10-PSSS055:GRATING_SP", name="grid_type"
)
append_object_to_object(
self, AdjustablePvEnum, "SARFE10-PSSS059:CRYSTAL_SP", name="crystal_type"
)
append_object_to_object(
self, AdjustablePvEnum, "SARFE10-PSSS055:PROBE_SP", name="probe"
)
append_object_to_object(
self, AdjustablePv, "SARFE10-PSSS059:ENERGY", name="energy_center_setpoint"
)
append_object_to_object(
self, AdjustablePv, "SARFE10-PSSS059:MOTOR_Z5", name="camera_z"
)
super().__init__(name=name)
self._append(MotorRecord, "SARFE10-PSSS055:MOTOR_X1", name="x_grating")
self._append(MotorRecord, "SARFE10-PSSS055:MOTOR_Y1", name="y_grating")
self._append(MotorRecord, "SARFE10-PSSS055:MOTOR_ROT_X1", name="rx_grating")
self._append(MotorRecord, "SARFE10-PSSS055:MOTOR_PROBE", name="transl_probe")
self._append(MotorRecord, "SARFE10-PSSS059:MOTOR_X2", name="x_girder")
self._append(MotorRecord, "SARFE10-PSSS059:MOTOR_X3", name="x_crystal")
self._append(MotorRecord, "SARFE10-PSSS059:MOTOR_Y3", name="y_crystal")
self._append(MotorRecord, "SARFE10-PSSS059:MOTOR_ROT_X3", name="rx_crystal")
self._append(MotorRecord, "SARFE10-PSSS059:MOTOR_Y4", name="y_alignment")
self._append(MotorRecord, "SARFE10-PSSS059:MOTOR_ROT_X4", name="rx_camera")
self._append(MotorRecord, "SARFE10-PSSS059:MOTOR_X5", name="x_camera")
self._append(AdjustablePvEnum, "SARFE10-PSSS055:GRATING_SP", name="grid_type")
self._append(
AdjustablePvEnum, "SARFE10-PSSS059:CRYSTAL_SP", name="crystal_type"
)
self._append(AdjustablePvEnum, "SARFE10-PSSS055:PROBE_SP", name="probe")
self._append(
AdjustablePv, "SARFE10-PSSS059:ENERGY", name="energy_center_setpoint"
)
self._append(AdjustablePv, "SARFE10-PSSS059:MOTOR_Z5", name="camera_z")
+7 -157
View File
@@ -12,157 +12,6 @@ import numpy as np
from time import sleep
class att_usd_targets(Assembly):
def __init__(self, name=None, Id=None, alias_namespace=None, xp=None):
super().__init__(name=name)
self.Id = Id
# self.name = name
self.alias = Alias(name)
self.E = None
self.E_min = 1500
self._sleeptime = 1
self.motor_configuration = {
"transl": {
"id": "-LIC10",
"pv_descr": " ",
"type": 2,
"sensor": 1,
"speed": 250,
"home_direction": "back",
},
}
self._xp = xp
self.E = None
for name, config in self.motor_configuration.items():
self._append(
SmaractStreamdevice,
pvname=Id + config["id"],
name=name,
is_setting=True,
is_display=False,
)
Al = materials.Al
self.targets = {
"mat": np.array([Al, Al, Al, Al, Al, Al, Al, Al]),
"d": np.array([0, 60, 160, 200, 300, 400, 500, 700]),
"pos": np.array([-35, -25, -15, -5, 5, 15, 25, 35]),
}
def _updateE(self, energy=None, check_once=False):
while not energy:
energy = PV("SARUN03-UIND030:FELPHOTENE").value
energy = energy * 1000
if energy < self.E_min:
energy = None
print(
f"Machine photon energy is below {self.E_min} - waiting for the machine to recover"
)
sleep(self._sleeptime)
self.E = energy
print("Set energy to %s eV" % energy)
return
def _calc_transmission(self):
t = np.array(
[
np.exp(-d / mat.absorption_length(self.E))
for d, mat in zip(self.targets["d"], self.targets["mat"])
]
)
self.targets["t"] = t
def _find_nearest(self, a, a0):
"Element in nd array `a` closest to the scalar value `a0`"
idx = np.abs(a - a0).argmin()
return idx, a[idx]
def set_transmission(self, value):
self._updateE()
self._calc_transmission()
idx, t = self._find_nearest(self.targets["t"], value)
pos = self.targets["pos"][idx]
self._xp.close()
self.transl.mv(pos)
print(f"Set transmission to {t:0.2E} | Moving to target {idx} at pos {pos}")
while abs(pos - self.transl.get_current_value()) > 0.1:
sleep(0.1)
print("transmission changed")
self._xp.open()
def get_current_value(self):
self._updateE()
self._calc_transmission()
idx, pos = self._find_nearest(
self.targets["pos"], self.transl.get_current_value()
)
t = self.targets["t"][idx]
return t
def set_stage_config(self):
for name, config in self.motor_configuration.items():
mot = self.__dict__[name]._device
mot.put("NAME", config["pv_descr"])
mot.put("STAGE_TYPE", config["type"])
mot.put("SET_SENSOR_TYPE", config["sensor"])
mot.put("CL_MAX_FREQ", config["speed"])
sleep(0.5)
mot.put("CALIBRATE.PROC", 1)
def home_smaract_stages(self, stages=None):
if stages == None:
stages = self.motor_configuration.keys()
print("#### Positions before homing ####")
print(self.__repr__())
for name in stages:
config = self.motor_configuration[name]
mot = self.__dict__[name]._device
print(
"#### Homing {} in {} direction ####".format(
name, config["home_direction"]
)
)
sleep(1)
if config["home_direction"] == "back":
mot.put("FRM_BACK.PROC", 1)
while mot.get("STATUS") == 7:
sleep(1)
if mot.get("GET_HOMED") == 0:
print(
"Homing failed, try homing {} in forward direction".format(name)
)
mot.put("FRM_FORW.PROC", 1)
elif config["home_direction"] == "forward":
mot.put("FRM_FORW.PROC", 1)
while mot.get("STATUS") == 7:
sleep(1)
if mot.get("GET_HOMED") == 0:
print(
"Homing failed, try homing {} in backward direction".format(
name
)
)
mot.put("FRM_BACK.PROC", 1)
def get_adjustable_positions_str(self):
ostr = "*****att_usd target position******\n"
for tkey, item in self.__dict__.items():
if hasattr(item, "get_current_value"):
pos = item.get_current_value()
ostr += " " + tkey.ljust(17) + " : % 14g\n" % pos
pos = self.get_current_value()
ostr += " " + "Transmission".ljust(17) + " : % 14.02E\n" % pos
return ostr
def __call__(self, *args, **kwargs):
self.set_transmission(*args, **kwargs)
def __repr__(self):
return self.get_adjustable_positions_str()
class Att_usd(Assembly):
"""This is an adjusted smaract record compatible version of the original att_usd by roman."""
@@ -172,8 +21,8 @@ class Att_usd(Assembly):
self.E = None
self.E_min = 1500
self._sleeptime = 1
self._append(SmaractRecord, "SARES23:LIC10", name="transl_2", is_setting=True)
self._append(SmaractRecord, "SARES23:LIC3", name="transl_1", is_setting=True)
self._append(SmaractRecord, "SARES23-LIC:MOT_10", name="transl_2", is_setting=True, is_display=True)
self._append(SmaractRecord, "SARES23-LIC:MOT_3", name="transl_1", is_setting=True, is_display=True)
self.motor_configuration = {
"transl_2": {
"id": "SARES23-LIC10",
@@ -289,8 +138,9 @@ class Att_usd(Assembly):
def _updateE(self, energy=None, check_once=False):
while not energy:
energy = PV("SARUN03-UIND030:FELPHOTENE").value
energy = energy * 1000
energy = PV("SAROP21-ARAMIS:ENERGY").value
if np.isnan(energy):
energy = PV("SARUN:FELPHOTENE").value*1000
if energy < self.E_min:
energy = None
print(
@@ -298,7 +148,7 @@ class Att_usd(Assembly):
)
sleep(self._sleeptime)
self.E = energy
print("Set energy to %s eV" % energy)
print("Calculating transmission for %s eV" % energy)
return
def _calc_transmission(self):
@@ -339,7 +189,7 @@ class Att_usd(Assembly):
self.transl_2.set_target_value(p2)
print(f"Set transmission to {t:0.2E} | Moving to pos {[p1, p2]}")
while (abs(p1 - self.transl_1.get_current_value()) > 0.05) or (
abs(p2 - self.transl_2.get_current_value() > 0.05)
(abs(p2 - self.transl_2.get_current_value()) > 0.05)
):
sleep(0.1)
print("transmission changed")
+5 -4
View File
@@ -6,7 +6,7 @@ from ..aliases import Alias
from ..elements.adjustable import AdjustableFS
from ..epics.adjustable import AdjustablePvEnum
from ..elements.assembly import Assembly
from numpy import isnan
class AttenuatorAramis:
def __init__(
@@ -45,8 +45,9 @@ class AttenuatorAramis:
def updateE(self, energy=None):
while not energy:
energy = PV("SARUN03-UIND030:FELPHOTENE").value
energy = energy * 1000
energy = PV("SAROP21-ARAMIS:ENERGY").value
if isnan(energy):
energy = PV("SARUN:FELPHOTENE").value*1000
if energy < self.E_min:
energy = None
print(
@@ -54,7 +55,7 @@ class AttenuatorAramis:
)
sleep(self._sleeptime)
PV(self.Id + ":ENERGY").put(energy)
print("Set energy to %s eV" % energy)
print("Calculating transmission for %s eV" % energy)
return
def set_transmission(self, value, energy=None):
+2 -1
View File
@@ -358,7 +358,8 @@ class AlvraDCM_FEL:
# self.IOCstatus = PV('ALVRA:running') # bool 0 running, 1 not running
self._FELcoupling = PV("SGE-OP2E-ARAMIS:MODE_SP") # string "Off" or "e-beam"
self._setEnergy = PV("SAROP11-ARAMIS:ENERGY_SP_USER") # float eV
self._getEnergy = PV("SAROP11-ARAMIS:ENERGY") # float eV
#self._getEnergy = PV("SAROP11-ARAMIS:ENERGY") # float eV
self._getEnergy = PV("SAROP21-ODCM098:ENERGY") # float eV
self.ebeamEnergy = PV("SARCL02-MBND100:P-READ") # float MeV/c
# self.ebeamEnergySP = PV('ALVRA:Energy_SP') # float MeV
self.dcmStop = PV("SAROP11-ODCM105:STOP.PROC") # stop the DCM motors
+181 -1
View File
@@ -1,5 +1,7 @@
from ..devices_general.motors import MotorRecord, MotorRecord_new
from eco.elements.adjustable import AdjustableFS, AdjustableVirtual
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
from ..epics.detector import DetectorPvData
from epics import PV
from ..devices_general.utilities import Changer
from time import sleep
@@ -13,6 +15,7 @@ from ..elements.adjustable import (
)
from ..devices_general.utilities import Changer
from ..elements.assembly import Assembly
from eco.xoptics.dcm_pathlength_compensation import MonoTimecompensation
@spec_convenience
@@ -21,13 +24,42 @@ from ..elements.assembly import Assembly
class DoubleCrystalMono(Assembly):
def __init__(
self,
pvname,
pvname=None,
name=None,
energy_sp="SAROP21-ARAMIS:ENERGY_SP",
energy_rb="SAROP21-ARAMIS:ENERGY",
fel=None,
las=None,
undulator_deadband_eV=None,
):
super().__init__(name=name)
self._fel = fel
self._las = las
self.undulator_deadband_eV = undulator_deadband_eV
self.pvname = pvname
self._append(
AdjustablePvEnum,
self.pvname + ":MODE",
pvname_set=self.pvname + ":MODE_SP",
name="mode",
)
self._append(
AdjustablePvEnum,
self.pvname + ":CRYSTAL",
pvname_set=self.pvname + ":CRYSTAL_SP",
name="crystal",
)
self._append(
AdjustablePvEnum,
self.pvname + ":DIFF_ORDER",
name="diffraction_order",
)
self._append(DcmConfig, self.pvname, name="mono_config")
self._append(
MotorRecord_new,
pvname + ":RX12",
@@ -54,6 +86,15 @@ class DoubleCrystalMono(Assembly):
pvname + ":RZ1",
name="roll1",
is_setting=True,
has_park_pv=True,
view_toplevel_only=True,
)
self._append(
AdjustablePv,
pvname + ":PIEZO1_VOLTAGE_SP",
pvreadbackname=pvname + ":PIEZO1_VOLTAGE",
name="roll1_piezo",
is_setting=True,
view_toplevel_only=True,
)
self._append(
@@ -68,6 +109,15 @@ class DoubleCrystalMono(Assembly):
pvname + ":RX2",
name="pitch2",
is_setting=True,
has_park_pv=True,
view_toplevel_only=True,
)
self._append(
AdjustablePv,
pvname + ":PIEZO2_VOLTAGE_SP",
pvreadbackname=pvname + ":PIEZO2_VOLTAGE",
name="pitch2_piezo",
is_setting=True,
view_toplevel_only=True,
)
self._append(
@@ -77,7 +127,81 @@ class DoubleCrystalMono(Assembly):
accuracy=0.5,
name="energy",
)
self._append(
DetectorPvData,
energy_rb,
name="readback",
is_setting=False,
is_display=False,
)
self.settings_collection.append(self)
if self._fel is not None:
self._append(
AdjustableFS,
"/photonics/home/gac-bernina/eco/configuration/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))]
e_und_curr = (
self._fel.aramis_photon_energy_undulators.get_current_value()
)
if (
np.abs(en - (e_und_curr + fel_ofs) * 1000)
< self.undulator_deadband_eV
):
return en, None
else:
return en, en / 1000 - fel_ofs
def en_get(monoen, felen):
return monoen
self._append(
AdjustableVirtual,
[self.energy, self._fel.aramis_photon_energy_undulators],
en_get,
en_set,
name="mono_und_energy",
)
if self._las is not None:
self._append(
MonoTimecompensation,
self._las.delay_glob,
self.mono_und_energy,
"/sf/bernina/config/eco/reference_values/dcm_reference_timing.json",
"/sf/bernina/config/eco/reference_values/dcm_reference_invert_delay.json",
name="mono_und_energy_time_corrected",
is_setting=False,
is_display=True,
)
if self._las is not None:
self._append(
MonoTimecompensation,
self._las.delay_glob,
self.energy,
"/sf/bernina/config/eco/reference_values/dcm_reference_timing.json",
"/sf/bernina/config/eco/reference_values/dcm_reference_invert_delay.json",
name="mono_time_corrected",
is_setting=False,
is_display=True,
)
def add_mono_und_calibration_point(self):
mono_energy = self.energy.get_current_value()
fel_offset = (
self.energy.get_current_value() / 1000
- self._fel.aramis_photon_energy_undulators.get_current_value()
)
self.mono_und_calib.mvr([[mono_energy, fel_offset]])
def reset_mono_und_calibration(self):
self.mono_und_calib.mv([])
def set_target_value(self, *args, **kwargs):
return self.energy.set_target_value(*args, **kwargs)
@@ -98,6 +222,62 @@ class DoubleCrystalMono(Assembly):
self.energy._pvreadback.clear_callbacks()
class DcmConfig(Assembly):
def __init__(self, pvbase, name=None):
super().__init__(name=name)
self.pvbase = pvbase
self._append(DetectorPvData, self.pvbase + ":PITCH1_OFF", name="pitch1_offset")
self._append(DetectorPvData, self.pvbase + ":ROLL1_OFF", name="roll1_offset")
self._append(DetectorPvData, self.pvbase + ":PITCH2_OFF", name="pitch2_offset")
self._append(DetectorPvData, self.pvbase + ":ROLL2_OFF", name="roll2_offset")
self._append(DetectorPvData, self.pvbase + ":T2_OFF", name="gap_offset")
self._append(DetectorPvData, self.pvbase + ":TX_OFF", name="x_offset")
self._append(DetectorPvData, self.pvbase + ":T2_MIN", name="gap_min")
self._append(DetectorPvData, self.pvbase + ":T2_MAX", name="gap_max")
self._append(DcmConfigSet, self.pvbase, "CRY1", name="config_Si111")
self._append(DcmConfigSet, self.pvbase, "CRY2", name="config_Si311")
self._append(DcmConfigSet, self.pvbase, "CRY3", name="config_InSb111")
class DcmConfigSet(Assembly):
# SAROP21-ODCM098:PITCH1_CRY1_OFF
def __init__(self, pvbase, par_set_name=None, name=None):
super().__init__(name=name)
self.pvbase = pvbase
self.par_set_name = par_set_name
self._append(
AdjustablePv,
self.pvbase + ":PITCH1_" + self.par_set_name + "_OFF",
name="pitch1_offset",
)
self._append(
AdjustablePv,
self.pvbase + ":ROLL1_" + self.par_set_name + "_OFF",
name="roll1_offset",
)
self._append(
AdjustablePv,
self.pvbase + ":PITCH2_" + self.par_set_name + "_OFF",
name="pitch2_offset",
)
self._append(
AdjustablePv,
self.pvbase + ":ROLL2_" + self.par_set_name + "_OFF",
name="roll2_offset",
)
self._append(
AdjustablePv,
self.pvbase + ":T2_" + self.par_set_name + "_OFF",
name="gap_offset",
)
self._append(
AdjustablePv,
self.pvbase + ":TX_" + self.par_set_name + "_OFF",
name="x_offset",
)
@spec_convenience
@default_representation
class EcolEnergy(Assembly):
+7 -1
View File
@@ -3,6 +3,8 @@ import numpy as np
from xrayutilities import materials
from ..elements.adjustable import AdjustableFS, AdjustableVirtual
from ..elements.assembly import Assembly
from eco import Adjustable
from eco.devices_general.motors import MotorRecord
def energy2tthe(energy, hkl=(1, 1, 1), material=materials.Si):
@@ -43,7 +45,11 @@ class MonoTimecompensation(Assembly):
default_value=True,
name="laser_delay_inverted",
)
self._laser_delay = laser_delay_seconds
if isinstance(laser_delay_seconds, Adjustable):
self._laser_delay = laser_delay_seconds
else:
raise Exception("issue getting laser delay for mono compensation")
self._mono_energy = mono_energy_eV
self._append(
AdjustableVirtual,
+29 -111
View File
@@ -10,7 +10,7 @@ from numbers import Number
from tabulate import tabulate
class KBMirrorBernina_new(Assembly):
class KBMirrorBernina(Assembly):
def __init__(
self,
pvname_ver,
@@ -26,6 +26,7 @@ class KBMirrorBernina_new(Assembly):
d_target=1520.0,
d_att=1420.0,
d_prof_dsd=3725.0,
d_plate_mondsd=3147.5,
name=None,
):
"""All distances are from sample interaction point at straight beam (no kb deflection), the units are expected in mm"""
@@ -37,6 +38,9 @@ class KBMirrorBernina_new(Assembly):
self._append(
KbHor, pvname_hor, name="hor", is_setting=True, is_display="recursive"
)
self._append(
AdjustablePvEnum, "SAROP21-OKB:MODE_SP", name="mode", is_setting=False
)
self.diffractometer = diffractometer
self.usd_table = usd_table
@@ -48,9 +52,10 @@ class KBMirrorBernina_new(Assembly):
self.d_win1 = d_win1
self.d_target = d_target
self.d_prof_dsd = d_prof_dsd
self.d_plate_mondsd = d_plate_mondsd
def calc_positions(self, the_kbver, the_kbhor):
"""angles in rad"""
"""angles in rad, positive only (i.e. describe incidence angles to KBs)"""
pos_calc = {}
pos_calc["y_kbhor"] = np.tan(2 * the_kbver) * np.abs(
self.d_kbver - self.d_kbhor
@@ -62,16 +67,25 @@ class KBMirrorBernina_new(Assembly):
pos_calc["ry_hex"] = 2 * the_kbhor
pos_calc["x_diff"] = np.tan(2 * the_kbhor) * np.abs(self.d_kbhor)
pos_calc["y_diff"] = np.tan(2 * the_kbver) * np.abs(self.d_kbver)
pos_calc["x_dsd"] = np.tan(2 * the_kbhor) * np.abs(
pos_calc["x_prof_dsd"] = np.tan(2 * the_kbhor) * np.abs(
self.d_kbhor + self.d_prof_dsd
)
pos_calc["y_dsd"] = np.tan(2 * the_kbver) * np.abs(
pos_calc["y_prof_dsd"] = np.tan(2 * the_kbver) * np.abs(
self.d_kbver + self.d_prof_dsd
)
pos_calc["x_plate_mon_dsd"] = np.tan(2 * the_kbhor) * np.abs(
self.d_kbhor + self.d_plate_mondsd
)
pos_calc["y_plate_mon_dsd"] = np.tan(2 * the_kbver) * np.abs(
self.d_kbver + self.d_plate_mondsd
)
return pos_calc
def calc_fwhm(self, fwhm_hor, fwhm_ver, z_focver=0, z_fochor=0, E_phot=None):
"""E_phot in eV, length units in mm."""
def calc_fwhm(
self, fwhm_hor, fwhm_ver, z_focver=0, z_fochor=0, distances={}, E_phot=None
):
"""calculates beamsize at different locations based on the Beam size before kb focusing.
E_phot in eV, length units in mm."""
lam = constants.c * constants.h / constants.electron_volt / E_phot
print(lam * 1e10)
fwhm_fac = 1 / np.sqrt(2 * np.log(2)) # w = fwhm_fac*fwhm
@@ -109,6 +123,13 @@ class KBMirrorBernina_new(Assembly):
res["sample"] = (fwhm_z(z_fochor, w0_hor), fwhm_z(z_focver, w0_ver))
# res['fwhm_kbver'] = (fwhm_z(self.d_kbver+z_fochor,w0_hor),fwhm_z(self.d_kbver+z_focver,w0_ver))
# res['fwhm_kbhor'] = (fwhm_z(self.d_kbhor+z_fochor,w0_hor),fwhm_z(self.d_kbhor+z_focver,w0_ver))
for tdn, tdv in distances.items():
res[tdn] = (
fwhm_z(tdv + z_fochor, w0_hor),
fwhm_z(tdv + z_focver, w0_ver),
)
return res
def move_hex_for_kb_angles(self, the_kbver, the_kbhor):
@@ -153,6 +174,8 @@ class KBMirrorBernina_new(Assembly):
ocoo = self.usd_table.get_coordinates()
odiffx = self.diffractometer.xbase.get_current_value()
odiffy = self.diffractometer.ybase.get_current_value()
# TODO implement motions for the angles as well. ISSUE is that the rx motion somehow decreases the distance to the usd too much for the shurt bellows (as of Feb 27 2023).
odiffrx = self.diffractometer.rxbase.get_current_value()
odiffnu = self.diffractometer.nu.get_current_value()
odiffmu = self.diffractometer.mu.get_current_value()
@@ -263,111 +286,6 @@ class KBMirrorBernina_new(Assembly):
return focpos_hor, focpos_ver, out
class KBMirrorBernina:
def __init__(
self,
kb_ver=None,
kb_hor=None,
usd_table=None,
d_kbver=3350.0,
d_kbhor=2600.0,
d_hex=1600.0,
d_win1=1945.0,
d_win2=1330.0,
d_target=1520.0,
d_att=1420.0,
):
"""All distances are from sample interaction point at straight beam (no kb deflection), the units are expected in mm"""
self.kb_ver = kb_ver
self.kb_hor = kb_hor
self.usd_table = usd_table
self.d_kbver = d_kbver
self.d_kbhor = d_kbhor
self.d_hex = d_hex
self.d_att = d_att
self.d_win2 = d_win2
self.d_win1 = d_win1
self.d_target = d_target
def calc_positions(self, the_kbver, the_kbhor):
"""angles in rad"""
y_kbhor = np.tan(2 * the_kbver) * np.abs(self.d_kbver - self.d_kbhor)
rx_kbhor = -2 * the_kbver
y_hex = np.tan(2 * the_kbver) * np.abs(self.d_kbver - self.d_hex)
x_hex = np.tan(2 * the_kbhor) * np.abs(self.d_kbhor - self.d_hex)
rx_hex = rx_kbhor
ry_hex = 2 * the_kbhor
return {
"y_kbhor": y_kbhor,
"rx_kbhor": rx_kbhor,
"x_hex": x_hex,
"y_hex": y_hex,
"rx_hex": rx_hex,
"ry_hex": ry_hex,
}
def calc_fwhm(self, fwhm_hor, fwhm_ver, z_focver=0, z_fochor=0, E_phot=None):
"""E_phot in eV, length units in mm."""
lam = constants.c * constants.h / constants.electron_volt / E_phot
print(lam * 1e10)
fwhm_fac = 1 / np.sqrt(2 * np.log(2)) # w = fwhm_fac*fwhm
# div_ver = np.arctan(fwhm_ver*fwhm_fac/2/(self.d_kbver+z_focver)) #half divergence
# div_hor = np.arctan(fwhm_hor*fwhm_fac/2/(self.d_kbhor+z_fochor))
c = lam / np.pi * 1e3
w0 = lambda w, z: (
w**2 - (w**4 - (2 * c * z) ** 2) ** 0.5
) ** 0.5 / np.sqrt(2)
w = lambda w0, z: (w0**2 + (c * z / w0) ** 2) ** 0.5
zr = lambda w0: w0**2 / c
fwhm_z = lambda z, w0: w(w0, z) / fwhm_fac
w0_hor = w0(fwhm_hor * fwhm_fac, self.d_kbhor + z_fochor)
w0_ver = w0(fwhm_ver * fwhm_fac, self.d_kbver + z_focver)
print(w0_hor)
res = {}
res["target"] = (
fwhm_z(self.d_target + z_fochor, w0_hor),
fwhm_z(self.d_target + z_focver, w0_ver),
)
res["win1"] = (
fwhm_z(self.d_win1 + z_fochor, w0_hor),
fwhm_z(self.d_win1 + z_focver, w0_ver),
)
res["win2"] = (
fwhm_z(self.d_win2 + z_fochor, w0_hor),
fwhm_z(self.d_win2 + z_focver, w0_ver),
)
res["att"] = (
fwhm_z(self.d_att + z_fochor, w0_hor),
fwhm_z(self.d_att + z_focver, w0_ver),
)
res["sample"] = (fwhm_z(z_fochor, w0_hor), fwhm_z(z_focver, w0_ver))
# res['fwhm_kbver'] = (fwhm_z(self.d_kbver+z_fochor,w0_hor),fwhm_z(self.d_kbver+z_focver,w0_ver))
# res['fwhm_kbhor'] = (fwhm_z(self.d_kbhor+z_fochor,w0_hor),fwhm_z(self.d_kbhor+z_focver,w0_ver))
return res
def move_hex_for_kb_angles(self, the_kbver, the_kbhor):
pos = self.calc_positions(the_kbver, the_kbhor)
x = pos["x_hex"]
y = pos["y_hex"]
rx = pos["rx_hex"] * 180 / np.pi
ry = pos["ry_hex"] * 180 / np.pi
z = rz = 0.0
ax, ay, az, arx, ary, arz = self.usd_table.get_coordinates()
print(
f"present upstream large hexapod position is (x/mm,y/mm,z/mm,rx/°,ry/°,rz/°) = ({ax:g},{ay:g},{az:g},{arx:g},{ary:g},{arz:g})"
)
print(
f"moving to (x/mm,y/mm,z/mm,rx/°,ry/°,rz/°) = ({x:g},{y:g},{z:g},{rx:g},{ry:g},{rz:g})"
)
if not input("start moving upstream large hexapod? (y/n)") == "y":
print("did nothing")
return
else:
self.usd_table.move_to_coordinates(x, y, z, rx, ry, rz)
def calc_focus_pos_intercept(size0, size1, distance):
"""calculates the position of the focus based on simple intercept theorem.
position is from where size 0 was measured, and the focus will be between the
+67 -10
View File
@@ -3,6 +3,7 @@ from ..devices_general.motors import MotorRecord
from ..elements.adjustable import AdjustableVirtual
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
import numpy as np
from epics import PV
class KbVer(Assembly):
@@ -40,6 +41,7 @@ class KbVer(Assembly):
lambda b1, b2: float(np.mean([b1, b2])),
lambda mn: self._get_benders_set_mean(mn),
name="bender_mean",
unit="mm",
is_setting=False,
is_display=True,
)
@@ -49,6 +51,7 @@ class KbVer(Assembly):
lambda b1, b2: float(np.diff([b1, b2])),
lambda mn: self._get_benders_set_diff(mn),
name="bender_diff",
unit="mm",
is_setting=False,
is_display=True,
)
@@ -68,11 +71,37 @@ class KbVer(Assembly):
)
#### actual motors ###
self._append(MotorRecord, pvname + ":TY1", name="_Y1", is_setting=True)
self._append(MotorRecord, pvname + ":TY2", name="_Y2", is_setting=True)
self._append(MotorRecord, pvname + ":TY3", name="_Y3", is_setting=True)
self._append(MotorRecord, pvname + ":TX1", name="_X1", is_setting=True)
self._append(MotorRecord, pvname + ":TX2", name="_X2", is_setting=True)
self._append(
MotorRecord, pvname + ":TY1", name="_Y1", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TY2", name="_Y2", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TY3", name="_Y3", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TX1", name="_X1", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TX2", name="_X2", is_setting=True, is_display=False
)
self._pv_sync_world = PV(pvname + ":SYNC_AXES")
self._pv_amp_reset = PV(pvname + ":RESET_AMP.PROC")
self._pv_parkall = PV(pvname + "::KILL_ALL.PROC")
self._pv_enable_all = PV(pvname + ":ENABLE_ALL.PROC")
self._pv_sync_all_axes = PV(pvname + ":SYNC.PROC")
self._pv_safety_on = PV(pvname + ":SAFETY_ON.PROC")
self._pv_safety_off = PV(pvname + ":SAFETY_OFF.PROC")
def sync_world(self):
self._pv_sync_world.put(1)
def sync_phys_axes(self):
self._pv_sync_all_axes.put(1)
def park_all(self):
self._pv_parkall.put(1)
def _get_bend_mean(self):
return float(
@@ -132,6 +161,7 @@ class KbHor(Assembly):
lambda b1, b2: float(np.mean([b1, b2])),
lambda mn: self._get_benders_set_mean(mn),
name="bender_mean",
unit="mm",
is_setting=False,
is_display=True,
)
@@ -141,6 +171,7 @@ class KbHor(Assembly):
lambda b1, b2: float(np.diff([b1, b2])),
lambda mn: self._get_benders_set_diff(mn),
name="bender_diff",
unit="mm",
is_setting=False,
is_display=True,
)
@@ -160,11 +191,37 @@ class KbHor(Assembly):
)
#### actual motors ###
self._append(MotorRecord, pvname + ":TY1", name="_Y1", is_setting=True)
self._append(MotorRecord, pvname + ":TY2", name="_Y2", is_setting=True)
self._append(MotorRecord, pvname + ":TY3", name="_Y3", is_setting=True)
self._append(MotorRecord, pvname + ":TX1", name="_X1", is_setting=True)
self._append(MotorRecord, pvname + ":TX2", name="_X2", is_setting=True)
self._append(
MotorRecord, pvname + ":TY1", name="_Y1", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TY2", name="_Y2", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TY3", name="_Y3", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TX1", name="_X1", is_setting=True, is_display=False
)
self._append(
MotorRecord, pvname + ":TX2", name="_X2", is_setting=True, is_display=False
)
self._pv_sync_world = PV(pvname + ":SYNC_AXES")
self._pv_amp_reset = PV(pvname + ":RESET_AMP.PROC")
self._pv_parkall = PV(pvname + "::KILL_ALL.PROC")
self._pv_enable_all = PV(pvname + ":ENABLE_ALL.PROC")
self._pv_sync_all_axes = PV(pvname + ":SYNC.PROC")
self._pv_safety_on = PV(pvname + ":SAFETY_ON.PROC")
self._pv_safety_off = PV(pvname + ":SAFETY_OFF.PROC")
def sync_world(self):
self._pv_sync_world.put(1)
def sync_phys_axes(self):
self._pv_sync_all_axes.put(1)
def park_all(self):
self._pv_parkall.put(1)
def _get_bend_mean(self):
return float(
+35
View File
@@ -1,6 +1,8 @@
from ..devices_general.motors import MotorRecord_new
from ..epics.adjustable import AdjustablePv, AdjustablePvEnum
from ..elements.assembly import Assembly
from ..elements.adjustable import AdjustableVirtual
import numpy as np
class OffsetMirror(Assembly):
@@ -46,3 +48,36 @@ class OffsetMirrorsBernina(Assembly):
is_setting=True,
is_display="recursive",
)
self._append(
AdjustableVirtual,
[self.mirr1.rz, self.mirr2.rz],
lambda b1, b2: float(np.mean([b1, b2])),
lambda mn: self._set_mean_2adj(mn, self.mirr1.rz, self.mirr2.rz),
name="rz_mean",
unit="mrad",
is_setting=False,
is_display=True,
)
self._append(
AdjustableVirtual,
[self.mirr1.rz, self.mirr2.rz],
lambda b1, b2: float(np.diff([b1, b2])),
lambda mn: self._set_diff_2adj(mn, self.mirr1.rz, self.mirr2.rz),
name="rz_diff",
unit="mrad",
is_setting=False,
is_display=True,
)
def _set_mean_2adj(self, val, adj0, adj1):
mn = np.mean([adj0.get_current_value(), adj1.get_current_value()])
df = val - mn
return adj0.get_current_value() + df, adj1.get_current_value() + df
def _set_diff_2adj(self, val, adj0, adj1):
df = val - np.diff([adj0.get_current_value(), adj1.get_current_value()])
return (
adj0.get_current_value() - df / 2,
adj1.get_current_value() + df / 2,
)
+2 -2
View File
@@ -38,12 +38,12 @@ class Pulsepick:
def open(self):
self._openclose.put(1)
self._evrsrc.put(62)
#self._evrsrc.put(62)
print("Opened Pulse Picker")
def close(self):
self._openclose.put(0)
self._evrsrc.put(62)
#self._evrsrc.put(62)
print("Closed Pulse Picker")
def trigger(self):
+64
View File
@@ -0,0 +1,64 @@
from epics import PV
from ..devices_general.utilities import Changer
from time import sleep
import numpy as np
from ..devices_general.motors import MotorRecord
from ..aliases import Alias
def addMotorRecordToSelf(self, name=None, Id=None):
try:
self.__dict__[name] = MotorRecord(Id, name=name)
self.alias.append(self.__dict__[name].alias)
except:
print(f"Warning! Could not find motor {name} (Id:{Id})")
class PulsePickerSwissfel(Assembly):
def __init__(self, Id=None, evronoff=None, evrsrc=None, name=None):
self.name = name
self.alias = Alias(name)
self.evrsrc = evrsrc
self.evronoff = evronoff
self.Id = Id
self._openclose = PV(self.evronoff)
self._evrsrc = PV(self.evrsrc)
addMotorRecordToSelf(self, Id=self.Id + ":MOTOR_X1", name="x")
addMotorRecordToSelf(self, Id=self.Id + ":MOTOR_Y1", name="y")
def movein(self):
self.x.set_target_value(4.45)
self.y.set_target_value(-1.75)
def moveout(self):
self.x.set_target_value(-5)
self.y.set_target_value(-1.75)
def open(self):
self._openclose.put(1)
self._evrsrc.put(62)
print("Opened Pulse Picker")
def close(self):
self._openclose.put(0)
self._evrsrc.put(62)
print("Closed Pulse Picker")
def trigger(self):
self._openclose.put(1)
self._evrsrc.put(0)
print("Set Pulse Picker to trigger (src 0 and output On)")
def get_status(self):
stat = self._evrsrc.get()
if stat == 62 and self._openclose.get() == 1:
return "open"
if self._openclose.get() == 0:
return "closed"
else:
return "unknown"
def __repr__(self):
return f"FEL pulse picker state {self.get_status()}."
Executable → Regular
+149 -15
View File
@@ -1,26 +1,124 @@
from ..devices_general.motors import MotorRecord
from enum import Enum
from eco.elements.adjustable import AdjustableGetSet
from eco.epics.adjustable import AdjustablePvEnum
from ..devices_general.motors import MotorRecord, SmaractRecord
from epics import PV
from ..aliases import Alias, append_object_to_object
from ..elements.assembly import Assembly
class RefLaser_Aramis:
def __init__(self, Id, elog=None, name=None, inpos=-18.947, outpos=-5):
class RefLaser_BerninaUSD(Assembly):
def __init__(
self,
pvname_mirrortranslation="SARES23-LIC:MOT_12",
pvname_onoff="SARES21-CPCL-PS7071:LV_OMPV_1_CH1_SWITCH_SP",
elog=None,
name=None,
):
super().__init__(name=name)
self.elog = elog
# append_object_to_object(self,
self._append(
SmaractRecord, pvname_mirrortranslation, name="x_mirror", is_setting=True
)
self._append(
AdjustablePvEnum, pvname_onoff, name="laser_power", is_setting=True
)
def movein(self, wait=False):
try:
self.presets.movein()
except:
print("No movein preset found.")
def moveout(self, wait=False):
try:
self.presets.moveout()
except:
print("No moveout preset found.")
# self._append(
# AdjustableGetSet,
# self.get_in_status,
# self.set,
# name="state",
# is_setting=False,
# )
# self._append(MotorRecord, self.Id + ":MOTOR_1", name="mirror", is_setting=True)
# self._append(
# RefLaserLaser,
# self.Id,
# name="laser",
# is_setting=True,
# is_display="recursive",
# )
# self._append(
# RefLaserAperture,
# "SAROP21-OLIR134",
# name="aperture",
# is_setting=True,
# is_display="recursive",
# )
# self._append(MotorRecord, pv_lir0 + ":MOTOR_MX", name="x_ap1", is_setting=True)
# self._append(MotorRecord, pv_lir0 + ":MOTOR_MY", name="y_ap1", is_setting=True)
# pv_lir1 = "SAROP21-OLIR138" # TODO hardcoded
# self._append(MotorRecord, pv_lir1 + ":MOTOR_MX", name="x_ap2", is_setting=True)
# self._append(MotorRecord, pv_lir1 + ":MOTOR_MY", name="y_ap2", is_setting=True)
# self.mirror.set_limits(-20, 0)
class RefLaser_Aramis(Assembly):
def __init__(self, Id, elog=None, name=None, inpos=-19, outpos=-5):
super().__init__(name=name)
self.Id = Id
self.elog = elog
self.name = name
# append_object_to_object(self,
self._inpos = inpos
self._outpos = outpos
self.mirrmotor = MotorRecord(self.Id + ":MOTOR_1")
self.x_trans = MotorRecord(self.Id + ":MOTOR_X1")
self.z_trans = MotorRecord(self.Id + ":MOTOR_Z1")
self.x_rot = MotorRecord(self.Id + ":MOTOR_ROT_X1")
self.z_rot = MotorRecord(self.Id + ":MOTOR_ROT_Z1")
self._append(
AdjustableGetSet,
self.get_in_status,
self.set,
name="state",
is_setting=False,
)
self._append(MotorRecord, self.Id + ":MOTOR_1", name="mirror", is_setting=True)
self._append(
RefLaserLaser,
self.Id,
name="laser",
is_setting=True,
is_display="recursive",
)
self._append(
RefLaserAperture,
"SAROP21-OLIR134",
name="aperture",
is_setting=True,
is_display="recursive",
)
# self._append(MotorRecord, pv_lir0 + ":MOTOR_MX", name="x_ap1", is_setting=True)
# self._append(MotorRecord, pv_lir0 + ":MOTOR_MY", name="y_ap1", is_setting=True)
# pv_lir1 = "SAROP21-OLIR138" # TODO hardcoded
# self._append(MotorRecord, pv_lir1 + ":MOTOR_MX", name="x_ap2", is_setting=True)
# self._append(MotorRecord, pv_lir1 + ":MOTOR_MY", name="y_ap2", is_setting=True)
self.mirror.set_limits(-20, 0)
def __call__(self, *args, **kwargs):
self.set(*args, **kwargs)
def __str__(self):
status = self.get_status()
status = self.get_in_status()
if status:
return "Reflaser is In."
elif status == False:
@@ -28,14 +126,15 @@ class RefLaser_Aramis:
elif status == None:
return "Reflaser status not defined."
def get_status(self):
v = self.mirrmotor.get_current_value()
def get_in_status(self):
v = self.mirror.get_current_value()
if abs(v - self._inpos) < 0.2:
isin = True
elif abs(v - self._outpos) < 0.2:
isin = False
else:
isin = None
# return State(isin)
return isin
def set(self, value):
@@ -47,9 +146,9 @@ class RefLaser_Aramis:
else:
print("String %s not recognized!" % value)
if value:
self.mirrmotor.set_target_value(self._inpos)
self.mirror.set_target_value(self._inpos)
else:
self.mirrmotor.set_target_value(self._outpos)
self.mirror.set_target_value(self._outpos)
def movein(self):
self.set("in")
@@ -58,4 +157,39 @@ class RefLaser_Aramis:
self.set("out")
def __repr__(self):
return self.__str__()
return self.__str__() + "\n" + super().__repr__()
class RefLaserAperture(Assembly):
def __init__(self, pvname, name=None):
super().__init__(name=name)
self._append(
MotorRecord,
pvname + ":MOTOR_MX",
name="x",
is_setting=True,
is_display=True,
)
self._append(
MotorRecord,
pvname + ":MOTOR_MY",
name="y",
is_setting=True,
is_display=True,
)
class RefLaserLaser(Assembly):
def __init__(self, pvname, name=None):
super().__init__(name=name)
self._append(MotorRecord, pvname + ":MOTOR_X1", name="x", is_setting=True)
self._append(MotorRecord, pvname + ":MOTOR_Z1", name="z", is_setting=True)
self._append(MotorRecord, pvname + ":MOTOR_ROT_X1", name="rx", is_setting=True)
self._append(MotorRecord, pvname + ":MOTOR_ROT_Z1", name="rz", is_setting=True)
class State(Enum):
IN = 1
OUT = 0
UNDEFINED = None
-75
View File
@@ -1,75 +0,0 @@
from ..devices_general.motors import MotorRecord
from epics import PV
from ..aliases import Alias, append_object_to_object
from ..elements.assembly import Assembly
class RefLaser_Aramis(Assembly):
def __init__(self, Id, elog=None, name=None, inpos=-18.818, outpos=-5):
super().__init__(name=name)
self.Id = Id
self.elog = elog
# append_object_to_object(self,
self._inpos = inpos
self._outpos = outpos
self._append(MotorRecord, self.Id + ":MOTOR_1", name="mirror", is_setting=True)
self._append(MotorRecord, self.Id + ":MOTOR_X1", name="x1", is_setting=True)
self._append(MotorRecord, self.Id + ":MOTOR_Z1", name="z1", is_setting=True)
self._append(
MotorRecord, self.Id + ":MOTOR_ROT_X1", name="rx1", is_setting=True
)
self._append(
MotorRecord, self.Id + ":MOTOR_ROT_Z1", name="rz1", is_setting=True
)
pv_lir0 = "SAROP21-OLIR136" # TODO hardcoded
self._append(MotorRecord, pv_lir0 + ":MOTOR_MX", name="x_ap1", is_setting=True)
self._append(MotorRecord, pv_lir0 + ":MOTOR_MY", name="y_ap1", is_setting=True)
pv_lir1 = "SAROP21-OLIR138" # TODO hardcoded
self._append(MotorRecord, pv_lir1 + ":MOTOR_MX", name="x_ap2", is_setting=True)
self._append(MotorRecord, pv_lir1 + ":MOTOR_MY", name="y_ap2", is_setting=True)
self.mirror.set_limits(-20, 0)
def __call__(self, *args, **kwargs):
self.set(*args, **kwargs)
def __str__(self):
status = self.get_in_status()
if status:
return "Reflaser is In."
elif status == False:
return "Reflaser is Out."
elif status == None:
return "Reflaser status not defined."
def get_in_status(self):
v = self.mirror.get_current_value()
if abs(v - self._inpos) < 0.2:
isin = True
elif abs(v - self._outpos) < 0.2:
isin = False
else:
isin = None
return isin
def set(self, value):
if type(value) is str:
if value.lower() == "in":
value = True
elif value.lower() == "out":
value = False
else:
print("String %s not recognized!" % value)
if value:
self.mirror.set_target_value(self._inpos)
else:
self.mirror.set_target_value(self._outpos)
def movein(self):
self.set("in")
def moveout(self):
self.set("out")
def __repr__(self):
return self.__str__() + "\n" + super().__repr__()
+19 -19
View File
@@ -329,15 +329,16 @@ class SlitBlades(Assembly):
@addSlitRepr
class SlitPosWidth:
class SlitPosWidth(Assembly):
def __init__(self, pvname, name=None, elog=None):
self.name = name
self.Id = pvname
self.alias = Alias(name)
append_object_to_object(self, MotorRecord, pvname + ":MOTOR_X", name="hpos")
append_object_to_object(self, MotorRecord, pvname + ":MOTOR_Y", name="vpos")
append_object_to_object(self, MotorRecord, pvname + ":MOTOR_W", name="hgap")
append_object_to_object(self, MotorRecord, pvname + ":MOTOR_H", name="vgap")
super().__init__(name=name)
self.pvname = pvname
self._append(MotorRecord, pvname + ":MOTOR_X", name="hpos")
self._append(MotorRecord, pvname + ":MOTOR_Y", name="vpos")
self._append(MotorRecord, pvname + ":MOTOR_W", name="hgap")
self._append(MotorRecord, pvname + ":MOTOR_H", name="vgap")
def getblade(pos, gap, direction=1):
return pos + direction * gap / 2
@@ -367,8 +368,7 @@ class SlitPosWidth:
[x + tx * self.vgap.get_current_value() for tx in [-1 / 2, 1 / 2]]
)
append_object_to_object(
self,
self._append(
AdjustableVirtual,
[self.vpos, self.vgap],
partial(getblade, direction=1),
@@ -376,8 +376,7 @@ class SlitPosWidth:
reset_current_value_to=True,
name="up",
)
append_object_to_object(
self,
self._append(
AdjustableVirtual,
[self.vpos, self.vgap],
partial(getblade, direction=-1),
@@ -385,8 +384,7 @@ class SlitPosWidth:
reset_current_value_to=True,
name="down",
)
append_object_to_object(
self,
self._append(
AdjustableVirtual,
[self.hpos, self.hgap],
partial(getblade, direction=1),
@@ -394,8 +392,7 @@ class SlitPosWidth:
reset_current_value_to=True,
name="left",
)
append_object_to_object(
self,
self._append(
AdjustableVirtual,
[self.hpos, self.hgap],
partial(getblade, direction=-1),
@@ -844,10 +841,10 @@ class SlitBladesGeneral(Assembly):
return tuple([tx + self.vpos.get_current_value() for tx in [-x / 2, x / 2]])
def sethpos(x):
return tuple([tx + self.hgap.get_current_value() for tx in [-x / 2, x / 2]])
return tuple([x + tx for tx in [-self.hgap.get_current_value()/2, self.hgap.get_current_value()/2]])
def setvpos(x):
return tuple([tx + self.vgap.get_current_value() for tx in [-x / 2, x / 2]])
return tuple([x + tx for tx in [-self.vgap.get_current_value()/2, self.vgap.get_current_value()/2]])
self._append(
AdjustableVirtual,
@@ -885,7 +882,10 @@ class SlitBladesGeneral(Assembly):
def _apply_on_all_blades(self, method_name, *args, **kwargs):
out = []
for blade in self.blade_motors:
out.append(blade.__dict__[method_name](*args, **kwargs))
if method_name in blade.__dict__.keys():
out.append(blade.__dict__[method_name](*args, **kwargs))
else:
out.append(blade.__getattribute__(method_name)(*args, **kwargs))
return out
def home_all_blades(self):