コード例 #1
0
ファイル: hsd_config.py プロジェクト: pcdshub/lcls2
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
コード例 #2
0
ファイル: wave8_config.py プロジェクト: slactjohnson/lcls2
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;
コード例 #3
0
ファイル: test_server.py プロジェクト: slaclab/lume-epics
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()
コード例 #4
0
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)
コード例 #5
0
ファイル: xpmtrantest.py プロジェクト: slac-lcls/lcls2
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)
コード例 #6
0
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
コード例 #7
0
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)
コード例 #8
0
ファイル: hsd_config.py プロジェクト: AntoineDujardin/lcls2
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)
コード例 #9
0
ファイル: main.py プロジェクト: slaclab/lcls-cu-inj-live
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()
コード例 #10
0
    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')
コード例 #11
0
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)
コード例 #12
0
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)
コード例 #13
0
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)
コード例 #14
0
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)
コード例 #15
0
ファイル: hsd_config.py プロジェクト: slactjohnson/lcls2
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)
コード例 #16
0
ファイル: wave8_config.py プロジェクト: slactjohnson/lcls2
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
コード例 #17
0
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)
コード例 #18
0
ファイル: ts_connect.py プロジェクト: AntoineDujardin/lcls2
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)
コード例 #19
0
ファイル: controller.py プロジェクト: slaclab/lume-epics
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()
コード例 #20
0
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
コード例 #21
0
#!/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)