def trigger(self): """ Trigger the EventSequencer This method reconfigures the EventSequencer to take a new reading. This means: * Stopping the EventSequencer if it is already running * Restarting the EventSequencer The returned status object will indicate different behavior based on the configuration of the EventSequencer itself. If set to "Run Forever", the status object merely indicates that we have succesfully started our sequence. Otherwise, the status object will be completed when the sequence we have set it to play is complete. """ # Stop the Sequencer if it is already running self.stop() # Fire the EventSequencer self.start() # If we are running forever, count this is as triggered if self.play_mode.get() == 2: logger.debug("EventSequencer is set to run forever, " "trigger is complete") return DeviceStatus(self, done=True, success=True) # Create our status def done(*args, value=None, old_value=None, **kwargs): return value == 2 and old_value == 0 # Create our status object return SubscriptionStatus(self.play_status, done, run=True)
def _clear_flag(self, flag, wait=False, timeout=10): """Clear flag whose information is in :attr:`._bit_flags`""" # Gather our flag information flag_info = self._bit_flags[flag] bit = flag_info['readback'] mask = flag_info.get('mask', 1) # Create a callback function to check for bit def flag_is_cleared(value=None, **kwargs): return not bool((int(value) >> bit) & mask) # Check that we need to actually set the flag if flag_is_cleared(value=self.bit_status.get()): logger.debug("%s flag is not currently active", flag) st = DeviceStatus(self) st.set_finished() return st # Issue our command logger.info('Clearing %s flag ...', flag) self.seq_seln.put(flag_info['clear']) # Generate a status st = SubscriptionStatus(self.bit_status, flag_is_cleared) if wait: status_wait(st, timeout=timeout) return st
def reinitialize(self, wait=False): """ Reinitialize the IMS motor. Parameters ---------- wait : bool Wait for the motor to be fully intialized. Returns ------- :class:`~ophyd.status.SubscriptionStatus` Status object reporting the initialization state of the motor. """ logger.info('Reinitializing motor') # Issue command self.reinit_command.put(1) # Check error def initialize_complete(value=None, **kwargs): return value != 3 # Generate a status st = SubscriptionStatus(self.error_severity, initialize_complete, settle_time=0.5) # Wait on status if requested if wait: status_wait(st) return st
def test_subscription_status(): # Arbitrary device d = Device("Tst:Prefix", name='test') # Mock callback m = Mock() # Full fake callback signature def cb(*args, done=False, **kwargs): # Run mock callback m() # Return finished or not return done status = SubscriptionStatus(d, cb, event_type=d.SUB_ACQ_DONE) # Run callbacks but do not mark as complete d._run_subs(sub_type=d.SUB_ACQ_DONE, done=False) time.sleep(0.1) # Wait for callbacks to run. assert m.called assert not status.done and not status.success # Run callbacks and mark as complete d._run_subs(sub_type=d.SUB_ACQ_DONE, done=True) time.sleep(0.1) # Wait for callbacks to run. assert status.done and status.success
def set(self, value, *args, **kwargs): """Set the sequencer start PV to the inputted value.""" def cb(*args, **kwargs): time.sleep(self._cb_sleep) return self.play_status.get() == 0 self.play_control.put(value) return SubscriptionStatus(self.play_status, cb)
def trigger(self): super().trigger() print(f"I got the trigger!") acquire_status = SubscriptionStatus(self.cam.acquire, acquire_high_to_low) self.cam.acquire.set(PerkinElmerCamera.Acquire.ACQUIRE) return acquire_status
def set(self, command): if command == 'prepare': # This function will receive Events from the IOC and check whether # we are seeing the trajectory_ready go low after having been high. def callback(value, old_value, **kwargs): if int(round(old_value)) == 1 and int(round(value)) == 0: if self._preparing or self._preparing is None: self._preparing = False return True else: self._preparing = True return False # Creating this status object subscribes `callback` Events from the # IOC. Starting at this line, we are now listening for the IOC to # tell us it is done. When it does, this status object will # complete (status.done = True). status = SubscriptionStatus(self.trajectory_ready, callback) # Finally, now that we are litsening to the IOC, prepare the # trajectory. self.prepare_trajectory.set('1') # Yes, the IOC requires a string. # Return the status object immediately, without waiting. The caller # will be able to watch for it to become done. return status if command == 'start': def callback(value, old_value, **kwargs): if int(round(old_value)) == 1 and int(round(value)) == 0: if self._starting or self._starting is None: self._starting = False return True else: self._starting = True return False status = SubscriptionStatus(self.trajectory_running, callback) self.start_trajectory.set('1') return status
def _wait(self, sig, *goals): """ Helper function to wait for a signal to reach a value. This is used here because most commands are only valid when mode is IDLE. """ def cb(value, *args, **kwargs): logger.debug((value, goals)) return value in goals status = SubscriptionStatus(sig, cb) status_wait(status)
def complete(self): # Use whether X in the coordinate system is moving # as a proxy for whether the total flyscan is done. # This might be brittle but there is no straightforward way # to check for done-ness, and this is easy to put together. # Should probably be improved to account for y also. def is_done(value, **kwargs): return not value x_moving = SubscriptionStatus(scan_in_progress, is_done) return x_moving
def set(self, val): def check_if_done(value, old_value, **kwargs): if ((val in ['Open', 1] and value == 0) or (val in ['Close', 0] and value == 1)): if self.st is not None: self.st._finished() self.st = None return True return False self.cmd.set(self.setmap[val]) status = SubscriptionStatus(self.status, check_if_done) return status
def trigger(self, *args, **kwargs): "Trigger one acquisition." if self._staged != Staged.yes: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") def callback(value, old_value, **kwargs): if int(round(old_value)) == 1 and int(round(value)) == 0: return True return False status = SubscriptionStatus(self.mca.when_acq_stops, callback, run=False) self._acquisition_signal.set(1) return status
def kickoff(self): """ Start the EventSequencer Returns ------- status : SubscriptionStatus Status indicating whether or not the EventSequencer has started """ self.start() # Start monitor signals super().kickoff() # Create our status def done(*args, value=None, old_value=None, **kwargs): return value == 2 and old_value == 0 # Create our status object return SubscriptionStatus(self.play_status, done, run=True)
def complete(self): """ Complete the EventSequencer's current sequence. The result of this method varies on the mode that the EventSequencer is configured. If the EventSequencer is either set to 'Run Once' or 'Run N Times' this method allows the current sequence to complete and returns a status object that indicates a successful completion. However, this mode of operation does not make sense if the EventSequencer is in 'Run Forever' mode. In this case, the EventSequencer is stopped immediately and a completed status object is returned. Returns ------- status : DeviceStatus or SubscriptionStatus Status indicating completion of sequence. Notes ----- If you want to stop the sequence from running regardless of configuration use the :meth:`.stop` command. """ # Clear the monitor subscriptions super().complete() # If we are running forever we can stop whenever if self.play_mode.get() == 2: logger.debug("EventSequencer is set to run forever, " "stopping immediately") self.stop() return DeviceStatus(self, done=True, success=True) # Otherwise we should wait for the sequencer to end def done(*args, value=None, old_value=None, timestamp=0, **kwargs): return all((value == 0, old_value == 2, timestamp > self.play_control.timestamp)) # Create a SubscriptionStatus logger.debug("EventSequencer has a determined stopping point, " " waiting for sequence to complete") st = SubscriptionStatus(self.play_status, done, run=True) return st
def complete(self): def callback_det(value, old_value, **kwargs): print(f"complete: {ttime.time()} {old_value} ---> {value}") if int(round(old_value)) == 1 and int(round(value)) == 0: return True else: return False streaming_st = SubscriptionStatus(self.det.streaming, callback_det) def callback_motor(): # print(f'callback_motor {ttime.time()}') self.det.stream.set(0) self.det.complete() for pb in self.pbs: pb.complete() self._motor_status.add_callback(callback_motor) print(f"complete operation is happening ({ttime.ctime(ttime.time())})") return streaming_st & self._motor_status
def kickoff(self): """ Start the EventSequencer. Returns ------- status : ~ophyd.status.SubscriptionStatus Status indicating whether or not the EventSequencer has started. """ self.start() # Start monitor signals super().kickoff() # Create our status def done(*args, value=None, old_value=None, timestamp=0, **kwargs): return all((value == 2, old_value == 0, timestamp > self.play_control.timestamp)) # Create our status object return SubscriptionStatus(self.play_status, done, run=True)
def kickoff(self, *args, **kwargs): set_and_wait(self.det.trig_source, 1) # TODO: handle it on the plan level # set_and_wait(self.motor, 'prepare') def callback(value, old_value, **kwargs): print(f"kickoff: {ttime.ctime()} {old_value} ---> {value}") if int(round(old_value)) == 0 and int(round(value)) == 1: # Now start mono move self._motor_status = self.motor.set("start") return True else: return False streaming_st = SubscriptionStatus(self.det.streaming, callback) self.det.stream.set(1) self.det.stage() for pb in self.pbs: pb.stage() pb.kickoff() return streaming_st
def trigger(self): """ Set the acquire PV to 1 and wait for the acquiring PV to go from high (1) to low (0). Return ------ SubscriptionStatus a Status object that indicates when acquisition is finished """ def callback(value, old_value, **kwargs): # print(f'{ttime.time()} {old_value} ---> {value}') if self._capturing and int(round(old_value)) == 1 and int(round(value)) == 0: self._capturing = False return True else: self._capturing = True return False status = SubscriptionStatus(self.acquiring, callback) self.acquire.set(1) return status
def trigger(self, *args, **kwargs): "Trigger one acquisition." if self._staged != Staged.yes: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") def callback(value, old_value, **kwargs): print(f' {self.name} old value {old_value} --> new_value {value}') if int(round(old_value)) == 1 and int(round(value)) == 0: if self._starting or self._starting is None: self.starting = False return True else: self.starting = True return False status = SubscriptionStatus(self.mca.when_acq_stops, callback, run=False) print(f' !!! attempting to put to {self._acquisition_signal.pvname} value 1') # ttime.sleep(0.1) timeout = 10 # s self._acquisition_signal.put(1) print(f' !!! sleeping 0.1s after putting') t0 = ttime.time() print(f'Start waiting at {ttime.ctime(t0)}...') while True: ttime.sleep(0.01) if int(round(self.mca.when_acq_stops.get())) == 1: print(f'Success, {self.mca.when_acq_stops.pvname} was set to 1. Waited for {ttime.time() - t0}s.') break else: if ttime.time() - t0 > timeout: print(f'Waited for {timeout}s, but the signal {self.mca.when_acq_stops.pvname} did not change. Attempting again.') self._acquisition_signal.put(1) break return status
def kickoff(self): set_scanning.put(1) def is_started(value, **kwargs): return bool(value) ready_to_scan = SubscriptionStatus(scan_in_progress, is_started) self._traj_info.update({ 'nx': int(self.hxn_stage.nx.get()), 'ny': int(self.hxn_stage.ny.get()), 'x_start': self.hxn_stage.x_start.get(), 'x_stop': self.hxn_stage.x_stop.get(), 'y_start': self.hxn_stage.y_start.get(), 'y_stop': self.hxn_stage.y_stop.get(), }) self._array_size.update({ 'height': getattr(self.detector, self.plugin_type).array_size.height.get(), 'width': getattr(self.detector, self.plugin_type).array_size.width.get() }) return ready_to_scan & self.hxn_stage.start_scan.set(1)
def set(self, device, function): return SubscriptionStatus(device, function)
def pe_count( filename="", exposure=1, num_images: int = 1, num_dark_images: int = 1, num_repetitions: int = 5, delay=60, ): year = "2020" # RE.md["year"] cycle = "C2" # RE.md["cycle"] proposal = "67890" # RE.md["PROPOSAL"] # write_path_template = 'Z:\\data\\pe1_data\\%Y\\%m\\%d\\' # write_path_template = f"Z:\\users\\{year}\\{cycle}\\{proposal}XRD\\" # file_path = datetime.now().strftime(write_path_template) # filename = filename + str(uuid.uuid4())[:6] # this is an example of what would be used at the beamline pe_detector.tiff_writer.resource_root_path = PureWindowsPath( f"Z:\\users\\") pe_detector.tiff_writer.relative_write_path = PureWindowsPath( f"{year}\\{cycle}\\{proposal}XRD\\") # for testing pe_detector.tiff_writer.resource_root_path = Path("/tmp/") pe_detector.tiff_writer.relative_write_path = Path( f"perkin_elmer/detector/{year}/{cycle}/XRD{proposal}" # remove "XRD" from the end? ) # start the run yield from bps.open_run() # stage the detector yield from bps.stage(pe_detector) yield from bps.mv(pe_detector.tiff_writer.file_number, 1) tiff_full_file_path = (pe_detector.tiff_writer.resource_root_path / pe_detector.tiff_writer.relative_write_path) print(f"tiff_full_file_path: {str(tiff_full_file_path)}") yield from bps.mv(pe_detector.tiff_writer.file_path, str(tiff_full_file_path)) for repetition_index in range(int(num_repetitions)): print("\n") print( "<<<<<<<<<<<<<<<<< Doing repetition {} out of {} >>>>>>>>>>>>>>>>>" .format(repetition_index + 1, num_repetitions)) # TiffWriter or similar plugin should do this yield from bps.mv(pe_detector.tiff_writer.file_name, filename + str(uuid.uuid4())) if num_dark_images > 0: # originally used pe_detector.num_dark_images # but this is really pe_num_offset_frames yield from bps.mv(pe_detector.cam.pe_num_offset_frames, num_dark_images) yield from bps.mv( pe_detector.cam.image_mode, PerkinElmerCamera.PerkinElmerImageMode.AVERAGE, ) # yield from bps.mv(fast_shutter, "Close") yield from bps.sleep(0.5) yield from bps.mv(pe_detector.tiff_writer.file_write_mode, NDFile.FileWriteMode.SINGLE) # acquire a "dark frame" pe_acquire_offset_status = SubscriptionStatus( pe_detector.cam.pe_acquire_offset, high_to_low_pe_acquire_offset) yield from bps.abs_set( pe_detector.cam.pe_acquire_offset, PerkinElmerCamera.AcquireOffset.ACQUIRE, wait=False, ) yield Msg("wait_for_status", None, pe_acquire_offset_status) yield from bps.mv(pe_detector.tiff_writer.write_file, NDFile.WriteFile.WRITE) # yield from bps.mv( # pe1.cam.image_mode, # NewPerkinElmerDetector.ImageMode.MULTIPLE # ) yield from bps.mv( pe_detector.cam.image_mode, PerkinElmerCamera.PerkinElmerImageMode.AVERAGE, ) yield from bps.mv(pe_detector.cam.acquire_time, exposure) yield from bps.mv(pe_detector.cam.num_images, num_images) # yield from bps.mv(fast_shutter, "Open") yield from bps.sleep(0.5) ## Below 'Capture' mode is used with 'Multiple' image_mode # yield from bps.mv(pe1.tiff_writer.file_write_mode, 'Capture') ## Below 'Single' mode is used with 'Average' image_mode yield from bps.mv( pe_detector.tiff_writer.file_write_mode, NDFile.FileWriteMode.SINGLE, # "Single" ) ## Uncomment 'capture' bit settings when used in 'Capture' mode # yield from bps.mv(pe1.tiff_writer.capture, 1) # this was the old way to initiate the acquisition # yield from bps.mv(pe_detector, "acquire_light") yield from bps.trigger_and_read([pe_detector], name="primary") # can TiffWriter or similar plugin do this? ##Below write_file is needed when used in 'Average' mode yield from bps.mv( pe_detector.tiff_writer.write_file, NDFile.WriteFile.WRITE # 1 ) yield from bps.sleep(delay) # unstage the detector yield from bps.unstage(pe_detector) # end the run yield from bps.close_run()