dls-dodal 1.36.3__py3-none-any.whl → 1.38.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/METADATA +4 -3
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/RECORD +58 -38
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/i02_1.py +37 -0
- dodal/beamlines/i03.py +34 -5
- dodal/beamlines/i04.py +16 -5
- dodal/beamlines/i10.py +105 -0
- dodal/beamlines/i13_1.py +20 -2
- dodal/beamlines/i22.py +15 -0
- dodal/beamlines/i24.py +14 -2
- dodal/beamlines/p99.py +6 -2
- dodal/beamlines/training_rig.py +10 -1
- dodal/common/crystal_metadata.py +3 -3
- dodal/common/udc_directory_provider.py +3 -1
- dodal/devices/aperturescatterguard.py +3 -0
- dodal/devices/{attenuator.py → attenuator/attenuator.py} +29 -1
- dodal/devices/attenuator/filter.py +11 -0
- dodal/devices/attenuator/filter_selections.py +72 -0
- dodal/devices/bimorph_mirror.py +151 -0
- dodal/devices/current_amplifiers/__init__.py +34 -0
- dodal/devices/current_amplifiers/current_amplifier.py +103 -0
- dodal/devices/current_amplifiers/current_amplifier_detector.py +109 -0
- dodal/devices/current_amplifiers/femto.py +143 -0
- dodal/devices/current_amplifiers/sr570.py +214 -0
- dodal/devices/current_amplifiers/struck_scaler_counter.py +79 -0
- dodal/devices/detector/det_dim_constants.py +15 -0
- dodal/devices/eiger_odin.py +3 -3
- dodal/devices/fast_grid_scan.py +8 -3
- dodal/devices/flux.py +10 -3
- dodal/devices/i03/beamstop.py +85 -0
- dodal/devices/i04/transfocator.py +67 -53
- dodal/devices/i10/rasor/rasor_current_amp.py +72 -0
- dodal/devices/i10/rasor/rasor_motors.py +62 -0
- dodal/devices/i10/rasor/rasor_scaler_cards.py +12 -0
- dodal/devices/i13_1/__init__.py +0 -0
- dodal/devices/i13_1/merlin.py +33 -0
- dodal/devices/i13_1/merlin_controller.py +52 -0
- dodal/devices/i13_1/merlin_io.py +17 -0
- dodal/devices/i24/beam_center.py +1 -1
- dodal/devices/p45.py +31 -20
- dodal/devices/p99/sample_stage.py +2 -28
- dodal/devices/robot.py +2 -2
- dodal/devices/s4_slit_gaps.py +8 -4
- dodal/devices/undulator_dcm.py +9 -11
- dodal/devices/util/lookup_tables.py +14 -10
- dodal/devices/zebra/__init__.py +0 -0
- dodal/devices/{zebra.py → zebra/zebra.py} +9 -33
- dodal/devices/zebra/zebra_constants_mapping.py +96 -0
- dodal/devices/zocalo/zocalo_interaction.py +2 -1
- dodal/devices/zocalo/zocalo_results.py +22 -2
- dodal/log.py +2 -2
- dodal/plans/wrapped.py +3 -3
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/LICENSE +0 -0
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/top_level.txt +0 -0
- /dodal/devices/{zebra_controlled_shutter.py → zebra/zebra_controlled_shutter.py} +0 -0
- /dodal/{devices/util → plans}/save_panda.py +0 -0
|
@@ -14,37 +14,7 @@ from ophyd_async.core import (
|
|
|
14
14
|
)
|
|
15
15
|
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
# Sources
|
|
19
|
-
DISCONNECT = 0
|
|
20
|
-
IN1_TTL = 1
|
|
21
|
-
IN2_TTL = 4
|
|
22
|
-
IN3_TTL = 7
|
|
23
|
-
IN4_TTL = 10
|
|
24
|
-
PC_ARM = 29
|
|
25
|
-
PC_GATE = 30
|
|
26
|
-
PC_PULSE = 31
|
|
27
|
-
AND3 = 34
|
|
28
|
-
AND4 = 35
|
|
29
|
-
OR1 = 36
|
|
30
|
-
PULSE1 = 52
|
|
31
|
-
PULSE2 = 53
|
|
32
|
-
SOFT_IN1 = 60
|
|
33
|
-
SOFT_IN2 = 61
|
|
34
|
-
SOFT_IN3 = 62
|
|
35
|
-
|
|
36
|
-
# Instrument specific
|
|
37
|
-
TTL_DETECTOR = 1
|
|
38
|
-
TTL_SHUTTER = 2
|
|
39
|
-
TTL_XSPRESS3 = 3
|
|
40
|
-
TTL_PANDA = 4
|
|
41
|
-
|
|
42
|
-
# The AND gate that controls the automatic shutter
|
|
43
|
-
AUTO_SHUTTER_GATE = 2
|
|
44
|
-
|
|
45
|
-
# The first two inputs of the auto shutter gate.
|
|
46
|
-
AUTO_SHUTTER_INPUT_1 = 1
|
|
47
|
-
AUTO_SHUTTER_INPUT_2 = 2
|
|
17
|
+
from dodal.devices.zebra.zebra_constants_mapping import ZebraMapping
|
|
48
18
|
|
|
49
19
|
|
|
50
20
|
class ArmSource(StrictEnum):
|
|
@@ -81,6 +51,11 @@ class I24Axes:
|
|
|
81
51
|
|
|
82
52
|
|
|
83
53
|
class RotationDirection(StrictEnum):
|
|
54
|
+
"""
|
|
55
|
+
Defines for a swept angle whether the scan width (sweep) is to be added or subtracted from
|
|
56
|
+
the initial angle to obtain the final angle.
|
|
57
|
+
"""
|
|
58
|
+
|
|
84
59
|
POSITIVE = "Positive"
|
|
85
60
|
NEGATIVE = "Negative"
|
|
86
61
|
|
|
@@ -281,7 +256,7 @@ class LogicGateConfiguration:
|
|
|
281
256
|
for input, (source, invert) in enumerate(
|
|
282
257
|
zip(self.sources, self.invert, strict=False)
|
|
283
258
|
):
|
|
284
|
-
input_strings.append(f"INP{input+1}={'!' if invert else ''}{source}")
|
|
259
|
+
input_strings.append(f"INP{input + 1}={'!' if invert else ''}{source}")
|
|
285
260
|
|
|
286
261
|
return ", ".join(input_strings)
|
|
287
262
|
|
|
@@ -298,7 +273,8 @@ class SoftInputs(StandardReadable):
|
|
|
298
273
|
class Zebra(StandardReadable):
|
|
299
274
|
"""The Zebra device."""
|
|
300
275
|
|
|
301
|
-
def __init__(self, name: str, prefix: str) -> None:
|
|
276
|
+
def __init__(self, mapping: ZebraMapping, name: str, prefix: str) -> None:
|
|
277
|
+
self.mapping = mapping
|
|
302
278
|
self.pc = PositionCompare(prefix, name)
|
|
303
279
|
self.output = ZebraOutputPanel(prefix, name)
|
|
304
280
|
self.inputs = SoftInputs(prefix, name)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from collections import Counter
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, model_validator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ZebraMappingValidations(BaseModel):
|
|
7
|
+
"""Raises an exception if field set to -1 is accessed, and validate against
|
|
8
|
+
multiple fields mapping to the same integer"""
|
|
9
|
+
|
|
10
|
+
def __getattribute__(self, name: str):
|
|
11
|
+
"""To protect against mismatch between the Zebra configuration that a plan expects and the Zebra which has
|
|
12
|
+
been instantiated, raise exception if a field which has been set to -1 is accessed."""
|
|
13
|
+
value = object.__getattribute__(self, name)
|
|
14
|
+
if not name.startswith("__"):
|
|
15
|
+
if value == -1:
|
|
16
|
+
raise UnmappedZebraException(
|
|
17
|
+
f"'{type(self).__name__}.{name}' was accessed but is set to -1. Please check the zebra mappings against the zebra's physical configuration"
|
|
18
|
+
)
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
@model_validator(mode="after")
|
|
22
|
+
def ensure_no_duplicate_connections(self):
|
|
23
|
+
"""Check that TTL outputs and sources are mapped to unique integers"""
|
|
24
|
+
|
|
25
|
+
integer_fields = {
|
|
26
|
+
key: value
|
|
27
|
+
for key, value in self.model_dump().items()
|
|
28
|
+
if isinstance(value, int) and value != -1
|
|
29
|
+
}
|
|
30
|
+
counted_vals = Counter(integer_fields.values())
|
|
31
|
+
integer_fields_with_duplicates = {
|
|
32
|
+
k: v for k, v in integer_fields.items() if counted_vals[v] > 1
|
|
33
|
+
}
|
|
34
|
+
if len(integer_fields_with_duplicates):
|
|
35
|
+
raise ValueError(
|
|
36
|
+
f"Each field in {type(self)} must be mapped to a unique integer. Duplicate fields: {integer_fields_with_duplicates}"
|
|
37
|
+
)
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ZebraTTLOutputs(ZebraMappingValidations):
|
|
42
|
+
"""Maps hardware to the Zebra TTL output (1-4) that they're physically wired to, or
|
|
43
|
+
None if that hardware is not connected. A value of -1 means this hardware is not connected."""
|
|
44
|
+
|
|
45
|
+
TTL_EIGER: int = Field(default=-1, ge=-1, le=4)
|
|
46
|
+
TTL_PILATUS: int = Field(default=-1, ge=-1, le=4)
|
|
47
|
+
TTL_FAST_SHUTTER: int = Field(default=-1, ge=-1, le=4)
|
|
48
|
+
TTL_DETECTOR: int = Field(default=-1, ge=-1, le=4)
|
|
49
|
+
TTL_SHUTTER: int = Field(default=-1, ge=-1, le=4)
|
|
50
|
+
TTL_XSPRESS3: int = Field(default=-1, ge=-1, le=4)
|
|
51
|
+
TTL_PANDA: int = Field(default=-1, ge=-1, le=4)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ZebraSources(ZebraMappingValidations):
|
|
55
|
+
"""Maps internal Zebra signal source to their integer PV value"""
|
|
56
|
+
|
|
57
|
+
DISCONNECT: int = Field(default=0, ge=0, le=63)
|
|
58
|
+
IN1_TTL: int = Field(default=1, ge=0, le=63)
|
|
59
|
+
IN2_TTL: int = Field(default=63, ge=0, le=63)
|
|
60
|
+
IN3_TTL: int = Field(default=7, ge=0, le=63)
|
|
61
|
+
IN4_TTL: int = Field(default=10, ge=0, le=63)
|
|
62
|
+
PC_ARM: int = Field(default=29, ge=0, le=63)
|
|
63
|
+
PC_GATE: int = Field(default=30, ge=0, le=63)
|
|
64
|
+
PC_PULSE: int = Field(default=31, ge=0, le=63)
|
|
65
|
+
AND3: int = Field(default=34, ge=0, le=63)
|
|
66
|
+
AND4: int = Field(default=35, ge=0, le=63)
|
|
67
|
+
OR1: int = Field(default=36, ge=0, le=63)
|
|
68
|
+
PULSE1: int = Field(default=52, ge=0, le=63)
|
|
69
|
+
PULSE2: int = Field(default=53, ge=0, le=63)
|
|
70
|
+
SOFT_IN1: int = Field(default=60, ge=0, le=63)
|
|
71
|
+
SOFT_IN2: int = Field(default=61, ge=0, le=63)
|
|
72
|
+
SOFT_IN3: int = Field(default=62, ge=0, le=63)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ZebraMapping(ZebraMappingValidations):
|
|
76
|
+
"""Mappings to locate a Zebra device's Ophyd signals based on a specific
|
|
77
|
+
Zebra's hardware configuration and wiring.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
# Zebra ophyd signal for connection can be accessed
|
|
81
|
+
# with, eg, zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR]
|
|
82
|
+
outputs: ZebraTTLOutputs = ZebraTTLOutputs()
|
|
83
|
+
|
|
84
|
+
# Zebra ophyd signal sources can be mapped to a zebra output by doing, eg,
|
|
85
|
+
# bps.abs_set(zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR],
|
|
86
|
+
# zebra.mapping.sources.AND3)
|
|
87
|
+
sources: ZebraSources = ZebraSources()
|
|
88
|
+
|
|
89
|
+
# Which of the Zebra's four AND gates is used to control the automatic shutter, if it's being used.
|
|
90
|
+
# After defining, the correct GateControl device can be accessed with, eg,
|
|
91
|
+
# zebra.logic_gates.and_gates[zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER]. Set to -1 if not being used.
|
|
92
|
+
AND_GATE_FOR_AUTO_SHUTTER: int = Field(default=-1, ge=-1, le=4)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class UnmappedZebraException(Exception):
|
|
96
|
+
pass
|
|
@@ -55,7 +55,8 @@ class ZocaloTrigger:
|
|
|
55
55
|
intended to be used in bluesky callback classes. To get results from zocalo back
|
|
56
56
|
into a plan, use the ZocaloResults ophyd device.
|
|
57
57
|
|
|
58
|
-
see https://github.
|
|
58
|
+
see https://diamondlightsource.github.io/dodal/main/how-to/zocalo.html for
|
|
59
|
+
more information about zocalo."""
|
|
59
60
|
|
|
60
61
|
def __init__(self, environment: str = ZOCALO_ENV):
|
|
61
62
|
self.zocalo_environment: str = environment
|
|
@@ -11,7 +11,7 @@ import workflows.recipe
|
|
|
11
11
|
import workflows.transport
|
|
12
12
|
from bluesky.protocols import Triggerable
|
|
13
13
|
from bluesky.utils import Msg
|
|
14
|
-
from deepdiff import DeepDiff
|
|
14
|
+
from deepdiff.diff import DeepDiff
|
|
15
15
|
from ophyd_async.core import (
|
|
16
16
|
Array1D,
|
|
17
17
|
AsyncStatus,
|
|
@@ -53,6 +53,26 @@ ZOCALO_STAGE_GROUP = "clear zocalo queue"
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
class XrcResult(TypedDict):
|
|
56
|
+
"""
|
|
57
|
+
Information about a diffracting centre.
|
|
58
|
+
|
|
59
|
+
NOTE: the coordinate systems of centre_of_mass and max_voxel/bounding_box are not
|
|
60
|
+
the same; centre_of_mass coordinates are continuous whereas max_voxel and bounding_box
|
|
61
|
+
coordinates are discrete.
|
|
62
|
+
Attributes:
|
|
63
|
+
centre_of_mass: The position of the centre of mass of the crystal, adjusted so that
|
|
64
|
+
grid box centres lie on integer grid coordinates, such that a 1x1x1 crystal detected in
|
|
65
|
+
a single grid box at 0, 0, 0, has c.o.m. of 0, 0, 0, not 0.5, 0.5, 0.5
|
|
66
|
+
max_voxel: Position of the voxel with the maximum count, in integer coordinates
|
|
67
|
+
max_count: max count achieved in a single voxel for the crystal
|
|
68
|
+
n_voxels: Number of voxels (aka grid boxes) in the diffracting centre
|
|
69
|
+
total_count: Total of above-threshold spot counts in the labelled voxels
|
|
70
|
+
bounding_box: The rectangular prism that bounds the crystal, expressed
|
|
71
|
+
as the volume of whole boxes as a half-open range i.e such that
|
|
72
|
+
p1 = (x1, y1, z1) <= p < p2 = (x2, y2, z2) and
|
|
73
|
+
p2 - p1 gives the dimensions in whole voxels.
|
|
74
|
+
"""
|
|
75
|
+
|
|
56
76
|
centre_of_mass: list[float]
|
|
57
77
|
max_voxel: list[int]
|
|
58
78
|
max_count: int
|
|
@@ -133,7 +153,7 @@ class ZocaloResults(StandardReadable, Triggerable):
|
|
|
133
153
|
self.use_cpu_and_gpu = use_cpu_and_gpu
|
|
134
154
|
|
|
135
155
|
self.centre_of_mass, self._com_setter = soft_signal_r_and_setter(
|
|
136
|
-
Array1D[np.
|
|
156
|
+
Array1D[np.float64], name="centre_of_mass"
|
|
137
157
|
)
|
|
138
158
|
self.bounding_box, self._bounding_box_setter = soft_signal_r_and_setter(
|
|
139
159
|
Array1D[np.uint64], name="bounding_box"
|
dodal/log.py
CHANGED
|
@@ -152,7 +152,7 @@ def set_up_graylog_handler(logger: Logger, host: str, port: int):
|
|
|
152
152
|
def set_up_INFO_file_handler(logger, path: Path, filename: str):
|
|
153
153
|
"""Set up a file handler for the logger, at INFO level, which will keep 30 days
|
|
154
154
|
of logs, rotating once per day. Creates the directory if necessary."""
|
|
155
|
-
print(f"Logging to INFO file handler {path/filename}")
|
|
155
|
+
print(f"Logging to INFO file handler {path / filename}")
|
|
156
156
|
path.mkdir(parents=True, exist_ok=True)
|
|
157
157
|
file_handler = TimedRotatingFileHandler(
|
|
158
158
|
filename=path / filename, when="MIDNIGHT", backupCount=INFO_LOG_DAYS
|
|
@@ -169,7 +169,7 @@ def set_up_DEBUG_memory_handler(
|
|
|
169
169
|
log file when it sees a message of severity ERROR. Creates the directory if
|
|
170
170
|
necessary"""
|
|
171
171
|
debug_path = path / "debug"
|
|
172
|
-
print(f"Logging to DEBUG handler {debug_path/filename}")
|
|
172
|
+
print(f"Logging to DEBUG handler {debug_path / filename}")
|
|
173
173
|
debug_path.mkdir(parents=True, exist_ok=True)
|
|
174
174
|
file_handler = TimedRotatingFileHandler(
|
|
175
175
|
filename=debug_path / filename, when="H", backupCount=DEBUG_LOG_FILES_TO_KEEP
|
dodal/plans/wrapped.py
CHANGED
|
@@ -49,9 +49,9 @@ def count(
|
|
|
49
49
|
Wraps bluesky.plans.count(det, num, delay, md=metadata) exposing only serializable
|
|
50
50
|
parameters and metadata."""
|
|
51
51
|
if isinstance(delay, Sequence):
|
|
52
|
-
assert (
|
|
53
|
-
|
|
54
|
-
)
|
|
52
|
+
assert len(delay) == num - 1, (
|
|
53
|
+
f"Number of delays given must be {num - 1}: was given {len(delay)}"
|
|
54
|
+
)
|
|
55
55
|
metadata = metadata or {}
|
|
56
56
|
metadata["shape"] = (num,)
|
|
57
57
|
yield from bp.count(tuple(detectors), num, delay=delay, md=metadata)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|