def _detectors_changed(self, *args): logger.info("Detector list updated: %s" % self.detectors) del self._detectors[:] for alias in self.detectors: pv = util.expand_alias(alias) try: det = stepscan.get_detector(pv, label=alias) except Exception as ex: logger.error("Bad detector: %s (%s) %s" % (pv, ex.__class__.__name__, ex)) except KeyboardInterrupt: logger.warning("Skipping detector list entry: %s" % pv) else: self._detectors.append(det)
def _motor_list_changed(self, *args): shell = self.shell new_motors = [motor for motor in self.motor_list if motor not in shell.user_ns] for motor in new_motors: try: expanded = util.expand_alias(motor) motor_inst = epics.Motor(expanded) except Exception as ex: logger.error('Bad motor: %s (%s) %s' % (motor, ex.__class__.__name__, ex)) except KeyboardInterrupt: logger.warning('Skipping motor list entry: %s' % motor) else: self.motors[motor] = motor_inst if util.is_valid_python_identifier(motor): shell.user_ns[motor] = motor_inst
def _get_device(self, record): record = util.expand_alias(record) try: return self._scaler_devices[record] except KeyError: pass try: dev = epics.devices.Scaler(record) except Exception as ex: logger.error('Bad scaler "%s" (%s) %s' % (record, ex.__class__.__name__, ex)) except KeyboardInterrupt: logger.warning('Skipping scaler list entry "%s"' % (record, )) if self.core.script_running: raise else: self._scaler_devices[record] = dev return dev
def scan_run( self, positioners, dwell_time, move_back=True, command="", dimensions=None, breakpoints=[], counters=[], detectors=[], triggers=[], run=True, **kwargs ): """ Perform a generic scan :param positioners: Motors to scan, with absolute position arrays previously set :param dwell_time: Seconds at each point :param move_back: Move all positioners back to their starting position post scan :param command: the command-line command used to start the scan :param dimensions: the scan dimensions :param counters: additional counters not normally included (can be a PV name) :param detectors: additional detectors not normally included :param triggers: additional triggers not normally included (can be a PV name) :param run: run the scan (or just return one ready to run) :param kwargs: passed onto the scan's ECLI info :returns: the scan instance """ array_shapes = set([pos.array.shape for pos in positioners]) if len(array_shapes) != 1: raise ValueError("Positioners must have the same position array dimensions") data_points = np.size(positioners[0].array) self.scan = sc = stepscan.StepScan() if dimensions is None or not isinstance(dimensions, (list, tuple)): dimensions = (data_points,) for positioner in positioners: sc.add_positioner(positioner) for counter in counters: sc.add_counter(counter) for trigger in triggers: sc.add_trigger(trigger) motor_plugin = get_plugin("ECLIMotor") for motor in motor_plugin.motor_list: motor_rec = motor_plugin.get_motor(motor) sc.add_extra_pvs([(motor, motor_rec.PV("RBV"))]) for pv in self.extra_pvs: if pv in self.core.aliases: name, pv = pv, self.core.aliases[pv] else: name = self.core.get_aliased_name(pv) sc.add_extra_pvs([(name, pv)]) mca_calib = {} for det_pv in set(self.detectors + list(detectors) + list(self.trigger_detectors.keys())): type_ = self.detector_types.get(det_pv, None) det = stepscan.get_detector(util.expand_alias(det_pv), kind=type_, label=det_pv) if det is None: logger.error("Scan %s invalid detector: %s" % (sc, det_pv)) return None logger.debug("Scan %s added detector: %s" % (sc, det)) sc.add_detector(det) # TODO bug report - hardware triggered detectors if det.trigger is not None: sc.triggers.remove(det.trigger) if det_pv in self.trigger_detectors: trigger_value = self.trigger_detectors[det_pv] logger.debug("Added detector trigger: %s = %s" % (det.trigger, trigger_value)) sc.add_trigger(det.trigger, value=trigger_value) if isinstance(det, stepscan.McaDetector): det_pv = util.expand_alias(det_pv) calib_pvs = ["%s.CALO", "%s.CALS", "%s.CALQ"] prefix = det.prefix calib = [epics.caget(pv % prefix) for pv in calib_pvs] mca_calib[det_pv] = calib # TODO StepScan bug report: # add_trigger needs to check for None (as in SimpleDetector) sc.triggers = [trigger for trigger in sc.triggers if trigger is not None] sc.set_dwelltime(dwell_time) start_pos = [pos.current() for pos in positioners] for counter in sc.counters: counter.label = str(counter.label) self._scan_number += 1 sc.ecli_info = { "command": command, "scan_number": self._scan_number, "dimensions": dimensions, "ndim": calc_ndim(dimensions), "scanning": [str(pos.label) for pos in positioners], "mca_calib": mca_calib, } if hasattr(sc, "timestamps"): # Added timestamps in ECLI stepscan fork sc.ecli_info["timestamps"] = sc.timestamps sc.get_timestamp = lambda i: sc.timestamps[i] else: sc.get_timestamp = lambda i: None sc.ecli_info.update(kwargs) sc.pos_settle_time = self.pos_settle_time sc.det_settle_time = self.det_settle_time if not run: return sc ex_raised = None # TODO: check all PVs prior to scan # Run the scan try: sc.run(None) except Exception as ex: if sc.message_thread is not None: sc.message_thread.cpt = None logger.error("Scan failed: (%s) %s" % (ex.__class__.__name__, ex)) ex_raised = ex finally: # Wait for the message thread to catch up if sc.message_thread is not None: sc.message_thread.join(1.0) if move_back: # Move the positioners back to their starting positions for pos, start in zip(positioners, start_pos): logger.info("Moving %s back to the starting position %g" % (pos, start)) try: pos.move_to(start, wait=True) except KeyboardInterrupt as ex: print("%s move to %g cancelled (current position=%s)" % (pos.label, start, pos.current())) ex_raised = ex if self.core.script_running: raise ex # Make a simple dictionary holding the scan data data = {} for counter in sc.counters: data[counter.label] = counter.buff # And export that data back to the user namespace as `scan_data` self.core.set_variable("scan_data", data) return sc