コード例 #1
0
    def __init__(
        self,
        producer: KafkaProducer,
        context: PVAContext,
        pv_name: str,
        output_topic: str,
        schema: str,
        periodic_update_ms: Optional[int] = None,
    ):
        self._logger = get_logger()
        self._producer = producer
        self._output_topic = output_topic

        request = context.makeRequest("field(value,timeStamp,alarm)")
        self._sub = context.monitor(pv_name,
                                    self._monitor_callback,
                                    request=request)
        self._pv_name = pv_name

        self._cached_update: Optional[Tuple[Value, int]] = None
        self._output_type = None
        self._stop_timer_flag = Event()
        self._repeating_timer = None
        self._cache_lock = Lock()

        try:
            self._message_publisher = schema_publishers[schema]
        except KeyError:
            raise ValueError(
                f"{schema} is not a recognised supported schema, use one of {list(schema_publishers.keys())}"
            )

        if periodic_update_ms is not None:
            self._repeating_timer = RepeatTimer(
                milliseconds_to_seconds(periodic_update_ms),
                self.publish_cached_update)
            self._repeating_timer.start()
コード例 #2
0
class P4PProvider(QObject,NTNDA_Channel_Provider) :
    callbacksignal = pyqtSignal()
    def __init__(self):
        QObject.__init__(self)
        NTNDA_Channel_Provider.__init__(self)
        self.callbacksignal.connect(self.mycallback)
        self.callbackDoneEvent = Event()
        self.firstCallback = True
        self.isClosed = True
        
    def start(self) :
        self.ctxt = Context('pva')
        self.firstCallback = True
        self.isClosed = False
        self.subscription = self.ctxt.monitor(
              self.getChannelName(),
              self.p4pcallback,
              request='field(value,dimension,codec,compressedSize,uncompressedSize)',
              notify_disconnect=True)
    def stop(self) :
        self.isClosed = True
        self.ctxt.close()
    def done(self) :
        pass
    def callback(self,arg) :
        self.NTNDA_Viewer.callback(arg)
    def p4pcallback(self,arg) :
        if self.isClosed : return
        self.struct = arg;
        self.callbacksignal.emit()
        self.callbackDoneEvent.wait()
        self.callbackDoneEvent.clear()
    def mycallback(self) :
        struct = self.struct
        arg = dict()
        try :
            argtype = str(type(struct))
            if argtype.find('Disconnected')>=0 :
                arg["status"] = "disconnected"
                self.callback(arg)
                self.firstCallback = True
                self.callbackDoneEvent.set()
                return
            if self.firstCallback :
                arg = dict()
                arg["status"] = "connected"
                self.callback(arg)
                self.firstCallback = False
                self.callback(arg)
            arg = dict()
            arg['value'] = struct['value']
            arg['dimension'] = struct['dimension']
            arg['codec'] = struct['codec']
            arg['compressedSize'] = struct['compressedSize']
            arg['uncompressedSize'] = struct['uncompressedSize']
            self.callback(arg)
            self.callbackDoneEvent.set()
            return
        except Exception as error:
            arg["exception"] = repr(error)
            self.callback(arg)
            self.callbackDoneEvent.set()
            return
コード例 #3
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()
コード例 #4
0
ファイル: live.py プロジェクト: mdavidsaver/image-blobs
    def main(self):
        cli = Context()

        pvs = {}
        # table of detected "features"
        self.features = pvs[args.output + 'features'] = SharedPV(
            nt=NTTable(columns=[
                ('X', 'd'),
                ('Y', 'd'),
                ('W', 'd'),
                ('H', 'd'),
                ('idx', 'd'),
            ]),
            initial=[])
        # output image (example)
        self.imgOut = pvs[args.output + 'img'] = SharedPV(nt=NTNDArray(),
                                                          initial=np.zeros(
                                                              (0, 0),
                                                              dtype='u1'))
        # display execution time
        self.execTime = pvs[args.output + 'etime'] = SharedPV(
            nt=NTScalar('d', display=True),
            initial={
                'value': 0.0,
                'display.units': 's',
            })
        # background threshold level
        bg = pvs[args.output + 'bg'] = SharedPV(nt=NTScalar('I', display=True),
                                                initial={
                                                    'value': self.bgLvl,
                                                    'display.units': 'px',
                                                })

        @bg.put
        def set_bg(pv, op):
            self.bgLvl = max(1, int(op.value()))
            pv.post(self.bgLvl)
            op.done()

        # image flattening mode
        imode = pvs[args.output + 'imode'] = SharedPV(
            nt=NTEnum(), initial={'choices': [e.name for e in ImageMode]})

        @imode.put
        def set_imode(pv, op):
            self.imode = ImageMode(op.value())
            pv.post(self.imode)
            op.done()

        # separately publish info of largest feature
        self.X = pvs[args.output + 'x'] = SharedPV(nt=NTScalar('d'),
                                                   initial=0.0)
        self.Y = pvs[args.output + 'y'] = SharedPV(nt=NTScalar('d'),
                                                   initial=0.0)
        self.W = pvs[args.output + 'w'] = SharedPV(nt=NTScalar('d'),
                                                   initial=0.0)
        self.H = pvs[args.output + 'h'] = SharedPV(nt=NTScalar('d'),
                                                   initial=0.0)

        print("Output PVs", list(pvs.keys()))

        # subscribe to input image PV and run local server
        with cli.monitor(self.args.input,
                         self.on_image,
                         request='record[pipeline=true,queueSize=2]'), Server(
                             providers=[pvs]):
            # park while work happens in other tasks
            done = threading.Event()
            signal.signal(signal.SIGINT, lambda x, y: done.set())
            done.wait()
コード例 #5
0
class P4PProvider(QObject):
    callbacksignal = pyqtSignal()

    def __init__(self):
        QObject.__init__(self)
        self.callbacksignal.connect(self.mycallback)
        self.callbackDoneEvent = Event()
        self.firstCallback = True
        self.isClosed = True
        self.channelName = "13SIM1:Pva1:Image"

    def setChannelName(self, channelName):
        self.channelName = channelName

    def getChannelName(self):
        return self.channelName

    def start(self):
        self.ctxt = Context("pva")
        self.firstCallback = True
        self.isClosed = False
        self.subscription = self.ctxt.monitor(
            self.getChannelName(),
            self.p4pcallback,
            request=
            "field(value,dimension,codec,compressedSize,uncompressedSize)",
            notify_disconnect=True,
        )

    def stop(self):
        self.isClosed = True
        self.ctxt.close()

    def callback(self, arg):
        self.NTNDA_Viewer.callback(arg)

    def p4pcallback(self, arg):
        if self.isClosed:
            return
        self.struct = arg
        self.callbacksignal.emit()
        self.callbackDoneEvent.wait()
        self.callbackDoneEvent.clear()

    def mycallback(self):
        struct = self.struct
        arg = dict()
        try:
            argtype = str(type(struct))
            if argtype.find("Disconnected") >= 0:
                arg["status"] = "disconnected"
                self.callback(arg)
                self.firstCallback = True
                self.callbackDoneEvent.set()
                return
            if self.firstCallback:
                arg = dict()
                arg["status"] = "connected"
                self.callback(arg)
                self.firstCallback = False
                self.callback(arg)
            arg = dict()
            arg["value"] = struct["value"]
            arg["dimension"] = struct["dimension"]
            arg["codec"] = struct["codec"]
            arg["compressedSize"] = struct["compressedSize"]
            arg["uncompressedSize"] = struct["uncompressedSize"]
            self.callback(arg)
            self.callbackDoneEvent.set()
            return
        except Exception as error:
            arg["exception"] = repr(error)
            self.callback(arg)
            self.callbackDoneEvent.set()
            return
コード例 #6
0
        if now < self._next_allowed:
            S.pause()
            TQ.push(S.resume, delay=self._next_allowed - now)

        else:
            self._next_allowed = N = now + self.period

        with iolock:
            print(now, self.name, V)


period = 1.0 / args.rate

subscriptions = []

for n in range(args.count):
    name = '%s%d' % (args.pvbase, n)
    S = ctxt.monitor(name,
                     RateLimit(name, period),
                     request='record[pipeline=True,queueSize=2]',
                     subscription_arg=True)
    subscriptions.append(S)  # must keep Subscription from being collected

try:
    while True:
        time.sleep(1000)
except KeyboardInterrupt:
    pass
finally:
    TQ.join()
コード例 #7
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()
コード例 #8
0
class pvGetClient(object):
    def __init__(self,
                 pvName,
                 monitor='False',
                 provider='pva',
                 timeout=5.0,
                 repeat=1.0,
                 showValue=False,
                 throw=False,
                 verbose=False,
                 checkPriorCount=False):
        self._lock = Lock()
        self._pvName = pvName
        self._history = {}
        self._priorValue = None
        self._Q = Queue()
        self._S = None
        self._T = None
        self._Op = None
        self._noConnectionYet = True
        self._shutDown = False
        self._provider = provider
        self._throw = throw
        #self._monitor = monitor
        self._repeat = repeat
        self._showValue = showValue
        self._timeout = timeout
        self._verbose = verbose
        self._checkPriorCount = checkPriorCount
        self._ctxt = Context(provider)
        #self._pvGetDone = threading.Event()
        self._pvGetPending = threading.Event()
        self._T = threading.Thread(target=self.pvGetTimeoutLoop,
                                   args=(self._timeout, self._throw,
                                         self._verbose))
        self._T.start()

    def __del__(self):
        if self._ctxt is not None:
            self._ctxt.close()
            self._ctxt = None

    def is_alive(self):
        if not self._T:
            return False
        return self._T.is_alive()

    def pvMonitor(self):
        # Monitor is is easy as p4p provides it.
        if self._ctxt is None:
            self._ctxt = Context(self._provider)
        self._S = self._ctxt.monitor(self._pvName,
                                     self.callback,
                                     notify_disconnect=True)
        # Above code does this:
        # R = Subscription( self._ctxt, self._pvName, self.callback, notify_disconnect=True )
        # R._S = super(Context, self).monitor( name, R._event, request )
        # self._S = R

    def pvGetInitiate(self, timeout=5.0, throw=False, verbose=True):
        #pdb.set_trace()
        # This code block does a synchronous get()
        #return self._ctxt.get( self._pvName, self.callback )

        # Initiate async non-blocking pvGet using a ClientOperation.
        # self.pvGetCallback() handles the response and places it on self._Q
        if self._ctxt is None:
            self._ctxt = Context(self._provider)
        raw_get = super(Context, self._ctxt).get
        try:
            assert self._Op is None
            self._Op = raw_get(self._pvName, self.pvGetCallback)
            self._pvGetPending.set()
        except:
            raise
        return

    def pvGetCallback(self, cbData):
        result = self.callback(cbData)
        #self._ctxt.disconnect()
        #self._noConnectionYet = True
        if result is not None:
            assert self._Q
            try:
                self._Q.put_nowait(result)
                if self._verbose:
                    print("%s: Added result to queue. %d on queue." %
                          (self._pvName, self._Q.qsize()))
            except:
                print("pvGetCallback %s: Error queuing result" % self._pvName)
        return

    def handleResult(self):
        # Get pvGet result from self._Q
        result = False
        try:
            result = self._Q.get(timeout=self._timeout)
        except Empty:
            curTime = time.time()
            raw_stamp = (int(curTime), int((curTime - int(curTime)) * 1e9))
            strTimeStamp = time.strftime("%Y-%m-%d %H:%M:%S",
                                         time.localtime(raw_stamp[0]))
            print('%s %s.%03d Timeout' %
                  (self._pvName, strTimeStamp, float(raw_stamp[1]) / 1e6))
            if self._throw:
                raise TimeoutError()

        if isinstance(result, Exception):
            if self._throw:
                raise result
            return None

        return result

    def pvGetTimeoutLoop(self, timeout=5.0, throw=False, verbose=True):
        status = False
        while not self._shutDown:
            # Wait for something to do.
            status = self._pvGetPending.wait(timeout=timeout)
            self._pvGetPending.clear()
            status = self.handleResult()
            if self._Op:
                self._Op.close()
                self._Op = None

            #self._ctxt.disconnect()
            #self._noConnectionYet = True
            self._ctxt.close()
            self._ctxt = None
            self._noConnectionYet = True

            if self._repeat is None:
                self._shutDown = True

            if self._shutDown:
                break
            time.sleep(self._repeat)

            self.pvGetInitiate()

        # Return on shutdown
        return

    def pvName(self):
        return self._pvName

    def callback(self, cbData):
        pvName = self._pvName
        if isinstance(cbData, (RemoteError, Disconnected, Cancelled)):
            if self._noConnectionYet and isinstance(cbData, Disconnected):
                return None
            if not isinstance(cbData, Cancelled):
                print('%s: %s' % (pvName, cbData))
            return None

        self._noConnectionYet = False
        pvValue = cbData

        # Make sure we have a raw_stamp
        #pdb.set_trace()
        raw_stamp = None
        if hasattr(pvValue, 'raw_stamp'):
            raw_stamp = pvValue.raw_stamp
        #elif hasattr( pvValue, 'timestamp' ):
        #	tsSec = pvValue.timestamp
        #	raw_stamp = ( int(tsSec), int((tsSec - int(tsSec)) * 1e9) )
        elif isinstance(pvValue, dict):
            if 'raw_stamp' in pvValue:
                raw_stamp = pvValue['raw_stamp']
            if 'timestamp' in pvValue:
                raw_stamp = pvValue['timestamp']
            if 'timeStamp' in pvValue:
                raw_stamp = pvValue['timeStamp']

        if raw_stamp is None or len(raw_stamp) != 2 or raw_stamp[0] == 0:
            if self._verbose:
                print("%s: No timestamp found.  Using TOD" % pvName)
            tsSec = time.time()
            raw_stamp = (int(tsSec), int((tsSec - int(tsSec)) * 1e9))

        if isinstance(pvValue, p4p.nt.scalar.ntwrappercommon):
            self.saveNtScalar(pvName, raw_stamp, pvValue)
            return cbData

        if isinstance(pvValue, p4p.wrapper.Value):
            if self._verbose:
                print('%s: ID=%s, type=%s' %
                      (pvName, pvValue.getID(), type(pvValue)))

            pvType = pvValue.type()
            if 'timeStamp' in pvValue:
                fieldTs = (pvValue['timeStamp.secondsPastEpoch'],
                           pvValue['timeStamp.nanoseconds'])
                if fieldTs[0]:
                    raw_stamp = fieldTs

            if pvValue.getID().startswith('epics:nt/NTTable:'):
                tableValue = pvValue['value']
                tableType = pvType['value']
                S, id, tableFields = tableType.aspy()
                assert S == 'S'
                assert id == 'structure'
                tableItems = tableValue.items()
                nCols = len(tableItems)
                nRows = len(tableItems[0][1])
                if self._verbose:
                    print("%s NTTable: nRows=%d, nCols=%d\n%s" %
                          (pvName, nRows, nCols, tableItems))
                for row in range(nRows):
                    # Build up fullName
                    fullName = pvName
                    for col in range(nCols):
                        spec = tableFields[col][1]
                        if spec == 'as':
                            fullName += '.' + tableItems[col][1][row]
                        elif spec != 'av' and spec != 'aU' and spec != 'aS':
                            self.saveValue(
                                fullName + '.' + tableFields[col][0],
                                raw_stamp, tableItems[col][1][row])
                return cbData

            # This method works fpr p2p/Stats and potentially other
            # simple PVStruct based PVs.
            #if pvValue.getID() == 'epics:p2p/Stats:1.0':
            for fieldName in pvValue.keys():
                pvField = pvValue[fieldName]
                if 'timeStamp' in pvField:
                    fieldTs = (pvField['timeStamp.secondsPastEpoch'],
                               pvField['timeStamp.nanoseconds'])
                    if fieldTs[0]:
                        raw_stamp = fieldTs
                fullName = pvName + '.' + fieldName
                if isinstance(pvField, p4p.nt.scalar.ntwrappercommon):
                    cbData = self.saveNtScalar(fullName, raw_stamp,
                                               pvField['value'])
                elif pvField.getID().startswith('epics:nt/NTScalar:'):
                    cbData = self.saveValue(fullName, raw_stamp,
                                            pvField['value'])

        # TODO: Handle other nt types
        return cbData

    def saveNtScalar(self, pvName, raw_stamp, pvValue):
        if self._verbose:
            print('%s: type=%s' % (pvName, type(pvValue)))

        if self._checkPriorCount:
            newValue = int(pvValue)
            if self._priorValue is not None:
                # Check for missed count
                expectedValue = self._priorValue + 1
                if expectedValue != newValue:
                    print('%s: missed %d counts!' %
                          (pvName, newValue - expectedValue))
            self._priorValue = newValue

        # Save value
        self.saveValue(pvName, raw_stamp, pvValue)
        return

    def saveValue(self, pvName, raw_stamp, value):
        #pdb.set_trace()
        if isinstance(value, p4p.nt.scalar.ntnumericarray):
            if value.size == 0:
                return
            value = value[0]
        if isinstance(value, list):
            if len(value) == 0:
                return
            value = value[0]

        # assert pvValue.type() == Scalar:
        if pvName not in self._history:
            self._history[pvName] = []
        self._history[pvName] += [[raw_stamp, value]]

        if self._showValue:
            strTimeStamp = time.strftime("%Y-%m-%d %H:%M:%S",
                                         time.localtime(raw_stamp[0]))
            print('%s %s.%03d %s' % (pvName, strTimeStamp,
                                     float(raw_stamp[1]) / 1e6, float(value)))

        if self._verbose:
            print('%s: value raw_stamp = %s' % (pvName, raw_stamp))
            print('%s: Num values = %d' % (pvName, len(self._history[pvName])))

    def writeValues(self, dirName):
        if not os.path.isdir(dirName):
            os.mkdir(dirName)
        for pvName in self._history:
            saveFile = os.path.join(dirName, pvName + '.pvget')
            try:
                pvHistory = self._history[pvName]
                with open(saveFile, "w") as f:
                    if self._verbose or True:
                        print("Writing %d values to %s ..." %
                              (len(pvHistory), saveFile))
                    # Not using json.dump so output matches similar
                    # stressTestClient pvCapture output
                    #json.dump( pvHistory, f, indent=4 )
                    #continue
                    f.write('[\n')
                    if len(pvHistory) > 1:
                        for tsVal in pvHistory[0:-1]:
                            f.write("\t[ [ %d, %d ], %d ],\n" %
                                    (tsVal[0][0], tsVal[0][1], tsVal[1]))
                    if len(pvHistory) > 0:
                        # Write last value
                        tsVal = pvHistory[-1]
                        f.write("\t[ [ %d, %d ], %d ],\n" %
                                (tsVal[0][0], tsVal[0][1], tsVal[1]))
                    f.write(']\n')
            except BaseException as e:
                print("Error: %s" % e)
                print("Unable to write values to %s" % saveFile)

    def closeSubscription(self):
        self._shutDown = True
        if self._S is not None:
            if self._verbose:
                print("Closing subscription to %s" % self._pvName)
            self._S.close()
            self._S = None

    def __exit__(self):
        self.closeSubscription()
コード例 #9
0
class P4PProvider(QObject) :
    callbacksignal = pyqtSignal()
    def __init__(self):
        QObject.__init__(self)
        self.callbacksignal.connect(self.mycallback)
        self.callbackDoneEvent = Event()
        self.firstCallback = True
        self.isClosed = True
        self.monitorRateOnly = False
        self.ncallbacks = 0
        self.lastTime = time.time() 
        
    def start(self) :
        self.ctxt = Context('pva')
        self.firstCallback = True
        self.isClosed = False
        self.subscription = self.ctxt.monitor(
              getDynamicRecordName(),
              self.p4pcallback,
              request='field()',
              notify_disconnect=True)
    def stop(self) :
        self.isClosed = True
        self.ctxt.close()
    def done(self) :
        pass
    def callback(self,arg) :
        self.viewer.callback(arg)
    def p4pcallback(self,arg) :
        if self.monitorRateOnly :
            self.ncallbacks += 1
            timenow = time.time() 
            timediff = timenow - self.lastTime
            if timediff<1 : return
            print('rate=',round(self.ncallbacks/timediff))
            self.lastTime = timenow
            self.ncallbacks = 0
            return
        if self.isClosed : return
        self.struct = arg;
        self.callbacksignal.emit()
        self.callbackDoneEvent.wait()
        self.callbackDoneEvent.clear()
    def mycallback(self) :
        struct = self.struct
        arg = dict()
        try :
            argtype = str(type(struct))
            if argtype.find('Disconnected')>=0 :
                arg["status"] = "disconnected"
                self.callback(arg)
                self.firstCallback = True
                self.callbackDoneEvent.set()
                return
            if self.firstCallback :
                arg = dict()
                arg["status"] = "connected"
                self.callback(arg)
                self.firstCallback = False
                self.callback(arg)
            data = DynamicRecordData()
            data.name = struct['name']
            data.x = struct['x']
            data.y = struct['y']
            data.xmin = struct['xmin']
            data.xmax = struct['xmax']
            data.ymin = struct['ymin']
            data.ymax = struct['ymax']
            arg = dict()
            arg['value'] = data
            self.callback(arg)
            self.callbackDoneEvent.set()
            return
        except Exception as error:
            arg["exception"] = repr(error)
            self.callback(arg)
            self.callbackDoneEvent.set()
            return