def hsd_unconfig(prefix): global epics_prefix epics_prefix = prefix ctxt = Context('pva') values = ctxt.get(epics_prefix + ':CONFIG') values['enable'] = 0 print(values) print(epics_prefix) ctxt.put(epics_prefix + ':CONFIG', values, wait=True) # This handshake seems to be necessary, or at least the .get() complete = False for i in range(100): complete = ctxt.get(epics_prefix + ':READY') != 0 if complete: break print('hsd_unconfig wait for complete', i) time.sleep(0.1) if complete: print('hsd unconfig complete') else: raise Exception('timed out waiting for hsd_unconfig') ctxt.close() return None
def wave8_unconfig(epics_prefix): return None; ctxt = Context('pva') ctxt.put(epics_prefix+':TriggerEventManager:TriggerEventBuffer[0]:MasterEnable', 0, wait=True) ctxt.close() return None;
def test_constant_variable_pva(value, prefix, server, model): ctxt = Context("pva", conf=PVA_CONFIG, maxsize=2) #check constant variable assignment for _, variable in model.input_variables.items(): pvname = f"{prefix}:{variable.name}" if variable.variable_type == "scalar": count = 3 successful_put = False while count > 0 and not successful_put: try: ctxt.put(pvname, value) successful_put = True except: ctxt.close() del ctxt time.sleep(3) ctxt = Context("pva", conf=PVA_CONFIG) count -= 1 if count == 0: raise Exception("Failed puts.") for _, variable in model.input_variables.items(): if variable.variable_type == "scalar": pvname = f"{prefix}:{variable.name}" count = 3 successful_get = False val = None while count > 0 and not successful_get: try: val = ctxt.get(pvname) successful_get = True except: ctxt.close() del ctxt time.sleep(5) ctxt = Context("pva", conf=PVA_CONFIG) time.sleep(1) count -= 1 if count == 0: raise Exception("Failed gets.") if variable.is_constant: assert val != value else: assert val == value ctxt.close()
def capvput(pvname, value, wait=False, timeout=5.0): global ctxt #channel = pvaccess.Channel(pvname, pvaccess.CA) #return channel.putFloat(value) if pvname.startswith('pva://'): if ctxt is None: from p4p.client.thread import Context ctxt = Context('pva') ctxt.put(pvname[6:], value, timeout=timeout, wait=wait) else: epics.caput(pvname, value, timeout=timeout, wait=wait)
class TranControl(object): def __init__(self, args): print('__init__') self.args = args self.pv_xpm_base = 'DAQ:LAB2:XPM:%d' % args.x self.pvListMsgHeader = [] # filled in at alloc for g in range(8): if self.args.p & (1 << g): self.pvListMsgHeader.append(self.pv_xpm_base + ":PART:" + str(g) + ':MsgHeader') self.pvGroupMsgInsert = self.pv_xpm_base + ':GroupMsgInsert' self.ctxt = Context('pva', nt=None) def pv_put(self, pvName, val): retval = False try: self.ctxt.put(pvName, val) except TimeoutError: self.report_error("self.ctxt.put('%s', %d) timed out" % (pvName, val)) except Exception: self.report_error("self.ctxt.put('%s', %d) failed" % (pvName, val)) else: retval = True logging.debug("self.ctxt.put('%s', %d)" % (pvName, val)) return retval def run(self): print('run') self.groups = self.args.p self.pv_put(self.pvGroupMsgInsert, self.args.p) timer = 1. / self.args.r def expired(): self.pv_put(self.pvGroupMsgInsert, self.args.p) self.transitions = Timer(timer, expired) self.transitions.start() self.transitions = Timer(timer, expired) self.transitions.start() while (True): time.sleep(1)
def wave8_config(prefix, connect_str, cfgtype, detname, detsegm, grp): global ctxt global lane global group group = grp cfg = get_config(connect_str, cfgtype, detname, detsegm) ocfg = cfg ctxt = Context('pva') epics_prefix = prefix + ':Top:' user_to_expert(ctxt, epics_prefix, cfg, full=True) # Assert clears names_clr = [ epics_prefix + 'BatcherEventBuilder:Blowoff', epics_prefix + 'TimingFrameRx:RxCountReset', epics_prefix + 'RawBuffers:CntRst', epics_prefix + 'Integrators:CntRst' ] values = [1] * len(names_clr) print('names {:}'.format(names_clr)) ctxt.put(names_clr, values) config_expert(ctxt, epics_prefix, cfg['expert']) ctxt.put(epics_prefix + 'TriggerEventManager:TriggerEventBuffer[%d]:MasterEnable' % lane, 1, wait=True) time.sleep(0.2) # Deassert clears values = [0] * len(names_clr) print('names {:}'.format(names_clr)) ctxt.put(names_clr, values) ctxt.put(epics_prefix + 'BatcherEventBuilder:Blowoff', 0, wait=True) cfg['firmwareVersion'] = ctxt.get(epics_prefix + 'AxiVersion:FpgaVersion').raw.value cfg['firmwareBuild'] = ctxt.get(epics_prefix + 'AxiVersion:BuildStamp').raw.value ctxt.close() v = json.dumps(cfg) return v
def ts_config(connect_json, cfgtype, detname): cfg = get_config(connect_json, cfgtype, detname) connect_info = json.loads(connect_json) # get the list of readout groups that the user has selected # so we only configure those readout_groups = [] connect_info = json.loads(connect_json) for nodes in connect_info['body']['drp'].values(): readout_groups.append(nodes['det_info']['readout']) readout_groups = set(readout_groups) control_info = connect_info['body']['control']['0']['control_info'] xpm_master = control_info['xpm_master'] pv_prefix = control_info['pv_base'] + ':XPM:' + str(xpm_master) + ':PART:' # this structure of epics variable names must mirror # the configdb. alternatively, we could consider # putting these in the configdb, perhaps as readonly fields. # only do a few of these for now, since Matt is switching # to rogue pvtable = {} mydict = {} for group in readout_groups: grp_prefix = 'group' + str(group) grp = cfg[grp_prefix] pvtable[grp_prefix] = { 'trigMode': 'L0Select', 'delay': 'L0Delay', 'fixed': { 'rate': 'L0Select_FixedRate' }, 'ac': { 'rate': 'L0Select_ACRate' }, 'seq': { 'mode': 'L0Select_Sequence' }, 'destination': { 'select': 'DstSelect' }, } epics_names_values(group, pvtable, cfg, mydict) # handle special cases that don't work in the "pvtable" paradigm # convert ac.ts0 through ac.ts5 to L0Select_ACTimeslot bitmask tsmask = 0 for tsnum in range(6): tsval = grp['ac']['ts' + str(tsnum)] tsmask |= 1 << tsval mydict[str(group) + ':L0Select_ACTimeslot'] = tsmask # L0Select_SeqBit is one var used by all of seq.(burst/fixed/local) if grp['seq']['mode'] == 15: # burst seqbit = grp['seq']['burst']['mode'] elif grp['seq']['mode'] == 16: # fixed rate seqbit = grp['seq']['fixed']['rate'] elif grp['seq']['mode'] == 17: # local seqbit = grp['seq']['local']['rate'] else: raise ValueError('Illegal value for trigger sequence mode') mydict[str(group) + ':L0Select_SeqBit'] = seqbit # DstSelect_Mask should come from destination.dest0 through dest15 dstmask = 0 for dstnum in range(16): dstval = grp['destination']['dest' + str(dstnum)] if dstval: dstmask |= 1 << dstnum mydict[str(group) + ':DstSelect_Mask'] = dstmask # 4 InhEnable/InhInterval/InhLimit for inhnum in range(4): mydict[str(group) + ':InhInterval' + str(inhnum)] = grp['inhibit' + str(inhnum)]['interval'] mydict[str(group) + ':InhLimit' + str(inhnum)] = grp['inhibit' + str(inhnum)]['limit'] mydict[str(group) + ':InhEnable' + str(inhnum)] = grp['inhibit' + str(inhnum)]['enable'] names = list(mydict.keys()) values = list(mydict.values()) names = [pv_prefix + n for n in names] print('TS config names and values:', names, values) # program the values ctxt = Context('pva') ctxt.put(names, values) ctxt.close() return json.dumps(cfg)
def hsd_config(connect_str, epics_prefix, cfgtype, detname, group): cfg = get_config(connect_str, cfgtype, detname) # this structure of epics variable names must mirror # the configdb. alternatively, we could consider # putting these in the configdb, perhaps as readonly fields. pvtable = { 'enable': 'enable', 'raw': { 'start': 'raw_start', 'gate': 'raw_gate', 'prescale': 'raw_prescale' }, 'fex': { 'start': 'fex_start', 'gate': 'fex_gate', 'prescale': 'fex_prescale', 'ymin': 'fex_ymin', 'ymax': 'fex_ymax', 'xpre': 'fex_xpre', 'xpost': 'fex_xpost' }, 'expert': { 'datamode': 'test_pattern', 'fullthresh': 'full_event', 'fullsize': 'full_size', 'fsrange': 'fs_range_vpp' }, } # look in the cfg dictionary for values that match the epics # variables in the pvtable values = {} epics_names_values(pvtable, cfg, values) values['readoutGroup'] = group # program the values ctxt = Context('pva') print(epics_prefix) ctxt.put(epics_prefix + ':READY', 0, wait=True) print(values) ctxt.put(epics_prefix + ':CONFIG', values, wait=True) # the completion of the "put" guarantees that all of the above # have completed (although in no particular order) complete = False for i in range(100): complete = ctxt.get(epics_prefix + ':READY') != 0 if complete: break print('hsd config wait for complete', i) time.sleep(0.1) if complete: print('hsd config complete') else: raise Exception('timed out waiting for hsd configure') ctxt.close() return json.dumps(cfg)
class CustomController: """ Controller class used to access process variables. Controllers are used for interfacing with both Channel Access and pvAccess process variables. The controller object is initialized using a single protocol has methods for both getting and setting values on the process variables. Attributes: protocol (str): Protocol for getting values from variables ("pva" for pvAccess, "ca" for Channel Access) context (Context): P4P threaded context instance for use with pvAccess. set_ca (bool): Update Channel Access variable on put. set_pva (bool): Update pvAccess variable on put. pv_registry (dict): Registry mapping pvname to dict of value and pv monitor Example: ``` # create PVAcess controller controller = Controller("pva") value = controller.get_value("scalar_input") image_value = controller.get_image("image_input") controller.close() ``` """ def __init__(self, protocol: str, prefix, track_inputs: bool = False, input_pvs: list = None): """ Initializes controller. Stores protocol and creates context attribute if using pvAccess. Args: protocol (str): Protocol for getting values from variables ("pva" for pvAccess, "ca" for Channel Access) """ self.protocol = protocol self.last_update = "" self.pv_registry = defaultdict() self.track_inputs = track_inputs self.input_pvs = [f"{prefix}:{variable}" for variable in input_pvs] self.prefix = prefix # initalize context for pva self.context = None if self.protocol == "pva": self.context = Context("pva") def ca_value_callback(self, pvname, value, *args, **kwargs): """Callback executed by Channel Access monitor. Args: pvname (str): Process variable name value (Union[np.ndarray, float]): Value to assign to process variable. """ self.pv_registry[pvname]["value"] = value if self.track_inputs: if pvname in self.input_pvs: self.last_update = datetime.now().strftime( '%m/%d/%Y, %H:%M:%S') def ca_connection_callback(self, *, pvname, conn, pv): """Callback used for monitoring connection and setting values to None on disconnect. """ # if disconnected, set value to None if not conn: self.pv_registry[pvname]["value"] = None def pva_value_callback(self, pvname, value): """Callback executed by pvAccess monitor. Args: pvname (str): Process variable name value (Union[np.ndarray, float]): Value to assign to process variable. """ if isinstance(value, Disconnected): self.pv_registry[pvname]["value"] = None else: self.pv_registry[pvname]["value"] = value if self.track_inputs: if pvname in self.input_pvs: self.last_update = datetime.now().strftime( '%m/%d/%Y, %H:%M:%S') def setup_pv_monitor(self, pvname): """Set up process variable monitor. Args: pvname (str): Process variable name """ if pvname in self.pv_registry: return if self.protocol == "ca": # add to registry (must exist for connection callback) self.pv_registry[pvname] = {"pv": None, "value": None} # create the pv pv_obj = PV(pvname, callback=self.ca_value_callback, connection_callback=self.ca_connection_callback) # update registry self.pv_registry[pvname]["pv"] = pv_obj elif self.protocol == "pva": cb = partial(self.pva_value_callback, pvname) # populate registry s.t. initially disconnected will populate self.pv_registry[pvname] = {"pv": None, "value": None} # create the monitor obj mon_obj = self.context.monitor(pvname, cb, notify_disconnect=True) # update registry with the monitor self.pv_registry[pvname]["pv"] = mon_obj def get(self, pvname: str) -> np.ndarray: """ Accesses and returns the value of a process variable. Args: pvname (str): Process variable name """ self.setup_pv_monitor(pvname) pv = self.pv_registry.get(pvname, None) if pv: #return pv.get("value", None) return pv["value"] return None def get_value(self, pvname): """Gets scalar value of a process variable. Args: pvname (str): Image process variable name. """ value = self.get(pvname) if value is None: value = DEFAULT_SCALAR_VALUE return value def get_image(self, pvname) -> dict: """Gets image data via controller protocol. Args: pvname (str): Image process variable name """ image = None if self.protocol == "ca": image_flat = self.get(f"{pvname}:ArrayData_RBV") nx = self.get(f"{pvname}:ArraySizeX_RBV") ny = self.get(f"{pvname}:ArraySizeY_RBV") x = self.get(f"{pvname}:MinX_RBV") y = self.get(f"{pvname}:MinY_RBV") x_max = self.get(f"{pvname}:MaxX_RBV") y_max = self.get(f"{pvname}:MaxY_RBV") if all([ image_def is not None for image_def in [image_flat, nx, ny, x, y, x_max, y_max] ]): dw = x_max - x dh = y_max - y image = image_flat.reshape(int(nx), int(ny)) elif self.protocol == "pva": # context returns numpy array with WRITEABLE=False # copy to manipulate array below image = self.get(pvname) if image is not None: attrib = image.attrib x = attrib["x_min"] y = attrib["y_min"] dw = attrib["x_max"] - attrib["x_min"] dh = attrib["y_max"] - attrib["y_min"] image = copy.copy(image) if image is not None: return { "image": [image], "x": [x], "y": [y], "dw": [dw], "dh": [dh], } else: return DEFAULT_IMAGE_DATA def put(self, pvname, value: Union[np.ndarray, float], timeout=1.0) -> None: """Assign the value of a process variable. Args: pvname (str): Name of the process variable value (Union[np.ndarray, float]): Value to assing to process variable. timeout (float): Operation timeout in seconds """ self.setup_pv_monitor(pvname) # allow no puts before a value has been collected registered = self.get(pvname) # if the value is registered if registered is not None: if self.protocol == "ca": self.pv_registry[pvname]["pv"].put(value, timeout=timeout) elif self.protocol == "pva": self.context.put(pvname, value, throw=False, timeout=timeout) else: logger.debug(f"No initial value set for {pvname}.") def close(self): if self.protocol == "pva": self.context.close()
data.name = curveData["name"] data.computeLimits() print('name=', data.name, ' xmin=', data.xmin, ' xmax=', data.xmax, ' ymin=', data.ymin, ' ymax=', data.ymax) npts = len(x) timestart = time.time() for ind in range(npts): xarr = np.empty([ind]) yarr = np.empty([ind]) for i in range(ind): xarr[i] = x[i] yarr[i] = y[i] data.x = xarr data.y = yarr if ind == 0: putdata = { "name": data.name, "xmin": data.xmin, "xmax": data.xmax, "ymin": data.ymin, "ymax": data.ymax, "x": x, "y": y } else: putdata = {"x": xarr, "y": yarr} ctxt.put(getDynamicRecordName(), putdata) timenow = time.time() timediff = timenow - timestart print('putrate=', str(round(npts / timediff)), ' per second')
class Controller: """ Controller class used to get and put process variables. Attributes ---------- protocol: str Protocol to use ("pva", "ca") context: p4p.client.thread.Context p4p threaded context instance """ def __init__(self, protocol: str): """ Store protocol and initialize context if using PVAccess. """ self.protocol = protocol # initalize context for pva self.context = None if protocol == "pva": self.context = Context("pva") def get(self, pvname: str): """ Get the value of a process variable. Parameters ---------- pvname: str Name of the process variable Returns ------- np.ndarray Returns numpy array containing value. """ if self.protocol == "ca": return caget(pvname) elif self.protocol == "pva": return self.context.get(pvname) def get_image(self, pvname): """ Gets image data based on protocol. Arguments --------- pvname: str Name of process variable Returns ------- dict Formatted image data of the form ``` { "image": [np.ndarray], "x": [float], "y": [float], "dw": [float], "dh": [float], } ``` """ if self.protocol == "ca": pvname = pvname.replace(":ArrayData_RBV", "") nx = self.get(f"{pvname}:ArraySizeX_RBV") ny = self.get(f"{pvname}:ArraySizeY_RBV") dw = self.get(f"{pvname}:dw") dh = self.get(f"{pvname}:dh") image = self.get(f"{pvname}:ArrayData_RBV") image = image.reshape(int(nx), int(ny)) elif self.protocol == "pva": # context returns np array with WRITEABLE=False # copy to manipulate array below output = self.get(pvname) attrib = output.attrib dw = attrib["dw"] dh = attrib["dh"] nx, ny = output.shape image = copy.copy(output) return { "image": [image], "x": [-dw / 2], "y": [-dh / 2], "dw": [dw], "dh": [dh], } def put(self, pvname, value: Union[np.ndarray, float]) -> None: """ Assign the value of a process variable. Parameters ---------- pvname: str Name of the process variable value Value to put. Either float or numpy array """ if self.protocol == "ca": caput(pvname, value) elif self.protocol == "pva": self.context.put(pvname, value)
def ts_config(connect_json,cfgtype,detname,detsegm): cfg = get_config(connect_json,cfgtype,detname,detsegm) connect_info = json.loads(connect_json) # get the list of readout groups that the user has selected # so we only configure those readout_groups = [] connect_info = json.loads(connect_json) for nodes in connect_info['body']['drp'].values(): readout_groups.append(nodes['det_info']['readout']) readout_groups = set(readout_groups) control_info = connect_info['body']['control']['0']['control_info'] xpm_master = control_info['xpm_master'] pv_prefix = control_info['pv_base']+':XPM:'+str(xpm_master)+':PART:' rcfg = cfg.copy() rcfg['user' ] = {} rcfg['expert'] = {} linacMode = cfg['user']['LINAC'] rcfg['user']['LINAC'] = linacMode rcfg['user']['Cu' if linacMode==0 else 'SC'] = {} pvdict = {} # dictionary of epics pv name : value for group in readout_groups: if linacMode == 0: # Cu grp_prefix = 'group'+str(group)+'_eventcode' eventcode = cfg['user']['Cu'][grp_prefix] rcfg['user']['Cu'][grp_prefix] = eventcode pvdict[str(group)+':L0Select' ] = 2 # eventCode pvdict[str(group)+':L0Select_EventCode'] = eventcode pvdict[str(group)+':DstSelect' ] = 1 # DontCare else: # SC grp_prefix = 'group'+str(group) grp = cfg['user']['SC'][grp_prefix] rcfg['user']['SC'][grp_prefix] = grp pvdict[str(group)+':L0Select' ] = grp['trigMode'] pvdict[str(group)+':L0Select_FixedRate'] = grp['fixed']['rate'] pvdict[str(group)+':L0Select_ACRate' ] = grp['ac']['rate'] pvdict[str(group)+':L0Select_EventCode'] = 0 # not an option pvdict[str(group)+':L0Select_Sequence' ] = grp['seq']['mode'] pvdict[str(group)+':DstSelect' ] = grp['destination']['select'] # convert ac.ts0 through ac.ts5 to L0Select_ACTimeslot bitmask tsmask = 0 for tsnum in range(6): tsval = grp['ac']['ts'+str(tsnum)] tsmask |= 1<<tsval pvdict[str(group)+':L0Select_ACTimeslot'] = tsmask # L0Select_SeqBit is one var used by all of seq.(burst/fixed/local) if grp['seq']['mode']==15: # burst seqbit = grp['seq']['burst']['mode'] elif grp['seq']['mode']==16: # fixed rate seqbit = grp['seq']['fixed']['rate'] elif grp['seq']['mode']==17: # local seqbit = grp['seq']['local']['rate'] else: raise ValueError('Illegal value for trigger sequence mode') pvdict[str(group)+':L0Select_SeqBit'] = seqbit # DstSelect_Mask should come from destination.dest0 through dest15 dstmask = 0 for dstnum in range(16): dstval = grp['destination']['dest'+str(dstnum)] if dstval: dstmask |= 1<<dstnum pvdict[str(group)+':DstSelect_Mask'] = dstmask grp_prefix = 'group'+str(group) grp = cfg['expert'][grp_prefix] rcfg['expert'][grp_prefix] = grp # 4 InhEnable/InhInterval/InhLimit for inhnum in range(4): pvdict[str(group)+':InhInterval'+str(inhnum)] = grp['inhibit'+str(inhnum)]['interval'] pvdict[str(group)+':InhLimit'+str(inhnum)] = grp['inhibit'+str(inhnum)]['limit'] pvdict[str(group)+':InhEnable'+str(inhnum)] = grp['inhibit'+str(inhnum)]['enable'] names = list(pvdict.keys()) values = list(pvdict.values()) names = [pv_prefix+n for n in names] # program the values ctxt = Context('pva') ctxt.put(names,values) # Capture firmware version for persistence in xtc pv_prefix = control_info['pv_base']+':XPM:'+str(xpm_master)+':' #rcfg['firmwareVersion'] = ctxt.get(pv_prefix+'FwVersion').raw.value rcfg['firmwareBuild' ] = ctxt.get(pv_prefix+'FwBuild').raw.value ctxt.close() return json.dumps(rcfg)
def apply_update(cfg): global pv_prefix rcfg = {} pvdict = {} # dictionary of epics pv name : value for key in cfg: if key == 'user': rcfg['user'] = {} linacMode = ocfg['user']['LINAC'] # this won't scan if full: rcfg['user']['LINAC'] = linacMode rcfg['user']['Cu' if linacMode == 0 else 'SC'] = {} for group in readout_groups: if linacMode == 0: # Cu try: grp_prefix = 'group' + str(group) + '_eventcode' eventcode = cfg['user']['Cu'][grp_prefix] rcfg['user']['Cu'][grp_prefix] = eventcode pvdict[str(group) + ':L0Select'] = 2 # eventCode pvdict[str(group) + ':L0Select_EventCode'] = eventcode pvdict[str(group) + ':DstSelect'] = 1 # DontCare except KeyError: pass else: # SC pass # nothing here to scan (too complicated to implement) if key == 'expert': rcfg['expert'] = {} for group in readout_groups: grp_prefix = 'group' + str(group) if grp_prefix in cfg['expert']: grp = cfg['expert'][grp_prefix] rcfg['expert'][grp_prefix] = {} # 4 InhEnable/InhInterval/InhLimit for inhnum in range(4): inhkey = 'inhibit' + str(inhnum) if inhkey in grp: inhgrp = grp[inhkey] rcfg['expert'][grp_prefix][inhkey] = inhgrp rgrp = rcfg['expert'][grp_prefix][inhkey] if 'interval' in inhgrp: pvdict[str(group) + ':InhInterval' + str(inhnum)] = inhgrp['interval'] if 'limit' in inhgrp: pvdict[str(group) + ':InhLimit' + str(inhnum)] = inhgrp['limit'] if 'enable' in inhgrp: pvdict[str(group) + ':InhEnable' + str(inhnum)] = inhgrp['enable'] else: rcfg[key] = cfg[key] names = list(pvdict.keys()) values = list(pvdict.values()) names = [pv_prefix + 'PART:' + n for n in names] # program the values ctxt = Context('pva') ctxt.put(names, values) ctxt.close() return json.dumps(rcfg)
def apply_config(cfg): global pv_prefix rcfg = {} rcfg = cfg.copy() rcfg['user'] = {} rcfg['expert'] = {} linacMode = cfg['user']['LINAC'] rcfg['user']['LINAC'] = linacMode rcfg['user']['Cu' if linacMode == 0 else 'SC'] = {} pvdict = {} # dictionary of epics pv name : value for group in readout_groups: if linacMode == 0: # Cu grp_prefix = 'group' + str(group) + '_eventcode' eventcode = cfg['user']['Cu'][grp_prefix] rcfg['user']['Cu'][grp_prefix] = eventcode pvdict[str(group) + ':L0Select'] = 2 # eventCode pvdict[str(group) + ':L0Select_EventCode'] = eventcode pvdict[str(group) + ':DstSelect'] = 1 # DontCare else: # SC grp_prefix = 'group' + str(group) grp = cfg['user']['SC'][grp_prefix] rcfg['user']['SC'][grp_prefix] = grp pvdict[str(group) + ':L0Select'] = grp['trigMode'] pvdict[str(group) + ':L0Select_FixedRate'] = grp['fixed']['rate'] pvdict[str(group) + ':L0Select_ACRate'] = grp['ac']['rate'] pvdict[str(group) + ':L0Select_EventCode'] = 0 # not an option pvdict[str(group) + ':L0Select_Sequence'] = grp['seq']['mode'] pvdict[str(group) + ':DstSelect'] = grp['destination']['select'] # convert ac.ts0 through ac.ts5 to L0Select_ACTimeslot bitmask tsmask = 0 for tsnum in range(6): tsval = grp['ac']['ts' + str(tsnum)] tsmask |= 1 << tsval pvdict[str(group) + ':L0Select_ACTimeslot'] = tsmask # L0Select_SeqBit is one var used by all of seq.(burst/fixed/local) if grp['seq']['mode'] == 15: # burst seqbit = grp['seq']['burst']['mode'] elif grp['seq']['mode'] == 16: # fixed rate seqbit = grp['seq']['fixed']['rate'] elif grp['seq']['mode'] == 17: # local seqbit = grp['seq']['local']['rate'] else: raise ValueError('Illegal value for trigger sequence mode') pvdict[str(group) + ':L0Select_SeqBit'] = seqbit # DstSelect_Mask should come from destination.dest0 through dest15 dstmask = 0 for dstnum in range(16): dstval = grp['destination']['dest' + str(dstnum)] if dstval: dstmask |= 1 << dstnum pvdict[str(group) + ':DstSelect_Mask'] = dstmask grp_prefix = 'group' + str(group) grp = cfg['expert'][grp_prefix] rcfg['expert'][grp_prefix] = grp # 4 InhEnable/InhInterval/InhLimit for inhnum in range(4): pvdict[str(group) + ':InhInterval' + str(inhnum)] = grp['inhibit' + str(inhnum)]['interval'] pvdict[str(group) + ':InhLimit' + str(inhnum)] = grp['inhibit' + str(inhnum)]['limit'] pvdict[str(group) + ':InhEnable' + str(inhnum)] = grp['inhibit' + str(inhnum)]['enable'] names = list(pvdict.keys()) values = list(pvdict.values()) names = [pv_prefix + 'PART:' + n for n in names] # program the values ctxt = Context('pva') ctxt.put(names, values) # Capture firmware version for persistence in xtc #rcfg['firmwareVersion'] = ctxt.get(pv_prefix+'FwVersion').raw.value rcfg['firmwareBuild'] = ctxt.get(pv_prefix + 'FwBuild').raw.value ctxt.close() return json.dumps(rcfg)
def hsd_config(connect_str, epics_prefix, cfgtype, detname, detsegm, group): cfg = get_config(connect_str, cfgtype, detname, detsegm) # fetch the current configuration for defaults not specified in the configuration ctxt = Context('pva') values = ctxt.get(epics_prefix + ':CONFIG') # fetch the xpm delay partitionDelay = ctxt.get(epics_prefix + ':MONTIMING').msgdelayset print('partitionDelay {:}'.format(partitionDelay)) # # Validate user raw values # raw = cfg['user']['raw'] raw_start = (raw['start_ns'] * 1300 / 7000 - partitionDelay * 200) * 160 / 200 # in "160MHz"(*13/14) clks if raw_start < 0: print('partitionDelay {:} raw_start_ns {:} raw_start {:}'.format( partitionDelay, raw['start_ns'], raw_start)) raise ValueError('raw_start computes to < 0') raw_gate = int(raw['gate_ns'] * 0.160 * 13 / 14) # in "160" MHz clks raw_nsamples = raw_gate * 40 if raw_gate < 0: raise ValueError('raw_gate computes to < 0') if raw_gate > 4000: raise ValueError('raw_gate computes to > 4000; raw_nsamples > 160000') # # Validate user fex values # fex = cfg['user']['fex'] fex_start = int((fex['start_ns'] * 1300 / 7000 - partitionDelay * 200) * 160 / 200) # in "160MHz"(*13/14) clks if fex_start < 0: print('partitionDelay {:} fex_start_ns {:} fex_start {:}'.format( partitionDelay, fex['start_ns'], fex_start)) raise ValueError('fex_start computes to < 0') fex_gate = int(fex['gate_ns'] * 0.160 * 13 / 14) # in "160" MHz clks fex_nsamples = fex_gate * 40 if fex_gate < 0: raise ValueError('fex_gate computes to < 0') # Place no constraint on upper bound. Assumes sparsification will reduce to < 160000 recorded samples # hsd_thr_ilv_native_fine firmware expects xpre,xpost in # of super samples (4 samples) fex_xpre = int((fex['xpre'] + 3) / 4) fex_xpost = int((fex['xpost'] + 3) / 4) # overwrite expert fields from user input expert = cfg['expert'] expert['readoutGroup'] = group expert['enable'] = 1 expert['raw_start'] = raw_start expert['raw_gate'] = raw_gate expert['raw_prescale'] = raw['prescale'] expert['fex_start'] = fex_start expert['fex_gate'] = fex_gate expert['fex_xpre'] = fex_xpre expert['fex_xpost'] = fex_xpost expert['fex_prescale'] = fex['prescale'] # program the values print(epics_prefix) ctxt.put(epics_prefix + ':READY', 0, wait=True) ctxt.put(epics_prefix + ':CONFIG', expert, wait=True) # the completion of the "put" guarantees that all of the above # have completed (although in no particular order) complete = False for i in range(100): complete = ctxt.get(epics_prefix + ':READY') != 0 if complete: break print('hsd config wait for complete', i) time.sleep(0.1) if complete: print('hsd config complete') else: raise Exception('timed out waiting for hsd configure') cfg['firmwareVersion'] = ctxt.get(epics_prefix + ':FWVERSION').raw.value cfg['firmwareBuild'] = ctxt.get(epics_prefix + ':FWBUILD').raw.value ctxt.close() return json.dumps(cfg)
def wave8_config(prefix,connect_str,cfgtype,detname,detsegm,group): global ctxt cfg = get_config(connect_str,cfgtype,detname,detsegm) ctxt = Context('pva') # | timing fiducial # PartitionDelay | TriggerEventManager.TriggerEventBuffer receives xpm trigger # TriggerDelay | TriggerEventManager.triggerBus asserts trigger # IntStart | Integrators.intStart (baseline latched) # IntLen | Intregrators.intEnd # | RawDataBuffer start # RawBuffLen | RawDataBuffer End epics_prefix = prefix + ':Top:' partitionDelay = ctxt.get(epics_prefix+'TriggerEventManager:XpmMessageAligner:PartitionDelay[%d]'%group) raw = cfg['user']['raw'] rawStart = raw['start_ns'] triggerDelay = rawStart*1300/7000 - partitionDelay*200 print('partitionDelay {:} rawStart {:} triggerDelay {:}'.format(partitionDelay,rawStart,triggerDelay)) if triggerDelay < 0: raise ValueError('triggerDelay computes to < 0') rawNsamples = int(raw['gate_ns']*0.25) if rawNsamples>256: raise ValueError('raw.gate_ns > 1020') raw['nsamples'] = rawNsamples fex = cfg['user']['fex'] intStart = fex['start_ns'] if intStart < rawStart: print('fex.start_ns {:} raw.start_ns {:}'.format(intStart,rawStart)) raise ValueError('fex.start_ns < raw.start_ns') fexTrigDelay = int((intStart-rawStart)*250/1000) if fexTrigDelay > 255: raise ValueError('fex.start_ns > raw.start_ns + 1020') fexNsamples = int(fex['gate_ns']*0.25) if fexNsamples>255: raise ValueError('fex.gate_ns > 1020') fex['nsamples'] = rawNsamples # Assert clears names_clr = [epics_prefix+'BatcherEventBuilder:Blowoff', epics_prefix+'TimingFrameRx:RxCountReset', epics_prefix+'RawBuffers:CntRst', epics_prefix+'Integrators:CntRst'] values = [1]*len(names_clr) print('names {:}'.format(names_clr)) ctxt.put(names_clr,values) expert = cfg['expert']['Top'] expert['TriggerEventManager']['TriggerEventBuffer[0]']['TriggerDelay'] = triggerDelay for i in range(8): expert['RawBuffers']['BuffEn[%d]'%i] = raw['enable[%d]'%i] # Firmware needs a value one less expert['RawBuffers']['BuffLen'] = rawNsamples-1 # Firmware needs a value one less prescale = raw['prescale'] if prescale>0: prescale -= 1 expert['RawBuffers']['TrigPrescale'] = prescale expert['Integrators']['TrigDelay'] = fexTrigDelay # Firmware needs a value one less expert['Integrators']['IntegralSize'] = fexNsamples-1 expert['Integrators']['BaselineSize'] = fex['baseline'] for i in range(4): expert['Integrators']['CorrCoefficientFloat64[%d]'%i] = fex['coeff[%d]'%i] expert['TriggerEventManager']['TriggerEventBuffer[0]']['Partition'] = group names = [] values = [] epics_put(cfg['expert'],prefix+':',names,values) ctxt.put(names,values) ctxt.put(epics_prefix+'TriggerEventManager:TriggerEventBuffer[0]:MasterEnable', 1, wait=True) time.sleep(0.2) # Deassert clears values = [0]*len(names_clr) ctxt.put(names_clr,values) ctxt.put(epics_prefix+'BatcherEventBuilder:Blowoff', 0, wait=True) cfg['firmwareVersion'] = ctxt.get(epics_prefix+'AxiVersion:FpgaVersion').raw.value cfg['firmwareBuild' ] = ctxt.get(epics_prefix+'AxiVersion:BuildStamp').raw.value ctxt.close() v = json.dumps(cfg) return v
class ts_connector: def __init__(self, json_connect_info): self.connect_info = json.loads(json_connect_info) print('*** connect_info') pp = pprint.PrettyPrinter() pp.pprint(self.connect_info) control_info = self.connect_info['body']['control']['0'][ 'control_info'] self.xpm_base = control_info['pv_base'] + ':XPM:' master_xpm_num = control_info['xpm_master'] self.master_xpm_pv = self.xpm_base + str(master_xpm_num) + ':' self.ctxt = Context('pva') self.get_xpm_info() self.get_readout_group_mask() # unfortunately, the hsd needs the Rx link reset before the Tx, # otherwise we get CRC errors on the link. # try commenting this out since Matt has made the links more reliable #self.xpm_link_reset('Rx') #self.xpm_link_reset('Tx') # must come after clear readout because clear readout increments # the event counters, and the pgp eb needs them to start from zero # comment this out since it was moved to control.py #self.l0_count_reset() # enables listening to deadtime self.xpm_link_enable() self.ctxt.close() def get_readout_group_mask(self): self.readout_group_mask = 0 for _, _, readout_group in self.xpm_info: self.readout_group_mask |= (1 << readout_group) def get_xpm_info(self): self.xpm_info = [] # FIXME: cpo/weaver think this doesn't work for digitizers, # for example, where the DRP node can't learn which XPM port # is feeding it timing information. Currently think we should # try to get the information from the XPM side, instead of the # drp side. for key, node_info in self.connect_info['body']['drp'].items(): try: # FIXME: should have a better method to map xpm ip # address to xpm number (used to create pv names) xpm_id = int(node_info['connect_info']['xpm_id']) xpm_port = node_info['connect_info']['xpm_port'] readout_group = node_info['det_info']['readout'] self.xpm_info.append((xpm_id, xpm_port, readout_group)) except KeyError: pass def xpm_link_disable(self, pv, groups): pv_names = [] for xpm_port in range(14): pv_names.append(pv + 'RemoteLinkId' + str(xpm_port)) print('link_ids: {:}'.format(pv_names)) link_ids = self.ctxt.get(pv_names) pv_names = [] downstream_xpm_names = [] for xpm_port in range(14): pv_names.append(pv + 'LinkGroupMask' + str(xpm_port)) link_masks = self.ctxt.get(pv_names) for i in range(14): xlink = xpm_link(link_ids[i]) # this gets run for all xpm's "downstream" of the master xpm if xlink.is_xpm(): downstream_xpm_names.append(self.xpm_base + str(xlink.xpm_num())) self.xpm_link_disable( self.xpm_base + str(xlink.xpm_num()) + ':', groups) link_masks[ i] = 0xff # xpm to xpm links should be enabled for everything else: link_masks[i] &= ~groups self.ctxt.put(pv_names, link_masks) # this code disables the "master" feature for each of the # downstream xpm's for the readout groups used by the new xpm master pv_names_downstream_xpm_master_enable = [] for name in downstream_xpm_names: for igroup in range(8): if (1 << igroup) & groups: pv_names_downstream_xpm_master_enable.append( name + ':PART:%d:Master' % igroup) num_master_disable = len(pv_names_downstream_xpm_master_enable) if (num_master_disable): print('*** Disable downstream xpm readout group master:', pv_names_downstream_xpm_master_enable) self.ctxt.put(pv_names_downstream_xpm_master_enable, [0] * num_master_disable) def xpm_link_disable_all(self): # Start from the master and recursively remove the groups from each downstream link self.xpm_link_disable(self.master_xpm_pv, self.readout_group_mask) def xpm_link_enable(self): self.xpm_link_disable_all() d = {} for xpm_num, xpm_port, readout_group in self.xpm_info: pvname = self.xpm_base + str( xpm_num) + ':' + 'LinkGroupMask' + str(xpm_port) if pvname in d: d[pvname] |= (1 << readout_group) else: d[pvname] = (1 << readout_group) pv_names = [] values = [] for name, value in d.items(): pv_names.append(name) values.append(value) print('*** setting xpm link enables', pv_names, values) self.ctxt.put(pv_names, values) def xpm_link_reset(self, style): # make pv name that looks like DAQ:LAB2:XPM:1:RxLinkReset11 # for xpm_num 1 and xpm_port 11 pv_names = [] for xpm_num, xpm_port, _ in self.xpm_info: pvname = self.xpm_base + str( xpm_num) + ':' + style + 'LinkReset' + str(xpm_port) pv_names.append(pvname) print('*** xpm link resetting', pv_names) self.ctxt.put(pv_names, len(pv_names) * [1]) # unfortunately need to wait for the links to relock, which # matt says takes "an appreciable fraction of a second". # empirically, the links seem unreliable unless we wait 2s. time.sleep(2) def l0_count_reset(self): pvL0Reset = self.master_xpm_pv + 'GroupL0Reset' print('*** resetting l0 count', self.readout_group_mask) self.ctxt.put(pvL0Reset, self.readout_group_mask)
class ts_connector: def __init__(self, json_connect_info): self.connect_info = json.loads(json_connect_info) print('*** connect_info', self.connect_info) control_info = self.connect_info['body']['control']['0'][ 'control_info'] self.xpm_base = control_info['pv_base'] + ':XPM:' master_xpm_num = control_info['xpm_master'] self.master_xpm_pv = self.xpm_base + str(master_xpm_num) + ':' self.ctxt = Context('pva') self.get_xpm_info() self.get_readout_group_mask() # unfortunately, the hsd needs the Rx link reset before the Tx, # otherwise we get CRC errors on the link. # try commenting this out since Matt has made the links more reliable #self.xpm_link_reset('Rx') #self.xpm_link_reset('Tx') # must come after clear readout because clear readout increments # the event counters, and the pgp eb needs them to start from zero # comment this out since it was moved to control.py #self.l0_count_reset() # enables listening to deadtime self.xpm_link_enable() self.ctxt.close() def get_readout_group_mask(self): self.readout_group_mask = 0 for _, _, readout_group in self.xpm_info: self.readout_group_mask |= (1 << readout_group) def get_xpm_info(self): self.xpm_info = [] for key, node_info in self.connect_info['body']['drp'].items(): try: # FIXME: should have a better method to map xpm ip # address to xpm number (used to create pv names) xpm_id = int(node_info['connect_info']['xpm_ip'].split('.')[2]) xpm_port = node_info['connect_info']['xpm_port'] readout_group = node_info['det_info']['readout'] self.xpm_info.append((xpm_id, xpm_port, readout_group)) except KeyError: pass def xpm_link_disable_all(self): # FIXME: need a mechanism to disable unused links in all # downstream XPMs. For now, just clear out our readout # groups from all the XPMs we know about from the collection, # which comes from the "remote link id" info in the drp nodes. xpms = [xpm_num for xpm_num, _, _ in self.xpm_info] unique_xpms = set(xpms) pv_names = [] for xpm_num in unique_xpms: for xpm_port in range(32): pv_names.append(self.xpm_base + str(xpm_num) + ':' + 'LinkGroupMask' + str(xpm_port)) current_group_masks = self.ctxt.get(pv_names) print(current_group_masks) # don't clear out group_mask 0xff (an indication that it's # a downstream XPM link) #pv_names_to_clear = [pv_name for (pv_name,group_mask) in zip(pv_names,current_group_masks) if (group_mask & self.readout_group_mask) and (group_mask != 0xff)] #print('*** clearing xpm links',pv_names_to_clear) #self.ctxt.put(pv_names_to_clear,len(pv_names_to_clear)*[0]) def xpm_link_enable(self): self.xpm_link_disable_all() pv_names = [] values = [] for xpm_num, xpm_port, readout_group in self.xpm_info: pvname = self.xpm_base + str( xpm_num) + ':' + 'LinkGroupMask' + str(xpm_port) pv_names.append(pvname) values.append((1 << readout_group)) print('*** setting xpm link enables', pv_names, values) self.ctxt.put(pv_names, values) def xpm_link_reset(self, style): # make pv name that looks like DAQ:LAB2:XPM:1:RxLinkReset11 # for xpm_num 1 and xpm_port 11 pv_names = [] for xpm_num, xpm_port, _ in self.xpm_info: pvname = self.xpm_base + str( xpm_num) + ':' + style + 'LinkReset' + str(xpm_port) pv_names.append(pvname) print('*** xpm link resetting', pv_names) self.ctxt.put(pv_names, len(pv_names) * [1]) # unfortunately need to wait for the links to relock, which # matt says takes "an appreciable fraction of a second". # empirically, the links seem unreliable unless we wait 2s. time.sleep(2) def l0_count_reset(self): pvL0Reset = self.master_xpm_pv + 'GroupL0Reset' print('*** resetting l0 count', self.readout_group_mask) self.ctxt.put(pvL0Reset, self.readout_group_mask)
class Controller: """ Controller class used to access process variables. Controllers are used for interfacing with both Channel Access and pvAccess process variables. The controller object is initialized using a single protocol has methods for both getting and setting values on the process variables. Attributes: _protocol (str): Protocol for getting values from variables ("pva" for pvAccess, "ca" for Channel Access) _context (Context): P4P threaded context instance for use with pvAccess. _pv_registry (dict): Registry mapping pvname to dict of value and pv monitor _input_pvs (dict): Dictionary of input process variables _output_pvs (dict): Dictionary out output process variables _prefix (str): Prefix to use for accessing variables last_input_update (datetime): Last update of input variables last_output_update (datetime): Last update of output variables Example: ``` # create PVAcess controller controller = Controller("pva") value = controller.get_value("scalar_input") image_value = controller.get_image("image_input") controller.close() ``` """ def __init__(self, protocol: str, input_pvs: dict, output_pvs: dict, prefix): """ Initializes controller. Stores protocol and creates context attribute if using pvAccess. Args: protocol (str): Protocol for getting values from variables ("pva" for pvAccess, "ca" for Channel Access) input_pvs (dict): Dict mapping input variable names to variable output_pvs (dict): Dict mapping output variable names to variable """ self._protocol = protocol self._pv_registry = defaultdict() self._input_pvs = input_pvs self._output_pvs = output_pvs self._prefix = prefix self.last_input_update = "" self.last_output_update = "" # initalize context for pva self._context = None if self._protocol == "pva": self._context = Context("pva") # initialize controller for variable in {**input_pvs, **output_pvs}.values(): if variable.variable_type == "image": self.get_image(variable.name) elif variable.variable_type == "array": self.get_array(variable.name) else: self.get_value(variable.name) def _ca_value_callback(self, pvname, value, *args, **kwargs): """Callback executed by Channel Access monitor. Args: pvname (str): Process variable name value (Union[np.ndarray, float]): Value to assign to process variable. """ pvname = pvname.replace(f"{self._prefix}:", "") self._pv_registry[pvname]["value"] = value if pvname in self._input_pvs: self.last_input_update = datetime.now().strftime( "%m/%d/%Y, %H:%M:%S") if pvname in self._output_pvs: self.last_output_update = datetime.now().strftime( "%m/%d/%Y, %H:%M:%S") def _ca_connection_callback(self, *, pvname, conn, pv): """Callback used for monitoring connection and setting values to None on disconnect. """ # if disconnected, set value to None pvname = pvname.replace(f"{self._prefix}:", "") if not conn: self._pv_registry[pvname]["value"] = None def _pva_value_callback(self, pvname, value): """Callback executed by pvAccess monitor. Args: pvname (str): Process variable name value (Union[np.ndarray, float]): Value to assign to process variable. """ if isinstance(value, Disconnected): self._pv_registry[pvname]["value"] = None else: self._pv_registry[pvname]["value"] = value if pvname in self._input_pvs: self.last_input_update = datetime.now().strftime( "%m/%d/%Y, %H:%M:%S") if pvname in self._output_pvs: self.last_output_update = datetime.now().strftime( "%m/%d/%Y, %H:%M:%S") def _set_up_pv_monitor(self, pvname): """Set up process variable monitor. Args: pvname (str): Process variable name """ if pvname in self._pv_registry: return if self._protocol == "ca": # add to registry (must exist for connection callback) self._pv_registry[pvname] = {"pv": None, "value": None} # create the pv pv_obj = PV( f"{self._prefix}:{pvname}", callback=self._ca_value_callback, connection_callback=self._ca_connection_callback, ) # update registry self._pv_registry[pvname]["pv"] = pv_obj elif self._protocol == "pva": cb = partial(self._pva_value_callback, pvname) # populate registry s.t. initially disconnected will populate self._pv_registry[pvname] = {"pv": None, "value": None} # create the monitor obj mon_obj = self._context.monitor(f"{self._prefix}:{pvname}", cb, notify_disconnect=True) # update registry with the monitor self._pv_registry[pvname]["pv"] = mon_obj def get(self, pvname: str) -> np.ndarray: """ Accesses and returns the value of a process variable. Args: pvname (str): Process variable name """ self._set_up_pv_monitor(pvname) pv = self._pv_registry.get(pvname, None) if pv: return pv["value"] return None def get_value(self, pvname): """Gets scalar value of a process variable. Args: pvname (str): Process variable name. """ value = self.get(pvname) if value is None: value = DEFAULT_SCALAR_VALUE return value def get_image(self, pvname) -> dict: """Gets image data via controller protocol. Args: pvname (str): Image process variable name """ image = None if self._protocol == "ca": image_flat = self.get(f"{pvname}:ArrayData_RBV") nx = self.get(f"{pvname}:ArraySizeX_RBV") ny = self.get(f"{pvname}:ArraySizeY_RBV") x = self.get(f"{pvname}:MinX_RBV") y = self.get(f"{pvname}:MinY_RBV") x_max = self.get(f"{pvname}:MaxX_RBV") y_max = self.get(f"{pvname}:MaxY_RBV") if all([ image_def is not None for image_def in [image_flat, nx, ny, x, y, x_max, y_max] ]): dw = x_max - x dh = y_max - y image = image_flat.reshape(int(nx), int(ny)) elif self._protocol == "pva": # context returns numpy array with WRITEABLE=False # copy to manipulate array below image = self.get(pvname) if image is not None: attrib = image.attrib x = attrib["x_min"] y = attrib["y_min"] dw = attrib["x_max"] - attrib["x_min"] dh = attrib["y_max"] - attrib["y_min"] image = copy.copy(image) if image is not None: return { "image": [image], "x": [x], "y": [y], "dw": [dw], "dh": [dh], } else: return DEFAULT_IMAGE_DATA def get_array(self, pvname) -> dict: """Gets array data via controller protocol. Args: pvname (str): Image process variable name """ array = None if self._protocol == "ca": array_flat = self.get(f"{pvname}:ArrayData_RBV") shape = self.get(f"{pvname}:ArraySize_RBV") if all( [array_def is not None for array_def in [array_flat, shape]]): array = np.array(array_flat).reshape(shape) elif self._protocol == "pva": # context returns numpy array with WRITEABLE=False # copy to manipulate array below array = self.get(pvname) if array is not None: return array else: return np.array([]) def put(self, pvname, value: float, timeout=1.0) -> None: """Assign the value of a scalar process variable. Args: pvname (str): Name of the process variable value (float): Value to assing to process variable. timeout (float): Operation timeout in seconds """ self._set_up_pv_monitor(pvname) # allow no puts before a value has been collected registered = self.get(pvname) # if the value is registered if registered is not None: if self._protocol == "ca": self._pv_registry[pvname]["pv"].put(value, timeout=timeout) elif self._protocol == "pva": self._context.put(f"{self._prefix}:{pvname}", value, throw=False, timeout=timeout) else: logger.debug(f"No initial value set for {pvname}.") def put_image( self, pvname, image_array: np.ndarray = None, x_min: float = None, x_max: float = None, y_min: float = None, y_max: float = None, timeout: float = 1.0, ) -> None: """Assign the value of a image process variable. Allows updates to individual attributes. Args: pvname (str): Name of the process variable image_array (np.ndarray): Value to assing to process variable. x_min (float): Minimum x value x_max (float): Maximum x value y_min (float): Minimum y value y_max (float): Maximum y value timeout (float): Operation timeout in seconds """ self._set_up_pv_monitor(pvname) # allow no puts before a value has been collected registered = self.get_image(pvname) # if the value is registered if registered is not None: if self._protocol == "ca": if image_array is not None: self._pv_registry[f"{pvname}:ArrayData_RBV"]["pv"].put( image_array.flatten(), timeout=timeout) if x_min: self._pv_registry[f"{pvname}:MinX_RBV"]["pv"].put( x_min, timeout=timeout) if x_max: self._pv_registry[f"{pvname}:MaxX_RBV"]["pv"].put( x_max, timeout=timeout) if y_min: self._pv_registry[f"{pvname}:MinY_RBV"]["pv"].put( y_min, timeout=timeout) if y_max: self._pv_registry[f"{pvname}:MaxY_RBV"]["pv"].put( y_max, timeout=timeout) elif self._protocol == "pva": # compose normative type pv = self._pv_registry[pvname] pv_array = pv["value"] if image_array: image_array.attrib = pv_array.attrib else: image_array = pv_array if x_min: image_array.attrib.x_min = x_min if x_max: image_array.attrib.x_max = x_max if y_min: image_array.attrib.y_min = y_min if y_max: image_array.attrib.y_max = y_max self._context.put(pvname, image_array, throw=False, timeout=timeout) else: logger.debug(f"No initial value set for {pvname}.") def put_array( self, pvname, array: np.ndarray = None, timeout: float = 1.0, ) -> None: """Assign the value of an array process variable. Allows updates to individual attributes. Args: pvname (str): Name of the process variable array (np.ndarray): Value to assing to process variable. timeout (float): Operation timeout in seconds """ self._set_up_pv_monitor(pvname) # allow no puts before a value has been collected registered = self.get_array(pvname) # if the value is registered if registered is not None: if self._protocol == "ca": if array is not None: self._pv_registry[f"{pvname}:ArrayData_RBV"]["pv"].put( array.flatten(), timeout=timeout) elif self._protocol == "pva": # compose normative type pv = self._pv_registry[pvname] array = pv["value"] self._context.put(pvname, array, throw=False, timeout=timeout) else: logger.debug(f"No initial value set for {pvname}.") def close(self): if self._protocol == "pva": self._context.close()
class AxisCom: def __init__(self, url_string, axisNum=1, log_debug=True): self.pvpfx = None # PV prefix, like IOC:m1 self.ctxt = None # P4P context, if any self.log_debug = log_debug if url_string.startswith("pva://"): self.url_scheme = "pva://" self.pvpfx = url_string[6:] from p4p.client.thread import Context self.ctxt = Context("pva") elif url_string.startswith("ca://"): # Channel access self.url_scheme = "ca://" self.pvpfx = url_string[5:] import epics as epics self.epics = epics else: help_and_exit(self, url_string, "invalid scheme") def get( self, pvsuf, as_string=False, count=None, as_numpy=True, timeout=25.0, use_monitor=False, ): pvname = self.pvpfx + pvsuf fullname = self.url_scheme + pvname ret = None if as_string == True: raise Exception("as_string=True not supported") if self.log_debug: print( f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {filnam} get {fullname}" ) if self.ctxt is not None: ret = self.ctxt.get(pvname, timeout=timeout) else: ret = self.epics.caget(pvname, timeout=timeout, use_monitor=use_monitor) if self.log_debug: print( f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {filnam} get {fullname} ret={ret}" ) if ret is None: raise Exception("get None") return ret def put( self, pvsuf, value, wait=False, timeout=5.0, ): pvname = self.pvpfx + pvsuf fullname = self.url_scheme + pvname ret = None if self.log_debug: if wait: print( f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {filnam} put {fullname} timeout={timeout} wait={wait} value={value}" ) print( f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {filnam} put {fullname} value={value}" ) if self.ctxt is not None: ret = self.ctxt.put(pvname, value, timeout=timeout, wait=wait) if self.log_debug: print( f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {filnam} put {fullname} value={value} pvput_ret={ret}" ) else: caput_ret = self.epics.caput(pvname, value, timeout=timeout, wait=wait) # This function returns 1 on success, # and a negative number if the timeout has been exceeded if self.log_debug: print( f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {filnam} put {fullname} value={value} caput_ret={ret}" ) if caput_ret != 1: raise Exception( f"caput({pvname},{value}) returned error {caput_ret}") def putDbgStrToLOG(self, value, wait=True, timeout=5.0): pvsuf = "-DbgStrToLOG" try: self.put(pvsuf, value, wait=wait, timeout=timeout) except Exception as ex: print( f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {filnam} put {pvsuf} value={value} ex={ex}" ) def getMotorPvName(self): return self.pvpfx
#!/usr/bin/env python from Dynamic_Common import getAddRecordName,getDynamicRecordName from p4p.client.thread import Context,Type,Value import numpy as np ctxt = Context('pva') pvAddRecord = ctxt.get(getAddRecordName()) print('pvAddRecord=',pvAddRecord) struct = Value(Type([ ('name', 's'), ('x', 'ad'), ('y', 'ad'), ('xmin', 'd'), ('xmax', 'd'), ('ymin', 'd'), ('ymax', 'd'), ])) pvAddRecord['argument']['recordName'] = getDynamicRecordName() pvAddRecord['argument']['union'] = struct ctxt.put(getAddRecordName(),pvAddRecord)