class EigerBase(AreaDetector, HxnModalTrigger): """ Eiger, sans any triggering behavior. Use EigerSingleTrigger or EigerFastTrigger below. """ num_triggers = ADComponent(EpicsSignalWithRBV, 'cam1:NumTriggers') file = Cpt(EigerSimulatedFilePlugin, suffix='cam1:', # write_path_template='/XF11ID/data/%Y/%m/%d/', # write_path_template='/data/eiger_tests/', write_path_template='/data/eiger1m/%Y/%m/%d/', #root='/XF11ID/', root='/data') beam_center_x = ADComponent(EpicsSignalWithRBV, 'cam1:BeamX') beam_center_y = ADComponent(EpicsSignalWithRBV, 'cam1:BeamY') wavelength = ADComponent(EpicsSignalWithRBV, 'cam1:Wavelength') det_distance = ADComponent(EpicsSignalWithRBV, 'cam1:DetDist') threshold_energy = ADComponent(EpicsSignalWithRBV, 'cam1:ThresholdEnergy') photon_energy = ADComponent(EpicsSignalWithRBV, 'cam1:PhotonEnergy') manual_trigger = ADComponent(EpicsSignalWithRBV, 'cam1:ManualTrigger') # the checkbox special_trigger_button = ADComponent(EpicsSignal, 'cam1:Trigger') # the button next to 'Start' and 'Stop' image = Cpt(ImagePlugin, 'image1:') stats1 = Cpt(StatsPlugin, 'Stats1:') stats2 = Cpt(StatsPlugin, 'Stats2:') stats3 = Cpt(StatsPlugin, 'Stats3:') stats4 = Cpt(StatsPlugin, 'Stats4:') stats5 = Cpt(StatsPlugin, 'Stats5:') roi1 = Cpt(ROIPlugin, 'ROI1:') roi2 = Cpt(ROIPlugin, 'ROI2:') roi3 = Cpt(ROIPlugin, 'ROI3:') roi4 = Cpt(ROIPlugin, 'ROI4:') proc1 = Cpt(ProcessPlugin, 'Proc1:') shutter_mode = ADComponent(EpicsSignalWithRBV, 'cam1:ShutterMode') # hotfix: shadow non-existant PV size_link = None def stage(self, *args, **kwargs): # before parent ret = super().stage(*args, **kwargs) # after parent set_and_wait(self.manual_trigger, 1) return ret def unstage(self): set_and_wait(self.manual_trigger, 0) super().unstage() @property def hints(self): return {'fields': [self.stats1.total.name]} def mode_internal(self): # super().mode_internal() count_time = self.count_time.get() if count_time is not None: self.cam.stage_sigs[self.cam.acquire_time] = count_time self.cam.stage_sigs[self.cam.acquire_period] = count_time + 0.020
class EigerSimulatedFilePlugin(Device, FileStoreBase): sequence_id = ADComponent(EpicsSignalRO, 'SequenceId') file_path = ADComponent(EpicsSignalWithRBV, 'FilePath', string=True) file_write_name_pattern = ADComponent(EpicsSignalWithRBV, 'FWNamePattern', string=True) file_write_images_per_file = ADComponent(EpicsSignalWithRBV, 'FWNImagesPerFile') current_run_start_uid = Cpt(Signal, value='', add_prefix=()) enable = SimpleNamespace(get=lambda: True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._datum_kwargs_map = dict() # store kwargs for each uid self.filestore_spec = 'AD_EIGER2' def stage(self): res_uid = new_short_uid() write_path = datetime.now().strftime(self.write_path_template) set_and_wait(self.file_path, write_path) set_and_wait(self.file_write_name_pattern, '{}_$id'.format(res_uid)) super().stage() fn = (PurePath(self.file_path.get()) / res_uid) ipf = int(self.file_write_images_per_file.get()) # logger.debug("Inserting resource with filename %s", fn) self._fn = fn res_kwargs = {'images_per_file': ipf} self._generate_resource(res_kwargs) def generate_datum(self, key, timestamp, datum_kwargs): # The detector keeps its own counter which is uses label HDF5 # sub-files. We access that counter via the sequence_id # signal and stash it in the datum. seq_id = 1 + int(self.sequence_id.get()) # det writes to the NEXT one datum_kwargs.update({'seq_id': seq_id}) return super().generate_datum(key, timestamp, datum_kwargs)
class EigerBase(AreaDetector): """ Eiger, sans any triggering behavior. Use EigerSingleTrigger or EigerFastTrigger below. """ def __init__(self, *args, **kwargs): kwargs.setdefault('labels', ['detectors', 'cameras']) super().__init__(*args, **kwargs) num_triggers = ADComponent(EpicsSignalWithRBV, 'cam1:NumTriggers') file = Cpt(EigerSimulatedFilePlugin, suffix='cam1:', write_path_template='/GPFS/xf04id/DATA/Eiger1M/%Y/%m/%d/', root='/GPFS/xf04id/') beam_center_x = ADComponent(EpicsSignalWithRBV, 'cam1:BeamX') beam_center_y = ADComponent(EpicsSignalWithRBV, 'cam1:BeamY') wavelength = ADComponent(EpicsSignalWithRBV, 'cam1:Wavelength') det_distance = ADComponent(EpicsSignalWithRBV, 'cam1:DetDist') threshold_energy = ADComponent(EpicsSignalWithRBV, 'cam1:ThresholdEnergy') photon_energy = ADComponent(EpicsSignalWithRBV, 'cam1:PhotonEnergy') # the checkbox manual_trigger = ADComponent(EpicsSignalWithRBV, 'cam1:ManualTrigger') # the button next to 'Start' and 'Stop' special_trigger_button = ADComponent(EpicsSignal, 'cam1:Trigger') image = Cpt(ImagePlugin, 'image1:') stats1 = Cpt(StatsPlugin, 'Stats1:') stats2 = Cpt(StatsPlugin, 'Stats2:') stats3 = Cpt(StatsPlugin, 'Stats3:') stats4 = Cpt(StatsPlugin, 'Stats4:') stats5 = Cpt(StatsPlugin, 'Stats5:') roi1 = Cpt(ROIPlugin, 'ROI1:') roi2 = Cpt(ROIPlugin, 'ROI2:') roi3 = Cpt(ROIPlugin, 'ROI3:') roi4 = Cpt(ROIPlugin, 'ROI4:') proc1 = Cpt(ProcessPlugin, 'Proc1:') shutter_mode = ADComponent(EpicsSignalWithRBV, 'cam1:ShutterMode') # hotfix: shadow non-existant PV size_link = None def stage(self): # before parent super().stage() # after parent set_and_wait(self.manual_trigger, 1) def unstage(self): set_and_wait(self.manual_trigger, 0) super().unstage()
class EigerSimulatedFilePlugin(Device, FileStoreBase): sequence_id = ADComponent(EpicsSignalRO, 'SequenceId') file_path = ADComponent(EpicsSignalWithRBV, 'FilePath', string=True) file_write_name_pattern = ADComponent(EpicsSignalWithRBV, 'FWNamePattern', string=True) file_write_images_per_file = ADComponent(EpicsSignalWithRBV, 'FWNImagesPerFile') current_run_start_uid = Cpt(Signal, value='', add_prefix=()) enable = SimpleNamespace(get=lambda: True) def __init__(self, *args, **kwargs): self.sequence_id_offset = 1 # This is changed for when a datum is a slice # also used by ophyd self.filestore_spec = "AD_EIGER2" self.frame_num = None super().__init__(*args, **kwargs) self._datum_kwargs_map = dict() # store kwargs for each uid def stage(self): res_uid = new_short_uid() write_path = datetime.now().strftime(self.write_path_template) set_and_wait(self.file_path, write_path + '/') set_and_wait(self.file_write_name_pattern, '{}_$id'.format(res_uid)) super().stage() fn = (PurePath(self.file_path.get()) / res_uid) ipf = int(self.file_write_images_per_file.get()) # logger.debug("Inserting resource with filename %s", fn) self._fn = fn res_kwargs = {'images_per_file' : ipf} self._generate_resource(res_kwargs) def generate_datum(self, key, timestamp, datum_kwargs): # The detector keeps its own counter which is uses label HDF5 # sub-files. We access that counter via the sequence_id # signal and stash it in the datum. seq_id = int(self.sequence_id_offset) + int(self.sequence_id.get()) # det writes to the NEXT one datum_kwargs.update({'seq_id': seq_id}) if self.frame_num is not None: datum_kwargs.update({'frame_num': self.frame_num}) return super().generate_datum(key, timestamp, datum_kwargs) def describe(self,): ret = super().describe() if hasattr(self.parent.cam, 'bit_depth'): cur_bits = self.parent.cam.bit_depth.get() dtype_str_map = {8: '|u1', 16: '<u2', 32:'<u4'} ret[self.parent._image_name]['dtype_str'] = dtype_str_map[cur_bits] return ret
class PCDSAreaDetectorBase(DetectorBase): """Standard area detector with no plugins.""" cam = ADComponent(cam.CamBase, '') def get_plugin_graph_edges(self, *, use_names=True, include_cam=False): """ Get a list of (source, destination) ports for all plugin chains. Parameters ---------- use_names : bool, optional By default, the ophyd names for each plugin are used. Set this to False to instead get the AreaDetector port names. include_cam : bool, optional Include plugins with 'CAM' as the source. As it is easy to assume that a camera without an explicit source is CAM, by default this method does not include it in the list. """ cam_port = self.cam.port_name.get() graph, port_map = self.get_asyn_digraph() port_edges = [(src, dest) for src, dest in graph.edges if src != cam_port or include_cam] if use_names: port_edges = [(port_map[src].name, port_map[dest].name) for src, dest in port_edges] return port_edges
class PCDSAreaDetectorBase(DetectorBase): """Standard area detector with no plugins.""" cam = ADComponent(cam.CamBase, '') def get_plugin_graph_edges(self, *, use_names=True, include_cam=False): """ Get a list of (source, destination) ports for all plugin chains. Parameters ---------- use_names : bool, optional By default, the ophyd names for each plugin are used. Set this to False to instead get the AreaDetector port names. include_cam : bool, optional Include plugins with 'CAM' as the source. As it is easy to assume that a camera without an explicit source is CAM, by default this method does not include it in the list. """ cam_port = self.cam.port_name.get() graph, port_map = self.get_asyn_digraph() port_edges = [(src, dest) for src, dest in graph.edges if src != cam_port or include_cam] if use_names: port_edges = [(port_map[src].name, port_map[dest].name) for src, dest in port_edges] return port_edges def screen(self, main: bool = False) -> None: """ Open camViewer screen for camera. Parameters ---------- main : bool, optional Set to True to bring up 'main' edm config screen. Defaults to False, which opens python viewer. """ if not shutil.which('camViewer'): logger.error('no camViewer available') return arglist = [ 'camViewer', '-H', str(get_hutch_name()).lower(), '-c', self.name ] if main: arglist.append('-m') logger.info('starting camviewer') subprocess.run(arglist, check=False)
class FeeOpalCam(cam.CamBase): """Opal camera used in the FEE for the PIMs.""" # enums? # trigger_modes = enum("Internal", "External", start=0) # exposure_modes = enum("Full Frame", "HW ROI", start=0) # acquire_enum = enum("Done", "Acquire", start=0) # test_patterns = enum("Off", "On", start=0) # trigger_line_enum = enum("CC1", "CC2", "CC3", "CC4", start=0) # polarity_enum = enum("Normal", "Inverted", start=0) # prescale_enum = enum("119 : 1 us", "1 : 1/119 us", start=0) # pack_enum = enum("24", "16", start=0) # video_out_enum = enum("Top Down", "Top & Bottom", start=0) # baud_rates = enum("9600", "19200", "38400", "57600", "115200", start=0) # Signals with RBV min_callback_time = ADComponent(EpicsSignalWithRBV, 'MinCallbackTime') blocking_callbacks = ADComponent(EpicsSignalWithRBV, 'BlockingCallbacks') enable_callbacks = ADComponent(EpicsSignalWithRBV, 'EnableCallbacks') dropped_arrays = ADComponent(EpicsSignalWithRBV, 'DroppedArrays') nd_array_address = ADComponent(EpicsSignalWithRBV, 'NDArrayAddress') queue_size = ADComponent(EpicsSignalWithRBV, 'QueueSize') nd_array_port = ADComponent(EpicsSignalWithRBV, 'NDArrayPort', string=True) # Signals pixel_size = ADComponent(EpicsSignal, 'PixelSize') exposure_mode = ADComponent(EpicsSignal, 'ExposureMode') test_pattern = ADComponent(EpicsSignal, 'TestPattern') trg_polarity = ADComponent(EpicsSignal, 'TrgPolarity') queue_use = ADComponent(EpicsSignal, 'QueueUse') queue_free_low = ADComponent(EpicsSignal, 'QueueFreeLow') queue_use_high = ADComponent(EpicsSignal, 'QueueUseHIGH') queue_use_hihi = ADComponent(EpicsSignal, 'QueueUseHIHI') num_col = ADComponent(EpicsSignal, 'NumCol') num_cycles = ADComponent(EpicsSignal, 'NumCycles') num_row = ADComponent(EpicsSignal, 'NumRow') num_trains = ADComponent(EpicsSignal, 'NumTrains') queue_free = ADComponent(EpicsSignal, 'QueueFree') status_word = ADComponent(EpicsSignal, 'StatusWord') trg2_frame = ADComponent(EpicsSignal, 'Trg2Frame') bl_set = ADComponent(EpicsSignal, 'BL_SET') fp_set = ADComponent(EpicsSignal, 'FP_SET') full_col = ADComponent(EpicsSignal, 'FullCol') full_row = ADComponent(EpicsSignal, 'FullRow') ga_set = ADComponent(EpicsSignal, 'GA_SET') it_set = ADComponent(EpicsSignal, 'IT_SET') ssus = ADComponent(EpicsSignal, 'SSUS') skip_col = ADComponent(EpicsSignal, 'SkipCol') skip_row = ADComponent(EpicsSignal, 'SkipRow') trg_code = ADComponent(EpicsSignal, 'TrgCode') trg_delay = ADComponent(EpicsSignal, 'TrgDelay') trg_width = ADComponent(EpicsSignal, 'TrgWidth') baud = ADComponent(EpicsSignal, 'Baud') evr_prescale = ADComponent(EpicsSignal, 'EvrPrescale') v_out = ADComponent(EpicsSignal, 'VOut') resp = ADComponent(EpicsSignal, 'Resp', string=True) cmd = ADComponent(EpicsSignal, 'CMD', string=True) cmd_evr = ADComponent(EpicsSignal, 'CmdEVR', string=True) cmd_free = ADComponent(EpicsSignal, 'CmdFree', string=True) cmd_full = ADComponent(EpicsSignal, 'CmdFull', string=True) cmd_init = ADComponent(EpicsSignal, 'CmdInit', string=True) cmd_roi = ADComponent(EpicsSignal, 'CmdROI', string=True) cmd_t_ptn = ADComponent(EpicsSignal, 'CmdTPtn', string=True) # Read Only Signals array_data = ADComponent(EpicsSignalRO, 'ArrayData') execution_time = ADComponent(EpicsSignalRO, 'ExecutionTime_RBV') temp_f = ADComponent(EpicsSignalRO, 'TempF_RBV') bl = ADComponent(EpicsSignalRO, 'BL_RBV') bits_per_pixel = ADComponent(EpicsSignalRO, 'BitsPerPixel_RBV') fp = ADComponent(EpicsSignalRO, 'FP_RBV') ga = ADComponent(EpicsSignalRO, 'GA_RBV') err = ADComponent(EpicsSignalRO, 'ERR_RBV') mid = ADComponent(EpicsSignalRO, 'MID_RBV') plugin_type = ADComponent(EpicsSignalRO, 'PluginType_RBV', string=True) sdk_version = ADComponent(EpicsSignalRO, 'SDKVersion_RBV', string=True) ufdt = ADComponent(EpicsSignalRO, 'UFDT_RBV', string=True) # Overridden Components # array_rate = Component(Signal) array_rate = Component(EpicsSignalRO, 'FrameRate') acquire = Component(EpicsSignal, 'Acquire') # Attrs that arent in the fee opal array_counter = Component(SynSignal) # C(SignalWithRBV, 'ArrayCounter') nd_attributes_file = Component( SynSignal) # C(EpicsSignal, 'NDAttributesFile', string=True) pool_alloc_buffers = Component( SynSignal) # C(EpicsSignalRO, 'PoolAllocBuffers') pool_free_buffers = Component( SynSignal) # C(EpicsSignalRO, 'PoolFreeBuffers') pool_max_buffers = Component( SynSignal) # C(EpicsSignalRO, 'PoolMaxBuffers') pool_max_mem = Component(SynSignal) # C(EpicsSignalRO, 'PoolMaxMem') pool_used_buffers = Component( SynSignal) # C(EpicsSignalRO, 'PoolUsedBuffers') pool_used_mem = Component(SynSignal) # C(EpicsSignalRO, 'PoolUsedMem') port_name = Component( SynSignal) # C(EpicsSignalRO, 'PortName_RBV', string=True) array_callbacks = Component( SynSignal) # C(SignalWithRBV, 'ArrayCallbacks') array_size = Component(SynSignal) # DDC(ad_group(EpicsSignalRO, # (('array_size_x', 'ArraySizeX_RBV'), # ('array_size_y', 'ArraySizeY_RBV'), # ('array_size_z', 'ArraySizeZ_RBV'))), # doc='Size of the array in the XYZ dimensions') color_mode = Component(SynSignal) # C(SignalWithRBV, 'ColorMode') data_type = Component(SynSignal) # C(SignalWithRBV, 'DataType') array_size_bytes = Component( SynSignal) # C(EpicsSignalRO, 'ArraySize_RBV')
class PCDSDetectorBase(DetectorBase): """ Standard area detector with no plugins. """ cam = ADComponent(cam.CamBase, ":")
class PCDSHDF5BlueskyTriggerable(SingleTrigger, PCDSAreaDetectorBase): """ Saves an HDF5 file in a bluesky plan. This class takes care of all the standard setup for saving the files using the HDF5 plugin for our area detector setups at LCLS during bluesky scans, including configuration of the stage signals and various site-specific settings. You can decide how many images we'll take at each point by setting the `num_images_per_point` attribute. There are two modes provided: "always aquire" and "aquire at points" with key differences in the behavior. You can select which one to use using the ``always_acquire`` keyword argument, which defaults to True. With always_aquire=True (default): - Viewers will remain updated throughout the scan, even between scan points. - The camera will be set to ``acquire`` at ``stage``, if needed, which begins taking images. This happens early in the scan. - At each point, the ``capture`` feature of the HDF5 plugin will be toggled on until we've saved an image count equal to the `num_images_per_point` attribute. The counting is handled by the plugin. - If the camera or server lags during the aquisition, the scan will patiently wait for the correct number of images to be saved. - There is no guarantee that the images are sequential, there can be gaps, but each image that does get saved will be trigger-synchronized. (And therefore, beam-synchronized with beam-synchronized triggers). - Each point in the scan will have its own associate hdf5 file. - If the camera needed to be set to ``acquire`` at ``stage``, it will revert to ``stop`` at ``unstage``. With always_acquire=False: - Viewers will only be updated at the specific scan points. - The HDF5 plugin will be set to ``capture`` at stage, and the camera will be configured to take a fixed number of images per acquisition cycle. - At each point, the ``acquire`` bit will be turned on, which causes `num_images_per_point` images to be acquired. - If the camera or server lags during the acquisition, it will be unable to complete it cleanly. - There is a guarantee that the images are sequential. - All the points in the scan will be grouped into one larger hdf5 file. This class can be also be used interactively via the ``save_images`` function. Parameters ---------- prefix : ``str`` The PV prefix that leads us to this camera's PVs. write_path : ``str`` The directory to drop our hdf5 files into. always_acquire : ``bool``, optional Determines which mode we use to collect images as described above. name : ``str``, keyword-only A name to associate with the camera for bluesky. """ hdf51 = ADComponent(HDF5FileStore, 'HDF51:', write_path_template='/dev/null', kind='normal', doc='Save output as an HDF5 file') def __init__( self, *args, write_path: str, always_acquire: bool = True, **kwargs, ): super().__init__(*args, **kwargs) self.hutch_name = get_hutch_name() self.always_acquire = always_acquire self.num_images_per_point = 1 self.hdf51.write_path_template = write_path self.hdf51.stage_sigs['file_template'] = '%s%s_%03d.h5' self.hdf51.stage_sigs['file_write_mode'] = 'Stream' del self.hdf51.stage_sigs["capture"] if always_acquire: # This mode is "acquire always, capture on trigger" # Override the default to Continuous, always go self.stage_sigs['cam.acquire'] = 1 self.stage_sigs['cam.image_mode'] = 2 # Make sure we toggle capture for trigger self._acquisition_signal = self.hdf51.capture else: # This mode is "acquire on trigger, capture always" # Confirm default of Multiple, start off # Redundantly set these here for code clarity self.stage_sigs['cam.acquire'] = 0 self.stage_sigs['cam.image_mode'] = 1 # If we include capture in stage, it must be last self.hdf51.stage_sigs["capture"] = 1 # Ensure we use the cam acquire as the trigger self._acquisition_signal = self.cam.acquire def stage(self) -> list[OphydObject]: """ Bluesky interface for setting up the plan. This will unpack all of our stage_sigs like normal, but also add a small delay to avoid a race condition in the IOC. """ rval = super().stage() # It takes a moment for the IOC to be ready sometimes time.sleep(0.1) return rval @property def num_images_per_point(self) -> int: """ The number of images to save at each point in the scan. """ if self.always_acquire: return self.cam.stage_sigs['num_images'] return self.hdf51.stage_sigs['num_capture'] @num_images_per_point.setter def num_images_per_point(self, num_images: int): if self.always_acquire: self.hdf51.stage_sigs['num_capture'] = num_images self.cam.stage_sigs['num_images'] = 1 else: self.hdf51.stage_sigs['num_capture'] = 0 self.cam.stage_sigs['num_images'] = num_images def save_images(self) -> None: """ Save images interactively as if we were currently at a scan point. """ self.stage() self.trigger().wait() self.unstage()
class SpecsDetector(SpecsSingleTrigger, DetectorBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.stage_sigs.update({self.cam.safe_state: 0}) self.stage_sigs.update({self.count_enable: 1}) self.stage_sigs.update({self.cam.data_type: 9}) self.count.kind = Kind.hinted cam = ADComponent(SpecsDetectorCam, 'cam1:') # hdf1 = ADComponent(SpecsHDF5Plugin, # name='hdf1',suffix='HDF1:', # write_path_template='/GPFS/xf23id/xf23id2/data/specs/%Y/%m/%d/', # write_path_template='/nsls2/xf23id1/xf23id2/data/specs/%Y/%m/%d/', #read_path_template='/nsls2/xf23id1/xf23id2/data/specs/%Y/%m/%d/', #write_path_template='X:\xf23id2\data\specs\\%Y\\%m\\%d\\', # root='/GPFS/xf23id/xf23id2/') #root='/nsls2/xf23id1/xf23id2/') count = ADComponent(EpicsSignalRO, 'Stats5:Total_RBV', kind=Kind.hinted, name='count') count_enable = ADComponent(EpicsSignal, 'Stats5:EnableCallbacks', kind=Kind.omitted, name='count_enable') # I need to find the actual PV for above acquisition_mode = None def set_mode(self, mode): '''This method returns a plan that swaps between 'single_count' mode and 'spectrum' mode. This method sets a range of parameters in order to run the detector in either 'single_count' mode, where a single value is read at each trigger, and 'spectrum' mode, where a spectrum is read at each trigger. Parameters ---------- mode : str or SPECSmode enum The mode to use for the data acquisition, the options are 'single_point' and 'spectrum'. ''' if type(mode) == str: try: mode = SPECSmode(mode) except ValueError: raise ValueError( f'The value provided via `mode` in {self.name}.set_mode does not have' f' the correct value type (should be "single_count", "spectrum", ' f'SPECSmode.single_count or SPECSmode.spectrum') elif type(mode) is not SPECSmode: raise TypeError( f'the mode supplied in {self.name}.set_mode is not of type `string` or' f' type SPECSmode, set was not performed') if mode is SPECSmode.single_count: yield from mv(self.cam.acquire_mode, 3) #,self.hdf1.enable, 0) elif mode == SPECSmode.spectrum: yield from mv(self.cam.acquire_mode, 0, self.hdf1.enable, 1) self.hdf1.kind = Kind.normal else: raise ValueError( f'The value provided via `mode` in {self.name}.set_mode does not have' f' the correct value type (should be "single_count", "spectrum", ' f'SPECSmode.single_count or SPECSmode.spectrum') self.acquisition_mode = mode
class SpecsDetectorCam(CamBase): '''This is a class that defines the cam class for a SPECS detector ''' # Controls connect = ADComponent(EpicsSignal, 'CONNECTED_RBV', write_pv='CONNECT', kind=Kind.omitted, name='connect') status_message = ADComponent(EpicsSignalRO, 'StatusMessage_RBV', string=True, kind=Kind.omitted, name='status_message') server_name = ADComponent(EpicsSignalRO, 'SERVER_NAME_RBV', string=True, kind=Kind.omitted, name='server_name') protocol_version = ADComponent(EpicsSignalRO, 'PROTOCOL_VERSION_RBV', string=True, kind=Kind.omitted, name='protocol_version') # Energy Controls pass_energy = ADComponent(EpicsSignalWithRBV, 'PASS_ENERGY', name='pass_energy') low_energy = ADComponent(EpicsSignalWithRBV, 'LOW_ENERGY', name='low_energy') high_energy = ADComponent(EpicsSignalWithRBV, 'HIGH_ENERGY', name='high_energy') energy_width = ADComponent(EpicsSignalRO, 'ENERGY_WIDTH_RBV', name='energy_width') kinetic_energy = ADComponent(EpicsSignalWithRBV, 'KINETIC_ENERGY', name='kinetic_energy') retarding_ratio = ADComponent(EpicsSignalWithRBV, 'RETARDING_RATIO', name='retarding_ratio') step_size = ADComponent(EpicsSignalWithRBV, 'STEP_SIZE', name='step_size') fat_values = ADComponent(EpicsSignalWithRBV, 'VALUES', name='fat_values') samples = ADComponent(EpicsSignalWithRBV, 'SAMPLES', name='samples') lens_mode = ADComponent(EpicsSignalWithRBV, 'LENS_MODE', name='lens_mode') # Configuration scan_range = ADComponent(EpicsSignalWithRBV, 'SCAN_RANGE', kind=Kind.config, name='scan_range') acquire_mode = ADComponent(EpicsSignalWithRBV, 'ACQ_MODE', kind=Kind.config, name='acquire_mode') define_spectrum = ADComponent(EpicsSignalWithRBV, 'DEFINE_SPECTRUM', kind=Kind.config, name='define_spectrum') validate_spectrum = ADComponent(EpicsSignalWithRBV, 'VALIDATE_SPECTRUM', kind=Kind.config, name='validate_spectrum') safe_state = ADComponent(EpicsSignalWithRBV, 'SAFE_STATE', kind=Kind.config, name='safe_state') pause_acq = ADComponent(EpicsSignalWithRBV, 'PAUSE_RBV', kind=Kind.config, name='pause_acq') #Data Progress current_point = ADComponent(EpicsSignalRO, 'CURRENT_POINT_RBV', kind=Kind.omitted, name='current_point') current_channel = ADComponent(EpicsSignalRO, 'CURRENT_CHANNEL_RBV', kind=Kind.omitted, name='current_channel') region_time_left = ADComponent(EpicsSignalRO, 'REGION_TIME_LEFT_RBV', kind=Kind.omitted, name='region_time_left') region_progress = ADComponent(EpicsSignalRO, 'REGION_PROGRESS_RBV', kind=Kind.omitted, name='region_progress') total_time_left = ADComponent(EpicsSignalRO, 'TOTAL_TIME_LEFT_RBV', kind=Kind.omitted, name='total_tiem_left') progress = ADComponent(EpicsSignalRO, 'PROGRESS_RBV', kind=Kind.omitted, name='progress') total_points_iteration = ADComponent(EpicsSignalRO, 'TOTAL_POINTS_ITERTION_RBV', kind=Kind.omitted, name='total_points_iteration') total_points = ADComponent(EpicsSignalRO, 'TOTAL_POINTS_RBV', kind=Kind.omitted, name='total_points')
class FeeOpalDetector(DetectorBase): """ Opal detector that in the FEE running using Dehong's IOC. """ cam = ADComponent(FeeOpalCam, ":")
class PulnixDetector(DetectorBase): """ Standard pulnix detector. """ cam = ADComponent(cam.CamBase, ":")