Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 40 additions & 11 deletions gpib_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

from labrad.server import LabradServer, setting
from twisted.internet.defer import inlineCallbacks
from twisted.internet import defer
from twisted.internet.reactor import callLater
from labrad.errors import DeviceNotSelectedError
import labrad.units as units
Expand Down Expand Up @@ -107,15 +108,10 @@ def refreshDevices(self):
deletions = set(self.devices.keys()) - set(addresses)
for addr in additions:
try:
if addr.startswith('GPIB'):
instName = addr
elif addr.startswith('TCPIP'):
instName = addr
elif addr.startswith('USB'):
instName = addr + '::INSTR'
else:
device_prefixes = ('GPIB', 'USB', 'TCPIP')
if not (addr.startswith(device_prefixes)):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't know you could pass a tuple to startswith. nice.

continue
instr = rm.get_instrument(instName)
instr = rm.get_instrument(addr)
instr.write_termination = ''
instr.clear()
if addr.endswith('SOCKET'):
Expand Down Expand Up @@ -145,6 +141,23 @@ def getDevice(self, c):
instr = self.devices[c['addr']]
return instr

def asynchronous_wait(self, seconds, result=None):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a utility function to do exactly this: labrad.util.wakeupCall

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maffoo , yep, that works. Superior. See: 094710d

"""
Pause the reactor for seconds to wait for GPIB devices that require
time between read and write calls.

Parameters
----------
seconds: float, sleep time in seconds

Returns
-------
Deferred
"""
d = defer.Deferred()
callLater(seconds, d.callback, result)
return d

@setting(0, addr='s', returns='s')
def address(self, c, addr=None):
"""Get or set the GPIB address for this context.
Expand Down Expand Up @@ -176,10 +189,26 @@ def read(self, c, n_bytes=None):
Otherwise, reads until the device stops sending.
"""
instr = self.getDevice(c)
if n_bytes is None:
ans = instr.read_raw()
if instr.resource_name.startswith('USB'):
try:
if n_bytes is None:
ans = instr.read()
else:
ans = instr.read(n_bytes)
except:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems dangerous to have a bare except here. Generally, I think it's preferable to do except Exception: so as not to catch things like KeyboardInterrupt

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.asynchronous_wait(0.1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would need a yield to actually wait for the deferred to fire before continuing:

yield self.asynchronous_wait(0.1)

or

yield labrad.util.wakeupCall(0.1)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maffoo, okay it appears that everything works without the yield. We tried with the yield and that ran into errors. The "read" method would likely need to use returnValue instead of return. I am not sure what else.

if n_bytes is None:
ans = instr.read()
else:
ans = instr.read(n_bytes)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not have this delay/retry logic here in the gpib server, since it seems like a device-specific requirement. Ideally, this would go in the device server for the devices that need it. One thing we might want to do is add a setting on the gpib server that just does a delay, so that then the device server could send a single packet that includes the required delays, e.g.:

p = cxn.gpib_bus.packet()
p.write(msg).delay(0.1).read()
result = yield p.send()
result.read

You could also do multiple writes with interleaved delays, etc, but all in one packet to the gpib bus server.

Similarly, we could add a delay parameter to the query setting, which would add a delay between the read and write in the same way.

ans = str(ans)
if ",," in ans:
ans = ans.replace(",,", ",") # rigol specific stupidity
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's going on here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a clarifying comment in https://github.com/CampbellGroup/servers/commit/66b48359c2ac336a5ea292f17bec261f374338cc.

With this device connected we get the following with pyvisa 1.7.

>>> import visa
>>> rm = visa.ResourceManager()
>>> rigol = rm.get_instrument('USB0::0x09C4::0x0400::DG1D150900538::INSTR')
>>> rigol.write('*IDN?')
>>> rigol.read()
u'RIGOL TECHNOLOGIES,DG1022 ,DG1D150900538,,00.03.00.09.00.02.11\n'

The double commas, before 00.03 are the use case we are taking care of.

else:
ans = instr.read_raw(n_bytes)
if n_bytes is None:
ans = instr.read_raw()
else:
ans = instr.read_raw(n_bytes)
return str(ans).strip()

@setting(5, data='s', returns='s')
Expand Down