From 10ecfa40739c6ee0748449d9c4e95b03be17394b Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Mon, 3 Dec 2018 22:48:57 +0100 Subject: [PATCH 01/10] add preliminary support for event queueing --- pyvisa-py/highlevel.py | 242 +++++++++++++++++++++++++++++++++++------ pyvisa-py/sessions.py | 92 +++++++++++++++- 2 files changed, 300 insertions(+), 34 deletions(-) diff --git a/pyvisa-py/highlevel.py b/pyvisa-py/highlevel.py index 6c361ebc..69c632b4 100644 --- a/pyvisa-py/highlevel.py +++ b/pyvisa-py/highlevel.py @@ -93,25 +93,53 @@ def get_debug_info(): def _init(self): #: map session handle to session object. - #: dict[int, session.Session] + #: dict[int, sessions.Session] self.sessions = {} - def _register(self, obj): + #: map event handle to event object. + #: dict[int, Event] + self.events = {} + + def _generate_handle(self): + """Creates a random but unique handle for a session object, + event or find list + + :return: handle + :rtype: int + """ + # VISA sessions, events, and find lists get unique logical identifiers + # from the same pool + handle = None + while (handle is None or + handle in self.sessions or + handle in self.events): + handle = random.randint(1000000, 9999999) + return handle + + def _register_session(self, obj): """Creates a random but unique session handle for a session object, - register it in the sessions dictionary and return the value + registers it in the sessions dictionary and returns the value :param obj: a session object. :return: session handle :rtype: int """ - session = None - - while session is None or session in self.sessions: - session = random.randint(1000000, 9999999) - + session = self._generate_handle() self.sessions[session] = obj return session + def _register_event(self, obj): + """Creates a random but unique session handle for an event object, + registers it in the event dictionary and returns the value + + :param obj: an event object. + :return: event handle + :rtype: int + """ + event_handle = self._generate_handle() + self.events[event_handle] = obj + return event_handle + def _return_handler(self, ret_value, func, arguments): """Check return values for errors and warnings. @@ -182,18 +210,20 @@ def open(self, session, resource_name, try: open_timeout = int(open_timeout) except ValueError: - raise ValueError('open_timeout (%r) must be an integer (or compatible type)' % open_timeout) + raise ValueError( + 'open_timeout (%r) must be an integer (or compatible type)' % open_timeout) try: parsed = rname.parse_resource_name(resource_name) except rname.InvalidResourceName: return 0, StatusCode.error_invalid_resource_name - cls = sessions.Session.get_session_class(parsed.interface_type_const, parsed.resource_class) + cls = sessions.Session.get_session_class( + parsed.interface_type_const, parsed.resource_class) sess = cls(session, resource_name, parsed, open_timeout) - return self._register(sess), StatusCode.success + return self._register_session(sess), StatusCode.success def clear(self, session): """Clears a device. @@ -214,7 +244,8 @@ def gpib_command(self, session, command_byte): """Write GPIB command byte on the bus. Corresponds to viGpibCommand function of the VISA library. - See: https://linux-gpib.sourceforge.io/doc_html/gpib-protocol.html#REFERENCE-COMMAND-BYTES + #REFERENCE-COMMAND-BYTES + See: https://linux-gpib.sourceforge.io/doc_html/gpib-protocol.html :param command_byte: command byte to send :type command_byte: int, must be [0 255] @@ -280,12 +311,16 @@ def close(self, session): :return: return value of the library call. :rtype: VISAStatus """ - try: - sess = self.sessions[session] - if sess is not self: - sess.close() - except KeyError: - return StatusCode.error_invalid_object + for container in (self.sessions, self.events): + try: + obj = container[session] + if obj is not self: + obj.close() + del container[session] + return StatusCode.success + except KeyError: + pass + return StatusCode.error_invalid_object def open_default_resource_manager(self): """This function returns a session to the Default Resource Manager resource. @@ -295,7 +330,7 @@ def open_default_resource_manager(self): :return: Unique logical identifier to a Default Resource Manager session, return value of the library call. :rtype: session, VISAStatus """ - return self._register(self), StatusCode.success + return self._register_session(self), StatusCode.success def list_resources(self, session, query='?*::INSTR'): """Returns a tuple of all connected devices matching query. @@ -368,12 +403,14 @@ def get_attribute(self, session, attribute): :return: The state of the queried attribute for a specified resource, return value of the library call. :rtype: unicode | str | list | int, VISAStatus """ - try: - sess = self.sessions[session] - except KeyError: - return None, StatusCode.error_invalid_object + for container in (self.sessions, self.events): + try: + obj = container[session] + break + except KeyError: + return None, StatusCode.error_invalid_object - return sess.get_attribute(attribute) + return obj.get_attribute(attribute) def set_attribute(self, session, attribute, attribute_state): """Sets the state of an attribute. @@ -387,12 +424,14 @@ def set_attribute(self, session, attribute, attribute_state): :rtype: VISAStatus """ - try: - sess = self.sessions[session] - except KeyError: - return StatusCode.error_invalid_object + for container in (self.sessions, self.events): + try: + obj = container[session] + break + except KeyError: + return StatusCode.error_invalid_object - return sess.set_attribute(attribute, attribute_state) + return obj.set_attribute(attribute, attribute_state) def lock(self, session, lock_type, timeout, requested_key=None): """Establishes an access mode to the specified resources. @@ -431,9 +470,146 @@ def unlock(self, session): return sess.unlock() def disable_event(self, session, event_type, mechanism): - # TODO: implement this for GPIB finalization - pass + """Disables notification of the specified event type(s) via the specified mechanism(s). + + Corresponds to viDisableEvent function of the VISA library. + + :param session: Unique logical identifier to a session. + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be disabled. + (Constants.VI_QUEUE, .VI_HNDLR, .VI_SUSPEND_HNDLR, .VI_ALL_MECH) + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + try: + sess = self.sessions[session] + except KeyError: + return StatusCode.error_invalid_object + + # TODO: add support for VI_HNDLR, VI_SUSPEND_HNDLR, VI_ALL_MECH + if mechanism != constants.VI_QUEUE: + return StatusCode.error_nonsupported_mechanism + + return sess.disable_event(event_type, mechanism) def discard_events(self, session, event_type, mechanism): - # TODO: implement this for GPIB finalization - pass + """Discards event occurrences for specified event types and mechanisms in a session. + + Corresponds to viDiscardEvents function of the VISA library. + + :param session: Unique logical identifier to a session. + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be discarded. + (Constants.VI_QUEUE, .VI_SUSPEND_HNDLR, .VI_ALL_MECH) + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + try: + sess = self.sessions[session] + except KeyError: + return StatusCode.error_invalid_object + + # TODO: add support for VI_HNDLR, VI_SUSPEND_HNDLR, VI_ALL_MECH + if mechanism != constants.VI_QUEUE: + return StatusCode.error_nonsupported_mechanism + + return sess.discard_events(event_type, mechanism) + + def enable_event(self, session, event_type, mechanism, context=None): + """Enable event occurrences for specified event types and mechanisms in a session. + + Corresponds to viEnableEvent function of the VISA library. + + :param session: Unique logical identifier to a session. + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be enabled. + (Constants.VI_QUEUE, .VI_HNDLR, .VI_SUSPEND_HNDLR) + :param context: + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + if context is None: + context = constants.VI_NULL + elif context != constants.VI_NULL: + warnings.warn('In enable_event, context will be set VI_NULL.') + context = constants.VI_NULL # according to spec VPP-4.3, section 3.7.3.1 + + try: + sess = self.sessions[session] + except KeyError: + return StatusCode.error_invalid_object + + return sess.enable_event(event_type, mechanism, context) + + def install_handler(self, session, event_type, handler, user_handle): + """Installs handlers for event callbacks. + + Corresponds to viInstallHandler function of the VISA library. + + :param session: Unique logical identifier to a session. + :param event_type: Logical event identifier. + :param handler: Interpreted as a valid reference to a handler to be installed by a client application. + :param user_handle: A value specified by an application that can be used for identifying handlers + uniquely for an event type. + :returns: a handler descriptor which consists of three elements: + - handler (a python callable) + - user handle (a ctypes object) + - ctypes handler (ctypes object wrapping handler) + and return value of the library call. + :rtype: int, :class:`pyvisa.constants.StatusCode` + """ + try: + sess = self.sessions[session] + except KeyError: + return StatusCode.error_invalid_object + + return sess.install_handler(event_type, handler, user_handle) + + def uninstall_handler(self, session, event_type, handler, user_handle=None): + """Uninstalls handlers for events. + + Corresponds to viUninstallHandler function of the VISA library. + + :param session: Unique logical identifier to a session. + :param event_type: Logical event identifier. + :param handler: Interpreted as a valid reference to a handler to be uninstalled by a client application. + :param user_handle: A value specified by an application that can be used for identifying handlers + uniquely in a session for an event. + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + try: + sess = self.sessions[session] + except KeyError: + return StatusCode.error_invalid_object + + return sess.uninstall_handler(event_type, handler, user_handle) + + def wait_on_event(self, session, in_event_type, timeout): + """Waits for an occurrence of the specified event for a given session. + + Corresponds to viWaitOnEvent function of the VISA library. + + :param session: Unique logical identifier to a session. + :param in_event_type: Logical identifier of the event(s) to wait for. + :param timeout: Absolute time period in time units that the resource shall wait for a specified event to + occur before returning the time elapsed error. The time unit is in milliseconds. + :return: - Logical identifier of the event actually received + - A handle specifying the unique occurrence of an event + - return value of the library call. + :rtype: - eventtype + - event + - :class:`pyvisa.constants.StatusCode` + """ + try: + sess = self.sessions[session] + except KeyError: + return None, None, StatusCode.error_invalid_object + + # FIXME: for now calling Session's own wait_on_event() is the only way + # to notify PyVisaLibrary of the event and store it + out_event_type, event_attrs, ret = sess.wait_on_event( + in_event_type, timeout) + event_handle = self._register_event(event_attrs) + + return out_event_type, event_handle, ret diff --git a/pyvisa-py/sessions.py b/pyvisa-py/sessions.py index b6fbd898..4b875a1d 100644 --- a/pyvisa-py/sessions.py +++ b/pyvisa-py/sessions.py @@ -203,7 +203,8 @@ def __init__(self, resource_manager_session, resource_name, parsed=None, constants.VI_ATTR_RSRC_CLASS: parsed.resource_class, constants.VI_ATTR_INTF_TYPE: parsed.interface_type, constants.VI_ATTR_TMO_VALUE: (self._get_timeout, - self._set_timeout)} + self._set_timeout), + constants.VI_ATTR_MAX_QUEUE_LENGTH: 50} #: Timeout expressed in second or None for the absence of a timeout. #: The default value is set when calling @@ -537,3 +538,92 @@ def _set_timeout(self, attribute, value): else: self.timeout = value / 1000.0 return StatusCode.success + + def disable_event(self, event_type, mechanism): + """Disables notification of the specified event type(s) via the specified mechanism(s). + + Corresponds to viDisableEvent function of the VISA library. + + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be disabled. + (Constants.VI_QUEUE, .VI_HNDLR, .VI_SUSPEND_HNDLR, .VI_ALL_MECH) + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + raise NotImplementedError + + def discard_events(self, event_type, mechanism): + """Discards event occurrences for specified event types and mechanisms in a session. + + Corresponds to viDiscardEvents function of the VISA library. + + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be discarded. + (Constants.VI_QUEUE, .VI_SUSPEND_HNDLR, .VI_ALL_MECH) + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + raise NotImplementedError + + def enable_event(self, event_type, mechanism, context=None): + """Enable event occurrences for specified event types and mechanisms in a session. + + Corresponds to viEnableEvent function of the VISA library. + + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be enabled. + (Constants.VI_QUEUE, .VI_HNDLR, .VI_SUSPEND_HNDLR) + :param context: + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + raise NotImplementedError + + def install_handler(self, event_type, handler, user_handle): + """Installs handlers for event callbacks. + + Corresponds to viInstallHandler function of the VISA library. + + :param event_type: Logical event identifier. + :param handler: Interpreted as a valid reference to a handler to be installed by a client application. + :param user_handle: A value specified by an application that can be used for identifying handlers + uniquely for an event type. + :returns: a handler descriptor which consists of three elements: + - handler (a python callable) + - user handle (a ctypes object) + - ctypes handler (ctypes object wrapping handler) + and return value of the library call. + :rtype: int, :class:`pyvisa.constants.StatusCode` + """ + raise NotImplementedError + + def uninstall_handler(self, event_type, handler, user_handle=None): + """Uninstalls handlers for events. + + Corresponds to viUninstallHandler function of the VISA library. + + :param event_type: Logical event identifier. + :param handler: Interpreted as a valid reference to a handler to be uninstalled by a client application. + :param user_handle: A value specified by an application that can be used for identifying handlers + uniquely in a session for an event. + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + raise NotImplementedError + + def wait_on_event(self, in_event_type, timeout): + """Waits for an occurrence of the specified event for a given session. + + Corresponds to viWaitOnEvent function of the VISA library. + + :param in_event_type: Logical identifier of the event(s) to wait for. + :param timeout: Absolute time period in time units that the resource shall wait for a specified event to + occur before returning the time elapsed error. The time unit is in milliseconds. + :return: - Logical identifier of the event actually received + - A handle specifying the unique occurrence of an event + - return value of the library call. + :rtype: - eventtype + - event object # TODO + - :class:`pyvisa.constants.StatusCode` + """ + raise NotImplementedError From 9f19bc992e89ae9c16f0a8af1090027cbb54edbd Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Mon, 3 Dec 2018 22:49:16 +0100 Subject: [PATCH 02/10] add preliminary support for GPIB INSTR events --- pyvisa-py/gpib.py | 158 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 6 deletions(-) diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py index 6a11958f..2955d086 100644 --- a/pyvisa-py/gpib.py +++ b/pyvisa-py/gpib.py @@ -25,6 +25,7 @@ def _patch_Gpib(): if not hasattr(Gpib, "close"): _old_del = Gpib.__del__ + def _inner(self): _old_del(self) self._own = False @@ -57,6 +58,8 @@ def _find_listeners(): # TODO: Check board indices other than 0. BOARD = 0 # TODO: Check secondary addresses. + + @Session.register(constants.InterfaceType.gpib, 'INSTR') class GPIBSession(Session): """A GPIB Session that uses linux-gpib to do the low level communication. @@ -82,10 +85,20 @@ def after_parsing(self): timeout = 13 send_eoi = 1 eos_mode = 0 - self.interface = Gpib(name=minor, pad=pad, sad=sad, timeout=timeout, send_eoi=send_eoi, eos_mode=eos_mode) - self.controller = Gpib(name=minor) # this is the bus controller device + self.interface = Gpib(name=minor, pad=pad, sad=sad, + timeout=timeout, send_eoi=send_eoi, eos_mode=eos_mode) + self.controller = Gpib(name=minor) # this is the bus controller device # force timeout setting to interface - self.set_attribute(constants.VI_ATTR_TMO_VALUE, attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default) + self.set_attribute(constants.VI_ATTR_TMO_VALUE, + attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default) + + # prepare set of allowed events + self.valid_event_types = [constants.VI_EVENT_IO_COMPLETION, + constants.VI_EVENT_SERVICE_REQ] + + self.enabled_queue_events = set() + + self.event_queue = [] def _get_timeout(self, attribute): if self.interface: @@ -150,9 +163,9 @@ def read(self, count): """ # 0x2000 = 8192 = END - checker = lambda current: self.interface.ibsta() & 8192 + def checker(current): return self.interface.ibsta() & 8192 - reader = lambda: self.interface.read(count) + def reader(): return self.interface.read(count) return self._read(reader, count, checker, False, None, False, gpib.GpibError) @@ -171,7 +184,7 @@ def write(self, data): try: self.interface.write(data) - count = self.interface.ibcnt() # number of bytes transmitted + count = self.interface.ibcnt() # number of bytes transmitted return count, StatusCode.success @@ -378,3 +391,136 @@ def read_stb(self): return self.interface.serial_poll(), StatusCode.success except gpib.GpibError: return 0, StatusCode.error_system_error + + def disable_event(self, event_type, mechanism): + """Disables notification of the specified event type(s) via the specified mechanism(s). + + Corresponds to viDisableEvent function of the VISA library. + + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be disabled. + (Constants.VI_QUEUE, .VI_HNDLR, .VI_SUSPEND_HNDLR, .VI_ALL_MECH) + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + + if event_type not in self.valid_event_types: + return StatusCode.error_invalid_event + + if mechanism in (constants.VI_QUEUE, constants.VI_ALL_MECH): + if event_type not in self.enabled_queue_events: + return StatusCode.success_event_already_disabled + + self.enabled_queue_events.remove(event_type) + return StatusCode.success + + return StatusCode.error_invalid_mechanism + + def discard_events(self, event_type, mechanism): + """Discards event occurrences for specified event types and mechanisms in a session. + + Corresponds to viDiscardEvents function of the VISA library. + + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be discarded. + (Constants.VI_QUEUE, .VI_SUSPEND_HNDLR, .VI_ALL_MECH) + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + if event_type not in self.valid_event_types: + return StatusCode.error_invalid_event + + if mechanism in (constants.VI_QUEUE, constants.VI_ALL_MECH): + self.event_queue = [(t, a) for t, a in self.event_queue if not ( + event_type == constants.VI_ALL_ENABLED_EVENTS or t == event_type)] + return StatusCode.success + + return StatusCode.error_invalid_mechanism + + def enable_event(self, event_type, mechanism, context=None): + """Enable event occurrences for specified event types and mechanisms in a session. + + Corresponds to viEnableEvent function of the VISA library. + + :param event_type: Logical event identifier. + :param mechanism: Specifies event handling mechanisms to be enabled. + (Constants.VI_QUEUE, .VI_HNDLR, .VI_SUSPEND_HNDLR) + :param context: + :return: return value of the library call. + :rtype: :class:`pyvisa.constants.StatusCode` + """ + + if event_type not in self.valid_event_types: + return StatusCode.error_invalid_event + + if mechanism in (constants.VI_QUEUE, constants.VI_ALL_MECH): + # enable GPIB autopoll + try: + self.controller.config(7, 1) + except gpib.GpibError: + return StatusCode.error_invalid_setup + + if event_type in self.enabled_queue_events: + return StatusCode.success_event_already_enabled + else: + self.enabled_queue_events.add(event_type) + return StatusCode.success + + # mechanisms which are not implemented: constants.VI_SUSPEND_HNDLR, constants.VI_ALL_MECH + return StatusCode.error_invalid_mechanism + + def wait_on_event(self, in_event_type, timeout): + """Waits for an occurrence of the specified event for a given session. + + Corresponds to viWaitOnEvent function of the VISA library. + + :param in_event_type: Logical identifier of the event(s) to wait for. + :param timeout: Absolute time period in time units that the resource shall wait for a specified event to + occur before returning the time elapsed error. The time unit is in milliseconds. + :return: - Logical identifier of the event actually received + - A handle specifying the unique occurrence of an event + - return value of the library call. + :rtype: - eventtype + - event object # TODO + - :class:`pyvisa.constants.StatusCode` + """ + + if in_event_type not in self.valid_event_types: + return StatusCode.error_invalid_event + + if in_event_type not in self.enabled_queue_events: + return StatusCode.error_not_enabled + + # if the event queue is empty, wait for more events + if not self.event_queue: + old_timeout = self._get_timeout(None) + self._set_timeout(None, timeout) + + event_mask = 0 + + if in_event_type in (constants.VI_EVENT_IO_COMPLETION, constants.VI_ALL_ENABLED_EVENTS): + event_mask |= gpib.CMPL + + if in_event_type in (constants.VI_EVENT_SERVICE_REQ, constants.VI_ALL_ENABLED_EVENTS): + event_mask |= gpib.RQS + + if timeout != 0: + event_mask |= gpib.TIMO + + self.interface.wait(event_mask) + sta = self.interface.ibsta() + + self._set_timeout(None, old_timeout) + + # TODO: set event attributes + if gpib.CMPL & event_mask & sta: + self.event_queue.append((constants.VI_EVENT_IO_COMPLETION, {})) + + if gpib.RQS & event_mask & sta: + self.event_queue.append((constants.VI_EVENT_SERVICE_REQ, {})) + + try: + out_event_type, event_data = self.event_queue.pop() + return out_event_type, event_data, StatusCode.error_timeout + except IndexError: + return None, None, StatusCode.error_timeout From 97761c5e5fdaac3f1369df3341330c7357ca32d3 Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Tue, 4 Dec 2018 00:53:47 +0100 Subject: [PATCH 03/10] fix GPIBSession.wait_on_event() --- pyvisa-py/gpib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py index 2955d086..6e269fda 100644 --- a/pyvisa-py/gpib.py +++ b/pyvisa-py/gpib.py @@ -493,13 +493,13 @@ def wait_on_event(self, in_event_type, timeout): # if the event queue is empty, wait for more events if not self.event_queue: - old_timeout = self._get_timeout(None) + old_timeout, _ = self._get_timeout(None) self._set_timeout(None, timeout) event_mask = 0 if in_event_type in (constants.VI_EVENT_IO_COMPLETION, constants.VI_ALL_ENABLED_EVENTS): - event_mask |= gpib.CMPL + event_mask |= 0x100 # CMPL if in_event_type in (constants.VI_EVENT_SERVICE_REQ, constants.VI_ALL_ENABLED_EVENTS): event_mask |= gpib.RQS @@ -513,7 +513,7 @@ def wait_on_event(self, in_event_type, timeout): self._set_timeout(None, old_timeout) # TODO: set event attributes - if gpib.CMPL & event_mask & sta: + if 0x100 & event_mask & sta: self.event_queue.append((constants.VI_EVENT_IO_COMPLETION, {})) if gpib.RQS & event_mask & sta: @@ -523,4 +523,4 @@ def wait_on_event(self, in_event_type, timeout): out_event_type, event_data = self.event_queue.pop() return out_event_type, event_data, StatusCode.error_timeout except IndexError: - return None, None, StatusCode.error_timeout + return in_event_type, None, StatusCode.error_timeout From 7f68a8250c5455bdcbf3e437647b215558a4880a Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Wed, 5 Dec 2018 23:14:06 +0100 Subject: [PATCH 04/10] fix timeout bugs in GPIBSession.wait_on_event() --- pyvisa-py/gpib.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py index 6e269fda..ad3ce0c1 100644 --- a/pyvisa-py/gpib.py +++ b/pyvisa-py/gpib.py @@ -493,8 +493,8 @@ def wait_on_event(self, in_event_type, timeout): # if the event queue is empty, wait for more events if not self.event_queue: - old_timeout, _ = self._get_timeout(None) - self._set_timeout(None, timeout) + old_timeout = self.timeout + self.timeout = timeout event_mask = 0 @@ -510,7 +510,7 @@ def wait_on_event(self, in_event_type, timeout): self.interface.wait(event_mask) sta = self.interface.ibsta() - self._set_timeout(None, old_timeout) + self.timeout = old_timeout # TODO: set event attributes if 0x100 & event_mask & sta: @@ -521,6 +521,7 @@ def wait_on_event(self, in_event_type, timeout): try: out_event_type, event_data = self.event_queue.pop() - return out_event_type, event_data, StatusCode.error_timeout + return out_event_type, event_data, StatusCode.success except IndexError: return in_event_type, None, StatusCode.error_timeout + From 25784a55640dc7f28c1e1cabb5c0b1c335ad022d Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Thu, 6 Dec 2018 22:05:22 +0100 Subject: [PATCH 05/10] change GPIBSession.valid_event_types to a set --- pyvisa-py/gpib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py index ad3ce0c1..560c147b 100644 --- a/pyvisa-py/gpib.py +++ b/pyvisa-py/gpib.py @@ -93,8 +93,8 @@ def after_parsing(self): attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default) # prepare set of allowed events - self.valid_event_types = [constants.VI_EVENT_IO_COMPLETION, - constants.VI_EVENT_SERVICE_REQ] + self.valid_event_types = {constants.VI_EVENT_IO_COMPLETION, + constants.VI_EVENT_SERVICE_REQ} self.enabled_queue_events = set() From a218881bf3d0bd3d1a6ad76edf969f054bac766c Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Thu, 6 Dec 2018 22:59:36 +0100 Subject: [PATCH 06/10] document PyVisaLibrary._generate_handle and corresponding VISA functions --- pyvisa-py/highlevel.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pyvisa-py/highlevel.py b/pyvisa-py/highlevel.py index 69c632b4..36c9b83a 100644 --- a/pyvisa-py/highlevel.py +++ b/pyvisa-py/highlevel.py @@ -101,8 +101,12 @@ def _init(self): self.events = {} def _generate_handle(self): - """Creates a random but unique handle for a session object, - event or find list + """Creates a random and unique handle. + + Handles are used for: + - session object (viSession) + - events (viEvent) + - find list (viFindList) :return: handle :rtype: int From 5717e23e4b344a7b76f2b6fb0e4a8e89697d9008 Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Fri, 7 Dec 2018 15:53:23 +0100 Subject: [PATCH 07/10] remove mechanism check from Session --- pyvisa-py/highlevel.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pyvisa-py/highlevel.py b/pyvisa-py/highlevel.py index 36c9b83a..38d301b4 100644 --- a/pyvisa-py/highlevel.py +++ b/pyvisa-py/highlevel.py @@ -490,10 +490,6 @@ def disable_event(self, session, event_type, mechanism): except KeyError: return StatusCode.error_invalid_object - # TODO: add support for VI_HNDLR, VI_SUSPEND_HNDLR, VI_ALL_MECH - if mechanism != constants.VI_QUEUE: - return StatusCode.error_nonsupported_mechanism - return sess.disable_event(event_type, mechanism) def discard_events(self, session, event_type, mechanism): @@ -513,10 +509,6 @@ def discard_events(self, session, event_type, mechanism): except KeyError: return StatusCode.error_invalid_object - # TODO: add support for VI_HNDLR, VI_SUSPEND_HNDLR, VI_ALL_MECH - if mechanism != constants.VI_QUEUE: - return StatusCode.error_nonsupported_mechanism - return sess.discard_events(event_type, mechanism) def enable_event(self, session, event_type, mechanism, context=None): From efd7bf0e3e278a7fbbf81482b7cd6d31d4118872 Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Sun, 9 Dec 2018 12:44:23 +0100 Subject: [PATCH 08/10] add a simple GPIBEvent class --- pyvisa-py/gpib.py | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py index 560c147b..1d2cedb6 100644 --- a/pyvisa-py/gpib.py +++ b/pyvisa-py/gpib.py @@ -38,6 +38,46 @@ def _inner(self): raise +class GPIBEvent(object): + """A simple event object that holds event attributes. + """ + + def __init__(self, attrs): + """Initialize GPIBEvent with an attribute dictionary. + + :param attrs: Event attributes to be stored. + :type attrs: dict or dictionary-like + """ + self.attrs = {attribute: value for attribute, value in attrs.items()} + + def __del__(self): + self.close() + + def get_attribute(self, attr): + """Retrieves the state of an attribute. + + Corresponds to viGetAttribute function of the VISA library for this particular event. + + :param attribute: Event attribute for which the state query is made (see Attributes.*) + :return: The state of the queried attribute, return value describing success. + :rtype: unicode | str | list | int, VISAStatus + """ + try: + return self.attrs[attr], StatusCode.success + except KeyError: + return None, StatusCode.error_nonsupported_attribute + + def close(self): + """Closes the event. + + Corresponds to viClose function of the VISA library. + + :return: return value of the library call. + :rtype: VISAStatus + """ + return StatusCode.success + + def _find_listeners(): """Find GPIB listeners. """ @@ -514,10 +554,12 @@ def wait_on_event(self, in_event_type, timeout): # TODO: set event attributes if 0x100 & event_mask & sta: - self.event_queue.append((constants.VI_EVENT_IO_COMPLETION, {})) + self.event_queue.append( + (constants.VI_EVENT_IO_COMPLETION, GPIBEvent({}))) if gpib.RQS & event_mask & sta: - self.event_queue.append((constants.VI_EVENT_SERVICE_REQ, {})) + self.event_queue.append( + (constants.VI_EVENT_SERVICE_REQ, GPIBEvent({}))) try: out_event_type, event_data = self.event_queue.pop() From fbcb51d74a860d9e7a7d30511e163d039aff64cc Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Mon, 10 Dec 2018 02:08:18 +0100 Subject: [PATCH 09/10] set basic GPIBEvent attributes --- pyvisa-py/gpib.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py index 1d2cedb6..f1edeef6 100644 --- a/pyvisa-py/gpib.py +++ b/pyvisa-py/gpib.py @@ -554,12 +554,24 @@ def wait_on_event(self, in_event_type, timeout): # TODO: set event attributes if 0x100 & event_mask & sta: + evt_type = constants.VI_EVENT_IO_COMPLETION + # TODO: implement all event attributes + # VI_ATTR_EVENT_TYPE: VI_EVENT_IO_COMPLETION, + # VI_ATTR_STATUS: return code of the asynchronous operation that has completed, + # VI_ATTR_JOB_ID: job ID of the asynchronous operation that has completed, + # VI_ATTR_BUFFER: the address of the buffer that was used in the asynchronous operation, + # VI_ATTR_RET_COUNT/VI_ATTR_RET_COUNT_32/VI_ATTR_RET_COUNT_64: number of elements that were asynchronously transferred, + # VI_ATTR_OPER_NAME: name of the operation generating the event + attrs = { + constants.VI_ATTR_EVENT_TYPE: constants.VI_EVENT_IO_COMPLETION} self.event_queue.append( - (constants.VI_EVENT_IO_COMPLETION, GPIBEvent({}))) + (constants.VI_EVENT_IO_COMPLETION, + GPIBEvent(attrs))) if gpib.RQS & event_mask & sta: self.event_queue.append( - (constants.VI_EVENT_SERVICE_REQ, GPIBEvent({}))) + (constants.VI_EVENT_SERVICE_REQ, + GPIBEvent({constants.VI_ATTR_EVENT_TYPE: constants.VI_EVENT_SERVICE_REQ}))) try: out_event_type, event_data = self.event_queue.pop() From 5fa23fd58d7e4600681413306792cfe4991fb96b Mon Sep 17 00:00:00 2001 From: Tomislav Ivek Date: Wed, 12 Dec 2018 09:07:32 +0100 Subject: [PATCH 10/10] introduce a generic EventData class --- pyvisa-py/gpib.py | 56 +++++-------------------------------------- pyvisa-py/sessions.py | 45 ++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py index f1edeef6..f8bf69db 100644 --- a/pyvisa-py/gpib.py +++ b/pyvisa-py/gpib.py @@ -15,7 +15,7 @@ from pyvisa import constants, logger, attributes -from .sessions import Session, UnknownAttribute +from .sessions import Session, UnknownAttribute, EventData try: import gpib @@ -38,46 +38,6 @@ def _inner(self): raise -class GPIBEvent(object): - """A simple event object that holds event attributes. - """ - - def __init__(self, attrs): - """Initialize GPIBEvent with an attribute dictionary. - - :param attrs: Event attributes to be stored. - :type attrs: dict or dictionary-like - """ - self.attrs = {attribute: value for attribute, value in attrs.items()} - - def __del__(self): - self.close() - - def get_attribute(self, attr): - """Retrieves the state of an attribute. - - Corresponds to viGetAttribute function of the VISA library for this particular event. - - :param attribute: Event attribute for which the state query is made (see Attributes.*) - :return: The state of the queried attribute, return value describing success. - :rtype: unicode | str | list | int, VISAStatus - """ - try: - return self.attrs[attr], StatusCode.success - except KeyError: - return None, StatusCode.error_nonsupported_attribute - - def close(self): - """Closes the event. - - Corresponds to viClose function of the VISA library. - - :return: return value of the library call. - :rtype: VISAStatus - """ - return StatusCode.success - - def _find_listeners(): """Find GPIB listeners. """ @@ -518,10 +478,10 @@ def wait_on_event(self, in_event_type, timeout): :param timeout: Absolute time period in time units that the resource shall wait for a specified event to occur before returning the time elapsed error. The time unit is in milliseconds. :return: - Logical identifier of the event actually received - - A handle specifying the unique occurrence of an event + - An object specifying the unique occurrence of an event - return value of the library call. :rtype: - eventtype - - event object # TODO + - EventData - :class:`pyvisa.constants.StatusCode` """ @@ -552,26 +512,22 @@ def wait_on_event(self, in_event_type, timeout): self.timeout = old_timeout - # TODO: set event attributes if 0x100 & event_mask & sta: - evt_type = constants.VI_EVENT_IO_COMPLETION # TODO: implement all event attributes # VI_ATTR_EVENT_TYPE: VI_EVENT_IO_COMPLETION, - # VI_ATTR_STATUS: return code of the asynchronous operation that has completed, + # VI_ATTR_STATUS: return code of the asynchronous IO operation that has completed, # VI_ATTR_JOB_ID: job ID of the asynchronous operation that has completed, # VI_ATTR_BUFFER: the address of the buffer that was used in the asynchronous operation, # VI_ATTR_RET_COUNT/VI_ATTR_RET_COUNT_32/VI_ATTR_RET_COUNT_64: number of elements that were asynchronously transferred, # VI_ATTR_OPER_NAME: name of the operation generating the event - attrs = { - constants.VI_ATTR_EVENT_TYPE: constants.VI_EVENT_IO_COMPLETION} self.event_queue.append( (constants.VI_EVENT_IO_COMPLETION, - GPIBEvent(attrs))) + EventData({constants.VI_ATTR_EVENT_TYPE: constants.VI_EVENT_IO_COMPLETION}))) if gpib.RQS & event_mask & sta: self.event_queue.append( (constants.VI_EVENT_SERVICE_REQ, - GPIBEvent({constants.VI_ATTR_EVENT_TYPE: constants.VI_EVENT_SERVICE_REQ}))) + EventData({constants.VI_ATTR_EVENT_TYPE: constants.VI_EVENT_SERVICE_REQ}))) try: out_event_type, event_data = self.event_queue.pop() diff --git a/pyvisa-py/sessions.py b/pyvisa-py/sessions.py index 4b875a1d..13774643 100644 --- a/pyvisa-py/sessions.py +++ b/pyvisa-py/sessions.py @@ -44,6 +44,47 @@ def __str__(self): __repr__ = __str__ +class EventData(object): + """A simple storage that holds attributes of a single event. + """ + + def __init__(self, attrs): + """Initialize EventData with an attribute dictionary. + + :param attrs: Event attributes to be stored. + :type attrs: dict or dictionary-like + """ + super() + self.attrs = {attr: value for attr, value in attrs.items()} + + def __del__(self): + self.close() + + def get_attribute(self, attr): + """Retrieves the state of an attribute. + + Corresponds to viGetAttribute function of the VISA library for this particular event. + + :param attribute: Event attribute for which the state query is made (see Attributes.*) + :return: The state of the queried attribute, return value describing success. + :rtype: unicode | str | list | int, VISAStatus + """ + try: + return self.attrs[attr], StatusCode.success + except KeyError: + return None, StatusCode.error_nonsupported_attribute + + def close(self): + """Closes the event. + + Corresponds to viClose function of the VISA library. + + :return: return value of the library call. + :rtype: VISAStatus + """ + return StatusCode.success + + class Session(compat.with_metaclass(abc.ABCMeta)): """A base class for Session objects. @@ -620,10 +661,10 @@ def wait_on_event(self, in_event_type, timeout): :param timeout: Absolute time period in time units that the resource shall wait for a specified event to occur before returning the time elapsed error. The time unit is in milliseconds. :return: - Logical identifier of the event actually received - - A handle specifying the unique occurrence of an event + - An object specifying the unique occurrence of an event - return value of the library call. :rtype: - eventtype - - event object # TODO + - EventData - :class:`pyvisa.constants.StatusCode` """ raise NotImplementedError