class SimDetector(SingleTrigger, AreaDetector): """ Simulated Detector used at 6-ID-D@APS This is based on the Point Grey detector """ cam1 = ADComponent(SimDetectorCam6IDD, suffix="cam1:") # camera proc1 = ADComponent(ProcessPlugin, suffix="Proc1:") # processing tiff1 = ADComponent(TIFFPlugin, suffix="TIFF1:") # tiff output hdf1 = ADComponent(HDF5Plugin6IDD, suffix="HDF1:") # HDF5 output @property def status(self): """List all related PVs and corresponding values""" # TODO: # provide acutal implementation here return "Not implemented yet" @property def help(self): """Return quick summary of the actual specs of the detector""" pass @property def position(self): """return the area detector position from the associated motor""" pass @position.setter def position(self, new_pos): """move the detector to the new location""" # NOTE: # This is for interactive control only, cannot be used in scan plan # We will need to use this position during scan, i.e. near field z scan pass
class PilatusFilePlugin(Device, FileStoreBulkWrite): file_path = ADComponent(EpicsSignalWithRBV, 'FilePath', string=True) file_number = ADComponent(EpicsSignalWithRBV, 'FileNumber') file_name = ADComponent(EpicsSignalWithRBV, 'FileName', string=True) file_template = ADComponent(EpicsSignalWithRBV, 'FileTemplate', string=True) reset_file_number = Cpt(Signal, name='reset_file_number', value=1) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._datum_kwargs_map = dict() # store kwargs for each uid def stage(self): global proposal_id global run_id global current_sample global data_path global collection_lock_file set_and_wait(self.file_template, '%s%s_%6.6d_'+self.parent.detector_id+'.cbf', timeout=99999) if self.reset_file_number.get() == 1: set_and_wait(self.file_number, 1, timeout=99999) # original code by Hugo # this is done now when login() #path = '/GPFS/xf16id/exp_path/' #rpath = str(proposal_id)+"/"+str(run_id)+"/" #fpath = path + rpath #makedirs(fpath) # modified by LY # camserver saves data to the local ramdisk, a background process then move them to data_path # interesting to note that camserver saves the data to filename.tmp, then rename it filename after done writing # must have the '/' at the end, since camserver will add it to the RBV # this should done only once for all Pilatus detectors if self.parent.name == first_Pilatus(): #print("first Pilatus is %s" % self.parent.name) change_path() #set_and_wait(self.file_path, "/ramdisk/", timeout=99999) set_and_wait(self.file_path, data_path, timeout=99999) set_and_wait(self.file_name, current_sample, timeout=99999) super().stage() res_kwargs = {'template': self.file_template.get(), 'filename': self.file_name.get(), 'frame_per_point': self.get_frames_per_point(), 'initial_number': self.file_number.get()} #self._resource = fs.insert_resource('AD_CBF', rpath, res_kwargs, root=path) self._resource = fs.insert_resource('AD_CBF', data_path, res_kwargs, root="/") if self.parent.name == first_Pilatus(): caput("XF:16IDC-ES:Sol{ctrl}ready", 1) def unstage(self): super().unstage() #if self.parent.name == first_Pilatus(): # release_lock() def get_frames_per_point(self): return 1
class GEDetector(SingleTrigger, AreaDetector): """Generic detector abstraction for GE""" # e.g. det = GEDetector("GE2:", name='det') # TODO # we migth need to switch to raw cam1 = ADComponent(CamBase, suffix="cam1:") proc1 = ADComponent(ProcessPlugin, suffix="Proc1:") tiff1 = ADComponent(TIFFPlugin, suffix="TIFF1:")
class PointGreyDetector6BM(SingleTrigger, AreaDetector): """Point Gray area detector used at 6BM""" # cam component cam = ADComponent(PointGreyDetectorCam6BM, "cam1:") # proc plugin proc1 = ADComponent(ProcessPlugin, suffix="Proc1:") # tiff plugin tiff1 = ADComponent(TIFFPlugin, suffix="TIFF1:") # HDF5 plugin hdf1 = ADComponent(HDF5Plugin6BM, suffix="HDF1:")
class MySimDetector(SingleTrigger, DetectorBase): """ADSimDetector""" cam = ADComponent(MyFixedCam, "cam1:") image = ADComponent(ImagePlugin_V34, "image1:") hdf1 = ADComponent( MyHDF5Plugin, "HDF1:", write_path_template=WRITE_PATH_TEMPLATE, read_path_template=READ_PATH_TEMPLATE, ) pva = ADComponent(PvaPlugin_V34, "Pva1:")
class MyDexelaDetector(SingleTrigger, DexelaDetector): """Dexela detector(s) as used by 9-ID-C USAXS.""" hdf1 = ADComponent( MyHDF5Plugin, "HDF1:", write_path_template=WRITE_HDF5_FILE_PATH_DEXELA, read_path_template=READ_HDF5_FILE_PATH_DEXELA, path_semantics="windows", ) image = ADComponent(ImagePlugin, "image1:") proc1 = ADComponent(MyProcessPlugin, "Proc1:")
class MyPcoDetector(SingleTrigger, AreaDetector): """PCO detectors as used by 2-BM tomography""" # TODO: configure the "root" and "write_path_template" attributes cam = ADComponent(MyPcoCam, "cam1:") image = ADComponent(ImagePlugin, "image1:") hdf1 = ADComponent( MyHDF5Plugin, "HDF1:", root="/", # root path for HDF5 files (for databroker filestore) write_path_template= "/tmp", # path for HDF5 files (for EPICS area detector) reg=db.reg, )
class MyPointGreyDetectorJPEG(MyPointGreyDetector, AreaDetector): """ Variation to write image as JPEG To save an image (using existing configuration):: blackfly_optical.stage() blackfly_optical.trigger() blackfly_optical.unstage() """ jpeg1 = ADComponent( EpicsDefinesJpegFileNames, suffix = "JPEG1:", root = DATABROKER_ROOT_PATH, write_path_template = WRITE_IMAGE_FILE_PATH, read_path_template = READ_IMAGE_FILE_PATH, kind="normal", ) trans1 = ADComponent(TransformPlugin, "Trans1:") cc1 = ADComponent(ColorConvPlugin, "CC1:") @property def image_file_name(self): return self.jpeg1.full_file_name.get() def image_prep(self, path, filename_base, order_number): plugin = self.jpeg1 path = "/mnt" + os.path.abspath(path) + "/" # MUST end with "/" yield from bps.mv( plugin.file_path, path, plugin.file_name, filename_base, plugin.file_number, order_number, ) @property def should_save_image(self): return _flag_save_sample_image_.get() in (1, "Yes") def take_image(self): yield from bps.stage(self) yield from bps.trigger(self, wait=True) yield from bps.unstage(self)
class MyHDF5Plugin(HDF5Plugin, FileStoreHDF5IterativeWrite): #class MyHDF5Plugin(HDF5Plugin): """adapt HDF5 plugin for AD 2.5+""" file_number_sync = None # FIXME: .put() works OK but .value returns numpy object metadata # In [48]: pco_edge.hdf1.xml_layout_file.get() # Out[48]: '<array size=21, type=time_char>' # FIXME: xml_layout_file = Component(EpicsSignalWithRBV, "XMLFileName", string=True) xml_layout_file = ADComponent( EpicsSignal, "XMLFileName", string=True) # use as WRITE-ONLY for now due to error above xml_layout_valid = ADComponent(EpicsSignalRO, "XMLValid_RBV") xml_layout_error_message = ADComponent(EpicsSignalRO, "XMLErrorMsg_RBV", string=True) def get_frames_per_point(self): return self.parent.cam.num_images.get()
class PilatusFilePlugin(Device, FileStoreIterativeWrite): file_path = ADComponent(EpicsSignalWithRBV, 'FilePath', string=True) file_number = ADComponent(EpicsSignalWithRBV, 'FileNumber') file_name = ADComponent(EpicsSignalWithRBV, 'FileName', string=True) file_template = ADComponent(EpicsSignalWithRBV, 'FileTemplate', string=True) file_number_reset = 1 sub_directory = None froot = data_file_path.gpfs enable = SimpleNamespace(get=lambda: True) # this is not necessary to record since it contains the UID for the scan, useful # to save in the CBF file but no need in the data store #file_header = ADComponent(EpicsSignal, "HeaderString", string=True) # this is not necessary to record in the data store either, move to the parent #reset_file_number = Cpt(Signal, name='reset_file_number', value=1) #filemover_files = Cpt(EpicsSignal, 'filemover.filename') #filemover_target_dir = Cpt(EpicsSignal, 'filemover.target') #filemover_move = Cpt(EpicsSignal, 'filemover.moving') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._datum_kwargs_map = dict() # store kwargs for each uid self.filestore_spec = 'AD_CBF' def stage(self): global proposal_id global run_id global current_sample global data_path f_tplt = '%s%s_%06d_' + self.parent.detector_id + '.cbf' set_and_wait(self.file_template, f_tplt, timeout=99999) if self.parent.name == first_Pilatus( ) or self.parent.name == first_PilatusExt(): #print("first Pilatus is %s" % self.parent.name) change_path() # if file number reset is Ture, use 1 as the next file # # if reset is False, when the first Pilatus/PilatusExt instance is staged, the file# will be # synchronized to the highest current value if PilatusFilePlugin.file_number_reset == 1: print("resetting file number for ", self.parent.name) # it is a bad idea to wait since auto-increment may change this value immediately #set_and_wait(self.file_number, 1, timeout=99999) self.file_number.put(1) print('done.') elif self.parent.name == first_Pilatus(): next_file_number = np.max( [d.file.file_number.get() for d in pilatus_detectors]) for d in pilatus_detectors: print("setting file number for %s to %d." % (d.name, next_file_number)) set_and_wait(d.file.file_number, next_file_number, timeout=99999) elif self.parent.name == first_PilatusExt(): next_file_number = np.max( [d.file.file_number.get() for d in pilatus_detectors_ext]) for d in pilatus_detectors_ext: print("setting file number for %s to %d." % (d.name, next_file_number)) set_and_wait(d.file.file_number, next_file_number, timeout=99999) if PilatusFilePlugin.sub_directory is not None: f_path = data_path + PilatusFilePlugin.sub_directory RE.md['subdir'] = PilatusFilePlugin.sub_directory else: f_path = data_path if 'subdir' in RE.md.keys(): del RE.md['subdir'] f_fn = current_sample # file_path must ends with '/' print('%s: setting file path ...' % self.name) #if DET_replace_data_path: if self.froot == data_file_path.ramdisk: #f_path = f_path.replace(default_data_path_root, substitute_data_path_root) f_path = f_path.replace(data_file_path.gpfs.value, data_file_path.ramdisk.value) set_and_wait(self.file_path, f_path, timeout=99999) #set_and_wait(self.file_path, f_path, timeout=99999) set_and_wait(self.file_name, f_fn, timeout=99999) self._fn = Path(f_path) fpp = self.get_frames_per_point() # when camserver collects in "multiple" mode, another number is added to the file name # even though the template does not specify it. # Camserver doesn't like the template to include the second number # The template will be revised in the CBF handler if fpp>1 print('%s: super().stage() ...' % self.name) super().stage() res_kwargs = { 'template': f_tplt, # self.file_template(), 'filename': f_fn, # self.file_name(), 'frame_per_point': fpp, 'initial_number': self.file_number.get() } print('%s: _generate_resource() ...' % self.name) self._generate_resource(res_kwargs) def unstage(self): super().unstage() ##12/19/17 commented out # move the files from ramdisk to GPFS #if self.filemover_move.get()==1: # print("files are still being moved from the detector server to ",self.filemover_target_dir.get()) # while self.filemover_move.get()==1: # sleep(1) # print("done.") #self.filemover_files.put(current_sample) #self.filemover_target_dir.put(data_path) #self.filemover_move.put(1) ##12/19/17 commented out #if self.parent.name == first_Pilatus() or self.parent.name == first_PilatusExt(): # release_lock() def get_frames_per_point(self): #return self.parent.cam.num_images.get() # always return 1 before 2018 return self.parent._num_images
class MyPointGreyDetector(SingleTrigger, AreaDetector): """PointGrey Black Fly detector(s) as used by 9-ID-C USAXS""" cam = ADComponent(PointGreyDetectorCam, "cam1:") image = ADComponent(ImagePlugin, "image1:")
class MyFixedCam(SimDetectorCam): pool_max_buffers = None offset = ADComponent(EpicsSignalWithRBV, "Offset")
class SimDetectorCam6IDD(PointGreyDetectorCam): """ Using SimDetector as PointGrey: cam plugin customizations (properties) """ auto_exposure_on_off = ADComponent(EpicsSignalWithRBV, "AutoExposureOnOff") auto_exposure_auto_mode = ADComponent(EpicsSignalWithRBV, "AutoExposureAutoMode") sharpness_on_off = ADComponent(EpicsSignalWithRBV, "SharpnessOnOff") sharpness_auto_mode = ADComponent(EpicsSignalWithRBV, "SharpnessAutoMode") gamma_on_off = ADComponent(EpicsSignalWithRBV, "GammaOnOff") shutter_auto_mode = ADComponent(EpicsSignalWithRBV, "ShutterAutoMode") gain_auto_mode = ADComponent(EpicsSignalWithRBV, "GainAutoMode") trigger_mode_on_off = ADComponent(EpicsSignalWithRBV, "TriggerModeOnOff") trigger_mode_auto_mode = ADComponent(EpicsSignalWithRBV, "TriggerModeAutoMode") trigger_delay_on_off = ADComponent(EpicsSignalWithRBV, "TriggerDelayOnOff") frame_rate_on_off = ADComponent(EpicsSignalWithRBV, "FrameRateOnOff") frame_rate_auto_mode = ADComponent(EpicsSignalWithRBV, "FrameRateAutoMode")
class RetigaDetectorCam(CamBase): """Retiga camera """ # NOTE: # Different camera module from different manufacture requires different # configuration, see # https://github.com/bluesky/ophyd/blob/master/ophyd/areadetector/cam.py # for more examples on how to make the PointGrey cam # NOTE: # - The PV settings are based on QIMAGE2@1-ID-C,APS # - The following PVs are defined in CamBase, but the available values # might differe from other cams // bookkeeping for inconsistent naming # --------------- ||| ------------ # MEDM/GUI/caQTdm ||| PV/BlueSky # --------------- ||| ------------ # acquire_time === exposure_time # attributes === nd_attributes_file # buffer max/used === pool_max/used_buffers # buffer alloc/free === pool_alloc/free_buffers # buffer max/used === pool_max_buffers # memory max/userd === pool_max/used_mem # image_counter === array_counter # image_rate === array_rate # Sensor size === max_size // bundled fields # Region start === min_x/y # Region size === size // bundled fields # Image size === array_size // bundled fields # # - Nominal values for special fields # * image_mode # 0: Single; 1: Multiple; 2: Continuous; 3: Single Fast # * trigger_mode # 0: Freerun; 1: EdgeHi; 2: EdgeLow; 3: PulseHi; 4: PulseLow # 5: Software; 6: StrobeHi 7: StrobeLow; 8: Trigger_last # DEV: # - PVs with ?? requires double check during testing # _html_docs = ['retigaDoc.html'] # ?? # --- # --- Define missing fields in the collect & attribute panel # --- auto_exposure = ADComponent(EpicsSignalWithRBV, 'aAutoExposure') # ?? exposure_max = ADComponent(EpicsSignalRO, 'ExposureMax_RBV') exposure_min = ADComponent(EpicsSignalRO, 'ExposureMin_RBV') exposure_status_message = ADComponent(EpicsSignalRO, 'qExposureStatusMessage_RBV') frame_status_message = ADComponent(EpicsSignalRO, 'qFrameStatusMessage_RBV') pool_used_mem_scan = ADComponent(EpicsSignalWithRBV, 'PoolUsedMem.SCAN') # ?? # available values for pool_used_mem_scan # 0: Passive; 1: Event; 2: I/O Intr; # 3: 10 second; 4: 5 second; 5: 2 second; 6: 1 second; # 7: 0.5 second; 8: 0.2 second; 9: 0.1 second # --- # --- Define missing fields in the Setup panel # --- asyn_io_connected = ADComponent( EpicsSignalWithRBV, 'AsynIO.CNCT') # 0: Disconnected; 1: Connected reset_cam = ADComponent(EpicsSignalWithRBV, 'qResetCam') # --- # --- Define missing fields in the Readout panel # --- binning = ADComponent(EpicsSignalWithRBV, 'qBinning') # 0: 1x1; 1: 2x2; 2: 4x4; 3: 8x8 # WARNING: # retiga does not have individual binning fields, therefore the predefined # bin_x and bin_y will lead to error... gain_max = ADComponent(EpicsSignalRO, 'GainMax_RBV') gain_min = ADComponent(EpicsSignalRO, 'GainMin_RBV') abs_offset = ADComponent(EpicsSignalWithRBV, 'qOffset') # ?? image_format = ADComponent(EpicsSignalWithRBV, 'qImageFormat') # -- available values for image_format # 0: Raw 8; 1: Raw 16; # 2: Mono 8; 3: Mono 16; # 4: RGB Plane 8; 5: GBR Plane 16; # 6: BGR 24; # 7: XRGB 32; # 8: RGB 48; # 9: BGRX 32; # 10: RGB 24; readout_speed = ADComponent(EpicsSignalWithRBV, 'qReadoutSpeed') # -- available values for readout speed # 0: 20 Mhz; 1: 10 Mhz; 2: 5 Mhz # --- # --- Define missing fileds in the Cooling Panel # --- cooler = ADComponent(EpicsSignalWithRBV, 'qCoolerActive')
class PointGreyDetector(SingleTrigger, AreaDetector): """PointGrey Detector used at 6-ID-D@APS for tomo and nf-HEDM""" cam1 = ADComponent(PointGreyDetectorCam6IDD, suffix="cam1:") # camera proc1 = ADComponent(ProcessPlugin, suffix="Proc1:") # processing tiff1 = ADComponent(TIFFPlugin, suffix="TIFF1:") # tiff output hdf1 = ADComponent(HDF5Plugin6IDD, suffix="HDF1:") # HDF5 output trans1 = ADComponent(TransformPlugin, suffix="Trans1:") # Transform images image1 = ADComponent(ImagePlugin, suffix="image1:") # Image plugin, rarely used in plan @property def status(self): """List all related PVs and corresponding values""" # TODO: # provide acutal implementation here return "Not implemented yet" @property def help(self): """Return quick summary of the actual specs of the detector""" pass @property def position(self): """return the area detector position from the associated motor""" pass @position.setter def position(self, new_pos): """move the detector to the new location""" # NOTE: # This is for interactive control only, cannot be used in scan plan # We will need to use this position during scan, i.e. near field z scan pass # TODO: # Additional PVs can be wrapped as property for interactive use when the # acutal PVs are known. def cont_acq(self, _exp, _period, _nframes=-1): """ cont_acq(a, b, c) acuqre 'c' images with exposure of 'a' seconds every 'b' seconds b is default to -1 to continue acquiring until manual interruption """ from time import sleep self.cam1.acquire_time.put(_exp) self.cam1.acquire_period.put(_period) self.cam1.image_mode.put("Continuous") # To be checked if _nframes <= 0: # do infinite number of frames.... print(f"Start taking images with {_exp} seconds of exposure\n") print(f"Press \"Stop\" on 1idPG4:cam1 to stop acquisition.\n") sleep(0.5) else: self.cam1.image_mode.put("Multiple") print( f"Start taking {_nframes} images with {_exp} seconds of exposure\n" ) print(f"Press \"Stop\" on 1idPG4:cam1 to stop acquisition.\n") self.cam1.n_images.put(_nframes) sleep(0.5) # To be updated self.cam1.acquire.put(1)
class HDF5Plugin6IDD(HDF5Plugin): """AD HDF5 plugin customizations (properties)""" xml_file_name = ADComponent(EpicsSignalWithRBV, "XMLFileName")
class VarexTransformPlugin(TransformPlugin): """Add Types handle""" transformation_type = ADComponent(EpicsSignal, "Type")
class MyPcoCam(PcoDetectorCam): """PCO Dimax detector""" pco_cancel_dump = ADComponent(EpicsSignal, "pco_cancel_dump") pco_live_view = ADComponent(EpicsSignal, "pco_live_view") pco_trigger_mode = ADComponent(EpicsSignal, "pco_trigger_mode") pco_edge_fastscan = ADComponent(EpicsSignal, "pco_edge_fastscan") pco_is_frame_rate_mode = ADComponent(EpicsSignal, "pco_is_frame_rate_mode") pco_imgs2dump = ADComponent(EpicsSignalWithRBV, "pco_imgs2dump") pco_dump_counter = ADComponent(EpicsSignal, "pco_dump_counter") pco_dump_camera_memory = ADComponent(EpicsSignal, "pco_dump_camera_memory") pco_max_imgs_seg0 = ADComponent(EpicsSignalRO, "pco_max_imgs_seg0_RBV") pco_ready2acquire = ADComponent(EpicsSignal, "pco_ready2acquire") pco_set_frame_rate = ADComponent(EpicsSignal, "pco_set_frame_rate") # FIXME: ophyd has problem with trying to unstage the RBV value inside RE() image_mode = ADComponent(EpicsSignal, "ImageMode") num_images = ADComponent(EpicsSignal, "NumImages") acquire_time = ADComponent(EpicsSignal, "AcquireTime") frame_type = ADComponent(EpicsSignal, "FrameType")