Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keithley 195A #307

Closed
thestumbler opened this issue Jun 30, 2021 · 15 comments
Closed

Keithley 195A #307

thestumbler opened this issue Jun 30, 2021 · 15 comments

Comments

@thestumbler
Copy link

I read issue #262 about parsing the status byte.
So, using 0.6.0 latest release from Jan 2020, I can read the status byte, but parsing function fails (as mentioned in the issue thread).

>>> sw = dmm.get_status_word()
>>> sw
'195 0230000000100101X0'
>>> dmm.parse_status_word(sw)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 306, in parse_status_word
    terminator) = struct.unpack('@4c2s3c2s5c2s', statusword[4:])
TypeError: a bytes-like object is required, not 'str'

I think I pulled in the HEAD and tried it out (at least the behavior was different). I cloned the repository, went into that directory and did a python -m pip install -e . Well, this may or may not parse the status word correctly, I don't know. Because now reading the status word causes an error, as does just a plain read().

>>> dmm.get_status_word()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/ik/instruments/keithley/keithley195.py", line 279, in get_status_word
    return self._file.read_raw()
  File "/files/gpib/ik/instruments/abstract_instruments/comm/visa_communicator.py", line 121, in read_raw
    msg = self._buf + self._conn.read()
TypeError: can't concat str to bytearray

I'm going to go back to the 0.6.0 version for now.

@thestumbler
Copy link
Author

Well, it looks like this issue permeates to a lot of functions. I can't do a measure, a read, set the input range. For example:

(venv) pi@raspi0-gpib gpib$ python
Python 3.7.3 (default, Jun 30 2021, 13:45:48)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import instruments as ik
/files/gpib/venv/lib/python3.7/site-packages/visa.py:23: FutureWarning: The visa module provided by PyVISA is being deprecated. You can replace `import visa` by `import pyvisa as visa` to achieve the same effect.

The reason for the deprecation is the possible conflict with the visa package provided by the https://github.com/visa-sdk/visa-python which can result in hard to debug situations.
  FutureWarning,
>>> dmm = ik.keithley.Keithley195.open_visa('GPIB0::5::INSTR')
>>> dmm.mode = dmm.Mode.resistance
>>> dmm.measure()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 267, in measure
    mode = self.mode
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 97, in mode
    return self.parse_status_word(self.get_status_word())['mode']
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 306, in parse_status_word
    terminator) = struct.unpack('@4c2s3c2s5c2s', statusword[4:])
TypeError: a bytes-like object is required, not 'str'
>>> print(dmm.measure(dmm.Mode.resistance))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 261, in measure
    current_mode = self.mode
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 97, in mode
    return self.parse_status_word(self.get_status_word())['mode']
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 306, in parse_status_word
    terminator) = struct.unpack('@4c2s3c2s5c2s', statusword[4:])
TypeError: a bytes-like object is required, not 'str'
>>> dmm.input_range
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 195, in input_range
    index = self.parse_status_word(self.get_status_word())['range']
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 306, in parse_status_word
    terminator) = struct.unpack('@4c2s3c2s5c2s', statusword[4:])
TypeError: a bytes-like object is required, not 'str'
>>> dmm.input_range = 2000
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 216, in input_range
    mode = self.mode
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 97, in mode
    return self.parse_status_word(self.get_status_word())['mode']
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/keithley/keithley195.py", line 306, in parse_status_word
    terminator) = struct.unpack('@4c2s3c2s5c2s', statusword[4:])
TypeError: a bytes-like object is required, not 'str'
>>> dmm.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/abstract_instruments/instrument.py", line 164, in read
    return self._file.read(size, encoding)
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/abstract_instruments/comm/abstract_comm.py", line 209, in read
    return self.read_raw(size).decode(encoding)
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/abstract_instruments/comm/visa_communicator.py", line 134, in read_raw
    msg = self._buf + self._conn.read()
TypeError: can't concat str to bytearray
>>> dmm.query('?')
'+0.14725E+3'
>>> dmm.write('F2X')
>>> dmm.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/abstract_instruments/instrument.py", line 164, in read
    return self._file.read(size, encoding)
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/abstract_instruments/comm/abstract_comm.py", line 209, in read
    return self.read_raw(size).decode(encoding)
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/abstract_instruments/comm/visa_communicator.py", line 134, in read_raw
    msg = self._buf + self._conn.read()
TypeError: can't concat str to bytearray
>>> dmm.query('?')
'+0.14710E+3'
>>>

By the way, this value I do get with the query command is correct. I have about 147 ohms connected to the meter. So I think the communication is good, it's just the parsing string vs bytes problem.

@scasagrande
Copy link
Contributor

scasagrande commented Jun 30, 2021 via email

@thestumbler
Copy link
Author

It seems very close to the issue addressed in #262. But those corrections aren’t formally incorporated into a release yet. I wonder if I goofed up trying to install from the HEAD

@scasagrande
Copy link
Contributor

scasagrande commented Jun 30, 2021 via email

@trappitsch
Copy link
Contributor

trappitsch commented Jun 30, 2021

I was at first surprised surprised that dmm.input_range = 2000 throws an error, however, even the setter there first checks for the mode from the device. So there is a read there too. I agree, it looks similar to what we did in #262. Could you test getting the status word manually, try the following (assuming you have it loaded as dmm as above):

dmm.sendcmd('U0DX')
retval = dmm._file.read_raw()
print(f"retval: {retval}")
print(f"type: {type(retval)}")

and post the output? Really curious what comes back form the Keithley. I don't have one of those around, but quoting @scasagrande from #262:

I actually have a 195, and I will say that its a pain to get working. If you don't do everything perfectly, it gets into a strange state and you have to power cycle it. At least, that's what I had to do.

@thestumbler
Copy link
Author

Sure. Just woke up (crack of dawn here in South Korea). I’ll send the results shortly. BTW, I am able to communicate and operate the meter directly from pyvisa, if that helps any.

@thestumbler
Copy link
Author

(venv) pi@raspi0-gpib gpib$ python
Python 3.7.3 (default, Jun 30 2021, 13:45:48)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvisa as visa
>>> import instruments as ik
/files/gpib/venv/lib/python3.7/site-packages/visa.py:23: FutureWarning: The visa module provided by PyVISA is being deprecated. You can replace `import visa` by `import pyvisa as
visa` to achieve the same effect.

The reason for the deprecation is the possible conflict with the visa package provided by the https://github.com/visa-sdk/visa-python which can result in hard to debug situations.
  FutureWarning,
>>> dmm = ik.keithley.Keithley195.open_visa('GPIB0::5::INSTR')
>>> dmm.sendcmd('U0DX')
>>> retval = dmm._file.read_raw()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/venv/lib/python3.7/site-packages/instruments/abstract_instruments/comm/visa_communicator.py", line 134, in read_raw
    msg = self._buf + self._conn.read()
TypeError: can't concat str to bytearray
>>> dmm.query('')
'+0.00409E+6'
>>> dmm.sendcmd('U0DX')
>>> dmm.query('')
'195 6260002000100102X0'
>>> dmm.sendcmd('U0DX')
>>> retval = dmm.query('')
>>> type(retval)
<class 'str'>
>>> retval
'195 6260002000100102X0'
>>>

@trappitsch
Copy link
Contributor

In the visa_communicator.py, query simply calls PyVISA's own routine, which sends a message, and then reads it out with the read(). PyVISA's read() returns a string. So far this makes sense and is according to the docs.

IK in the visa_communicator.py has the following routine for read_raw:

def read_raw(self, size=-1):
    """
    Read bytes in from the pyVISA connection.

    :param int size: The number of bytes to read in from the VISA
        connection.

    :return: The read bytes from the VISA connection
    :rtype: `bytes`
    """
    if size >= 0:
        self._buf += self._conn.read_bytes(size)
        msg = self._buf[:size]
        # Remove the front of the buffer.
        del self._buf[:size]

    elif size == -1:
        # Read the whole contents, appending the buffer we've already read.
        msg = self._buf + self._conn.read()
        # Reset the contents of the buffer.
        self._buf = bytearray()
    else:
        raise ValueError("Must read a positive value of characters, or "
                         "-1 for all characters.")
    return msg

The return type here should be bytes, however, if called with the default argument for size, which is done where the Keithley195 calls the read_raw, IK calls on pyvisa's read() routine, which we already said returns a string. @scasagrande: Is this the right interpretation of this?

Should above routine use read_raw() - see here - instead of read?

@scasagrande
Copy link
Contributor

scasagrande commented Jul 3, 2021 via email

@trappitsch
Copy link
Contributor

Put the fix in and wrote tests for the visa communicator, see #308. @thestumbler: Would be great if you could test it out with the and the actual instrument to make sure it fixes your issue!

@thestumbler
Copy link
Author

I did a test, and it seems okay. Here is a short program I wrote:

#!/usr/bin/env python3
import instruments as ik

dmm = ik.keithley.Keithley195.open_visa('GPIB0::5::INSTR')
dmm.timeout = 4000

# This one still fails
# retval = dmm._file.read_raw()

# I am doing some resistance measurements
# the settings I'm using are as follows:
#   F2R3P1S0G4T5
# Let's set these using ik functions

# F2 Measurement mode
dmm.mode = dmm.Mode.resistance
# R3 Input range
dmm.input_range = 2000

# no setter/getter for these...
# P1 Filter setting
dmm.write('P1X')
# S0 Sample Rate settingt
dmm.write('S0X')
# G4 ... this is display setting, but
# I don't need to control this when using
# IK because it handles the values and units
# internally

# T5 Trigger mode
dmm.trigger_mode = dmm.TriggerMode.x_one_shot

# read some resistances...
for _ in range(5):
  val = dmm.measure()
  print(val)

Here is the error message from the read_raw() function

(venv) pi@raspi0-gpib gpib$ python -i ik-test.py
15.53 ohm
15.65 ohm
15.92 ohm
16.03 ohm
15.47 ohm
>>> dmm._file.read_raw()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/files/gpib/ik/instruments/abstract_instruments/comm/visa_communicator.py", line 118, in read_raw
    msg = self._buf + self._conn.read_raw()
  File "/files/gpib/venv/lib/python3.7/site-packages/pyvisa/resources/messagebased.py", line 405, in read_raw
    return bytes(self._read_raw(size))
  File "/files/gpib/venv/lib/python3.7/site-packages/pyvisa/resources/messagebased.py", line 442, in _read_raw
    chunk, status = self.visalib.read(self.session, size)
  File "/files/gpib/venv/lib/python3.7/site-packages/pyvisa_py/highlevel.py", line 519, in read
    return data, self.handle_return_value(session, status_code)
  File "/files/gpib/venv/lib/python3.7/site-packages/pyvisa/highlevel.py", line 251, in handle_return_value
    raise errors.VisaIOError(rv)
pyvisa.errors.VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.
>>>

@thestumbler
Copy link
Author

By the way, is there any way to get to the PyVisa resource manager associated with an IK device?

When I'm finished doing computer controlled testing, I want to put things back in a reasonable state. This seems to require some manipulation of GPIB signals (although I'm not 100% sure of myself). I've been reasonably successful doing some combination of dmm.clear() and ctl.control_ren(x) commands, but these require access to the PyVisa instances:

rm = pyvisa.Resource.Manager()
dmm = rm.open_resources('GPIO0::5::INSTR')
ctl = rm.open_resources('GPIO0::INTFC')

I looked in the visa_communicator() module but it wasn't obvious to me if/how I could get ahold of those objects or their equivalent.
Maybe there is a PyVisa / IK function that closes everything nicely, but I haven't found it. There is a _file.close() function, but it didn't seem to do anything (maybe only bookkeeping things, not actually talking to the instrument).

@trappitsch
Copy link
Contributor

I'm not surprised the read_raw() fails in the above examples. You don't seem to be writing anything to the instrument that you can read afterwards again. the read_raw() function should work as expected, you actually call it for example when running a measurement with dmm.measure(). There, IK first checks what mode the instrument is in, which uses the read_raw() down the line to get the status word. Since this is running smoothly, I assume that the issue seems to fixed with #308.

The visa communicator has a close function, which will simply pass it up the chain to pyvisa and send the .close() command. Close methods briefly seem to have been discussed in #215, @scasagrande probably has more info on this. I currently can't find the clear function being exposed directly.

Finally, when you open the instrument as shown above via IK, you basically create an instrument in instrument.py (line 594) via:

ins = pyvisa.ResourceManager().open_resource(resource_name)

This ins is then passed on to the visa communicator. You can see in there (line 36) that the visa instrument is then named self._conn. This means you should be able to directly use the visa functions, in your case, via

dmm._conn.clear()

for example. Not the nicest way and there might be an argument for exposing clear() to the user via IK, this would probably be a separate issue though.

@bilderbuchi
Copy link
Contributor

Also, if you really need to get to the PyVISA ResourceManager, you can use a resource's resource_manager attribute, e.g. from the above could it should be dmm._conn.resource_manager.

@thestumbler
Copy link
Author

I agree. As far as I’m concerned #308 fixes this #307 issue I was having.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants