def __init__(self, pv_files, macros): super().__init__(pv_files, macros) # On the A3200 we can read the number of encoder counts per rotation from the controller # Unfortunately the Ensemble does not support this pso_model = self.epics_pvs['PSOControllerModel'].get(as_string=True) if (pso_model == 'A3200'): pso_axis = self.epics_pvs['PSOAxisName'].get(as_string=True) self.epics_pvs['PSOCommand.BOUT'].put("UNITSTOCOUNTS(%s, 360.0)" % pso_axis, wait=True, timeout=10.0) reply = self.epics_pvs['PSOCommand.BINP'].get(as_string=True) counts_per_rotation = float(reply[1:]) self.epics_pvs['PSOCountsPerRotation'].put(counts_per_rotation) # Setting the pva servers to broadcast dark and flat fields if 'PvaStream' in self.pv_prefixes: prefix = self.pv_prefixes['PvaStream'] self.pva_stream_dark = pvaccess.PvObject({ 'value': [pvaccess.pvaccess.ScalarType.FLOAT], 'sizex': pvaccess.pvaccess.ScalarType.INT, 'sizey': pvaccess.pvaccess.ScalarType.INT }) self.pva_server_dark = pvaccess.PvaServer( prefix + 'StreamDarkFields', self.pva_stream_dark) self.pva_stream_flat = pvaccess.PvObject({ 'value': [pvaccess.pvaccess.ScalarType.FLOAT], 'sizex': pvaccess.pvaccess.ScalarType.INT, 'sizey': pvaccess.pvaccess.ScalarType.INT }) self.pva_server_flat = pvaccess.PvaServer( prefix + 'StreamFlatFields', self.pva_stream_flat) self.pva_stream_theta = pvaccess.PvObject({ 'value': [pvaccess.pvaccess.ScalarType.DOUBLE], 'sizex': pvaccess.pvaccess.ScalarType.INT }) self.pva_server_theta = pvaccess.PvaServer(prefix + 'StreamTheta', self.pva_stream_theta) self.stream_init()
def main(): server = pva.PvaServer() server.start() N_IMAGES = 100 NX = 1024 NY = 1024 COLOR_MODE = 0 IMAGE_RATE = 1.0 # Hz EXTRA_FIELDS_OBJECT = pva.PvObject( { 'customField1': pva.INT, 'customField2': pva.STRING }, { 'customField1': 10, 'customField2': 'GeneratedBy: PvaPy' }) CHANNEL = 'pvapy:image' for i in range(0, N_IMAGES): print('Image id: {}'.format(i)) image = createImage(i, NX, NY, COLOR_MODE, EXTRA_FIELDS_OBJECT) time.sleep(1 / IMAGE_RATE) if i == 0: server.addRecord(CHANNEL, image) else: server.update(CHANNEL, image)
def _start_pva_server(self): if self._pva_server is None: self._pva_server = pvaccess.PvaServer() # {mri: Endpoint} self._endpoints = {} for mri in self._published: self._add_new_pva_channel(mri) self._spawned = self.spawn(self._pva_server.startListener)
def __init__(self,stype): [ntheta,nz,n] = [1024,1024,1024] rate = 5*1024**3#GB/s buffer_size = 100000 # queue self.data_queue = queue.Queue(maxsize=buffer_size) self.epics_pvs = {} # pva type channel that contains projection and metadata image_pv_name = "2bmbSP2:Pva1:" self.epics_pvs['PvaPImage'] = pva.Channel(image_pv_name + 'Image') self.epics_pvs['PvaPDataType_RBV'] = pva.Channel(image_pv_name + 'DataType_RBV') self.pva_plugin_image = self.epics_pvs['PvaPImage'] # create pva type pv for reconstrucion by copying metadata from the data pv, but replacing the sizes # This way the ADViewer (NDViewer) plugin can be also used for visualizing reconstructions. pva_image_data = self.pva_plugin_image.get('') pva_image_dict = pva_image_data.getStructureDict() self.pv_rec = pva.PvObject(pva_image_dict) # run server for reconstruction pv recon_pva_name = "2bmb:Rec" if(stype=='server'): self.server_rec = pva.PvaServer(recon_pva_name, self.pv_rec) pva_image_data = self.pva_plugin_image.get('') width = pva_image_data['dimension'][0]['size'] height = pva_image_data['dimension'][1]['size'] self.pv_rec['dimension'] = [{'size': width, 'fullSize': width, 'binning': 1}, {'size': height, 'fullSize': height, 'binning': 1}] self.epics_pvs['PvaPImage'] = pva.Channel(recon_pva_name) self.pva_rec_image = self.epics_pvs['PvaPImage'] #self.pv_rec['value'] = ({'floatValue': rec.flatten()},) # self.theta = self.epics_pvs['ThetaArray'].get()[:self.epics_pvs['NumAngles'].get()] # start monitoring projection data datatype_list = self.epics_pvs['PvaPDataType_RBV'].get()['value'] self.datatype = datatype_list['choices'][datatype_list['index']].lower() self.datatype='uint16' self.buffer_size=buffer_size self.height=height self.width=width self.cur_id=0 self.tmp=np.zeros([height*width],dtype='uint16')
def streaming(theta, nthetap): """ Main computational function, take data from pvdata ('2bmbSP1:Pva1:Image'), reconstruct orthogonal slices and write the result to pvrec ('AdImage') """ # init streaming pv for the detector c = pva.Channel('2bmbSP1:Pva1:Image') pvdata = c.get('') # take dimensions n = pvdata['dimension'][0]['size'] nz = pvdata['dimension'][1]['size'] # init streaming pv for reconstrucion with copuing dictionary from pvdata pvdatad = pvdata.getStructureDict() pvrec = pva.PvObject(pvdatad) # set dimensions for data pvrec['dimension'] = [{ 'size': 3 * n, 'fullSize': 3 * n, 'binning': 1 }, { 'size': n, 'fullSize': n, 'binning': 1 }] s = pva.PvaServer('AdImage', pvrec) # init with slices through the middle ix = n // 2 iy = n // 2 iz = nz // 2 # I suggest using buffers that has only certain number of angles, # e.g. nhetap=50, this buffer is continuously update with monitoring # the detector pv (function addProjection), called inside pv monitor databuffer = np.zeros([nthetap, nz, n], dtype='float32') thetabuffer = np.zeros(nthetap, dtype='float32') def addProjection(pv): curid = pv['uniqueId'] databuffer[np.mod(curid, nthetap)] = pv['value'][0]['ubyteValue'].reshape( nz, n).astype('float32') thetabuffer[np.mod(curid, nthetap)] = theta[np.mod( curid, ntheta)] # take some theta with respect to id c.monitor(addProjection, '') # solver class on gpu with OrthoRec(nthetap, n, nz) as slv: # memory for result slices recall = np.zeros([n, 3 * n], dtype='float32') while (True): # infinite loop over angular partitions # new = take ix,iy,iz from gui new = None flgx, flgy, flgz = 0, 0, 0 # recompute slice or not if (new != None): [newx, newy, newz] = new if (newx != ix): ix = newx # change slice id flgx = 1 # recompute flg if (newy != iy): iy = newy flgy = 1 if (newz != iz): iz = newz flgz = 1 # take interlaced projections and corresponding angles # I guess there should be read/write locks here. datap = databuffer.copy() thetap = thetabuffer.copy() print('data partition norm:', np.linalg.norm(datap)) print('partition angles:', thetap) # recover 3 ortho slices recx, recy, recz = slv.rec_ortho(datap, thetap, n // 2, ix, iy, iz, flgx, flgy, flgz) # concatenate (supposing nz<n) recall[:nz, :n] = recx recall[:nz, n:2 * n] = recy recall[:, 2 * n:] = recz # 1s reconstruction rate time.sleep(1) # write to pv pvrec['value'] = ({'floatValue': recall.flatten()}, )
# these new pv is them served as 'AdImage'. # run it with: # # python -i test_03.py # # then from a terminal you can get the image with: # pvget AdImage | more import pvaccess as pva import numpy as np import time c = pva.Channel('2bmbSP1:Pva1:Image') pv1 = c.get('') pv1d = pv1.getStructureDict() print(pv1d) exit() # copy dictionaries for value and dimension fields pv2 = pva.PvObject(pv1d) image = (np.random.random([512, 256]) * 256).astype('float32') pv2['value'] = ({'floatValue': image}, ) # set dimensions for data pv2['dimension'] = [{'size':image.shape[0], 'fullSize':image.shape[0], 'binning':1},\ {'size':image.shape[1], 'fullSize':image.shape[0], 'binning':1}] s = pva.PvaServer('AdImage', pv2) while (True): image = (np.random.random([512, 256])).astype('float32').flatten() pv2['value'] = ({'floatValue': image}, ) time.sleep(1)
def __init__(self, pv_files, macros): log.setup_custom_logger("./tomostream.log") # init pvs self.config_pvs = {} self.control_pvs = {} self.pv_prefixes = {} if not isinstance(pv_files, list): pv_files = [pv_files] for pv_file in pv_files: self.read_pv_file(pv_file, macros) self.show_pvs() self.epics_pvs = {**self.config_pvs, **self.control_pvs} prefix = self.pv_prefixes['TomoScan'] # tomoscan pvs self.epics_pvs['FrameType'] = PV(prefix + 'FrameType') self.epics_pvs['NumAngles'] = PV(prefix + 'NumAngles') self.epics_pvs['RotationStep'] = PV(prefix + 'RotationStep') # Replace PSOPVPrefix to link to check a TomoScanStream PV so it returns if scan IOC is down # self.epics_pvs['PSOPVPrefix'] = PV(prefix + 'PSOPVPrefix') # if self.epics_pvs['PSOPVPrefix'].get(as_string=True) == None: # log.error("TomoScan is down") # log.error("Type exit() here and start TomoScan first") # return # pva type channel for flat and dark fields pv broadcasted from the detector machine self.epics_pvs['PvaDark'] = pva.Channel(self.epics_pvs['DarkPVAName'].get()) self.pva_dark = self.epics_pvs['PvaDark'] self.epics_pvs['PvaFlat'] = pva.Channel(self.epics_pvs['FlatPVAName'].get()) self.pva_flat = self.epics_pvs['PvaFlat'] self.epics_pvs['PvaTheta'] = pva.Channel(self.epics_pvs['ThetaPVAName'].get()) self.pva_theta = self.epics_pvs['PvaTheta'] # pva type channel that contains projection and metadata image_pv_name = PV(self.epics_pvs['ImagePVAPName'].get()).get() self.epics_pvs['PvaPImage'] = pva.Channel(image_pv_name + 'Image') self.epics_pvs['PvaPDataType_RBV'] = pva.Channel(image_pv_name + 'DataType_RBV') self.pva_plugin_image = self.epics_pvs['PvaPImage'] # create pva type pv for reconstrucion by copying metadata from the data pv, but replacing the sizes # This way the ADViewer (NDViewer) plugin can be also used for visualizing reconstructions. pva_image_data = self.pva_plugin_image.get('') pva_image_dict = pva_image_data.getStructureDict() self.pv_rec = pva.PvObject(pva_image_dict) # run server for reconstruction pv recon_pva_name = self.epics_pvs['ReconPVAName'].get() self.server_rec = pva.PvaServer(recon_pva_name, self.pv_rec) self.epics_pvs['StartRecon'].put('Done') self.epics_pvs['AbortRecon'].put('Yes') self.epics_pvs['StartRecon'].add_callback(self.pv_callback) self.epics_pvs['AbortRecon'].add_callback(self.pv_callback) # Set ^C, ^Z interrupt to abort the stream reconstruction signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGTSTP, self.signal_handler) # Start the watchdog timer thread thread = threading.Thread(target=self.reset_watchdog, args=(), daemon=True) thread.start()
def __init__(self, pv_files, macros): self.scan_is_running = False self.config_pvs = {} self.control_pvs = {} self.pv_prefixes = {} # These variables are set in begin_scan(). # They are used to prevent reading PVs repeatedly, and so that if the users changes # a PV during the scan it won't mess things up. self.exposure_time = None self.rotation_start = None self.rotation_step = None self.rotation_stop = None self.rotation_resolution = None self.max_rotation_speed = None self.return_rotation = None self.num_angles = None self.num_dark_fields = None self.dark_field_mode = None self.num_flat_fields = None self.flat_field_mode = None self.total_images = None self.file_path_rbv = None self.file_name_rbv = None self.file_number = None self.file_template = None if not isinstance(pv_files, list): pv_files = [pv_files] for pv_file in pv_files: self.read_pv_file(pv_file, macros) if 'Rotation' not in self.control_pvs: log.error('RotationPVName must be present in autoSettingsFile') sys.exit() if 'Camera' not in self.pv_prefixes: log.error('CameraPVPrefix must be present in autoSettingsFile') sys.exit() if 'FilePlugin' not in self.pv_prefixes: log.error('FilePluginPVPrefix must be present in autoSettingsFile') sys.exit() #Define PVs we will need from the rotation motor, which is on another IOC rotation_pv_name = self.control_pvs['Rotation'].pvname self.control_pvs['RotationSpeed'] = PV(rotation_pv_name + '.VELO') self.control_pvs['RotationMaxSpeed'] = PV(rotation_pv_name + '.VMAX') self.control_pvs['RotationResolution'] = PV(rotation_pv_name + '.MRES') self.control_pvs['RotationSet'] = PV(rotation_pv_name + '.SET') self.control_pvs['RotationStop'] = PV(rotation_pv_name + '.STOP') self.control_pvs['RotationDmov'] = PV(rotation_pv_name + '.DMOV') self.control_pvs['RotationDirection'] = PV(rotation_pv_name + '.DIR') #Define PVs from the camera IOC that we will need prefix = self.pv_prefixes['Camera'] camera_prefix = prefix + 'cam1:' self.control_pvs['CamManufacturer'] = PV(camera_prefix + 'Manufacturer_RBV') self.control_pvs['CamModel'] = PV(camera_prefix + 'Model_RBV') self.control_pvs['CamAcquire'] = PV(camera_prefix + 'Acquire') self.control_pvs['CamAcquireBusy'] = PV(camera_prefix + 'AcquireBusy') self.control_pvs['CamImageMode'] = PV(camera_prefix + 'ImageMode') self.control_pvs['CamTriggerMode'] = PV(camera_prefix + 'TriggerMode') self.control_pvs['CamNumImages'] = PV(camera_prefix + 'NumImages') self.control_pvs['CamNumImagesCounter'] = PV(camera_prefix + 'NumImagesCounter_RBV') self.control_pvs['CamAcquireTime'] = PV(camera_prefix + 'AcquireTime') self.control_pvs['CamAcquireTimeRBV'] = PV(camera_prefix + 'AcquireTime_RBV') self.control_pvs['CamBinX'] = PV(camera_prefix + 'BinX') self.control_pvs['CamBinY'] = PV(camera_prefix + 'BinY') self.control_pvs['CamWaitForPlugins'] = PV(camera_prefix + 'WaitForPlugins') self.control_pvs['PortNameRBV'] = PV(camera_prefix + 'PortName_RBV') # If this is a Point Grey camera then assume we are running ADSpinnaker # and create some PVs specific to that driver manufacturer = self.control_pvs['CamManufacturer'].get(as_string=True) model = self.control_pvs['CamModel'].get(as_string=True) if (manufacturer.find('Point Grey') != -1) or (manufacturer.find('FLIR') != -1): self.control_pvs['CamExposureMode'] = PV(camera_prefix + 'ExposureMode') self.control_pvs['CamTriggerOverlap'] = PV(camera_prefix + 'TriggerOverlap') self.control_pvs['CamPixelFormat'] = PV(camera_prefix + 'PixelFormat') self.control_pvs['CamArrayCallbacks'] = PV(camera_prefix + 'ArrayCallbacks') self.control_pvs['CamFrameRateEnable'] = PV(camera_prefix + 'FrameRateEnable') self.control_pvs['CamTriggerSource'] = PV(camera_prefix + 'TriggerSource') if model.find('Grasshopper3') != -1: self.control_pvs['CamVideoMode'] = PV(camera_prefix + 'GC_VideoMode_RBV') # Set some initial PV values self.control_pvs['CamWaitForPlugins'].put('Yes') self.control_pvs['StartScan'].put(0) prefix = self.pv_prefixes['FilePlugin'] self.control_pvs['FPNDArrayPort'] = PV(prefix + 'NDArrayPort') self.control_pvs['FPFileWriteMode'] = PV(prefix + 'FileWriteMode') self.control_pvs['FPNumCapture'] = PV(prefix + 'NumCapture') self.control_pvs['FPNumCaptured'] = PV(prefix + 'NumCaptured_RBV') self.control_pvs['FPCapture'] = PV(prefix + 'Capture') self.control_pvs['FPCaptureRBV'] = PV(prefix + 'Capture_RBV') self.control_pvs['FPFilePath'] = PV(prefix + 'FilePath') self.control_pvs['FPFilePathRBV'] = PV(prefix + 'FilePath_RBV') self.control_pvs['FPFilePathExists'] = PV(prefix + 'FilePathExists_RBV') self.control_pvs['FPFileName'] = PV(prefix + 'FileName') self.control_pvs['FPFileNameRBV'] = PV(prefix + 'FileName_RBV') self.control_pvs['FPFileNumber'] = PV(prefix + 'FileNumber') self.control_pvs['FPAutoIncrement'] = PV(prefix + 'AutoIncrement') self.control_pvs['FPFileTemplate'] = PV(prefix + 'FileTemplate') self.control_pvs['FPFullFileName'] = PV(prefix + 'FullFileName_RBV') self.control_pvs['FPAutoSave'] = PV(prefix + 'AutoSave') self.control_pvs['FPEnableCallbacks'] = PV(prefix + 'EnableCallbacks') # Set some initial PV values file_path = self.config_pvs['FilePath'].get(as_string=True) self.control_pvs['FPFilePath'].put(file_path) file_name = self.config_pvs['FileName'].get(as_string=True) self.control_pvs['FPFileName'].put(file_name) self.control_pvs['FPAutoSave'].put('No') self.control_pvs['FPFileWriteMode'].put('Stream') self.control_pvs['FPEnableCallbacks'].put('Enable') #Define PVs from the MCS or PSO that live on another IOC if 'MCS' in self.pv_prefixes: prefix = self.pv_prefixes['MCS'] self.control_pvs['MCSEraseStart'] = PV(prefix + 'EraseStart') self.control_pvs['MCSStopAll'] = PV(prefix + 'StopAll') self.control_pvs['MCSPrescale'] = PV(prefix + 'Prescale') self.control_pvs['MCSDwell'] = PV(prefix + 'Dwell') self.control_pvs['MCSLNEOutputWidth'] = PV(prefix + 'LNEOutputWidth') self.control_pvs['MCSChannelAdvance'] = PV(prefix + 'ChannelAdvance') self.control_pvs['MCSMaxChannels'] = PV(prefix + 'MaxChannels') self.control_pvs['MCSNuseAll'] = PV(prefix + 'NuseAll') if 'PSO' in self.pv_prefixes: prefix = self.pv_prefixes['PSO'] self.control_pvs['PSOscanDelta'] = PV(prefix + 'scanDelta') self.control_pvs['PSOstartPos'] = PV(prefix + 'startPos') self.control_pvs['PSOendPos'] = PV(prefix + 'endPos') self.control_pvs['PSOslewSpeed'] = PV(prefix + 'slewSpeed') self.control_pvs['PSOtaxi'] = PV(prefix + 'taxi') self.control_pvs['PSOfly'] = PV(prefix + 'fly') self.control_pvs['PSOscanControl'] = PV(prefix + 'scanControl') self.control_pvs['PSOcalcProjections'] = PV(prefix + 'numTriggers') self.control_pvs['ThetaArray'] = PV(prefix + 'motorPos.AVAL') if 'PvaPlugin' in self.pv_prefixes: prefix = self.pv_prefixes['PvaPlugin'] self.control_pvs['PVANDArrayPort'] = PV(prefix + 'NDArrayPort') self.control_pvs['PVAEnableCallbacks'] = PV(prefix + 'EnableCallbacks') if 'RoiPlugin' in self.pv_prefixes: prefix = self.pv_prefixes['RoiPlugin'] self.control_pvs['ROINDArrayPort'] = PV(prefix + 'NDArrayPort') self.control_pvs['ROIScale'] = PV(prefix + 'Scale') self.control_pvs['ROIBinX'] = PV(prefix + 'BinX') self.control_pvs['ROIBinY'] = PV(prefix + 'BinY') self.control_pvs['ROIEnableCallbacks'] = PV(prefix + 'EnableCallbacks') if 'CbPlugin' in self.pv_prefixes: prefix = self.pv_prefixes['CbPlugin'] self.control_pvs['CBPortNameRBV'] = PV(prefix + 'PortName_RBV') self.control_pvs['CBNDArrayPort'] = PV(prefix + 'NDArrayPort') self.control_pvs['CBPreCount'] = PV(prefix + 'PreCount') self.control_pvs['CBPostCount'] = PV(prefix + 'PostCount') self.control_pvs['CBCapture'] = PV(prefix + 'Capture') self.control_pvs['CBCaptureRBV'] = PV(prefix + 'Capture_RBV') self.control_pvs['CBTrigger'] = PV(prefix + 'Trigger') self.control_pvs['CBTriggerRBV'] = PV(prefix + 'Trigger_RBV') self.control_pvs['CBCurrentQtyRBV'] = PV(prefix + 'CurrentQty_RBV') self.control_pvs['CBEnableCallbacks'] = PV(prefix + 'EnableCallbacks') self.control_pvs['CBStatusMessage'] = PV(prefix + 'StatusMessage') self.epics_pvs = {**self.config_pvs, **self.control_pvs} # Wait 1 second for all PVs to connect time.sleep(1) self.check_pvs_connected() # Setting the pva servers to broadcast dark and flat fields if 'PvaStream' in self.pv_prefixes: prefix = self.pv_prefixes['PvaStream'] self.pva_stream_dark = pvaccess.PvObject({ 'value': [pvaccess.pvaccess.ScalarType.FLOAT], 'sizex': pvaccess.pvaccess.ScalarType.INT, 'sizey': pvaccess.pvaccess.ScalarType.INT }) self.pva_server_dark = pvaccess.PvaServer(prefix + 'dark', self.pva_stream_dark) self.pva_stream_flat = pvaccess.PvObject({ 'value': [pvaccess.pvaccess.ScalarType.FLOAT], 'sizex': pvaccess.pvaccess.ScalarType.INT, 'sizey': pvaccess.pvaccess.ScalarType.INT }) self.pva_server_flat = pvaccess.PvaServer(prefix + 'flat', self.pva_stream_flat) # Configure callbacks on a few PVs for epics_pv in ('MoveSampleIn', 'MoveSampleOut', 'StartScan', 'AbortScan', 'ExposureTime', 'FilePath', 'FPFilePathExists'): self.epics_pvs[epics_pv].add_callback(self.pv_callback) # Synchronize the FilePathExists PV self.copy_file_path_exists() # Set ^C interrupt to abort the scan signal.signal(signal.SIGINT, self.signal_handler) # Start the watchdog timer thread thread = threading.Thread(target=self.reset_watchdog, args=(), daemon=True) thread.start()
def create_pva_server(self): #self.log_debug("Creating PVA server object") self._server = pvaccess.PvaServer()
import pvaccess as pva ARRAY_SIZE = 10 * 1024 N_FLAT_ARRAYS = 100 dataStruct = { 'ArrayId': pva.UINT, 'Time': [pva.DOUBLE], 'Sinusoid': [pva.FLOAT], 'Triangle': [pva.FLOAT], } for i in range(0, N_FLAT_ARRAYS): arrayName = 'Flat%03d' % (i + 1) dataStruct[arrayName] = [pva.FLOAT] srv = pva.PvaServer('server1_data', pva.PvObject(dataStruct)) t0 = 0.0 n = 0 dt = 1. / 1000 startTime = unixTime.time() # Time is double (8 bytes), other arrays are floats (4 bytes each) dataSize = ARRAY_SIZE * (8 + N_FLAT_ARRAYS * 4) dataSizeMB = dataSize / (1024 * 1024) while True: time = [t0 + dt * i for i in range(0, ARRAY_SIZE)] # Disable sinusoid/triangle for performance measurements #sinusoid = [sin(2*pi*1.1*t + pi/2) for t in time] sinusoid = [] #triangle = [(2/pi)*asin(sin(2*pi*1.1*t)) for t in time] triangle = []
def streaming(): """ Main computational function, take data from pvdata ('2bmbSP1:Pva1:Image'), reconstruct orthogonal slices and write the result to pvrec ('AdImage') """ ##### init pvs ###### # init pvs for the streaming GUI # orthoslices chStreamX = pva.Channel('2bmS1:StreamX', pva.CA) chStreamY = pva.Channel('2bmS1:StreamY', pva.CA) chStreamZ = pva.Channel('2bmS1:StreamZ', pva.CA) # frame type chStreamFrameType = pva.Channel('2bma:TomoScan:FrameType', pva.CA) # theta array chStreamThetaArray = pva.Channel('2bma:PSOFly2:motorPos.AVAL', pva.CA) # total number of fly scan angles chStreamNumAngles = pva.Channel('2bma:TomoScan:NumAngles', pva.CA) # total number of dark fields chStreamNumDarkFields = pva.Channel('2bma:TomoScan:NumDarkFields', pva.CA) # total number of flat fields chStreamNumFlatFields = pva.Channel('2bma:TomoScan:NumFlatFields', pva.CA) # dark field mode chStreamDarkFieldMode = pva.Channel('2bma:TomoScan:DarkFieldMode', pva.CA) # flat field mode chStreamFlatFieldMode = pva.Channel('2bma:TomoScan:FlatFieldMode', pva.CA) # NEW: buffer size for for projections #chStreamBS = pva.Channel('2bmS1:StreamBS', pva.CA) # NEW: rotation center #chStreamRC = pva.Channel('2bmS1:StreamRC', pva.CA) ## init pva streaming pv for the detector # NEW: PV channel that contains projection and metadata (angle, flag: regular, flat or dark) chdata = pva.Channel('2bmbSP1:Pva1:Image') pvdata = chdata.get('') # init pva streaming pv for reconstrucion with coping dictionary from pvdata pvdict = pvdata.getStructureDict() pvrec = pva.PvObject(pvdict) # take dimensions n = pvdata['dimension'][0]['size'] nz = pvdata['dimension'][1]['size'] # set dimensions for reconstruction pvrec['dimension'] = [{ 'size': 3 * n, 'fullSize': 3 * n, 'binning': 1 }, { 'size': n, 'fullSize': n, 'binning': 1 }] ##### run server for reconstruction pv ##### # NEW: replace AdImage by a new name for Reconstruction PV, e.g. 2bmS1:StreamREC s = pva.PvaServer('AdImage', pvrec) ##### procedures before running fly ####### # 0) form circular buffer, whenever the angle goes higher than 180 # than corresponding projection is replacing the first one ntheta = 180 #chStreamBS.get('')['value'] nflatinit = chStreamNumFlatFields.get('')['value'] ndarkinit = chStreamNumDarkFields.get('')['value'] databuffer = np.zeros([ntheta, nz * n], dtype='uint8') flatbuffer = np.zeros([nflatinit, nz * n], dtype='uint8') darkbuffer = np.zeros([ndarkinit, nz * n], dtype='uint8') thetabuffer = np.zeros(ntheta, dtype='float32') # Load angles theta = chStreamThetaArray.get( '')['value'][:chStreamNumAngles.get('')['value']] # find first id of the projection data, skipping ids for dadrk and flat fields flatmodeall = chStreamFlatFieldMode.get('')['value'] flatmode = flatmodeall['choices'][flatmodeall['index']] darkmodeall = chStreamDarkFieldMode.get('')['value'] darkmode = darkmodeall['choices'][darkmodeall['index']] ## the following is not needed, since the id is set to zero before acquiring pojections # firstid = chdata.get('')['uniqueId'] # if(flatmode == 'Start' or flatmode=='Both'): # firstid += chStreamNumFlatFields.get('')['value'] # if(darkmode == 'Start' or darkmode=='Both'): # firstid += chStreamNumDarkFields.get('')['value'] # print('first projection id', firstid) nflat = 0 ndark = 0 nproj = 0 # number of streamed images of each type def addData(pv): """ read data from the detector, 3 types: flat, dark, projection""" nonlocal nflat nonlocal ndark nonlocal nproj #with mrwlock.w_locked(): curid = pv['uniqueId'] ftypeall = chStreamFrameType.get('')['value'] ftype = ftypeall['choices'][ftypeall['index']] if (ftype == 'FlatField'): flatbuffer[nflat] = pv['value'][0]['ubyteValue'] nflat += 1 if (ftype == 'DarkField'): darkbuffer[ndark] = pv['value'][0]['ubyteValue'] ndark += 1 if (ftype == 'Projection'): databuffer[np.mod(nproj, ntheta)] = pv['value'][0]['ubyteValue'] thetabuffer[np.mod(nproj, ntheta)] = theta[curid - 1] nproj += 1 print('add:', ftype, 'id:', curid) # start monitoring projection data chdata.monitor(addData, '') # create solver class on GPU slv = OrthoRec(ntheta, n, nz) # allocate memory for result slices recall = np.zeros([n, 3 * n], dtype='float32') # wait until dark and flats are acquired while (nproj == 0): 1 # init data as flat databuffer[:] = flatbuffer[0] print(databuffer.shape) # copy dark and flat to GPU print('copy dark and flat to GPU') slv.set_flat(np.mean(flatbuffer[:nflat], axis=0)) slv.set_dark(np.mean(darkbuffer[:ndark], axis=0)) ##### streaming reconstruction ###### while (True): #with mrwlock.r_locked(): # lock buffer before reading datap = databuffer.copy() thetap = thetabuffer.copy() # take 3 ortho slices ids idx = chStreamX.get('')['value'] idy = chStreamY.get('')['value'] idz = chStreamZ.get('')['value'] # NEW: center center = 1224 #chStreamC.get('')['value'] # print(thetap) # reconstruct on GPU #tic() recx, recy, recz = slv.rec_ortho(datap, thetap * np.pi / 180, center, idx, idy, idz) #print('rec time:',toc(),'norm',np.linalg.norm(recx)) # concatenate (supposing nz<n) recall[:nz, :n] = recx recall[:nz, n:2 * n] = recy recall[:, 2 * n:] = recz # write to pv pvrec['value'] = ({'floatValue': recall.flatten()}, ) # reconstruction rate limit time.sleep(0.1)
#!/usr/bin/env python import pvaccess as pva s = pva.PvaServer('foo', pva.PvObject({'value': pva.INT})) c = pva.Channel('foo') c.get() s.removeRecord('foo')
def streaming(): """ Main computational function, take data from pvdata ('2bmbSP1:Pva1:Image'), reconstruct orthogonal slices and write the result to pvrec ('AdImage') """ ##### init pvs ###### # init ca pvs chscanDelta = pva.Channel('2bma:PSOFly2:scanDelta', pva.CA) chrotangle = pva.Channel('2bma:m82', pva.CA) chrotangleset = pva.Channel('2bma:m82.SET', pva.CA) chrotanglestop = pva.Channel('2bma:m82.STOP', pva.CA) chStreamX = pva.Channel('2bmS1:StreamX', pva.CA) chStreamY = pva.Channel('2bmS1:StreamY', pva.CA) chStreamZ = pva.Channel('2bmS1:StreamZ', pva.CA) # init pva streaming pv for the detector chdata = pva.Channel('2bmbSP1:Pva1:Image') pvdata = chdata.get('') # init pva streaming pv for reconstrucion with coping dictionary from pvdata pvdict = pvdata.getStructureDict() pvrec = pva.PvObject(pvdict) # take dimensions n = pvdata['dimension'][0]['size'] nz = pvdata['dimension'][1]['size'] # set dimensions for reconstruction pvrec['dimension'] = [{ 'size': 3 * n, 'fullSize': 3 * n, 'binning': 1 }, { 'size': n, 'fullSize': n, 'binning': 1 }] ##### run server for reconstruction pv ##### s = pva.PvaServer('AdImage', pvrec) ##### procedures before running fly ####### # 0) form circular buffer, whenever the angle goes higher than 180 # than corresponding projection is replacing the first one scanDelta = chscanDelta.get('')['value'] ntheta = np.int(180 / scanDelta + 0.5) databuffer = np.zeros([ntheta, nz * n], dtype='uint8') thetabuffer = np.zeros(ntheta, dtype='float32') # 1) stop rotation, replace rotation stage angle to a value in [0,360) chrotanglestop.put(1) time.sleep(3) rotangle = chrotangle.get('')['value'] chrotangleset.put(1) chrotangle.put(rotangle - rotangle // 360 * 360) chrotangleset.put(0) # 2) take flat field flat = takeflat(chdata) firstid = chdata.get('')['uniqueId'] # 3) create solver class on GPU, and copy flat field to gpu slv = OrthoRec(ntheta, n, nz) slv.set_flat(flat) # 4) allocate memory for result slices recall = np.zeros([n, 3 * n], dtype='float32') # 5) start monitoring the detector pv for data collection def addProjection(pv): with mrwlock.w_locked(): curid = pv['uniqueId'] databuffer[np.mod(curid, ntheta)] = pv['value'][0]['ubyteValue'] thetabuffer[np.mod(curid, ntheta)] = (curid - firstid) * scanDelta #print(firstid, curid) chdata.monitor(addProjection, '') ##### start acquisition ####### start_fly() ##### streaming reconstruction ###### while (True): # infinite loop over angular partitions with mrwlock.r_locked(): # lock buffer before reading datap = databuffer.copy() thetap = thetabuffer.copy() # take 3 ortho slices ids idx = chStreamX.get('')['value'] idy = chStreamY.get('')['value'] idz = chStreamZ.get('')['value'] # reconstruct on GPU tic() recx, recy, recz = slv.rec_ortho(datap, thetap * np.pi / 180, n // 2, idx, idy, idz) print('rec time:', toc()) # concatenate (supposing nz<n) recall[:nz, :n] = recx recall[:nz, n:2 * n] = recy recall[:, 2 * n:] = recz # write to pv pvrec['value'] = ({'floatValue': recall.flatten()}, ) # reconstruction rate limit time.sleep(0.1)