1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
.. _intro-event_handling:
Event handling
==============
.. include:: ../substitutions.sub
VISA supports generating events on the instrument side usually when a register
change and handling then on the controller using two different mechanisms:
- storing the events in a queue
- calling a dedicated handler function registered for that purpose when the
event occurs
PyVISA supports using both mechanism and tries to provide a convenient interface
to both. Below we give a couple of example of how to use each mechanism (using
a fictional instrument).
Waiting on events using a queue
-------------------------------
First let's have a look at how to wait for an event to occur which will be stored
in a queue.
.. code-block:: python
from pyvisa import ResourceManager, constants
rm = ResourceManager
with rm.open_resource("TCPIP::192.168.0.2::INSTR") as instr:
# Type of event we want to be notified about
event_type = constants.EventType.service_request
# Mechanism by which we want to be notified
event_mech = constants.EventMechanism.queue
instr.enable_event(event_type, event_mech)
# Instrument specific code to enable service request
# (for example on operation complete OPC)
instr.write("*SRE 1")
instr.write("INIT")
# Wait for the event to occur
response = instr.wait_on_event(event_type, 1000)
assert response.event.event_type == event_type
assert response.timed_out = False
instr.disable_event(event_type, event_mech)
Let's examine the code. First, to avoid repeating ourselves, we store the type
of event we want to be notified about and the mechanism we want to use to be notified.
And we enable event notifications.
.. code:: python
# Type of event we want to be notified about
event_type = constants.EventType.service_request
# Mechanism by which we want to be notified
event_mech = constants.EventMechanism.queue
instr.enable_event(event_type, event_mech)
Next we need to setup our instrument to generate the kind of event at the right
time and start the operation that will lead to the event. For the sake of that
example we are going to consider a Service Request event. Usually service request
can be enabled for a range of register state, the details depending on the
instrument. One useful case is to generate a service request when an operation
is complete which is what we are pretending to do here.
Finally we wait for the event to occur and we specify a timeout of 1000ms to
avoid waiting forever. Once we receive the event we disable event handling.
Registering handlers for event
------------------------------
Rather than waiting for an event, it can sometimes be convenient to take immediate
action when an event occurs, in which case having the VISA library call a function
directly can be useful. Let's see how.
.. note::
One can enable event handling using both mechanisms (``constants.EventMechanism.all``)
.. code-block:: python
from time import sleep
from pyvisa import ResourceManager, constants
rm = ResourceManager
def handle_event(resource, event, user_handle):
resource.called = True
print(f"Handled event {event.event_type} on {resource}")
with rm.open_resource("TCPIP::192.168.0.2::INSTR") as instr:
instr.called = False
# Type of event we want to be notified about
event_type = constants.EventType.service_request
# Mechanism by which we want to be notified
event_mech = constants.EventMechanism.handler
wrapped = instr.wrap_handler(handle_event)
user_handle = instr.install_handler(event_type, wrapped, 42)
instr.enable_event(event_type, event_mech, None)
# Instrument specific code to enable service request
# (for example on operation complete OPC)
instr.write("*SRE 1")
instr.write("INIT")
while not instr.called:
sleep(10)
instr.disable_event(event_type, event_mech)
instr.uninstall_handler(event_type, wrapped, user_handle)
Our handler function needs to have a specific signature to be used by VISA. The
expected signature is (session, event_type, event_context, user_handle). This
signature is not exactly convenient since it forces us to deal with a number of
low-level details such session (ID of a resource in VISA) and event_context that
serves the same purpose for events. One way to get a nicer interface is to wrap
the handler using the |wrap_handler| method of the |Resource| object. The wrapped
function is expected to have the following signature: (resource, event, user_handle)
which the signature of our handler:
.. code:: python
def handle_event(resource, event, user_handle):
resource.called = True
print(f"Handled event {event.event_type} on {resource}")
And before installing the handler, we wrap it:
.. code:: python
wrapped = instr.wrap_handler(handle_event)
When wrapping a handler, you need to use the resource on which it is going to be
installed to wrap it. Furthermore note that in order to uninstall a handler you
need to keep the wrapped version around.
Next we install the handler and enable the event processing:
.. code:: python
user_handle = instr.install_handler(event_type, wrapped, 42)
instr.enable_event(event_type, event_mech, None)
When installing a handler one can optionally specify a user handle that will be
passed to the handler. This handle can be used to identify which handler is called
when registering the same handler multiple times on the same resource. That value
may have to be converted by the backend. As a consequence the value passed to
the handler may not the same as the value registered and its value will be to
the backend dependent. For this reason you need to keep the converted value
returned by install handler to uninstall the handler at a later time.
.. note::
In the case of ctwrapper that ships with PyVISA, the value is converted
to an equivalent ctypes object (c_float for a float, c_int for an integer, etc)
|