Repository: pymeasure/pymeasure

Issue:
Unable to reach Prologix USB GPIB adapter
Dear pymeasure dev team,

I just wanted to raise you an issue concerning the Prologix adapter on version 0.11.0
I can't address it anymore...

I saw it now depends on Visa adapter class, but I cannot find a way to communicate with.
Does anyone can provide an example please ?

Best,
Benjamin.


Discussion:
Hey Benjamin,

thanks for raising that issue. Basically, all the changes are under the hood and you should be able to use the adapter as usual.

Could you please provide us with the error message and how you invoked your instrument/the prologix adapter?
Besides, what VISA library do you have installed? Only pyvisa-py (dependency of pymeasure)?
Also, did this work with 0.10.0?
Hi guys,
thanks for the quick replies.

Yes the prologix adapter com was working well with version 0.10.0

I call my instrument through a prologix adapter exactely how it is show into the doc.
I only have pyvisa-py

With the 0.11.0 version it creates error with visa and GPIB.
Try to install linux-GPIB but still not work... At the end it's just a basic serial link no ?

I will provide you the error msg when I will be back to work.
best,

Benjamin.
Is the resource string "COM" or "ASRL"?

With pyvisa you have to use the second Form ("ASRL2"), while with other implementations (or pyserial), you can use "COM1" etc.

For that reason I asked about the visa implementation and the parameters. 

Maybe we have to adjust the documentation. 
> With pyvisa you have to use the second Form ("ASRL2"), while with other implementations (or pyserial), you can use "COM1" etc.

This is only partially correct -- this depends on the used VISA backend, some of them (e.g. the NI one I think) "autocorrect" `COM1` to the proper visa string `'ASRL1::INSTR', but `pyvisa-py` does not. See https://github.com/pyvisa/pyvisa-py/issues/230

In any case, you can use `pymeasure.list_resources()` to print the available resource strings and cross-check.
Hi,
the listed ressources append like this "ASLR/dev/ttyUSB7::INSTR".
But I cannot find a way to communicate with the instrument....

> This is only partially correct -- this depends on the used VISA backend, some of them (e.g. the NI one I think) "autocorrect" COM1 to the proper visa string 'ASRL1::INSTR', but pyvisa-py` does not. See https://github.com/pyvisa/pyvisa-py/issues/230

That's what I tried to convey, that pyvisa-py (oh, I see that I forgot the "-py") requires "ASRL" and does not understand "COM". According to the docs and my experience, "::INSTR" is optional.

> But I cannot find a way to communicate with the instrument....

Please post the error message @Bnjamn 
Probably a Timeout...
Can you run as the first thing `pymeasure.log.setup_logging(console=True, console_level='DEBUG')`, maybe that yields some more info later? (Not sure at which point this gets stuck, so this might not show anything in addition)
Ah yes, do you specify the timeout? VISA expects `timeout` in **ms**, while serial expects it in **s**.
I think I didn't specify a timeout yet.
Here is the issue : 

Traceback (most recent call last):
  File "main.py", line 17, in <module>
    multim = AdvantestR6561(adapterGPIB.gpib(9))   
  File "/nfs/home/breynier/.local/lib/python3.8/site-packages/pymeasure/adapters/prologix.py", line 190, in gpib
    return PrologixAdapter(self, address, **kwargs)
  File "/nfs/home/breynier/.local/lib/python3.8/site-packages/pymeasure/adapters/prologix.py", line 84, in __init__
    super().__init__(resource_name,
  File "/nfs/home/breynier/.local/lib/python3.8/site-packages/pymeasure/adapters/visa.py", line 100, in __init__
    self.query_delay = resource_name.query_delay
AttributeError: 'PrologixAdapter' object has no attribute 'query_delay'

Here is the code :

import pymeasure
from pymeasure.instruments.advantest import AdvantestR6561
from pymeasure.adapters import SerialAdapter, PrologixAdapter, VISAAdapter
from pymeasure.instruments import list_resources

print("START")
adapterGPIB = PrologixAdapter('ASRL/dev/ttyUSB7')
multim = AdvantestR6561(adapterGPIB.gpib(9))   
print("END")

I found the problem: Line 100 in the visa Adapter.

If you use that Prologix adapter for a single device,
Create it with `PrologixAdapter(resname, address)` instead of adding the address via `.gpib(address)`. `address` and `resname` are the address and resource name.

~~~python
PrologixAdapter('ASRL/dev/ttyUSB7', 9)
multim = AdvantestR6561(adapterGPIB)
~~~

If you have more devices, add a `adapterGpib.query_delay=0` attribute before calling `.gpib(9)`


I'm sorry for causing that issue.
With 0.11 we changed a lot about how adapters work.
As a part of these changes, this line (for backward compatibility) got introduced.
We added quite a few tests, but that slipped.

I hope my proposed solution works until we publish a fix (I have to look through the adapters lest I introduce another bug). 
Hello bmoneke,

You hit right !
Just try your fix and now I'm able to talk to the instrument :-)
Thank you very much !!

I've been confused with the changes in the adapter, the use of VISA and everything.
Now it's a little bit more clear for me.

Looking forward to contribute to the lib soon.
I should come up with some new instruments ;-)

Best, 
benjamin.
@bmoneke it seems something weird is going on with the instantiation here. I think the root cause is that, when creating `adapterGPIB`, a `query_delay` default kwarg (from `VISAAdapter` init arguments) gets passed to `Adapter` init, but this one [drops](https://github.com/pymeasure/pymeasure/blob/c459d1662c123bd43709b4b3b0bd0792f81cc598/pymeasure/adapters/adapter.py#L48) all its `kwargs` unused! It seems we should at least pop off and define a `query_delay` attribute (even though it's deprecated).

Should we warn if `Adapter` gets `kwargs` that will just be dropped? I'm unsure of the implications w.r.t. normal usage, if we rely on that.
After this line:
https://github.com/pymeasure/pymeasure/blob/c459d1662c123bd43709b4b3b0bd0792f81cc598/pymeasure/adapters/visa.py#L90

We should add
~~~
    # old
    warn... 
    # new:
    kwargs['query_delay'] = query_delay
# new in main level:
self.query_delay = query_delay
~~~

That way the connection receives the argument (necessary for deprecated `ask` calling `query `)
.
I can do it next week (I'm at my phone, which already deleted my message while you wrote...). 

In principle we should not accept kwargs in `Adapter`, that you notice, when you gave a wrong argument. 
Fixing this problem, I want to remove `**kwargs` from the `Adapter` parameters. That way you will notice, if you hand an adapter a wrong parameter (e.g. Timout instead of timeout) and I think it is good Python usage. 

Do you agree @bilderbuchi @msmttchr? 
I think, this kwargs is necessary to keep signature aligned in a design that uses "cooperative classes" (see https://rhettinger.wordpress.com/2011/05/26/super-considered-super/)

If I understand it correctly, `Adapter`'s `__init__` should call in turn `super` with kwargs.

You have two cases:
- `Adapter` ancestor is `object`, in this case kwargs should be empty
- `Adapter` ancestor is something else, in this case we pass along kwargs as it is.

Agree with Michele. Yes that super call should be added. 

> It seems we should at least pop off and define a `query_delay` attribute (even though it's deprecated).

This still seems the correct way to fix this. If there's no `query_delay` in the kwargs, use a `None` default. kwargs minus `query_delay` gets passed on.

citing Raymond Hettinger:
> A more flexible approach is to have every method in the ancestor tree cooperatively designed to accept keyword arguments and a keyword-arguments dictionary, to remove any arguments that it needs, and to forward the remaining arguments using **kwds, eventually leaving the dictionary empty for the final call in the chain.

> citing Raymond Hettinger:

That was, what I had in mind. Thanks for clarifying, how to use it best.
Then I'll add that super call to the Adapter.
We do not need to pop `query_delay` as it is in the parameters list of the Visa Adapter . 

Resolving patch:
diff --git a/pymeasure/adapters/adapter.py b/pymeasure/adapters/adapter.py
--- a/pymeasure/adapters/adapter.py
+++ b/pymeasure/adapters/adapter.py
@@ -45,10 +45,11 @@ class Adapter:
             Implement it in the instrument's `read` method instead.
 
     :param log: Parent logger of the 'Adapter' logger.
-    :param kwargs: all other keyword arguments are ignored.
+    :param kwargs: Keyword arguments just to be cooperative.
     """
 
     def __init__(self, preprocess_reply=None, log=None, **kwargs):
+        super().__init__(**kwargs)
         self.preprocess_reply = preprocess_reply
         self.connection = None
         if log is None:
diff --git a/pymeasure/adapters/visa.py b/pymeasure/adapters/visa.py
--- a/pymeasure/adapters/visa.py
+++ b/pymeasure/adapters/visa.py
@@ -85,9 +85,11 @@ class VISAAdapter(Adapter):
 
     def __init__(self, resource_name, visa_library='', preprocess_reply=None,
                  query_delay=0, log=None, **kwargs):
-        super().__init__(preprocess_reply=preprocess_reply, log=log, query_delay=query_delay)
+        super().__init__(preprocess_reply=preprocess_reply, log=log)
         if query_delay:
             warn("Implement in Instrument's 'wait_until_read' instead.", FutureWarning)
+            kwargs.setdefault("query_delay", query_delay)
+        self.query_delay = query_delay
         if isinstance(resource_name, ProtocolAdapter):
             self.connection = resource_name
             self.connection.write_raw = self.connection.write_bytes


Test patch:
diff --git a/tests/adapters/test_visa.py b/tests/adapters/test_visa.py
--- a/tests/adapters/test_visa.py
+++ b/tests/adapters/test_visa.py
@@ -42,12 +42,14 @@ def adapter():
                        read_termination="\n")
 
 
-def test_nested_adapter(adapter):
-    adapter.query_delay = 10
-    a = VISAAdapter(adapter)
+@pytest.mark.parametrize("query_delay", (0, 10))
+def test_nested_adapter(query_delay):
+    a0 = VISAAdapter(SIM_RESOURCE, visa_library='@sim', read_termination="\n",
+                     query_delay=query_delay)
+    a = VISAAdapter(a0)
     assert a.resource_name == SIM_RESOURCE
-    assert a.connection == adapter.connection
-    assert a.query_delay == 10
+    assert a.connection == a0.connection
+    assert a.query_delay == query_delay
 
 
 def test_ProtocolAdapter():