question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Query over TCPIP that should timeout instead returns a blank string?

See original GitHub issue

Although it should be raising a timeout error, all I get back is the empty string and PyVisa continues on as if everything is alright. I’ve traced it all the way down to rpc.Client.make_call() (link) and the query is definitely returning error code 15, which represents a timeout according to the vxi11.ErrorCodes enum (link).

It looks like the only possible places that the error would be raised are in highlevel.PyVisaLibrary._return_handler() (link) and highlevel.PyVisaLibrary.list_resources() (link), and neither of those seem to be hit for some reason.

I’m having a hard time tracking this down, but it’s forcing us to roll back to installing a more commonly used VISA lib, and we dislike that very much. 😉

Any thoughts as to why this might be happening?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:11 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
joshsleepercommented, Jun 27, 2016

Hey @hgrecco,

I’ve researched a bit more, and it looks like highlevel.PyVisaLibrary._return_handler()(link) might never actually be called, which would explain why read and write timeouts aren’t being handled properly. In fact, a comment from the docstring explicitly states TODO: THIS IS JUST COPIED PASTED FROM NIVisaLibrary.

Debugging my way through a query that times out using the standard NI Visa (and thus pyvisa.ctwrapper.highlevel.NIVisaLibrary), I believe I discovered a handful of things.

  1. NIVisaLibrary._init() calls functions.set_signatures(self.lib, errcheck=self._return_handler) (link), which is what I believe is setting NIVisaLibrary._return_handler() to handle return data from NIVisaLibrary.read() and NIVisaLibrary.write(). PyVisaLibrary doesn’t seem to do anything equivalent to that, which is why I think errors like timeout are getting ignored when they’re clearly detected at lower levels.
    • Also, on an unrelated note, I can’t figure out why you’re using _init() instead of the built-in __init__(). Is there a reason for creating your own init function instead of making use of the one Python provides?
  2. Even though the vxi11.ErrorCodes enum (link) has a nice (but still pared down, I believe) list of error codes to use, tcpip.TCPIPInstrSession.read() incorrectly returns all errors as StatusCode.error_io (link). This means that even when timeout errors are detected properly, they (and all other errors) are masked under the generic StatusCode.error_io label (which translates to VI_ERROR_IO in the output.)

These two things seem pretty major as far as parity with the NI Visa backend goes, and I’d love to get them resolved so we can keep using this backend ourselves.

For now, I’ve made the following changes to pyvisa-py files on our end that seems to get us in the right direction:

  1. Added an additional check to highlevel.PyVisaLibrary.read() and highlevel.PyVisaLibrary.write() to properly handle translation (and raising!) of pyvisa error codes. The modifications look like this:
def read(self, session, count):
    """Reads data from device or interface synchronously.

    Corresponds to viRead function of the VISA library.

    :param session: Unique logical identifier to a session.
    :param count: Number of bytes to be read.
    :return: data read, return value of the library call.
    :rtype: bytes, VISAStatus
    """

    # from the session handle, dispatch to the read method of the session object.
    try:
        ret = self.sessions[session].read(count)
    except KeyError:
        return constants.StatusCode.error_invalid_object
    else:
        ret_code = constants.StatusCode(ret[1])
        if ret_code < 0:
            raise errors.VisaIOError(ret_code)

        return ret

def write(self, session, data):
    """Writes data to device or interface synchronously.

    Corresponds to viWrite function of the VISA library.

    :param session: Unique logical identifier to a session.
    :param data: data to be written.
    :type data: str
    :return: Number of bytes actually transferred, return value of the library call.
    :rtype: int, VISAStatus
    """

    # from the session handle, dispatch to the write method of the session object.
    try:
        ret = self.sessions[session].write(data)
    except KeyError:
        return constants.StatusCode.error_invalid_object
    else:
        ret_code = constants.StatusCode(ret[1])
        if ret_code < 0:
            raise errors.VisaIOError(ret_code)

        return ret
  1. I’ve also made some preliminary changes to tcpip.TCPIPInstrSession.read() and tcpip.TCPIPInstrSession.write(), although I’m not confident this is the correct way to handle things, and even if it is they could do with being fleshed out beyond this:
def read(self, count):
    """Reads data from device or interface synchronously.

    Corresponds to viRead function of the VISA library.

    :param count: Number of bytes to be read.
    :return: data read, return value of the library call.
    :rtype: bytes, VISAStatus
    """
    if count < self.max_recv_size:
        chunk_length = count
    else:
        chunk_length = self.max_recv_size

    flags = 0
    reason = 0

    if self.get_attribute(constants.VI_ATTR_TERMCHAR_EN)[0]:
        term_char, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR)
    else:
        term_char = 0

    if term_char:
        flags = vxi11.OP_FLAG_TERMCHAR_SET
        term_char = str(term_char).encode('utf-8')[0]

    read_data = bytearray()

    end_reason = vxi11.RX_END | vxi11.RX_CHR

    read_fun = self.interface.device_read

    status = SUCCESS

    while reason & end_reason == 0:
        error, reason, data = read_fun(self.link, chunk_length, self.timeout,
                                       self.lock_timeout, flags, term_char)

        if error == vxi11.ErrorCodes.io_timeout:
            return read_data, StatusCode.error_timeout
        elif error:
            return read_data, StatusCode.error_io

        read_data.extend(data)
        count -= len(data)

        if count <= 0:
            status = StatusCode.success_max_count_read
            break

        chunk_length = min(count, chunk_length)

    return bytes(read_data), status

def write(self, data):
    """Writes data to device or interface synchronously.

    Corresponds to viWrite function of the VISA library.

    :param data: data to be written.
    :type data: str
    :return: Number of bytes actually transferred, return value of the library call.
    :rtype: int, VISAStatus
    """

    send_end, _ = self.get_attribute(constants.VI_ATTR_SEND_END_EN)

    chunk_size = 1024
    try:

        if send_end:
            flags = vxi11.OP_FLAG_TERMCHAR_SET
        else:
            flags = 0

        num = len(data)

        offset = 0

        while num > 0:
            if num <= chunk_size:
                flags |= vxi11.OP_FLAG_END

            block = data[offset:offset + self.max_recv_size]

            error, size = self.interface.device_write(self.link, self.timeout,
                                                      self.lock_timeout, flags, block)

            if error == vxi11.ErrorCodes.io_timeout:
                return offset, StatusCode.error_timeout
            elif error or size < len(block):
                return offset, StatusCode.error_io

            offset += size
            num -= size

        return offset, SUCCESS
    except vxi11.Vxi11Error:
        return 0, StatusCode.error_timeout

Please feel free to correct me if I’m wrong on any point(s), since I’m by no means well versed in the code structure. I’m just someone with a vested interest in a pure-Python VISA backend and trying to make sure the problems we encounter are our own and not the packages we use. 😉

Let me know what’s the best way I can help, or if there’s anything else you need from me, but I’d love to see this get resolved. I’ve always hated having to install a full VISA lib, so pyvisa-py really is a godsend for me.

1reaction
MatthieuDartiailhcommented, Nov 5, 2017

Closed by #100

Read more comments on GitHub >

github_iconTop Results From Across the Web

Connection Timeout vs. Read Timeout for Java Sockets
In this tutorial, we'll focus on the timeout exceptions of Java socket programming. Our goal is to understand why these exceptions occur, ...
Read more >
Creating a Valid Connection String Using TCP IP - SQL Server ...
The following query returns the protocol used for the current connection. Copy. SELECT net_transport FROM sys.dm_exec_connections WHERE ...
Read more >
JDBC connection failed, error: TCP/IP connection to host failed
Open SQL Server Configuration Manager, and then expand SQL Server 2012 Network Configuration. Click Protocols for InstanceName, and then make sure TCP/IP is ......
Read more >
Function _viExecCommand - AutoIt
Send a Command/Query to an Instrument/Device through the VISA interface (GPIB / TCP) ... If it is not specified the last set timeout...
Read more >
Error:(VISA: timeout expired before operation completed).
Yet, the commonest teletype termination is carriage return newline (placing the carriage at home on a blank line of paper).
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found