def kickoff(self): armed_status = DeviceStatus(self) self._disarmed_status = disarmed_status = DeviceStatus(self) pc = self.pos_capt external = bool(pc.arm.source.get()) # Using external trigger? if external: armed_signal = pc.arm.input_status else: armed_signal = pc.arm.output disarmed_signal = self.download_status self._collection_ts = time.time() def armed_status_cb(value, old_value, obj, **kwargs): if int(old_value) == 0 and int(value) == 1: armed_status._finished() obj.clear_sub(armed_status_cb) def disarmed_status_cb(value, old_value, obj, **kwargs): # I'm getting a stale 1 -> 0 update, so use timestamps to filter that out if int(old_value) == 1 and int(value) == 0: disarmed_status._finished() obj.clear_sub(disarmed_status_cb) armed_signal.subscribe(armed_status_cb, run=False) disarmed_signal.subscribe(disarmed_status_cb, run=False) # Arm it if not External if not external: self.pos_capt.arm.arm.put(1) return armed_status
def set(self, val): if self._set_st is not None: raise RuntimeError(f'trying to set {self.name}' ' while a set is in progress') cmd_map = {self.open_str: self.open_cmd, self.close_str: self.close_cmd} target_map = {self.open_str: self.open_val, self.close_str: self.close_val} cmd_sig = cmd_map[val] target_val = target_map[val] st = DeviceStatus(self) if self.status.get() == target_val: st._finished() return st self._set_st = st print(self.name, val, id(st)) enums = self.status.enum_strs def shutter_cb(value, timestamp, **kwargs): value = enums[int(value)] if value == target_val: self._set_st = None self.status.clear_sub(shutter_cb) st._finished() cmd_enums = cmd_sig.enum_strs count = 0 def cmd_retry_cb(value, timestamp, **kwargs): nonlocal count value = cmd_enums[int(value)] count += 1 if count > self.MAX_ATTEMPTS: cmd_sig.clear_sub(cmd_retry_cb) self._set_st = None self.status.clear_sub(shutter_cb) st._finished(success=False) if value == 'None': if not st.done: time.sleep(self.RETRY_PERIOD) cmd_sig.set(1) ts = datetime.datetime.fromtimestamp(timestamp) \ .strftime(_time_fmtstr) if count > 2: msg = '** ({}) Had to reactuate shutter while {}ing' print(msg.format(ts, val if val != 'Close' else val[:-1])) else: cmd_sig.clear_sub(cmd_retry_cb) cmd_sig.subscribe(cmd_retry_cb, run=False) self.status.subscribe(shutter_cb) cmd_sig.set(1) return st
def trigger(self): if self._staged != Staged.yes: raise RuntimeError( "This device must be staged before being triggered") st = DeviceStatus(self) self._compute() st.set_finished() return st
def kickoff(self): """ Set 'ready' to True and return a status object tied to 'injected'. """ self.ready.set(1) self.hplc_status = HPLCStatus.waiting_injected self._injected_status = DeviceStatus(self.injected) self._done_status = DeviceStatus(self.done) return self._injected_status
def trigger(self): "Trigger one acquisition." if self._staged != Staged.yes: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") self._status = DeviceStatus(self) self._acquisition_signal.put(1, callback=self._acquisition_done) return self._status
def trigger(self): if self._staged != Staged.yes: raise RuntimeError( "This device must be staged before being triggered") st = DeviceStatus(self) gap = self.gap.get() datum = self._datum_factory(datum_kwargs={"gap": gap}) self._asset_docs_cache.append(("datum", datum)) self.image.put(datum["datum_id"]) st.set_finished() return st
class SaturnSoftTrigger(BlueskyInterface): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._status = None self._acquisition_signal = self.mca.erase_start self.stage_sigs[self.mca.stop_signal] = 1 self.stage_sigs[self.dxp.preset_mode] = 'Real time' #self.stage_sigs[self.dxp.preset_mode] = 'Live time' self._count_signal = self.mca.preset_real_time self._count_time = None def stage(self): if self._count_time is not None: self.stage_sigs[self._count_signal] = self._count_time super().stage() def unstage(self): try: super().unstage() finally: if self._count_signal in self.stage_sigs: del self.stage_sigs[self._count_signal] self._count_time = None def trigger(self): "Trigger one acquisition." if self._staged != Staged.yes: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") self._status = DeviceStatus(self) self._acquisition_signal.put(1, callback=self._acquisition_done) return self._status def _acquisition_done(self, **kwargs): '''pyepics callback for when put completion finishes''' if self._status is not None: self._status._finished() self._status = None @property def count_time(self): '''Exposure time, as set by bluesky''' return self._count_time @count_time.setter def count_time(self, count_time): self._count_time = count_time
class SaturnSoftTrigger(BlueskyInterface): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._status = None self._acquisition_signal = self.mca.erase_start self.stage_sigs[self.mca.stop_signal] = 1 self.stage_sigs[self.dxp.preset_mode] = 'Real time' self._count_signal = self.mca.preset_real_time self._count_time = None def stage(self): if self._count_time is not None: self.stage_sigs[self._count_signal] = self._count_time super().stage() def unstage(self): try: super().unstage() finally: if self._count_signal in self.stage_sigs: del self.stage_sigs[self._count_signal] self._count_time = None def trigger(self): "Trigger one acquisition." if self._staged != Staged.yes: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") self._status = DeviceStatus(self) self._acquisition_signal.put(1, callback=self._acquisition_done) return self._status def _acquisition_done(self, **kwargs): '''pyepics callback for when put completion finishes''' if self._status is not None: self._status._finished() self._status = None @property def count_time(self): '''Exposure time, as set by bluesky''' return self._count_time @count_time.setter def count_time(self, count_time): self._count_time = count_time
def trigger(self): "Trigger one acquisition." if not self._staged: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") self._save_started = False self._status = DeviceStatus(self) self._desired_number_of_sets = self.number_of_sets.get() self._plugin.num_capture.put(self._desired_number_of_sets) self.dispatch(self._image_name, ttime.time()) # reset the proc buffer, this needs to be generalized self.proc.reset_filter.put(1) self._plugin.capture.put(1) # Now the TIFF plugin is capturing. return self._status
def trigger(self): status = DeviceStatus(self) if self.status: status._finished() else: self._statuses.append(status) if not status.done: logger.warning('---') logger.warning('Waiting for beam status to change...') logger.warning('---') return status
def set(self, value): # interface for BlueSky plans """value is either Taxi, Fly, or Return""" # allowed = "Taxi Fly Return".split() allowed = "Taxi Fly".split() if str(value).lower() not in list(map(str.lower, allowed)): msg = "value should be one of: " + " | ".join(allowed) msg + " received " + str(value) raise ValueError(msg) if self.busy.value: raise RuntimeError("shutter is operating") status = DeviceStatus(self) def action(): """the real action of ``set()`` is here""" if str(value).lower() == "taxi": self.taxi() elif str(value).lower() == "fly": self.fly() # elif str(value).lower() == "return": # self.motor.move(self.return_position) def run_and_wait(): """handle the ``action()`` in a thread""" self.busy.put(True) action() self.busy.put(False) status._finished(success=True) threading.Thread(target=run_and_wait, daemon=True).start() return status
def set(self, value): # interface for BlueSky plans """value is either Taxi or Fly""" if str(value).lower() not in ("fly", "taxi"): msg = "value should be either Taxi or Fly." msg + " received " + str(value) raise ValueError(msg) if self.busy.value: raise RuntimeError("shutter is operating") status = DeviceStatus(self) def action(): """the real action of ``set()`` is here""" if str(value).lower() == "taxi": self.taxi() elif str(value).lower() == "fly": self.fly() def run_and_wait(): """handle the ``action()`` in a thread""" self.busy.put(True) action() self.busy.put(False) status._finished(success=True) threading.Thread(target=run_and_delay, daemon=True).start() return status
def set(self, val): st = self._status if st is not None: st._finished(success=False) self._status = None st = self._status = DeviceStatus(self) self.setpoint.put(val) return st
def kickoff(self): """ run the trajectory """ if self.verified == False: raise Exception("trajectory not defined/verified.") #self._traj_status = DeviceStatus(self.ExecuteState) self._traj_status = DeviceStatus(self) ##self.exec_traj(self.traj_par['run_forward_traj']) # should not block th = threading.Thread(target=self.exec_traj, args=(self.traj_par['run_forward_traj'], )) th.start() # always done, the scan should never even try to wait for this #status = DeviceStatus(self) #status._finished() return self._traj_status
def set(self, value): # check that a set is not in progress, and if not set the lock. if not self._set_lock.acquire(blocking=False): raise SetInProgress('attempting to set {} '.format(self.name) + 'while a set is in progress') # define some required values set_value = value status = DeviceStatus(self) initial_timestamp = None # grab these values here to avoidmutliple calls. equilibrium_time = self.equilibrium_time.get() tolerance = self.tolerance.get() # setup a cleanup function for the timer, this matches including # timeout in `status` but also ensures that the callback is removed. def timer_cleanup(): print('Set of {} timed out after {} s'.format( self.name, self.timeout.get())) self._set_lock.release() self.readback.clear_sub(status_indicator) status._finished(success=False) self._cb_timer = threading.Timer(self.timeout.get(), timer_cleanup) # set up the done moving indicator logic def status_indicator(value, timestamp, **kwargs): # add a Timer to ensure that timeout occurs. if not self._cb_timer.is_alive(): self._cb_timer.start() nonlocal initial_timestamp if abs(value - set_value) < tolerance: if initial_timestamp: if (timestamp - initial_timestamp) > equilibrium_time: status._finished() self._cb_timer.cancel() self._set_lock.release() self.readback.clear_sub(status_indicator) else: initial_timestamp = timestamp else: initial_timestamp = None # Start the move. self.setpoint.put(set_value) # subscribe to the read value to indicate the set is done. self._cid = self.readback.subscribe(status_indicator) # hand the status object back to the RE return status
def set(self, val): if self._set_st is not None: raise RuntimeError('trying to set while a set is in progress') cmd_map = { self.open_str: self.open_cmd, self.close_str: self.close_cmd } target_map = { self.open_str: self.open_val, self.close_str: self.close_val } cmd_sig = cmd_map[val] target_val = target_map[val] st = self._set_st = DeviceStatus(self) enums = self.status.enum_strs def shutter_cb(value, timestamp, **kwargs): value = enums[int(value)] if value == target_val: self._set_st._finished() self._set_st = None self.status.clear_sub(shutter_cb) cmd_enums = cmd_sig.enum_strs count = 0 def cmd_retry_cb(value, timestamp, **kwargs): nonlocal count value = cmd_enums[int(value)] # ts = datetime.datetime.fromtimestamp(timestamp).strftime(_time_fmtstr) # print('sh', ts, val, st) count += 1 if count > 5: cmd_sig.clear_sub(cmd_retry_cb) st._finished(success=False) if value == 'None': if not st.done: yield from bps.sleep(.5) cmd_sig.set(1) ts = datetime.datetime.fromtimestamp(timestamp).strftime( _time_fmtstr) print( '** ({}) Had to reactuate shutter while {}ing'.format( ts, val)) else: cmd_sig.clear_sub(cmd_retry_cb) cmd_sig.subscribe(cmd_retry_cb, run=False) cmd_sig.set(1) self.status.subscribe(shutter_cb) return st
def set(self, value, *, wait=False, **kwargs): if value not in ('Open', 'Close'): raise ValueError( "must be 'Open' or 'Close', not {!r}".format(value)) if wait: raise RuntimeError() if self._st is not None: raise RuntimeError() self._target = value self._st = st = DeviceStatus(self, timeout=None) self.cmd.put(value) return st
def kickoff(self): self._running_status = running_status = DeviceStatus(self) holding_status = DeviceStatus(self) if self.hold.get(): def state_cb(value, old_value, **kwargs): if old_value != 2 and value == 2: holding_status._finished() self.state.clear_sub(state_cb) self.state.subscribe(state_cb, run=False) else: holding_status._finished() self.go.put(1, wait=True) def running_cb(value, old_value, obj, **kwargs): if old_value == 1 and value == 0: obj.clear_sub(running_cb) running_status._finished() self.running.subscribe(running_cb, run=False) return holding_status
def trigger(self): "Trigger one acquisition." if self._staged != Staged.yes: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") self._status = DeviceStatus(self) # TODO Start the Aerotech controller and hook a callback or a Thread # to call self._status._finished() when it is done. # This will cause a Datum to be created. self.dispatch(self._image_name, time.time()) super().trigger() return self._status
def trigger(self): self.sts = sts = DeviceStatus(self) # in the not step case, just return a done status object if self.mode_settings.scan_type.get() != 'step': sts._finished() return sts self._spec_saved.clear() def monitor(): success = self._spec_saved.wait(60) sts._finished(success=success) # hold a ref for gc reasons self._th = threading.Thread(target=monitor) self._th.start() return sts
def set(self, value): desired_value = value status = DeviceStatus(self) def are_we_there_yet(value, *args, **kwargs): if abs(value[-1] - desired_value) < self.tolerance: # This alerts the RunEngine that it can move on. status._finished() # Start us moving. self.setpoint.put(desired_value) # The function are_we_there_yet will receive # updates from pyepics as the readback changes. self.readback.subscribe(are_we_there_yet) # Hand this back the RunEngine immediately. return status
def set(self, value, **kwargs): """interface to use bps.mv()""" if value != 1: return working_status = DeviceStatus(self) started = False def exsc_cb(value, timestamp, **kwargs): value = int(value) if started and value == 0: working_status._finished() self.exsc.subscribe(exsc_cb) self.exsc.set(1) started = True return working_status
def complete(self): #print('complete():', self.name) # Callback will not work with simulation run control # self.runcontrol_stop.put(1) self.runcontrol_stop.put(1, wait=True) status = DeviceStatus(self) enums = self.runcontrol_stateenum.enum_strs def inner_cb(value, old_value, **kwargs): old_value, value = enums[int(old_value)], enums[int(value)] #print('complete():', kwargs['timestamp'], old_value, value, value == 'Idle') if value == "Idle": status._finished(success=True) self.runcontrol_stateenum.clear_sub(inner_cb) self.runcontrol_stateenum.subscribe(inner_cb) return status
def trigger(self): if self._staged != Staged.yes: raise RuntimeError("not staged") self._status = DeviceStatus(self) self.settings.erase.put(1) self._acquisition_signal.put(1, wait=False) trigger_time = ttime.time() if self._mode is SRXMode.step: for sn in self.read_attrs: if sn.startswith("channel") and "." not in sn: ch = getattr(self, sn) self.dispatch(ch.name, trigger_time) elif self._mode is SRXMode.fly: self.dispatch(self._f_key, trigger_time) else: raise Exception(f"unexpected mode {self._mode}") self._abs_trigger_count += 1 return self._status
def complete(self): print("complete()") expected_triggers = self.expected_triggers.get() start = self.start.get() end = self.end.get() positive = end > start status = DeviceStatus(self) print(f'start={start:.3}, end={end:.3}, positive={positive}') def check_triggers(): if self.actual_triggers.get() != expected_triggers: status._finished(success=False) def callback(value, old_value, **kwargs): # status._finished(success=False) if (positive and value > end) or ((not positive) and value < end): print('Motion profile done. Waiting, then checking triggers.') time.sleep(5) # grace period for triggers to finish success = self.actual_triggers.get() == expected_triggers if success: print( "Fly scan completion was never confirmed, but we have enough trigger. Finishing successfully..." ) status._finished(success=True) else: print( "Not enough triggers received. Stopping with failed status." ) status._finished(success=False) self.motor_rbv.clear_sub(callback) # When we get put-completion from the SSEQ record, # validate that we have the correct number of triggers immediately. # If we do not, finish with failure. fly_status = self.fly.set(self.fly.enum_strs[1]) fly_status.add_callback(check_triggers) # Watch the motor readback, which is expected to provide readings past it # setpoint / destination. Once it reaches its destination, wait for some # interval and then fail if self.motor_rbv.subscribe(callback) return status
def set(self, new_position, *, timeout=None): # to be subscribed to 'value' cb on readback sts = self._done_sts = DeviceStatus(self, timeout=timeout) if self.setpoint.get() == new_position: self._done_sts._finished() self._done_sts = None return sts self._setpoint = new_position self.setpoint.set(self._setpoint) # todo, set up subscription forwarding if self._target_channel == 'setpoint': self.setpoint.subscribe(local_cb, 'value') else: target = getattr(self, self._target_channel).T target.subscribe(self._value_cb, 'value') return self._done_sts
def kickoff(self): #print('kickoff():', self.name) status = DeviceStatus(self) enums = self.runcontrol_stateenum.enum_strs def inner_cb(value, old_value, **kwargs): old_value, value = enums[int(old_value)], enums[int(value)] #print('kickoff():', old_value, value, time.time()) if value == "Run": status._finished(success=True) self.runcontrol_stateenum.clear_sub(inner_cb) self.runcontrol_stateenum.subscribe(inner_cb) # Callback will not work with simulation run control #self.runcontrol_start.put(1) self.runcontrol_start.put(1, wait=True) return status
def set(self, value): # check that a set is not in progress, and if not set the lock. if self._set_lock: raise Exception('attempting to set {}'.format(self.name) + 'while a set is in progress'.format(self.name)) self._set_lock = True # define some required values set_value = value status = DeviceStatus(self, timeout=self.timeout.get()) initial_timestamp = None # grab these values here to avoidmutliple calls. equilibrium_time = self.equilibrium_time.get() tolerance = self.tolerance.get() # set up the done moving indicator logic def status_indicator(value, timestamp, **kwargs): nonlocal initial_timestamp if abs(value - set_value) < tolerance: if initial_timestamp: if (timestamp - initial_timestamp) > equilibrium_time: status._finished() self._set_lock = False self.readback.clear_sub(status_indicator) else: initial_timestamp = timestamp else: initial_timestamp = None # Start the move. self.setpoint.put(set_value) # subscribe to the read value to indicate the set is done. self.readback.subscribe(status_indicator) # hand the status object back to the RE return status
def wait_until_settled(self, timeout=None, timeout_fail=False): """ plan: wait for controller signal to reach target within tolerance """ # see: https://stackoverflow.com/questions/2829329/catch-a-threads-exception-in-the-caller-thread-in-python t0 = time.time() _st = DeviceStatus(self.signal) if self.settled: # just in case signal already at target _st._finished(success=True) else: started = False def changing_cb(*args, **kwargs): if started and self.settled: _st._finished(success=True) token = self.signal.subscribe(changing_cb) started = True report = 0 while not _st.done and not self.settled: elapsed = time.time() - t0 if timeout is not None and elapsed > timeout: _st._finished(success=self.settled) msg = f"{self.controller_name} Timeout after {elapsed:.2f}s" msg += f", target {self.target.get():.2f}{self.units.get()}" msg += f", now {self.signal.get():.2f}{self.units.get()}" print(msg) if timeout_fail: raise TimeoutError(msg) continue if elapsed >= report: report += self.report_interval_s msg = f"Waiting {elapsed:.1f}s" msg += f" to reach {self.target.get():.2f}{self.units.get()}" msg += f", now {self.signal.get():.2f}{self.units.get()}" print(msg) yield from bps.sleep(self.poll_s) self.signal.unsubscribe(token) self.record_signal() elapsed = time.time() - t0 print(f"Total time: {elapsed:.3f}s, settled:{_st.success}")
def set(self, value, **kwargs): """set the shutter to "close" or "open" """ # first, validate the input value acceptables = (self.close_str, self.open_str) if value not in acceptables: msg = "value should be one of " + " | ".join(acceptables) msg += " : received " + str(value) raise ValueError(msg) command_signal = { self.open_str: self.open_bit, self.close_str: self.close_bit }[value] expected_value = { self.open_str: self.open_val, self.close_str: self.close_val }[value] working_status = DeviceStatus(self) simulate_delay = self.pss_state.value != expected_value def shutter_cb(value, timestamp, **kwargs): self.pss_state.clear_sub(shutter_cb) if simulate_delay: time.sleep(self.response_time) self.pss_state.set(expected_value) working_status._finished() self.pss_state.subscribe(shutter_cb) command_signal.put(1) # finally, make sure both signals are reset self.open_bit.put(0) self.close_bit.put(0) return working_status
def trigger(self): status = DeviceStatus(self) i = next(self._counter) thread = threading.Thread(target=self._capture, args=(status, i)) thread.start() return status
def trigger(self): # Re-implement this to trigger as desired in bluesky status = DeviceStatus(self) status._finished() return status