Skip to content

Commit

Permalink
Merge branch 'slaclab:master' into pr-1078
Browse files Browse the repository at this point in the history
  • Loading branch information
YektaY authored May 23, 2024
2 parents 3d0376e + cd256db commit aeee048
Show file tree
Hide file tree
Showing 16 changed files with 1,475 additions and 545 deletions.
33 changes: 33 additions & 0 deletions docs/source/data_plugins/p4p_plugin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,36 @@ Examples
A small pva testing ioc is included under ``examples/testing/pva_testing_ioc.py``. This can be run in order to
generate a couple of test PVs, which can be connected to using the example .ui file under
``examples/pva/pva.ui``.


RPC
---

The P4P data plugin also supports **remote method calls** (RPC) addresses.

RPC addresses allow for calling methods on a target IOC, and receiving back the method's result.
RPC addresses must contain arguments matching the name and data-type of those defined in the target's method.
These arguments are static and set in the widget's channel address.

RPCs can be set using a pva address in the following format::

pva://<address>?<arg_1_name>=<arg_1_value>&<arg_2_name>=<arg_2_value>&...(pydm_pollrate=<poll_rate_float>)

"pydm_pollrate" is an optional parameter, but when included must be placed after the arg name/value pairs in the address.
When "pydm_pollrate" is not used, the last arg name/value pair must still end with a "&" character.
(when not used, the RPC will be called once and not be polled)

Arguments are also optional. When not used, end the address with the "&" character (followed by the optional "pydm_pollrate")::

pva://<address>&(pydm_pollrate=<poll_rate_float>)

Example RPC addresses:

pva://my_address?arg1=value1&
pva://my_address?arg1=value1&arg2=value2&pydm_pollrate=10.5
pva://KLYS:LI12:11:ATTN_CUR&
pva://KLYS:LI12:11:ATTN_CUR&pydm_pollrate=2.0

Additional examples of using RPCs with PyDMLabels are provided in ``examples/rpc/rpc_labels.py``.
To run examples, first make sure ``python examples/testing_ioc/rpc_testing_ioc.py`` is actively
running in another terminal.
57 changes: 57 additions & 0 deletions examples/archiver_time_plot/archiver_time_plot_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from pydm import Display

from qtpy import QtCore
from qtpy.QtWidgets import QHBoxLayout, QApplication, QCheckBox
from pydm.widgets import PyDMArchiverTimePlot


class archiver_time_plot_example(Display):
def __init__(self, parent=None, args=None, macros=None):
super(archiver_time_plot_example, self).__init__(parent=parent, args=args, macros=None)
self.app = QApplication.instance()
self.setup_ui()

def minimumSizeHint(self):
return QtCore.QSize(100, 100)

def ui_filepath(self):
return None

def setup_ui(self):
self.main_layout = QHBoxLayout()
self.setLayout(self.main_layout)
self.plot_live = PyDMArchiverTimePlot(background=[255, 255, 255, 255])
self.plot_archived = PyDMArchiverTimePlot(background=[255, 255, 255, 255])
self.chkbx_live = QCheckBox()
self.chkbx_live.setChecked(True)
self.chkbx_archived = QCheckBox()
self.chkbx_archived.setChecked(True)
self.main_layout.addWidget(self.chkbx_live)
self.main_layout.addWidget(self.plot_live)
self.main_layout.addWidget(self.plot_archived)
self.main_layout.addWidget(self.chkbx_archived)

curve_live = self.plot_live.addYChannel(
y_channel="ca://XCOR:LI29:302:IACT",
name="name",
color="red",
yAxisName="Axis",
useArchiveData=True,
liveData=True,
)

curve_archived = self.plot_archived.addYChannel(
y_channel="ca://XCOR:LI28:302:IACT",
name="name",
color="blue",
yAxisName="Axis",
useArchiveData=True,
liveData=False,
)

self.chkbx_live.stateChanged.connect(lambda x: self.set_live(curve_live, x))
self.chkbx_archived.stateChanged.connect(lambda x: self.set_live(curve_archived, x))

@staticmethod
def set_live(curve, live):
curve.liveData = live
262 changes: 262 additions & 0 deletions examples/rpc/rpc_labels.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>562</width>
<height>502</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This label displays the result of a Remote Procedure Call (RPC), which gets back the result of simple function of the &amp;quot;target object&amp;quot; (connected channel)&lt;/p&gt;&lt;p&gt;The RPC channel specifies two int args and gets back the calculated result of adding them together. Arguments specified in RPC channels must have constant values.&lt;/p&gt;&lt;p&gt;The following 3 example PyDMLabels display the result from 3 different functions with 3 different RPC calls, showcasing use of differing number of args and arg types. (hold middle-click and hover over the result to see the RPC address used)&lt;/p&gt;&lt;p&gt;For this example to work properly, first have running in another terminal &amp;quot;python examples/testing_ioc/rpc_testing_ioc.py&amp;quot;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>(no polling) 2 + 7 = </string>
</property>
</widget>
</item>
<item>
<widget class="PyDMLabel" name="PyDMLabel">
<property name="toolTip">
<string/>
</property>
<property name="precision" stdset="0">
<number>0</number>
</property>
<property name="showUnits" stdset="0">
<bool>false</bool>
</property>
<property name="precisionFromPV" stdset="0">
<bool>true</bool>
</property>
<property name="alarmSensitiveContent" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveBorder" stdset="0">
<bool>true</bool>
</property>
<property name="PyDMToolTip" stdset="0">
<string/>
</property>
<property name="channel" stdset="0">
<string>pva://pv:call:add_two_ints?a=2&amp;b=7&amp;</string>
</property>
<property name="enableRichText" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>(pollrate=0.2 sec) 2 + 7 (*-1 if negate) =</string>
</property>
</widget>
</item>
<item>
<widget class="PyDMLabel" name="PyDMLabel_2">
<property name="toolTip">
<string/>
</property>
<property name="precision" stdset="0">
<number>0</number>
</property>
<property name="showUnits" stdset="0">
<bool>false</bool>
</property>
<property name="precisionFromPV" stdset="0">
<bool>true</bool>
</property>
<property name="alarmSensitiveContent" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveBorder" stdset="0">
<bool>true</bool>
</property>
<property name="PyDMToolTip" stdset="0">
<string/>
</property>
<property name="channel" stdset="0">
<string>pva://pv:call:add_three_ints_negate_option?a=2&amp;b=7&amp;negate=True&amp;pydm_pollrate=0.2</string>
</property>
<property name="enableRichText" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>(pollrate=1 sec) 3 + 7.8 =</string>
</property>
</widget>
</item>
<item>
<widget class="PyDMLabel" name="PyDMLabel_3">
<property name="toolTip">
<string/>
</property>
<property name="precision" stdset="0">
<number>2</number>
</property>
<property name="showUnits" stdset="0">
<bool>false</bool>
</property>
<property name="precisionFromPV" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveContent" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveBorder" stdset="0">
<bool>true</bool>
</property>
<property name="PyDMToolTip" stdset="0">
<string/>
</property>
<property name="channel" stdset="0">
<string>pva://pv:call:add_int_float?a=3&amp;b=7.8&amp;pydm_pollrate=1.0</string>
</property>
<property name="enableRichText" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>(no polling) returns string &quot;Hello!!&quot;</string>
</property>
</widget>
</item>
<item>
<widget class="PyDMLabel" name="PyDMLabel_4">
<property name="toolTip">
<string/>
</property>
<property name="precision" stdset="0">
<number>0</number>
</property>
<property name="showUnits" stdset="0">
<bool>false</bool>
</property>
<property name="precisionFromPV" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveContent" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveBorder" stdset="0">
<bool>true</bool>
</property>
<property name="PyDMToolTip" stdset="0">
<string/>
</property>
<property name="channel" stdset="0">
<string>pva://pv:call:take_return_string?a=Hello&amp;</string>
</property>
<property name="enableRichText" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>(pollrate=1 sec, no args) random float [0,10] = </string>
</property>
</widget>
</item>
<item>
<widget class="PyDMLabel" name="PyDMLabel_5">
<property name="toolTip">
<string/>
</property>
<property name="precision" stdset="0">
<number>2</number>
</property>
<property name="showUnits" stdset="0">
<bool>false</bool>
</property>
<property name="precisionFromPV" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveContent" stdset="0">
<bool>false</bool>
</property>
<property name="alarmSensitiveBorder" stdset="0">
<bool>true</bool>
</property>
<property name="PyDMToolTip" stdset="0">
<string/>
</property>
<property name="channel" stdset="0">
<string>pva://pv:call:no_args&amp;pydm_pollrate=1.0</string>
</property>
<property name="enableRichText" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PyDMLabel</class>
<extends>QLabel</extends>
<header>pydm.widgets.label</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
20 changes: 20 additions & 0 deletions examples/rpc/rpc_testing_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
This is an example of a simple client that sends RPCs.
To demo, first run 'python examples/testing_ioc/rpc_testing_ioc.py'
from another terminal,
then run this file with 'python rpc_testing_client.py'
"""

from p4p.client.thread import Context
from p4p.nt import NTURI

ctx = Context("pva")

# NTURI() lets us wrap argument into Value type needed in rpc call
# https://mdavidsaver.github.io/p4p/nt.html#p4p.nt.NTURI
AidaBPMSURI = NTURI([("a", "i"), ("b", "i")])

request = AidaBPMSURI.wrap("pv:call:add_two_ints", scheme="pva", kws={"a": 7, "b": 3})
response = ctx.rpc("pv:call:add_two_ints", request, timeout=10)

print(response) # should print something like 'Wed Dec 31 16:00:00 1969 10'
Loading

0 comments on commit aeee048

Please sign in to comment.