def test_configure_with_breakpoints(self): xs = LineGenerator("x", "mm", 0.0, 0.5, 3, alternate=True) ys = LineGenerator("y", "mm", 0.0, 0.1, 2) generator = CompoundGenerator([ys, xs], [], [], 0.1) generator.prepare() completed_steps = 0 steps_to_do = 3 info = ExposureDeadtimeInfo(0.01, 1000, 0.0) part_info = dict(anyname=[info]) self.set_attributes(self.child, triggerMode="Internal") self.o.on_configure( self.context, completed_steps, steps_to_do, part_info, generator, fileDir="/tmp", breakpoints=[3, 3], exposure=info.calculate_exposure(generator.duration), ) assert self.child.handled_requests.mock_calls == [ call.put("arrayCallbacks", True), call.put("arrayCounter", 0), call.put("exposure", 0.1 - 0.01 - 0.0001), call.put("imageMode", "Multiple"), call.put("numImages", 3), call.put("acquirePeriod", 0.1 - 0.0001), ] assert not self.o.is_hardware_triggered
def setUp(self): super().setUp() # In this case we only move x and expect energy to be moved in between runs outer = LineGenerator("energy", "keV", 10.0, 11.0, 5) self.steps_to_do = 10 inner = LineGenerator("x", "mm", 0.0, 0.5, self.steps_to_do) self.generator = CompoundGenerator([outer, inner], [], [], 0.1) self.generator.prepare() self.completed_steps = 0 self.info = ExposureDeadtimeInfo(0.01, 1000, 0.0) self.part_info = dict(anyname=[self.info])
def test_post_run_armed_with_hardware_trigger_and_breakpoints(self): xs = LineGenerator("x", "mm", 0.0, 0.5, 100, alternate=True) ys = LineGenerator("y", "mm", 0.0, 0.1, 5) generator = CompoundGenerator([ys, xs], [], [], 0.1) generator.prepare() info = ExposureDeadtimeInfo(0.01, 1000, 0.0) part_info = dict(anyname=[info]) breakpoints = [100, 400] # This would have been done by initial configure self.o.is_hardware_triggered = True self.o.done_when_reaches = 100 self.o.on_post_run_armed(self.context, 100, 400, part_info, generator, breakpoints) assert self.child.handled_requests.mock_calls == [ call.put("arrayCallbacks", True), call.put("arrayCounter", 100), call.put("imageMode", "Multiple"), call.put("numImages", 400), call.put("acquirePeriod", 0.1 - 0.0001), call.post("start"), call.when_value_matches("acquiring", True, None), ] assert self.o.done_when_reaches == 500
def test_seek_with_hardware_trigger_and_breakpoints(self): self.o.is_hardware_triggered = True # Calling seek after 10 completed steps with 10 left until a breakpoint self.o.done_when_reaches = 10 # Build our seek parameters xs = LineGenerator("x", "mm", 0.0, 0.5, 20, alternate=True) ys = LineGenerator("y", "mm", 0.0, 0.1, 3) generator = CompoundGenerator([ys, xs], [], [], 0.1) generator.prepare() completed_steps = 10 steps_to_do = 10 info = ExposureDeadtimeInfo(0.01, 1000, 0.0) part_info = dict(anyname=[info]) breakpoints = [20, 20, 20] self.o.on_seek( self.context, completed_steps, steps_to_do, part_info, generator, fileDir="/tmp", breakpoints=breakpoints, exposure=info.calculate_exposure(generator.duration), ) # Check we got the right calls to setup the driver assert self.child.handled_requests.mock_calls == [ call.put("arrayCallbacks", True), call.put("arrayCounter", 10), call.put("exposure", 0.1 - 0.01 - 0.0001), call.put("imageMode", "Multiple"), call.put("numImages", 10), call.put("acquirePeriod", 0.1 - 0.0001), call.post("start"), call.when_value_matches("acquiring", True, None), ] assert self.o.done_when_reaches == 20 assert len(self.context._subscriptions) == 0
class TestDetectorDriverPartNestedConfigure(TestDetectorDriverPart): def setUp(self): super().setUp() # In this case we only move x and expect energy to be moved in between runs outer = LineGenerator("energy", "keV", 10.0, 11.0, 5) self.steps_to_do = 10 inner = LineGenerator("x", "mm", 0.0, 0.5, self.steps_to_do) self.generator = CompoundGenerator([outer, inner], [], [], 0.1) self.generator.prepare() self.completed_steps = 0 self.info = ExposureDeadtimeInfo(0.01, 1000, 0.0) self.part_info = dict(anyname=[self.info]) def configure(self): self.o.on_configure( self.context, self.completed_steps, self.steps_to_do, self.part_info, self.generator, fileDir="/tmp", exposure=self.info.calculate_exposure(self.generator.duration), ) def test_nested_configure(self): self.set_attributes(self.child, triggerMode="Internal") self.configure() # When not hardware triggered we set numImages for the inner scan only assert self.child.handled_requests.mock_calls == [ call.put("arrayCallbacks", True), call.put("arrayCounter", 0), call.put("exposure", 0.1 - 0.01 - 0.0001), call.put("imageMode", "Multiple"), call.put("numImages", 10), call.put("acquirePeriod", 0.1 - 0.0001), ] def test_nested_hardware_triggered_configure(self): self.set_attributes(self.child, triggerMode="External") self.configure() # When hardware triggered we set numImages to the total frames and call start assert self.child.handled_requests.mock_calls == [ call.put("arrayCallbacks", True), call.put("arrayCounter", 0), call.put("exposure", 0.1 - 0.01 - 0.0001), call.put("imageMode", "Multiple"), call.put("numImages", 50), call.put("acquirePeriod", 0.1 - 0.0001), call.post("start"), call.when_value_matches("acquiring", True, None), ]
def test_post_run_armed_with_hardware_trigger(self): xs = LineGenerator("x", "mm", 0.0, 0.5, 100, alternate=True) ys = LineGenerator("y", "mm", 0.0, 0.1, 5) generator = CompoundGenerator([ys, xs], [], [], 0.1) generator.prepare() info = ExposureDeadtimeInfo(0.01, 1000, 0.0) part_info = dict(anyname=[info]) # This would have been done by initial configure self.o.is_hardware_triggered = True self.o.done_when_reaches = 100 self.o.on_post_run_armed(self.context, 100, 100, part_info, generator) assert self.o.done_when_reaches == 200
def setup_detector( self, context: Context, completed_steps: scanning.hooks.ACompletedSteps, steps_to_do: scanning.hooks.AStepsToDo, num_images: int, duration: float, part_info: scanning.hooks.APartInfo, initial_configure: bool = True, **kwargs: Any, ) -> None: child = context.block_view(self.mri) if initial_configure: # This is an initial configure, so reset arrayCounter to 0 array_counter = 0 self.done_when_reaches = steps_to_do else: # This is rewinding or setting up for another batch, # skip to a uniqueID that has not been produced yet array_counter = self.done_when_reaches self.done_when_reaches += steps_to_do self.uniqueid_offset = completed_steps - array_counter for k, v in dict( arrayCounter=array_counter, imageMode=self.multiple_image_mode, numImages=num_images, arrayCallbacks=True, ).items(): if k not in kwargs and k in child: kwargs[k] = v child.put_attribute_values(kwargs) # Might need to reset acquirePeriod as it's sometimes wrong # in some detectors try: info: ExposureDeadtimeInfo = ExposureDeadtimeInfo.filter_single_value( part_info) except BadValueError: # This is ok, no exposure info pass else: exposure = kwargs.get("exposure", info.calculate_exposure(duration)) child.acquirePeriod.put_value(exposure + info.readout_time)